Merge remote-tracking branch 'upstream/master' into ed-20-11-2024-upstream-sync
# Conflicts: # Content.Server/Chemistry/EntitySystems/InjectorSystem.cs # Content.Server/Traits/TraitSystem.cs # Content.Shared/CCVar/CCVars.cs # Content.Shared/Inventory/InventorySystem.Relay.cs # Resources/Maps/box.yml # Resources/Maps/core.yml
This commit is contained in:
@@ -22,11 +22,11 @@ namespace Content.Client.Administration.UI.BanPanel;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
public event Action<string?, (IPAddress, int)?, bool, byte[]?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||
public event Action<string?, (IPAddress, int)?, bool, ImmutableTypedHwid?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||
public event Action<string>? PlayerChanged;
|
||||
private string? PlayerUsername { get; set; }
|
||||
private (IPAddress, int)? IpAddress { get; set; }
|
||||
private byte[]? Hwid { get; set; }
|
||||
private ImmutableTypedHwid? Hwid { get; set; }
|
||||
private double TimeEntered { get; set; }
|
||||
private uint Multiplier { get; set; }
|
||||
private bool HasBanFlag { get; set; }
|
||||
@@ -371,9 +371,8 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
private void OnHwidChanged()
|
||||
{
|
||||
var hwidString = HwidLine.Text;
|
||||
var length = 3 * (hwidString.Length / 4) - hwidString.TakeLast(2).Count(c => c == '=');
|
||||
Hwid = new byte[length];
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !Convert.TryFromBase64String(hwidString, Hwid, out _))
|
||||
ImmutableTypedHwid? hwid = null;
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !ImmutableTypedHwid.TryParse(hwidString, out hwid))
|
||||
{
|
||||
ErrorLevel |= ErrorLevelEnum.Hwid;
|
||||
HwidLine.ModulateSelfOverride = Color.Red;
|
||||
@@ -390,7 +389,7 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
Hwid = null;
|
||||
return;
|
||||
}
|
||||
Hwid = Convert.FromHexString(hwidString);
|
||||
Hwid = hwid;
|
||||
}
|
||||
|
||||
private void OnTypeChanged()
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<Label Name="ExpiryLabel" Text="{Loc admin-note-editor-expiry-label}" Visible="False" />
|
||||
<HistoryLineEdit Name="ExpiryLineEdit" PlaceHolder="{Loc admin-note-editor-expiry-placeholder}"
|
||||
Visible="False" HorizontalExpand="True" />
|
||||
<OptionButton Name="ExpiryLengthDropdown" Visible="False" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<OptionButton Name="TypeOption" HorizontalAlignment="Center" />
|
||||
|
||||
@@ -17,6 +17,17 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
|
||||
private enum Multipliers
|
||||
{
|
||||
Minutes,
|
||||
Hours,
|
||||
Days,
|
||||
Weeks,
|
||||
Months,
|
||||
Years,
|
||||
Centuries
|
||||
}
|
||||
|
||||
public event Action<int, NoteType, string, NoteSeverity?, bool, DateTime?>? SubmitPressed;
|
||||
|
||||
public NoteEdit(SharedAdminNote? note, string playerName, bool canCreate, bool canEdit)
|
||||
@@ -31,6 +42,20 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
|
||||
ResetSubmitButton();
|
||||
|
||||
// It's weird to use minutes as the IDs, but it works and makes sense kind of :)
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-minutes"), (int) Multipliers.Minutes);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-hours"), (int) Multipliers.Hours);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-days"), (int) Multipliers.Days);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-weeks"), (int) Multipliers.Weeks);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-months"), (int) Multipliers.Months);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-years"), (int) Multipliers.Years);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-centuries"), (int) Multipliers.Centuries);
|
||||
ExpiryLengthDropdown.OnItemSelected += OnLengthChanged;
|
||||
|
||||
ExpiryLengthDropdown.SelectId((int) Multipliers.Weeks);
|
||||
|
||||
ExpiryLineEdit.OnTextChanged += OnTextChanged;
|
||||
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-note"), (int) NoteType.Note);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-message"), (int) NoteType.Message);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-watchlist"), (int) NoteType.Watchlist);
|
||||
@@ -172,8 +197,9 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
{
|
||||
ExpiryLabel.Visible = !PermanentCheckBox.Pressed;
|
||||
ExpiryLineEdit.Visible = !PermanentCheckBox.Pressed;
|
||||
ExpiryLengthDropdown.Visible = !PermanentCheckBox.Pressed;
|
||||
|
||||
ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") : string.Empty;
|
||||
ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? 1.ToString() : string.Empty;
|
||||
}
|
||||
|
||||
private void OnSecretPressed(BaseButton.ButtonEventArgs _)
|
||||
@@ -187,6 +213,16 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
SeverityOption.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnLengthChanged(OptionButton.ItemSelectedEventArgs args)
|
||||
{
|
||||
ExpiryLengthDropdown.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnTextChanged(HistoryLineEdit.LineEditEventArgs args)
|
||||
{
|
||||
ParseExpiryTime();
|
||||
}
|
||||
|
||||
private void OnSubmitButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
if (!ParseExpiryTime())
|
||||
@@ -263,13 +299,24 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !DateTime.TryParse(ExpiryLineEdit.Text, out var result) || DateTime.UtcNow > result)
|
||||
if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !uint.TryParse(ExpiryLineEdit.Text, out var inputInt))
|
||||
{
|
||||
ExpiryLineEdit.ModulateSelfOverride = Color.Red;
|
||||
return false;
|
||||
}
|
||||
|
||||
ExpiryTime = result.ToUniversalTime();
|
||||
var mult = ExpiryLengthDropdown.SelectedId switch
|
||||
{
|
||||
(int) Multipliers.Minutes => TimeSpan.FromMinutes(1).TotalMinutes,
|
||||
(int) Multipliers.Hours => TimeSpan.FromHours(1).TotalMinutes,
|
||||
(int) Multipliers.Days => TimeSpan.FromDays(1).TotalMinutes,
|
||||
(int) Multipliers.Weeks => TimeSpan.FromDays(7).TotalMinutes,
|
||||
(int) Multipliers.Months => TimeSpan.FromDays(30).TotalMinutes,
|
||||
(int) Multipliers.Years => TimeSpan.FromDays(365).TotalMinutes,
|
||||
(int) Multipliers.Centuries => TimeSpan.FromDays(36525).TotalMinutes,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(ExpiryLengthDropdown.SelectedId), "Multiplier out of range :(")
|
||||
};
|
||||
ExpiryTime = DateTime.UtcNow.AddMinutes(inputInt * mult);
|
||||
ExpiryLineEdit.ModulateSelfOverride = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Linq;
|
||||
using Content.Shared.Alert;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -24,8 +25,7 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
|
||||
SubscribeLocalEvent<AlertsComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<AlertsComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
|
||||
SubscribeLocalEvent<AlertsComponent, AfterAutoHandleStateEvent>(ClientAlertsHandleState);
|
||||
SubscribeLocalEvent<AlertsComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
protected override void LoadPrototypes()
|
||||
{
|
||||
@@ -47,6 +47,16 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<AlertsComponent> alerts, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not AlertComponentState cast)
|
||||
return;
|
||||
|
||||
alerts.Comp.Alerts = cast.Alerts;
|
||||
|
||||
UpdateHud(alerts);
|
||||
}
|
||||
|
||||
protected override void AfterShowAlert(Entity<AlertsComponent> alerts)
|
||||
{
|
||||
UpdateHud(alerts);
|
||||
@@ -57,11 +67,6 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
UpdateHud(alerts);
|
||||
}
|
||||
|
||||
private void ClientAlertsHandleState(Entity<AlertsComponent> alerts, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateHud(alerts);
|
||||
}
|
||||
|
||||
private void UpdateHud(Entity<AlertsComponent> entity)
|
||||
{
|
||||
if (_playerManager.LocalEntity == entity.Owner)
|
||||
|
||||
@@ -65,6 +65,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ClothingComponent, GetEquipmentVisualsEvent>(OnGetVisuals);
|
||||
SubscribeLocalEvent<ClothingComponent, InventoryTemplateUpdated>(OnInventoryTemplateUpdated);
|
||||
|
||||
SubscribeLocalEvent<InventoryComponent, VisualsChangedEvent>(OnVisualsChanged);
|
||||
SubscribeLocalEvent<SpriteComponent, DidUnequipEvent>(OnDidUnequip);
|
||||
@@ -77,11 +78,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
var enumerator = _inventorySystem.GetSlotEnumerator((uid, component));
|
||||
while (enumerator.NextItem(out var item, out var slot))
|
||||
{
|
||||
RenderEquipment(uid, item, slot.Name, component);
|
||||
}
|
||||
UpdateAllSlots(uid, component);
|
||||
|
||||
// No clothing equipped -> make sure the layer is hidden, though this should already be handled by on-unequip.
|
||||
if (args.Sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var layer))
|
||||
@@ -91,6 +88,23 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInventoryTemplateUpdated(Entity<ClothingComponent> ent, ref InventoryTemplateUpdated args)
|
||||
{
|
||||
UpdateAllSlots(ent.Owner, clothing: ent.Comp);
|
||||
}
|
||||
|
||||
private void UpdateAllSlots(
|
||||
EntityUid uid,
|
||||
InventoryComponent? inventoryComponent = null,
|
||||
ClothingComponent? clothing = null)
|
||||
{
|
||||
var enumerator = _inventorySystem.GetSlotEnumerator((uid, inventoryComponent));
|
||||
while (enumerator.NextItem(out var item, out var slot))
|
||||
{
|
||||
RenderEquipment(uid, item, slot.Name, inventoryComponent, clothingComponent: clothing);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGetVisuals(EntityUid uid, ClothingComponent item, GetEquipmentVisualsEvent args)
|
||||
{
|
||||
if (!TryComp(args.Equipee, out InventoryComponent? inventory))
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.4" Margin="0 0 5 0">
|
||||
<BoxContainer Orientation="Vertical" MinWidth="243" Margin="0 0 5 0">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 0 0 5">
|
||||
<LineEdit Name="SearchBar" PlaceHolder="Search" HorizontalExpand="True"/>
|
||||
<OptionButton Name="OptionCategories" Access="Public" MinSize="130 0"/>
|
||||
</BoxContainer>
|
||||
<ItemList Name="Recipes" Access="Public" SelectMode="Single" VerticalExpand="True"/>
|
||||
<ScrollContainer Name="RecipesGridScrollContainer" VerticalExpand="True" Access="Public" Visible="False">
|
||||
<GridContainer Name="RecipesGrid" Columns="5" Access="Public"/>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.6">
|
||||
<Button Name="FavoriteButton" Visible="false" HorizontalExpand="False"
|
||||
HorizontalAlignment="Right" Margin="0 0 0 15"/>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="MenuGridViewButton" ToggleMode="True" Text="{Loc construction-menu-grid-view}"/>
|
||||
<Button Name="FavoriteButton" Visible="false"/>
|
||||
</BoxContainer>
|
||||
<Control>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="0 0 0 5">
|
||||
<BoxContainer Orientation="Horizontal" Align="Center">
|
||||
|
||||
@@ -25,11 +25,16 @@ namespace Content.Client.Construction.UI
|
||||
OptionButton OptionCategories { get; }
|
||||
|
||||
bool EraseButtonPressed { get; set; }
|
||||
bool GridViewButtonPressed { get; set; }
|
||||
bool BuildButtonPressed { get; set; }
|
||||
|
||||
ItemList Recipes { get; }
|
||||
ItemList RecipeStepList { get; }
|
||||
|
||||
|
||||
ScrollContainer RecipesGridScrollContainer { get; }
|
||||
GridContainer RecipesGrid { get; }
|
||||
|
||||
event EventHandler<(string search, string catagory)> PopulateRecipes;
|
||||
event EventHandler<ItemList.Item?> RecipeSelected;
|
||||
event EventHandler RecipeFavorited;
|
||||
@@ -72,9 +77,16 @@ namespace Content.Client.Construction.UI
|
||||
set => EraseButton.Pressed = value;
|
||||
}
|
||||
|
||||
public bool GridViewButtonPressed
|
||||
{
|
||||
get => MenuGridViewButton.Pressed;
|
||||
set => MenuGridViewButton.Pressed = value;
|
||||
}
|
||||
|
||||
public ConstructionMenu()
|
||||
{
|
||||
SetSize = MinSize = new Vector2(720, 320);
|
||||
SetSize = new Vector2(560, 450);
|
||||
MinSize = new Vector2(560, 320);
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
@@ -102,6 +114,9 @@ namespace Content.Client.Construction.UI
|
||||
EraseButton.OnToggled += args => EraseButtonToggled?.Invoke(this, args.Pressed);
|
||||
|
||||
FavoriteButton.OnPressed += args => RecipeFavorited?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
MenuGridViewButton.OnPressed += _ =>
|
||||
PopulateRecipes?.Invoke(this, (SearchBar.Text, Categories[OptionCategories.SelectedId]));
|
||||
}
|
||||
|
||||
public event EventHandler? ClearAllGhosts;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Systems.MenuBar.Widgets;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -11,7 +12,6 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
@@ -33,10 +33,12 @@ namespace Content.Client.Construction.UI
|
||||
|
||||
private readonly IConstructionMenuView _constructionView;
|
||||
private readonly EntityWhitelistSystem _whitelistSystem;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
|
||||
private ConstructionSystem? _constructionSystem;
|
||||
private ConstructionPrototype? _selected;
|
||||
private List<ConstructionPrototype> _favoritedRecipes = [];
|
||||
private Dictionary<string, TextureButton> _recipeButtons = new();
|
||||
private string _selectedCategory = string.Empty;
|
||||
private string _favoriteCatName = "construction-category-favorites";
|
||||
private string _forAllCategoryName = "construction-category-all";
|
||||
@@ -85,6 +87,7 @@ namespace Content.Client.Construction.UI
|
||||
IoCManager.InjectDependencies(this);
|
||||
_constructionView = new ConstructionMenu();
|
||||
_whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||
|
||||
// This is required so that if we load after the system is initialized, we can bind to it immediately
|
||||
if (_systemManager.TryGetEntitySystem<ConstructionSystem>(out var constructionSystem))
|
||||
@@ -150,12 +153,24 @@ namespace Content.Client.Construction.UI
|
||||
PopulateInfo(_selected);
|
||||
}
|
||||
|
||||
private void OnGridViewRecipeSelected(object? sender, ConstructionPrototype? recipe)
|
||||
{
|
||||
if (recipe is null)
|
||||
{
|
||||
_selected = null;
|
||||
_constructionView.ClearRecipeInfo();
|
||||
return;
|
||||
}
|
||||
|
||||
_selected = recipe;
|
||||
if (_placementManager.IsActive && !_placementManager.Eraser) UpdateGhostPlacement();
|
||||
PopulateInfo(_selected);
|
||||
}
|
||||
|
||||
private void OnViewPopulateRecipes(object? sender, (string search, string catagory) args)
|
||||
{
|
||||
var (search, category) = args;
|
||||
var recipesList = _constructionView.Recipes;
|
||||
|
||||
recipesList.Clear();
|
||||
var recipes = new List<ConstructionPrototype>();
|
||||
|
||||
var isEmptyCategory = string.IsNullOrEmpty(category) || category == _forAllCategoryName;
|
||||
@@ -204,12 +219,73 @@ namespace Content.Client.Construction.UI
|
||||
|
||||
recipes.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCulture));
|
||||
|
||||
foreach (var recipe in recipes)
|
||||
{
|
||||
recipesList.Add(GetItem(recipe, recipesList));
|
||||
}
|
||||
var recipesList = _constructionView.Recipes;
|
||||
recipesList.Clear();
|
||||
|
||||
// There is apparently no way to set which
|
||||
var recipesGrid = _constructionView.RecipesGrid;
|
||||
recipesGrid.RemoveAllChildren();
|
||||
|
||||
_constructionView.RecipesGridScrollContainer.Visible = _constructionView.GridViewButtonPressed;
|
||||
_constructionView.Recipes.Visible = !_constructionView.GridViewButtonPressed;
|
||||
|
||||
if (_constructionView.GridViewButtonPressed)
|
||||
{
|
||||
foreach (var recipe in recipes)
|
||||
{
|
||||
var itemButton = new TextureButton
|
||||
{
|
||||
TextureNormal = _spriteSystem.Frame0(recipe.Icon),
|
||||
VerticalAlignment = Control.VAlignment.Center,
|
||||
Name = recipe.Name,
|
||||
ToolTip = recipe.Name,
|
||||
Scale = new Vector2(1.35f),
|
||||
ToggleMode = true,
|
||||
};
|
||||
var itemButtonPanelContainer = new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = StyleNano.ButtonColorDefault },
|
||||
Children = { itemButton },
|
||||
};
|
||||
|
||||
itemButton.OnToggled += buttonToggledEventArgs =>
|
||||
{
|
||||
SelectGridButton(itemButton, buttonToggledEventArgs.Pressed);
|
||||
|
||||
if (buttonToggledEventArgs.Pressed &&
|
||||
_selected != null &&
|
||||
_recipeButtons.TryGetValue(_selected.Name, out var oldButton))
|
||||
{
|
||||
oldButton.Pressed = false;
|
||||
SelectGridButton(oldButton, false);
|
||||
}
|
||||
|
||||
OnGridViewRecipeSelected(this, buttonToggledEventArgs.Pressed ? recipe : null);
|
||||
};
|
||||
|
||||
recipesGrid.AddChild(itemButtonPanelContainer);
|
||||
_recipeButtons[recipe.Name] = itemButton;
|
||||
var isCurrentButtonSelected = _selected == recipe;
|
||||
itemButton.Pressed = isCurrentButtonSelected;
|
||||
SelectGridButton(itemButton, isCurrentButtonSelected);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var recipe in recipes)
|
||||
{
|
||||
recipesList.Add(GetItem(recipe, recipesList));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectGridButton(TextureButton button, bool select)
|
||||
{
|
||||
if (button.Parent is not PanelContainer buttonPanel)
|
||||
return;
|
||||
|
||||
button.Modulate = select ? Color.Green : Color.White;
|
||||
var buttonColor = select ? StyleNano.ButtonColorDefault : Color.Transparent;
|
||||
buttonPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = buttonColor };
|
||||
}
|
||||
|
||||
private void PopulateCategories(string? selectCategory = null)
|
||||
@@ -260,11 +336,10 @@ namespace Content.Client.Construction.UI
|
||||
|
||||
private void PopulateInfo(ConstructionPrototype prototype)
|
||||
{
|
||||
var spriteSys = _systemManager.GetEntitySystem<SpriteSystem>();
|
||||
_constructionView.ClearRecipeInfo();
|
||||
|
||||
_constructionView.SetRecipeInfo(
|
||||
prototype.Name, prototype.Description, spriteSys.Frame0(prototype.Icon),
|
||||
prototype.Name, prototype.Description, _spriteSystem.Frame0(prototype.Icon),
|
||||
prototype.Type != ConstructionType.Item,
|
||||
!_favoritedRecipes.Contains(prototype));
|
||||
|
||||
@@ -277,7 +352,6 @@ namespace Content.Client.Construction.UI
|
||||
if (_constructionSystem?.GetGuide(prototype) is not { } guide)
|
||||
return;
|
||||
|
||||
var spriteSys = _systemManager.GetEntitySystem<SpriteSystem>();
|
||||
|
||||
foreach (var entry in guide.Entries)
|
||||
{
|
||||
@@ -293,20 +367,20 @@ namespace Content.Client.Construction.UI
|
||||
// The padding needs to be applied regardless of text length... (See PadLeft documentation)
|
||||
text = text.PadLeft(text.Length + entry.Padding);
|
||||
|
||||
var icon = entry.Icon != null ? spriteSys.Frame0(entry.Icon) : Texture.Transparent;
|
||||
var icon = entry.Icon != null ? _spriteSystem.Frame0(entry.Icon) : Texture.Transparent;
|
||||
stepList.AddItem(text, icon, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static ItemList.Item GetItem(ConstructionPrototype recipe, ItemList itemList)
|
||||
private ItemList.Item GetItem(ConstructionPrototype recipe, ItemList itemList)
|
||||
{
|
||||
return new(itemList)
|
||||
{
|
||||
Metadata = recipe,
|
||||
Text = recipe.Name,
|
||||
Icon = recipe.Icon.Frame0(),
|
||||
Icon = _spriteSystem.Frame0(recipe.Icon),
|
||||
TooltipEnabled = true,
|
||||
TooltipText = recipe.Description
|
||||
TooltipText = recipe.Description,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Content.Client.Crayon.UI
|
||||
private void PopulateCrayons()
|
||||
{
|
||||
var crayonDecals = _protoManager.EnumeratePrototypes<DecalPrototype>().Where(x => x.Tags.Contains("crayon"));
|
||||
_menu?.Populate(crayonDecals);
|
||||
_menu?.Populate(crayonDecals.ToList());
|
||||
}
|
||||
|
||||
public override void OnProtoReload(PrototypesReloadedEventArgs args)
|
||||
@@ -44,6 +44,16 @@ namespace Content.Client.Crayon.UI
|
||||
PopulateCrayons();
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
base.ReceiveMessage(message);
|
||||
|
||||
if (_menu is null || message is not CrayonUsedMessage crayonMessage)
|
||||
return;
|
||||
|
||||
_menu.AdvanceState(crayonMessage.DrawnDecal);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
Title="{Loc 'crayon-window-title'}"
|
||||
MinSize="250 300"
|
||||
SetSize="250 300">
|
||||
MinSize="450 500"
|
||||
SetSize="450 500">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<ColorSelectorSliders Name="ColorSelector" Visible="False" />
|
||||
<LineEdit Name="Search" />
|
||||
<LineEdit Name="Search" Margin="0 0 0 8" PlaceHolder="{Loc 'crayon-window-placeholder'}" />
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<GridContainer Name="Grid" Columns="6">
|
||||
<!-- Crayon decals get added here by code -->
|
||||
</GridContainer>
|
||||
<BoxContainer Name="Grids" Orientation="Vertical">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Crayon;
|
||||
using Content.Shared.Decals;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
@@ -18,7 +20,12 @@ namespace Content.Client.Crayon.UI
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CrayonWindow : DefaultWindow
|
||||
{
|
||||
private Dictionary<string, Texture>? _decals;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
private readonly SpriteSystem _spriteSystem = default!;
|
||||
|
||||
private Dictionary<string, List<(string Name, Texture Texture)>>? _decals;
|
||||
private List<string>? _allDecals;
|
||||
private string? _autoSelected;
|
||||
private string? _selected;
|
||||
private Color _color;
|
||||
|
||||
@@ -28,8 +35,10 @@ namespace Content.Client.Crayon.UI
|
||||
public CrayonWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
|
||||
|
||||
Search.OnTextChanged += _ => RefreshList();
|
||||
Search.OnTextChanged += SearchChanged;
|
||||
ColorSelector.OnColorChanged += SelectColor;
|
||||
}
|
||||
|
||||
@@ -44,51 +53,94 @@ namespace Content.Client.Crayon.UI
|
||||
private void RefreshList()
|
||||
{
|
||||
// Clear
|
||||
Grid.DisposeAllChildren();
|
||||
if (_decals == null)
|
||||
Grids.DisposeAllChildren();
|
||||
|
||||
if (_decals == null || _allDecals == null)
|
||||
return;
|
||||
|
||||
var filter = Search.Text;
|
||||
foreach (var (decal, tex) in _decals)
|
||||
var comma = filter.IndexOf(',');
|
||||
var first = (comma == -1 ? filter : filter[..comma]).Trim();
|
||||
|
||||
var names = _decals.Keys.ToList();
|
||||
names.Sort((a, b) => a == "random" ? 1 : b == "random" ? -1 : a.CompareTo(b));
|
||||
|
||||
if (_autoSelected != null && first != _autoSelected && _allDecals.Contains(first))
|
||||
{
|
||||
if (!decal.Contains(filter))
|
||||
_selected = first;
|
||||
_autoSelected = _selected;
|
||||
OnSelected?.Invoke(_selected);
|
||||
}
|
||||
|
||||
foreach (var categoryName in names)
|
||||
{
|
||||
var locName = Loc.GetString("crayon-category-" + categoryName);
|
||||
var category = _decals[categoryName].Where(d => locName.Contains(first) || d.Name.Contains(first)).ToList();
|
||||
|
||||
if (category.Count == 0)
|
||||
continue;
|
||||
|
||||
var button = new TextureButton()
|
||||
var label = new Label
|
||||
{
|
||||
TextureNormal = tex,
|
||||
Name = decal,
|
||||
ToolTip = decal,
|
||||
Modulate = _color,
|
||||
Text = locName
|
||||
};
|
||||
button.OnPressed += ButtonOnPressed;
|
||||
if (_selected == decal)
|
||||
|
||||
var grid = new GridContainer
|
||||
{
|
||||
var panelContainer = new PanelContainer()
|
||||
Columns = 6,
|
||||
Margin = new Thickness(0, 0, 0, 16)
|
||||
};
|
||||
|
||||
Grids.AddChild(label);
|
||||
Grids.AddChild(grid);
|
||||
|
||||
foreach (var (name, texture) in category)
|
||||
{
|
||||
var button = new TextureButton()
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = StyleNano.ButtonColorDefault,
|
||||
},
|
||||
Children =
|
||||
{
|
||||
button,
|
||||
},
|
||||
TextureNormal = texture,
|
||||
Name = name,
|
||||
ToolTip = name,
|
||||
Modulate = _color,
|
||||
Scale = new System.Numerics.Vector2(2, 2)
|
||||
};
|
||||
Grid.AddChild(panelContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Grid.AddChild(button);
|
||||
button.OnPressed += ButtonOnPressed;
|
||||
|
||||
if (_selected == name)
|
||||
{
|
||||
var panelContainer = new PanelContainer()
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = StyleNano.ButtonColorDefault,
|
||||
},
|
||||
Children =
|
||||
{
|
||||
button,
|
||||
},
|
||||
};
|
||||
grid.AddChild(panelContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
grid.AddChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SearchChanged(LineEdit.LineEditEventArgs obj)
|
||||
{
|
||||
_autoSelected = ""; // Placeholder to kick off the auto-select in refreshlist()
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
private void ButtonOnPressed(ButtonEventArgs obj)
|
||||
{
|
||||
if (obj.Button.Name == null) return;
|
||||
|
||||
_selected = obj.Button.Name;
|
||||
_autoSelected = null;
|
||||
OnSelected?.Invoke(_selected);
|
||||
RefreshList();
|
||||
}
|
||||
@@ -107,12 +159,38 @@ namespace Content.Client.Crayon.UI
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
public void Populate(IEnumerable<DecalPrototype> prototypes)
|
||||
public void AdvanceState(string drawnDecal)
|
||||
{
|
||||
_decals = new Dictionary<string, Texture>();
|
||||
var filter = Search.Text;
|
||||
if (!filter.Contains(',') || !filter.Contains(drawnDecal))
|
||||
return;
|
||||
|
||||
var first = filter[..filter.IndexOf(',')].Trim();
|
||||
|
||||
if (first.Equals(drawnDecal, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
Search.Text = filter[(filter.IndexOf(',') + 1)..].Trim();
|
||||
_autoSelected = first;
|
||||
}
|
||||
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
public void Populate(List<DecalPrototype> prototypes)
|
||||
{
|
||||
_decals = [];
|
||||
_allDecals = [];
|
||||
|
||||
prototypes.Sort((a, b) => a.ID.CompareTo(b.ID));
|
||||
|
||||
foreach (var decalPrototype in prototypes)
|
||||
{
|
||||
_decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0());
|
||||
var category = "random";
|
||||
if (decalPrototype.Tags.Count > 1 && decalPrototype.Tags[1].StartsWith("crayon-"))
|
||||
category = decalPrototype.Tags[1].Replace("crayon-", "");
|
||||
var list = _decals.GetOrNew(category);
|
||||
list.Add((decalPrototype.ID, _spriteSystem.Frame0(decalPrototype.Sprite)));
|
||||
_allDecals.Add(decalPrototype.ID);
|
||||
}
|
||||
|
||||
RefreshList();
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Content.Client.Info
|
||||
AddInfoButton("server-info-website-button", CCVars.InfoLinksWebsite);
|
||||
AddInfoButton("server-info-wiki-button", CCVars.InfoLinksWiki);
|
||||
AddInfoButton("server-info-forum-button", CCVars.InfoLinksForum);
|
||||
AddInfoButton("server-info-telegram-button", CCVars.InfoLinksTelegram);
|
||||
|
||||
var guidebookController = UserInterfaceManager.GetUIController<GuidebookUIController>();
|
||||
var guidebookButton = new Button() { Text = Loc.GetString("server-info-guidebook-button") };
|
||||
|
||||
@@ -235,9 +235,23 @@ namespace Content.Client.Inventory
|
||||
EntityManager.RaisePredictiveEvent(new InteractInventorySlotEvent(GetNetEntity(item.Value), altInteract: true));
|
||||
}
|
||||
|
||||
protected override void UpdateInventoryTemplate(Entity<InventoryComponent> ent)
|
||||
{
|
||||
base.UpdateInventoryTemplate(ent);
|
||||
|
||||
if (TryComp(ent, out InventorySlotsComponent? inventorySlots))
|
||||
{
|
||||
foreach (var slot in ent.Comp.Slots)
|
||||
{
|
||||
if (inventorySlots.SlotData.TryGetValue(slot.Name, out var slotData))
|
||||
slotData.SlotDef = slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SlotData
|
||||
{
|
||||
public readonly SlotDefinition SlotDef;
|
||||
public SlotDefinition SlotDef;
|
||||
public EntityUid? HeldEntity => Container?.ContainedEntity;
|
||||
public bool Blocked;
|
||||
public bool Highlighted;
|
||||
|
||||
@@ -17,6 +17,7 @@ using Content.Shared.Inventory.VirtualItem;
|
||||
using Content.Shared.Strip.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
@@ -29,10 +30,13 @@ namespace Content.Client.Inventory
|
||||
[UsedImplicitly]
|
||||
public sealed class StrippableBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
||||
|
||||
private readonly ExamineSystem _examine;
|
||||
private readonly InventorySystem _inv;
|
||||
private readonly SharedCuffableSystem _cuffable;
|
||||
private readonly StrippableSystem _strippable;
|
||||
|
||||
[ViewVariables]
|
||||
private const int ButtonSeparation = 4;
|
||||
@@ -51,6 +55,8 @@ namespace Content.Client.Inventory
|
||||
_examine = EntMan.System<ExamineSystem>();
|
||||
_inv = EntMan.System<InventorySystem>();
|
||||
_cuffable = EntMan.System<SharedCuffableSystem>();
|
||||
_strippable = EntMan.System<StrippableSystem>();
|
||||
|
||||
_virtualHiddenEntity = EntMan.SpawnEntity(HiddenPocketEntityId, MapCoordinates.Nullspace);
|
||||
}
|
||||
|
||||
@@ -198,7 +204,8 @@ namespace Content.Client.Inventory
|
||||
var entity = container.ContainedEntity;
|
||||
|
||||
// If this is a full pocket, obscure the real entity
|
||||
if (entity != null && slotDef.StripHidden)
|
||||
// this does not work for modified clients because they are still sent the real entity
|
||||
if (entity != null && _strippable.IsStripHidden(slotDef, _player.LocalEntity))
|
||||
entity = _virtualHiddenEntity;
|
||||
|
||||
var button = new SlotButton(new SlotData(slotDef, container));
|
||||
|
||||
@@ -279,7 +279,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
|
||||
_profileEditor.OnOpenGuidebook += _guide.OpenHelp;
|
||||
|
||||
_characterSetup = new CharacterSetupGui(EntityManager, _prototypeManager, _resourceCache, _preferencesManager, _profileEditor);
|
||||
_characterSetup = new CharacterSetupGui(_profileEditor);
|
||||
|
||||
_characterSetup.CloseButton.OnPressed += _ =>
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:style="clr-namespace:Content.Client.Stylesheets"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
VerticalExpand="True">
|
||||
<Control>
|
||||
<PanelContainer Name="BackgroundPanel" />
|
||||
@@ -10,10 +11,15 @@
|
||||
<Label Text="{Loc 'character-setup-gui-character-setup-label'}"
|
||||
Margin="8 0 0 0" VAlign="Center"
|
||||
StyleClasses="LabelHeadingBigger" />
|
||||
|
||||
<Button Name="StatsButton" HorizontalExpand="True"
|
||||
Text="{Loc 'character-setup-gui-character-setup-stats-button'}"
|
||||
StyleClasses="ButtonBig"
|
||||
HorizontalAlignment="Right" />
|
||||
<cc:CommandButton Name="AdminRemarksButton"
|
||||
Command="adminremarks"
|
||||
Text="{Loc 'character-setup-gui-character-setup-adminremarks-button'}"
|
||||
StyleClasses="ButtonBig" />
|
||||
<Button Name="RulesButton"
|
||||
Text="{Loc 'character-setup-gui-character-setup-rules-button'}"
|
||||
StyleClasses="ButtonBig"/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Info.PlaytimeStats;
|
||||
using Content.Client.Resources;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -8,6 +9,7 @@ using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
@@ -18,28 +20,23 @@ namespace Content.Client.Lobby.UI
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CharacterSetupGui : Control
|
||||
{
|
||||
private readonly IClientPreferencesManager _preferencesManager;
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IPrototypeManager _protomanager;
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protomanager = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
private readonly Button _createNewCharacterButton;
|
||||
|
||||
public event Action<int>? SelectCharacter;
|
||||
public event Action<int>? DeleteCharacter;
|
||||
|
||||
public CharacterSetupGui(
|
||||
IEntityManager entManager,
|
||||
IPrototypeManager protoManager,
|
||||
IResourceCache resourceCache,
|
||||
IClientPreferencesManager preferencesManager,
|
||||
HumanoidProfileEditor profileEditor)
|
||||
public CharacterSetupGui(HumanoidProfileEditor profileEditor)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_preferencesManager = preferencesManager;
|
||||
_entManager = entManager;
|
||||
_protomanager = protoManager;
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var panelTex = _resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
{
|
||||
Texture = panelTex,
|
||||
@@ -56,7 +53,7 @@ namespace Content.Client.Lobby.UI
|
||||
|
||||
_createNewCharacterButton.OnPressed += args =>
|
||||
{
|
||||
preferencesManager.CreateCharacter(HumanoidCharacterProfile.Random());
|
||||
_preferencesManager.CreateCharacter(HumanoidCharacterProfile.Random());
|
||||
ReloadCharacterPickers();
|
||||
args.Event.Handle();
|
||||
};
|
||||
@@ -65,6 +62,8 @@ namespace Content.Client.Lobby.UI
|
||||
RulesButton.OnPressed += _ => new RulesAndInfoWindow().Open();
|
||||
|
||||
StatsButton.OnPressed += _ => new PlaytimeStatsWindow().OpenCentered();
|
||||
|
||||
_cfg.OnValueChanged(CCVars.SeeOwnNotes, p => AdminRemarksButton.Visible = p, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Client.Message;
|
||||
using Content.Client.UserInterface.Systems.EscapeMenu;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
|
||||
[ViewVariables]
|
||||
protected bool IsActive;
|
||||
protected virtual SlotFlags TargetSlots => ~SlotFlags.POCKET;
|
||||
|
||||
@@ -102,7 +103,7 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
|
||||
args.Components.Add(component);
|
||||
}
|
||||
|
||||
private void RefreshOverlay(EntityUid uid)
|
||||
protected void RefreshOverlay(EntityUid uid)
|
||||
{
|
||||
if (uid != _player.LocalSession?.AttachedEntity)
|
||||
return;
|
||||
|
||||
@@ -21,9 +21,16 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ShowHealthBarsComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
|
||||
_overlay = new(EntityManager, _prototype);
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<ShowHealthBarsComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
RefreshOverlay(ent);
|
||||
}
|
||||
|
||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component)
|
||||
{
|
||||
base.UpdateInternal(component);
|
||||
|
||||
@@ -17,6 +17,7 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public HashSet<string> DamageContainers = new();
|
||||
|
||||
public override void Initialize()
|
||||
@@ -24,6 +25,7 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DamageableComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
SubscribeLocalEvent<ShowHealthIconsComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
}
|
||||
|
||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthIconsComponent> component)
|
||||
@@ -43,6 +45,11 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
||||
DamageContainers.Clear();
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<ShowHealthIconsComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
RefreshOverlay(ent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(Entity<DamageableComponent> entity, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (!IsActive)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Pulling.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -14,6 +17,8 @@ public sealed class MoverController : SharedMoverController
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -135,4 +140,15 @@ public sealed class MoverController : SharedMoverController
|
||||
{
|
||||
return _timing is { IsFirstTimePredicted: true, InSimulation: true };
|
||||
}
|
||||
|
||||
public override void SetSprinting(Entity<InputMoverComponent> entity, ushort subTick, bool walking)
|
||||
{
|
||||
// Logger.Info($"[{_gameTiming.CurTick}/{subTick}] Sprint: {enabled}");
|
||||
base.SetSprinting(entity, subTick, walking);
|
||||
|
||||
if (walking && _cfg.GetCVar(CCVars.ToggleWalk))
|
||||
_alerts.ShowAlert(entity, WalkingAlert, showCooldown: false, autoRemove: false);
|
||||
else
|
||||
_alerts.ClearAlert(entity, WalkingAlert);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,8 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
_modules.Clear();
|
||||
foreach (var module in chassis.ModuleContainer.ContainedEntities)
|
||||
{
|
||||
var control = new BorgModuleControl(module, _entity);
|
||||
var moduleComponent = _entity.GetComponent<BorgModuleComponent>(module);
|
||||
var control = new BorgModuleControl(module, _entity, !moduleComponent.DefaultModule);
|
||||
control.RemoveButtonPressed += () =>
|
||||
{
|
||||
RemoveModuleButtonPressed?.Invoke(module);
|
||||
|
||||
@@ -9,7 +9,7 @@ public sealed partial class BorgModuleControl : PanelContainer
|
||||
{
|
||||
public Action? RemoveButtonPressed;
|
||||
|
||||
public BorgModuleControl(EntityUid entity, IEntityManager entityManager)
|
||||
public BorgModuleControl(EntityUid entity, IEntityManager entityManager, bool canRemove)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
@@ -20,6 +20,7 @@ public sealed partial class BorgModuleControl : PanelContainer
|
||||
{
|
||||
RemoveButtonPressed?.Invoke();
|
||||
};
|
||||
RemoveButton.Visible = canRemove;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
43
Content.Client/Silicons/Borgs/BorgSelectTypeMenu.xaml
Normal file
43
Content.Client/Silicons/Borgs/BorgSelectTypeMenu.xaml
Normal file
@@ -0,0 +1,43 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Title="{Loc 'borg-select-type-menu-title'}"
|
||||
SetSize="550 300">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
|
||||
<!-- Left pane: selection of borg type -->
|
||||
<BoxContainer Orientation="Vertical" MinWidth="200" Margin="2 0">
|
||||
<Label Text="{Loc 'borg-select-type-menu-available'}" StyleClasses="LabelHeading" />
|
||||
<ScrollContainer HScrollEnabled="False" VerticalExpand="True">
|
||||
<BoxContainer Name="SelectionsContainer" Orientation="Vertical" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<customControls:VSeparator />
|
||||
|
||||
<!-- Right pane: information about selected borg module, confirm button. -->
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="2 0">
|
||||
<Label Text="{Loc 'borg-select-type-menu-information'}" StyleClasses="LabelHeading" />
|
||||
<Control VerticalExpand="True">
|
||||
<controls:Placeholder Name="InfoPlaceholder" PlaceholderText="{Loc 'borg-select-type-menu-select-type'}" />
|
||||
<BoxContainer Name="InfoContents" Orientation="Vertical" Visible="False">
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 0 0 4">
|
||||
<EntityPrototypeView Name="ChassisView" Scale="2,2" />
|
||||
<Label Name="NameLabel" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
|
||||
<RichTextLabel Name="DescriptionLabel" VerticalExpand="True" VerticalAlignment="Top" />
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
<controls:ConfirmButton Name="ConfirmTypeButton" Text="{Loc 'borg-select-type-menu-confirm'}"
|
||||
Disabled="True" HorizontalAlignment="Right"
|
||||
MinWidth="200" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<controls:StripeBack Margin="0 0 0 4">
|
||||
<Label Text="{Loc 'borg-select-type-menu-bottom-text'}" HorizontalAlignment="Center" StyleClasses="LabelSubText" Margin="4 4 0 4"/>
|
||||
</controls:StripeBack>
|
||||
</BoxContainer>
|
||||
|
||||
</controls:FancyWindow>
|
||||
81
Content.Client/Silicons/Borgs/BorgSelectTypeMenu.xaml.cs
Normal file
81
Content.Client/Silicons/Borgs/BorgSelectTypeMenu.xaml.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System.Linq;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared.Guidebook;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Silicons.Borgs;
|
||||
|
||||
/// <summary>
|
||||
/// Menu used by borgs to select their type.
|
||||
/// </summary>
|
||||
/// <seealso cref="BorgSelectTypeUserInterface"/>
|
||||
/// <seealso cref="BorgSwitchableTypeComponent"/>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BorgSelectTypeMenu : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private BorgTypePrototype? _selectedBorgType;
|
||||
|
||||
public event Action<ProtoId<BorgTypePrototype>>? ConfirmedBorgType;
|
||||
|
||||
[ValidatePrototypeId<GuideEntryPrototype>]
|
||||
private static readonly List<ProtoId<GuideEntryPrototype>> GuidebookEntries = new() { "Cyborgs", "Robotics" };
|
||||
|
||||
public BorgSelectTypeMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var group = new ButtonGroup();
|
||||
foreach (var borgType in _prototypeManager.EnumeratePrototypes<BorgTypePrototype>().OrderBy(PrototypeName))
|
||||
{
|
||||
var button = new Button
|
||||
{
|
||||
Text = PrototypeName(borgType),
|
||||
Group = group,
|
||||
};
|
||||
button.OnPressed += _ =>
|
||||
{
|
||||
_selectedBorgType = borgType;
|
||||
UpdateInformation(borgType);
|
||||
};
|
||||
SelectionsContainer.AddChild(button);
|
||||
}
|
||||
|
||||
ConfirmTypeButton.OnPressed += ConfirmButtonPressed;
|
||||
HelpGuidebookIds = GuidebookEntries;
|
||||
}
|
||||
|
||||
private void UpdateInformation(BorgTypePrototype prototype)
|
||||
{
|
||||
_selectedBorgType = prototype;
|
||||
|
||||
InfoContents.Visible = true;
|
||||
InfoPlaceholder.Visible = false;
|
||||
ConfirmTypeButton.Disabled = false;
|
||||
|
||||
NameLabel.Text = PrototypeName(prototype);
|
||||
DescriptionLabel.Text = Loc.GetString($"borg-type-{prototype.ID}-desc");
|
||||
ChassisView.SetPrototype(prototype.DummyPrototype);
|
||||
}
|
||||
|
||||
private void ConfirmButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
if (_selectedBorgType == null)
|
||||
return;
|
||||
|
||||
ConfirmedBorgType?.Invoke(_selectedBorgType);
|
||||
}
|
||||
|
||||
private static string PrototypeName(BorgTypePrototype prototype)
|
||||
{
|
||||
return Loc.GetString($"borg-type-{prototype.ID}-name");
|
||||
}
|
||||
}
|
||||
30
Content.Client/Silicons/Borgs/BorgSelectTypeUserInterface.cs
Normal file
30
Content.Client/Silicons/Borgs/BorgSelectTypeUserInterface.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Silicons.Borgs;
|
||||
|
||||
/// <summary>
|
||||
/// User interface used by borgs to select their type.
|
||||
/// </summary>
|
||||
/// <seealso cref="BorgSelectTypeMenu"/>
|
||||
/// <seealso cref="BorgSwitchableTypeComponent"/>
|
||||
/// <seealso cref="BorgSwitchableTypeUiKey"/>
|
||||
[UsedImplicitly]
|
||||
public sealed class BorgSelectTypeUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private BorgSelectTypeMenu? _menu;
|
||||
|
||||
public BorgSelectTypeUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = this.CreateWindow<BorgSelectTypeMenu>();
|
||||
_menu.ConfirmedBorgType += prototype => SendMessage(new BorgSelectTypeMessage(prototype));
|
||||
}
|
||||
}
|
||||
81
Content.Client/Silicons/Borgs/BorgSwitchableTypeSystem.cs
Normal file
81
Content.Client/Silicons/Borgs/BorgSwitchableTypeSystem.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Silicons.Borgs;
|
||||
|
||||
/// <summary>
|
||||
/// Client side logic for borg type switching. Sets up primarily client-side visual information.
|
||||
/// </summary>
|
||||
/// <seealso cref="SharedBorgSwitchableTypeSystem"/>
|
||||
/// <seealso cref="BorgSwitchableTypeComponent"/>
|
||||
public sealed class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeSystem
|
||||
{
|
||||
[Dependency] private readonly BorgSystem _borgSystem = default!;
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BorgSwitchableTypeComponent, AfterAutoHandleStateEvent>(AfterStateHandler);
|
||||
SubscribeLocalEvent<BorgSwitchableTypeComponent, ComponentStartup>(OnComponentStartup);
|
||||
}
|
||||
|
||||
private void OnComponentStartup(Entity<BorgSwitchableTypeComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
UpdateEntityAppearance(ent);
|
||||
}
|
||||
|
||||
private void AfterStateHandler(Entity<BorgSwitchableTypeComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateEntityAppearance(ent);
|
||||
}
|
||||
|
||||
protected override void UpdateEntityAppearance(
|
||||
Entity<BorgSwitchableTypeComponent> entity,
|
||||
BorgTypePrototype prototype)
|
||||
{
|
||||
if (TryComp(entity, out SpriteComponent? sprite))
|
||||
{
|
||||
sprite.LayerSetState(BorgVisualLayers.Body, prototype.SpriteBodyState);
|
||||
sprite.LayerSetState(BorgVisualLayers.LightStatus, prototype.SpriteToggleLightState);
|
||||
}
|
||||
|
||||
if (TryComp(entity, out BorgChassisComponent? chassis))
|
||||
{
|
||||
_borgSystem.SetMindStates(
|
||||
(entity.Owner, chassis),
|
||||
prototype.SpriteHasMindState,
|
||||
prototype.SpriteNoMindState);
|
||||
|
||||
if (TryComp(entity, out AppearanceComponent? appearance))
|
||||
{
|
||||
// Queue update so state changes apply.
|
||||
_appearance.QueueUpdate(entity, appearance);
|
||||
}
|
||||
}
|
||||
|
||||
if (prototype.SpriteBodyMovementState is { } movementState)
|
||||
{
|
||||
var spriteMovement = EnsureComp<SpriteMovementComponent>(entity);
|
||||
spriteMovement.NoMovementLayers.Clear();
|
||||
spriteMovement.NoMovementLayers["movement"] = new PrototypeLayerData
|
||||
{
|
||||
State = prototype.SpriteBodyState,
|
||||
};
|
||||
spriteMovement.MovementLayers.Clear();
|
||||
spriteMovement.MovementLayers["movement"] = new PrototypeLayerData
|
||||
{
|
||||
State = movementState,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
RemComp<SpriteMovementComponent>(entity);
|
||||
}
|
||||
|
||||
base.UpdateEntityAppearance(entity, prototype);
|
||||
}
|
||||
}
|
||||
@@ -92,4 +92,18 @@ public sealed class BorgSystem : SharedBorgSystem
|
||||
sprite.LayerSetState(MMIVisualLayers.Base, state);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the sprite states used for the borg "is there a mind or not" indication.
|
||||
/// </summary>
|
||||
/// <param name="borg">The entity and component to modify.</param>
|
||||
/// <param name="hasMindState">The state to use if the borg has a mind.</param>
|
||||
/// <param name="noMindState">The state to use if the borg has no mind.</param>
|
||||
/// <seealso cref="BorgChassisComponent.HasMindState"/>
|
||||
/// <seealso cref="BorgChassisComponent.NoMindState"/>
|
||||
public void SetMindStates(Entity<BorgChassisComponent> borg, string hasMindState, string noMindState)
|
||||
{
|
||||
borg.Comp.HasMindState = hasMindState;
|
||||
borg.Comp.NoMindState = noMindState;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Shared.Singularity.EntitySystems;
|
||||
using Content.Shared.Singularity.Components;
|
||||
|
||||
namespace Content.Client.Singularity.EntitySystems;
|
||||
namespace Content.Client.Singularity.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// The client-side version of <see cref="SharedEventHorizonSystem"/>.
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Content.Shared.Singularity.EntitySystems;
|
||||
using Content.Shared.Singularity.Components;
|
||||
|
||||
namespace Content.Client.Singularity.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// The client-side version of <see cref="SharedSingularityGeneratorSystem"/>.
|
||||
/// Manages <see cref="SingularityGeneratorComponent"/>s.
|
||||
/// Exists to make relevant signal handlers (ie: <see cref="SharedSingularityGeneratorSystem.OnEmagged"/>) work on the client.
|
||||
/// </summary>
|
||||
public sealed class SingularityGeneratorSystem : SharedSingularityGeneratorSystem
|
||||
{}
|
||||
@@ -5,7 +5,7 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Singularity.EntitySystems;
|
||||
namespace Content.Client.Singularity.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// The client-side version of <see cref="SharedSingularitySystem"/>.
|
||||
|
||||
@@ -307,12 +307,6 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
||||
_entity.GetNetEntity(storageEnt),
|
||||
new ItemStorageLocation(DraggingRotation, position)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_entity.RaisePredictiveEvent(new StorageRemoveItemEvent(
|
||||
_entity.GetNetEntity(draggingGhost.Entity),
|
||||
_entity.GetNetEntity(storageEnt)));
|
||||
}
|
||||
|
||||
_menuDragHelper.EndDrag();
|
||||
_container?.BuildItemPieces();
|
||||
|
||||
Reference in New Issue
Block a user