Merge remote-tracking branch 'upstream/master' into ed-04-06-2024-upstream
# Conflicts: # Resources/Prototypes/Accents/word_replacements.yml
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Access.UI
|
||||
{
|
||||
@@ -40,7 +42,7 @@ namespace Content.Client.Access.UI
|
||||
SendMessage(new AgentIDCardJobChangedMessage(newJob));
|
||||
}
|
||||
|
||||
public void OnJobIconChanged(string newJobIconId)
|
||||
public void OnJobIconChanged(ProtoId<StatusIconPrototype> newJobIconId)
|
||||
{
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Content.Client.Access.UI
|
||||
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
|
||||
}
|
||||
|
||||
public void SetAllowedIcons(HashSet<string> icons, string currentJobIconId)
|
||||
public void SetAllowedIcons(HashSet<ProtoId<StatusIconPrototype>> icons, string currentJobIconId)
|
||||
{
|
||||
IconGrid.DisposeAllChildren();
|
||||
|
||||
@@ -46,10 +46,8 @@ namespace Content.Client.Access.UI
|
||||
var i = 0;
|
||||
foreach (var jobIconId in icons)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<StatusIconPrototype>(jobIconId, out var jobIcon))
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
|
||||
continue;
|
||||
}
|
||||
|
||||
String styleBase = StyleBase.ButtonOpenBoth;
|
||||
var modulo = i % JobIconColumnCount;
|
||||
@@ -77,7 +75,7 @@ namespace Content.Client.Access.UI
|
||||
};
|
||||
|
||||
jobIconButton.AddChild(jobIconTexture);
|
||||
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID);
|
||||
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIconId);
|
||||
IconGrid.AddChild(jobIconButton);
|
||||
|
||||
if (jobIconId.Equals(currentJobIconId))
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
using Content.Shared.Antag;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Antag;
|
||||
|
||||
/// <summary>
|
||||
/// Used for assigning specified icons for antags.
|
||||
/// </summary>
|
||||
public sealed class AntagStatusIconSystem : SharedStatusIconSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
|
||||
SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(GetIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetIcon);
|
||||
SubscribeLocalEvent<InitialInfectedComponent, GetStatusIconsEvent>(GetIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Status Icon on an entity if the player is supposed to see it.
|
||||
/// </summary>
|
||||
private void GetIcon<T>(EntityUid uid, T comp, ref GetStatusIconsEvent ev) where T: IAntagStatusIconComponent
|
||||
{
|
||||
var ent = _player.LocalSession?.AttachedEntity;
|
||||
|
||||
var canEv = new CanDisplayStatusIconsEvent(ent);
|
||||
RaiseLocalEvent(uid, ref canEv);
|
||||
|
||||
if (!canEv.Cancelled)
|
||||
ev.StatusIcons.Add(_prototype.Index(comp.StatusIcon));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Rev Icon on an entity if the player is supposed to see it. This additional function is needed to deal
|
||||
/// with a special case where if someone is a head rev we only want to display the headrev icon.
|
||||
/// </summary>
|
||||
private void GetRevIcon(EntityUid uid, RevolutionaryComponent comp, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(uid))
|
||||
return;
|
||||
|
||||
GetIcon(uid, comp, ref ev);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Rounding;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -150,6 +151,9 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance))
|
||||
return;
|
||||
|
||||
if (!TryComp<ItemComponent>(uid, out var item))
|
||||
return;
|
||||
|
||||
if (!AppearanceSystem.TryGetData<float>(uid, SolutionContainerVisuals.FillFraction, out var fraction, appearance))
|
||||
return;
|
||||
|
||||
@@ -159,7 +163,8 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
||||
{
|
||||
var layer = new PrototypeLayerData();
|
||||
|
||||
var key = "inhand-" + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite;
|
||||
var heldPrefix = item.HeldPrefix == null ? "inhand-" : $"{item.HeldPrefix}-inhand-";
|
||||
var key = heldPrefix + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite;
|
||||
|
||||
layer.State = key;
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Inventory.Events;
|
||||
|
||||
namespace Content.Client.Clothing.Systems;
|
||||
|
||||
public sealed class WaddleClothingSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<WaddleWhenWornComponent, GotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<WaddleWhenWornComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||
}
|
||||
|
||||
private void OnGotEquipped(EntityUid entity, WaddleWhenWornComponent comp, GotEquippedEvent args)
|
||||
{
|
||||
var waddleAnimComp = EnsureComp<WaddleAnimationComponent>(args.Equipee);
|
||||
|
||||
waddleAnimComp.AnimationLength = comp.AnimationLength;
|
||||
waddleAnimComp.HopIntensity = comp.HopIntensity;
|
||||
waddleAnimComp.RunAnimationLengthMultiplier = comp.RunAnimationLengthMultiplier;
|
||||
waddleAnimComp.TumbleIntensity = comp.TumbleIntensity;
|
||||
}
|
||||
|
||||
private void OnGotUnequipped(EntityUid entity, WaddleWhenWornComponent comp, GotUnequippedEvent args)
|
||||
{
|
||||
RemComp<WaddleAnimationComponent>(args.Equipee);
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ public sealed class ShowHealthBarsCommand : LocalizedCommands
|
||||
var showHealthBarsComponent = new ShowHealthBarsComponent
|
||||
{
|
||||
DamageContainers = args.ToList(),
|
||||
HealthStatusIcon = null,
|
||||
NetSyncEnabled = false
|
||||
};
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ namespace Content.Client.LateJoin
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
|
||||
var jobIcon = _prototypeManager.Index<StatusIconPrototype>(prototype.Icon);
|
||||
var jobIcon = _prototypeManager.Index(prototype.Icon);
|
||||
icon.Texture = _sprites.Frame0(jobIcon.Icon);
|
||||
jobSelector.AddChild(icon);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Client.Lobby.UI.Loadouts;
|
||||
using Content.Client.Lobby.UI.Roles;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared._CP14.Humanoid;
|
||||
using Content.Shared.CCVar;
|
||||
@@ -467,38 +468,96 @@ namespace Content.Client.Lobby.UI
|
||||
var traits = _prototypeManager.EnumeratePrototypes<TraitPrototype>().OrderBy(t => Loc.GetString(t.Name)).ToList();
|
||||
TabContainer.SetTabTitle(3, Loc.GetString("humanoid-profile-editor-traits-tab"));
|
||||
|
||||
if (traits.Count > 0)
|
||||
{
|
||||
foreach (var trait in traits)
|
||||
{
|
||||
var selector = new TraitPreferenceSelector(trait);
|
||||
|
||||
if (Profile?.TraitPreferences.Contains(trait.ID) == true)
|
||||
{
|
||||
selector.Preference = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
selector.Preference = false;
|
||||
}
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithTraitPreference(trait.ID, preference);
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
TraitsList.AddChild(selector);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (traits.Count < 1)
|
||||
{
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
// TODO: Localise
|
||||
Text = "No traits available :(",
|
||||
Text = Loc.GetString("humanoid-profile-editor-no-traits"),
|
||||
FontColorOverride = Color.Gray,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
//Setup model
|
||||
Dictionary<string, List<string>> model = new();
|
||||
List<string> defaultTraits = new();
|
||||
model.Add("default", defaultTraits);
|
||||
|
||||
foreach (var trait in traits)
|
||||
{
|
||||
if (trait.Category == null)
|
||||
{
|
||||
defaultTraits.Add(trait.ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!model.ContainsKey(trait.Category))
|
||||
{
|
||||
model.Add(trait.Category, new());
|
||||
}
|
||||
model[trait.Category].Add(trait.ID);
|
||||
}
|
||||
|
||||
//Create UI view from model
|
||||
foreach (var (categoryId, traitId) in model)
|
||||
{
|
||||
TraitCategoryPrototype? category = null;
|
||||
if (categoryId != "default")
|
||||
{
|
||||
category = _prototypeManager.Index<TraitCategoryPrototype>(categoryId);
|
||||
// Label
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString(category.Name),
|
||||
Margin = new Thickness(0, 10, 0, 0),
|
||||
StyleClasses = { StyleBase.StyleClassLabelHeading },
|
||||
});
|
||||
}
|
||||
|
||||
List<TraitPreferenceSelector?> selectors = new();
|
||||
var selectionCount = 0;
|
||||
|
||||
foreach (var traitProto in traitId)
|
||||
{
|
||||
var trait = _prototypeManager.Index<TraitPrototype>(traitProto);
|
||||
var selector = new TraitPreferenceSelector(trait);
|
||||
|
||||
selector.Preference = Profile?.TraitPreferences.Contains(trait.ID) == true;
|
||||
if (selector.Preference)
|
||||
selectionCount += trait.Cost;
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithTraitPreference(trait.ID, categoryId, preference);
|
||||
SetDirty();
|
||||
RefreshTraits(); // If too many traits are selected, they will be reset to the real value.
|
||||
};
|
||||
selectors.Add(selector);
|
||||
}
|
||||
|
||||
// Selection counter
|
||||
if (category is { MaxTraitPoints: >= 0 })
|
||||
{
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("humanoid-profile-editor-trait-count-hint", ("current", selectionCount) ,("max", category.MaxTraitPoints)),
|
||||
FontColorOverride = Color.Gray
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var selector in selectors)
|
||||
{
|
||||
if (selector == null)
|
||||
continue;
|
||||
|
||||
if (category is { MaxTraitPoints: >= 0 } &&
|
||||
selector.Cost + selectionCount > category.MaxTraitPoints)
|
||||
{
|
||||
selector.Checkbox.Label.FontColorOverride = Color.Red;
|
||||
}
|
||||
|
||||
TraitsList.AddChild(selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -807,7 +866,7 @@ namespace Content.Client.Lobby.UI
|
||||
TextureScale = new Vector2(2, 2),
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
var jobIcon = _prototypeManager.Index<StatusIconPrototype>(job.Icon);
|
||||
var jobIcon = _prototypeManager.Index(job.Icon);
|
||||
icon.Texture = jobIcon.Icon.Frame0();
|
||||
selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon);
|
||||
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<BoxContainer Name="Container"
|
||||
Orientation="Horizontal">
|
||||
<CheckBox Name="Checkbox"/>
|
||||
<CheckBox Name="Checkbox" Access="Public"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Content.Client.Lobby.UI.Roles;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class TraitPreferenceSelector : Control
|
||||
{
|
||||
public int Cost;
|
||||
|
||||
public bool Preference
|
||||
{
|
||||
get => Checkbox.Pressed;
|
||||
@@ -20,7 +22,12 @@ public sealed partial class TraitPreferenceSelector : Control
|
||||
public TraitPreferenceSelector(TraitPrototype trait)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
Checkbox.Text = Loc.GetString(trait.Name);
|
||||
|
||||
var text = trait.Cost != 0 ? $"[{trait.Cost}] " : "";
|
||||
text += Loc.GetString(trait.Name);
|
||||
|
||||
Cost = trait.Cost;
|
||||
Checkbox.Text = text;
|
||||
Checkbox.OnToggled += OnCheckBoxToggled;
|
||||
|
||||
if (trait.Description is { } desc)
|
||||
|
||||
@@ -2,94 +2,115 @@
|
||||
using Content.Client.Buckle;
|
||||
using Content.Client.Gravity;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Movement.Systems;
|
||||
|
||||
public sealed class WaddleAnimationSystem : EntitySystem
|
||||
public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
|
||||
[Dependency] private readonly GravitySystem _gravity = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
[Dependency] private readonly BuckleSystem _buckle = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, MoveInputEvent>(OnMovementInput);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StartedWaddlingEvent>(OnStartedWalking);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StoppedWaddlingEvent>(OnStoppedWalking);
|
||||
base.Initialize();
|
||||
|
||||
SubscribeAllEvent<StartedWaddlingEvent>(OnStartWaddling);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StunnedEvent>(OnStunned);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, KnockedDownEvent>(OnKnockedDown);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, BuckleChangeEvent>(OnBuckleChange);
|
||||
SubscribeAllEvent<StoppedWaddlingEvent>(OnStopWaddling);
|
||||
}
|
||||
|
||||
private void OnMovementInput(EntityUid entity, WaddleAnimationComponent component, MoveInputEvent args)
|
||||
private void OnStartWaddling(StartedWaddlingEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
// Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume
|
||||
// they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR.
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.HasDirectionalMovement && component.IsCurrentlyWaddling)
|
||||
{
|
||||
var stopped = new StoppedWaddlingEvent(entity);
|
||||
|
||||
RaiseLocalEvent(entity, ref stopped);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Only start waddling if we're not currently AND we're actually moving.
|
||||
if (component.IsCurrentlyWaddling || !args.HasDirectionalMovement)
|
||||
return;
|
||||
|
||||
var started = new StartedWaddlingEvent(entity);
|
||||
|
||||
RaiseLocalEvent(entity, ref started);
|
||||
if (TryComp<WaddleAnimationComponent>(GetEntity(msg.Entity), out var comp))
|
||||
StartWaddling((GetEntity(msg.Entity), comp));
|
||||
}
|
||||
|
||||
private void OnStartedWalking(EntityUid uid, WaddleAnimationComponent component, StartedWaddlingEvent args)
|
||||
private void OnStopWaddling(StoppedWaddlingEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (_animation.HasRunningAnimation(uid, component.KeyName))
|
||||
if (TryComp<WaddleAnimationComponent>(GetEntity(msg.Entity), out var comp))
|
||||
StopWaddling((GetEntity(msg.Entity), comp));
|
||||
}
|
||||
|
||||
private void StartWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
if (_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName))
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(uid, out var mover))
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
return;
|
||||
|
||||
if (_gravity.IsWeightless(uid))
|
||||
if (_gravity.IsWeightless(entity.Owner))
|
||||
return;
|
||||
|
||||
|
||||
if (!_actionBlocker.CanMove(uid, mover))
|
||||
if (!_actionBlocker.CanMove(entity.Owner, mover))
|
||||
return;
|
||||
|
||||
// Do nothing if buckled in
|
||||
if (_buckle.IsBuckled(uid))
|
||||
if (_buckle.IsBuckled(entity.Owner))
|
||||
return;
|
||||
|
||||
// Do nothing if crit or dead (for obvious reasons)
|
||||
if (_mobState.IsIncapacitated(uid))
|
||||
if (_mobState.IsIncapacitated(entity.Owner))
|
||||
return;
|
||||
|
||||
var tumbleIntensity = component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity;
|
||||
var len = mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength;
|
||||
PlayWaddleAnimationUsing(
|
||||
(entity.Owner, entity.Comp),
|
||||
CalculateAnimationLength(entity.Comp, mover),
|
||||
CalculateTumbleIntensity(entity.Comp)
|
||||
);
|
||||
}
|
||||
|
||||
component.LastStep = !component.LastStep;
|
||||
component.IsCurrentlyWaddling = true;
|
||||
private static float CalculateTumbleIntensity(WaddleAnimationComponent component)
|
||||
{
|
||||
return component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity;
|
||||
}
|
||||
|
||||
private static float CalculateAnimationLength(WaddleAnimationComponent component, InputMoverComponent mover)
|
||||
{
|
||||
return mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength;
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(Entity<WaddleAnimationComponent> entity, ref AnimationCompletedEvent args)
|
||||
{
|
||||
if (args.Key != entity.Comp.KeyName)
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
return;
|
||||
|
||||
PlayWaddleAnimationUsing(
|
||||
(entity.Owner, entity.Comp),
|
||||
CalculateAnimationLength(entity.Comp, mover),
|
||||
CalculateTumbleIntensity(entity.Comp)
|
||||
);
|
||||
}
|
||||
|
||||
private void StopWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
if (!_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName))
|
||||
return;
|
||||
|
||||
_animation.Stop(entity.Owner, entity.Comp.KeyName);
|
||||
|
||||
if (!TryComp<SpriteComponent>(entity.Owner, out var sprite))
|
||||
return;
|
||||
|
||||
sprite.Offset = new Vector2();
|
||||
sprite.Rotation = Angle.FromDegrees(0);
|
||||
}
|
||||
|
||||
private void PlayWaddleAnimationUsing(Entity<WaddleAnimationComponent> entity, float len, float tumbleIntensity)
|
||||
{
|
||||
entity.Comp.LastStep = !entity.Comp.LastStep;
|
||||
|
||||
var anim = new Animation()
|
||||
{
|
||||
@@ -116,58 +137,13 @@ public sealed class WaddleAnimationSystem : EntitySystem
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), 0),
|
||||
new AnimationTrackProperty.KeyFrame(component.HopIntensity, len/2),
|
||||
new AnimationTrackProperty.KeyFrame(entity.Comp.HopIntensity, len/2),
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), len/2),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_animation.Play(uid, anim, component.KeyName);
|
||||
}
|
||||
|
||||
private void OnStoppedWalking(EntityUid uid, WaddleAnimationComponent component, StoppedWaddlingEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(EntityUid uid, WaddleAnimationComponent component, AnimationCompletedEvent args)
|
||||
{
|
||||
var started = new StartedWaddlingEvent(uid);
|
||||
|
||||
RaiseLocalEvent(uid, ref started);
|
||||
}
|
||||
|
||||
private void OnStunned(EntityUid uid, WaddleAnimationComponent component, StunnedEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void OnKnockedDown(EntityUid uid, WaddleAnimationComponent component, KnockedDownEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void OnBuckleChange(EntityUid uid, WaddleAnimationComponent component, BuckleChangeEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void StopWaddling(EntityUid uid, WaddleAnimationComponent component)
|
||||
{
|
||||
if (!component.IsCurrentlyWaddling)
|
||||
return;
|
||||
|
||||
_animation.Stop(uid, component.KeyName);
|
||||
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sprite.Offset = new Vector2();
|
||||
sprite.Rotation = Angle.FromDegrees(0);
|
||||
|
||||
component.IsCurrentlyWaddling = false;
|
||||
_animation.Play(entity.Owner, anim, entity.Comp.KeyName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public sealed class TargetOutlineSystem : EntitySystem
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
|
||||
private bool _enabled = false;
|
||||
|
||||
@@ -137,7 +138,7 @@ public sealed class TargetOutlineSystem : EntitySystem
|
||||
|
||||
// check the entity whitelist
|
||||
if (valid && Whitelist != null)
|
||||
valid = Whitelist.IsValid(entity);
|
||||
valid = _whitelistSystem.IsWhitelistPass(Whitelist, entity);
|
||||
|
||||
// and check the cancellable event
|
||||
if (valid && ValidationEvent != null)
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.StatusIcon;
|
||||
using Content.Client.UserInterface.Systems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Shared.Maths.Color;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
@@ -19,19 +22,27 @@ namespace Content.Client.Overlays;
|
||||
public sealed class EntityHealthBarOverlay : Overlay
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IPrototypeManager _prototype;
|
||||
|
||||
private readonly SharedTransformSystem _transform;
|
||||
private readonly MobStateSystem _mobStateSystem;
|
||||
private readonly MobThresholdSystem _mobThresholdSystem;
|
||||
private readonly StatusIconSystem _statusIconSystem;
|
||||
private readonly ProgressColorSystem _progressColor;
|
||||
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
public HashSet<string> DamageContainers = new();
|
||||
public ProtoId<StatusIconPrototype>? StatusIcon;
|
||||
|
||||
public EntityHealthBarOverlay(IEntityManager entManager)
|
||||
public EntityHealthBarOverlay(IEntityManager entManager, IPrototypeManager prototype)
|
||||
{
|
||||
_entManager = entManager;
|
||||
_prototype = prototype;
|
||||
_transform = _entManager.System<SharedTransformSystem>();
|
||||
_mobStateSystem = _entManager.System<MobStateSystem>();
|
||||
_mobThresholdSystem = _entManager.System<MobThresholdSystem>();
|
||||
_statusIconSystem = _entManager.System<StatusIconSystem>();
|
||||
_progressColor = _entManager.System<ProgressColorSystem>();
|
||||
}
|
||||
|
||||
@@ -44,6 +55,7 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
const float scale = 1f;
|
||||
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation);
|
||||
_prototype.TryIndex(StatusIcon, out var statusIcon);
|
||||
|
||||
var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>();
|
||||
while (query.MoveNext(out var uid,
|
||||
@@ -52,31 +64,23 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
out var damageableComponent,
|
||||
out var spriteComponent))
|
||||
{
|
||||
if (_entManager.TryGetComponent<MetaDataComponent>(uid, out var metaDataComponent) &&
|
||||
metaDataComponent.Flags.HasFlag(MetaDataFlags.InContainer))
|
||||
{
|
||||
if (statusIcon != null && !_statusIconSystem.IsVisible((uid, _entManager.GetComponent<MetaDataComponent>(uid)), statusIcon))
|
||||
continue;
|
||||
}
|
||||
|
||||
// We want the stealth user to still be able to see his health bar himself
|
||||
if (!xformQuery.TryGetComponent(uid, out var xform) ||
|
||||
xform.MapID != args.MapId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (damageableComponent.DamageContainerID == null || !DamageContainers.Contains(damageableComponent.DamageContainerID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// we use the status icon component bounds if specified otherwise use sprite
|
||||
var bounds = _entManager.GetComponentOrNull<StatusIconComponent>(uid)?.Bounds ?? spriteComponent.Bounds;
|
||||
var worldPos = _transform.GetWorldPosition(xform, xformQuery);
|
||||
|
||||
if (!bounds.Translated(worldPos).Intersects(args.WorldAABB))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// we are all progressing towards death every day
|
||||
if (CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent) is not { } deathProgress)
|
||||
|
||||
@@ -19,10 +19,10 @@ public sealed class ShowCriminalRecordIconsSystem : EquipmentHudSystem<ShowCrimi
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, CriminalRecordComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.StatusIcon.Id, out var iconPrototype))
|
||||
if (_prototype.TryIndex(component.StatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Overlays;
|
||||
using Robust.Client.Graphics;
|
||||
using System.Linq;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
@@ -11,6 +13,7 @@ namespace Content.Client.Overlays;
|
||||
public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComponent>
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
private EntityHealthBarOverlay _overlay = default!;
|
||||
|
||||
@@ -18,16 +21,21 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_overlay = new(EntityManager);
|
||||
_overlay = new(EntityManager, _prototype);
|
||||
}
|
||||
|
||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component)
|
||||
{
|
||||
base.UpdateInternal(component);
|
||||
|
||||
foreach (var damageContainerId in component.Components.SelectMany(x => x.DamageContainers))
|
||||
foreach (var comp in component.Components)
|
||||
{
|
||||
_overlay.DamageContainers.Add(damageContainerId);
|
||||
foreach (var damageContainerId in comp.DamageContainers)
|
||||
{
|
||||
_overlay.DamageContainers.Add(damageContainerId);
|
||||
}
|
||||
|
||||
_overlay.StatusIcon = comp.HealthStatusIcon;
|
||||
}
|
||||
|
||||
if (!_overlayMan.HasOverlay<EntityHealthBarOverlay>())
|
||||
|
||||
@@ -24,7 +24,6 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DamageableComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
|
||||
}
|
||||
|
||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthIconsComponent> component)
|
||||
@@ -46,7 +45,7 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
||||
|
||||
private void OnGetStatusIconsEvent(Entity<DamageableComponent> entity, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (!IsActive || args.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
var healthIcons = DecideHealthIcons(entity);
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed class ShowHungerIconsSystem : EquipmentHudSystem<ShowHungerIconsCo
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_hunger.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
|
||||
@@ -25,7 +25,7 @@ public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponen
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
var iconId = JobIconForNoId;
|
||||
|
||||
@@ -19,10 +19,10 @@ public sealed class ShowMindShieldIconsSystem : EquipmentHudSystem<ShowMindShiel
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.MindShieldStatusIcon.Id, out var iconPrototype))
|
||||
if (_prototype.TryIndex(component.MindShieldStatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,10 @@ public sealed class ShowSyndicateIconsSystem : EquipmentHudSystem<ShowSyndicateI
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.SyndStatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsCo
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_thirst.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
|
||||
@@ -1,44 +1,37 @@
|
||||
using Content.Shared.Antag;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Revolutionary;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Revolutionary;
|
||||
|
||||
/// <summary>
|
||||
/// Used for the client to get status icons from other revs.
|
||||
/// </summary>
|
||||
public sealed class RevolutionarySystem : EntitySystem
|
||||
public sealed class RevolutionarySystem : SharedRevolutionarySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RevolutionaryComponent, CanDisplayStatusIconsEvent>(OnCanShowRevIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, CanDisplayStatusIconsEvent>(OnCanShowRevIcon);
|
||||
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetHeadRevIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether a client should display the rev icon.
|
||||
/// </summary>
|
||||
private void OnCanShowRevIcon<T>(EntityUid uid, T comp, ref CanDisplayStatusIconsEvent args) where T : IAntagStatusIconComponent
|
||||
private void GetRevIcon(Entity<RevolutionaryComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
args.Cancelled = !CanDisplayIcon(args.User, comp.IconVisibleToGhost);
|
||||
if (HasComp<HeadRevolutionaryComponent>(ent))
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The criteria that determine whether a client should see Rev/Head rev icons.
|
||||
/// </summary>
|
||||
private bool CanDisplayIcon(EntityUid? uid, bool visibleToGhost)
|
||||
private void GetHeadRevIcon(Entity<HeadRevolutionaryComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(uid) || HasComp<RevolutionaryComponent>(uid))
|
||||
return true;
|
||||
|
||||
if (visibleToGhost && HasComp<GhostComponent>(uid))
|
||||
return true;
|
||||
|
||||
return HasComp<ShowRevIconsComponent>(uid);
|
||||
if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,13 +30,12 @@ public sealed class SSDIndicatorSystem : EntitySystem
|
||||
{
|
||||
if (component.IsSSD &&
|
||||
_cfg.GetCVar(CCVars.ICShowSSDIndicator) &&
|
||||
!args.InContainer &&
|
||||
!_mobState.IsDead(uid) &&
|
||||
!HasComp<ActiveNPCComponent>(uid) &&
|
||||
TryComp<MindContainerComponent>(uid, out var mindContainer) &&
|
||||
mindContainer.ShowExamineInfo)
|
||||
{
|
||||
args.StatusIcons.Add(_prototype.Index<StatusIconPrototype>(component.Icon));
|
||||
args.StatusIcons.Add(_prototype.Index(component.Icon));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public sealed class StatusIconOverlay : Overlay
|
||||
var query = _entity.AllEntityQueryEnumerator<StatusIconComponent, SpriteComponent, TransformComponent, MetaDataComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp, out var sprite, out var xform, out var meta))
|
||||
{
|
||||
if (xform.MapID != args.MapId)
|
||||
if (xform.MapID != args.MapId || !sprite.Visible)
|
||||
continue;
|
||||
|
||||
var bounds = comp.Bounds ?? sprite.Bounds;
|
||||
@@ -72,6 +72,8 @@ public sealed class StatusIconOverlay : Overlay
|
||||
|
||||
foreach (var proto in icons)
|
||||
{
|
||||
if (!_statusIcon.IsVisible((uid, meta), proto))
|
||||
continue;
|
||||
|
||||
var curTime = _timing.RealTime;
|
||||
var texture = _sprite.GetFrame(proto.Icon, curTime);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.StatusIcon;
|
||||
@@ -13,6 +17,8 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
|
||||
|
||||
private bool _globalEnabled;
|
||||
private bool _localEnabled;
|
||||
@@ -54,10 +60,34 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
|
||||
if (meta.EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
return list;
|
||||
|
||||
var inContainer = (meta.Flags & MetaDataFlags.InContainer) != 0;
|
||||
var ev = new GetStatusIconsEvent(list, inContainer);
|
||||
var ev = new GetStatusIconsEvent(list);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
return ev.StatusIcons;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For overlay to check if an entity can be seen.
|
||||
/// </summary>
|
||||
public bool IsVisible(Entity<MetaDataComponent> ent, StatusIconData data)
|
||||
{
|
||||
var viewer = _playerManager.LocalSession?.AttachedEntity;
|
||||
|
||||
// Always show our icons to our entity
|
||||
if (viewer == ent.Owner)
|
||||
return true;
|
||||
|
||||
if (data.VisibleToGhosts && HasComp<GhostComponent>(viewer))
|
||||
return true;
|
||||
|
||||
if (data.HideInContainer && (ent.Comp.Flags & MetaDataFlags.InContainer) != 0)
|
||||
return false;
|
||||
|
||||
if (data.HideOnStealth && TryComp<StealthComponent>(ent, out var stealth) && stealth.Enabled)
|
||||
return false;
|
||||
|
||||
if (data.ShowTo != null && !_entityWhitelist.IsValid(data.ShowTo, viewer))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Client.Interactable.Components;
|
||||
using Content.Client.StatusIcon;
|
||||
using Content.Shared.Stealth;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -18,6 +19,7 @@ public sealed class StealthSystem : SharedStealthSystem
|
||||
base.Initialize();
|
||||
|
||||
_shader = _protoMan.Index<ShaderPrototype>("Stealth").InstanceUnique();
|
||||
|
||||
SubscribeLocalEvent<StealthComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<StealthComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<StealthComponent, BeforePostShaderRenderEvent>(OnShaderRender);
|
||||
@@ -93,4 +95,3 @@ public sealed class StealthSystem : SharedStealthSystem
|
||||
args.Sprite.Color = new Color(visibility, visibility, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Content.Shared.Atmos.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
||||
{
|
||||
@@ -30,7 +29,7 @@ namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window = new GasTankWindow(this);
|
||||
_window = new GasTankWindow(this, EntMan.GetComponent<MetaDataComponent>(Owner).EntityName);
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
@@ -10,201 +10,194 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank;
|
||||
|
||||
public sealed class GasTankWindow
|
||||
: BaseWindow
|
||||
{
|
||||
public sealed class GasTankWindow
|
||||
: BaseWindow
|
||||
private readonly RichTextLabel _lblPressure;
|
||||
private readonly FloatSpinBox _spbPressure;
|
||||
private readonly RichTextLabel _lblInternals;
|
||||
private readonly Button _btnInternals;
|
||||
|
||||
public GasTankWindow(GasTankBoundUserInterface owner, string uidName)
|
||||
{
|
||||
private GasTankBoundUserInterface _owner;
|
||||
private readonly Label _lblName;
|
||||
private readonly BoxContainer _topContainer;
|
||||
private readonly Control _contentContainer;
|
||||
Control contentContainer;
|
||||
BoxContainer topContainer;
|
||||
TextureButton btnClose;
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
var rootContainer = new LayoutContainer { Name = "GasTankRoot" };
|
||||
AddChild(rootContainer);
|
||||
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
|
||||
private readonly IResourceCache _resourceCache = default!;
|
||||
private readonly RichTextLabel _lblPressure;
|
||||
private readonly FloatSpinBox _spbPressure;
|
||||
private readonly RichTextLabel _lblInternals;
|
||||
private readonly Button _btnInternals;
|
||||
|
||||
public GasTankWindow(GasTankBoundUserInterface owner)
|
||||
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
{
|
||||
TextureButton btnClose;
|
||||
_resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
_owner = owner;
|
||||
var rootContainer = new LayoutContainer {Name = "GasTankRoot"};
|
||||
AddChild(rootContainer);
|
||||
Texture = panelTex,
|
||||
Modulate = Color.FromHex("#25252A"),
|
||||
};
|
||||
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
|
||||
var panelTex = _resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
var topPanel = new PanelContainer
|
||||
{
|
||||
PanelOverride = back,
|
||||
MouseFilter = MouseFilterMode.Pass
|
||||
};
|
||||
|
||||
var bottomWrap = new LayoutContainer
|
||||
{
|
||||
Name = "BottomWrap"
|
||||
};
|
||||
|
||||
rootContainer.AddChild(topPanel);
|
||||
rootContainer.AddChild(bottomWrap);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(topPanel, LayoutContainer.LayoutPreset.Wide);
|
||||
LayoutContainer.SetMarginBottom(topPanel, -85);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(bottomWrap, LayoutContainer.LayoutPreset.VerticalCenterWide);
|
||||
LayoutContainer.SetGrowHorizontal(bottomWrap, LayoutContainer.GrowDirection.Both);
|
||||
|
||||
|
||||
var topContainerWrap = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
{
|
||||
Texture = panelTex,
|
||||
Modulate = Color.FromHex("#25252A"),
|
||||
};
|
||||
|
||||
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
|
||||
var topPanel = new PanelContainer
|
||||
{
|
||||
PanelOverride = back,
|
||||
MouseFilter = MouseFilterMode.Pass
|
||||
};
|
||||
|
||||
var bottomWrap = new LayoutContainer
|
||||
{
|
||||
Name = "BottomWrap"
|
||||
};
|
||||
|
||||
rootContainer.AddChild(topPanel);
|
||||
rootContainer.AddChild(bottomWrap);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(topPanel, LayoutContainer.LayoutPreset.Wide);
|
||||
LayoutContainer.SetMarginBottom(topPanel, -85);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(bottomWrap, LayoutContainer.LayoutPreset.VerticalCenterWide);
|
||||
LayoutContainer.SetGrowHorizontal(bottomWrap, LayoutContainer.GrowDirection.Both);
|
||||
|
||||
|
||||
var topContainerWrap = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
(topContainer = new BoxContainer
|
||||
{
|
||||
(_topContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
}),
|
||||
new Control {MinSize = new Vector2(0, 110)}
|
||||
}
|
||||
};
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
}),
|
||||
new Control {MinSize = new Vector2(0, 110)}
|
||||
}
|
||||
};
|
||||
|
||||
rootContainer.AddChild(topContainerWrap);
|
||||
rootContainer.AddChild(topContainerWrap);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(topContainerWrap, LayoutContainer.LayoutPreset.Wide);
|
||||
LayoutContainer.SetAnchorPreset(topContainerWrap, LayoutContainer.LayoutPreset.Wide);
|
||||
|
||||
var font = _resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13);
|
||||
var font = resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13);
|
||||
|
||||
var topRow = new BoxContainer
|
||||
var topRow = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Margin = new Thickness(4, 2, 12, 2),
|
||||
Children =
|
||||
{
|
||||
(new Label
|
||||
{
|
||||
Text = uidName,
|
||||
FontOverride = font,
|
||||
FontColorOverride = StyleNano.NanoGold,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
HorizontalAlignment = HAlignment.Left,
|
||||
Margin = new Thickness(0, 0, 20, 0),
|
||||
}),
|
||||
(btnClose = new TextureButton
|
||||
{
|
||||
StyleClasses = {DefaultWindow.StyleClassWindowCloseButton},
|
||||
VerticalAlignment = VAlignment.Center
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var middle = new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#202025") },
|
||||
Children =
|
||||
{
|
||||
(contentContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(8, 4),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
topContainer.AddChild(topRow);
|
||||
topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") }
|
||||
});
|
||||
topContainer.AddChild(middle);
|
||||
topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") }
|
||||
});
|
||||
|
||||
|
||||
_lblPressure = new RichTextLabel();
|
||||
contentContainer.AddChild(_lblPressure);
|
||||
|
||||
//internals
|
||||
_lblInternals = new RichTextLabel
|
||||
{ MinSize = new Vector2(200, 0), VerticalAlignment = VAlignment.Center };
|
||||
_btnInternals = new Button { Text = Loc.GetString("gas-tank-window-internals-toggle-button") };
|
||||
|
||||
contentContainer.AddChild(
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Margin = new Thickness(4, 2, 12, 2),
|
||||
Children =
|
||||
{
|
||||
(_lblName = new Label
|
||||
{
|
||||
Text = Loc.GetString("gas-tank-window-label"),
|
||||
FontOverride = font,
|
||||
FontColorOverride = StyleNano.NanoGold,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
HorizontalAlignment = HAlignment.Left,
|
||||
Margin = new Thickness(0, 0, 20, 0),
|
||||
}),
|
||||
(btnClose = new TextureButton
|
||||
{
|
||||
StyleClasses = {DefaultWindow.StyleClassWindowCloseButton},
|
||||
VerticalAlignment = VAlignment.Center
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var middle = new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#202025")},
|
||||
Children =
|
||||
{
|
||||
(_contentContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(8, 4),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
_topContainer.AddChild(topRow);
|
||||
_topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#525252ff")}
|
||||
});
|
||||
_topContainer.AddChild(middle);
|
||||
_topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#525252ff")}
|
||||
Margin = new Thickness(0, 7, 0, 0),
|
||||
Children = { _lblInternals, _btnInternals }
|
||||
});
|
||||
|
||||
|
||||
_lblPressure = new RichTextLabel();
|
||||
_contentContainer.AddChild(_lblPressure);
|
||||
|
||||
//internals
|
||||
_lblInternals = new RichTextLabel
|
||||
{MinSize = new Vector2(200, 0), VerticalAlignment = VAlignment.Center};
|
||||
_btnInternals = new Button {Text = Loc.GetString("gas-tank-window-internals-toggle-button") };
|
||||
|
||||
_contentContainer.AddChild(
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Margin = new Thickness(0, 7, 0, 0),
|
||||
Children = {_lblInternals, _btnInternals}
|
||||
});
|
||||
|
||||
// Separator
|
||||
_contentContainer.AddChild(new Control
|
||||
{
|
||||
MinSize = new Vector2(0, 10)
|
||||
});
|
||||
|
||||
_contentContainer.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("gas-tank-window-output-pressure-label"),
|
||||
Align = Label.AlignMode.Center
|
||||
});
|
||||
_spbPressure = new FloatSpinBox
|
||||
{
|
||||
IsValid = f => f >= 0 || f <= 3000,
|
||||
Margin = new Thickness(25, 0, 25, 7)
|
||||
};
|
||||
_contentContainer.AddChild(_spbPressure);
|
||||
|
||||
// Handlers
|
||||
_spbPressure.OnValueChanged += args =>
|
||||
{
|
||||
_owner.SetOutputPressure(args.Value);
|
||||
};
|
||||
|
||||
_btnInternals.OnPressed += args =>
|
||||
{
|
||||
_owner.ToggleInternals();
|
||||
};
|
||||
|
||||
btnClose.OnPressed += _ => Close();
|
||||
}
|
||||
|
||||
public void UpdateState(GasTankBoundUserInterfaceState state)
|
||||
// Separator
|
||||
contentContainer.AddChild(new Control
|
||||
{
|
||||
_lblPressure.SetMarkup(Loc.GetString("gas-tank-window-tank-pressure-text", ("tankPressure", $"{state.TankPressure:0.##}")));
|
||||
_btnInternals.Disabled = !state.CanConnectInternals;
|
||||
_lblInternals.SetMarkup(Loc.GetString("gas-tank-window-internal-text",
|
||||
("status", Loc.GetString(state.InternalsConnected ? "gas-tank-window-internal-connected" : "gas-tank-window-internal-disconnected"))));
|
||||
if (state.OutputPressure.HasValue)
|
||||
{
|
||||
_spbPressure.Value = state.OutputPressure.Value;
|
||||
}
|
||||
}
|
||||
MinSize = new Vector2(0, 10)
|
||||
});
|
||||
|
||||
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
|
||||
contentContainer.AddChild(new Label
|
||||
{
|
||||
return DragMode.Move;
|
||||
}
|
||||
Text = Loc.GetString("gas-tank-window-output-pressure-label"),
|
||||
Align = Label.AlignMode.Center
|
||||
});
|
||||
_spbPressure = new FloatSpinBox
|
||||
{
|
||||
IsValid = f => f >= 0 || f <= 3000,
|
||||
Margin = new Thickness(25, 0, 25, 7)
|
||||
};
|
||||
contentContainer.AddChild(_spbPressure);
|
||||
|
||||
protected override bool HasPoint(Vector2 point)
|
||||
// Handlers
|
||||
_spbPressure.OnValueChanged += args =>
|
||||
{
|
||||
return false;
|
||||
owner.SetOutputPressure(args.Value);
|
||||
};
|
||||
|
||||
_btnInternals.OnPressed += args =>
|
||||
{
|
||||
owner.ToggleInternals();
|
||||
};
|
||||
|
||||
btnClose.OnPressed += _ => Close();
|
||||
}
|
||||
|
||||
public void UpdateState(GasTankBoundUserInterfaceState state)
|
||||
{
|
||||
_lblPressure.SetMarkup(Loc.GetString("gas-tank-window-tank-pressure-text", ("tankPressure", $"{state.TankPressure:0.##}")));
|
||||
_btnInternals.Disabled = !state.CanConnectInternals;
|
||||
_lblInternals.SetMarkup(Loc.GetString("gas-tank-window-internal-text",
|
||||
("status", Loc.GetString(state.InternalsConnected ? "gas-tank-window-internal-connected" : "gas-tank-window-internal-disconnected"))));
|
||||
if (state.OutputPressure.HasValue)
|
||||
{
|
||||
_spbPressure.Value = state.OutputPressure.Value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
|
||||
{
|
||||
return DragMode.Move;
|
||||
}
|
||||
|
||||
protected override bool HasPoint(Vector2 point)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,40 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Zombies;
|
||||
|
||||
public sealed class ZombieSystem : EntitySystem
|
||||
public sealed class ZombieSystem : SharedZombieSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ZombieComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<ZombieComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons);
|
||||
SubscribeLocalEvent<InitialInfectedComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons);
|
||||
SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(GetZombieIcon);
|
||||
SubscribeLocalEvent<InitialInfectedComponent, GetStatusIconsEvent>(GetInitialInfectedIcon);
|
||||
}
|
||||
|
||||
private void GetZombieIcon(Entity<ZombieComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
var iconPrototype = _prototype.Index(ent.Comp.StatusIcon);
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
private void GetInitialInfectedIcon(Entity<InitialInfectedComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<ZombieComponent>(ent))
|
||||
return;
|
||||
|
||||
var iconPrototype = _prototype.Index(ent.Comp.StatusIcon);
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartup args)
|
||||
@@ -31,29 +50,4 @@ public sealed class ZombieSystem : EntitySystem
|
||||
sprite.LayerSetColor(i, component.SkinColor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a player should be able to see the StatusIcon for zombies.
|
||||
/// </summary>
|
||||
private void OnCanDisplayStatusIcons(EntityUid uid, ZombieComponent component, ref CanDisplayStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<ZombieComponent>(args.User) || HasComp<InitialInfectedComponent>(args.User) || HasComp<ShowZombieIconsComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (component.IconVisibleToGhost && HasComp<GhostComponent>(args.User))
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnCanDisplayStatusIcons(EntityUid uid, InitialInfectedComponent component, ref CanDisplayStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<InitialInfectedComponent>(args.User) && !HasComp<ZombieComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (component.IconVisibleToGhost && HasComp<GhostComponent>(args.User))
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user