forked from space-syndicate/space-station-14
master 21.7 syns
This commit is contained in:
@@ -77,6 +77,8 @@ csharp_style_expression_bodied_methods = false:suggestion
|
||||
#csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:suggestion
|
||||
|
||||
csharp_style_namespace_declarations = file_scoped:suggestion
|
||||
|
||||
# Pattern matching preferences
|
||||
#csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
#csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Benchmarks;
|
||||
|
||||
@@ -18,9 +19,11 @@ namespace Content.Benchmarks;
|
||||
[Virtual, MemoryDiagnoser]
|
||||
public class SpawnEquipDeleteBenchmark
|
||||
{
|
||||
private static readonly EntProtoId Mob = "MobHuman";
|
||||
private static readonly ProtoId<StartingGearPrototype> CaptainStartingGear = "CaptainGear";
|
||||
|
||||
private TestPair _pair = default!;
|
||||
private StationSpawningSystem _spawnSys = default!;
|
||||
private const string Mob = "MobHuman";
|
||||
private StartingGearPrototype _gear = default!;
|
||||
private EntityUid _entity;
|
||||
private EntityCoordinates _coords;
|
||||
@@ -39,7 +42,7 @@ public class SpawnEquipDeleteBenchmark
|
||||
var mapData = await _pair.CreateTestMap();
|
||||
_coords = mapData.GridCoords;
|
||||
_spawnSys = server.System<StationSpawningSystem>();
|
||||
_gear = server.ProtoMan.Index<StartingGearPrototype>("CaptainGear");
|
||||
_gear = server.ProtoMan.Index(CaptainStartingGear);
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace Content.Client.Access.UI;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GroupedAccessLevelChecklist : BoxContainer
|
||||
{
|
||||
private static readonly ProtoId<AccessGroupPrototype> GeneralAccessGroup = "General";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
|
||||
private bool _isMonotone;
|
||||
@@ -63,7 +65,7 @@ public sealed partial class GroupedAccessLevelChecklist : BoxContainer
|
||||
|
||||
// Ensure that the 'general' access group is added to handle
|
||||
// misc. access levels that aren't associated with any group
|
||||
if (_protoManager.TryIndex<AccessGroupPrototype>("General", out var generalAccessProto))
|
||||
if (_protoManager.TryIndex(GeneralAccessGroup, out var generalAccessProto))
|
||||
_groupedAccessLevels.TryAdd(generalAccessProto, new());
|
||||
|
||||
// Assign known access levels with their associated groups
|
||||
|
||||
@@ -8,6 +8,21 @@
|
||||
<OptionButton Name="SolutionOption" HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 4">
|
||||
<Button Name="VVButton"
|
||||
Text="{Loc 'admin-solutions-window-vv-button'}"
|
||||
ToolTip="{Loc 'admin-solutions-window-vv-button-tooltip'}"
|
||||
Disabled="True"
|
||||
HorizontalExpand="True"
|
||||
StyleClasses="OpenRight"/>
|
||||
<Button Name="SolutionButton"
|
||||
Text="{Loc 'admin-solutions-window-solution-button'}"
|
||||
ToolTip="{Loc 'admin-solutions-window-solution-button-tooltip'}"
|
||||
Disabled="True"
|
||||
HorizontalExpand="True"
|
||||
StyleClasses="OpenLeft"/>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- The total volume / capacity of the solution -->
|
||||
<BoxContainer Name="VolumeBox" Orientation="Vertical" HorizontalExpand="True" Margin="0 4"/>
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
@@ -20,6 +21,7 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IClientGameTiming _timing = default!;
|
||||
[Dependency] private readonly IClientAdminManager _admin = default!;
|
||||
|
||||
private NetEntity _target = NetEntity.Invalid;
|
||||
private string? _selectedSolution;
|
||||
@@ -34,6 +36,11 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
||||
|
||||
SolutionOption.OnItemSelected += SolutionSelected;
|
||||
AddButton.OnPressed += OpenAddReagentWindow;
|
||||
VVButton.OnPressed += OpenVVWindow;
|
||||
SolutionButton.OnPressed += OpenSolutionWindow;
|
||||
|
||||
VVButton.Disabled = !_admin.CanViewVar();
|
||||
SolutionButton.Disabled = !_admin.CanViewVar();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
@@ -271,6 +278,32 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
||||
_addReagentWindow.OpenCentered();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the corresponding solution entity in a ViewVariables window.
|
||||
/// </summary>
|
||||
private void OpenVVWindow(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
if (_solutions == null
|
||||
|| _selectedSolution == null
|
||||
|| !_solutions.TryGetValue(_selectedSolution, out var uid)
|
||||
|| !_entityManager.TryGetNetEntity(uid, out var netEntity))
|
||||
return;
|
||||
_consoleHost.ExecuteCommand($"vv {netEntity}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the corresponding Solution instance in a ViewVariables window.
|
||||
/// </summary>
|
||||
private void OpenSolutionWindow(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
if (_solutions == null
|
||||
|| _selectedSolution == null
|
||||
|| !_solutions.TryGetValue(_selectedSolution, out var uid)
|
||||
|| !_entityManager.TryGetNetEntity(uid, out var netEntity))
|
||||
return;
|
||||
_consoleHost.ExecuteCommand($"vv /entity/{netEntity}/Solution/Solution");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When a new solution is selected, set _selectedSolution and update the reagent list.
|
||||
/// </summary>
|
||||
|
||||
@@ -62,11 +62,11 @@ public sealed class AnomalySystem : SharedAnomalySystem
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<AnomalySupercriticalComponent, SpriteComponent>();
|
||||
var query = EntityQueryEnumerator<AnomalyComponent, AnomalySupercriticalComponent, SpriteComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var super, out var sprite))
|
||||
while (query.MoveNext(out var uid, out var anomaly, out var super, out var sprite))
|
||||
{
|
||||
var completion = 1f - (float)((super.EndTime - _timing.CurTime) / super.SupercriticalDuration);
|
||||
var completion = 1f - (float) ((super.EndTime - _timing.CurTime) / anomaly.SupercriticalDuration);
|
||||
var scale = completion * (super.MaxScaleAmount - 1f) + 1f;
|
||||
_sprite.SetScale((uid, sprite), new Vector2(scale, scale));
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||
using Content.Shared.SprayPainter.Prototypes;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Atmos.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Used to change the appearance of gas canisters.
|
||||
/// </summary>
|
||||
public sealed class GasCanisterAppearanceSystem : VisualizerSystem<GasCanisterComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, GasCanisterComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (!AppearanceSystem.TryGetData<string>(uid, PaintableVisuals.Prototype, out var protoName, args.Component) || args.Sprite is not { } old)
|
||||
return;
|
||||
|
||||
if (!_prototypeManager.HasIndex(protoName))
|
||||
return;
|
||||
|
||||
// Create the given prototype and get its first layer.
|
||||
var tempUid = Spawn(protoName);
|
||||
SpriteSystem.LayerSetRsiState(uid, 0, SpriteSystem.LayerGetRsiState(tempUid, 0));
|
||||
QueueDel(tempUid);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,46 @@
|
||||
using Content.Client.Atmos.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Content.Client.UserInterface.Systems.Storage.Controls;
|
||||
using Content.Shared.Atmos.Piping;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Item;
|
||||
|
||||
namespace Content.Client.Atmos.EntitySystems;
|
||||
|
||||
public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PipeColorVisualsComponent, GetInhandVisualsEvent>(OnGetVisuals);
|
||||
SubscribeLocalEvent<PipeColorVisualsComponent, BeforeRenderInGridEvent>(OnDrawInGrid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is used to display the color changes of the pipe on the screen..
|
||||
/// </summary>
|
||||
private void OnGetVisuals(Entity<PipeColorVisualsComponent> item, ref GetInhandVisualsEvent args)
|
||||
{
|
||||
foreach (var (_, layerData) in args.Layers)
|
||||
{
|
||||
if (TryComp(item.Owner, out AtmosPipeColorComponent? pipeColor))
|
||||
layerData.Color = pipeColor.Color;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is used to change the pipe's color in a container grid.
|
||||
/// </summary>
|
||||
private void OnDrawInGrid(Entity<PipeColorVisualsComponent> item, ref BeforeRenderInGridEvent args)
|
||||
{
|
||||
if (TryComp(item.Owner, out AtmosPipeColorComponent? pipeColor))
|
||||
args.Color = pipeColor.Color;
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, PipeColorVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (TryComp<SpriteComponent>(uid, out var sprite)
|
||||
@@ -15,6 +50,8 @@ public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisual
|
||||
var layer = sprite[PipeVisualLayers.Pipe];
|
||||
layer.Color = color.WithAlpha(layer.Color.A);
|
||||
}
|
||||
|
||||
_itemSystem.VisualsChanged(uid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2007/xaml"
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
MinSize="500 500" Resizable="True" Title="Air Alarm">
|
||||
MinSize="500 500" Resizable="True" Title="{Loc air-alarm-ui-title}">
|
||||
<BoxContainer Orientation="Vertical" Margin="5 5 5 5">
|
||||
<!-- Status (pressure, temperature, alarm state, device total, address, etc) -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 0 0 2">
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace Content.Client.Atmos.Overlays
|
||||
{
|
||||
public sealed class GasTileOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IMapManager _mapManager;
|
||||
private readonly SharedMapSystem _mapSystem;
|
||||
@@ -54,7 +56,7 @@ namespace Content.Client.Atmos.Overlays
|
||||
_mapManager = IoCManager.Resolve<IMapManager>();
|
||||
_mapSystem = entManager.System<SharedMapSystem>();
|
||||
_xformSys = xformSys;
|
||||
_shader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_shader = protoMan.Index(UnshadedShader).Instance();
|
||||
ZIndex = GasOverlayZIndex;
|
||||
|
||||
_gasCount = system.VisibleGasId.Length;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
using Content.Shared.Bed;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Bed;
|
||||
|
||||
public sealed class StasisBedSystem : VisualizerSystem<StasisBedVisualsComponent>
|
||||
{
|
||||
protected override void OnAppearanceChange(EntityUid uid, StasisBedVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite != null
|
||||
&& AppearanceSystem.TryGetData<bool>(uid, StasisBedVisuals.IsOn, out var isOn, args.Component))
|
||||
{
|
||||
SpriteSystem.LayerSetVisible((uid, args.Sprite), StasisBedVisualLayers.IsOn, isOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum StasisBedVisualLayers : byte
|
||||
{
|
||||
IsOn,
|
||||
}
|
||||
6
Content.Client/Body/Systems/MetabolizerSystem.cs
Normal file
6
Content.Client/Body/Systems/MetabolizerSystem.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using Content.Shared.Body.Systems;
|
||||
|
||||
namespace Content.Client.Body.Systems;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class MetabolizerSystem : SharedMetabolizerSystem;
|
||||
@@ -18,14 +18,10 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
|
||||
{
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
||||
|
||||
[ValidatePrototypeId<MixingCategoryPrototype>]
|
||||
private const string DefaultMixingCategory = "DummyMix";
|
||||
[ValidatePrototypeId<MixingCategoryPrototype>]
|
||||
private const string DefaultGrindCategory = "DummyGrind";
|
||||
[ValidatePrototypeId<MixingCategoryPrototype>]
|
||||
private const string DefaultJuiceCategory = "DummyJuice";
|
||||
[ValidatePrototypeId<MixingCategoryPrototype>]
|
||||
private const string DefaultCondenseCategory = "DummyCondense";
|
||||
private static readonly ProtoId<MixingCategoryPrototype> DefaultMixingCategory = "DummyMix";
|
||||
private static readonly ProtoId<MixingCategoryPrototype> DefaultGrindCategory = "DummyGrind";
|
||||
private static readonly ProtoId<MixingCategoryPrototype> DefaultJuiceCategory = "DummyJuice";
|
||||
private static readonly ProtoId<MixingCategoryPrototype> DefaultCondenseCategory = "DummyCondense";
|
||||
|
||||
private readonly Dictionary<string, List<ReagentSourceData>> _reagentSources = new();
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.Client.CombatMode
|
||||
{
|
||||
public sealed class ColoredScreenBorderOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> Shader = "ColoredScreenBorder";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
@@ -16,7 +18,7 @@ namespace Content.Client.CombatMode
|
||||
public ColoredScreenBorderOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_shader = _prototypeManager.Index<ShaderPrototype>("ColoredScreenBorder").Instance();
|
||||
_shader = _prototypeManager.Index(Shader).Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
|
||||
@@ -27,8 +27,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
||||
|
||||
private EntityUid _owner;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
public const string NoBoardEffectId = "FlatpackerNoBoardEffect";
|
||||
public static readonly EntProtoId NoBoardEffectId = "FlatpackerNoBoardEffect";
|
||||
|
||||
private EntityUid? _currentBoard = EntityUid.Invalid;
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ namespace Content.Client.Cooldown
|
||||
{
|
||||
public sealed class CooldownGraphic : Control
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> Shader = "CooldownAnimation";
|
||||
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
|
||||
@@ -15,7 +17,7 @@ namespace Content.Client.Cooldown
|
||||
public CooldownGraphic()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_shader = _protoMan.Index<ShaderPrototype>("CooldownAnimation").InstanceUnique();
|
||||
_shader = _protoMan.Index(Shader).InstanceUnique();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -33,8 +33,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
||||
|
||||
public readonly EntityUid Console;
|
||||
|
||||
[ValidatePrototypeId<LocalizedDatasetPrototype>]
|
||||
private const string ReasonPlaceholders = "CriminalRecordsWantedReasonPlaceholders";
|
||||
private static readonly ProtoId<LocalizedDatasetPrototype> ReasonPlaceholders = "CriminalRecordsWantedReasonPlaceholders";
|
||||
|
||||
public Action<uint?>? OnKeySelected;
|
||||
public Action<StationRecordFilterType, string>? OnFiltersChanged;
|
||||
@@ -296,7 +295,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
||||
|
||||
var field = "reason";
|
||||
var title = Loc.GetString("criminal-records-status-" + status.ToString().ToLower());
|
||||
var placeholders = _proto.Index<LocalizedDatasetPrototype>(ReasonPlaceholders);
|
||||
var placeholders = _proto.Index(ReasonPlaceholders);
|
||||
var placeholder = Loc.GetString("criminal-records-console-reason-placeholder", ("placeholder", _random.Pick(placeholders))); // just funny it doesn't actually get used
|
||||
var prompt = Loc.GetString("criminal-records-console-reason");
|
||||
var entry = new QuickDialogEntry(field, QuickDialogEntryType.LongText, prompt, placeholder);
|
||||
|
||||
@@ -1,7 +1,126 @@
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Client.Stunnable;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Damage.Systems;
|
||||
|
||||
public sealed partial class StaminaSystem : SharedStaminaSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly StunSystem _stun = default!; // Clientside Stun System
|
||||
|
||||
private const string StaminaAnimationKey = "stamina";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StaminaComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
SubscribeLocalEvent<ActiveStaminaComponent, ComponentShutdown>(OnActiveStaminaShutdown);
|
||||
SubscribeLocalEvent<StaminaComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
}
|
||||
|
||||
protected override void OnStamHandleState(Entity<StaminaComponent> entity, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
base.OnStamHandleState(entity, ref args);
|
||||
|
||||
TryStartAnimation(entity);
|
||||
}
|
||||
|
||||
private void OnActiveStaminaShutdown(Entity<ActiveStaminaComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
// If we don't have active stamina, we shouldn't have stamina damage. If the update loop can trust it we can trust it.
|
||||
if (!TryComp<StaminaComponent>(entity, out var stamina))
|
||||
return;
|
||||
|
||||
StopAnimation((entity, stamina));
|
||||
}
|
||||
|
||||
protected override void OnShutdown(Entity<StaminaComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
base.OnShutdown(entity, ref args);
|
||||
|
||||
StopAnimation(entity);
|
||||
}
|
||||
|
||||
private void OnMobStateChanged(Entity<StaminaComponent> ent, ref MobStateChangedEvent args)
|
||||
{
|
||||
if (args.NewMobState == MobState.Dead)
|
||||
StopAnimation(ent);
|
||||
}
|
||||
|
||||
private void TryStartAnimation(Entity<StaminaComponent> entity)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(entity, out var sprite))
|
||||
return;
|
||||
|
||||
// If the animation is running, the system should update it accordingly
|
||||
// If we're below the threshold to animate, don't try to animate
|
||||
// If we're in stamcrit don't override it
|
||||
if (entity.Comp.AnimationThreshold > entity.Comp.StaminaDamage || _animation.HasRunningAnimation(entity, StaminaAnimationKey))
|
||||
return;
|
||||
|
||||
// Don't animate if we're dead
|
||||
if (_mobState.IsDead(entity))
|
||||
return;
|
||||
|
||||
entity.Comp.StartOffset = sprite.Offset;
|
||||
|
||||
PlayAnimation((entity, entity.Comp, sprite));
|
||||
}
|
||||
|
||||
private void StopAnimation(Entity<StaminaComponent, SpriteComponent?> entity)
|
||||
{
|
||||
if(!Resolve(entity, ref entity.Comp2))
|
||||
return;
|
||||
|
||||
_animation.Stop(entity.Owner, StaminaAnimationKey);
|
||||
entity.Comp1.StartOffset = entity.Comp2.Offset;
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(Entity<StaminaComponent> entity, ref AnimationCompletedEvent args)
|
||||
{
|
||||
if (args.Key != StaminaAnimationKey || !args.Finished || !TryComp<SpriteComponent>(entity, out var sprite))
|
||||
return;
|
||||
|
||||
// stop looping if we're below the threshold
|
||||
if (entity.Comp.AnimationThreshold > entity.Comp.StaminaDamage)
|
||||
{
|
||||
_animation.Stop(entity.Owner, StaminaAnimationKey);
|
||||
_sprite.SetOffset((entity, sprite), entity.Comp.StartOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasComp<AnimationPlayerComponent>(entity))
|
||||
return;
|
||||
|
||||
PlayAnimation((entity, entity.Comp, sprite));
|
||||
}
|
||||
|
||||
private void PlayAnimation(Entity<StaminaComponent, SpriteComponent> entity)
|
||||
{
|
||||
var step = Math.Clamp((entity.Comp1.StaminaDamage - entity.Comp1.AnimationThreshold) /
|
||||
(entity.Comp1.CritThreshold - entity.Comp1.AnimationThreshold),
|
||||
0f,
|
||||
1f); // The things I do for project 0 warnings
|
||||
var frequency = entity.Comp1.FrequencyMin + step * entity.Comp1.FrequencyMod;
|
||||
var jitter = entity.Comp1.JitterAmplitudeMin + step * entity.Comp1.JitterAmplitudeMod;
|
||||
var breathing = entity.Comp1.BreathingAmplitudeMin + step * entity.Comp1.BreathingAmplitudeMod;
|
||||
|
||||
_animation.Play(entity.Owner,
|
||||
_stun.GetFatigueAnimation(entity.Comp2,
|
||||
frequency,
|
||||
entity.Comp1.Jitters,
|
||||
jitter * entity.Comp1.JitterMin,
|
||||
jitter * entity.Comp1.JitterMax,
|
||||
breathing,
|
||||
entity.Comp1.StartOffset,
|
||||
ref entity.Comp1.LastJitter),
|
||||
StaminaAnimationKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public sealed class DeliveryVisualizerSystem : VisualizerSystem<DeliveryComponen
|
||||
|
||||
if (!_prototype.TryIndex<JobIconPrototype>(job, out var icon))
|
||||
{
|
||||
SpriteSystem.LayerSetTexture((uid, args.Sprite), DeliveryVisualLayers.JobStamp, SpriteSystem.Frame0(_prototype.Index("JobIconUnknown")));
|
||||
SpriteSystem.LayerSetTexture((uid, args.Sprite), DeliveryVisualLayers.JobStamp, SpriteSystem.Frame0(_prototype.Index(UnknownIcon).Icon));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
using Content.Shared.Devour;
|
||||
|
||||
namespace Content.Client.Devour;
|
||||
public sealed class DevourSystem : SharedDevourSystem
|
||||
{
|
||||
}
|
||||
@@ -14,6 +14,8 @@ namespace Content.Client.DoAfter;
|
||||
|
||||
public sealed class DoAfterOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IGameTiming _timing;
|
||||
private readonly IPlayerManager _player;
|
||||
@@ -50,7 +52,7 @@ public sealed class DoAfterOverlay : Overlay
|
||||
var sprite = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/progress_bar.rsi"), "icon");
|
||||
_barTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite);
|
||||
|
||||
_unshadedShader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_unshadedShader = protoManager.Index(UnshadedShader).Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.SprayPainter.Prototypes;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Doors;
|
||||
|
||||
public sealed class DoorSystem : SharedDoorSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -85,8 +86,8 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
if (!AppearanceSystem.TryGetData<DoorState>(entity, DoorVisuals.State, out var state, args.Component))
|
||||
state = DoorState.Closed;
|
||||
|
||||
if (AppearanceSystem.TryGetData<string>(entity, DoorVisuals.BaseRSI, out var baseRsi, args.Component))
|
||||
UpdateSpriteLayers((entity.Owner, args.Sprite), baseRsi);
|
||||
if (AppearanceSystem.TryGetData<string>(entity, PaintableVisuals.Prototype, out var prototype, args.Component))
|
||||
UpdateSpriteLayers((entity.Owner, args.Sprite), prototype);
|
||||
|
||||
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey))
|
||||
_animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey);
|
||||
@@ -139,14 +140,14 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSpriteLayers(Entity<SpriteComponent> sprite, string baseRsi)
|
||||
private void UpdateSpriteLayers(Entity<SpriteComponent> sprite, string targetProto)
|
||||
{
|
||||
if (!_resourceCache.TryGetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / baseRsi, out var res))
|
||||
{
|
||||
Log.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace);
|
||||
if (!_prototypeManager.TryIndex(targetProto, out var target))
|
||||
return;
|
||||
}
|
||||
|
||||
_sprite.SetBaseRsi(sprite.AsNullable(), res.RSI);
|
||||
if (!target.TryGetComponent(out SpriteComponent? targetSprite, _componentFactory))
|
||||
return;
|
||||
|
||||
_sprite.SetBaseRsi(sprite.AsNullable(), targetSprite.BaseRSI);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Content.Client.Drowsiness;
|
||||
|
||||
public sealed class DrowsinessOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> Shader = "Drowsiness";
|
||||
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
@@ -33,7 +35,7 @@ public sealed class DrowsinessOverlay : Overlay
|
||||
|
||||
_statusEffects = _sysMan.GetEntitySystem<StatusEffectsSystem>();
|
||||
|
||||
_drowsinessShader = _prototypeManager.Index<ShaderPrototype>("Drowsiness").InstanceUnique();
|
||||
_drowsinessShader = _prototypeManager.Index(Shader).InstanceUnique();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace Content.Client.Drugs;
|
||||
|
||||
public sealed class RainbowOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> Shader = "Rainbow";
|
||||
|
||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
@@ -41,7 +43,7 @@ public sealed class RainbowOverlay : Overlay
|
||||
|
||||
_statusEffects = _sysMan.GetEntitySystem<StatusEffectsSystem>();
|
||||
|
||||
_rainbowShader = _prototypeManager.Index<ShaderPrototype>("Rainbow").InstanceUnique();
|
||||
_rainbowShader = _prototypeManager.Index(Shader).InstanceUnique();
|
||||
_config.OnValueChanged(CCVars.ReducedMotion, OnReducedMotionChanged, invokeImmediately: true);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Content.Client.Drunk;
|
||||
|
||||
public sealed class DrunkOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> Shader = "Drunk";
|
||||
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
@@ -30,7 +32,7 @@ public sealed class DrunkOverlay : Overlay
|
||||
public DrunkOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_drunkShader = _prototypeManager.Index<ShaderPrototype>("Drunk").InstanceUnique();
|
||||
_drunkShader = _prototypeManager.Index(Shader).InstanceUnique();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace Content.Client.Explosion;
|
||||
[UsedImplicitly]
|
||||
public sealed class ExplosionOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
@@ -26,7 +28,7 @@ public sealed class ExplosionOverlay : Overlay
|
||||
public ExplosionOverlay(SharedAppearanceSystem appearanceSystem)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_shader = _proto.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_shader = _proto.Index(UnshadedShader).Instance();
|
||||
_transformSystem = _entMan.System<SharedTransformSystem>();
|
||||
_appearance = appearanceSystem;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ namespace Content.Client.Eye.Blinding
|
||||
{
|
||||
public sealed class BlindOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> GreyscaleShader = "GreyscaleFullscreen";
|
||||
private static readonly ProtoId<ShaderPrototype> CircleShader = "CircleMask";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
@@ -27,8 +30,8 @@ namespace Content.Client.Eye.Blinding
|
||||
public BlindOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_greyscaleShader = _prototypeManager.Index<ShaderPrototype>("GreyscaleFullscreen").InstanceUnique();
|
||||
_circleMaskShader = _prototypeManager.Index<ShaderPrototype>("CircleMask").InstanceUnique();
|
||||
_greyscaleShader = _prototypeManager.Index(GreyscaleShader).InstanceUnique();
|
||||
_circleMaskShader = _prototypeManager.Index(CircleShader).InstanceUnique();
|
||||
}
|
||||
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,9 @@ namespace Content.Client.Eye.Blinding
|
||||
{
|
||||
public sealed class BlurryVisionOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> CataractsShader = "Cataracts";
|
||||
private static readonly ProtoId<ShaderPrototype> CircleShader = "CircleMask";
|
||||
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
@@ -33,8 +36,8 @@ namespace Content.Client.Eye.Blinding
|
||||
public BlurryVisionOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_cataractsShader = _prototypeManager.Index<ShaderPrototype>("Cataracts").InstanceUnique();
|
||||
_circleMaskShader = _prototypeManager.Index<ShaderPrototype>("CircleMask").InstanceUnique();
|
||||
_cataractsShader = _prototypeManager.Index(CataractsShader).InstanceUnique();
|
||||
_circleMaskShader = _prototypeManager.Index(CircleShader).InstanceUnique();
|
||||
|
||||
_circleMaskShader.SetParameter("CircleMinDist", 0.0f);
|
||||
_circleMaskShader.SetParameter("CirclePow", NoMotion_Pow);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Flash;
|
||||
using Content.Shared.Flash.Components;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -11,25 +13,31 @@ namespace Content.Client.Flash
|
||||
{
|
||||
public sealed class FlashOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> FlashedEffectShader = "FlashedEffect";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
|
||||
private readonly SharedFlashSystem _flash;
|
||||
private readonly StatusEffectsSystem _statusSys;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
private readonly ShaderInstance _shader;
|
||||
public float PercentComplete = 0.0f;
|
||||
private bool _reducedMotion;
|
||||
public float PercentComplete;
|
||||
public Texture? ScreenshotTexture;
|
||||
|
||||
public FlashOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_shader = _prototypeManager.Index<ShaderPrototype>("FlashedEffect").InstanceUnique();
|
||||
_shader = _prototypeManager.Index(FlashedEffectShader).InstanceUnique();
|
||||
_flash = _entityManager.System<SharedFlashSystem>();
|
||||
_statusSys = _entityManager.System<StatusEffectsSystem>();
|
||||
|
||||
_configManager.OnValueChanged(CCVars.ReducedMotion, (b) => { _reducedMotion = b; }, invokeImmediately: true);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
@@ -47,8 +55,8 @@ namespace Content.Client.Flash
|
||||
return;
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
var lastsFor = (float) (time.Value.Item2 - time.Value.Item1).TotalSeconds;
|
||||
var timeDone = (float) (curTime - time.Value.Item1).TotalSeconds;
|
||||
var lastsFor = (float)(time.Value.Item2 - time.Value.Item1).TotalSeconds;
|
||||
var timeDone = (float)(curTime - time.Value.Item1).TotalSeconds;
|
||||
|
||||
PercentComplete = timeDone / lastsFor;
|
||||
}
|
||||
@@ -74,10 +82,22 @@ namespace Content.Client.Flash
|
||||
return;
|
||||
|
||||
var worldHandle = args.WorldHandle;
|
||||
_shader.SetParameter("percentComplete", PercentComplete);
|
||||
worldHandle.UseShader(_shader);
|
||||
worldHandle.DrawTextureRectRegion(ScreenshotTexture, args.WorldBounds);
|
||||
worldHandle.UseShader(null);
|
||||
if (_reducedMotion)
|
||||
{
|
||||
// TODO: This is a very simple placeholder.
|
||||
// Replace it with a proper shader once we come up with something good.
|
||||
// Turns out making an effect that is supposed to be a bright, sudden, and disorienting flash
|
||||
// not do any of that while also being equivalent in terms of game balance is hard.
|
||||
var alpha = 1 - MathF.Pow(PercentComplete, 8f); // similar falloff curve to the flash shader
|
||||
worldHandle.DrawRect(args.WorldBounds, new Color(0f, 0f, 0f, alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
_shader.SetParameter("percentComplete", PercentComplete);
|
||||
worldHandle.UseShader(_shader);
|
||||
worldHandle.DrawTextureRectRegion(ScreenshotTexture, args.WorldBounds);
|
||||
worldHandle.UseShader(null);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DisposeBehavior()
|
||||
|
||||
@@ -21,8 +21,7 @@ namespace Content.Client.Guidebook.Controls;
|
||||
[UsedImplicitly, GenerateTypedNameReferences]
|
||||
public sealed partial class GuideReagentReaction : BoxContainer, ISearchableControl
|
||||
{
|
||||
[ValidatePrototypeId<MixingCategoryPrototype>]
|
||||
private const string DefaultMixingCategory = "DummyMix";
|
||||
private static readonly ProtoId<MixingCategoryPrototype> DefaultMixingCategory = "DummyMix";
|
||||
|
||||
private readonly IPrototypeManager _protoMan;
|
||||
|
||||
@@ -55,7 +54,7 @@ public sealed partial class GuideReagentReaction : BoxContainer, ISearchableCont
|
||||
}
|
||||
else
|
||||
{
|
||||
mixingCategories.Add(protoMan.Index<MixingCategoryPrototype>(DefaultMixingCategory));
|
||||
mixingCategories.Add(protoMan.Index(DefaultMixingCategory));
|
||||
}
|
||||
SetMixingCategory(mixingCategories, prototype, sysMan);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace Content.Client.Input
|
||||
human.AddFunction(EngineKeyFunctions.MoveLeft);
|
||||
human.AddFunction(EngineKeyFunctions.MoveRight);
|
||||
human.AddFunction(EngineKeyFunctions.Walk);
|
||||
human.AddFunction(ContentKeyFunctions.ToggleKnockdown);
|
||||
human.AddFunction(ContentKeyFunctions.SwapHands);
|
||||
human.AddFunction(ContentKeyFunctions.SwapHandsReverse);
|
||||
human.AddFunction(ContentKeyFunctions.Drop);
|
||||
|
||||
@@ -7,17 +7,14 @@ namespace Content.Client.Interactable.Components
|
||||
[RegisterComponent]
|
||||
public sealed partial class InteractionOutlineComponent : Component
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> ShaderInRange = "SelectionOutlineInrange";
|
||||
private static readonly ProtoId<ShaderPrototype> ShaderOutOfRange = "SelectionOutline";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
private const float DefaultWidth = 1;
|
||||
|
||||
[ValidatePrototypeId<ShaderPrototype>]
|
||||
private const string ShaderInRange = "SelectionOutlineInrange";
|
||||
|
||||
[ValidatePrototypeId<ShaderPrototype>]
|
||||
private const string ShaderOutOfRange = "SelectionOutline";
|
||||
|
||||
private bool _inRange;
|
||||
private ShaderInstance? _shader;
|
||||
private int _lastRenderScale;
|
||||
@@ -65,7 +62,7 @@ namespace Content.Client.Interactable.Components
|
||||
{
|
||||
var shaderName = inRange ? ShaderInRange : ShaderOutOfRange;
|
||||
|
||||
var instance = _prototypeManager.Index<ShaderPrototype>(shaderName).InstanceUnique();
|
||||
var instance = _prototypeManager.Index(shaderName).InstanceUnique();
|
||||
instance.SetParameter("outline_width", DefaultWidth * renderScale);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,10 @@ namespace Content.Client.Interaction;
|
||||
/// </summary>
|
||||
public sealed class DragDropSystem : SharedDragDropSystem
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> ShaderDropTargetInRange = "SelectionOutlineInrange";
|
||||
|
||||
private static readonly ProtoId<ShaderPrototype> ShaderDropTargetOutOfRange = "SelectionOutline";
|
||||
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
@@ -54,12 +58,6 @@ public sealed class DragDropSystem : SharedDragDropSystem
|
||||
// mousedown event so it can be treated like a regular click
|
||||
private const float MaxMouseDownTimeForReplayingClick = 0.85f;
|
||||
|
||||
[ValidatePrototypeId<ShaderPrototype>]
|
||||
private const string ShaderDropTargetInRange = "SelectionOutlineInrange";
|
||||
|
||||
[ValidatePrototypeId<ShaderPrototype>]
|
||||
private const string ShaderDropTargetOutOfRange = "SelectionOutline";
|
||||
|
||||
/// <summary>
|
||||
/// Current entity being dragged around.
|
||||
/// </summary>
|
||||
@@ -113,8 +111,8 @@ public sealed class DragDropSystem : SharedDragDropSystem
|
||||
|
||||
Subs.CVar(_cfgMan, CCVars.DragDropDeadZone, SetDeadZone, true);
|
||||
|
||||
_dropTargetInRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetInRange).Instance();
|
||||
_dropTargetOutOfRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetOutOfRange).Instance();
|
||||
_dropTargetInRangeShader = _prototypeManager.Index(ShaderDropTargetInRange).Instance();
|
||||
_dropTargetOutOfRangeShader = _prototypeManager.Index(ShaderDropTargetOutOfRange).Instance();
|
||||
// needs to fire on mouseup and mousedown so we can detect a drag / drop
|
||||
CommandBinds.Builder
|
||||
.BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(OnUse, false, true), new[] { typeof(SharedInteractionSystem) })
|
||||
|
||||
@@ -15,6 +15,10 @@ namespace Content.Client.Light;
|
||||
/// </summary>
|
||||
public sealed class AmbientOcclusionOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
private static readonly ProtoId<ShaderPrototype> StencilMaskShader = "StencilMask";
|
||||
private static readonly ProtoId<ShaderPrototype> StencilEqualDrawShader = "StencilEqualDraw";
|
||||
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
@@ -87,7 +91,7 @@ public sealed class AmbientOcclusionOverlay : Overlay
|
||||
args.WorldHandle.RenderInRenderTarget(_aoTarget,
|
||||
() =>
|
||||
{
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("unshaded").Instance());
|
||||
worldHandle.UseShader(_proto.Index(UnshadedShader).Instance());
|
||||
var invMatrix = _aoTarget.GetWorldToLocalMatrix(viewport.Eye!, scale);
|
||||
|
||||
foreach (var entry in query.QueryAabb(mapId, worldBounds))
|
||||
@@ -110,7 +114,7 @@ public sealed class AmbientOcclusionOverlay : Overlay
|
||||
() =>
|
||||
{
|
||||
// Don't want lighting affecting it.
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("unshaded").Instance());
|
||||
worldHandle.UseShader(_proto.Index(UnshadedShader).Instance());
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
|
||||
{
|
||||
@@ -131,11 +135,11 @@ public sealed class AmbientOcclusionOverlay : Overlay
|
||||
}, Color.Transparent);
|
||||
|
||||
// Draw the stencil texture to depth buffer.
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.UseShader(_proto.Index(StencilMaskShader).Instance());
|
||||
worldHandle.DrawTextureRect(_aoStencilTarget!.Texture, worldBounds);
|
||||
|
||||
// Draw the Blurred AO texture finally.
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilEqualDraw").Instance());
|
||||
worldHandle.UseShader(_proto.Index(StencilEqualDrawShader).Instance());
|
||||
worldHandle.DrawTextureRect(_aoTarget!.Texture, worldBounds, color);
|
||||
|
||||
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
||||
|
||||
@@ -238,6 +238,9 @@ namespace Content.Client.Light.Components
|
||||
|
||||
public override void OnInitialize()
|
||||
{
|
||||
// This is very janky. This could easily result in no visible animation at all if the random values happen
|
||||
// to all be close to each other.
|
||||
// TODO ANIMATIONS
|
||||
_randomValue1 = (float)InterpolateLinear(StartValue, EndValue, (float)_random.NextDouble());
|
||||
_randomValue2 = (float)InterpolateLinear(StartValue, EndValue, (float)_random.NextDouble());
|
||||
_randomValue3 = (float)InterpolateLinear(StartValue, EndValue, (float)_random.NextDouble());
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Content.Client.Light;
|
||||
|
||||
public sealed class SunShadowOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> MixShader = "Mix";
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
@@ -150,7 +152,7 @@ public sealed class SunShadowOverlay : Overlay
|
||||
viewport.LightRenderTarget.GetWorldToLocalMatrix(eye, scale);
|
||||
worldHandle.SetTransform(invMatrix);
|
||||
|
||||
var maskShader = _protoManager.Index<ShaderPrototype>("Mix").Instance();
|
||||
var maskShader = _protoManager.Index(MixShader).Instance();
|
||||
worldHandle.UseShader(maskShader);
|
||||
|
||||
worldHandle.DrawTextureRect(_target.Texture, worldBounds, Color.Black.WithAlpha(alpha));
|
||||
|
||||
@@ -104,8 +104,7 @@ namespace Content.Client.Lobby.UI
|
||||
|
||||
private bool _isDirty;
|
||||
|
||||
[ValidatePrototypeId<GuideEntryPrototype>]
|
||||
private const string DefaultSpeciesGuidebook = "Species";
|
||||
private static readonly ProtoId<GuideEntryPrototype> DefaultSpeciesGuidebook = "Species";
|
||||
|
||||
public event Action<List<ProtoId<GuideEntryPrototype>>>? OnOpenGuidebook;
|
||||
|
||||
@@ -823,9 +822,9 @@ namespace Content.Client.Lobby.UI
|
||||
var species = Profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||
var page = DefaultSpeciesGuidebook;
|
||||
if (_prototypeManager.HasIndex<GuideEntryPrototype>(species))
|
||||
page = species;
|
||||
page = new ProtoId<GuideEntryPrototype>(species.Id); // Gross. See above todo comment.
|
||||
|
||||
if (_prototypeManager.TryIndex<GuideEntryPrototype>(DefaultSpeciesGuidebook, out var guideRoot))
|
||||
if (_prototypeManager.TryIndex(DefaultSpeciesGuidebook, out var guideRoot))
|
||||
{
|
||||
var dict = new Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry>();
|
||||
dict.Add(DefaultSpeciesGuidebook, guideRoot);
|
||||
@@ -1457,17 +1456,13 @@ namespace Content.Client.Lobby.UI
|
||||
{
|
||||
return;
|
||||
}
|
||||
var hairMarking = Profile.Appearance.HairStyleId switch
|
||||
{
|
||||
HairStyles.DefaultHairStyle => new List<Marking>(),
|
||||
_ => new() { new(Profile.Appearance.HairStyleId, new List<Color>() { Profile.Appearance.HairColor }) },
|
||||
};
|
||||
var hairMarking = Profile.Appearance.HairStyleId == HairStyles.DefaultHairStyle
|
||||
? new List<Marking>()
|
||||
: new() { new(Profile.Appearance.HairStyleId, new List<Color>() { Profile.Appearance.HairColor }) };
|
||||
|
||||
var facialHairMarking = Profile.Appearance.FacialHairStyleId switch
|
||||
{
|
||||
HairStyles.DefaultFacialHairStyle => new List<Marking>(),
|
||||
_ => new() { new(Profile.Appearance.FacialHairStyleId, new List<Color>() { Profile.Appearance.FacialHairColor }) },
|
||||
};
|
||||
var facialHairMarking = Profile.Appearance.FacialHairStyleId == HairStyles.DefaultFacialHairStyle
|
||||
? new List<Marking>()
|
||||
: new() { new(Profile.Appearance.FacialHairStyleId, new List<Color>() { Profile.Appearance.FacialHairColor }) };
|
||||
|
||||
HairStylePicker.UpdateData(
|
||||
hairMarking,
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Content.Client.Mapping;
|
||||
|
||||
public sealed class MappingOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
[Dependency] private readonly IEntityManager _entities = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
@@ -35,7 +37,7 @@ public sealed class MappingOverlay : Overlay
|
||||
_sprite = _entities.System<SpriteSystem>();
|
||||
|
||||
_state = state;
|
||||
_shader = _prototypes.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_shader = _prototypes.Index(UnshadedShader).Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
|
||||
@@ -57,7 +57,8 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem
|
||||
rotation += 2 * Math.PI;
|
||||
RaisePredictiveEvent(new RequestMouseRotatorRotationEvent
|
||||
{
|
||||
Rotation = rotation
|
||||
Rotation = rotation,
|
||||
User = GetNetEntity(player)
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -77,7 +78,8 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem
|
||||
|
||||
RaisePredictiveEvent(new RequestMouseRotatorRotationEvent
|
||||
{
|
||||
Rotation = angle
|
||||
Rotation = angle,
|
||||
User = GetNetEntity(player)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.Client.Movement.Systems;
|
||||
|
||||
public sealed class FloorOcclusionSystem : SharedFloorOcclusionSystem
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> HorizontalCut = "HorizontalCut";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
|
||||
private EntityQuery<SpriteComponent> _spriteQuery;
|
||||
@@ -48,7 +50,7 @@ public sealed class FloorOcclusionSystem : SharedFloorOcclusionSystem
|
||||
if (!_spriteQuery.Resolve(sprite.Owner, ref sprite.Comp, false))
|
||||
return;
|
||||
|
||||
var shader = _proto.Index<ShaderPrototype>("HorizontalCut").Instance();
|
||||
var shader = _proto.Index(HorizontalCut).Instance();
|
||||
|
||||
if (sprite.Comp.PostShader is not null && sprite.Comp.PostShader != shader)
|
||||
return;
|
||||
|
||||
@@ -23,8 +23,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
||||
[Dependency] private readonly ActionsSystem _actions = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string Action = "ActionClearNetworkLinkOverlays";
|
||||
private static readonly EntProtoId Action = "ActionClearNetworkLinkOverlays";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
||||
@@ -6,13 +6,16 @@
|
||||
Resizable="False">
|
||||
|
||||
<BoxContainer Orientation="Vertical" SeparationOverride="4" MinWidth="150">
|
||||
<changelog:ChangelogButton Access="Public" Name="ChangelogButton"/>
|
||||
<ui:VoteCallMenuButton />
|
||||
<Button Access="Public" Name="OptionsButton" Text="{Loc 'ui-escape-options'}" />
|
||||
<PanelContainer StyleClasses="LowDivider" Margin="0 2.5 0 2.5" />
|
||||
<Button Access="Public" Name="RulesButton" Text="{Loc 'ui-escape-rules'}" />
|
||||
<Button Access="Public" Name="GuidebookButton" Text="{Loc 'ui-escape-guidebook'}" />
|
||||
<Button Access="Public" Name="WikiButton" Text="{Loc 'ui-escape-wiki'}" />
|
||||
<changelog:ChangelogButton Access="Public" Name="ChangelogButton" />
|
||||
<PanelContainer StyleClasses="LowDivider" Margin="0 2.5 0 2.5" />
|
||||
<Button Access="Public" Name="OptionsButton" Text="{Loc 'ui-escape-options'}" />
|
||||
<PanelContainer StyleClasses="LowDivider" Margin="0 2.5 0 2.5" />
|
||||
<Button Access="Public" Name="DisconnectButton" Text="{Loc 'ui-escape-disconnect'}" />
|
||||
<Button Access="Public" Name="QuitButton" Text="{Loc 'ui-escape-quit'}" />
|
||||
<Button Access="Public" Name="QuitButton" Text="{Loc 'ui-escape-quit'}" StyleClasses="ButtonColorRed" />
|
||||
</BoxContainer>
|
||||
</ui1:EscapeMenu>
|
||||
|
||||
@@ -162,6 +162,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AddButton(EngineKeyFunctions.Walk);
|
||||
AddCheckBox("ui-options-hotkey-toggle-walk", _cfg.GetCVar(CCVars.ToggleWalk), HandleToggleWalk);
|
||||
InitToggleWalk();
|
||||
AddButton(ContentKeyFunctions.ToggleKnockdown);
|
||||
|
||||
AddHeader("ui-options-header-camera");
|
||||
AddButton(EngineKeyFunctions.CameraRotateLeft);
|
||||
|
||||
@@ -15,6 +15,9 @@ namespace Content.Client.Outline;
|
||||
/// </summary>
|
||||
public sealed class TargetOutlineSystem : EntitySystem
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> ShaderTargetValid = "SelectionOutlineInrange";
|
||||
private static readonly ProtoId<ShaderPrototype> ShaderTargetInvalid = "SelectionOutline";
|
||||
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
@@ -70,12 +73,6 @@ public sealed class TargetOutlineSystem : EntitySystem
|
||||
|
||||
private Vector2 LookupVector => new(LookupSize, LookupSize);
|
||||
|
||||
[ValidatePrototypeId<ShaderPrototype>]
|
||||
private const string ShaderTargetValid = "SelectionOutlineInrange";
|
||||
|
||||
[ValidatePrototypeId<ShaderPrototype>]
|
||||
private const string ShaderTargetInvalid = "SelectionOutline";
|
||||
|
||||
private ShaderInstance? _shaderTargetValid;
|
||||
private ShaderInstance? _shaderTargetInvalid;
|
||||
|
||||
@@ -85,8 +82,8 @@ public sealed class TargetOutlineSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_shaderTargetValid = _prototypeManager.Index<ShaderPrototype>(ShaderTargetValid).InstanceUnique();
|
||||
_shaderTargetInvalid = _prototypeManager.Index<ShaderPrototype>(ShaderTargetInvalid).InstanceUnique();
|
||||
_shaderTargetValid = _prototypeManager.Index(ShaderTargetValid).InstanceUnique();
|
||||
_shaderTargetInvalid = _prototypeManager.Index(ShaderTargetInvalid).InstanceUnique();
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Content.Client.Overlays;
|
||||
|
||||
public sealed partial class BlackAndWhiteOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> Shader = "GreyscaleFullscreen";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
@@ -15,7 +17,7 @@ public sealed partial class BlackAndWhiteOverlay : Overlay
|
||||
public BlackAndWhiteOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_greyscaleShader = _prototypeManager.Index<ShaderPrototype>("GreyscaleFullscreen").InstanceUnique();
|
||||
_greyscaleShader = _prototypeManager.Index(Shader).InstanceUnique();
|
||||
ZIndex = 10; // draw this over the DamageOverlay, RainbowOverlay etc.
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Content.Client.Overlays;
|
||||
|
||||
public sealed partial class NoirOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> Shader = "Noir";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
@@ -15,7 +17,7 @@ public sealed partial class NoirOverlay : Overlay
|
||||
public NoirOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_noirShader = _prototypeManager.Index<ShaderPrototype>("Noir").InstanceUnique();
|
||||
_noirShader = _prototypeManager.Index(Shader).InstanceUnique();
|
||||
ZIndex = 9; // draw this over the DamageOverlay, RainbowOverlay etc, but before the black and white shader
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@ public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponen
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
|
||||
[ValidatePrototypeId<JobIconPrototype>]
|
||||
private const string JobIconForNoId = "JobIconNoId";
|
||||
private static readonly ProtoId<JobIconPrototype> JobIconForNoId = "JobIconNoId";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -52,7 +51,7 @@ public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponen
|
||||
}
|
||||
}
|
||||
|
||||
if (_prototype.TryIndex<JobIconPrototype>(iconId, out var iconPrototype))
|
||||
if (_prototype.TryIndex(iconId, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
else
|
||||
Log.Error($"Invalid job icon prototype: {iconPrototype}");
|
||||
|
||||
@@ -45,13 +45,13 @@ public sealed partial class StencilOverlay
|
||||
}, Color.Transparent);
|
||||
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.UseShader(_protoManager.Index(StencilMask).Instance());
|
||||
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
|
||||
var curTime = _timing.RealTime;
|
||||
var sprite = _sprite.GetFrame(new SpriteSpecifier.Texture(new ResPath("/Textures/Parallaxes/noise.png")), curTime);
|
||||
|
||||
// Draw the rain
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
|
||||
worldHandle.UseShader(_protoManager.Index(StencilDraw).Instance());
|
||||
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, new Vector2(0.5f, 0f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,13 +55,13 @@ public sealed partial class StencilOverlay
|
||||
}, Color.Transparent);
|
||||
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.UseShader(_protoManager.Index(StencilMask).Instance());
|
||||
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
|
||||
var curTime = _timing.RealTime;
|
||||
var sprite = _sprite.GetFrame(weatherProto.Sprite, curTime);
|
||||
|
||||
// Draw the rain
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
|
||||
worldHandle.UseShader(_protoManager.Index(StencilDraw).Instance());
|
||||
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, Vector2.Zero, modulate: (weatherProto.Color ?? Color.White).WithAlpha(alpha));
|
||||
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
|
||||
@@ -17,6 +17,10 @@ namespace Content.Client.Overlays;
|
||||
/// </summary>
|
||||
public sealed partial class StencilOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> CircleShader = "WorldGradientCircle";
|
||||
private static readonly ProtoId<ShaderPrototype> StencilMask = "StencilMask";
|
||||
private static readonly ProtoId<ShaderPrototype> StencilDraw = "StencilDraw";
|
||||
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
@@ -43,7 +47,7 @@ public sealed partial class StencilOverlay : Overlay
|
||||
_sprite = sprite;
|
||||
_weather = weather;
|
||||
IoCManager.InjectDependencies(this);
|
||||
_shader = _protoManager.Index<ShaderPrototype>("WorldGradientCircle").InstanceUnique();
|
||||
_shader = _protoManager.Index(CircleShader).InstanceUnique();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Content.Client.Paper.UI;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class StampLabel : Label
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> PaperStamp = "PaperStamp";
|
||||
|
||||
/// A scale that's applied to the text to ensure it
|
||||
/// fits in the allowed space.
|
||||
private Vector2 _textScaling = Vector2.One;
|
||||
@@ -26,7 +28,7 @@ public sealed partial class StampLabel : Label
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
var prototypes = IoCManager.Resolve<IPrototypeManager>();
|
||||
_stampShader = prototypes.Index<ShaderPrototype>("PaperStamp").InstanceUnique();
|
||||
_stampShader = prototypes.Index(PaperStamp).InstanceUnique();
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace Content.Client.Paper.UI;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class StampWidget : PanelContainer
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> PaperStamp = "PaperStamp";
|
||||
|
||||
private StyleBoxTexture _borderTexture;
|
||||
private ShaderInstance? _stampShader;
|
||||
|
||||
@@ -42,7 +44,7 @@ public sealed partial class StampWidget : PanelContainer
|
||||
PanelOverride = _borderTexture;
|
||||
|
||||
var prototypes = IoCManager.Resolve<IPrototypeManager>();
|
||||
_stampShader = prototypes.Index<ShaderPrototype>("PaperStamp").InstanceUnique();
|
||||
_stampShader = prototypes.Index(PaperStamp).InstanceUnique();
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
|
||||
@@ -195,7 +195,6 @@ public sealed class GeneratedParallaxCache : IPostInjectInit
|
||||
public required ResPath ConfigPath;
|
||||
public required Task<Texture> LoadTask;
|
||||
public required CancellationTokenSource CancellationSource;
|
||||
public ValueList<CancellationTokenRegistration> CancelRegistrations;
|
||||
|
||||
public int RefCount;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@ public sealed class ParallaxSystem : SharedParallaxSystem
|
||||
[Dependency] private readonly IParallaxManager _parallax = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
|
||||
[ValidatePrototypeId<ParallaxPrototype>]
|
||||
private const string Fallback = "Default";
|
||||
private static readonly ProtoId<ParallaxPrototype> Fallback = "Default";
|
||||
|
||||
public const int ParallaxZIndex = 0;
|
||||
|
||||
|
||||
@@ -262,6 +262,7 @@ public sealed partial class ParticleAcceleratorControlMenu : FancyWindow
|
||||
|
||||
public sealed class PASegmentControl : Control
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> GreyscaleShaderId = "Greyscale";
|
||||
private readonly ShaderInstance _greyScaleShader;
|
||||
private readonly TextureRect _base;
|
||||
private readonly TextureRect _unlit;
|
||||
@@ -272,7 +273,7 @@ public sealed class PASegmentControl : Control
|
||||
|
||||
public PASegmentControl()
|
||||
{
|
||||
_greyScaleShader = IoCManager.Resolve<IPrototypeManager>().Index<ShaderPrototype>("Greyscale").Instance();
|
||||
_greyScaleShader = IoCManager.Resolve<IPrototypeManager>().Index(GreyscaleShaderId).Instance();
|
||||
|
||||
AddChild(_base = new TextureRect());
|
||||
AddChild(_unlit = new TextureRect());
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace Content.Client.Popups;
|
||||
/// </summary>
|
||||
public sealed class PopupOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
private readonly IConfigurationManager _configManager;
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IPlayerManager _playerMgr;
|
||||
@@ -48,7 +50,7 @@ public sealed class PopupOverlay : Overlay
|
||||
_popup = popup;
|
||||
_controller = controller;
|
||||
|
||||
_shader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_shader = protoManager.Index(UnshadedShader).Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
|
||||
@@ -9,7 +9,7 @@ public static class StaticPowerSystem
|
||||
public static bool IsPowered(this EntitySystem system, EntityUid uid, IEntityManager entManager, ApcPowerReceiverComponent? receiver = null)
|
||||
{
|
||||
if (receiver == null && !entManager.TryGetComponent(uid, out receiver))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
return receiver.Powered;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ namespace Content.Client.Power.Generation.Teg;
|
||||
/// <seealso cref="TegCirculatorComponent"/>
|
||||
public sealed class TegSystem : EntitySystem
|
||||
{
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string ArrowPrototype = "TegCirculatorArrow";
|
||||
private static readonly EntProtoId ArrowPrototype = "TegCirculatorArrow";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
||||
@@ -48,8 +48,9 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
|
||||
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))
|
||||
level = 0;
|
||||
return;
|
||||
|
||||
var positiveCharge = level > 0;
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, positiveCharge);
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace Content.Client.Radiation.Overlays
|
||||
{
|
||||
public sealed class RadiationPulseOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> RadiationShader = "Radiation";
|
||||
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
@@ -29,7 +31,7 @@ namespace Content.Client.Radiation.Overlays
|
||||
public RadiationPulseOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_baseShader = _prototypeManager.Index<ShaderPrototype>("Radiation").Instance().Duplicate();
|
||||
_baseShader = _prototypeManager.Index(RadiationShader).Instance().Duplicate();
|
||||
}
|
||||
|
||||
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace Content.Client.Shuttles;
|
||||
/// </summary>
|
||||
public sealed class FtlArrivalOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities;
|
||||
|
||||
private EntityLookupSystem _lookups;
|
||||
@@ -36,7 +38,7 @@ public sealed class FtlArrivalOverlay : Overlay
|
||||
_maps = _entManager.System<SharedMapSystem>();
|
||||
_sprites = _entManager.System<SpriteSystem>();
|
||||
|
||||
_shader = _protos.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_shader = _protos.Index(UnshadedShader).Instance();
|
||||
}
|
||||
|
||||
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
||||
|
||||
@@ -220,9 +220,9 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
||||
|
||||
var gridCentre = Vector2.Transform(gridBody.LocalCenter, curGridToView);
|
||||
|
||||
var distance = gridCentre.Length();
|
||||
var gridDistance = (gridBody.LocalCenter - xform.LocalPosition).Length();
|
||||
var labelText = Loc.GetString("shuttle-console-iff-label", ("name", labelName),
|
||||
("distance", $"{distance:0.0}"));
|
||||
("distance", $"{gridDistance:0.0}"));
|
||||
|
||||
var mapCoords = _transform.GetWorldPosition(gUid);
|
||||
var coordsText = $"({mapCoords.X:0.0}, {mapCoords.Y:0.0})";
|
||||
|
||||
@@ -25,7 +25,6 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
|
||||
|
||||
public event Action<ProtoId<BorgTypePrototype>>? ConfirmedBorgType;
|
||||
|
||||
[ValidatePrototypeId<GuideEntryPrototype>]
|
||||
private static readonly List<ProtoId<GuideEntryPrototype>> GuidebookEntries = new() { "Cyborgs", "Robotics" };
|
||||
|
||||
public BorgSelectTypeMenu()
|
||||
|
||||
@@ -84,12 +84,13 @@ public sealed partial class LawDisplay : Control
|
||||
|
||||
radioChannelButton.OnPressed += _ =>
|
||||
{
|
||||
switch (radioChannel)
|
||||
if (radioChannel == SharedChatSystem.CommonChannel)
|
||||
{
|
||||
case SharedChatSystem.CommonChannel:
|
||||
_chatManager.SendMessage($"{SharedChatSystem.RadioCommonPrefix} {lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Radio); break;
|
||||
default:
|
||||
_chatManager.SendMessage($"{SharedChatSystem.RadioChannelPrefix}{radioChannelProto.KeyCode} {lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Radio); break;
|
||||
_chatManager.SendMessage($"{SharedChatSystem.RadioCommonPrefix} {lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Radio);
|
||||
}
|
||||
else
|
||||
{
|
||||
_chatManager.SendMessage($"{SharedChatSystem.RadioChannelPrefix}{radioChannelProto.KeyCode} {lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Radio);
|
||||
}
|
||||
_nextAllowedPress[radioChannelButton] = _timing.CurTime + PressCooldown;
|
||||
};
|
||||
|
||||
@@ -12,6 +12,10 @@ namespace Content.Client.Silicons.StationAi;
|
||||
|
||||
public sealed class StationAiOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> CameraStaticShader = "CameraStatic";
|
||||
private static readonly ProtoId<ShaderPrototype> StencilMaskShader = "StencilMask";
|
||||
private static readonly ProtoId<ShaderPrototype> StencilDrawShader = "StencilDraw";
|
||||
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
@@ -91,7 +95,7 @@ public sealed class StationAiOverlay : Overlay
|
||||
() =>
|
||||
{
|
||||
worldHandle.SetTransform(invMatrix);
|
||||
var shader = _proto.Index<ShaderPrototype>("CameraStatic").Instance();
|
||||
var shader = _proto.Index(CameraStaticShader).Instance();
|
||||
worldHandle.UseShader(shader);
|
||||
worldHandle.DrawRect(worldBounds, Color.White);
|
||||
},
|
||||
@@ -114,11 +118,11 @@ public sealed class StationAiOverlay : Overlay
|
||||
}
|
||||
|
||||
// Use the lighting as a mask
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.UseShader(_proto.Index(StencilMaskShader).Instance());
|
||||
worldHandle.DrawTextureRect(_stencilTexture!.Texture, worldBounds);
|
||||
|
||||
// Draw the static
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilDraw").Instance());
|
||||
worldHandle.UseShader(_proto.Index(StencilDrawShader).Instance());
|
||||
worldHandle.DrawTextureRect(_staticTexture!.Texture, worldBounds);
|
||||
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Content.Client.Singularity
|
||||
{
|
||||
public sealed class SingularityOverlay : Overlay, IEntityEventSubscriber
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> Shader = "Singularity";
|
||||
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
private SharedTransformSystem? _xformSystem = null;
|
||||
@@ -29,7 +31,7 @@ namespace Content.Client.Singularity
|
||||
public SingularityOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_shader = _prototypeManager.Index<ShaderPrototype>("Singularity").Instance().Duplicate();
|
||||
_shader = _prototypeManager.Index(Shader).Instance().Duplicate();
|
||||
_shader.SetParameter("maxDistance", MaxDistance * EyeManager.PixelsPerMeter);
|
||||
_entMan.EventBus.SubscribeEvent<PixelToMapEvent>(EventSource.Local, this, OnProjectFromScreenToMap);
|
||||
ZIndex = 101; // Should be drawn after the placement overlay so admins placing items near the singularity can tell where they're going.
|
||||
|
||||
@@ -1,56 +1,129 @@
|
||||
using Content.Shared.SprayPainter;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Graphics;
|
||||
using Content.Client.Items;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.SprayPainter;
|
||||
using Content.Shared.SprayPainter.Components;
|
||||
using Content.Shared.SprayPainter.Prototypes;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.SprayPainter;
|
||||
|
||||
/// <summary>
|
||||
/// Client-side spray painter functions. Caches information for spray painter windows and updates the UI to reflect component state.
|
||||
/// </summary>
|
||||
public sealed class SprayPainterSystem : SharedSprayPainterSystem
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
|
||||
public List<SprayPainterEntry> Entries { get; private set; } = new();
|
||||
public List<SprayPainterDecalEntry> Decals = [];
|
||||
public Dictionary<string, List<string>> PaintableGroupsByCategory = new();
|
||||
public Dictionary<string, Dictionary<string, EntProtoId>> PaintableStylesByGroup = new();
|
||||
|
||||
protected override void CacheStyles()
|
||||
public override void Initialize()
|
||||
{
|
||||
base.CacheStyles();
|
||||
base.Initialize();
|
||||
|
||||
Entries.Clear();
|
||||
foreach (var style in Styles)
|
||||
Subs.ItemStatus<SprayPainterComponent>(ent => new StatusControl(ent));
|
||||
SubscribeLocalEvent<SprayPainterComponent, AfterAutoHandleStateEvent>(OnStateUpdate);
|
||||
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
|
||||
|
||||
CachePrototypes();
|
||||
}
|
||||
|
||||
private void OnStateUpdate(Entity<SprayPainterComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateUi(ent);
|
||||
}
|
||||
|
||||
protected override void UpdateUi(Entity<SprayPainterComponent> ent)
|
||||
{
|
||||
if (_ui.TryGetOpenUi(ent.Owner, SprayPainterUiKey.Key, out var bui))
|
||||
bui.Update();
|
||||
}
|
||||
|
||||
private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
|
||||
{
|
||||
if (!args.WasModified<PaintableGroupCategoryPrototype>() || !args.WasModified<PaintableGroupPrototype>() || !args.WasModified<DecalPrototype>())
|
||||
return;
|
||||
|
||||
CachePrototypes();
|
||||
}
|
||||
|
||||
private void CachePrototypes()
|
||||
{
|
||||
PaintableGroupsByCategory.Clear();
|
||||
PaintableStylesByGroup.Clear();
|
||||
foreach (var category in Proto.EnumeratePrototypes<PaintableGroupCategoryPrototype>().OrderBy(x => x.ID))
|
||||
{
|
||||
var name = style.Name;
|
||||
string? iconPath = Groups
|
||||
.FindAll(x => x.StylePaths.ContainsKey(name))?
|
||||
.MaxBy(x => x.IconPriority)?.StylePaths[name];
|
||||
if (iconPath == null)
|
||||
var groupList = new List<string>();
|
||||
foreach (var groupId in category.Groups)
|
||||
{
|
||||
Entries.Add(new SprayPainterEntry(name, null));
|
||||
continue;
|
||||
if (!Proto.TryIndex(groupId, out var group))
|
||||
continue;
|
||||
|
||||
groupList.Add(groupId);
|
||||
PaintableStylesByGroup[groupId] = group.Styles;
|
||||
}
|
||||
|
||||
RSIResource doorRsi = _resourceCache.GetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / new ResPath(iconPath));
|
||||
if (!doorRsi.RSI.TryGetState("closed", out var icon))
|
||||
{
|
||||
Entries.Add(new SprayPainterEntry(name, null));
|
||||
continue;
|
||||
}
|
||||
if (groupList.Count > 0)
|
||||
PaintableGroupsByCategory[category.ID] = groupList;
|
||||
}
|
||||
|
||||
Entries.Add(new SprayPainterEntry(name, icon.Frame0));
|
||||
Decals.Clear();
|
||||
foreach (var decalPrototype in Proto.EnumeratePrototypes<DecalPrototype>().OrderBy(x => x.ID))
|
||||
{
|
||||
if (!decalPrototype.Tags.Contains("station")
|
||||
&& !decalPrototype.Tags.Contains("markings")
|
||||
|| decalPrototype.Tags.Contains("dirty"))
|
||||
continue;
|
||||
|
||||
Decals.Add(new SprayPainterDecalEntry(decalPrototype.ID, decalPrototype.Sprite));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly RichTextLabel _label;
|
||||
private readonly Entity<SprayPainterComponent> _entity;
|
||||
private DecalPaintMode? _lastPaintingDecals = null;
|
||||
|
||||
public StatusControl(Entity<SprayPainterComponent> ent)
|
||||
{
|
||||
_entity = ent;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
AddChild(_label);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_entity.Comp.DecalMode == _lastPaintingDecals)
|
||||
return;
|
||||
|
||||
_lastPaintingDecals = _entity.Comp.DecalMode;
|
||||
|
||||
string modeLocString = _entity.Comp.DecalMode switch
|
||||
{
|
||||
DecalPaintMode.Add => "spray-painter-item-status-add",
|
||||
DecalPaintMode.Remove => "spray-painter-item-status-remove",
|
||||
_ => "spray-painter-item-status-off"
|
||||
};
|
||||
|
||||
_label.SetMarkupPermissive(Robust.Shared.Localization.Loc.GetString("spray-painter-item-status-label",
|
||||
("mode", Robust.Shared.Localization.Loc.GetString(modeLocString))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SprayPainterEntry
|
||||
{
|
||||
public string Name;
|
||||
public Texture? Icon;
|
||||
|
||||
public SprayPainterEntry(string name, Texture? icon)
|
||||
{
|
||||
Name = name;
|
||||
Icon = icon;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// A spray paintable decal, mapped by ID.
|
||||
/// </summary>
|
||||
public sealed record SprayPainterDecalEntry(string Name, SpriteSpecifier Sprite);
|
||||
|
||||
@@ -1,42 +1,96 @@
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.SprayPainter;
|
||||
using Content.Shared.SprayPainter.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.SprayPainter.UI;
|
||||
|
||||
public sealed class SprayPainterBoundUserInterface : BoundUserInterface
|
||||
/// <summary>
|
||||
/// A BUI for a spray painter. Allows selecting pipe colours, decals, and paintable object types sorted by category.
|
||||
/// </summary>
|
||||
public sealed class SprayPainterBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
|
||||
{
|
||||
[ViewVariables]
|
||||
private SprayPainterWindow? _window;
|
||||
|
||||
public SprayPainterBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<SprayPainterWindow>();
|
||||
|
||||
_window.OnSpritePicked = OnSpritePicked;
|
||||
_window.OnColorPicked = OnColorPicked;
|
||||
|
||||
if (EntMan.TryGetComponent(Owner, out SprayPainterComponent? comp))
|
||||
if (_window == null)
|
||||
{
|
||||
_window.Populate(EntMan.System<SprayPainterSystem>().Entries, comp.Index, comp.PickedColor, comp.ColorPalette);
|
||||
_window = this.CreateWindow<SprayPainterWindow>();
|
||||
|
||||
_window.OnSpritePicked += OnSpritePicked;
|
||||
_window.OnSetPipeColor += OnSetPipeColor;
|
||||
_window.OnTabChanged += OnTabChanged;
|
||||
_window.OnDecalChanged += OnDecalChanged;
|
||||
_window.OnDecalColorChanged += OnDecalColorChanged;
|
||||
_window.OnDecalAngleChanged += OnDecalAngleChanged;
|
||||
_window.OnDecalSnapChanged += OnDecalSnapChanged;
|
||||
}
|
||||
|
||||
var sprayPainter = EntMan.System<SprayPainterSystem>();
|
||||
_window.PopulateCategories(sprayPainter.PaintableStylesByGroup, sprayPainter.PaintableGroupsByCategory, sprayPainter.Decals);
|
||||
Update();
|
||||
|
||||
if (EntMan.TryGetComponent(Owner, out SprayPainterComponent? sprayPainterComp))
|
||||
_window.SetSelectedTab(sprayPainterComp.SelectedTab);
|
||||
}
|
||||
|
||||
private void OnSpritePicked(ItemList.ItemListSelectedEventArgs args)
|
||||
public override void Update()
|
||||
{
|
||||
SendMessage(new SprayPainterSpritePickedMessage(args.ItemIndex));
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
if (!EntMan.TryGetComponent(Owner, out SprayPainterComponent? sprayPainter))
|
||||
return;
|
||||
|
||||
_window.PopulateColors(sprayPainter.ColorPalette);
|
||||
if (sprayPainter.PickedColor != null)
|
||||
_window.SelectColor(sprayPainter.PickedColor);
|
||||
_window.SetSelectedStyles(sprayPainter.StylesByGroup);
|
||||
_window.SetSelectedDecal(sprayPainter.SelectedDecal);
|
||||
_window.SetDecalAngle(sprayPainter.SelectedDecalAngle);
|
||||
_window.SetDecalColor(sprayPainter.SelectedDecalColor);
|
||||
_window.SetDecalSnap(sprayPainter.SnapDecals);
|
||||
}
|
||||
|
||||
private void OnColorPicked(ItemList.ItemListSelectedEventArgs args)
|
||||
private void OnDecalSnapChanged(bool snap)
|
||||
{
|
||||
SendPredictedMessage(new SprayPainterSetDecalSnapMessage(snap));
|
||||
}
|
||||
|
||||
private void OnDecalAngleChanged(int angle)
|
||||
{
|
||||
SendPredictedMessage(new SprayPainterSetDecalAngleMessage(angle));
|
||||
}
|
||||
|
||||
private void OnDecalColorChanged(Color? color)
|
||||
{
|
||||
SendPredictedMessage(new SprayPainterSetDecalColorMessage(color));
|
||||
}
|
||||
|
||||
private void OnDecalChanged(ProtoId<DecalPrototype> protoId)
|
||||
{
|
||||
SendPredictedMessage(new SprayPainterSetDecalMessage(protoId));
|
||||
}
|
||||
|
||||
private void OnTabChanged(int index, bool isSelectedTabWithDecals)
|
||||
{
|
||||
SendPredictedMessage(new SprayPainterTabChangedMessage(index, isSelectedTabWithDecals));
|
||||
}
|
||||
|
||||
private void OnSpritePicked(string group, string style)
|
||||
{
|
||||
SendPredictedMessage(new SprayPainterSetPaintableStyleMessage(group, style));
|
||||
}
|
||||
|
||||
private void OnSetPipeColor(ItemList.ItemListSelectedEventArgs args)
|
||||
{
|
||||
var key = _window?.IndexToColorKey(args.ItemIndex);
|
||||
SendMessage(new SprayPainterColorPickedMessage(key));
|
||||
SendPredictedMessage(new SprayPainterSetPipeColorMessage(key));
|
||||
}
|
||||
}
|
||||
|
||||
26
Content.Client/SprayPainter/UI/SprayPainterDecals.xaml
Normal file
26
Content.Client/SprayPainter/UI/SprayPainterDecals.xaml
Normal file
@@ -0,0 +1,26 @@
|
||||
<controls:SprayPainterDecals
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.SprayPainter.UI">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Text="{Loc 'spray-painter-selected-decals'}" />
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<GridContainer Columns="7" Name="DecalsGrid">
|
||||
<!-- populated by code -->
|
||||
</GridContainer>
|
||||
</ScrollContainer>
|
||||
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<ColorSelectorSliders Name="ColorSelector" IsAlphaVisible="True" />
|
||||
<CheckBox Name="UseCustomColorCheckBox" Text="{Loc 'spray-painter-use-custom-color'}" />
|
||||
<CheckBox Name="SnapToTileCheckBox" Text="{Loc 'spray-painter-use-snap-to-tile'}" />
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'spray-painter-angle-rotation'}" />
|
||||
<SpinBox Name="AngleSpinBox" HorizontalExpand="True" />
|
||||
<Button Text="{Loc 'spray-painter-angle-rotation-90-sub'}" Name="SubAngleButton" />
|
||||
<Button Text="{Loc 'spray-painter-angle-rotation-reset'}" Name="SetZeroAngleButton" />
|
||||
<Button Text="{Loc 'spray-painter-angle-rotation-90-add'}" Name="AddAngleButton" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:SprayPainterDecals>
|
||||
174
Content.Client/SprayPainter/UI/SprayPainterDecals.xaml.cs
Normal file
174
Content.Client/SprayPainter/UI/SprayPainterDecals.xaml.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Decals;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.SprayPainter.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Used to control decal painting parameters for the spray painter.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class SprayPainterDecals : Control
|
||||
{
|
||||
public Action<ProtoId<DecalPrototype>>? OnDecalSelected;
|
||||
public Action<Color?>? OnColorChanged;
|
||||
public Action<int>? OnAngleChanged;
|
||||
public Action<bool>? OnSnapChanged;
|
||||
|
||||
private SpriteSystem? _sprite;
|
||||
private string _selectedDecal = string.Empty;
|
||||
private List<SprayPainterDecalEntry> _decals = [];
|
||||
|
||||
public SprayPainterDecals()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
AddAngleButton.OnButtonUp += _ => AngleSpinBox.Value += 90;
|
||||
SubAngleButton.OnButtonUp += _ => AngleSpinBox.Value -= 90;
|
||||
SetZeroAngleButton.OnButtonUp += _ => AngleSpinBox.Value = 0;
|
||||
AngleSpinBox.ValueChanged += args => OnAngleChanged?.Invoke(args.Value);
|
||||
|
||||
UseCustomColorCheckBox.OnPressed += UseCustomColorCheckBoxOnOnPressed;
|
||||
SnapToTileCheckBox.OnPressed += SnapToTileCheckBoxOnOnPressed;
|
||||
ColorSelector.OnColorChanged += OnColorSelected;
|
||||
}
|
||||
|
||||
private void UseCustomColorCheckBoxOnOnPressed(BaseButton.ButtonEventArgs _)
|
||||
{
|
||||
OnColorChanged?.Invoke(UseCustomColorCheckBox.Pressed ? ColorSelector.Color : null);
|
||||
UpdateColorButtons(UseCustomColorCheckBox.Pressed);
|
||||
}
|
||||
|
||||
private void SnapToTileCheckBoxOnOnPressed(BaseButton.ButtonEventArgs _)
|
||||
{
|
||||
OnSnapChanged?.Invoke(SnapToTileCheckBox.Pressed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the decal list.
|
||||
/// </summary>
|
||||
public void PopulateDecals(List<SprayPainterDecalEntry> decals, SpriteSystem sprite)
|
||||
{
|
||||
_sprite ??= sprite;
|
||||
|
||||
_decals = decals;
|
||||
DecalsGrid.Children.Clear();
|
||||
|
||||
foreach (var decal in decals)
|
||||
{
|
||||
var button = new TextureButton()
|
||||
{
|
||||
TextureNormal = sprite.Frame0(decal.Sprite),
|
||||
Name = decal.Name,
|
||||
ToolTip = decal.Name,
|
||||
Scale = new Vector2(2, 2),
|
||||
};
|
||||
button.OnPressed += DecalButtonOnPressed;
|
||||
|
||||
if (UseCustomColorCheckBox.Pressed)
|
||||
{
|
||||
button.Modulate = ColorSelector.Color;
|
||||
}
|
||||
|
||||
if (_selectedDecal == decal.Name)
|
||||
{
|
||||
var panelContainer = new PanelContainer()
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = StyleNano.ButtonColorDefault,
|
||||
},
|
||||
Children =
|
||||
{
|
||||
button,
|
||||
},
|
||||
};
|
||||
DecalsGrid.AddChild(panelContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecalsGrid.AddChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnColorSelected(Color color)
|
||||
{
|
||||
if (!UseCustomColorCheckBox.Pressed)
|
||||
return;
|
||||
|
||||
OnColorChanged?.Invoke(color);
|
||||
|
||||
UpdateColorButtons(UseCustomColorCheckBox.Pressed);
|
||||
}
|
||||
|
||||
private void UpdateColorButtons(bool apply)
|
||||
{
|
||||
Color modulateColor = apply ? ColorSelector.Color : Color.White;
|
||||
foreach (var button in DecalsGrid.Children)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case TextureButton:
|
||||
button.Modulate = modulateColor;
|
||||
break;
|
||||
case PanelContainer panelContainer:
|
||||
{
|
||||
foreach (TextureButton textureButton in panelContainer.Children)
|
||||
textureButton.Modulate = modulateColor;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DecalButtonOnPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
if (obj.Button.Name is not { } name)
|
||||
return;
|
||||
|
||||
_selectedDecal = name;
|
||||
OnDecalSelected?.Invoke(_selectedDecal);
|
||||
|
||||
if (_sprite is null)
|
||||
return;
|
||||
|
||||
PopulateDecals(_decals, _sprite);
|
||||
}
|
||||
|
||||
public void SetSelectedDecal(string name)
|
||||
{
|
||||
_selectedDecal = name;
|
||||
|
||||
if (_sprite is null)
|
||||
return;
|
||||
|
||||
PopulateDecals(_decals, _sprite);
|
||||
}
|
||||
|
||||
public void SetAngle(int degrees)
|
||||
{
|
||||
AngleSpinBox.OverrideValue(degrees);
|
||||
}
|
||||
|
||||
public void SetColor(Color? color)
|
||||
{
|
||||
UseCustomColorCheckBox.Pressed = color != null;
|
||||
if (color != null)
|
||||
ColorSelector.Color = color.Value;
|
||||
UpdateColorButtons(UseCustomColorCheckBox.Pressed);
|
||||
}
|
||||
|
||||
public void SetSnap(bool snap)
|
||||
{
|
||||
SnapToTileCheckBox.Pressed = snap;
|
||||
}
|
||||
}
|
||||
12
Content.Client/SprayPainter/UI/SprayPainterGroup.xaml
Normal file
12
Content.Client/SprayPainter/UI/SprayPainterGroup.xaml
Normal file
@@ -0,0 +1,12 @@
|
||||
<BoxContainer
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Orientation="Vertical">
|
||||
<Label Text="{Loc 'spray-painter-selected-style'}" />
|
||||
<controls:ListContainer
|
||||
Name="StyleList"
|
||||
Toggle="True"
|
||||
Group="True">
|
||||
<!-- populated by code -->
|
||||
</controls:ListContainer>
|
||||
</BoxContainer>
|
||||
66
Content.Client/SprayPainter/UI/SprayPainterGroup.xaml.cs
Normal file
66
Content.Client/SprayPainter/UI/SprayPainterGroup.xaml.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.SprayPainter.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Used to display a group of paintable styles in the spray painter menu.
|
||||
/// (e.g. each type of paintable locker or plastic crate)
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class SprayPainterGroup : BoxContainer
|
||||
{
|
||||
public event Action<SpriteListData>? OnButtonPressed;
|
||||
|
||||
public SprayPainterGroup()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
StyleList.GenerateItem = GenerateItems;
|
||||
}
|
||||
|
||||
public void PopulateList(List<SpriteListData> spriteList)
|
||||
{
|
||||
StyleList.PopulateList(spriteList);
|
||||
}
|
||||
|
||||
public void SelectItemByStyle(string key)
|
||||
{
|
||||
foreach (var elem in StyleList.Data)
|
||||
{
|
||||
if (elem is not SpriteListData spriteElem)
|
||||
continue;
|
||||
|
||||
if (spriteElem.Style == key)
|
||||
{
|
||||
StyleList.Select(spriteElem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateItems(ListData data, ListContainerButton button)
|
||||
{
|
||||
if (data is not SpriteListData spriteListData)
|
||||
return;
|
||||
|
||||
var box = new BoxContainer() { Orientation = LayoutOrientation.Horizontal };
|
||||
var protoView = new EntityPrototypeView();
|
||||
protoView.SetPrototype(spriteListData.Prototype);
|
||||
var label = new Label()
|
||||
{
|
||||
Text = Loc.GetString($"spray-painter-style-{spriteListData.Group.ToLower()}-{spriteListData.Style.ToLower()}")
|
||||
};
|
||||
|
||||
box.AddChild(protoView);
|
||||
box.AddChild(label);
|
||||
button.AddChild(box);
|
||||
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
|
||||
button.OnPressed += _ => OnButtonPressed?.Invoke(spriteListData);
|
||||
|
||||
if (spriteListData.SelectedIndex == button.Index)
|
||||
button.Pressed = true;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,6 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
MinSize="500 300"
|
||||
SetSize="500 500"
|
||||
Title="{Loc 'spray-painter-window-title'}">
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True"
|
||||
SeparationOverride="4"
|
||||
MinWidth="450">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True"
|
||||
SeparationOverride="4"
|
||||
MinWidth="200">
|
||||
<Label Name="SelectedSpriteLabel"
|
||||
Text="{Loc 'spray-painter-selected-style'}">
|
||||
</Label>
|
||||
<ItemList Name="SpriteList"
|
||||
SizeFlagsStretchRatio="8"
|
||||
VerticalExpand="True"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True"
|
||||
SeparationOverride="4"
|
||||
MinWidth="200">
|
||||
<Label Name="SelectedColorLabel"
|
||||
Text="{Loc 'spray-painter-selected-color'}"/>
|
||||
<ItemList Name="ColorList"
|
||||
SizeFlagsStretchRatio="8"
|
||||
VerticalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
MinSize="520 300"
|
||||
SetSize="520 700"
|
||||
Title="{Loc 'spray-painter-window-title'}">
|
||||
<TabContainer Name="Tabs"/>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
using System.Linq;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Decals;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.SprayPainter.UI;
|
||||
|
||||
/// <summary>
|
||||
/// A window to select spray painter settings by object type, as well as pipe colours and decals.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class SprayPainterWindow : DefaultWindow
|
||||
{
|
||||
@@ -15,13 +22,33 @@ public sealed partial class SprayPainterWindow : DefaultWindow
|
||||
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
|
||||
public Action<ItemList.ItemListSelectedEventArgs>? OnSpritePicked;
|
||||
public Action<ItemList.ItemListSelectedEventArgs>? OnColorPicked;
|
||||
// Events
|
||||
public event Action<string, string>? OnSpritePicked;
|
||||
public event Action<int, bool>? OnTabChanged;
|
||||
public event Action<ProtoId<DecalPrototype>>? OnDecalChanged;
|
||||
public event Action<ItemList.ItemListSelectedEventArgs>? OnSetPipeColor;
|
||||
public event Action<Color?>? OnDecalColorChanged;
|
||||
public event Action<int>? OnDecalAngleChanged;
|
||||
public event Action<bool>? OnDecalSnapChanged;
|
||||
|
||||
// Pipe color data
|
||||
private ItemList _colorList = default!;
|
||||
public Dictionary<string, int> ItemColorIndex = new();
|
||||
|
||||
private Dictionary<string, Color> currentPalette = new();
|
||||
private const string colorLocKeyPrefix = "pipe-painter-color-";
|
||||
private List<SprayPainterEntry> CurrentEntries = new List<SprayPainterEntry>();
|
||||
private Dictionary<string, Color> _currentPalette = new();
|
||||
private const string ColorLocKeyPrefix = "pipe-painter-color-";
|
||||
|
||||
// Paintable objects
|
||||
private Dictionary<string, Dictionary<string, EntProtoId>> _currentStylesByGroup = new();
|
||||
private Dictionary<string, List<string>> _currentGroupsByCategory = new();
|
||||
|
||||
// Tab controls
|
||||
private Dictionary<string, SprayPainterGroup> _paintableControls = new();
|
||||
private BoxContainer? _pipeControl;
|
||||
|
||||
// Decals
|
||||
private List<SprayPainterDecalEntry> _currentDecals = [];
|
||||
private SprayPainterDecals? _sprayPainterDecals;
|
||||
|
||||
private readonly SpriteSpecifier _colorEntryIconTexture = new SpriteSpecifier.Rsi(
|
||||
new ResPath("Structures/Piping/Atmospherics/pipe.rsi"),
|
||||
@@ -32,13 +59,14 @@ public sealed partial class SprayPainterWindow : DefaultWindow
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_spriteSystem = _sysMan.GetEntitySystem<SpriteSystem>();
|
||||
Tabs.OnTabChanged += (index) => OnTabChanged?.Invoke(index, _sprayPainterDecals?.GetPositionInParent() == index);
|
||||
}
|
||||
|
||||
private string GetColorLocString(string? colorKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(colorKey))
|
||||
return Loc.GetString("pipe-painter-no-color-selected");
|
||||
var locKey = colorLocKeyPrefix + colorKey;
|
||||
var locKey = ColorLocKeyPrefix + colorKey;
|
||||
|
||||
if (!_loc.TryGetString(locKey, out var locString))
|
||||
locString = colorKey;
|
||||
@@ -48,51 +76,229 @@ public sealed partial class SprayPainterWindow : DefaultWindow
|
||||
|
||||
public string? IndexToColorKey(int index)
|
||||
{
|
||||
return (string?) ColorList[index].Metadata;
|
||||
return _colorList[index].Text;
|
||||
}
|
||||
|
||||
public void Populate(List<SprayPainterEntry> entries, int selectedStyle, string? selectedColorKey, Dictionary<string, Color> palette)
|
||||
private void OnStyleSelected(ListData data)
|
||||
{
|
||||
// Only clear if the entries change. Otherwise the list would "jump" after selecting an item
|
||||
if (!CurrentEntries.Equals(entries))
|
||||
if (data is SpriteListData listData)
|
||||
OnSpritePicked?.Invoke(listData.Group, listData.Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper to allow for selecting/deselecting the event to avoid loops
|
||||
/// </summary>
|
||||
private void OnColorPicked(ItemList.ItemListSelectedEventArgs args)
|
||||
{
|
||||
OnSetPipeColor?.Invoke(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup function for the window.
|
||||
/// </summary>
|
||||
/// <param name="stylesByGroup">Each group, mapped by name to the set of named styles by their associated entity prototype.</param>
|
||||
/// <param name="groupsByCategory">The set of categories and the groups associated with them.</param>
|
||||
/// <param name="decals">A list of each decal.</param>
|
||||
public void PopulateCategories(Dictionary<string, Dictionary<string, EntProtoId>> stylesByGroup, Dictionary<string, List<string>> groupsByCategory, List<SprayPainterDecalEntry> decals)
|
||||
{
|
||||
bool tabsCleared = false;
|
||||
var lastTab = Tabs.CurrentTab;
|
||||
|
||||
if (!_currentGroupsByCategory.Equals(groupsByCategory))
|
||||
{
|
||||
CurrentEntries = entries;
|
||||
SpriteList.Clear();
|
||||
foreach (var entry in entries)
|
||||
// Destroy all existing tabs
|
||||
tabsCleared = true;
|
||||
_paintableControls.Clear();
|
||||
_pipeControl = null;
|
||||
_sprayPainterDecals = null;
|
||||
Tabs.RemoveAllChildren();
|
||||
}
|
||||
|
||||
// Only clear if the entries change. Otherwise the list would "jump" after selecting an item
|
||||
if (tabsCleared || !_currentStylesByGroup.Equals(stylesByGroup))
|
||||
{
|
||||
_currentStylesByGroup = stylesByGroup;
|
||||
|
||||
var tabIndex = 0;
|
||||
foreach (var (categoryName, categoryGroups) in groupsByCategory.OrderBy(c => c.Key))
|
||||
{
|
||||
SpriteList.AddItem(entry.Name, entry.Icon);
|
||||
if (categoryGroups.Count <= 0)
|
||||
continue;
|
||||
|
||||
// Repopulating controls:
|
||||
// ensure that categories with multiple groups have separate subtabs
|
||||
// but single-group categories do not.
|
||||
if (tabsCleared)
|
||||
{
|
||||
TabContainer? subTabs = null;
|
||||
if (categoryGroups.Count > 1)
|
||||
subTabs = new();
|
||||
|
||||
foreach (var group in categoryGroups)
|
||||
{
|
||||
if (!stylesByGroup.TryGetValue(group, out var styles))
|
||||
continue;
|
||||
|
||||
var groupControl = new SprayPainterGroup();
|
||||
groupControl.OnButtonPressed += OnStyleSelected;
|
||||
_paintableControls[group] = groupControl;
|
||||
if (categoryGroups.Count > 1)
|
||||
{
|
||||
if (subTabs != null)
|
||||
{
|
||||
subTabs?.AddChild(groupControl);
|
||||
var subTabLocalization = Loc.GetString("spray-painter-tab-group-" + group.ToLower());
|
||||
TabContainer.SetTabTitle(groupControl, subTabLocalization);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Tabs.AddChild(groupControl);
|
||||
}
|
||||
}
|
||||
|
||||
if (subTabs != null)
|
||||
Tabs.AddChild(subTabs);
|
||||
|
||||
var tabLocalization = Loc.GetString("spray-painter-tab-category-" + categoryName.ToLower());
|
||||
Tabs.SetTabTitle(tabIndex, tabLocalization);
|
||||
tabIndex++;
|
||||
}
|
||||
|
||||
// Finally, populate all groups with new data.
|
||||
foreach (var group in categoryGroups)
|
||||
{
|
||||
if (!stylesByGroup.TryGetValue(group, out var styles) ||
|
||||
!_paintableControls.TryGetValue(group, out var control))
|
||||
continue;
|
||||
|
||||
var dataList = styles
|
||||
.Select(e => new SpriteListData(group, e.Key, e.Value, 0))
|
||||
.OrderBy(d => Loc.GetString($"spray-painter-style-{group.ToLower()}-{d.Style.ToLower()}"))
|
||||
.ToList();
|
||||
control.PopulateList(dataList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentPalette.Equals(palette))
|
||||
{
|
||||
currentPalette = palette;
|
||||
ItemColorIndex.Clear();
|
||||
ColorList.Clear();
|
||||
PopulateColors(_currentPalette);
|
||||
|
||||
if (!_currentDecals.Equals(decals))
|
||||
{
|
||||
_currentDecals = decals;
|
||||
|
||||
if (_sprayPainterDecals is null)
|
||||
{
|
||||
_sprayPainterDecals = new SprayPainterDecals();
|
||||
|
||||
_sprayPainterDecals.OnDecalSelected += id => OnDecalChanged?.Invoke(id);
|
||||
_sprayPainterDecals.OnColorChanged += color => OnDecalColorChanged?.Invoke(color);
|
||||
_sprayPainterDecals.OnAngleChanged += angle => OnDecalAngleChanged?.Invoke(angle);
|
||||
_sprayPainterDecals.OnSnapChanged += snap => OnDecalSnapChanged?.Invoke(snap);
|
||||
|
||||
Tabs.AddChild(_sprayPainterDecals);
|
||||
TabContainer.SetTabTitle(_sprayPainterDecals, Loc.GetString("spray-painter-tab-category-decals"));
|
||||
}
|
||||
|
||||
_sprayPainterDecals.PopulateDecals(decals, _spriteSystem);
|
||||
}
|
||||
|
||||
if (tabsCleared)
|
||||
SetSelectedTab(lastTab);
|
||||
}
|
||||
|
||||
public void PopulateColors(Dictionary<string, Color> palette)
|
||||
{
|
||||
// Create pipe tab controls if they don't exist
|
||||
bool tabCreated = false;
|
||||
if (_pipeControl == null)
|
||||
{
|
||||
_pipeControl = new BoxContainer() { Orientation = BoxContainer.LayoutOrientation.Vertical };
|
||||
|
||||
var label = new Label() { Text = Loc.GetString("spray-painter-selected-color") };
|
||||
|
||||
_colorList = new ItemList() { VerticalExpand = true };
|
||||
_colorList.OnItemSelected += OnColorPicked;
|
||||
|
||||
_pipeControl.AddChild(label);
|
||||
_pipeControl.AddChild(_colorList);
|
||||
|
||||
Tabs.AddChild(_pipeControl);
|
||||
TabContainer.SetTabTitle(_pipeControl, Loc.GetString("spray-painter-tab-category-pipes"));
|
||||
tabCreated = true;
|
||||
}
|
||||
|
||||
// Populate the tab if needed (new tab/new data)
|
||||
if (tabCreated || !_currentPalette.Equals(palette))
|
||||
{
|
||||
_currentPalette = palette;
|
||||
ItemColorIndex.Clear();
|
||||
_colorList.Clear();
|
||||
|
||||
int index = 0;
|
||||
foreach (var color in palette)
|
||||
{
|
||||
var locString = GetColorLocString(color.Key);
|
||||
var item = ColorList.AddItem(locString, _spriteSystem.Frame0(_colorEntryIconTexture));
|
||||
var item = _colorList.AddItem(locString, _spriteSystem.Frame0(_colorEntryIconTexture), metadata: color.Key);
|
||||
item.IconModulate = color.Value;
|
||||
item.Metadata = color.Key;
|
||||
|
||||
ItemColorIndex.Add(color.Key, ColorList.IndexOf(item));
|
||||
ItemColorIndex.Add(color.Key, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable event so we don't send a new event for pre-selectedStyle entry and end up in a loop
|
||||
|
||||
if (selectedColorKey != null)
|
||||
{
|
||||
var index = ItemColorIndex[selectedColorKey];
|
||||
ColorList.OnItemSelected -= OnColorPicked;
|
||||
ColorList[index].Selected = true;
|
||||
ColorList.OnItemSelected += OnColorPicked;
|
||||
}
|
||||
|
||||
SpriteList.OnItemSelected -= OnSpritePicked;
|
||||
SpriteList[selectedStyle].Selected = true;
|
||||
SpriteList.OnItemSelected += OnSpritePicked;
|
||||
}
|
||||
|
||||
# region Setters
|
||||
public void SetSelectedStyles(Dictionary<string, string> selectedStyles)
|
||||
{
|
||||
foreach (var (group, style) in selectedStyles)
|
||||
{
|
||||
if (!_paintableControls.TryGetValue(group, out var control))
|
||||
continue;
|
||||
|
||||
control.SelectItemByStyle(style);
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectColor(string color)
|
||||
{
|
||||
if (_colorList != null && ItemColorIndex.TryGetValue(color, out var colorIdx))
|
||||
{
|
||||
_colorList.OnItemSelected -= OnColorPicked;
|
||||
_colorList[colorIdx].Selected = true;
|
||||
_colorList.OnItemSelected += OnColorPicked;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSelectedTab(int tab)
|
||||
{
|
||||
Tabs.CurrentTab = int.Min(tab, Tabs.ChildCount - 1);
|
||||
}
|
||||
|
||||
public void SetSelectedDecal(string decal)
|
||||
{
|
||||
if (_sprayPainterDecals != null)
|
||||
_sprayPainterDecals.SetSelectedDecal(decal);
|
||||
}
|
||||
|
||||
public void SetDecalAngle(int angle)
|
||||
{
|
||||
if (_sprayPainterDecals != null)
|
||||
_sprayPainterDecals.SetAngle(angle);
|
||||
}
|
||||
|
||||
public void SetDecalColor(Color? color)
|
||||
{
|
||||
if (_sprayPainterDecals != null)
|
||||
_sprayPainterDecals.SetColor(color);
|
||||
}
|
||||
|
||||
public void SetDecalSnap(bool snap)
|
||||
{
|
||||
if (_sprayPainterDecals != null)
|
||||
_sprayPainterDecals.SetSnap(snap);
|
||||
}
|
||||
# endregion
|
||||
}
|
||||
|
||||
public record SpriteListData(string Group, string Style, EntProtoId Prototype, int SelectedIndex) : ListData;
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Content.Client.StatusIcon;
|
||||
|
||||
public sealed class StatusIconOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
[Dependency] private readonly IEntityManager _entity = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
@@ -29,7 +31,7 @@ public sealed class StatusIconOverlay : Overlay
|
||||
_sprite = _entity.System<SpriteSystem>();
|
||||
_transform = _entity.System<TransformSystem>();
|
||||
_statusIcon = _entity.System<StatusIconSystem>();
|
||||
_unshadedShader = _prototype.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_unshadedShader = _prototype.Index(UnshadedShader).Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Content.Client.Stealth;
|
||||
|
||||
public sealed class StealthSystem : SharedStealthSystem
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> Shader = "Stealth";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
@@ -20,7 +22,7 @@ public sealed class StealthSystem : SharedStealthSystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_shader = _protoMan.Index<ShaderPrototype>("Stealth").InstanceUnique();
|
||||
_shader = _protoMan.Index(Shader).InstanceUnique();
|
||||
|
||||
SubscribeLocalEvent<StealthComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<StealthComponent, ComponentStartup>(OnStartup);
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
using Content.Shared.SprayPainter.Prototypes;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Storage.Visualizers;
|
||||
|
||||
public sealed class EntityStorageVisualizerSystem : VisualizerSystem<EntityStorageVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -26,12 +31,34 @@ public sealed class EntityStorageVisualizerSystem : VisualizerSystem<EntityStora
|
||||
SpriteSystem.LayerSetRsiState((uid, sprite), StorageVisualLayers.Base, comp.StateBaseClosed);
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, EntityStorageVisualsComponent comp, ref AppearanceChangeEvent args)
|
||||
protected override void OnAppearanceChange(EntityUid uid,
|
||||
EntityStorageVisualsComponent comp,
|
||||
ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, StorageVisuals.Open, out var open, args.Component))
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, StorageVisuals.Open, out var open, args.Component))
|
||||
return;
|
||||
|
||||
var forceRedrawBase = false;
|
||||
if (AppearanceSystem.TryGetData<string>(uid, PaintableVisuals.Prototype, out var prototype, args.Component))
|
||||
{
|
||||
if (_prototypeManager.TryIndex(prototype, out var proto))
|
||||
{
|
||||
if (proto.TryGetComponent(out SpriteComponent? sprite, _componentFactory))
|
||||
{
|
||||
SpriteSystem.SetBaseRsi((uid, args.Sprite), sprite.BaseRSI);
|
||||
}
|
||||
if (proto.TryGetComponent(out EntityStorageVisualsComponent? visuals, _componentFactory))
|
||||
{
|
||||
comp.StateBaseOpen = visuals.StateBaseOpen;
|
||||
comp.StateBaseClosed = visuals.StateBaseClosed;
|
||||
comp.StateDoorOpen = visuals.StateDoorOpen;
|
||||
comp.StateDoorClosed = visuals.StateDoorClosed;
|
||||
forceRedrawBase = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open/Closed state for the storage entity.
|
||||
if (SpriteSystem.LayerMapTryGet((uid, args.Sprite), StorageVisualLayers.Door, out _, false))
|
||||
{
|
||||
@@ -52,6 +79,8 @@ public sealed class EntityStorageVisualizerSystem : VisualizerSystem<EntityStora
|
||||
|
||||
if (comp.StateBaseOpen != null)
|
||||
SpriteSystem.LayerSetRsiState((uid, args.Sprite), StorageVisualLayers.Base, comp.StateBaseOpen);
|
||||
else if (forceRedrawBase && comp.StateBaseClosed != null)
|
||||
SpriteSystem.LayerSetRsiState((uid, args.Sprite), StorageVisualLayers.Base, comp.StateBaseClosed);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -68,6 +97,8 @@ public sealed class EntityStorageVisualizerSystem : VisualizerSystem<EntityStora
|
||||
|
||||
if (comp.StateBaseClosed != null)
|
||||
SpriteSystem.LayerSetRsiState((uid, args.Sprite), StorageVisualLayers.Base, comp.StateBaseClosed);
|
||||
else if (forceRedrawBase && comp.StateBaseOpen != null)
|
||||
SpriteSystem.LayerSetRsiState((uid, args.Sprite), StorageVisualLayers.Base, comp.StateBaseOpen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<BoxContainer Margin="8,8,8,8" Orientation="Vertical">
|
||||
<BoxContainer Margin="8,0,8,8" Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="StoreItemName" HorizontalExpand="True" />
|
||||
<Label Name="DiscountSubText"
|
||||
HorizontalAlignment="Right"/>
|
||||
<Button
|
||||
Name="StoreItemBuyButton"
|
||||
MinWidth="64"
|
||||
HorizontalAlignment="Right"
|
||||
Access="Public" />
|
||||
</BoxContainer>
|
||||
<PanelContainer StyleClasses="HighDivider" />
|
||||
<PanelContainer StyleClasses="HighDivider" Margin="0,1,0,2"/>
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
|
||||
<TextureRect
|
||||
Name="StoreItemTexture"
|
||||
Margin="0,0,4,0"
|
||||
MinSize="48 48"
|
||||
Stretch="KeepAspectCentered" />
|
||||
<Control MinWidth="5"/>
|
||||
<RichTextLabel Name="StoreItemDescription" />
|
||||
<BoxContainer Orientation="Vertical" VerticalAlignment="Center" Margin="0,0,4,0">
|
||||
<TextureRect
|
||||
Name="StoreItemTexture"
|
||||
Margin="0,0,4,0"
|
||||
MinSize="48 48"
|
||||
Stretch="KeepAspectCentered" />
|
||||
<Button
|
||||
Name="StoreItemBuyButton"
|
||||
MinWidth="48"
|
||||
MaxHeight="48"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
Margin="0,4,0,0"
|
||||
Access="Public" />
|
||||
<Label Name="DiscountSubText"
|
||||
HorizontalAlignment="Center"/>
|
||||
</BoxContainer>
|
||||
<RichTextLabel Name="StoreItemDescription" VerticalAlignment="Top"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
@@ -1,9 +1,182 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Stunnable;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Client.Stunnable
|
||||
namespace Content.Client.Stunnable;
|
||||
|
||||
public sealed class StunSystem : SharedStunSystem
|
||||
{
|
||||
public sealed class StunSystem : SharedStunSystem
|
||||
{
|
||||
[Dependency] private readonly SharedCombatModeSystem _combat = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SpriteSystem _spriteSystem = default!;
|
||||
|
||||
private readonly int[] _sign = [-1, 1];
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StunVisualsComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<StunVisualsComponent, AppearanceChangeEvent>(OnAppearanceChanged);
|
||||
|
||||
CommandBinds.Builder
|
||||
.BindAfter(EngineKeyFunctions.UseSecondary, new PointerInputCmdHandler(OnUseSecondary, true, true), typeof(SharedInteractionSystem))
|
||||
.Register<StunSystem>();
|
||||
}
|
||||
|
||||
private bool OnUseSecondary(in PointerInputCmdHandler.PointerInputCmdArgs args)
|
||||
{
|
||||
if (args.Session?.AttachedEntity is not {Valid: true} uid)
|
||||
return false;
|
||||
|
||||
if (args.EntityUid != uid || !HasComp<KnockedDownComponent>(uid) || !_combat.IsInCombatMode(uid))
|
||||
return false;
|
||||
|
||||
RaisePredictiveEvent(new ForceStandUpEvent());
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add stun visual layers
|
||||
/// </summary>
|
||||
private void OnComponentInit(Entity<StunVisualsComponent> entity, ref ComponentInit args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(entity, out var sprite))
|
||||
return;
|
||||
|
||||
var spriteEntity = (entity.Owner, sprite);
|
||||
|
||||
_spriteSystem.LayerMapReserve(spriteEntity, StunVisualLayers.StamCrit);
|
||||
_spriteSystem.LayerSetVisible(spriteEntity, StunVisualLayers.StamCrit, false);
|
||||
_spriteSystem.LayerSetOffset(spriteEntity, StunVisualLayers.StamCrit, new Vector2(0, 0.3125f));
|
||||
|
||||
_spriteSystem.LayerSetRsi(spriteEntity, StunVisualLayers.StamCrit, entity.Comp.StarsPath);
|
||||
|
||||
UpdateAppearance((entity, sprite), entity.Comp.State);
|
||||
}
|
||||
|
||||
private void OnAppearanceChanged(Entity<StunVisualsComponent> entity, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite != null)
|
||||
UpdateAppearance((entity, args.Sprite), entity.Comp.State);
|
||||
}
|
||||
|
||||
private void UpdateAppearance(Entity<SpriteComponent?> entity, string state)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp))
|
||||
return;
|
||||
|
||||
if (!_spriteSystem.LayerMapTryGet((entity, entity.Comp), StunVisualLayers.StamCrit, out var index, false))
|
||||
return;
|
||||
|
||||
var visible = Appearance.TryGetData<bool>(entity, StunVisuals.SeeingStars, out var stars) && stars;
|
||||
|
||||
_spriteSystem.LayerSetVisible((entity, entity.Comp), index, visible);
|
||||
_spriteSystem.LayerSetRsiState((entity, entity.Comp), index, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A simple fatigue animation, a mild modification of the jittering animation. The animation constructor is
|
||||
/// quite complex, but that's because the AnimationSystem doesn't have proper adjustment layers. In a potential
|
||||
/// future where proper adjustment layers are added feel free to clean this up to be an animation with two adjustment
|
||||
/// layers rather than one mega layer.
|
||||
/// </summary>
|
||||
/// <param name="sprite">The spriteComponent we're adjusting the offset of</param>
|
||||
/// <param name="frequency">How many times per second does the animation run?</param>
|
||||
/// <param name="jitters">How many times should we jitter during the animation? Also determines breathing frequency</param>
|
||||
/// <param name="minJitter">Mininum jitter offset multiplier for X and Y directions</param>
|
||||
/// <param name="maxJitter">Maximum jitter offset multiplier for X and Y directions</param>
|
||||
/// <param name="breathing">Maximum breathing offset, this is in the Y direction</param>
|
||||
/// <param name="startOffset">Starting offset because we don't have adjustment layers</param>
|
||||
/// <param name="lastJitter">Last jitter so we don't jitter to the same quadrant</param>
|
||||
/// <returns></returns>
|
||||
public Animation GetFatigueAnimation(SpriteComponent sprite,
|
||||
float frequency,
|
||||
int jitters,
|
||||
Vector2 minJitter,
|
||||
Vector2 maxJitter,
|
||||
float breathing,
|
||||
Vector2 startOffset,
|
||||
ref Vector2 lastJitter)
|
||||
{
|
||||
// avoid animations with negative length or infinite length
|
||||
if (frequency <= 0)
|
||||
return new Animation();
|
||||
|
||||
var breaths = new Vector2(0, breathing * 2) / jitters;
|
||||
|
||||
var length = 1 / frequency;
|
||||
var frames = length / jitters;
|
||||
|
||||
var keyFrames = new List<AnimationTrackProperty.KeyFrame> { new(sprite.Offset, 0f) };
|
||||
|
||||
// Spits out a list of keyframes to feed to the AnimationPlayer based on the variables we've inputted
|
||||
for (var i = 1; i <= jitters; i++)
|
||||
{
|
||||
var offset = new Vector2(_random.NextFloat(minJitter.X, maxJitter.X),
|
||||
_random.NextFloat(minJitter.Y, maxJitter.Y));
|
||||
offset.X *= _random.Pick(_sign);
|
||||
offset.Y *= _random.Pick(_sign);
|
||||
|
||||
if (i == 1 && Math.Sign(offset.X) == Math.Sign(lastJitter.X)
|
||||
&& Math.Sign(offset.Y) == Math.Sign(lastJitter.Y))
|
||||
{
|
||||
// If the sign is the same as last time on both axis we flip one randomly
|
||||
// to avoid jitter staying in one quadrant too much.
|
||||
if (_random.Prob(0.5f))
|
||||
offset.X *= -1;
|
||||
else
|
||||
offset.Y *= -1;
|
||||
}
|
||||
|
||||
lastJitter = offset;
|
||||
|
||||
// For the first half of the jitter, we vertically displace the sprite upwards to simulate breathing in
|
||||
if (i <= jitters / 2)
|
||||
{
|
||||
keyFrames.Add(new AnimationTrackProperty.KeyFrame(startOffset + breaths * i + offset, frames));
|
||||
}
|
||||
// For the next quarter we displace the sprite down, to about 12.5% breathing offset below our starting position
|
||||
// Simulates breathing out
|
||||
else if (i < jitters * 3 / 4)
|
||||
{
|
||||
keyFrames.Add(
|
||||
new AnimationTrackProperty.KeyFrame(startOffset + breaths * ( jitters - i * 1.5f ) + offset, frames));
|
||||
}
|
||||
// Return to our starting position for breathing, jitter reaches its final position
|
||||
else
|
||||
{
|
||||
keyFrames.Add(
|
||||
new AnimationTrackProperty.KeyFrame(startOffset + breaths * ( i - jitters ) + offset, frames));
|
||||
}
|
||||
}
|
||||
|
||||
return new Animation
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(length),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
// Heavy Breathing
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Offset),
|
||||
InterpolationMode = AnimationInterpolationMode.Cubic,
|
||||
KeyFrames = keyFrames,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum StunVisualLayers : byte
|
||||
{
|
||||
StamCrit,
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Content.Client.SurveillanceCamera.UI;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class SurveillanceCameraMonitorWindow : DefaultWindow
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> CameraStaticShader = "CameraStatic";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
@@ -53,7 +55,7 @@ public sealed partial class SurveillanceCameraMonitorWindow : DefaultWindow
|
||||
|
||||
// This could be done better. I don't want to deal with stylesheets at the moment.
|
||||
var texture = _resourceCache.GetTexture("/Textures/Interface/Nano/square_black.png");
|
||||
var shader = _prototypeManager.Index<ShaderPrototype>("CameraStatic").Instance().Duplicate();
|
||||
var shader = _prototypeManager.Index(CameraStaticShader).Instance().Duplicate();
|
||||
|
||||
CameraView.ViewportSize = new Vector2i(500, 500);
|
||||
CameraView.Eye = _defaultEye; // sure
|
||||
|
||||
@@ -38,7 +38,7 @@ public sealed partial class SurveillanceCameraSetupWindow : DefaultWindow
|
||||
}
|
||||
|
||||
// Pass in a list of frequency prototype IDs.
|
||||
public void LoadAvailableNetworks(uint currentNetwork, List<string> networks)
|
||||
public void LoadAvailableNetworks(uint currentNetwork, List<ProtoId<DeviceFrequencyPrototype>> networks)
|
||||
{
|
||||
NetworkSelector.Clear();
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Content.Client.UserInterface.RichText;
|
||||
/// </summary>
|
||||
public sealed class MonoTag : IMarkupTag
|
||||
{
|
||||
[ValidatePrototypeId<FontPrototype>] public const string MonoFont = "Monospace";
|
||||
public static readonly ProtoId<FontPrototype> MonoFont = "Monospace";
|
||||
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
@@ -67,8 +67,7 @@ public sealed partial class ChatUIController : UIController
|
||||
[UISystemDependency] private readonly MindSystem? _mindSystem = default!;
|
||||
[UISystemDependency] private readonly RoleCodewordSystem? _roleCodewordSystem = default!;
|
||||
|
||||
[ValidatePrototypeId<ColorPalettePrototype>]
|
||||
private const string ChatNamePalette = "ChatNames";
|
||||
private static readonly ProtoId<ColorPalettePrototype> ChatNamePalette = "ChatNames";
|
||||
private string[] _chatNameColors = default!;
|
||||
private bool _chatNameColorsEnabled;
|
||||
|
||||
@@ -232,7 +231,7 @@ public sealed partial class ChatUIController : UIController
|
||||
gameplayStateLoad.OnScreenLoad += OnScreenLoad;
|
||||
gameplayStateLoad.OnScreenUnload += OnScreenUnload;
|
||||
|
||||
var nameColors = _prototypeManager.Index<ColorPalettePrototype>(ChatNamePalette).Colors.Values.ToArray();
|
||||
var nameColors = _prototypeManager.Index(ChatNamePalette).Colors.Values.ToArray();
|
||||
_chatNameColors = new string[nameColors.Length];
|
||||
for (var i = 0; i < nameColors.Length; i++)
|
||||
{
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Content.Client.UserInterface.Systems.DamageOverlays.Overlays;
|
||||
|
||||
public sealed class DamageOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> CircleMaskShader = "GradientCircleMask";
|
||||
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
@@ -49,9 +51,9 @@ public sealed class DamageOverlay : Overlay
|
||||
{
|
||||
// TODO: Replace
|
||||
IoCManager.InjectDependencies(this);
|
||||
_oxygenShader = _prototypeManager.Index<ShaderPrototype>("GradientCircleMask").InstanceUnique();
|
||||
_critShader = _prototypeManager.Index<ShaderPrototype>("GradientCircleMask").InstanceUnique();
|
||||
_bruteShader = _prototypeManager.Index<ShaderPrototype>("GradientCircleMask").InstanceUnique();
|
||||
_oxygenShader = _prototypeManager.Index(CircleMaskShader).InstanceUnique();
|
||||
_critShader = _prototypeManager.Index(CircleMaskShader).InstanceUnique();
|
||||
_bruteShader = _prototypeManager.Index(CircleMaskShader).InstanceUnique();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
|
||||
@@ -19,8 +19,7 @@ public sealed class InfoUIController : UIController, IOnStateExited<GameplayStat
|
||||
private RulesPopup? _rulesPopup;
|
||||
private RulesAndInfoWindow? _infoWindow;
|
||||
|
||||
[ValidatePrototypeId<GuideEntryPrototype>]
|
||||
private const string DefaultRuleset = "DefaultRuleset";
|
||||
private static readonly ProtoId<GuideEntryPrototype> DefaultRuleset = "DefaultRuleset";
|
||||
|
||||
public ProtoId<GuideEntryPrototype> RulesEntryId = DefaultRuleset;
|
||||
|
||||
@@ -92,7 +91,7 @@ public sealed class InfoUIController : UIController, IOnStateExited<GameplayStat
|
||||
{
|
||||
if (!_prototype.TryIndex(RulesEntryId, out var guideEntryPrototype))
|
||||
{
|
||||
guideEntryPrototype = _prototype.Index<GuideEntryPrototype>(DefaultRuleset);
|
||||
guideEntryPrototype = _prototype.Index(DefaultRuleset);
|
||||
Log.Error($"Couldn't find the following prototype: {RulesEntryId}. Falling back to {DefaultRuleset}, please check that the server has the rules set up correctly");
|
||||
return guideEntryPrototype;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,12 @@ public sealed class ItemGridPiece : Control, IEntityControl
|
||||
|
||||
handle.SetTransform(pos, iconRotation);
|
||||
var box = new UIBox2(root, root + sprite.Size * scale);
|
||||
handle.DrawTextureRect(sprite, box);
|
||||
|
||||
var ev = new BeforeRenderInGridEvent(new Color(255, 255, 255));
|
||||
_entityManager.EventBus.RaiseLocalEvent(Entity, ev);
|
||||
|
||||
handle.DrawTextureRect(sprite, box, ev.Color);
|
||||
|
||||
handle.SetTransform(GlobalPixelPosition, Angle.Zero);
|
||||
}
|
||||
else
|
||||
@@ -298,6 +303,19 @@ public sealed class ItemGridPiece : Control, IEntityControl
|
||||
public EntityUid? UiEntity => Entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This event gets raised before a sprite gets drawn in a grid and lets to change the sprite color for several gameobjects that have special sprites to render in containers.
|
||||
/// </summary>
|
||||
public sealed class BeforeRenderInGridEvent : EntityEventArgs
|
||||
{
|
||||
public Color Color { get; set; }
|
||||
|
||||
public BeforeRenderInGridEvent(Color color)
|
||||
{
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ItemGridPieceMarks
|
||||
{
|
||||
First,
|
||||
|
||||
@@ -39,8 +39,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
public const string HitscanProto = "HitscanEffect";
|
||||
public static readonly EntProtoId HitscanProto = "HitscanEffect";
|
||||
|
||||
public bool SpreadOverlay
|
||||
{
|
||||
|
||||
@@ -7,10 +7,12 @@ namespace Content.IntegrationTests.Tests.Atmos
|
||||
[TestOf(typeof(AtmosAlarmThreshold))]
|
||||
public sealed class AlarmThresholdTest
|
||||
{
|
||||
private const string AlarmThresholdTestDummyId = "AlarmThresholdTestDummy";
|
||||
|
||||
[TestPrototypes]
|
||||
private const string Prototypes = @"
|
||||
private const string Prototypes = $@"
|
||||
- type: alarmThreshold
|
||||
id: AlarmThresholdTestDummy
|
||||
id: {AlarmThresholdTestDummyId}
|
||||
upperBound: !type:AlarmThresholdSetting
|
||||
threshold: 5
|
||||
lowerBound: !type:AlarmThresholdSetting
|
||||
@@ -30,7 +32,7 @@ namespace Content.IntegrationTests.Tests.Atmos
|
||||
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||
AtmosAlarmThreshold threshold = default!;
|
||||
|
||||
var proto = prototypeManager.Index<AtmosAlarmThresholdPrototype>("AlarmThresholdTestDummy");
|
||||
var proto = prototypeManager.Index<AtmosAlarmThresholdPrototype>(AlarmThresholdTestDummyId);
|
||||
threshold = new(proto);
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace Content.IntegrationTests.Tests.Commands
|
||||
[TestOf(typeof(RejuvenateSystem))]
|
||||
public sealed class RejuvenateTest
|
||||
{
|
||||
private static readonly ProtoId<DamageGroupPrototype> TestDamageGroup = "Toxin";
|
||||
|
||||
[TestPrototypes]
|
||||
private const string Prototypes = @"
|
||||
- type: entity
|
||||
@@ -62,7 +64,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
||||
});
|
||||
|
||||
// Kill the entity
|
||||
DamageSpecifier damage = new(prototypeManager.Index<DamageGroupPrototype>("Toxin"), FixedPoint2.New(10000000));
|
||||
DamageSpecifier damage = new(prototypeManager.Index(TestDamageGroup), FixedPoint2.New(10000000));
|
||||
|
||||
damSystem.TryChangeDamage(human, damage, true);
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ public sealed class SuicideCommandTests
|
||||
components:
|
||||
- type: MaterialReclaimer";
|
||||
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
|
||||
private static readonly ProtoId<DamageTypePrototype> DamageType = "Slash";
|
||||
|
||||
/// <summary>
|
||||
/// Run the suicide command in the console
|
||||
/// Should successfully kill the player and ghost them
|
||||
@@ -144,7 +146,7 @@ public sealed class SuicideCommandTests
|
||||
mobThresholdsComp = entManager.GetComponent<MobThresholdsComponent>(player);
|
||||
damageableComp = entManager.GetComponent<DamageableComponent>(player);
|
||||
|
||||
if (protoMan.TryIndex<DamageTypePrototype>("Slash", out var slashProto))
|
||||
if (protoMan.TryIndex(DamageType, out var slashProto))
|
||||
damageableSystem.TryChangeDamage(player, new DamageSpecifier(slashProto, FixedPoint2.New(46.5)));
|
||||
});
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.IntegrationTests.Tests.Construction.Interaction;
|
||||
|
||||
public sealed class WindowRepair : InteractionTest
|
||||
{
|
||||
private static readonly ProtoId<DamageTypePrototype> BluntDamageType = "Blunt";
|
||||
|
||||
[Test]
|
||||
public async Task RepairReinforcedWindow()
|
||||
{
|
||||
@@ -16,7 +18,7 @@ public sealed class WindowRepair : InteractionTest
|
||||
// Damage the entity.
|
||||
var sys = SEntMan.System<DamageableSystem>();
|
||||
var comp = Comp<DamageableComponent>();
|
||||
var damageType = Server.ResolveDependency<IPrototypeManager>().Index<DamageTypePrototype>("Blunt");
|
||||
var damageType = Server.ProtoMan.Index(BluntDamageType);
|
||||
var damage = new DamageSpecifier(damageType, FixedPoint2.New(10));
|
||||
Assert.That(comp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero));
|
||||
await Server.WaitPost(() => sys.TryChangeDamage(SEntMan.GetEntity(Target), damage, ignoreResistances: true));
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -14,66 +12,79 @@ namespace Content.IntegrationTests.Tests.Damageable
|
||||
[TestOf(typeof(DamageableSystem))]
|
||||
public sealed class DamageableTest
|
||||
{
|
||||
private const string TestDamageableEntityId = "TestDamageableEntityId";
|
||||
private const string TestGroup1 = "TestGroup1";
|
||||
private const string TestGroup2 = "TestGroup2";
|
||||
private const string TestGroup3 = "TestGroup3";
|
||||
private const string TestDamage1 = "TestDamage1";
|
||||
private const string TestDamage2a = "TestDamage2a";
|
||||
private const string TestDamage2b = "TestDamage2b";
|
||||
|
||||
private const string TestDamage3a = "TestDamage3a";
|
||||
|
||||
private const string TestDamage3b = "TestDamage3b";
|
||||
private const string TestDamage3c = "TestDamage3c";
|
||||
|
||||
[TestPrototypes]
|
||||
private const string Prototypes = @"
|
||||
private const string Prototypes = $@"
|
||||
# Define some damage groups
|
||||
- type: damageType
|
||||
id: TestDamage1
|
||||
id: {TestDamage1}
|
||||
name: damage-type-blunt
|
||||
|
||||
- type: damageType
|
||||
id: TestDamage2a
|
||||
id: {TestDamage2a}
|
||||
name: damage-type-blunt
|
||||
|
||||
- type: damageType
|
||||
id: TestDamage2b
|
||||
id: {TestDamage2b}
|
||||
name: damage-type-blunt
|
||||
|
||||
- type: damageType
|
||||
id: TestDamage3a
|
||||
id: {TestDamage3a}
|
||||
name: damage-type-blunt
|
||||
|
||||
- type: damageType
|
||||
id: TestDamage3b
|
||||
id: {TestDamage3b}
|
||||
name: damage-type-blunt
|
||||
|
||||
- type: damageType
|
||||
id: TestDamage3c
|
||||
id: {TestDamage3c}
|
||||
name: damage-type-blunt
|
||||
|
||||
# Define damage Groups with 1,2,3 damage types
|
||||
- type: damageGroup
|
||||
id: TestGroup1
|
||||
id: {TestGroup1}
|
||||
name: damage-group-brute
|
||||
damageTypes:
|
||||
- TestDamage1
|
||||
- {TestDamage1}
|
||||
|
||||
- type: damageGroup
|
||||
id: TestGroup2
|
||||
id: {TestGroup2}
|
||||
name: damage-group-brute
|
||||
damageTypes:
|
||||
- TestDamage2a
|
||||
- TestDamage2b
|
||||
- {TestDamage2a}
|
||||
- {TestDamage2b}
|
||||
|
||||
- type: damageGroup
|
||||
id: TestGroup3
|
||||
id: {TestGroup3}
|
||||
name: damage-group-brute
|
||||
damageTypes:
|
||||
- TestDamage3a
|
||||
- TestDamage3b
|
||||
- TestDamage3c
|
||||
- {TestDamage3a}
|
||||
- {TestDamage3b}
|
||||
- {TestDamage3c}
|
||||
|
||||
# This container should not support TestDamage1 or TestDamage2b
|
||||
- type: damageContainer
|
||||
id: testDamageContainer
|
||||
supportedGroups:
|
||||
- TestGroup3
|
||||
- {TestGroup3}
|
||||
supportedTypes:
|
||||
- TestDamage2a
|
||||
- {TestDamage2a}
|
||||
|
||||
- type: entity
|
||||
id: TestDamageableEntityId
|
||||
name: TestDamageableEntityId
|
||||
id: {TestDamageableEntityId}
|
||||
name: {TestDamageableEntityId}
|
||||
components:
|
||||
- type: Damageable
|
||||
damageContainer: testDamageContainer
|
||||
@@ -113,20 +124,20 @@ namespace Content.IntegrationTests.Tests.Damageable
|
||||
{
|
||||
var coordinates = map.MapCoords;
|
||||
|
||||
sDamageableEntity = sEntityManager.SpawnEntity("TestDamageableEntityId", coordinates);
|
||||
sDamageableEntity = sEntityManager.SpawnEntity(TestDamageableEntityId, coordinates);
|
||||
sDamageableComponent = sEntityManager.GetComponent<DamageableComponent>(sDamageableEntity);
|
||||
sDamageableSystem = sEntitySystemManager.GetEntitySystem<DamageableSystem>();
|
||||
|
||||
group1 = sPrototypeManager.Index<DamageGroupPrototype>("TestGroup1");
|
||||
group2 = sPrototypeManager.Index<DamageGroupPrototype>("TestGroup2");
|
||||
group3 = sPrototypeManager.Index<DamageGroupPrototype>("TestGroup3");
|
||||
group1 = sPrototypeManager.Index<DamageGroupPrototype>(TestGroup1);
|
||||
group2 = sPrototypeManager.Index<DamageGroupPrototype>(TestGroup2);
|
||||
group3 = sPrototypeManager.Index<DamageGroupPrototype>(TestGroup3);
|
||||
|
||||
type1 = sPrototypeManager.Index<DamageTypePrototype>("TestDamage1");
|
||||
type2a = sPrototypeManager.Index<DamageTypePrototype>("TestDamage2a");
|
||||
type2b = sPrototypeManager.Index<DamageTypePrototype>("TestDamage2b");
|
||||
type3a = sPrototypeManager.Index<DamageTypePrototype>("TestDamage3a");
|
||||
type3b = sPrototypeManager.Index<DamageTypePrototype>("TestDamage3b");
|
||||
type3c = sPrototypeManager.Index<DamageTypePrototype>("TestDamage3c");
|
||||
type1 = sPrototypeManager.Index<DamageTypePrototype>(TestDamage1);
|
||||
type2a = sPrototypeManager.Index<DamageTypePrototype>(TestDamage2a);
|
||||
type2b = sPrototypeManager.Index<DamageTypePrototype>(TestDamage2b);
|
||||
type3a = sPrototypeManager.Index<DamageTypePrototype>(TestDamage3a);
|
||||
type3b = sPrototypeManager.Index<DamageTypePrototype>(TestDamage3b);
|
||||
type3c = sPrototypeManager.Index<DamageTypePrototype>(TestDamage3c);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(5);
|
||||
|
||||
@@ -54,8 +54,8 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBrute");
|
||||
var burnDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBurn");
|
||||
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>(TestBruteDamageGroupId);
|
||||
var burnDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>(TestBurnDamageGroupId);
|
||||
|
||||
DamageSpecifier bruteDamage = new(bruteDamageGroup, FixedPoint2.New(5));
|
||||
DamageSpecifier burnDamage = new(burnDamageGroup, FixedPoint2.New(5));
|
||||
|
||||
@@ -49,8 +49,8 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var bluntDamageType = protoManager.Index<DamageTypePrototype>("TestBlunt");
|
||||
var slashDamageType = protoManager.Index<DamageTypePrototype>("TestSlash");
|
||||
var bluntDamageType = protoManager.Index<DamageTypePrototype>(TestBluntDamageTypeId);
|
||||
var slashDamageType = protoManager.Index<DamageTypePrototype>(TestSlashDamageTypeId);
|
||||
|
||||
var bluntDamage = new DamageSpecifier(bluntDamageType, 5);
|
||||
var slashDamage = new DamageSpecifier(slashDamageType, 5);
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var coordinates = sEntityManager.GetComponent<TransformComponent>(sDestructibleEntity).Coordinates;
|
||||
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBrute");
|
||||
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>(TestBruteDamageGroupId);
|
||||
DamageSpecifier bruteDamage = new(bruteDamageGroup, 50);
|
||||
|
||||
#pragma warning disable NUnit2045 // Interdependent assertions.
|
||||
|
||||
@@ -7,48 +7,56 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
public const string DestructibleDestructionEntityId = "DestructibleTestsDestructibleDestructionEntity";
|
||||
public const string DestructibleDamageTypeEntityId = "DestructibleTestsDestructibleDamageTypeEntity";
|
||||
public const string DestructibleDamageGroupEntityId = "DestructibleTestsDestructibleDamageGroupEntity";
|
||||
public const string TestBruteDamageGroupId = "TestBrute";
|
||||
public const string TestBurnDamageGroupId = "TestBurn";
|
||||
public const string TestBluntDamageTypeId = "TestBlunt";
|
||||
public const string TestSlashDamageTypeId = "TestSlash";
|
||||
public const string TestPiercingDamageTypeId = "TestPiercing";
|
||||
public const string TestHeatDamageTypeId = "TestHeat";
|
||||
public const string TestShockDamageTypeId = "TestShock";
|
||||
public const string TestColdDamageTypeId = "TestCold";
|
||||
|
||||
[TestPrototypes]
|
||||
public const string DamagePrototypes = $@"
|
||||
- type: damageType
|
||||
id: TestBlunt
|
||||
id: {TestBluntDamageTypeId}
|
||||
name: damage-type-blunt
|
||||
|
||||
- type: damageType
|
||||
id: TestSlash
|
||||
id: {TestSlashDamageTypeId}
|
||||
name: damage-type-slash
|
||||
|
||||
- type: damageType
|
||||
id: TestPiercing
|
||||
id: {TestPiercingDamageTypeId}
|
||||
name: damage-type-piercing
|
||||
|
||||
- type: damageType
|
||||
id: TestHeat
|
||||
id: {TestHeatDamageTypeId}
|
||||
name: damage-type-heat
|
||||
|
||||
- type: damageType
|
||||
id: TestShock
|
||||
id: {TestShockDamageTypeId}
|
||||
name: damage-type-shock
|
||||
|
||||
- type: damageType
|
||||
id: TestCold
|
||||
id: {TestColdDamageTypeId}
|
||||
name: damage-type-cold
|
||||
|
||||
- type: damageGroup
|
||||
id: TestBrute
|
||||
id: {TestBruteDamageGroupId}
|
||||
name: damage-group-brute
|
||||
damageTypes:
|
||||
- TestBlunt
|
||||
- TestSlash
|
||||
- TestPiercing
|
||||
- {TestBluntDamageTypeId}
|
||||
- {TestSlashDamageTypeId}
|
||||
- {TestPiercingDamageTypeId}
|
||||
|
||||
- type: damageGroup
|
||||
id: TestBurn
|
||||
id: {TestBurnDamageGroupId}
|
||||
name: damage-group-burn
|
||||
damageTypes:
|
||||
- TestHeat
|
||||
- TestShock
|
||||
- TestCold
|
||||
- {TestHeatDamageTypeId}
|
||||
- {TestShockDamageTypeId}
|
||||
- {TestColdDamageTypeId}
|
||||
|
||||
- type: entity
|
||||
id: {SpawnedEntityId}
|
||||
@@ -114,10 +122,10 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
!type:AndTrigger
|
||||
triggers:
|
||||
- !type:DamageTypeTrigger
|
||||
damageType: TestBlunt
|
||||
damageType: {TestBluntDamageTypeId}
|
||||
damage: 10
|
||||
- !type:DamageTypeTrigger
|
||||
damageType: TestSlash
|
||||
damageType: {TestSlashDamageTypeId}
|
||||
damage: 10
|
||||
|
||||
- type: entity
|
||||
@@ -131,10 +139,10 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
!type:AndTrigger
|
||||
triggers:
|
||||
- !type:DamageGroupTrigger
|
||||
damageGroup: TestBrute
|
||||
damageGroup: {TestBruteDamageGroupId}
|
||||
damage: 10
|
||||
- !type:DamageGroupTrigger
|
||||
damageGroup: TestBurn
|
||||
damageGroup: {TestBurnDamageGroupId}
|
||||
damage: 10";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var bluntDamage = new DamageSpecifier(sPrototypeManager.Index<DamageTypePrototype>("TestBlunt"), 10);
|
||||
var bluntDamage = new DamageSpecifier(sPrototypeManager.Index<DamageTypePrototype>(TestBluntDamageTypeId), 10);
|
||||
|
||||
sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true);
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ public sealed class AbsorbentTest
|
||||
solutionContainerSystem.AddSolution(refillableSoln.Value, new Solution(NonEvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfNonEvaporable));
|
||||
|
||||
// Act
|
||||
absorbentSystem.Mop(user, refillable, absorbent, component);
|
||||
absorbentSystem.Mop((absorbent, component), user, refillable);
|
||||
|
||||
// Assert
|
||||
var absorbentComposition = absorbentSolution.GetReagentPrototypes(prototypeManager).ToDictionary(r => r.Key.ID, r => r.Value);
|
||||
@@ -167,7 +167,7 @@ public sealed class AbsorbentTest
|
||||
solutionContainerSystem.AddSolution(refillableSoln.Value, new Solution(NonEvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfNonEvaporable));
|
||||
|
||||
// Act
|
||||
absorbentSystem.Mop(user, refillable, absorbent, component);
|
||||
absorbentSystem.Mop((absorbent, component), user, refillable);
|
||||
|
||||
// Assert
|
||||
var absorbentComposition = absorbentSolution.GetReagentPrototypes(prototypeManager).ToDictionary(r => r.Key.ID, r => r.Value);
|
||||
|
||||
@@ -4,7 +4,6 @@ using Content.Shared.Tag;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Linter;
|
||||
|
||||
@@ -66,25 +65,25 @@ public sealed class StaticFieldValidationTest
|
||||
[Reflect(false)]
|
||||
private sealed class StringValid
|
||||
{
|
||||
[ValidatePrototypeId<TagPrototype>] public static string Tag = "StaticFieldTestTag";
|
||||
public static readonly ProtoId<TagPrototype> Tag = "StaticFieldTestTag";
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class StringInvalid
|
||||
{
|
||||
[ValidatePrototypeId<TagPrototype>] public static string Tag = string.Empty;
|
||||
public static readonly ProtoId<TagPrototype> Tag = string.Empty;
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class StringArrayValid
|
||||
{
|
||||
[ValidatePrototypeId<TagPrototype>] public static string[] Tag = ["StaticFieldTestTag", "StaticFieldTestTag"];
|
||||
public static readonly ProtoId<TagPrototype>[] Tag = ["StaticFieldTestTag", "StaticFieldTestTag"];
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class StringArrayInvalid
|
||||
{
|
||||
[ValidatePrototypeId<TagPrototype>] public static string[] Tag = [string.Empty, "StaticFieldTestTag", string.Empty];
|
||||
public static readonly ProtoId<TagPrototype>[] Tag = [string.Empty, "StaticFieldTestTag", string.Empty];
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
|
||||
@@ -1,38 +1,48 @@
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Ghost.Roles;
|
||||
using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Players;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Players;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Minds;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class GhostRoleTests
|
||||
{
|
||||
private const string GhostRoleProtoId = "GhostRoleTestEntity";
|
||||
private const string TestMobProtoId = "GhostRoleTestMob";
|
||||
|
||||
[TestPrototypes]
|
||||
private const string Prototypes = @"
|
||||
- type: entity
|
||||
id: GhostRoleTestEntity
|
||||
components:
|
||||
- type: MindContainer
|
||||
- type: GhostRole
|
||||
- type: GhostTakeoverAvailable
|
||||
";
|
||||
private const string Prototypes = $"""
|
||||
- type: entity
|
||||
id: {GhostRoleProtoId}
|
||||
components:
|
||||
- type: MindContainer
|
||||
- type: GhostRole
|
||||
- type: GhostTakeoverAvailable
|
||||
- type: MobState
|
||||
|
||||
- type: entity
|
||||
id: {TestMobProtoId}
|
||||
components:
|
||||
- type: MobState # MobState is required for correct determination of if the player can return to body or not
|
||||
""";
|
||||
|
||||
/// <summary>
|
||||
/// This is a simple test that just checks if a player can take a ghost role and then regain control of their
|
||||
/// original entity without encountering errors.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TakeRoleAndReturn()
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public async Task TakeRoleAndReturn(bool adminGhost)
|
||||
{
|
||||
var ghostCommand = adminGhost ? "aghost" : "ghost";
|
||||
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings
|
||||
{
|
||||
Dirty = true,
|
||||
@@ -49,36 +59,67 @@ public sealed class GhostRoleTests
|
||||
var conHost = client.ResolveDependency<IConsoleHost>();
|
||||
var mindSystem = entMan.System<SharedMindSystem>();
|
||||
var session = sPlayerMan.Sessions.Single();
|
||||
var originalMindId = session.ContentData()!.Mind!.Value;
|
||||
var originalPlayerMindId = session.ContentData()!.Mind!.Value;
|
||||
|
||||
// Check that there are no ghosts
|
||||
Assert.That(entMan.Count<GhostComponent>(), Is.Zero);
|
||||
|
||||
// Spawn player entity & attach
|
||||
EntityUid originalMob = default;
|
||||
EntityUid originalPlayerMob = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
originalMob = entMan.SpawnEntity(null, mapData.GridCoords);
|
||||
mindSystem.TransferTo(originalMindId, originalMob, true);
|
||||
originalPlayerMob = entMan.SpawnEntity(TestMobProtoId, mapData.GridCoords);
|
||||
mindSystem.TransferTo(originalPlayerMindId, originalPlayerMob, true);
|
||||
});
|
||||
|
||||
// Check player got attached.
|
||||
await pair.RunTicksSync(10);
|
||||
Assert.That(session.AttachedEntity, Is.EqualTo(originalMob));
|
||||
var originalMind = entMan.GetComponent<MindComponent>(originalMindId);
|
||||
Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob));
|
||||
Assert.That(originalMind.VisitingEntity, Is.Null);
|
||||
var originalPlayerMind = entMan.GetComponent<MindComponent>(originalPlayerMindId);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// Check player got attached.
|
||||
Assert.That(session.AttachedEntity, Is.EqualTo(originalPlayerMob));
|
||||
Assert.That(originalPlayerMind.OwnedEntity, Is.EqualTo(originalPlayerMob));
|
||||
Assert.That(originalPlayerMind.VisitingEntity, Is.Null);
|
||||
Assert.That(originalPlayerMind.OriginalOwnerUserId, Is.EqualTo(session.UserId));
|
||||
|
||||
// Check that there are still no ghosts
|
||||
Assert.That(entMan.Count<GhostComponent>(), Is.Zero);
|
||||
});
|
||||
|
||||
// Use the ghost command
|
||||
conHost.ExecuteCommand("ghost");
|
||||
conHost.ExecuteCommand(ghostCommand);
|
||||
await pair.RunTicksSync(10);
|
||||
var ghost = session.AttachedEntity;
|
||||
Assert.That(entMan.HasComponent<GhostComponent>(ghost));
|
||||
Assert.That(ghost, Is.Not.EqualTo(originalMob));
|
||||
Assert.That(session.ContentData()?.Mind, Is.EqualTo(originalMindId));
|
||||
Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob), $"Original mob: {originalMob}, Ghost: {ghost}");
|
||||
Assert.That(originalMind.VisitingEntity, Is.EqualTo(ghost));
|
||||
var ghostOne = session.AttachedEntity;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// Assert that the ghost is a new entity with a new mind
|
||||
Assert.That(entMan.HasComponent<GhostComponent>(ghostOne));
|
||||
Assert.That(ghostOne, Is.Not.EqualTo(originalPlayerMob));
|
||||
Assert.That(session.ContentData()?.Mind, Is.EqualTo(originalPlayerMindId));
|
||||
if (adminGhost)
|
||||
{
|
||||
// aghost, so the player mob should still own the mind, but the mind is visiting the ghost.
|
||||
Assert.That(originalPlayerMind.OwnedEntity, Is.EqualTo(originalPlayerMob));
|
||||
Assert.That(originalPlayerMind.VisitingEntity, Is.EqualTo(ghostOne));
|
||||
Assert.That(originalPlayerMind.UserId, Is.EqualTo(session.UserId));
|
||||
}
|
||||
else
|
||||
{
|
||||
// player ghost, can't return. The mind is owned by the ghost, and is not visiting.
|
||||
Assert.That(originalPlayerMind.OwnedEntity, Is.EqualTo(ghostOne));
|
||||
Assert.That(originalPlayerMind.VisitingEntity, Is.Null);
|
||||
}
|
||||
|
||||
// Check that we're tracking the original owner for round end screen
|
||||
Assert.That(originalPlayerMind.OriginalOwnerUserId, Is.EqualTo(session.UserId));
|
||||
|
||||
// Check that there is only one ghost
|
||||
Assert.That(entMan.Count<GhostComponent>(), Is.EqualTo(1));
|
||||
});
|
||||
|
||||
// Spawn ghost takeover entity.
|
||||
EntityUid ghostRole = default;
|
||||
await server.WaitPost(() => ghostRole = entMan.SpawnEntity("GhostRoleTestEntity", mapData.GridCoords));
|
||||
await server.WaitPost(() => ghostRole = entMan.SpawnEntity(GhostRoleProtoId, mapData.GridCoords));
|
||||
|
||||
// Take the ghost role
|
||||
await server.WaitPost(() =>
|
||||
@@ -89,40 +130,118 @@ public sealed class GhostRoleTests
|
||||
|
||||
// Check player got attached to ghost role.
|
||||
await pair.RunTicksSync(10);
|
||||
var newMindId = session.ContentData()!.Mind!.Value;
|
||||
var newMind = entMan.GetComponent<MindComponent>(newMindId);
|
||||
Assert.That(newMindId, Is.Not.EqualTo(originalMindId));
|
||||
Assert.That(session.AttachedEntity, Is.EqualTo(ghostRole));
|
||||
Assert.That(newMind.OwnedEntity, Is.EqualTo(ghostRole));
|
||||
Assert.That(newMind.VisitingEntity, Is.Null);
|
||||
var ghostRoleMindId = session.ContentData()!.Mind!.Value;
|
||||
var ghostRoleMind = entMan.GetComponent<MindComponent>(ghostRoleMindId);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// Check that the ghost role mind is new
|
||||
Assert.That(ghostRoleMindId, Is.Not.EqualTo(originalPlayerMindId));
|
||||
|
||||
// Original mind should be unaffected, but the ghost will have deleted itself.
|
||||
Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob));
|
||||
Assert.That(originalMind.VisitingEntity, Is.Null);
|
||||
Assert.That(entMan.Deleted(ghost));
|
||||
// Check that the session and mind are properly attached to the ghost role
|
||||
Assert.That(session.AttachedEntity, Is.EqualTo(ghostRole));
|
||||
Assert.That(ghostRoleMind.OwnedEntity, Is.EqualTo(ghostRole));
|
||||
Assert.That(ghostRoleMind.VisitingEntity, Is.Null);
|
||||
|
||||
// Original mind should be unaffected, but the ghost will have deleted itself.
|
||||
if (adminGhost)
|
||||
{
|
||||
// aghost case, the original player mob should still own the mind, and that mind is not visiting.
|
||||
Assert.That(originalPlayerMind.OwnedEntity, Is.EqualTo(originalPlayerMob));
|
||||
}
|
||||
else
|
||||
{
|
||||
// player ghost case, the original mind is disconnected and not owned by an entity.
|
||||
// This mind cannot be returned to
|
||||
Assert.That(originalPlayerMind.OwnedEntity, Is.Null);
|
||||
}
|
||||
|
||||
// In either case the original player mind is not visiting anything, not connected to any user.
|
||||
Assert.That(originalPlayerMind.VisitingEntity, Is.Null);
|
||||
Assert.That(originalPlayerMind.UserId, Is.Null);
|
||||
|
||||
// Now the original owner of both minds should permanently be set to this session.
|
||||
Assert.That(originalPlayerMind.OriginalOwnerUserId, Is.EqualTo(session.UserId));
|
||||
Assert.That(ghostRoleMind.OriginalOwnerUserId, Is.EqualTo(session.UserId));
|
||||
|
||||
// Make sure that the ghost was deleted
|
||||
Assert.That(entMan.Deleted(ghostOne));
|
||||
|
||||
// Check that there is are no lingereing ghosts
|
||||
Assert.That(entMan.Count<GhostComponent>(), Is.Zero);
|
||||
});
|
||||
|
||||
// Ghost again.
|
||||
conHost.ExecuteCommand("ghost");
|
||||
conHost.ExecuteCommand(ghostCommand);
|
||||
await pair.RunTicksSync(10);
|
||||
var otherGhost = session.AttachedEntity;
|
||||
Assert.That(entMan.HasComponent<GhostComponent>(otherGhost));
|
||||
Assert.That(otherGhost, Is.Not.EqualTo(originalMob));
|
||||
Assert.That(otherGhost, Is.Not.EqualTo(ghostRole));
|
||||
Assert.That(session.ContentData()?.Mind, Is.EqualTo(newMindId));
|
||||
Assert.That(newMind.OwnedEntity, Is.EqualTo(ghostRole));
|
||||
Assert.That(newMind.VisitingEntity, Is.EqualTo(session.AttachedEntity));
|
||||
var ghostTwo = session.AttachedEntity;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// Check that the new ghost is a new entity
|
||||
Assert.That(entMan.HasComponent<GhostComponent>(ghostTwo));
|
||||
Assert.That(ghostTwo, Is.Not.EqualTo(originalPlayerMob));
|
||||
Assert.That(ghostTwo, Is.Not.EqualTo(ghostRole));
|
||||
Assert.That(session.ContentData()?.Mind, Is.EqualTo(ghostRoleMindId));
|
||||
|
||||
if(adminGhost)
|
||||
{
|
||||
// aghost case, the ghost role mind should be owned by the ghost role entity,
|
||||
// the ghost role mind is visiting the new ghost
|
||||
Assert.That(ghostRoleMind.OwnedEntity, Is.EqualTo(ghostRole));
|
||||
Assert.That(ghostRoleMind.VisitingEntity, Is.EqualTo(ghostTwo));
|
||||
}
|
||||
else
|
||||
{
|
||||
// player ghost, can't return. The mind is owned by the ghost, and is not visiting.
|
||||
Assert.That(ghostRoleMind.OwnedEntity, Is.EqualTo(ghostTwo));
|
||||
Assert.That(ghostRoleMind.VisitingEntity, Is.Null);
|
||||
}
|
||||
|
||||
// Check that the original mind is still not attached to a user
|
||||
Assert.That(originalPlayerMind.UserId, Is.Null);
|
||||
|
||||
// Check that original owners of other minds are still tracked
|
||||
Assert.That(originalPlayerMind.OriginalOwnerUserId, Is.EqualTo(session.UserId));
|
||||
Assert.That(ghostRoleMind.OriginalOwnerUserId, Is.EqualTo(session.UserId));
|
||||
|
||||
// Check that there is exactly one ghost
|
||||
Assert.That(entMan.Count<GhostComponent>(), Is.EqualTo(1));
|
||||
});
|
||||
|
||||
if (!adminGhost)
|
||||
{
|
||||
// End of the normal player ghost role test
|
||||
await pair.CleanReturnAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
// Next, control the original entity again:
|
||||
await server.WaitPost(() => mindSystem.SetUserId(originalMindId, session.UserId));
|
||||
await server.WaitPost(() => mindSystem.SetUserId(originalPlayerMindId, session.UserId));
|
||||
await pair.RunTicksSync(10);
|
||||
Assert.That(session.AttachedEntity, Is.EqualTo(originalMob));
|
||||
Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob));
|
||||
Assert.That(originalMind.VisitingEntity, Is.Null);
|
||||
|
||||
// the ghost-role mind is unaffected, though the ghost will have deleted itself
|
||||
Assert.That(newMind.OwnedEntity, Is.EqualTo(ghostRole));
|
||||
Assert.That(newMind.VisitingEntity, Is.Null);
|
||||
Assert.That(entMan.Deleted(otherGhost));
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// Check that we are attached
|
||||
Assert.That(session.AttachedEntity, Is.EqualTo(originalPlayerMob));
|
||||
|
||||
// Check the ownership of the original mind
|
||||
Assert.That(originalPlayerMind.OwnedEntity, Is.EqualTo(originalPlayerMob));
|
||||
Assert.That(originalPlayerMind.VisitingEntity, Is.Null);
|
||||
Assert.That(originalPlayerMind.UserId, Is.EqualTo(session.UserId));
|
||||
|
||||
// Check that the ghost-role mind is unaffected
|
||||
Assert.That(ghostRoleMind.OwnedEntity, Is.EqualTo(ghostRole));
|
||||
Assert.That(ghostRoleMind.VisitingEntity, Is.Null);
|
||||
|
||||
// Check that the second ghost is deleted
|
||||
Assert.That(entMan.Deleted(ghostTwo));
|
||||
|
||||
// Check that the original owners of the previous minds are still tracked
|
||||
Assert.That(originalPlayerMind.OriginalOwnerUserId, Is.EqualTo(session.UserId));
|
||||
Assert.That(ghostRoleMind.OriginalOwnerUserId, Is.EqualTo(session.UserId));
|
||||
|
||||
// Check that there is are no lingereing ghosts
|
||||
Assert.That(entMan.Count<GhostComponent>(), Is.Zero);
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user