Re-organize all projects (#4166)
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.GameObjects.Components.Access.SharedIdCardConsoleComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Access
|
||||
{
|
||||
public class IdCardConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public IdCardConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private IdCardConsoleWindow? _window;
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new IdCardConsoleWindow(this, _prototypeManager) {Title = Owner.Owner.Name};
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
var castState = (IdCardConsoleBoundUserInterfaceState) state;
|
||||
_window?.UpdateState(castState);
|
||||
}
|
||||
|
||||
public void ButtonPressed(UiButton button)
|
||||
{
|
||||
SendMessage(new IdButtonPressedMessage(button));
|
||||
}
|
||||
|
||||
public void SubmitData(string newFullName, string newJobTitle, List<string> newAccessList)
|
||||
{
|
||||
SendMessage(new WriteToTargetIdMessage(
|
||||
newFullName,
|
||||
newJobTitle,
|
||||
newAccessList));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Access;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.GameObjects.Components.Access.SharedIdCardConsoleComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Access
|
||||
{
|
||||
public class IdCardConsoleWindow : SS14Window
|
||||
{
|
||||
private readonly Button _privilegedIdButton;
|
||||
private readonly Button _targetIdButton;
|
||||
|
||||
private readonly Label _privilegedIdLabel;
|
||||
private readonly Label _targetIdLabel;
|
||||
|
||||
private readonly Label _fullNameLabel;
|
||||
private readonly LineEdit _fullNameLineEdit;
|
||||
private readonly Label _jobTitleLabel;
|
||||
private readonly LineEdit _jobTitleLineEdit;
|
||||
|
||||
private readonly Button _fullNameSaveButton;
|
||||
private readonly Button _jobTitleSaveButton;
|
||||
|
||||
private readonly IdCardConsoleBoundUserInterface _owner;
|
||||
|
||||
private readonly Dictionary<string, Button> _accessButtons = new();
|
||||
|
||||
private string? _lastFullName;
|
||||
private string? _lastJobTitle;
|
||||
|
||||
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager)
|
||||
{
|
||||
MinSize = SetSize = (650, 290);
|
||||
_owner = owner;
|
||||
var vBox = new VBoxContainer();
|
||||
|
||||
vBox.AddChild(new GridContainer
|
||||
{
|
||||
Columns = 3,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Privileged ID:")},
|
||||
(_privilegedIdButton = new Button()),
|
||||
(_privilegedIdLabel = new Label()),
|
||||
|
||||
new Label {Text = Loc.GetString("Target ID:")},
|
||||
(_targetIdButton = new Button()),
|
||||
(_targetIdLabel = new Label())
|
||||
}
|
||||
});
|
||||
|
||||
_privilegedIdButton.OnPressed += _ => _owner.ButtonPressed(UiButton.PrivilegedId);
|
||||
_targetIdButton.OnPressed += _ => _owner.ButtonPressed(UiButton.TargetId);
|
||||
|
||||
// Separator
|
||||
vBox.AddChild(new Control {MinSize = (0, 8)});
|
||||
|
||||
// Name and job title line edits.
|
||||
vBox.AddChild(new GridContainer
|
||||
{
|
||||
Columns = 3,
|
||||
HSeparationOverride = 4,
|
||||
Children =
|
||||
{
|
||||
// Name
|
||||
(_fullNameLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString("Full name:")
|
||||
}),
|
||||
(_fullNameLineEdit = new LineEdit
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
}),
|
||||
(_fullNameSaveButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("Save"),
|
||||
Disabled = true
|
||||
}),
|
||||
|
||||
// Title
|
||||
(_jobTitleLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString("Job title:")
|
||||
}),
|
||||
(_jobTitleLineEdit = new LineEdit
|
||||
{
|
||||
HorizontalExpand = true
|
||||
}),
|
||||
(_jobTitleSaveButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("Save"),
|
||||
Disabled = true
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
_fullNameLineEdit.OnTextEntered += _ => SubmitData();
|
||||
_fullNameLineEdit.OnTextChanged += _ =>
|
||||
{
|
||||
_fullNameSaveButton.Disabled = _fullNameSaveButton.Text == _lastFullName;
|
||||
};
|
||||
_fullNameSaveButton.OnPressed += _ => SubmitData();
|
||||
|
||||
_jobTitleLineEdit.OnTextEntered += _ => SubmitData();
|
||||
_jobTitleLineEdit.OnTextChanged += _ =>
|
||||
{
|
||||
_jobTitleSaveButton.Disabled = _jobTitleLineEdit.Text == _lastJobTitle;
|
||||
};
|
||||
_jobTitleSaveButton.OnPressed += _ => SubmitData();
|
||||
|
||||
// Separator
|
||||
vBox.AddChild(new Control {MinSize = (0, 8)});
|
||||
|
||||
{
|
||||
var grid = new GridContainer
|
||||
{
|
||||
Columns = 5,
|
||||
HorizontalAlignment = HAlignment.Center
|
||||
};
|
||||
vBox.AddChild(grid);
|
||||
|
||||
foreach (var accessLevel in prototypeManager.EnumeratePrototypes<AccessLevelPrototype>())
|
||||
{
|
||||
var newButton = new Button
|
||||
{
|
||||
Text = accessLevel.Name,
|
||||
ToggleMode = true,
|
||||
};
|
||||
grid.AddChild(newButton);
|
||||
_accessButtons.Add(accessLevel.ID, newButton);
|
||||
newButton.OnPressed += _ => SubmitData();
|
||||
}
|
||||
}
|
||||
|
||||
Contents.AddChild(vBox);
|
||||
}
|
||||
|
||||
public void UpdateState(IdCardConsoleBoundUserInterfaceState state)
|
||||
{
|
||||
_privilegedIdButton.Text = state.IsPrivilegedIdPresent
|
||||
? Loc.GetString("Eject")
|
||||
: Loc.GetString("Insert");
|
||||
|
||||
_privilegedIdLabel.Text = state.PrivilegedIdName;
|
||||
|
||||
_targetIdButton.Text = state.IsTargetIdPresent
|
||||
? Loc.GetString("Eject")
|
||||
: Loc.GetString("Insert");
|
||||
|
||||
_targetIdLabel.Text = state.TargetIdName;
|
||||
|
||||
var interfaceEnabled =
|
||||
state.IsPrivilegedIdPresent && state.IsPrivilegedIdAuthorized && state.IsTargetIdPresent;
|
||||
|
||||
var fullNameDirty = _lastFullName != null && _fullNameLineEdit.Text != state.TargetIdFullName;
|
||||
var jobTitleDirty = _lastJobTitle != null && _jobTitleLineEdit.Text != state.TargetIdJobTitle;
|
||||
|
||||
_fullNameLabel.Modulate = interfaceEnabled ? Color.White : Color.Gray;
|
||||
_fullNameLineEdit.Editable = interfaceEnabled;
|
||||
if (!fullNameDirty)
|
||||
{
|
||||
_fullNameLineEdit.Text = state.TargetIdFullName ?? "";
|
||||
}
|
||||
|
||||
_fullNameSaveButton.Disabled = !interfaceEnabled || !fullNameDirty;
|
||||
|
||||
_jobTitleLabel.Modulate = interfaceEnabled ? Color.White : Color.Gray;
|
||||
_jobTitleLineEdit.Editable = interfaceEnabled;
|
||||
if (!jobTitleDirty)
|
||||
{
|
||||
_jobTitleLineEdit.Text = state.TargetIdJobTitle ?? "";
|
||||
}
|
||||
|
||||
_jobTitleSaveButton.Disabled = !interfaceEnabled || !jobTitleDirty;
|
||||
|
||||
foreach (var (accessName, button) in _accessButtons)
|
||||
{
|
||||
button.Disabled = !interfaceEnabled;
|
||||
if (interfaceEnabled)
|
||||
{
|
||||
button.Pressed = state.TargetIdAccessList?.Contains(accessName) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
_lastFullName = state.TargetIdFullName;
|
||||
_lastJobTitle = state.TargetIdJobTitle;
|
||||
}
|
||||
|
||||
private void SubmitData()
|
||||
{
|
||||
_owner.SubmitData(
|
||||
_fullNameLineEdit.Text,
|
||||
_jobTitleLineEdit.Text,
|
||||
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
|
||||
_accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.ActionBlocking;
|
||||
using Content.Shared.Preferences.Appearance;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.ActionBlocking
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class CuffableComponent : SharedCuffableComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
private string? _currentRSI;
|
||||
|
||||
[ViewVariables] [ComponentDependency] private readonly SpriteComponent? _spriteComponent = null;
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState is not CuffableComponentState cuffState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CanStillInteract = cuffState.CanStillInteract;
|
||||
|
||||
if (_spriteComponent != null)
|
||||
{
|
||||
_spriteComponent.LayerSetVisible(HumanoidVisualLayers.Handcuffs, cuffState.NumHandsCuffed > 0);
|
||||
_spriteComponent.LayerSetColor(HumanoidVisualLayers.Handcuffs, cuffState.Color);
|
||||
|
||||
if (cuffState.NumHandsCuffed > 0)
|
||||
{
|
||||
if (_currentRSI != cuffState.RSI) // we don't want to keep loading the same RSI
|
||||
{
|
||||
_currentRSI = cuffState.RSI;
|
||||
|
||||
if (_currentRSI != null)
|
||||
{
|
||||
_spriteComponent.LayerSetState(HumanoidVisualLayers.Handcuffs, new RSI.StateId(cuffState.IconState), new ResourcePath(_currentRSI));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_spriteComponent.LayerSetState(HumanoidVisualLayers.Handcuffs, new RSI.StateId(cuffState.IconState)); // TODO: safety check to see if RSI contains the state?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
_spriteComponent?.LayerSetVisible(HumanoidVisualLayers.Handcuffs, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.ActionBlocking;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.ActionBlocking
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedHandcuffComponent))]
|
||||
public class HandcuffComponent : SharedHandcuffComponent
|
||||
{
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState is not HandcuffedComponentState state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.IconState == string.Empty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent<SpriteComponent>(out var sprite))
|
||||
{
|
||||
sprite.LayerSetState(0, new RSI.StateId(state.IconState)); // TODO: safety check to see if RSI contains the state?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
using Content.Client.GameObjects.Components.Mobs;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared.GameObjects.Components.Actor;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Actor
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class CharacterInfoComponent : SharedCharacterInfoComponent, ICharacterUI
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
private CharacterInfoControl _control = default!;
|
||||
|
||||
public Control Scene { get; private set; } = default!;
|
||||
public UIPriority Priority => UIPriority.Info;
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
|
||||
Scene = _control = new CharacterInfoControl(_resourceCache);
|
||||
}
|
||||
|
||||
public void Opened()
|
||||
{
|
||||
SendNetworkMessage(new RequestCharacterInfoMessage());
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, netChannel, session);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case CharacterInfoMessage characterInfoMessage:
|
||||
_control.UpdateUI(characterInfoMessage);
|
||||
if (Owner.TryGetComponent(out ISpriteComponent? spriteComponent))
|
||||
{
|
||||
_control.SpriteView.Sprite = spriteComponent;
|
||||
}
|
||||
|
||||
_control.NameLabel.Text = Owner.Name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CharacterInfoControl : VBoxContainer
|
||||
{
|
||||
public SpriteView SpriteView { get; }
|
||||
public Label NameLabel { get; }
|
||||
public Label SubText { get; }
|
||||
|
||||
public VBoxContainer ObjectivesContainer { get; }
|
||||
|
||||
public CharacterInfoControl(IResourceCache resourceCache)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
AddChild(new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(SpriteView = new SpriteView { Scale = (2, 2)}),
|
||||
new VBoxContainer
|
||||
{
|
||||
VerticalAlignment = VAlignment.Top,
|
||||
Children =
|
||||
{
|
||||
(NameLabel = new Label()),
|
||||
(SubText = new Label
|
||||
{
|
||||
VerticalAlignment = VAlignment.Top,
|
||||
StyleClasses = {StyleNano.StyleClassLabelSubText},
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AddChild(new Placeholder()
|
||||
{
|
||||
PlaceholderText = Loc.GetString("Health & status effects")
|
||||
});
|
||||
|
||||
AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("Objectives"),
|
||||
HorizontalAlignment = HAlignment.Center
|
||||
});
|
||||
ObjectivesContainer = new VBoxContainer();
|
||||
AddChild(ObjectivesContainer);
|
||||
|
||||
AddChild(new Placeholder()
|
||||
{
|
||||
PlaceholderText = Loc.GetString("Antagonist Roles")
|
||||
});
|
||||
}
|
||||
|
||||
public void UpdateUI(CharacterInfoMessage characterInfoMessage)
|
||||
{
|
||||
SubText.Text = characterInfoMessage.JobTitle;
|
||||
|
||||
ObjectivesContainer.RemoveAllChildren();
|
||||
foreach (var (groupId, objectiveConditions) in characterInfoMessage.Objectives)
|
||||
{
|
||||
var vbox = new VBoxContainer
|
||||
{
|
||||
Modulate = Color.Gray
|
||||
};
|
||||
|
||||
vbox.AddChild(new Label
|
||||
{
|
||||
Text = groupId,
|
||||
Modulate = Color.LightSkyBlue
|
||||
});
|
||||
|
||||
foreach (var objectiveCondition in objectiveConditions)
|
||||
{
|
||||
var hbox = new HBoxContainer();
|
||||
hbox.AddChild(new ProgressTextureRect
|
||||
{
|
||||
Texture = objectiveCondition.SpriteSpecifier.Frame0(),
|
||||
Progress = objectiveCondition.Progress,
|
||||
VerticalAlignment = VAlignment.Center
|
||||
});
|
||||
hbox.AddChild(new Control
|
||||
{
|
||||
MinSize = (10,0)
|
||||
});
|
||||
hbox.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label{Text = objectiveCondition.Title},
|
||||
new Label{Text = objectiveCondition.Description}
|
||||
}
|
||||
}
|
||||
);
|
||||
vbox.AddChild(hbox);
|
||||
}
|
||||
ObjectivesContainer.AddChild(vbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.GameObjects.Components.Mobs;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Actor
|
||||
{
|
||||
/// <summary>
|
||||
/// A semi-abstract component which gets added to entities upon attachment and collects all character
|
||||
/// user interfaces into a single window and keybind for the user
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class CharacterInterface : Component
|
||||
{
|
||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||
|
||||
public override string Name => "Character Interface Component";
|
||||
|
||||
/// <summary>
|
||||
/// Window to hold each of the character interfaces
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if it would otherwise be empty.
|
||||
/// </remarks>
|
||||
public CharacterWindow? Window { get; private set; }
|
||||
|
||||
private List<ICharacterUI>? _uiComponents;
|
||||
|
||||
/// <summary>
|
||||
/// Create the window with all character UIs and bind it to a keypress
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
//Use all the character ui interfaced components to create the character window
|
||||
_uiComponents = Owner.GetAllComponents<ICharacterUI>().ToList();
|
||||
if (_uiComponents.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Window = new CharacterWindow(_uiComponents);
|
||||
Window.OnClose += () => _gameHud.CharacterButtonDown = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of window and the keypress binding
|
||||
/// </summary>
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
if (_uiComponents != null)
|
||||
{
|
||||
foreach (var component in _uiComponents)
|
||||
{
|
||||
// Make sure these don't get deleted when the window is disposed.
|
||||
component.Scene.Orphan();
|
||||
}
|
||||
}
|
||||
|
||||
_uiComponents = null;
|
||||
|
||||
Window?.Close();
|
||||
Window = null;
|
||||
|
||||
var inputMgr = IoCManager.Resolve<IInputManager>();
|
||||
inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, null);
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PlayerAttachedMsg _:
|
||||
if (Window != null)
|
||||
{
|
||||
_gameHud.CharacterButtonVisible = true;
|
||||
_gameHud.CharacterButtonToggled = b =>
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
Window.OpenCentered();
|
||||
}
|
||||
else
|
||||
{
|
||||
Window.Close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PlayerDetachedMsg _:
|
||||
if (Window != null)
|
||||
{
|
||||
_gameHud.CharacterButtonVisible = false;
|
||||
Window.Close();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A window that collects and shows all the individual character user interfaces
|
||||
/// </summary>
|
||||
public class CharacterWindow : SS14Window
|
||||
{
|
||||
private readonly VBoxContainer _contentsVBox;
|
||||
private readonly List<ICharacterUI> _windowComponents;
|
||||
|
||||
public CharacterWindow(List<ICharacterUI> windowComponents)
|
||||
{
|
||||
Title = "Character";
|
||||
|
||||
_contentsVBox = new VBoxContainer();
|
||||
Contents.AddChild(_contentsVBox);
|
||||
|
||||
windowComponents.Sort((a, b) => ((int) a.Priority).CompareTo((int) b.Priority));
|
||||
foreach (var element in windowComponents)
|
||||
{
|
||||
_contentsVBox.AddChild(element.Scene);
|
||||
}
|
||||
|
||||
_windowComponents = windowComponents;
|
||||
}
|
||||
|
||||
protected override void Opened()
|
||||
{
|
||||
base.Opened();
|
||||
foreach (var windowComponent in _windowComponents)
|
||||
{
|
||||
windowComponent.Opened();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines ordering of the character user interface, small values come sooner
|
||||
/// </summary>
|
||||
public enum UIPriority
|
||||
{
|
||||
First = 0,
|
||||
Info = 5,
|
||||
Species = 100,
|
||||
Last = 99999
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using Content.Client.GameObjects.EntitySystems.DoAfter;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Actor
|
||||
{
|
||||
public class ProgressTextureRect : TextureRect
|
||||
{
|
||||
public float Progress;
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
{
|
||||
var dims = Texture != null ? GetDrawDimensions(Texture) : UIBox2.FromDimensions(Vector2.Zero, PixelSize);
|
||||
dims.Top = Math.Max(dims.Bottom - dims.Bottom * Progress,0);
|
||||
handle.DrawRect(dims, DoAfterHelpers.GetProgressColor(Progress));
|
||||
|
||||
base.Draw(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class AnimationsTestComponent : Component
|
||||
{
|
||||
public override string Name => "AnimationsTest";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
var animations = Owner.GetComponent<AnimationPlayerComponent>();
|
||||
animations.Play(new Animation
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(20),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(ITransformComponent),
|
||||
Property = nameof(ITransformComponent.LocalRotation),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(Angle.Zero, 0),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(1440), 20)
|
||||
}
|
||||
},
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(ISpriteComponent),
|
||||
Property = "layer/0/texture",
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame("Objects/toolbox_r.png", 0),
|
||||
new AnimationTrackProperty.KeyFrame("Objects/Toolbox_b.png", 5),
|
||||
new AnimationTrackProperty.KeyFrame("Objects/Toolbox_y.png", 5),
|
||||
new AnimationTrackProperty.KeyFrame("Objects/toolbox_r.png", 5),
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "yes");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
using Content.Client.Arcade;
|
||||
using Content.Shared.Arcade;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Arcade
|
||||
{
|
||||
public class BlockGameBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private BlockGameMenu? _menu;
|
||||
|
||||
public BlockGameBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = new BlockGameMenu(this);
|
||||
_menu.OnClose += Close;
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case BlockGameMessages.BlockGameVisualUpdateMessage updateMessage:
|
||||
switch (updateMessage.GameVisualType)
|
||||
{
|
||||
case BlockGameMessages.BlockGameVisualType.GameField:
|
||||
_menu?.UpdateBlocks(updateMessage.Blocks);
|
||||
break;
|
||||
case BlockGameMessages.BlockGameVisualType.HoldBlock:
|
||||
_menu?.UpdateHeldBlock(updateMessage.Blocks);
|
||||
break;
|
||||
case BlockGameMessages.BlockGameVisualType.NextBlock:
|
||||
_menu?.UpdateNextBlock(updateMessage.Blocks);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case BlockGameMessages.BlockGameScoreUpdateMessage scoreUpdate:
|
||||
_menu?.UpdatePoints(scoreUpdate.Points);
|
||||
break;
|
||||
case BlockGameMessages.BlockGameUserStatusMessage userMessage:
|
||||
_menu?.SetUsability(userMessage.IsPlayer);
|
||||
break;
|
||||
case BlockGameMessages.BlockGameSetScreenMessage statusMessage:
|
||||
if (statusMessage.isStarted) _menu?.SetStarted();
|
||||
_menu?.SetScreen(statusMessage.Screen);
|
||||
if (statusMessage is BlockGameMessages.BlockGameGameOverScreenMessage gameOverScreenMessage)
|
||||
_menu?.SetGameoverInfo(gameOverScreenMessage.FinalScore, gameOverScreenMessage.LocalPlacement, gameOverScreenMessage.GlobalPlacement);
|
||||
break;
|
||||
case BlockGameMessages.BlockGameHighScoreUpdateMessage highScoreUpdateMessage:
|
||||
_menu?.UpdateHighscores(highScoreUpdateMessage.LocalHighscores,
|
||||
highScoreUpdateMessage.GlobalHighscores);
|
||||
break;
|
||||
case BlockGameMessages.BlockGameLevelUpdateMessage levelUpdateMessage:
|
||||
_menu?.UpdateLevel(levelUpdateMessage.Level);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendAction(BlockGamePlayerAction action)
|
||||
{
|
||||
SendMessage(new BlockGameMessages.BlockGamePlayerActionMessage(action));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_menu?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using Content.Client.Arcade;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.GameObjects.Components.Arcade.SharedSpaceVillainArcadeComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Arcade
|
||||
{
|
||||
public class SpaceVillainArcadeBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables] private SpaceVillainArcadeMenu? _menu;
|
||||
|
||||
//public SharedSpaceVillainArcadeComponent SpaceVillainArcade;
|
||||
|
||||
public SpaceVillainArcadeBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
SendAction(PlayerAction.RequestData);
|
||||
}
|
||||
|
||||
public void SendAction(PlayerAction action)
|
||||
{
|
||||
SendMessage(new SpaceVillainArcadePlayerActionMessage(action));
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
/*if(!Owner.Owner.TryGetComponent(out SharedSpaceVillainArcadeComponent spaceVillainArcade))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SpaceVillainArcade = spaceVillainArcade;*/
|
||||
|
||||
_menu = new SpaceVillainArcadeMenu(this);
|
||||
|
||||
_menu.OnClose += Close;
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
if (message is SpaceVillainArcadeDataUpdateMessage msg) _menu?.UpdateInfo(msg);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing) _menu?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Client.Items.Components;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.Utility;
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.Vapor;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Body
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IBody))]
|
||||
public class BodyComponent : SharedBodyComponent, IDraggable
|
||||
{
|
||||
bool IDraggable.CanStartDrag(StartDragDropEvent args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IDraggable.CanDrop(CanDropEvent args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Body.Mechanism;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Body.Mechanism
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedMechanismComponent))]
|
||||
[ComponentReference(typeof(IMechanism))]
|
||||
public class MechanismComponent : SharedMechanismComponent { }
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Body.Part;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Body.Part
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedBodyPartComponent))]
|
||||
[ComponentReference(typeof(IBodyPart))]
|
||||
public class BodyPartComponent : SharedBodyPartComponent
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Body.Scanner;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Body.Scanner
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class BodyScannerBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private BodyScannerDisplay? _display;
|
||||
|
||||
[ViewVariables]
|
||||
private IEntity? _entity;
|
||||
|
||||
public BodyScannerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) { }
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_display = new BodyScannerDisplay(this);
|
||||
_display.OnClose += Close;
|
||||
_display.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not BodyScannerUIState scannerState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Owner.Owner.EntityManager.TryGetEntity(scannerState.Uid, out _entity))
|
||||
{
|
||||
throw new ArgumentException($"Received an invalid entity with id {scannerState.Uid} for body scanner with id {Owner.Owner.Uid} at {Owner.Owner.Transform.MapPosition}");
|
||||
}
|
||||
|
||||
_display?.UpdateDisplay(_entity);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_display?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.Components.Body.Mechanism;
|
||||
using Content.Shared.GameObjects.Components.Body.Part;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using static Robust.Client.UserInterface.Controls.ItemList;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Body.Scanner
|
||||
{
|
||||
public sealed class BodyScannerDisplay : SS14Window
|
||||
{
|
||||
private IEntity? _currentEntity;
|
||||
private IBodyPart? _currentBodyPart;
|
||||
|
||||
private IBody? CurrentBody => _currentEntity?.GetComponentOrNull<IBody>();
|
||||
|
||||
public BodyScannerDisplay(BodyScannerBoundUserInterface owner)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
Owner = owner;
|
||||
Title = Loc.GetString("Body Scanner");
|
||||
|
||||
var hSplit = new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
// Left half
|
||||
new ScrollContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
(BodyPartList = new ItemList())
|
||||
}
|
||||
},
|
||||
// Right half
|
||||
new VBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
// Top half of the right half
|
||||
new VBoxContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
Children =
|
||||
{
|
||||
(BodyPartLabel = new Label()),
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = "Health: "
|
||||
},
|
||||
(BodyPartHealth = new Label())
|
||||
}
|
||||
},
|
||||
new ScrollContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
Children =
|
||||
{
|
||||
(MechanismList = new ItemList())
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Bottom half of the right half
|
||||
(MechanismInfoLabel = new RichTextLabel
|
||||
{
|
||||
VerticalExpand = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Contents.AddChild(hSplit);
|
||||
|
||||
BodyPartList.OnItemSelected += BodyPartOnItemSelected;
|
||||
MechanismList.OnItemSelected += MechanismOnItemSelected;
|
||||
MinSize = SetSize = (800, 600);
|
||||
}
|
||||
|
||||
public BodyScannerBoundUserInterface Owner { get; }
|
||||
|
||||
private ItemList BodyPartList { get; }
|
||||
|
||||
private Label BodyPartLabel { get; }
|
||||
|
||||
private Label BodyPartHealth { get; }
|
||||
|
||||
private ItemList MechanismList { get; }
|
||||
|
||||
private RichTextLabel MechanismInfoLabel { get; }
|
||||
|
||||
public void UpdateDisplay(IEntity entity)
|
||||
{
|
||||
_currentEntity = entity;
|
||||
BodyPartList.Clear();
|
||||
|
||||
var body = CurrentBody;
|
||||
|
||||
if (body == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (part, _) in body.Parts)
|
||||
{
|
||||
BodyPartList.AddItem(Loc.GetString(part.Name));
|
||||
}
|
||||
}
|
||||
|
||||
public void BodyPartOnItemSelected(ItemListSelectedEventArgs args)
|
||||
{
|
||||
var body = CurrentBody;
|
||||
|
||||
if (body == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var slot = body.SlotAt(args.ItemIndex);
|
||||
_currentBodyPart = body.PartAt(args.ItemIndex).Key;
|
||||
|
||||
if (slot.Part != null)
|
||||
{
|
||||
UpdateBodyPartBox(slot.Part, slot.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBodyPartBox(IBodyPart part, string slotName)
|
||||
{
|
||||
BodyPartLabel.Text = $"{Loc.GetString(slotName)}: {Loc.GetString(part.Owner.Name)}";
|
||||
|
||||
// TODO BODY Part damage
|
||||
if (part.Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
BodyPartHealth.Text = Loc.GetString("{0} damage", damageable.TotalDamage);
|
||||
}
|
||||
|
||||
MechanismList.Clear();
|
||||
|
||||
foreach (var mechanism in part.Mechanisms)
|
||||
{
|
||||
MechanismList.AddItem(mechanism.Name);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO BODY Guaranteed this is going to crash when a part's mechanisms change. This part is left as an exercise for the reader.
|
||||
public void MechanismOnItemSelected(ItemListSelectedEventArgs args)
|
||||
{
|
||||
UpdateMechanismBox(_currentBodyPart?.Mechanisms.ElementAt(args.ItemIndex));
|
||||
}
|
||||
|
||||
private void UpdateMechanismBox(IMechanism? mechanism)
|
||||
{
|
||||
// TODO BODY Improve UI
|
||||
if (mechanism == null)
|
||||
{
|
||||
MechanismInfoLabel.SetMessage("");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO BODY Mechanism description
|
||||
var message =
|
||||
Loc.GetString(
|
||||
$"{mechanism.Name}\nHealth: {mechanism.CurrentDurability}/{mechanism.MaxDurability}");
|
||||
|
||||
MechanismInfoLabel.SetMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Body.Surgery;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Body.Surgery
|
||||
{
|
||||
// TODO BODY Make window close if target or surgery tool gets too far away from user.
|
||||
|
||||
/// <summary>
|
||||
/// Generic client-side UI list popup that allows users to choose from an option
|
||||
/// of limbs or organs to operate on.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class SurgeryBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private SurgeryWindow? _window;
|
||||
|
||||
public SurgeryBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) { }
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
_window = new SurgeryWindow();
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case RequestBodyPartSurgeryUIMessage msg:
|
||||
HandleBodyPartRequest(msg);
|
||||
break;
|
||||
case RequestMechanismSurgeryUIMessage msg:
|
||||
HandleMechanismRequest(msg);
|
||||
break;
|
||||
case RequestBodyPartSlotSurgeryUIMessage msg:
|
||||
HandleBodyPartSlotRequest(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleBodyPartRequest(RequestBodyPartSurgeryUIMessage msg)
|
||||
{
|
||||
_window?.BuildDisplay(msg.Targets, BodyPartSelectedCallback);
|
||||
}
|
||||
|
||||
private void HandleMechanismRequest(RequestMechanismSurgeryUIMessage msg)
|
||||
{
|
||||
_window?.BuildDisplay(msg.Targets, MechanismSelectedCallback);
|
||||
}
|
||||
|
||||
private void HandleBodyPartSlotRequest(RequestBodyPartSlotSurgeryUIMessage msg)
|
||||
{
|
||||
_window?.BuildDisplay(msg.Targets, BodyPartSlotSelectedCallback);
|
||||
}
|
||||
|
||||
private void BodyPartSelectedCallback(int selectedOptionData)
|
||||
{
|
||||
SendMessage(new ReceiveBodyPartSurgeryUIMessage(selectedOptionData));
|
||||
}
|
||||
|
||||
private void MechanismSelectedCallback(int selectedOptionData)
|
||||
{
|
||||
SendMessage(new ReceiveMechanismSurgeryUIMessage(selectedOptionData));
|
||||
}
|
||||
|
||||
private void BodyPartSlotSelectedCallback(int selectedOptionData)
|
||||
{
|
||||
SendMessage(new ReceiveBodyPartSlotSurgeryUIMessage(selectedOptionData));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Body.Surgery
|
||||
{
|
||||
public class SurgeryWindow : SS14Window
|
||||
{
|
||||
public delegate void OptionSelectedCallback(int selectedOptionData);
|
||||
|
||||
private readonly VBoxContainer _optionsBox;
|
||||
private OptionSelectedCallback? _optionSelectedCallback;
|
||||
|
||||
public SurgeryWindow()
|
||||
{
|
||||
MinSize = SetSize = (300, 400);
|
||||
Title = Loc.GetString("Surgery");
|
||||
RectClipContent = true;
|
||||
|
||||
var vSplitContainer = new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new ScrollContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
HScrollEnabled = true,
|
||||
VScrollEnabled = true,
|
||||
Children =
|
||||
{
|
||||
(_optionsBox = new VBoxContainer
|
||||
{
|
||||
HorizontalExpand = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Contents.AddChild(vSplitContainer);
|
||||
}
|
||||
|
||||
public void BuildDisplay(Dictionary<string, int> data, OptionSelectedCallback callback)
|
||||
{
|
||||
_optionsBox.DisposeAllChildren();
|
||||
_optionSelectedCallback = callback;
|
||||
|
||||
foreach (var (displayText, callbackData) in data)
|
||||
{
|
||||
var button = new SurgeryButton(callbackData);
|
||||
|
||||
button.SetOnToggleBehavior(OnButtonPressed);
|
||||
button.SetDisplayText(Loc.GetString(displayText));
|
||||
|
||||
_optionsBox.AddChild(button);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
if (args.Button.Parent is SurgeryButton surgery)
|
||||
{
|
||||
_optionSelectedCallback?.Invoke(surgery.CallbackData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SurgeryButton : PanelContainer
|
||||
{
|
||||
public Button Button { get; }
|
||||
|
||||
private SpriteView SpriteView { get; }
|
||||
|
||||
private Label DisplayText { get; }
|
||||
|
||||
public int CallbackData { get; }
|
||||
|
||||
public SurgeryButton(int callbackData)
|
||||
{
|
||||
CallbackData = callbackData;
|
||||
|
||||
Button = new Button
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalExpand = true,
|
||||
ToggleMode = true,
|
||||
MouseFilter = MouseFilterMode.Stop
|
||||
};
|
||||
|
||||
AddChild(Button);
|
||||
|
||||
AddChild(new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(SpriteView = new SpriteView
|
||||
{
|
||||
MinSize = new Vector2(32.0f, 32.0f)
|
||||
}),
|
||||
(DisplayText = new Label
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
Text = "N/A",
|
||||
}),
|
||||
(new Control
|
||||
{
|
||||
HorizontalExpand = true
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void SetDisplayText(string text)
|
||||
{
|
||||
DisplayText.Text = text;
|
||||
}
|
||||
|
||||
public void SetOnToggleBehavior(Action<BaseButton.ButtonToggledEventArgs> behavior)
|
||||
{
|
||||
Button.OnToggled += behavior;
|
||||
}
|
||||
|
||||
public void SetSprite()
|
||||
{
|
||||
//button.SpriteView.Sprite = sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Botany;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Botany
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class PlantHolderVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
|
||||
sprite.LayerMapReserveBlank(PlantHolderLayers.Plant);
|
||||
sprite.LayerMapReserveBlank(PlantHolderLayers.HealthLight);
|
||||
sprite.LayerMapReserveBlank(PlantHolderLayers.WaterLight);
|
||||
sprite.LayerMapReserveBlank(PlantHolderLayers.NutritionLight);
|
||||
sprite.LayerMapReserveBlank(PlantHolderLayers.AlertLight);
|
||||
sprite.LayerMapReserveBlank(PlantHolderLayers.HarvestLight);
|
||||
|
||||
var hydroTools = new ResourcePath("Constructible/Hydroponics/overlays.rsi");
|
||||
|
||||
sprite.LayerSetSprite(PlantHolderLayers.HealthLight,
|
||||
new SpriteSpecifier.Rsi(hydroTools, "lowhealth3"));
|
||||
sprite.LayerSetSprite(PlantHolderLayers.WaterLight,
|
||||
new SpriteSpecifier.Rsi(hydroTools, "lowwater3"));
|
||||
sprite.LayerSetSprite(PlantHolderLayers.NutritionLight,
|
||||
new SpriteSpecifier.Rsi(hydroTools, "lownutri3"));
|
||||
sprite.LayerSetSprite(PlantHolderLayers.AlertLight,
|
||||
new SpriteSpecifier.Rsi(hydroTools, "alert3"));
|
||||
sprite.LayerSetSprite(PlantHolderLayers.HarvestLight,
|
||||
new SpriteSpecifier.Rsi(hydroTools, "harvest3"));
|
||||
|
||||
// Let's make those invisible for now.
|
||||
sprite.LayerSetVisible(PlantHolderLayers.Plant, false);
|
||||
sprite.LayerSetVisible(PlantHolderLayers.HealthLight, false);
|
||||
sprite.LayerSetVisible(PlantHolderLayers.WaterLight, false);
|
||||
sprite.LayerSetVisible(PlantHolderLayers.NutritionLight, false);
|
||||
sprite.LayerSetVisible(PlantHolderLayers.AlertLight, false);
|
||||
sprite.LayerSetVisible(PlantHolderLayers.HarvestLight, false);
|
||||
|
||||
// Pretty unshaded lights!
|
||||
sprite.LayerSetShader(PlantHolderLayers.HealthLight, "unshaded");
|
||||
sprite.LayerSetShader(PlantHolderLayers.WaterLight, "unshaded");
|
||||
sprite.LayerSetShader(PlantHolderLayers.NutritionLight, "unshaded");
|
||||
sprite.LayerSetShader(PlantHolderLayers.AlertLight, "unshaded");
|
||||
sprite.LayerSetShader(PlantHolderLayers.HarvestLight, "unshaded");
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (component.TryGetData<SpriteSpecifier>(PlantHolderVisuals.Plant, out var specifier))
|
||||
{
|
||||
var valid = !specifier.Equals(SpriteSpecifier.Invalid);
|
||||
sprite.LayerSetVisible(PlantHolderLayers.Plant, valid);
|
||||
if(valid)
|
||||
sprite.LayerSetSprite(PlantHolderLayers.Plant, specifier);
|
||||
}
|
||||
|
||||
if (component.TryGetData<bool>(PlantHolderVisuals.HealthLight, out var health))
|
||||
{
|
||||
sprite.LayerSetVisible(PlantHolderLayers.HealthLight, health);
|
||||
}
|
||||
|
||||
if (component.TryGetData<bool>(PlantHolderVisuals.WaterLight, out var water))
|
||||
{
|
||||
sprite.LayerSetVisible(PlantHolderLayers.WaterLight, water);
|
||||
}
|
||||
|
||||
if (component.TryGetData<bool>(PlantHolderVisuals.NutritionLight, out var nutrition))
|
||||
{
|
||||
sprite.LayerSetVisible(PlantHolderLayers.NutritionLight, nutrition);
|
||||
}
|
||||
|
||||
if (component.TryGetData<bool>(PlantHolderVisuals.AlertLight, out var alert))
|
||||
{
|
||||
sprite.LayerSetVisible(PlantHolderLayers.AlertLight, alert);
|
||||
}
|
||||
|
||||
if (component.TryGetData<bool>(PlantHolderVisuals.HarvestLight, out var harvest))
|
||||
{
|
||||
sprite.LayerSetVisible(PlantHolderLayers.HarvestLight, harvest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum PlantHolderLayers : byte
|
||||
{
|
||||
Plant,
|
||||
HealthLight,
|
||||
WaterLight,
|
||||
NutritionLight,
|
||||
AlertLight,
|
||||
HarvestLight,
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#nullable enable
|
||||
using Content.Shared.GameObjects.Components.Buckle;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Buckle
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedBuckleComponent))]
|
||||
public class BuckleComponent : SharedBuckleComponent
|
||||
{
|
||||
private bool _buckled;
|
||||
private int? _originalDrawDepth;
|
||||
|
||||
public override bool Buckled => _buckled;
|
||||
|
||||
public override bool TryBuckle(IEntity? user, IEntity to)
|
||||
{
|
||||
// TODO: Prediction
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState is not BuckleComponentState buckle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_buckled = buckle.Buckled;
|
||||
LastEntityBuckledTo = buckle.LastEntityBuckledTo;
|
||||
DontCollide = buckle.DontCollide;
|
||||
|
||||
if (!Owner.TryGetComponent(out SpriteComponent? ownerSprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_buckled && buckle.DrawDepth.HasValue)
|
||||
{
|
||||
_originalDrawDepth ??= ownerSprite.DrawDepth;
|
||||
ownerSprite.DrawDepth = buckle.DrawDepth.Value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_originalDrawDepth.HasValue && !buckle.DrawDepth.HasValue)
|
||||
{
|
||||
ownerSprite.DrawDepth = _originalDrawDepth.Value;
|
||||
_originalDrawDepth = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Buckle;
|
||||
using Content.Shared.GameObjects.Components.Strap;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Buckle
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class BuckleVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
if (!component.TryGetData<bool>(BuckleVisuals.Buckled, out var buckled) ||
|
||||
!buckled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!component.TryGetData<int>(StrapVisuals.RotationAngle, out var angle))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetRotation(component, Angle.FromDegrees(angle));
|
||||
}
|
||||
|
||||
private void SetRotation(AppearanceComponent component, Angle rotation)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (!sprite.Owner.TryGetComponent(out AnimationPlayerComponent? animation))
|
||||
{
|
||||
sprite.Rotation = rotation;
|
||||
return;
|
||||
}
|
||||
|
||||
if (animation.HasRunningAnimation("rotate"))
|
||||
{
|
||||
animation.Stop("rotate");
|
||||
}
|
||||
|
||||
animation.Play(new Animation
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(0.125),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(ISpriteComponent),
|
||||
Property = nameof(ISpriteComponent.Rotation),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(sprite.Rotation, 0),
|
||||
new AnimationTrackProperty.KeyFrame(rotation, 0.125f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "rotate");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
using System;
|
||||
using Content.Client.UserInterface.Cargo;
|
||||
using Content.Shared.GameObjects.Components.Cargo;
|
||||
using Content.Shared.Prototypes.Cargo;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.GameObjects.Components.Cargo.SharedCargoConsoleComponent;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Cargo
|
||||
{
|
||||
public class CargoConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private CargoConsoleMenu? _menu;
|
||||
|
||||
[ViewVariables]
|
||||
private CargoConsoleOrderMenu? _orderMenu;
|
||||
|
||||
[ViewVariables]
|
||||
public GalacticMarketComponent? Market { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public CargoOrderDatabaseComponent? Orders { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public bool RequestOnly { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public int BankId { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public string? BankName { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public int BankBalance { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public (int CurrentCapacity, int MaxCapacity) ShuttleCapacity { get; private set; }
|
||||
|
||||
private CargoProductPrototype? _product;
|
||||
|
||||
public CargoConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
if (!Owner.Owner.TryGetComponent(out GalacticMarketComponent? market) ||
|
||||
!Owner.Owner.TryGetComponent(out CargoOrderDatabaseComponent? orders)) return;
|
||||
|
||||
Market = market;
|
||||
Orders = orders;
|
||||
|
||||
_menu = new CargoConsoleMenu(this);
|
||||
_orderMenu = new CargoConsoleOrderMenu();
|
||||
|
||||
_menu.OnClose += Close;
|
||||
|
||||
_menu.Populate();
|
||||
|
||||
Market.OnDatabaseUpdated += _menu.PopulateProducts;
|
||||
Market.OnDatabaseUpdated += _menu.PopulateCategories;
|
||||
Orders.OnDatabaseUpdated += _menu.PopulateOrders;
|
||||
|
||||
_menu.CallShuttleButton.OnPressed += (_) =>
|
||||
{
|
||||
SendMessage(new CargoConsoleShuttleMessage());
|
||||
};
|
||||
_menu.OnItemSelected += (args) =>
|
||||
{
|
||||
if (args.Button.Parent is not CargoProductRow row)
|
||||
return;
|
||||
_product = row.Product;
|
||||
_orderMenu.Requester.Text = "";
|
||||
_orderMenu.Reason.Text = "";
|
||||
_orderMenu.Amount.Value = 1;
|
||||
_orderMenu.OpenCentered();
|
||||
};
|
||||
_menu.OnOrderApproved += ApproveOrder;
|
||||
_menu.OnOrderCanceled += RemoveOrder;
|
||||
_orderMenu.SubmitButton.OnPressed += (_) =>
|
||||
{
|
||||
if (AddOrder())
|
||||
{
|
||||
_orderMenu.Close();
|
||||
}
|
||||
};
|
||||
|
||||
_menu.OpenCentered();
|
||||
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not CargoConsoleInterfaceState cState)
|
||||
return;
|
||||
if (RequestOnly != cState.RequestOnly)
|
||||
{
|
||||
RequestOnly = cState.RequestOnly;
|
||||
_menu?.UpdateRequestOnly();
|
||||
}
|
||||
BankId = cState.BankId;
|
||||
BankName = cState.BankName;
|
||||
BankBalance = cState.BankBalance;
|
||||
ShuttleCapacity = cState.ShuttleCapacity;
|
||||
_menu?.UpdateCargoCapacity();
|
||||
_menu?.UpdateBankData();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (!disposing) return;
|
||||
|
||||
if (Market != null && _menu != null)
|
||||
{
|
||||
Market.OnDatabaseUpdated -= _menu.PopulateProducts;
|
||||
Market.OnDatabaseUpdated -= _menu.PopulateCategories;
|
||||
}
|
||||
|
||||
if (Orders != null && _menu != null)
|
||||
{
|
||||
Orders.OnDatabaseUpdated -= _menu.PopulateOrders;
|
||||
}
|
||||
|
||||
_menu?.Dispose();
|
||||
_orderMenu?.Dispose();
|
||||
}
|
||||
|
||||
private bool AddOrder()
|
||||
{
|
||||
int orderAmt = _orderMenu?.Amount.Value ?? 0;
|
||||
if (orderAmt < 1 || orderAmt > ShuttleCapacity.MaxCapacity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SendMessage(new CargoConsoleAddOrderMessage(
|
||||
_orderMenu?.Requester.Text ?? "",
|
||||
_orderMenu?.Reason.Text ?? "",
|
||||
_product?.ID ?? "",
|
||||
orderAmt));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RemoveOrder(ButtonEventArgs args)
|
||||
{
|
||||
if (args.Button.Parent?.Parent is not CargoOrderRow row || row.Order == null)
|
||||
return;
|
||||
|
||||
SendMessage(new CargoConsoleRemoveOrderMessage(row.Order.OrderNumber));
|
||||
}
|
||||
|
||||
private void ApproveOrder(ButtonEventArgs args)
|
||||
{
|
||||
if (args.Button.Parent?.Parent is not CargoOrderRow row || row.Order == null)
|
||||
return;
|
||||
|
||||
if (ShuttleCapacity.CurrentCapacity == ShuttleCapacity.MaxCapacity)
|
||||
return;
|
||||
|
||||
SendMessage(new CargoConsoleApproveOrderMessage(row.Order.OrderNumber));
|
||||
_menu?.UpdateCargoCapacity();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects.Components.Cargo;
|
||||
using Content.Shared.Prototypes.Cargo;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Cargo
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class CargoOrderDatabaseComponent : SharedCargoOrderDatabaseComponent
|
||||
{
|
||||
private readonly List<CargoOrderData> _orders = new();
|
||||
|
||||
public IReadOnlyList<CargoOrderData> Orders => _orders;
|
||||
/// <summary>
|
||||
/// Event called when the database is updated.
|
||||
/// </summary>
|
||||
public event Action? OnDatabaseUpdated;
|
||||
|
||||
// TODO add account selector menu
|
||||
|
||||
/// <summary>
|
||||
/// Removes all orders from the database.
|
||||
/// </summary>
|
||||
public virtual void Clear()
|
||||
{
|
||||
_orders.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an order to the database.
|
||||
/// </summary>
|
||||
/// <param name="order">The order to be added.</param>
|
||||
public virtual void AddOrder(CargoOrderData order)
|
||||
{
|
||||
if (!_orders.Contains(order))
|
||||
_orders.Add(order);
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
if (curState is not CargoOrderDatabaseState state)
|
||||
return;
|
||||
Clear();
|
||||
if (state.Orders == null)
|
||||
return;
|
||||
foreach (var order in state.Orders)
|
||||
{
|
||||
AddOrder(order);
|
||||
}
|
||||
|
||||
OnDatabaseUpdated?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Cargo;
|
||||
using Content.Shared.Prototypes.Cargo;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Cargo
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GalacticMarketComponent : SharedGalacticMarketComponent
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Event called when the database is updated.
|
||||
/// </summary>
|
||||
public event Action? OnDatabaseUpdated;
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
if (curState is not GalacticMarketState state)
|
||||
return;
|
||||
_productIds.Clear();
|
||||
foreach (var productId in state.Products)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(productId, out CargoProductPrototype? product))
|
||||
continue;
|
||||
_products.Add(product);
|
||||
}
|
||||
|
||||
OnDatabaseUpdated?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Chemistry.ReagentDispenser;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using static Content.Shared.GameObjects.Components.Chemistry.ChemMaster.SharedChemMasterComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry.ChemMaster
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="ChemMasterWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class ChemMasterBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private ChemMasterWindow? _window;
|
||||
|
||||
public ChemMasterBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called each time a chem master UI instance is opened. Generates the window and fills it with
|
||||
/// relevant info. Sets the actions for static buttons.
|
||||
/// </summary>
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
//Setup window layout/elements
|
||||
_window = new ChemMasterWindow
|
||||
{
|
||||
Title = Loc.GetString("ChemMaster 4000"),
|
||||
};
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
//Setup static button actions.
|
||||
_window.EjectButton.OnPressed += _ => PrepareData(UiAction.Eject, null, null, null);
|
||||
_window.BufferTransferButton.OnPressed += _ => PrepareData(UiAction.Transfer, null, null, null);
|
||||
_window.BufferDiscardButton.OnPressed += _ => PrepareData(UiAction.Discard, null, null, null);
|
||||
_window.CreatePills.OnPressed += _ => PrepareData(UiAction.CreatePills, null, _window.PillAmount.Value, null);
|
||||
_window.CreateBottles.OnPressed += _ => PrepareData(UiAction.CreateBottles, null, null, _window.BottleAmount.Value);
|
||||
|
||||
_window.OnChemButtonPressed += (args, button) => PrepareData(UiAction.ChemButton, button, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the ui each time new state data is sent from the server.
|
||||
/// </summary>
|
||||
/// <param name="state">
|
||||
/// Data of the <see cref="SharedReagentDispenserComponent"/> that this ui represents.
|
||||
/// Sent from the server.
|
||||
/// </param>
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
var castState = (ChemMasterBoundUserInterfaceState)state;
|
||||
|
||||
_window?.UpdateState(castState); //Update window state
|
||||
}
|
||||
|
||||
private void PrepareData(UiAction action, ChemButton? button, int? pillAmount, int? bottleAmount)
|
||||
{
|
||||
if (button != null)
|
||||
{
|
||||
SendMessage(new UiActionMessage(action, button.Amount, button.Id, button.isBuffer, null, null));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessage(new UiActionMessage(action, null, null, null, pillAmount, bottleAmount));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,439 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Chemistry.ChemMaster;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.GameObjects.Components.Chemistry.ChemMaster.SharedChemMasterComponent;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry.ChemMaster
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedChemMasterComponent"/>
|
||||
/// </summary>
|
||||
public class ChemMasterWindow : SS14Window
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
/// <summary>Contains info about the reagent container such as it's contents, if one is loaded into the dispenser.</summary>
|
||||
private readonly VBoxContainer ContainerInfo;
|
||||
|
||||
private readonly VBoxContainer BufferInfo;
|
||||
|
||||
private readonly VBoxContainer PackagingInfo;
|
||||
|
||||
/// <summary>Ejects the reagent container from the dispenser.</summary>
|
||||
public Button EjectButton { get; }
|
||||
|
||||
public Button BufferTransferButton { get; }
|
||||
public Button BufferDiscardButton { get; }
|
||||
|
||||
public bool BufferModeTransfer = true;
|
||||
|
||||
public event Action<ButtonEventArgs, ChemButton>? OnChemButtonPressed;
|
||||
|
||||
public HBoxContainer PillInfo { get; set; }
|
||||
public HBoxContainer BottleInfo { get; set; }
|
||||
public SpinBox PillAmount { get; set; }
|
||||
public SpinBox BottleAmount { get; set; }
|
||||
public Button CreatePills { get; }
|
||||
public Button CreateBottles { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create and initialize the chem master UI client-side. Creates the basic layout,
|
||||
/// actual data isn't filled in until the server sends data about the chem master.
|
||||
/// </summary>
|
||||
public ChemMasterWindow()
|
||||
{
|
||||
MinSize = SetSize = (400, 525);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Contents.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
//Container
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Container")},
|
||||
new Control {HorizontalExpand = true},
|
||||
(EjectButton = new Button {Text = Loc.GetString("Eject")})
|
||||
}
|
||||
},
|
||||
//Wrap the container info in a PanelContainer so we can color it's background differently.
|
||||
new PanelContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
SizeFlagsStretchRatio = 6,
|
||||
MinSize = (0, 200),
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = new Color(27, 27, 30)
|
||||
},
|
||||
Children =
|
||||
{
|
||||
//Currently empty, when server sends state data this will have container contents and fill volume.
|
||||
(ContainerInfo = new VBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("No container loaded.")
|
||||
}
|
||||
}
|
||||
}),
|
||||
}
|
||||
},
|
||||
|
||||
//Padding
|
||||
new Control {MinSize = (0.0f, 10.0f)},
|
||||
|
||||
//Buffer
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Buffer")},
|
||||
new Control {HorizontalExpand = true},
|
||||
(BufferTransferButton = new Button {Text = Loc.GetString("Transfer"), Pressed = BufferModeTransfer, StyleClasses = { StyleBase.ButtonOpenRight }}),
|
||||
(BufferDiscardButton = new Button {Text = Loc.GetString("Discard"), Pressed = !BufferModeTransfer, StyleClasses = { StyleBase.ButtonOpenLeft }})
|
||||
}
|
||||
},
|
||||
|
||||
//Wrap the buffer info in a PanelContainer so we can color it's background differently.
|
||||
new PanelContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
SizeFlagsStretchRatio = 6,
|
||||
MinSize = (0, 100),
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = new Color(27, 27, 30)
|
||||
},
|
||||
Children =
|
||||
{
|
||||
//Buffer reagent list
|
||||
(BufferInfo = new VBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("Buffer empty.")
|
||||
}
|
||||
}
|
||||
}),
|
||||
}
|
||||
},
|
||||
|
||||
//Padding
|
||||
new Control {MinSize = (0.0f, 10.0f)},
|
||||
|
||||
//Packaging
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Packaging ")},
|
||||
}
|
||||
},
|
||||
|
||||
//Wrap the packaging info in a PanelContainer so we can color it's background differently.
|
||||
new PanelContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
SizeFlagsStretchRatio = 6,
|
||||
MinSize = (0, 100),
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = new Color(27, 27, 30)
|
||||
},
|
||||
Children =
|
||||
{
|
||||
//Packaging options
|
||||
(PackagingInfo = new VBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
}),
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
//Pills
|
||||
PillInfo = new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("Pills:")
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
PackagingInfo.AddChild(PillInfo);
|
||||
|
||||
var pillPadding = new Control {HorizontalExpand = true};
|
||||
PillInfo.AddChild(pillPadding);
|
||||
|
||||
PillAmount = new SpinBox
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Value = 1
|
||||
};
|
||||
PillAmount.InitDefaultButtons();
|
||||
PillAmount.IsValid = (n) => (n > 0 && n <= 10);
|
||||
PillInfo.AddChild(PillAmount);
|
||||
|
||||
var pillVolume = new Label
|
||||
{
|
||||
Text = " max 50u/each ",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
};
|
||||
PillInfo.AddChild((pillVolume));
|
||||
|
||||
CreatePills = new Button {Text = Loc.GetString("Create")};
|
||||
PillInfo.AddChild(CreatePills);
|
||||
|
||||
//Bottles
|
||||
BottleInfo = new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("Bottles:")
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
PackagingInfo.AddChild(BottleInfo);
|
||||
|
||||
var bottlePadding = new Control {HorizontalExpand = true};
|
||||
BottleInfo.AddChild(bottlePadding);
|
||||
|
||||
BottleAmount = new SpinBox
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Value = 1
|
||||
};
|
||||
BottleAmount.InitDefaultButtons();
|
||||
BottleAmount.IsValid = (n) => (n > 0 && n <= 10);
|
||||
BottleInfo.AddChild(BottleAmount);
|
||||
|
||||
var bottleVolume = new Label
|
||||
{
|
||||
Text = " max 30u/each ",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
};
|
||||
BottleInfo.AddChild((bottleVolume));
|
||||
|
||||
CreateBottles = new Button {Text = Loc.GetString("Create")};
|
||||
BottleInfo.AddChild(CreateBottles);
|
||||
}
|
||||
|
||||
private ChemButton MakeChemButton(string text, ReagentUnit amount, string id, bool isBuffer, string styleClass)
|
||||
{
|
||||
var button = new ChemButton(text, amount, id, isBuffer, styleClass);
|
||||
button.OnPressed += args
|
||||
=> OnChemButtonPressed?.Invoke(args, button);
|
||||
return button;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the UI state when new state data is received from the server.
|
||||
/// </summary>
|
||||
/// <param name="state">State data sent by the server.</param>
|
||||
public void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
var castState = (ChemMasterBoundUserInterfaceState) state;
|
||||
Title = castState.DispenserName;
|
||||
UpdatePanelInfo(castState);
|
||||
if (Contents.Children != null)
|
||||
{
|
||||
SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||
EjectButton.Disabled = !castState.HasBeaker;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This searches recursively through all the children of "parent"
|
||||
/// and sets the Disabled value of any buttons found to "val"
|
||||
/// </summary>
|
||||
/// <param name="parent">The control which childrens get searched</param>
|
||||
/// <param name="val">The value to which disabled gets set</param>
|
||||
private void SetButtonDisabledRecursive(Control parent, bool val)
|
||||
{
|
||||
foreach (var child in parent.Children)
|
||||
{
|
||||
if (child is Button but)
|
||||
{
|
||||
but.Disabled = val;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.Children != null)
|
||||
{
|
||||
SetButtonDisabledRecursive(child, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the container, buffer, and packaging panels.
|
||||
/// </summary>
|
||||
/// <param name="state">State data for the dispenser.</param>
|
||||
private void UpdatePanelInfo(ChemMasterBoundUserInterfaceState state)
|
||||
{
|
||||
BufferModeTransfer = state.BufferModeTransfer;
|
||||
BufferTransferButton.Pressed = BufferModeTransfer;
|
||||
BufferDiscardButton.Pressed = !BufferModeTransfer;
|
||||
|
||||
ContainerInfo.Children.Clear();
|
||||
|
||||
if (!state.HasBeaker)
|
||||
{
|
||||
ContainerInfo.Children.Add(new Label {Text = Loc.GetString("No container loaded.")});
|
||||
return;
|
||||
}
|
||||
|
||||
ContainerInfo.Children.Add(new HBoxContainer // Name of the container and its fill status (Ex: 44/100u)
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = $"{state.ContainerName}: "},
|
||||
new Label
|
||||
{
|
||||
Text = $"{state.BeakerCurrentVolume}/{state.BeakerMaxVolume}",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var reagent in state.ContainerReagents)
|
||||
{
|
||||
var name = Loc.GetString("Unknown reagent");
|
||||
//Try to the prototype for the given reagent. This gives us it's name.
|
||||
if (_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
|
||||
{
|
||||
name = proto.Name;
|
||||
}
|
||||
|
||||
if (proto != null)
|
||||
{
|
||||
ContainerInfo.Children.Add(new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = $"{name}: "},
|
||||
new Label
|
||||
{
|
||||
Text = $"{reagent.Quantity}u",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
},
|
||||
|
||||
//Padding
|
||||
new Control {HorizontalExpand = true},
|
||||
|
||||
MakeChemButton("1", ReagentUnit.New(1), reagent.ReagentId, false, StyleBase.ButtonOpenRight),
|
||||
MakeChemButton("5", ReagentUnit.New(5), reagent.ReagentId, false, StyleBase.ButtonOpenBoth),
|
||||
MakeChemButton("10", ReagentUnit.New(10), reagent.ReagentId, false, StyleBase.ButtonOpenBoth),
|
||||
MakeChemButton("25", ReagentUnit.New(25), reagent.ReagentId, false, StyleBase.ButtonOpenBoth),
|
||||
MakeChemButton("All", ReagentUnit.New(-1), reagent.ReagentId, false, StyleBase.ButtonOpenLeft),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
BufferInfo.Children.Clear();
|
||||
|
||||
if (!state.BufferReagents.Any())
|
||||
{
|
||||
BufferInfo.Children.Add(new Label {Text = Loc.GetString("Buffer empty.")});
|
||||
return;
|
||||
}
|
||||
|
||||
var bufferHBox = new HBoxContainer();
|
||||
BufferInfo.AddChild(bufferHBox);
|
||||
|
||||
var bufferLabel = new Label {Text = "buffer: "};
|
||||
bufferHBox.AddChild(bufferLabel);
|
||||
var bufferVol = new Label
|
||||
{
|
||||
Text = $"{state.BufferCurrentVolume}",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
};
|
||||
bufferHBox.AddChild(bufferVol);
|
||||
|
||||
foreach (var reagent in state.BufferReagents)
|
||||
{
|
||||
var name = Loc.GetString("Unknown reagent");
|
||||
//Try to the prototype for the given reagent. This gives us it's name.
|
||||
if (_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
|
||||
{
|
||||
name = proto.Name;
|
||||
}
|
||||
|
||||
if (proto != null)
|
||||
{
|
||||
BufferInfo.Children.Add(new HBoxContainer
|
||||
{
|
||||
//SizeFlagsHorizontal = SizeFlags.ShrinkEnd,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = $"{name}: "},
|
||||
new Label
|
||||
{
|
||||
Text = $"{reagent.Quantity}u",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
},
|
||||
|
||||
//Padding
|
||||
new Control {HorizontalExpand = true},
|
||||
|
||||
MakeChemButton("1", ReagentUnit.New(1), reagent.ReagentId, true, StyleBase.ButtonOpenRight),
|
||||
MakeChemButton("5", ReagentUnit.New(5), reagent.ReagentId, true, StyleBase.ButtonOpenBoth),
|
||||
MakeChemButton("10", ReagentUnit.New(10), reagent.ReagentId, true, StyleBase.ButtonOpenBoth),
|
||||
MakeChemButton("25", ReagentUnit.New(25), reagent.ReagentId, true, StyleBase.ButtonOpenBoth),
|
||||
MakeChemButton("All", ReagentUnit.New(-1), reagent.ReagentId, true, StyleBase.ButtonOpenLeft),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ChemButton : Button
|
||||
{
|
||||
public ReagentUnit Amount { get; set; }
|
||||
public bool isBuffer = true;
|
||||
public string Id { get; set; }
|
||||
public ChemButton(string _text, ReagentUnit _amount, string _id, bool _isBuffer, string _styleClass)
|
||||
{
|
||||
AddStyleClass(_styleClass);
|
||||
Text = _text;
|
||||
Amount = _amount;
|
||||
Id = _id;
|
||||
isBuffer = _isBuffer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class FoamVisualizer : AppearanceVisualizer, ISerializationHooks
|
||||
{
|
||||
private const string AnimationKey = "foamdissolve_animation";
|
||||
|
||||
[DataField("animationTime")]
|
||||
private float _delay = 0.6f;
|
||||
|
||||
[DataField("animationState")]
|
||||
private string _state = "foam-dissolve";
|
||||
|
||||
private Animation _foamDissolve = new();
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
_foamDissolve = new Animation {Length = TimeSpan.FromSeconds(_delay)};
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
_foamDissolve.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = FoamVisualLayers.Base;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame(_state, 0f));
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.TryGetData<bool>(FoamVisuals.State, out var state))
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
if (component.Owner.TryGetComponent(out AnimationPlayerComponent? animPlayer))
|
||||
{
|
||||
if (!animPlayer.HasRunningAnimation(AnimationKey))
|
||||
animPlayer.Play(_foamDissolve, AnimationKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (component.TryGetData<Color>(FoamVisuals.Color, out var color))
|
||||
{
|
||||
if (component.Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
sprite.Color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum FoamVisualLayers : byte
|
||||
{
|
||||
Base
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class HyposprayComponent : SharedHyposprayComponent, IItemStatus
|
||||
{
|
||||
[ViewVariables] private ReagentUnit CurrentVolume { get; set; }
|
||||
[ViewVariables] private ReagentUnit TotalVolume { get; set; }
|
||||
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState is not HyposprayComponentState cState)
|
||||
return;
|
||||
|
||||
CurrentVolume = cState.CurVolume;
|
||||
TotalVolume = cState.MaxVolume;
|
||||
_uiUpdateNeeded = true;
|
||||
}
|
||||
|
||||
Control IItemStatus.MakeControl()
|
||||
{
|
||||
return new StatusControl(this);
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly HyposprayComponent _parent;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
public StatusControl(HyposprayComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}};
|
||||
AddChild(_label);
|
||||
|
||||
parent._uiUpdateNeeded = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
if (!_parent._uiUpdateNeeded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_parent._uiUpdateNeeded = false;
|
||||
|
||||
_label.SetMarkup(Loc.GetString(
|
||||
"Volume: [color=white]{0}/{1}[/color]",
|
||||
_parent.CurrentVolume, _parent.TotalVolume));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry
|
||||
{
|
||||
/// <summary>
|
||||
/// Client behavior for injectors & syringes. Used for item status on injectors
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class InjectorComponent : SharedInjectorComponent, IItemStatus
|
||||
{
|
||||
[ViewVariables] private ReagentUnit CurrentVolume { get; set; }
|
||||
[ViewVariables] private ReagentUnit TotalVolume { get; set; }
|
||||
[ViewVariables] private InjectorToggleMode CurrentMode { get; set; }
|
||||
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
|
||||
|
||||
//Add/remove item status code
|
||||
Control IItemStatus.MakeControl() => new StatusControl(this);
|
||||
void IItemStatus.DestroyControl(Control control) { }
|
||||
|
||||
//Handle net updates
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState is not InjectorComponentState state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentVolume = state.CurrentVolume;
|
||||
TotalVolume = state.TotalVolume;
|
||||
CurrentMode = state.CurrentMode;
|
||||
_uiUpdateNeeded = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Item status control for injectors
|
||||
/// </summary>
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly InjectorComponent _parent;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
public StatusControl(InjectorComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
AddChild(_label);
|
||||
|
||||
parent._uiUpdateNeeded = true;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
if (!_parent._uiUpdateNeeded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_parent._uiUpdateNeeded = false;
|
||||
|
||||
//Update current volume and injector state
|
||||
var modeStringLocalized = _parent.CurrentMode switch
|
||||
{
|
||||
InjectorToggleMode.Draw => Loc.GetString("Draw"),
|
||||
InjectorToggleMode.Inject => Loc.GetString("Inject"),
|
||||
_ => Loc.GetString("Invalid")
|
||||
};
|
||||
_label.SetMarkup(Loc.GetString("Volume: [color=white]{0}/{1}[/color] | [color=white]{2}[/color]",
|
||||
_parent.CurrentVolume, _parent.TotalVolume, modeStringLocalized));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.GameObjects.Components.Chemistry.ReagentDispenser;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using static Content.Shared.GameObjects.Components.Chemistry.ReagentDispenser.SharedReagentDispenserComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry.ReagentDispenser
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="ReagentDispenserWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class ReagentDispenserBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private ReagentDispenserWindow? _window;
|
||||
private ReagentDispenserBoundUserInterfaceState? _lastState;
|
||||
|
||||
public ReagentDispenserBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called each time a dispenser UI instance is opened. Generates the dispenser window and fills it with
|
||||
/// relevant info. Sets the actions for static buttons.
|
||||
/// <para>Buttons which can change like reagent dispense buttons have their actions set in <see cref="UpdateReagentsList"/>.</para>
|
||||
/// </summary>
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
//Setup window layout/elements
|
||||
_window = new ReagentDispenserWindow
|
||||
{
|
||||
Title = Loc.GetString("Reagent dispenser"),
|
||||
};
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
//Setup static button actions.
|
||||
_window.EjectButton.OnPressed += _ => ButtonPressed(UiButton.Eject);
|
||||
_window.ClearButton.OnPressed += _ => ButtonPressed(UiButton.Clear);
|
||||
_window.DispenseButton1.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount1);
|
||||
_window.DispenseButton5.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount5);
|
||||
_window.DispenseButton10.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount10);
|
||||
_window.DispenseButton15.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount15);
|
||||
_window.DispenseButton20.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount20);
|
||||
_window.DispenseButton25.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount25);
|
||||
_window.DispenseButton30.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount30);
|
||||
_window.DispenseButton50.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount50);
|
||||
_window.DispenseButton100.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount100);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the ui each time new state data is sent from the server.
|
||||
/// </summary>
|
||||
/// <param name="state">
|
||||
/// Data of the <see cref="SharedReagentDispenserComponent"/> that this ui represents.
|
||||
/// Sent from the server.
|
||||
/// </param>
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
var castState = (ReagentDispenserBoundUserInterfaceState)state;
|
||||
_lastState = castState;
|
||||
|
||||
UpdateReagentsList(castState.Inventory); //Update reagents list & reagent button actions
|
||||
_window?.UpdateState(castState); //Update window state
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the list of reagents that this dispenser can dispense on the UI.
|
||||
/// </summary>
|
||||
/// <param name="inventory">A list of the reagents which can be dispensed.</param>
|
||||
private void UpdateReagentsList(List<ReagentDispenserInventoryEntry> inventory)
|
||||
{
|
||||
if (_window == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window.UpdateReagentsList(inventory);
|
||||
|
||||
for (var i = 0; i < _window.ChemicalList.Children.Count(); i++)
|
||||
{
|
||||
var button = (Button)_window.ChemicalList.Children.ElementAt(i);
|
||||
var i1 = i;
|
||||
button.OnPressed += _ => ButtonPressed(UiButton.Dispense, i1);
|
||||
button.OnMouseEntered += _ =>
|
||||
{
|
||||
if (_lastState != null)
|
||||
{
|
||||
_window.UpdateContainerInfo(_lastState, inventory[i1].ID);
|
||||
}
|
||||
};
|
||||
button.OnMouseExited += _ =>
|
||||
{
|
||||
if (_lastState != null)
|
||||
{
|
||||
_window.UpdateContainerInfo(_lastState);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonPressed(UiButton button, int dispenseIndex = -1)
|
||||
{
|
||||
SendMessage(new UiButtonPressedMessage(button, dispenseIndex));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,331 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Chemistry.ReagentDispenser;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.GameObjects.Components.Chemistry.ReagentDispenser.SharedReagentDispenserComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry.ReagentDispenser
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedReagentDispenserComponent"/>
|
||||
/// </summary>
|
||||
public class ReagentDispenserWindow : SS14Window
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
/// <summary>Contains info about the reagent container such as it's contents, if one is loaded into the dispenser.</summary>
|
||||
private readonly VBoxContainer ContainerInfo;
|
||||
|
||||
/// <summary>Sets the dispense amount to 1 when pressed.</summary>
|
||||
public Button DispenseButton1 { get; }
|
||||
|
||||
/// <summary>Sets the dispense amount to 5 when pressed.</summary>
|
||||
public Button DispenseButton5 { get; }
|
||||
|
||||
/// <summary>Sets the dispense amount to 10 when pressed.</summary>
|
||||
public Button DispenseButton10 { get; }
|
||||
|
||||
/// <summary>Sets the dispense amount to 15 when pressed.</summary>
|
||||
public Button DispenseButton15 { get; }
|
||||
|
||||
/// <summary>Sets the dispense amount to 20 when pressed.</summary>
|
||||
public Button DispenseButton20 { get; }
|
||||
|
||||
/// <summary>Sets the dispense amount to 25 when pressed.</summary>
|
||||
public Button DispenseButton25 { get; }
|
||||
|
||||
/// <summary>Sets the dispense amount to 30 when pressed.</summary>
|
||||
public Button DispenseButton30 { get; }
|
||||
|
||||
/// <summary>Sets the dispense amount to 50 when pressed.</summary>
|
||||
public Button DispenseButton50 { get; }
|
||||
|
||||
/// <summary>Sets the dispense amount to 100 when pressed.</summary>
|
||||
public Button DispenseButton100 { get; }
|
||||
|
||||
/// <summary>Ejects the reagent container from the dispenser.</summary>
|
||||
public Button ClearButton { get; }
|
||||
|
||||
/// <summary>Removes all reagents from the reagent container.</summary>
|
||||
public Button EjectButton { get; }
|
||||
|
||||
/// <summary>A grid of buttons for each reagent which can be dispensed.</summary>
|
||||
public GridContainer ChemicalList { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create and initialize the dispenser UI client-side. Creates the basic layout,
|
||||
/// actual data isn't filled in until the server sends data about the dispenser.
|
||||
/// </summary>
|
||||
public ReagentDispenserWindow()
|
||||
{
|
||||
SetSize = MinSize = (590, 400);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var dispenseAmountGroup = new ButtonGroup();
|
||||
|
||||
Contents.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
//First, our dispense amount buttons
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Amount")},
|
||||
//Padding
|
||||
new Control {MinSize = (20, 0)},
|
||||
(DispenseButton1 = new Button {Text = "1", Group = dispenseAmountGroup, StyleClasses = { StyleBase.ButtonOpenRight }}),
|
||||
(DispenseButton5 = new Button {Text = "5", Group = dispenseAmountGroup, StyleClasses = { StyleBase.ButtonOpenBoth }}),
|
||||
(DispenseButton10 = new Button {Text = "10", Group = dispenseAmountGroup, StyleClasses = { StyleBase.ButtonOpenBoth }}),
|
||||
(DispenseButton15 = new Button {Text = "15", Group = dispenseAmountGroup, StyleClasses = { StyleBase.ButtonOpenBoth }}),
|
||||
(DispenseButton20 = new Button {Text = "20", Group = dispenseAmountGroup, StyleClasses = { StyleBase.ButtonOpenBoth }}),
|
||||
(DispenseButton25 = new Button {Text = "25", Group = dispenseAmountGroup, StyleClasses = { StyleBase.ButtonOpenBoth }}),
|
||||
(DispenseButton30 = new Button {Text = "30", Group = dispenseAmountGroup, StyleClasses = { StyleBase.ButtonOpenBoth }}),
|
||||
(DispenseButton50 = new Button {Text = "50", Group = dispenseAmountGroup, StyleClasses = { StyleBase.ButtonOpenBoth }}),
|
||||
(DispenseButton100 = new Button {Text = "100", Group = dispenseAmountGroup, StyleClasses = { StyleBase.ButtonOpenLeft }}),
|
||||
}
|
||||
},
|
||||
//Padding
|
||||
new Control {MinSize = (0.0f, 10.0f)},
|
||||
//Grid of which reagents can be dispensed.
|
||||
(ChemicalList = new GridContainer
|
||||
{
|
||||
Columns = 5
|
||||
}),
|
||||
//Padding
|
||||
new Control {MinSize = (0.0f, 10.0f)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Container: ")},
|
||||
(ClearButton = new Button {Text = Loc.GetString("Clear"), StyleClasses = {StyleBase.ButtonOpenRight}}),
|
||||
(EjectButton = new Button {Text = Loc.GetString("Eject"), StyleClasses = {StyleBase.ButtonOpenLeft}})
|
||||
}
|
||||
},
|
||||
//Wrap the container info in a PanelContainer so we can color it's background differently.
|
||||
new PanelContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
SizeFlagsStretchRatio = 6,
|
||||
MinSize = (0, 150),
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = new Color(27, 27, 30)
|
||||
},
|
||||
Children =
|
||||
{
|
||||
//Currently empty, when server sends state data this will have container contents and fill volume.
|
||||
(ContainerInfo = new VBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("No container loaded.")
|
||||
}
|
||||
}
|
||||
}),
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the button grid of reagents which can be dispensed.
|
||||
/// <para>The actions for these buttons are set in <see cref="ReagentDispenserBoundUserInterface.UpdateReagentsList"/>.</para>
|
||||
/// </summary>
|
||||
/// <param name="inventory">Reagents which can be dispensed by this dispenser</param>
|
||||
public void UpdateReagentsList(List<ReagentDispenserInventoryEntry> inventory)
|
||||
{
|
||||
if (ChemicalList == null) return;
|
||||
if (inventory == null) return;
|
||||
|
||||
ChemicalList.Children.Clear();
|
||||
|
||||
foreach (var entry in inventory)
|
||||
{
|
||||
if (_prototypeManager.TryIndex(entry.ID, out ReagentPrototype? proto))
|
||||
{
|
||||
ChemicalList.AddChild(new Button {Text = proto.Name});
|
||||
}
|
||||
else
|
||||
{
|
||||
ChemicalList.AddChild(new Button {Text = Loc.GetString("Reagent name not found")});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This searches recursively through all the children of "parent"
|
||||
/// and sets the Disabled value of any buttons found to "val"
|
||||
/// </summary>
|
||||
/// <param name="parent">The control which childrens get searched</param>
|
||||
/// <param name="val">The value to which disabled gets set</param>
|
||||
private void SetButtonDisabledRecursive(Control parent, bool val)
|
||||
{
|
||||
foreach (var child in parent.Children)
|
||||
{
|
||||
if (child is Button but)
|
||||
{
|
||||
but.Disabled = val;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.Children != null)
|
||||
{
|
||||
SetButtonDisabledRecursive(child, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the UI state when new state data is received from the server.
|
||||
/// </summary>
|
||||
/// <param name="state">State data sent by the server.</param>
|
||||
public void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
var castState = (ReagentDispenserBoundUserInterfaceState) state;
|
||||
Title = castState.DispenserName;
|
||||
UpdateContainerInfo(castState);
|
||||
|
||||
// Disable all buttons if not powered
|
||||
if (Contents.Children != null)
|
||||
{
|
||||
SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||
EjectButton.Disabled = false;
|
||||
}
|
||||
|
||||
// Disable the Clear & Eject button if no beaker
|
||||
if (!castState.HasBeaker)
|
||||
{
|
||||
ClearButton.Disabled = true;
|
||||
EjectButton.Disabled = true;
|
||||
}
|
||||
|
||||
switch (castState.SelectedDispenseAmount.Int())
|
||||
{
|
||||
case 1:
|
||||
DispenseButton1.Pressed = true;
|
||||
break;
|
||||
case 5:
|
||||
DispenseButton5.Pressed = true;
|
||||
break;
|
||||
case 10:
|
||||
DispenseButton10.Pressed = true;
|
||||
break;
|
||||
case 15:
|
||||
DispenseButton15.Pressed = true;
|
||||
break;
|
||||
case 20:
|
||||
DispenseButton20.Pressed = true;
|
||||
break;
|
||||
case 25:
|
||||
DispenseButton25.Pressed = true;
|
||||
break;
|
||||
case 30:
|
||||
DispenseButton30.Pressed = true;
|
||||
break;
|
||||
case 50:
|
||||
DispenseButton50.Pressed = true;
|
||||
break;
|
||||
case 100:
|
||||
DispenseButton100.Pressed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the fill state and list of reagents held by the current reagent container, if applicable.
|
||||
/// <para>Also highlights a reagent if it's dispense button is being mouse hovered.</para>
|
||||
/// </summary>
|
||||
/// <param name="state">State data for the dispenser.</param>
|
||||
/// <param name="highlightedReagentId">Prototype id of the reagent whose dispense button is currently being mouse hovered.</param>
|
||||
public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state, string highlightedReagentId = "")
|
||||
{
|
||||
ContainerInfo.Children.Clear();
|
||||
|
||||
if (!state.HasBeaker)
|
||||
{
|
||||
ContainerInfo.Children.Add(new Label {Text = Loc.GetString("No container loaded.")});
|
||||
return;
|
||||
}
|
||||
|
||||
ContainerInfo.Children.Add(new HBoxContainer // Name of the container and its fill status (Ex: 44/100u)
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = $"{state.ContainerName}: "},
|
||||
new Label
|
||||
{
|
||||
Text = $"{state.BeakerCurrentVolume}/{state.BeakerMaxVolume}",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (state.ContainerReagents == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var reagent in state.ContainerReagents)
|
||||
{
|
||||
var name = Loc.GetString("Unknown reagent");
|
||||
//Try to the prototype for the given reagent. This gives us it's name.
|
||||
if (_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
|
||||
{
|
||||
name = proto.Name;
|
||||
}
|
||||
|
||||
//Check if the reagent is being moused over. If so, color it green.
|
||||
if (proto != null && proto.ID == highlightedReagentId)
|
||||
{
|
||||
ContainerInfo.Children.Add(new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = $"{name}: ",
|
||||
StyleClasses = {StyleNano.StyleClassPowerStateGood}
|
||||
},
|
||||
new Label
|
||||
{
|
||||
Text = $"{reagent.Quantity}u",
|
||||
StyleClasses = {StyleNano.StyleClassPowerStateGood}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else //Otherwise, color it the normal colors.
|
||||
{
|
||||
ContainerInfo.Children.Add(new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = $"{name}: "},
|
||||
new Label
|
||||
{
|
||||
Text = $"{reagent.Quantity}u",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class SmokeVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.TryGetData<Color>(SmokeVisuals.Color, out var color))
|
||||
{
|
||||
if (component.Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
sprite.Color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedSolutionContainerComponent))]
|
||||
public class SolutionContainerComponent : SharedSolutionContainerComponent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class SolutionContainerVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[DataField("maxFillLevels")] private int _maxFillLevels = 0;
|
||||
[DataField("fillBaseName")] private string? _fillBaseName = null;
|
||||
[DataField("layer")] private SolutionContainerLayers _layer = SolutionContainerLayers.Fill;
|
||||
[DataField("changeColor")] private bool _changeColor = true;
|
||||
[DataField("emptySpriteName")] private string? _emptySpriteName = null;
|
||||
[DataField("emptySpriteColor")] private Color _emptySpriteColor = Color.White;
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (_maxFillLevels <= 0 || _fillBaseName == null) return;
|
||||
|
||||
if (!component.TryGetData(SolutionContainerVisuals.VisualState,
|
||||
out SolutionContainerVisualState state)) return;
|
||||
|
||||
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite)) return;
|
||||
if (!sprite.LayerMapTryGet(_layer, out var fillLayer)) return;
|
||||
|
||||
var fillPercent = state.FilledVolumePercent;
|
||||
var closestFillSprite = (int) Math.Round(fillPercent * _maxFillLevels);
|
||||
|
||||
if (closestFillSprite > 0)
|
||||
{
|
||||
sprite.LayerSetVisible(fillLayer, true);
|
||||
|
||||
var stateName = _fillBaseName + closestFillSprite;
|
||||
sprite.LayerSetState(fillLayer, stateName);
|
||||
|
||||
if (_changeColor)
|
||||
sprite.LayerSetColor(fillLayer, state.Color);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_emptySpriteName == null)
|
||||
sprite.LayerSetVisible(fillLayer, false);
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(fillLayer, _emptySpriteName);
|
||||
if (_changeColor)
|
||||
sprite.LayerSetColor(fillLayer, _emptySpriteColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
using System;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class ClickableComponent : Component
|
||||
{
|
||||
public override string Name => "Clickable";
|
||||
|
||||
[Dependency] private readonly IClickMapManager _clickMapManager = default!;
|
||||
|
||||
[ViewVariables] [DataField("bounds")] private DirBoundData _data = DirBoundData.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Used to check whether a click worked.
|
||||
/// </summary>
|
||||
/// <param name="worldPos">The world position that was clicked.</param>
|
||||
/// <param name="drawDepth">
|
||||
/// The draw depth for the sprite that captured the click.
|
||||
/// </param>
|
||||
/// <returns>True if the click worked, false otherwise.</returns>
|
||||
public bool CheckClick(Vector2 worldPos, out int drawDepth, out uint renderOrder)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out ISpriteComponent? sprite) || !sprite.Visible)
|
||||
{
|
||||
drawDepth = default;
|
||||
renderOrder = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var transform = Owner.Transform;
|
||||
var localPos = transform.InvWorldMatrix.Transform(worldPos);
|
||||
var spriteMatrix = Matrix3.Invert(sprite.GetLocalMatrix());
|
||||
|
||||
localPos = spriteMatrix.Transform(localPos);
|
||||
|
||||
var found = false;
|
||||
var worldRotation = transform.WorldRotation;
|
||||
|
||||
if (_data.All.Contains(localPos))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: diagonal support?
|
||||
|
||||
var modAngle = sprite.NoRotation ? SpriteComponent.CalcRectWorldAngle(worldRotation, 4) : Angle.Zero;
|
||||
var dir = sprite.EnableDirectionOverride ? sprite.DirectionOverride : worldRotation.GetCardinalDir();
|
||||
|
||||
modAngle += dir.ToAngle();
|
||||
|
||||
var layerPos = modAngle.RotateVec(localPos);
|
||||
|
||||
var boundsForDir = dir switch
|
||||
{
|
||||
Direction.East => _data.East,
|
||||
Direction.North => _data.North,
|
||||
Direction.South => _data.South,
|
||||
Direction.West => _data.West,
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
|
||||
if (boundsForDir.Contains(layerPos))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
foreach (var layer in sprite.AllLayers)
|
||||
{
|
||||
if (!layer.Visible) continue;
|
||||
|
||||
var dirCount = sprite.GetLayerDirectionCount(layer);
|
||||
var dir = layer.EffectiveDirection(worldRotation);
|
||||
var modAngle = sprite.NoRotation ? SpriteComponent.CalcRectWorldAngle(worldRotation, dirCount) : Angle.Zero;
|
||||
modAngle += dir.Convert().ToAngle();
|
||||
|
||||
var layerPos = modAngle.RotateVec(localPos);
|
||||
|
||||
var localOffset = layerPos * EyeManager.PixelsPerMeter * (1, -1);
|
||||
if (layer.Texture != null)
|
||||
{
|
||||
if (_clickMapManager.IsOccluding(layer.Texture,
|
||||
(Vector2i) (localOffset + layer.Texture.Size / 2f)))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (layer.RsiState != default)
|
||||
{
|
||||
var rsi = layer.ActualRsi;
|
||||
if (rsi == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var (mX, mY) = localOffset + rsi.Size / 2;
|
||||
|
||||
if (_clickMapManager.IsOccluding(rsi, layer.RsiState, dir,
|
||||
layer.AnimationFrame, ((int) mX, (int) mY)))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawDepth = sprite.DrawDepth;
|
||||
renderOrder = sprite.RenderOrder;
|
||||
return found;
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
public sealed class DirBoundData
|
||||
{
|
||||
[ViewVariables] [DataField("all")] public Box2 All;
|
||||
[ViewVariables] [DataField("north")] public Box2 North;
|
||||
[ViewVariables] [DataField("south")] public Box2 South;
|
||||
[ViewVariables] [DataField("east")] public Box2 East;
|
||||
[ViewVariables] [DataField("west")] public Box2 West;
|
||||
|
||||
public static DirBoundData Default { get; } = new();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Spawns a set of entities on the client only, and removes them when this component is removed.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class ClientEntitySpawnerComponent : Component
|
||||
{
|
||||
public override string Name => "ClientEntitySpawner";
|
||||
|
||||
[DataField("prototypes")] private List<string> _prototypes = new() { "HVDummyWire" };
|
||||
|
||||
private readonly List<IEntity> _entity = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SpawnEntities();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
RemoveEntities();
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
private void SpawnEntities()
|
||||
{
|
||||
foreach (var proto in _prototypes)
|
||||
{
|
||||
var entity = Owner.EntityManager.SpawnEntity(proto, Owner.Transform.Coordinates);
|
||||
_entity.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveEntities()
|
||||
{
|
||||
foreach (var entity in _entity)
|
||||
{
|
||||
Owner.EntityManager.DeleteEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using static Content.Shared.GameObjects.Components.Medical.SharedCloningPodComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.CloningPod
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class CloningPodBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
public CloningPodBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private CloningPodWindow? _window;
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
|
||||
_window = new CloningPodWindow(new Dictionary<int, string?>());
|
||||
_window.OnClose += Close;
|
||||
_window.CloneButton.OnPressed += _ =>
|
||||
{
|
||||
if (_window.SelectedScan != null)
|
||||
{
|
||||
SendMessage(new CloningPodUiButtonPressedMessage(UiButton.Clone, (int) _window.SelectedScan));
|
||||
}
|
||||
};
|
||||
_window.EjectButton.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new CloningPodUiButtonPressedMessage(UiButton.Eject, null));
|
||||
};
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
_window?.Populate((CloningPodBoundUserInterfaceState) state);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using static Content.Shared.GameObjects.Components.Medical.SharedCloningPodComponent;
|
||||
using static Content.Shared.GameObjects.Components.Medical.SharedCloningPodComponent.CloningPodStatus;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.CloningPod
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class CloningPodVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
if (!component.TryGetData(CloningPodVisuals.Status, out CloningPodStatus status)) return;
|
||||
sprite.LayerSetState(CloningPodVisualLayers.Machine, StatusToMachineStateId(status));
|
||||
}
|
||||
|
||||
private string StatusToMachineStateId(CloningPodStatus status)
|
||||
{
|
||||
//TODO: implement NoMind for if the mind is not yet in the body
|
||||
//TODO: Find a use for GORE POD
|
||||
switch (status)
|
||||
{
|
||||
case Cloning: return "pod_1";
|
||||
case NoMind: return "pod_e";
|
||||
case Gore: return "pod_g";
|
||||
case Idle: return "pod_0";
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(status), status, "unknown CloningPodStatus");
|
||||
}
|
||||
}
|
||||
|
||||
public enum CloningPodVisualLayers : byte
|
||||
{
|
||||
Machine,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
using static Content.Shared.GameObjects.Components.Medical.SharedCloningPodComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.CloningPod
|
||||
{
|
||||
public sealed class CloningPodWindow : SS14Window
|
||||
{
|
||||
private Dictionary<int, string?> _scanManager;
|
||||
|
||||
private readonly VBoxContainer _scanList;
|
||||
public readonly Button CloneButton;
|
||||
public readonly Button EjectButton;
|
||||
private CloningScanButton? _selectedButton;
|
||||
private readonly Label _progressLabel;
|
||||
private readonly ProgressBar _cloningProgressBar;
|
||||
private readonly Label _mindState;
|
||||
|
||||
private CloningPodBoundUserInterfaceState? _lastUpdate;
|
||||
|
||||
public int? SelectedScan;
|
||||
|
||||
public CloningPodWindow(Dictionary<int, string?> scanManager)
|
||||
{
|
||||
SetSize = MinSize = (250, 300);
|
||||
_scanManager = scanManager;
|
||||
|
||||
Title = Loc.GetString("Cloning Machine");
|
||||
|
||||
Contents.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new ScrollContainer
|
||||
{
|
||||
MinSize = new Vector2(200.0f, 0.0f),
|
||||
VerticalExpand = true,
|
||||
Children =
|
||||
{
|
||||
(_scanList = new VBoxContainer())
|
||||
}
|
||||
},
|
||||
new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(CloneButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("Clone")
|
||||
})
|
||||
}
|
||||
},
|
||||
(_cloningProgressBar = new ProgressBar
|
||||
{
|
||||
MinSize = (200, 20),
|
||||
MinValue = 0,
|
||||
MaxValue = 120,
|
||||
Page = 0,
|
||||
Value = 0.5f,
|
||||
Children =
|
||||
{
|
||||
(_progressLabel = new Label())
|
||||
}
|
||||
}),
|
||||
(EjectButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("Eject Body")
|
||||
}),
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("Neural Interface: ")
|
||||
},
|
||||
(_mindState = new Label()
|
||||
{
|
||||
Text = Loc.GetString("No Activity"),
|
||||
FontColorOverride = Color.Red
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BuildCloneList();
|
||||
}
|
||||
|
||||
public void Populate(CloningPodBoundUserInterfaceState state)
|
||||
{
|
||||
//Ignore useless updates or we can't interact with the UI
|
||||
//TODO: come up with a better comparision, probably write a comparator because '.Equals' doesn't work
|
||||
if (_lastUpdate == null || _lastUpdate.MindIdName.Count != state.MindIdName.Count)
|
||||
{
|
||||
_scanManager = state.MindIdName;
|
||||
BuildCloneList();
|
||||
}
|
||||
_lastUpdate = state;
|
||||
|
||||
_cloningProgressBar.MaxValue = state.Maximum;
|
||||
UpdateProgress();
|
||||
_mindState.Text = Loc.GetString(state.MindPresent ? "Consciousness Detected" : "No Activity");
|
||||
_mindState.FontColorOverride = state.MindPresent ? Color.Green : Color.Red;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
private void UpdateProgress()
|
||||
{
|
||||
if (_lastUpdate == null)
|
||||
return;
|
||||
float simulatedProgress = _lastUpdate.Progress;
|
||||
if (_lastUpdate.Progressing)
|
||||
{
|
||||
TimeSpan sinceReference = IoCManager.Resolve<IGameTiming>().CurTime - _lastUpdate.ReferenceTime;
|
||||
simulatedProgress += (float) sinceReference.TotalSeconds;
|
||||
simulatedProgress = MathHelper.Clamp(simulatedProgress, 0f, _lastUpdate.Maximum);
|
||||
}
|
||||
var percentage = simulatedProgress / _cloningProgressBar.MaxValue * 100;
|
||||
_progressLabel.Text = $"{percentage:0}%";
|
||||
_cloningProgressBar.Value = simulatedProgress;
|
||||
}
|
||||
|
||||
private void BuildCloneList()
|
||||
{
|
||||
_scanList.RemoveAllChildren();
|
||||
_selectedButton = null;
|
||||
|
||||
foreach (var scan in _scanManager)
|
||||
{
|
||||
var button = new CloningScanButton
|
||||
{
|
||||
Scan = scan.Value ?? string.Empty,
|
||||
Id = scan.Key
|
||||
};
|
||||
button.ActualButton.OnToggled += OnItemButtonToggled;
|
||||
var entityLabelText = scan.Value;
|
||||
|
||||
button.EntityLabel.Text = entityLabelText;
|
||||
|
||||
if (scan.Key == SelectedScan)
|
||||
{
|
||||
_selectedButton = button;
|
||||
_selectedButton.ActualButton.Pressed = true;
|
||||
}
|
||||
|
||||
//TODO: replace with body's face
|
||||
/*var tex = IconComponent.GetScanIcon(scan, resourceCache);
|
||||
var rect = button.EntityTextureRect;
|
||||
if (tex != null)
|
||||
{
|
||||
rect.Texture = tex.Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.Dispose();
|
||||
}
|
||||
|
||||
rect.Dispose();
|
||||
*/
|
||||
|
||||
_scanList.AddChild(button);
|
||||
}
|
||||
|
||||
//TODO: set up sort
|
||||
//_filteredScans.Sort((a, b) => string.Compare(a.ToString(), b.ToString(), StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private void OnItemButtonToggled(BaseButton.ButtonToggledEventArgs args)
|
||||
{
|
||||
var item = (CloningScanButton) args.Button.Parent!;
|
||||
if (_selectedButton == item)
|
||||
{
|
||||
_selectedButton = null;
|
||||
SelectedScan = null;
|
||||
return;
|
||||
}
|
||||
else if (_selectedButton != null)
|
||||
{
|
||||
_selectedButton.ActualButton.Pressed = false;
|
||||
}
|
||||
|
||||
_selectedButton = null;
|
||||
SelectedScan = null;
|
||||
|
||||
_selectedButton = item;
|
||||
SelectedScan = item.Id;
|
||||
}
|
||||
|
||||
[DebuggerDisplay("cloningbutton {" + nameof(Index) + "}")]
|
||||
private class CloningScanButton : Control
|
||||
{
|
||||
public string Scan { get; set; } = default!;
|
||||
public int Id { get; set; }
|
||||
public Button ActualButton { get; private set; }
|
||||
public Label EntityLabel { get; private set; }
|
||||
public TextureRect EntityTextureRect { get; private set; }
|
||||
public int Index { get; set; }
|
||||
|
||||
public CloningScanButton()
|
||||
{
|
||||
AddChild(ActualButton = new Button
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalExpand = true,
|
||||
ToggleMode = true,
|
||||
});
|
||||
|
||||
AddChild(new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(EntityTextureRect = new TextureRect
|
||||
{
|
||||
MinSize = (32, 32),
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
Stretch = TextureRect.StretchMode.KeepAspectCentered,
|
||||
CanShrink = true
|
||||
}),
|
||||
(EntityLabel = new Label
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
Text = "",
|
||||
ClipText = true
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
#nullable enable
|
||||
using Content.Client.GameObjects.Components.HUD.Inventory;
|
||||
using Content.Client.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.Components.Storage;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Clothing
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedItemComponent))]
|
||||
[ComponentReference(typeof(ItemComponent))]
|
||||
public class ClothingComponent : ItemComponent
|
||||
{
|
||||
[DataField("femaleMask")]
|
||||
private FemaleClothingMask _femaleMask = FemaleClothingMask.UniformFull;
|
||||
public override string Name => "Clothing";
|
||||
public override uint? NetID => ContentNetIDs.CLOTHING;
|
||||
|
||||
private string? _clothingEquippedPrefix;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("ClothingPrefix")]
|
||||
public string? ClothingEquippedPrefix
|
||||
{
|
||||
get => _clothingEquippedPrefix;
|
||||
set
|
||||
{
|
||||
if (_clothingEquippedPrefix == value)
|
||||
return;
|
||||
|
||||
_clothingEquippedPrefix = value;
|
||||
|
||||
if(!Initialized) return;
|
||||
|
||||
if (!Owner.TryGetContainer(out IContainer? container))
|
||||
return;
|
||||
if (!container.Owner.TryGetComponent(out ClientInventoryComponent? inventory))
|
||||
return;
|
||||
if (!inventory.TryFindItemSlots(Owner, out EquipmentSlotDefines.Slots? slots))
|
||||
return;
|
||||
|
||||
inventory.SetSlotVisuals(slots.Value, Owner);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
ClothingEquippedPrefix = ClothingEquippedPrefix;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public FemaleClothingMask FemaleMask
|
||||
{
|
||||
get => _femaleMask;
|
||||
set => _femaleMask = value;
|
||||
}
|
||||
|
||||
public (RSI rsi, RSI.StateId stateId)? GetEquippedStateInfo(EquipmentSlotDefines.SlotFlags slot, string? speciesId=null)
|
||||
{
|
||||
if (RsiPath == null)
|
||||
return null;
|
||||
|
||||
var rsi = IoCManager.Resolve<IResourceCache>().GetResource<RSIResource>(SharedSpriteComponent.TextureRoot / RsiPath).RSI;
|
||||
var prefix = ClothingEquippedPrefix ?? EquippedPrefix;
|
||||
var stateId = prefix != null ? $"{prefix}-equipped-{slot}" : $"equipped-{slot}";
|
||||
if (speciesId != null)
|
||||
{
|
||||
var speciesState = $"{stateId}-{speciesId}";
|
||||
if (rsi.TryGetState(speciesState, out _))
|
||||
{
|
||||
return (rsi, speciesState);
|
||||
}
|
||||
}
|
||||
|
||||
if (rsi.TryGetState(stateId, out _))
|
||||
{
|
||||
return (rsi, stateId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState is not ClothingComponentState state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClothingEquippedPrefix = state.ClothingEquippedPrefix;
|
||||
EquippedPrefix = state.EquippedPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
public enum FemaleClothingMask : byte
|
||||
{
|
||||
NoMask = 0,
|
||||
UniformFull,
|
||||
UniformTop
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
using System;
|
||||
using Content.Client.Command;
|
||||
using Content.Shared.GameObjects.Components.Command;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Command
|
||||
{
|
||||
public class CommunicationsConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
[ViewVariables] private CommunicationsConsoleMenu? _menu;
|
||||
|
||||
public bool CanAnnounce { get; private set; }
|
||||
public bool CanCall { get; private set; }
|
||||
|
||||
public bool CountdownStarted { get; private set; }
|
||||
|
||||
public int Countdown => _expectedCountdownTime == null
|
||||
? 0 : Math.Max((int)_expectedCountdownTime.Value.Subtract(_gameTiming.CurTime).TotalSeconds, 0);
|
||||
private TimeSpan? _expectedCountdownTime;
|
||||
|
||||
public CommunicationsConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = new CommunicationsConsoleMenu(this);
|
||||
_menu.OnClose += Close;
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
public void EmergencyShuttleButtonPressed()
|
||||
{
|
||||
if (CountdownStarted)
|
||||
RecallShuttle();
|
||||
else
|
||||
CallShuttle();
|
||||
}
|
||||
|
||||
public void AnnounceButtonPressed(string message)
|
||||
{
|
||||
var msg = message.Length <= 256 ? message.Trim() : $"{message.Trim().Substring(0, 256)}...";
|
||||
SendMessage(new CommunicationsConsoleAnnounceMessage(msg));
|
||||
}
|
||||
|
||||
public void CallShuttle()
|
||||
{
|
||||
SendMessage(new CommunicationsConsoleCallEmergencyShuttleMessage());
|
||||
}
|
||||
|
||||
public void RecallShuttle()
|
||||
{
|
||||
SendMessage(new CommunicationsConsoleRecallEmergencyShuttleMessage());
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not CommunicationsConsoleInterfaceState commsState)
|
||||
return;
|
||||
|
||||
CanAnnounce = commsState.CanAnnounce;
|
||||
CanCall = commsState.CanCall;
|
||||
_expectedCountdownTime = commsState.ExpectedCountdownEnd;
|
||||
CountdownStarted = commsState.CountdownStarted;
|
||||
|
||||
if (_menu != null)
|
||||
{
|
||||
_menu.UpdateCountdown();
|
||||
_menu.EmergencyShuttleButton.Disabled = !CanCall;
|
||||
_menu.AnnounceButton.Disabled = !CanAnnounce;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
|
||||
_menu?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class ComputerVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[DataField("key")]
|
||||
private string KeyboardState = "generic_key";
|
||||
[DataField("screen")]
|
||||
private string ScreenState = "generic";
|
||||
[DataField("body")]
|
||||
private string BodyState = "computer";
|
||||
[DataField("bodyBroken")]
|
||||
private string BodyBrokenState = "broken";
|
||||
private string ScreenBroken = "computer_broken";
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
sprite.LayerSetState(Layers.Screen, ScreenState);
|
||||
|
||||
if (!string.IsNullOrEmpty(KeyboardState))
|
||||
{
|
||||
sprite.LayerSetState(Layers.Keyboard, $"{KeyboardState}_off");
|
||||
sprite.LayerSetState(Layers.KeyboardOn, KeyboardState);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (!component.TryGetData(ComputerVisuals.Powered, out bool powered))
|
||||
{
|
||||
powered = true;
|
||||
}
|
||||
|
||||
component.TryGetData(ComputerVisuals.Broken, out bool broken);
|
||||
|
||||
if (broken)
|
||||
{
|
||||
sprite.LayerSetState(Layers.Body, BodyBrokenState);
|
||||
sprite.LayerSetState(Layers.Screen, ScreenBroken);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(Layers.Body, BodyState);
|
||||
sprite.LayerSetState(Layers.Screen, ScreenState);
|
||||
}
|
||||
|
||||
sprite.LayerSetVisible(Layers.Screen, powered);
|
||||
if (sprite.LayerMapTryGet(Layers.KeyboardOn, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(Layers.KeyboardOn, powered);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Layers : byte
|
||||
{
|
||||
Body,
|
||||
Screen,
|
||||
Keyboard,
|
||||
KeyboardOn
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using static Content.Shared.GameObjects.Components.SharedConfigurationComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Configuration
|
||||
{
|
||||
public class ConfigurationBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
public Regex? Validation { get; internal set; }
|
||||
|
||||
public ConfigurationBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private ConfigurationMenu? _menu;
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_menu = new ConfigurationMenu(this);
|
||||
|
||||
_menu.OnClose += Close;
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not ConfigurationBoundUserInterfaceState configurationState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_menu?.Populate(configurationState);
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
base.ReceiveMessage(message);
|
||||
|
||||
if (message is ValidationUpdateMessage msg)
|
||||
{
|
||||
Validation = new Regex(msg.ValidationString, RegexOptions.Compiled);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendConfiguration(Dictionary<string, string> config)
|
||||
{
|
||||
SendMessage(new ConfigurationUpdatedMessage(config));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing && _menu != null)
|
||||
{
|
||||
_menu.OnClose -= Close;
|
||||
_menu.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using static Content.Shared.GameObjects.Components.SharedConfigurationComponent;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Configuration
|
||||
{
|
||||
public class ConfigurationMenu : SS14Window
|
||||
{
|
||||
public ConfigurationBoundUserInterface Owner { get; }
|
||||
|
||||
private readonly VBoxContainer _baseContainer;
|
||||
private readonly VBoxContainer _column;
|
||||
private readonly HBoxContainer _row;
|
||||
|
||||
private readonly List<(string name, LineEdit input)> _inputs;
|
||||
|
||||
public ConfigurationMenu(ConfigurationBoundUserInterface owner)
|
||||
{
|
||||
MinSize = SetSize = (300, 250);
|
||||
Owner = owner;
|
||||
|
||||
_inputs = new List<(string name, LineEdit input)>();
|
||||
|
||||
Title = Loc.GetString("configuration-menu-device-title");
|
||||
|
||||
_baseContainer = new VBoxContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true
|
||||
};
|
||||
|
||||
_column = new VBoxContainer
|
||||
{
|
||||
Margin = new Thickness(8),
|
||||
SeparationOverride = 16,
|
||||
};
|
||||
|
||||
_row = new HBoxContainer
|
||||
{
|
||||
SeparationOverride = 16,
|
||||
HorizontalExpand = true
|
||||
};
|
||||
|
||||
var confirmButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("configuration-menu-confirm"),
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
|
||||
confirmButton.OnButtonUp += OnConfirm;
|
||||
|
||||
var outerColumn = new ScrollContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
ModulateSelfOverride = Color.FromHex("#202025")
|
||||
};
|
||||
|
||||
outerColumn.AddChild(_column);
|
||||
_baseContainer.AddChild(outerColumn);
|
||||
_baseContainer.AddChild(confirmButton);
|
||||
Contents.AddChild(_baseContainer);
|
||||
}
|
||||
|
||||
public void Populate(ConfigurationBoundUserInterfaceState state)
|
||||
{
|
||||
_column.Children.Clear();
|
||||
_inputs.Clear();
|
||||
|
||||
foreach (var field in state.Config)
|
||||
{
|
||||
var label = new Label
|
||||
{
|
||||
Margin = new Thickness(0, 0, 8, 0),
|
||||
Name = field.Key,
|
||||
Text = field.Key + ":",
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = .2f,
|
||||
MinSize = new Vector2(60, 0)
|
||||
};
|
||||
|
||||
var input = new LineEdit
|
||||
{
|
||||
Name = field.Key + "-input",
|
||||
Text = field.Value,
|
||||
IsValid = Validate,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = .8f
|
||||
};
|
||||
|
||||
_inputs.Add((field.Key, input));
|
||||
|
||||
var row = new HBoxContainer();
|
||||
CopyProperties(_row, row);
|
||||
|
||||
row.AddChild(label);
|
||||
row.AddChild(input);
|
||||
_column.AddChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConfirm(ButtonEventArgs args)
|
||||
{
|
||||
var config = GenerateDictionary(_inputs, "Text");
|
||||
|
||||
Owner.SendConfiguration(config);
|
||||
Close();
|
||||
}
|
||||
|
||||
private bool Validate(string value)
|
||||
{
|
||||
return Owner.Validation == null || Owner.Validation.IsMatch(value);
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GenerateDictionary(IEnumerable<(string name, LineEdit input)> inputs, string propertyName)
|
||||
{
|
||||
var dictionary = new Dictionary<string, string>();
|
||||
|
||||
foreach (var input in inputs)
|
||||
{
|
||||
dictionary.Add(input.name, input.input.Text);
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
private static void CopyProperties<T>(T from, T to) where T : Control
|
||||
{
|
||||
foreach (var property in from.AllAttachedProperties)
|
||||
{
|
||||
to.SetValue(property.Key, property.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using Content.Shared.Construction;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Construction
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ConstructionGhostComponent : Component, IExamine
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override string Name => "ConstructionGhost";
|
||||
|
||||
[ViewVariables] public ConstructionPrototype? Prototype { get; set; }
|
||||
[ViewVariables] public int GhostId { get; set; }
|
||||
|
||||
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
if (Prototype == null) return;
|
||||
|
||||
message.AddMarkup(Loc.GetString("Building: [color=cyan]{0}[/color]\n", Prototype.Name));
|
||||
|
||||
if (!_prototypeManager.TryIndex(Prototype.Graph, out ConstructionGraphPrototype? graph)) return;
|
||||
var startNode = graph.Nodes[Prototype.StartNode];
|
||||
|
||||
if (!graph.TryPath(Prototype.StartNode, Prototype.TargetNode, out var path) ||
|
||||
!startNode.TryGetEdge(path[0].Name, out var edge))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
edge.Steps[0].DoExamine(message, inDetailsRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Construction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Construction
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class MachineFrameVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.TryGetData<int>(MachineFrameVisuals.State, out var data))
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
sprite.LayerSetState(0, $"box_{data}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Conveyor;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Conveyor
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ConveyorVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[DataField("state_running")]
|
||||
private string? _stateRunning;
|
||||
|
||||
[DataField("state_stopped")]
|
||||
private string? _stateStopped;
|
||||
|
||||
[DataField("state_reversed")]
|
||||
private string? _stateReversed;
|
||||
|
||||
private void ChangeState(AppearanceComponent appearance)
|
||||
{
|
||||
if (!appearance.Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
appearance.TryGetData(ConveyorVisuals.State, out ConveyorState state);
|
||||
|
||||
var texture = state switch
|
||||
{
|
||||
ConveyorState.Off => _stateStopped,
|
||||
ConveyorState.Forward => _stateRunning,
|
||||
ConveyorState.Reversed => _stateReversed,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
sprite.LayerSetState(0, texture);
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var appearance = entity.EnsureComponent<AppearanceComponent>();
|
||||
ChangeState(appearance);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
ChangeState(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.MachineLinking;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Conveyor
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class TwoWayLeverVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[DataField("state_forward")]
|
||||
private string? _stateForward;
|
||||
|
||||
[DataField("state_off")]
|
||||
private string? _stateOff;
|
||||
|
||||
[DataField("state_reversed")]
|
||||
private string? _stateReversed;
|
||||
|
||||
private void ChangeState(AppearanceComponent appearance)
|
||||
{
|
||||
if (!appearance.Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
appearance.TryGetData(TwoWayLeverVisuals.State, out TwoWayLeverSignal state);
|
||||
|
||||
var texture = state switch
|
||||
{
|
||||
TwoWayLeverSignal.Middle => _stateOff,
|
||||
TwoWayLeverSignal.Right => _stateForward,
|
||||
TwoWayLeverSignal.Left => _stateReversed,
|
||||
_ => _stateOff
|
||||
};
|
||||
|
||||
sprite.LayerSetState(0, texture);
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var appearance = entity.EnsureComponent<AppearanceComponent>();
|
||||
ChangeState(appearance);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
ChangeState(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Crayon
|
||||
{
|
||||
public class CrayonBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
public CrayonBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private CrayonWindow? _menu;
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_menu = new CrayonWindow(this);
|
||||
|
||||
_menu.OnClose += Close;
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
var crayonDecals = prototypeManager.EnumeratePrototypes<CrayonDecalPrototype>().FirstOrDefault();
|
||||
if (crayonDecals != null)
|
||||
_menu.Populate(crayonDecals);
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
_menu?.UpdateState((CrayonBoundUserInterfaceState) state);
|
||||
}
|
||||
|
||||
public void Select(string state)
|
||||
{
|
||||
SendMessage(new CrayonSelectMessage(state));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_menu?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Crayon
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class CrayonComponent : SharedCrayonComponent, IItemStatus
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
|
||||
[ViewVariables(VVAccess.ReadWrite)] private string Color => _color;
|
||||
[ViewVariables] private int Charges { get; set; }
|
||||
[ViewVariables] private int Capacity { get; set; }
|
||||
|
||||
Control IItemStatus.MakeControl()
|
||||
{
|
||||
return new StatusControl(this);
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState is not CrayonComponentState state)
|
||||
return;
|
||||
|
||||
_color = state.Color;
|
||||
SelectedState = state.State;
|
||||
Charges = state.Charges;
|
||||
Capacity = state.Capacity;
|
||||
|
||||
_uiUpdateNeeded = true;
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly CrayonComponent _parent;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
public StatusControl(CrayonComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
AddChild(_label);
|
||||
|
||||
parent._uiUpdateNeeded = true;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_parent._uiUpdateNeeded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_parent._uiUpdateNeeded = false;
|
||||
_label.SetMarkup(Loc.GetString("Drawing: [color={0}]{1}[/color] ({2}/{3})",
|
||||
_parent.Color,
|
||||
_parent.SelectedState,
|
||||
_parent.Charges,
|
||||
_parent.Capacity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Crayon
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class CrayonDecalVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<SpriteComponent>();
|
||||
|
||||
if (component.TryGetData(CrayonVisuals.State, out string state))
|
||||
{
|
||||
sprite.LayerSetState(0, state);
|
||||
}
|
||||
|
||||
if (component.TryGetData(CrayonVisuals.Color, out Color color))
|
||||
{
|
||||
sprite.LayerSetColor(0, color);
|
||||
}
|
||||
|
||||
if (component.TryGetData(CrayonVisuals.Rotation, out Angle rotation))
|
||||
{
|
||||
sprite.Rotation = rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Crayon
|
||||
{
|
||||
public class CrayonWindow : SS14Window
|
||||
{
|
||||
public CrayonBoundUserInterface Owner { get; }
|
||||
private readonly LineEdit _search;
|
||||
private readonly GridContainer _grid;
|
||||
private Dictionary<string, Texture>? _decals;
|
||||
private string? _selected;
|
||||
private Color _color;
|
||||
|
||||
public CrayonWindow(CrayonBoundUserInterface owner)
|
||||
{
|
||||
MinSize = SetSize = (250, 300);
|
||||
Title = Loc.GetString("Crayon");
|
||||
Owner = owner;
|
||||
|
||||
var vbox = new VBoxContainer();
|
||||
Contents.AddChild(vbox);
|
||||
|
||||
_search = new LineEdit();
|
||||
_search.OnTextChanged += (_) => RefreshList();
|
||||
vbox.AddChild(_search);
|
||||
|
||||
_grid = new GridContainer()
|
||||
{
|
||||
Columns = 6,
|
||||
};
|
||||
var gridScroll = new ScrollContainer()
|
||||
{
|
||||
VerticalExpand = true,
|
||||
Children =
|
||||
{
|
||||
_grid
|
||||
}
|
||||
};
|
||||
vbox.AddChild(gridScroll);
|
||||
}
|
||||
|
||||
private void RefreshList()
|
||||
{
|
||||
// Clear
|
||||
_grid.RemoveAllChildren();
|
||||
if (_decals == null)
|
||||
return;
|
||||
|
||||
var filter = _search.Text;
|
||||
foreach (var (decal, tex) in _decals)
|
||||
{
|
||||
if (!decal.Contains(filter))
|
||||
continue;
|
||||
|
||||
var button = new TextureButton()
|
||||
{
|
||||
TextureNormal = tex,
|
||||
Name = decal,
|
||||
ToolTip = decal,
|
||||
Modulate = _color
|
||||
};
|
||||
button.OnPressed += ButtonOnPressed;
|
||||
if (_selected == decal)
|
||||
{
|
||||
var panelContainer = new PanelContainer()
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = StyleNano.ButtonColorDefault,
|
||||
},
|
||||
Children =
|
||||
{
|
||||
button
|
||||
}
|
||||
};
|
||||
_grid.AddChild(panelContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
_grid.AddChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonOnPressed(ButtonEventArgs obj)
|
||||
{
|
||||
if (obj.Button.Name != null)
|
||||
{
|
||||
Owner.Select(obj.Button.Name);
|
||||
_selected = obj.Button.Name;
|
||||
RefreshList();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateState(CrayonBoundUserInterfaceState state)
|
||||
{
|
||||
_selected = state.Selected;
|
||||
_color = state.Color;
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
public void Populate(CrayonDecalPrototype proto)
|
||||
{
|
||||
var path = new ResourcePath(proto.SpritePath);
|
||||
_decals = new Dictionary<string, Texture>();
|
||||
foreach (var state in proto.Decals)
|
||||
{
|
||||
var rsi = new SpriteSpecifier.Rsi(path, state);
|
||||
_decals.Add(state, rsi.Frame0());
|
||||
}
|
||||
|
||||
RefreshList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalMailingUnitComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="DisposalMailingUnitWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class DisposalMailingUnitBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private DisposalMailingUnitWindow? _window;
|
||||
|
||||
public DisposalMailingUnitBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private void ButtonPressed(UiButton button)
|
||||
{
|
||||
SendMessage(new UiButtonPressedMessage(button));
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new DisposalMailingUnitWindow();
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject);
|
||||
_window.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage);
|
||||
_window.Power.OnPressed += _ => ButtonPressed(UiButton.Power);
|
||||
_window.TargetListContainer.OnItemSelected += TargetSelected;
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not DisposalMailingUnitBoundUserInterfaceState cast)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
private void TargetSelected(ItemList.ItemListSelectedEventArgs item)
|
||||
{
|
||||
SendMessage(new UiTargetUpdateMessage(_window?.TargetList[item.ItemIndex]));
|
||||
//(ノ°Д°)ノ︵ ┻━┻
|
||||
if (_window != null) _window.Engage.Disabled = false;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedDisposalMailingUnitComponent))]
|
||||
public class DisposalMailingUnitComponent : SharedDisposalMailingUnitComponent
|
||||
{
|
||||
public override bool DragDropOn(DragDropEvent eventArgs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalMailingUnitComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedDisposalMailingUnitComponent"/>
|
||||
/// </summary>
|
||||
public class DisposalMailingUnitWindow : SS14Window
|
||||
{
|
||||
private readonly Label _unitState;
|
||||
private readonly ProgressBar _pressureBar;
|
||||
private readonly Label _pressurePercentage;
|
||||
public readonly Button Engage;
|
||||
public readonly Button Eject;
|
||||
public readonly Button Power;
|
||||
|
||||
public readonly ItemList TargetListContainer;
|
||||
public List<string> TargetList;
|
||||
private readonly Label _tagLabel;
|
||||
|
||||
public DisposalMailingUnitWindow()
|
||||
{
|
||||
MinSize = SetSize = (460, 230);
|
||||
TargetList = new List<string>();
|
||||
Contents.AddChild(new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new VBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(8, 0),
|
||||
Children =
|
||||
{
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("State: ")},
|
||||
new Control {MinSize = (4, 0)},
|
||||
(_unitState = new Label {Text = Loc.GetString("Ready")})
|
||||
}
|
||||
},
|
||||
new Control {MinSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Pressure:")},
|
||||
new Control {MinSize = (4, 0)},
|
||||
(_pressureBar = new ProgressBar
|
||||
{
|
||||
MinSize = (100, 20),
|
||||
HorizontalExpand = true,
|
||||
MinValue = 0,
|
||||
MaxValue = 1,
|
||||
Page = 0,
|
||||
Value = 0.5f,
|
||||
Children =
|
||||
{
|
||||
(_pressurePercentage = new Label())
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
new Control {MinSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Handle:")},
|
||||
new Control
|
||||
{
|
||||
MinSize = (4, 0),
|
||||
HorizontalExpand = true
|
||||
},
|
||||
(Engage = new Button
|
||||
{
|
||||
MinSize = (16, 0),
|
||||
Text = Loc.GetString("Engage"),
|
||||
ToggleMode = true,
|
||||
Disabled = true
|
||||
})
|
||||
}
|
||||
},
|
||||
new Control {MinSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Eject:")},
|
||||
new Control
|
||||
{
|
||||
MinSize = (4, 0),
|
||||
HorizontalExpand = true
|
||||
},
|
||||
(Eject = new Button
|
||||
{
|
||||
MinSize = (16, 0),
|
||||
Text = Loc.GetString("Eject Contents"),
|
||||
//HorizontalAlignment = HAlignment.Right
|
||||
})
|
||||
}
|
||||
},
|
||||
new Control {MinSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(Power = new CheckButton {Text = Loc.GetString("Power")}),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new VBoxContainer
|
||||
{
|
||||
Margin = new Thickness(12, 0, 8, 0),
|
||||
Children =
|
||||
{
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("Select a destination:")
|
||||
}
|
||||
}
|
||||
},
|
||||
new Control {MinSize = new Vector2(0, 8)},
|
||||
new HBoxContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
Children =
|
||||
{
|
||||
(TargetListContainer = new ItemList
|
||||
{
|
||||
SelectMode = ItemList.ItemListSelectMode.Single,
|
||||
HorizontalExpand = true,
|
||||
VerticalExpand = true
|
||||
})
|
||||
}
|
||||
},
|
||||
new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = Color.FromHex("#ACBDBA")
|
||||
},
|
||||
HorizontalExpand = true,
|
||||
MinSize = new Vector2(0, 1),
|
||||
},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new HBoxContainer
|
||||
{
|
||||
Margin = new Thickness(4, 0, 0, 0),
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("This unit:")
|
||||
},
|
||||
new Control
|
||||
{
|
||||
MinSize = new Vector2(4, 0)
|
||||
},
|
||||
(_tagLabel = new Label
|
||||
{
|
||||
Text = "-",
|
||||
VerticalAlignment = VAlignment.Bottom
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdatePressureBar(float pressure)
|
||||
{
|
||||
_pressureBar.Value = pressure;
|
||||
|
||||
var normalized = pressure / _pressureBar.MaxValue;
|
||||
|
||||
const float leftHue = 0.0f; // Red
|
||||
const float middleHue = 0.066f; // Orange
|
||||
const float rightHue = 0.33f; // Green
|
||||
const float saturation = 1.0f; // Uniform saturation
|
||||
const float value = 0.8f; // Uniform value / brightness
|
||||
const float alpha = 1.0f; // Uniform alpha
|
||||
|
||||
// These should add up to 1.0 or your transition won't be smooth
|
||||
const float leftSideSize = 0.5f; // Fraction of _chargeBar lerped from leftHue to middleHue
|
||||
const float rightSideSize = 0.5f; // Fraction of _chargeBar lerped from middleHue to rightHue
|
||||
|
||||
float finalHue;
|
||||
if (normalized <= leftSideSize)
|
||||
{
|
||||
normalized /= leftSideSize; // Adjust range to 0.0 to 1.0
|
||||
finalHue = MathHelper.Lerp(leftHue, middleHue, normalized);
|
||||
}
|
||||
else
|
||||
{
|
||||
normalized = (normalized - leftSideSize) / rightSideSize; // Adjust range to 0.0 to 1.0.
|
||||
finalHue = MathHelper.Lerp(middleHue, rightHue, normalized);
|
||||
}
|
||||
|
||||
// Check if null first to avoid repeatedly creating this.
|
||||
_pressureBar.ForegroundStyleBoxOverride ??= new StyleBoxFlat();
|
||||
|
||||
var foregroundStyleBoxOverride = (StyleBoxFlat) _pressureBar.ForegroundStyleBoxOverride;
|
||||
foregroundStyleBoxOverride.BackgroundColor =
|
||||
Color.FromHsv(new Vector4(finalHue, saturation, value, alpha));
|
||||
|
||||
var percentage = pressure / _pressureBar.MaxValue * 100;
|
||||
_pressurePercentage.Text = $" {percentage:0}%";
|
||||
}
|
||||
|
||||
public void UpdateState(DisposalMailingUnitBoundUserInterfaceState state)
|
||||
{
|
||||
Title = state.UnitName;
|
||||
_unitState.Text = state.UnitState;
|
||||
UpdatePressureBar(state.Pressure);
|
||||
Power.Pressed = state.Powered;
|
||||
Engage.Pressed = state.Engaged;
|
||||
PopulateTargetList(state.Tags);
|
||||
_tagLabel.Text = state.Tag;
|
||||
TargetList = state.Tags;
|
||||
}
|
||||
|
||||
private void PopulateTargetList(List<string> tags)
|
||||
{
|
||||
TargetListContainer.Clear();
|
||||
foreach (var target in tags)
|
||||
{
|
||||
TargetListContainer.AddItem(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="DisposalRouterWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class DisposalRouterBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private DisposalRouterWindow? _window;
|
||||
|
||||
public DisposalRouterBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new DisposalRouterWindow();
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text);
|
||||
_window.TagInput.OnTextEntered += args => ButtonPressed(UiAction.Ok, args.Text);
|
||||
|
||||
}
|
||||
|
||||
private void ButtonPressed(UiAction action, string tag)
|
||||
{
|
||||
SendMessage(new UiActionMessage(action, tag));
|
||||
_window?.Close();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not DisposalRouterUserInterfaceState cast)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedDisposalRouterComponent"/>
|
||||
/// </summary>
|
||||
public class DisposalRouterWindow : SS14Window
|
||||
{
|
||||
public readonly LineEdit TagInput;
|
||||
public readonly Button Confirm;
|
||||
|
||||
public DisposalRouterWindow()
|
||||
{
|
||||
MinSize = SetSize = (500, 110);
|
||||
Title = Loc.GetString("Disposal Router");
|
||||
|
||||
Contents.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Tags:")},
|
||||
new Control {MinSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(TagInput = new LineEdit
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
MinSize = (320, 0),
|
||||
ToolTip = Loc.GetString("A comma separated list of tags"),
|
||||
IsValid = tags => TagRegex.IsMatch(tags)
|
||||
}),
|
||||
new Control {MinSize = (10, 0)},
|
||||
(Confirm = new Button {Text = Loc.GetString("Confirm")})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void UpdateState(DisposalRouterUserInterfaceState state)
|
||||
{
|
||||
TagInput.Text = state.Tags;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="DisposalTaggerWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class DisposalTaggerBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private DisposalTaggerWindow? _window;
|
||||
|
||||
public DisposalTaggerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new DisposalTaggerWindow();
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text);
|
||||
_window.TagInput.OnTextEntered += args => ButtonPressed(UiAction.Ok, args.Text);
|
||||
|
||||
}
|
||||
|
||||
private void ButtonPressed(UiAction action, string tag)
|
||||
{
|
||||
SendMessage(new UiActionMessage(action, tag));
|
||||
_window?.Close();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not DisposalTaggerUserInterfaceState cast)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedDisposalTaggerComponent"/>
|
||||
/// </summary>
|
||||
public class DisposalTaggerWindow : SS14Window
|
||||
{
|
||||
public readonly LineEdit TagInput;
|
||||
public readonly Button Confirm;
|
||||
|
||||
public DisposalTaggerWindow()
|
||||
{
|
||||
MinSize = SetSize = (500, 110);
|
||||
Title = Loc.GetString("Disposal Tagger");
|
||||
|
||||
Contents.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Tag:")},
|
||||
new Control {MinSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(TagInput = new LineEdit
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
MinSize = (320, 0),
|
||||
IsValid = tag => TagRegex.IsMatch(tag)
|
||||
}),
|
||||
new Control {MinSize = (10, 0)},
|
||||
(Confirm = new Button {Text = Loc.GetString("Confirm")})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void UpdateState(DisposalTaggerUserInterfaceState state)
|
||||
{
|
||||
TagInput.Text = state.Tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalUnitComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="DisposalUnitWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class DisposalUnitBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private DisposalUnitWindow? _window;
|
||||
|
||||
public DisposalUnitBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private void ButtonPressed(UiButton button)
|
||||
{
|
||||
SendMessage(new UiButtonPressedMessage(button));
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new DisposalUnitWindow();
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject);
|
||||
_window.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage);
|
||||
_window.Power.OnPressed += _ => ButtonPressed(UiButton.Power);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not DisposalUnitBoundUserInterfaceState cast)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedDisposalUnitComponent))]
|
||||
public class DisposalUnitComponent : SharedDisposalUnitComponent
|
||||
{
|
||||
public override bool DragDropOn(DragDropEvent eventArgs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalUnitComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class DisposalUnitVisualizer : AppearanceVisualizer, ISerializationHooks
|
||||
{
|
||||
private const string AnimationKey = "disposal_unit_animation";
|
||||
|
||||
[DataField("state_anchored", required: true)]
|
||||
private string? _stateAnchored;
|
||||
|
||||
[DataField("state_unanchored", required: true)]
|
||||
private string? _stateUnAnchored;
|
||||
|
||||
[DataField("state_charging", required: true)]
|
||||
private string? _stateCharging;
|
||||
|
||||
[DataField("overlay_charging", required: true)]
|
||||
private string? _overlayCharging;
|
||||
|
||||
[DataField("overlay_ready", required: true)]
|
||||
private string? _overlayReady;
|
||||
|
||||
[DataField("overlay_full", required: true)]
|
||||
private string? _overlayFull;
|
||||
|
||||
[DataField("overlay_engaged", required: true)]
|
||||
private string? _overlayEngaged;
|
||||
|
||||
[DataField("state_flush", required: true)]
|
||||
private string? _stateFlush;
|
||||
|
||||
[DataField("flush_sound", required: true)]
|
||||
private string? _flushSound;
|
||||
|
||||
[DataField("flush_time", required: true)]
|
||||
private float _flushTime;
|
||||
|
||||
private Animation _flushAnimation = default!;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
_flushAnimation = new Animation {Length = TimeSpan.FromSeconds(_flushTime)};
|
||||
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
_flushAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = DisposalUnitVisualLayers.Base;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame(_stateFlush, 0));
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
_flushAnimation.AnimationTracks.Add(sound);
|
||||
|
||||
if (_flushSound != null)
|
||||
{
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_flushSound, 0));
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeState(AppearanceComponent appearance)
|
||||
{
|
||||
if (!appearance.TryGetData(Visuals.VisualState, out VisualState state))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!appearance.Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case VisualState.UnAnchored:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateUnAnchored);
|
||||
break;
|
||||
case VisualState.Anchored:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateAnchored);
|
||||
break;
|
||||
case VisualState.Charging:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateCharging);
|
||||
break;
|
||||
case VisualState.Flushing:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateAnchored);
|
||||
|
||||
var animPlayer = appearance.Owner.GetComponent<AnimationPlayerComponent>();
|
||||
|
||||
if (!animPlayer.HasRunningAnimation(AnimationKey))
|
||||
{
|
||||
animPlayer.Play(_flushAnimation, AnimationKey);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (!appearance.TryGetData(Visuals.Handle, out HandleState handleState))
|
||||
{
|
||||
handleState = HandleState.Normal;
|
||||
}
|
||||
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.Handle, handleState != HandleState.Normal);
|
||||
|
||||
switch (handleState)
|
||||
{
|
||||
case HandleState.Normal:
|
||||
break;
|
||||
case HandleState.Engaged:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Handle, _overlayEngaged);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (!appearance.TryGetData(Visuals.Light, out LightState lightState))
|
||||
{
|
||||
lightState = LightState.Off;
|
||||
}
|
||||
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.Light, lightState != LightState.Off);
|
||||
|
||||
switch (lightState)
|
||||
{
|
||||
case LightState.Off:
|
||||
break;
|
||||
case LightState.Charging:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Light, _overlayCharging);
|
||||
break;
|
||||
case LightState.Full:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Light, _overlayFull);
|
||||
break;
|
||||
case LightState.Ready:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Light, _overlayReady);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
entity.EnsureComponent<AnimationPlayerComponent>();
|
||||
var appearance = entity.EnsureComponent<AppearanceComponent>();
|
||||
|
||||
ChangeState(appearance);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
ChangeState(component);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DisposalUnitVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
Handle,
|
||||
Light
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalUnitComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedDisposalUnitComponent"/>
|
||||
/// </summary>
|
||||
public class DisposalUnitWindow : SS14Window
|
||||
{
|
||||
private readonly Label _unitState;
|
||||
private readonly ProgressBar _pressureBar;
|
||||
public readonly Button Engage;
|
||||
public readonly Button Eject;
|
||||
public readonly Button Power;
|
||||
|
||||
public DisposalUnitWindow()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
MinSize = SetSize = (300, 140);
|
||||
Resizable = false;
|
||||
Contents.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new HBoxContainer
|
||||
{
|
||||
SeparationOverride = 4,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("ui-disposal-unit-label-state")},
|
||||
(_unitState = new Label {Text = Loc.GetString("ui-disposal-unit-label-status")})
|
||||
}
|
||||
},
|
||||
new Control {MinSize = (0, 5)},
|
||||
new HBoxContainer
|
||||
{
|
||||
SeparationOverride = 4,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("ui-disposal-unit-label-pressure")},
|
||||
(_pressureBar = new ProgressBar
|
||||
{
|
||||
MinSize = (190, 20),
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
MinValue = 0,
|
||||
MaxValue = 1,
|
||||
Page = 0,
|
||||
Value = 0.5f
|
||||
})
|
||||
}
|
||||
},
|
||||
new Control {MinSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(Engage = new Button
|
||||
{
|
||||
Text = Loc.GetString("ui-disposal-unit-button-flush"),
|
||||
StyleClasses = {StyleBase.ButtonOpenRight},
|
||||
ToggleMode = true
|
||||
}),
|
||||
|
||||
(Eject = new Button
|
||||
{
|
||||
Text = Loc.GetString("ui-disposal-unit-button-eject"),
|
||||
StyleClasses = {StyleBase.ButtonOpenBoth}
|
||||
}),
|
||||
|
||||
(Power = new CheckButton
|
||||
{
|
||||
Text = Loc.GetString("ui-disposal-unit-button-power"),
|
||||
StyleClasses = {StyleBase.ButtonOpenLeft}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdatePressureBar(float pressure)
|
||||
{
|
||||
_pressureBar.Value = pressure;
|
||||
|
||||
var normalized = pressure / _pressureBar.MaxValue;
|
||||
|
||||
const float leftHue = 0.0f; // Red
|
||||
const float middleHue = 0.066f; // Orange
|
||||
const float rightHue = 0.33f; // Green
|
||||
const float saturation = 1.0f; // Uniform saturation
|
||||
const float value = 0.8f; // Uniform value / brightness
|
||||
const float alpha = 1.0f; // Uniform alpha
|
||||
|
||||
// These should add up to 1.0 or your transition won't be smooth
|
||||
const float leftSideSize = 0.5f; // Fraction of _chargeBar lerped from leftHue to middleHue
|
||||
const float rightSideSize = 0.5f; // Fraction of _chargeBar lerped from middleHue to rightHue
|
||||
|
||||
float finalHue;
|
||||
if (normalized <= leftSideSize)
|
||||
{
|
||||
normalized /= leftSideSize; // Adjust range to 0.0 to 1.0
|
||||
finalHue = MathHelper.Lerp(leftHue, middleHue, normalized);
|
||||
}
|
||||
else
|
||||
{
|
||||
normalized = (normalized - leftSideSize) / rightSideSize; // Adjust range to 0.0 to 1.0.
|
||||
finalHue = MathHelper.Lerp(middleHue, rightHue, normalized);
|
||||
}
|
||||
|
||||
// Check if null first to avoid repeatedly creating this.
|
||||
_pressureBar.ForegroundStyleBoxOverride ??= new StyleBoxFlat();
|
||||
|
||||
var foregroundStyleBoxOverride = (StyleBoxFlat) _pressureBar.ForegroundStyleBoxOverride;
|
||||
foregroundStyleBoxOverride.BackgroundColor =
|
||||
Color.FromHsv(new Vector4(finalHue, saturation, value, alpha));
|
||||
}
|
||||
|
||||
public void UpdateState(DisposalUnitBoundUserInterfaceState state)
|
||||
{
|
||||
Title = state.UnitName;
|
||||
_unitState.Text = state.UnitState;
|
||||
UpdatePressureBar(state.Pressure);
|
||||
Power.Pressed = state.Powered;
|
||||
Engage.Pressed = state.Engaged;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class DisposalVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[DataField("state_free")]
|
||||
private string? _stateFree;
|
||||
|
||||
[DataField("state_anchored")]
|
||||
private string? _stateAnchored;
|
||||
|
||||
[DataField("state_broken")]
|
||||
private string? _stateBroken;
|
||||
|
||||
private void ChangeState(AppearanceComponent appearance)
|
||||
{
|
||||
if (!appearance.Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!appearance.TryGetData(DisposalTubeVisuals.VisualState, out DisposalTubeVisualState state))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var texture = state switch
|
||||
{
|
||||
DisposalTubeVisualState.Free => _stateFree,
|
||||
DisposalTubeVisualState.Anchored => _stateAnchored,
|
||||
DisposalTubeVisualState.Broken => _stateBroken,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
sprite.LayerSetState(0, texture);
|
||||
|
||||
if (state == DisposalTubeVisualState.Anchored)
|
||||
{
|
||||
appearance.Owner.EnsureComponent<SubFloorHideComponent>();
|
||||
}
|
||||
else if (appearance.Owner.HasComponent<SubFloorHideComponent>())
|
||||
{
|
||||
appearance.Owner.RemoveComponent<SubFloorHideComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var appearance = entity.EnsureComponent<AppearanceComponent>();
|
||||
ChangeState(appearance);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
ChangeState(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.GameObjects.EntitySystems.DoAfter;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class DoAfterComponent : SharedDoAfterComponent
|
||||
{
|
||||
public override string Name => "DoAfter";
|
||||
|
||||
public IReadOnlyDictionary<byte, ClientDoAfter> DoAfters => _doAfters;
|
||||
private readonly Dictionary<byte, ClientDoAfter> _doAfters = new();
|
||||
|
||||
public readonly List<(TimeSpan CancelTime, ClientDoAfter Message)> CancelledDoAfters = new();
|
||||
|
||||
public DoAfterGui? Gui { get; set; }
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, netChannel, session);
|
||||
switch (message)
|
||||
{
|
||||
case CancelledDoAfterMessage msg:
|
||||
Cancel(msg.ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
Enable();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
Disable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For handling PVS so we dispose of controls if they go out of range
|
||||
/// </summary>
|
||||
public void Enable()
|
||||
{
|
||||
if (Gui?.Disposed == false)
|
||||
return;
|
||||
|
||||
Gui = new DoAfterGui {AttachedEntity = Owner};
|
||||
|
||||
foreach (var (_, doAfter) in _doAfters)
|
||||
{
|
||||
Gui.AddDoAfter(doAfter);
|
||||
}
|
||||
|
||||
foreach (var (_, cancelled) in CancelledDoAfters)
|
||||
{
|
||||
Gui.CancelDoAfter(cancelled.ID);
|
||||
}
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
Gui?.Dispose();
|
||||
Gui = null;
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not DoAfterComponentState state)
|
||||
return;
|
||||
|
||||
var toRemove = new List<ClientDoAfter>();
|
||||
|
||||
foreach (var (id, doAfter) in _doAfters)
|
||||
{
|
||||
var found = false;
|
||||
|
||||
foreach (var clientdoAfter in state.DoAfters)
|
||||
{
|
||||
if (clientdoAfter.ID == id)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
toRemove.Add(doAfter);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var doAfter in toRemove)
|
||||
{
|
||||
Remove(doAfter);
|
||||
}
|
||||
|
||||
foreach (var doAfter in state.DoAfters)
|
||||
{
|
||||
if (_doAfters.ContainsKey(doAfter.ID))
|
||||
continue;
|
||||
|
||||
_doAfters.Add(doAfter.ID, doAfter);
|
||||
}
|
||||
|
||||
if (Gui == null || Gui.Disposed)
|
||||
return;
|
||||
|
||||
foreach (var (_, doAfter) in _doAfters)
|
||||
{
|
||||
Gui.AddDoAfter(doAfter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a DoAfter without showing a cancellation graphic.
|
||||
/// </summary>
|
||||
/// <param name="clientDoAfter"></param>
|
||||
public void Remove(ClientDoAfter clientDoAfter)
|
||||
{
|
||||
_doAfters.Remove(clientDoAfter.ID);
|
||||
|
||||
var found = false;
|
||||
|
||||
for (var i = CancelledDoAfters.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var cancelled = CancelledDoAfters[i];
|
||||
|
||||
if (cancelled.Message == clientDoAfter)
|
||||
{
|
||||
CancelledDoAfters.RemoveAt(i);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
_doAfters.Remove(clientDoAfter.ID);
|
||||
|
||||
Gui?.RemoveDoAfter(clientDoAfter.ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark a DoAfter as cancelled and show a cancellation graphic.
|
||||
/// </summary>
|
||||
/// Actual removal is handled by DoAfterEntitySystem.
|
||||
/// <param name="id"></param>
|
||||
/// <param name="currentTime"></param>
|
||||
public void Cancel(byte id, TimeSpan? currentTime = null)
|
||||
{
|
||||
foreach (var (_, cancelled) in CancelledDoAfters)
|
||||
{
|
||||
if (cancelled.ID == id)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_doAfters.ContainsKey(id))
|
||||
return;
|
||||
|
||||
var doAfterMessage = _doAfters[id];
|
||||
currentTime ??= IoCManager.Resolve<IGameTiming>().CurTime;
|
||||
CancelledDoAfters.Add((currentTime.Value, doAfterMessage));
|
||||
Gui?.CancelDoAfter(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
using System;
|
||||
using Content.Client.GameObjects.Components.Wires;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.GameObjects.Components.Doors;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Doors
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class AirlockVisualizer : AppearanceVisualizer, ISerializationHooks
|
||||
{
|
||||
private const string AnimationKey = "airlock_animation";
|
||||
|
||||
[DataField("open_sound", required: true)]
|
||||
private string _openSound = default!;
|
||||
|
||||
[DataField("close_sound", required: true)]
|
||||
private string _closeSound = default!;
|
||||
|
||||
[DataField("deny_sound", required: true)]
|
||||
private string _denySound = default!;
|
||||
|
||||
[DataField("animation_time")]
|
||||
private float _delay = 0.8f;
|
||||
|
||||
private Animation CloseAnimation = default!;
|
||||
private Animation OpenAnimation = default!;
|
||||
private Animation DenyAnimation = default!;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
CloseAnimation = new Animation {Length = TimeSpan.FromSeconds(_delay)};
|
||||
{
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
CloseAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = DoorVisualLayers.Base;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing", 0f));
|
||||
|
||||
var flickUnlit = new AnimationTrackSpriteFlick();
|
||||
CloseAnimation.AnimationTracks.Add(flickUnlit);
|
||||
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
|
||||
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing_unlit", 0f));
|
||||
|
||||
var flickMaintenancePanel = new AnimationTrackSpriteFlick();
|
||||
CloseAnimation.AnimationTracks.Add(flickMaintenancePanel);
|
||||
flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel;
|
||||
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_closing", 0f));
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
CloseAnimation.AnimationTracks.Add(sound);
|
||||
|
||||
if (_closeSound != null)
|
||||
{
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_closeSound, 0));
|
||||
}
|
||||
}
|
||||
|
||||
OpenAnimation = new Animation {Length = TimeSpan.FromSeconds(_delay)};
|
||||
{
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
OpenAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = DoorVisualLayers.Base;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening", 0f));
|
||||
|
||||
var flickUnlit = new AnimationTrackSpriteFlick();
|
||||
OpenAnimation.AnimationTracks.Add(flickUnlit);
|
||||
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
|
||||
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening_unlit", 0f));
|
||||
|
||||
var flickMaintenancePanel = new AnimationTrackSpriteFlick();
|
||||
OpenAnimation.AnimationTracks.Add(flickMaintenancePanel);
|
||||
flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel;
|
||||
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_opening", 0f));
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
OpenAnimation.AnimationTracks.Add(sound);
|
||||
|
||||
if (_openSound != null)
|
||||
{
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_openSound, 0));
|
||||
}
|
||||
}
|
||||
|
||||
DenyAnimation = new Animation {Length = TimeSpan.FromSeconds(0.3f)};
|
||||
{
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
DenyAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = DoorVisualLayers.BaseUnlit;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("deny_unlit", 0f));
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
DenyAnimation.AnimationTracks.Add(sound);
|
||||
|
||||
if (_denySound != null)
|
||||
{
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_denySound, 0, () => AudioHelpers.WithVariation(0.05f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
if (!entity.HasComponent<AnimationPlayerComponent>())
|
||||
{
|
||||
entity.AddComponent<AnimationPlayerComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
var animPlayer = component.Owner.GetComponent<AnimationPlayerComponent>();
|
||||
if (!component.TryGetData(DoorVisuals.VisualState, out DoorVisualState state))
|
||||
{
|
||||
state = DoorVisualState.Closed;
|
||||
}
|
||||
|
||||
var unlitVisible = true;
|
||||
var boltedVisible = false;
|
||||
var weldedVisible = false;
|
||||
|
||||
if (animPlayer.HasRunningAnimation(AnimationKey))
|
||||
{
|
||||
animPlayer.Stop(AnimationKey);
|
||||
}
|
||||
switch (state)
|
||||
{
|
||||
case DoorVisualState.Open:
|
||||
sprite.LayerSetState(DoorVisualLayers.Base, "open");
|
||||
unlitVisible = false;
|
||||
break;
|
||||
case DoorVisualState.Closed:
|
||||
sprite.LayerSetState(DoorVisualLayers.Base, "closed");
|
||||
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "closed_unlit");
|
||||
sprite.LayerSetState(DoorVisualLayers.BaseBolted, "bolted_unlit");
|
||||
sprite.LayerSetState(WiresVisualizer.WiresVisualLayers.MaintenancePanel, "panel_open");
|
||||
break;
|
||||
case DoorVisualState.Opening:
|
||||
animPlayer.Play(OpenAnimation, AnimationKey);
|
||||
break;
|
||||
case DoorVisualState.Closing:
|
||||
animPlayer.Play(CloseAnimation, AnimationKey);
|
||||
break;
|
||||
case DoorVisualState.Deny:
|
||||
animPlayer.Play(DenyAnimation, AnimationKey);
|
||||
break;
|
||||
case DoorVisualState.Welded:
|
||||
weldedVisible = true;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (component.TryGetData(DoorVisuals.Powered, out bool powered) && !powered)
|
||||
{
|
||||
unlitVisible = false;
|
||||
}
|
||||
if (component.TryGetData(DoorVisuals.BoltLights, out bool lights) && lights)
|
||||
{
|
||||
boltedVisible = true;
|
||||
}
|
||||
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible);
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseWelded, weldedVisible);
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseBolted, unlitVisible && boltedVisible);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DoorVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
BaseUnlit,
|
||||
BaseWelded,
|
||||
BaseBolted,
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Doors;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Doors
|
||||
{
|
||||
/// <summary>
|
||||
/// Bare-bones client-side door component; used to stop door-based mispredicts.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedDoorComponent))]
|
||||
public class ClientDoorComponent : SharedDoorComponent
|
||||
{
|
||||
private bool _stateChangeHasProgressed = false;
|
||||
private TimeSpan _timeOffset;
|
||||
|
||||
public override DoorState State
|
||||
{
|
||||
protected set
|
||||
{
|
||||
if (State == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.State = value;
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new DoorStateMessage(this, State));
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not DoorComponentState doorCompState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentlyCrushing = doorCompState.CurrentlyCrushing;
|
||||
StateChangeStartTime = doorCompState.StartTime;
|
||||
State = doorCompState.DoorState;
|
||||
|
||||
if (StateChangeStartTime == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeOffset = State switch
|
||||
{
|
||||
DoorState.Opening => OpenTimeOne,
|
||||
DoorState.Closing => CloseTimeOne,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
|
||||
if (doorCompState.CurTime >= StateChangeStartTime + _timeOffset)
|
||||
{
|
||||
_stateChangeHasProgressed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_stateChangeHasProgressed = false;
|
||||
}
|
||||
|
||||
public void OnUpdate()
|
||||
{
|
||||
if (!_stateChangeHasProgressed)
|
||||
{
|
||||
if (GameTiming.CurTime < StateChangeStartTime + _timeOffset) return;
|
||||
|
||||
if (State == DoorState.Opening)
|
||||
{
|
||||
OnPartialOpen();
|
||||
}
|
||||
else
|
||||
{
|
||||
OnPartialClose();
|
||||
}
|
||||
|
||||
_stateChangeHasProgressed = true;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DoorStateMessage : EntityEventArgs
|
||||
{
|
||||
public ClientDoorComponent Component { get; }
|
||||
public SharedDoorComponent.DoorState State { get; }
|
||||
|
||||
public DoorStateMessage(ClientDoorComponent component, SharedDoorComponent.DoorState state)
|
||||
{
|
||||
Component = component;
|
||||
State = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class EmergencyLightComponent : Component
|
||||
{
|
||||
public override string Name => "EmergencyLight";
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
var animation = new Animation
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(4),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(PointLightComponent),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
Property = nameof(PointLightComponent.Rotation),
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(Angle.Zero, 0),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(1080), 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var playerComponent = Owner.EnsureComponent<AnimationPlayerComponent>();
|
||||
playerComponent.Play(animation, "emergency");
|
||||
|
||||
playerComponent.AnimationCompleted += s => playerComponent.Play(animation, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ExpendableLightVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.TryGetData(ExpendableLightVisuals.State, out string lightBehaviourID))
|
||||
{
|
||||
if (component.Owner.TryGetComponent<LightBehaviourComponent>(out var lightBehaviour))
|
||||
{
|
||||
lightBehaviour.StopLightBehaviour();
|
||||
|
||||
if (lightBehaviourID != string.Empty)
|
||||
{
|
||||
lightBehaviour.StartLightBehaviour(lightBehaviourID);
|
||||
}
|
||||
else if (component.Owner.TryGetComponent<PointLightComponent>(out var light))
|
||||
{
|
||||
light.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Explosion;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Explosion
|
||||
{
|
||||
[UsedImplicitly]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public class ClusterFlashVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[DataField("state")]
|
||||
private string? _state;
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (!component.Owner.TryGetComponent<ISpriteComponent>(out var sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.TryGetData(ClusterFlashVisuals.GrenadesCounter, out int grenadesCounter))
|
||||
{
|
||||
sprite.LayerSetState(0, $"{_state}-{grenadesCounter}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class FlashLightVisualizer : AppearanceVisualizer
|
||||
{
|
||||
private readonly Animation _radiatingLightAnimation = new()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(1),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(PointLightComponent),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
Property = nameof(PointLightComponent.Radius),
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(3.0f, 0),
|
||||
new AnimationTrackProperty.KeyFrame(2.0f, 0.5f),
|
||||
new AnimationTrackProperty.KeyFrame(3.0f, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private readonly Animation _blinkingLightAnimation = new()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(1),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty()
|
||||
{
|
||||
ComponentType = typeof(PointLightComponent),
|
||||
//To create the blinking effect we go from nearly zero radius, to the light radius, and back
|
||||
//We do this instead of messing with the `PointLightComponent.enabled` because we don't want the animation to affect component behavior
|
||||
InterpolationMode = AnimationInterpolationMode.Nearest,
|
||||
Property = nameof(PointLightComponent.Radius),
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(0.1f, 0),
|
||||
new AnimationTrackProperty.KeyFrame(2f, 0.5f),
|
||||
new AnimationTrackProperty.KeyFrame(0.1f, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private Action<string>? _radiatingCallback;
|
||||
private Action<string>? _blinkingCallback;
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
if (component.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.TryGetData(HandheldLightVisuals.Power,
|
||||
out HandheldLightPowerStates state))
|
||||
{
|
||||
PlayAnimation(component, state);
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayAnimation(AppearanceComponent component, HandheldLightPowerStates state)
|
||||
{
|
||||
component.Owner.EnsureComponent(out AnimationPlayerComponent animationPlayer);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case HandheldLightPowerStates.LowPower:
|
||||
if (!animationPlayer.HasRunningAnimation("radiatingLight"))
|
||||
{
|
||||
animationPlayer.Play(_radiatingLightAnimation, "radiatingLight");
|
||||
_radiatingCallback = (s) => animationPlayer.Play(_radiatingLightAnimation, s);
|
||||
animationPlayer.AnimationCompleted += _radiatingCallback;
|
||||
}
|
||||
|
||||
break;
|
||||
case HandheldLightPowerStates.Dying:
|
||||
animationPlayer.Stop("radiatingLight");
|
||||
animationPlayer.AnimationCompleted -= _radiatingCallback;
|
||||
if (!animationPlayer.HasRunningAnimation("blinkingLight"))
|
||||
{
|
||||
animationPlayer.Play(_blinkingLightAnimation, "blinkingLight");
|
||||
_blinkingCallback = (s) => animationPlayer.Play(_blinkingLightAnimation, s);
|
||||
animationPlayer.AnimationCompleted += _blinkingCallback;
|
||||
}
|
||||
|
||||
break;
|
||||
case HandheldLightPowerStates.FullPower:
|
||||
if (animationPlayer.HasRunningAnimation("blinkingLight"))
|
||||
{
|
||||
animationPlayer.Stop("blinkingLight");
|
||||
animationPlayer.AnimationCompleted -= _blinkingCallback;
|
||||
}
|
||||
|
||||
if (animationPlayer.HasRunningAnimation("radiatingLight"))
|
||||
{
|
||||
animationPlayer.Stop("radiatingLight");
|
||||
animationPlayer.AnimationCompleted -= _radiatingCallback;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Fluids;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Fluids
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class SprayVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[DataField("safety_on_state")]
|
||||
private string? _safetyOnState;
|
||||
[DataField("safety_off_state")]
|
||||
private string? _safetyOffState;
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.TryGetData<bool>(SprayVisuals.Safety, out var safety))
|
||||
{
|
||||
SetSafety(component, safety);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSafety(AppearanceComponent component, bool safety)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
sprite.LayerSetState(SprayVisualLayers.Base, safety ? _safetyOnState : _safetyOffState);
|
||||
}
|
||||
}
|
||||
|
||||
public enum SprayVisualLayers : byte
|
||||
{
|
||||
Base
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.GUI;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.GUI
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedStrippableComponent))]
|
||||
public class StrippableComponent : SharedStrippableComponent
|
||||
{
|
||||
public override bool Drop(DragDropEvent args)
|
||||
{
|
||||
// TODO: Prediction
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Gravity;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Gravity
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GravityGeneratorBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private GravityGeneratorWindow? _window;
|
||||
|
||||
public bool IsOn;
|
||||
|
||||
public GravityGeneratorBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base (owner, uiKey)
|
||||
{
|
||||
SendMessage(new SharedGravityGeneratorComponent.GeneratorStatusRequestMessage());
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
IsOn = false;
|
||||
|
||||
_window = new GravityGeneratorWindow(this);
|
||||
|
||||
_window.Switch.OnPressed += (_) =>
|
||||
{
|
||||
SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(!IsOn));
|
||||
SendMessage(new SharedGravityGeneratorComponent.GeneratorStatusRequestMessage());
|
||||
};
|
||||
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
var castState = (SharedGravityGeneratorComponent.GeneratorState) state;
|
||||
IsOn = castState.On;
|
||||
_window?.UpdateButton();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public class GravityGeneratorWindow : SS14Window
|
||||
{
|
||||
public Label Status;
|
||||
|
||||
public Button Switch;
|
||||
|
||||
public GravityGeneratorBoundUserInterface Owner;
|
||||
|
||||
public GravityGeneratorWindow(GravityGeneratorBoundUserInterface ui)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Owner = ui;
|
||||
|
||||
Title = Loc.GetString("Gravity Generator Control");
|
||||
|
||||
var vBox = new VBoxContainer
|
||||
{
|
||||
MinSize = new Vector2(250, 100)
|
||||
};
|
||||
Status = new Label
|
||||
{
|
||||
Text = Loc.GetString("Current Status: " + (Owner.IsOn ? "On" : "Off")),
|
||||
FontColorOverride = Owner.IsOn ? Color.ForestGreen : Color.Red
|
||||
};
|
||||
Switch = new Button
|
||||
{
|
||||
Text = Loc.GetString(Owner.IsOn ? "Turn Off" : "Turn On"),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
MinSize = new Vector2(150, 60)
|
||||
};
|
||||
|
||||
vBox.AddChild(Status);
|
||||
vBox.AddChild(Switch);
|
||||
|
||||
Contents.AddChild(vBox);
|
||||
}
|
||||
|
||||
public void UpdateButton()
|
||||
{
|
||||
Status.Text = Loc.GetString("Current Status: " + (Owner.IsOn ? "On" : "Off"));
|
||||
Status.FontColorOverride = Owner.IsOn ? Color.ForestGreen : Color.Red;
|
||||
Switch.Text = Loc.GetString(Owner.IsOn ? "Turn Off" : "Turn On");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
#nullable enable
|
||||
using Content.Shared.GameObjects.Components.Gravity;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Gravity
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GravityGeneratorVisualizer : AppearanceVisualizer, ISerializationHooks
|
||||
{
|
||||
[DataField("spritemap")]
|
||||
private Dictionary<string, string> _rawSpriteMap = new();
|
||||
private Dictionary<GravityGeneratorStatus, string> _spriteMap = new();
|
||||
|
||||
void ISerializationHooks.BeforeSerialization()
|
||||
{
|
||||
_rawSpriteMap = new Dictionary<string, string>();
|
||||
foreach (var (status, sprite) in _spriteMap)
|
||||
{
|
||||
_rawSpriteMap.Add(status.ToString().ToLower(), sprite);
|
||||
}
|
||||
}
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
// Get Sprites for each status
|
||||
foreach (var status in (GravityGeneratorStatus[]) Enum.GetValues(typeof(GravityGeneratorStatus)))
|
||||
{
|
||||
if (_rawSpriteMap.TryGetValue(status.ToString().ToLower(), out var sprite))
|
||||
{
|
||||
_spriteMap[status] = sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
if (!entity.TryGetComponent(out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
sprite.LayerMapReserveBlank(GravityGeneratorVisualLayers.Base);
|
||||
sprite.LayerMapReserveBlank(GravityGeneratorVisualLayers.Core);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<SpriteComponent>();
|
||||
|
||||
if (component.TryGetData(GravityGeneratorVisuals.State, out GravityGeneratorStatus state))
|
||||
{
|
||||
if (_spriteMap.TryGetValue(state, out var spriteState))
|
||||
{
|
||||
var layer = sprite.LayerMapGet(GravityGeneratorVisualLayers.Base);
|
||||
sprite.LayerSetState(layer, spriteState);
|
||||
}
|
||||
}
|
||||
|
||||
if (component.TryGetData(GravityGeneratorVisuals.CoreVisible, out bool visible))
|
||||
{
|
||||
var layer = sprite.LayerMapGet(GravityGeneratorVisualLayers.Core);
|
||||
sprite.LayerSetVisible(layer, visible);
|
||||
}
|
||||
}
|
||||
|
||||
public enum GravityGeneratorVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
Core
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Client.GameObjects.Components.Clothing;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects.EntitySystems.EffectBlocker;
|
||||
using Content.Shared.Preferences.Appearance;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
|
||||
using static Content.Shared.GameObjects.Components.Inventory.SharedInventoryComponent.ClientInventoryMessage;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.HUD.Inventory
|
||||
{
|
||||
/// <summary>
|
||||
/// A character UI which shows items the user has equipped within his inventory
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedInventoryComponent))]
|
||||
public class ClientInventoryComponent : SharedInventoryComponent, IEffectBlocker
|
||||
{
|
||||
private readonly Dictionary<Slots, IEntity> _slots = new();
|
||||
|
||||
public IReadOnlyDictionary<Slots, IEntity> AllSlots => _slots;
|
||||
|
||||
[ViewVariables] public InventoryInterfaceController InterfaceController { get; private set; } = default!;
|
||||
|
||||
[ComponentDependency]
|
||||
private ISpriteComponent? _sprite;
|
||||
|
||||
private bool _playerAttached = false;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("speciesId")] public string? SpeciesId { get; set; }
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
if (_playerAttached)
|
||||
{
|
||||
InterfaceController?.PlayerDetached();
|
||||
}
|
||||
InterfaceController?.Dispose();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
var controllerType = ReflectionManager.LooseGetType(InventoryInstance.InterfaceControllerTypeName);
|
||||
var args = new object[] {this};
|
||||
InterfaceController = DynamicTypeFactory.CreateInstance<InventoryInterfaceController>(controllerType, args);
|
||||
InterfaceController.Initialize();
|
||||
|
||||
if (_sprite != null)
|
||||
{
|
||||
foreach (var mask in InventoryInstance.SlotMasks.OrderBy(s => InventoryInstance.SlotDrawingOrder(s)))
|
||||
{
|
||||
if (mask == Slots.NONE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_sprite.LayerMapReserveBlank(mask);
|
||||
}
|
||||
}
|
||||
|
||||
// Component state already came in but we couldn't set anything visually because, well, we didn't initialize yet.
|
||||
foreach (var (slot, entity) in _slots)
|
||||
{
|
||||
_setSlot(slot, entity);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsEquipped(IEntity item)
|
||||
{
|
||||
return item != null && _slots.Values.Any(e => e == item);
|
||||
}
|
||||
|
||||
public override float WalkSpeedModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
var mod = 1f;
|
||||
foreach (var slot in _slots.Values)
|
||||
{
|
||||
if (slot != null)
|
||||
{
|
||||
foreach (var modifier in slot.GetAllComponents<IMoveSpeedModifier>())
|
||||
{
|
||||
mod *= modifier.WalkSpeedModifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
}
|
||||
|
||||
public override float SprintSpeedModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
var mod = 1f;
|
||||
foreach (var slot in _slots.Values)
|
||||
{
|
||||
if (slot != null)
|
||||
{
|
||||
foreach (var modifier in slot.GetAllComponents<IMoveSpeedModifier>())
|
||||
{
|
||||
mod *= modifier.SprintSpeedModifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not InventoryComponentState state)
|
||||
return;
|
||||
|
||||
var doneSlots = new HashSet<Slots>();
|
||||
|
||||
foreach (var (slot, entityUid) in state.Entities)
|
||||
{
|
||||
if (!Owner.EntityManager.TryGetEntity(entityUid, out var entity))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!_slots.ContainsKey(slot) || _slots[slot] != entity)
|
||||
{
|
||||
_slots[slot] = entity;
|
||||
_setSlot(slot, entity);
|
||||
}
|
||||
doneSlots.Add(slot);
|
||||
}
|
||||
|
||||
if (state.HoverEntity != null)
|
||||
{
|
||||
var (slot, (entityUid, fits)) = state.HoverEntity.Value;
|
||||
var entity = Owner.EntityManager.GetEntity(entityUid);
|
||||
|
||||
InterfaceController?.HoverInSlot(slot, entity, fits);
|
||||
}
|
||||
|
||||
foreach (var slot in _slots.Keys.ToList())
|
||||
{
|
||||
if (!doneSlots.Contains(slot))
|
||||
{
|
||||
_clearSlot(slot);
|
||||
_slots.Remove(slot);
|
||||
}
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? mod))
|
||||
{
|
||||
mod.RefreshMovementSpeedModifiers();
|
||||
}
|
||||
}
|
||||
|
||||
private void _setSlot(Slots slot, IEntity entity)
|
||||
{
|
||||
SetSlotVisuals(slot, entity);
|
||||
|
||||
InterfaceController?.AddToSlot(slot, entity);
|
||||
}
|
||||
|
||||
internal void SetSlotVisuals(Slots slot, IEntity entity)
|
||||
{
|
||||
if (_sprite == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out ClothingComponent? clothing))
|
||||
{
|
||||
var flag = SlotMasks[slot];
|
||||
var data = clothing.GetEquippedStateInfo(flag, SpeciesId);
|
||||
if (data != null)
|
||||
{
|
||||
var (rsi, state) = data.Value;
|
||||
_sprite.LayerSetVisible(slot, true);
|
||||
_sprite.LayerSetState(slot, state, rsi);
|
||||
_sprite.LayerSetAutoAnimated(slot, true);
|
||||
|
||||
if (slot == Slots.INNERCLOTHING && _sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out _))
|
||||
{
|
||||
_sprite.LayerSetState(HumanoidVisualLayers.StencilMask, clothing.FemaleMask switch
|
||||
{
|
||||
FemaleClothingMask.NoMask => "female_none",
|
||||
FemaleClothingMask.UniformTop => "female_top",
|
||||
_ => "female_full",
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_sprite.LayerSetVisible(slot, false);
|
||||
}
|
||||
|
||||
internal void ClearAllSlotVisuals()
|
||||
{
|
||||
if (_sprite == null)
|
||||
return;
|
||||
|
||||
foreach (var slot in InventoryInstance.SlotMasks)
|
||||
{
|
||||
if (slot != Slots.NONE)
|
||||
{
|
||||
_sprite.LayerSetVisible(slot, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _clearSlot(Slots slot)
|
||||
{
|
||||
InterfaceController?.RemoveFromSlot(slot);
|
||||
_sprite?.LayerSetVisible(slot, false);
|
||||
}
|
||||
|
||||
public void SendEquipMessage(Slots slot)
|
||||
{
|
||||
var equipMessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Equip);
|
||||
SendNetworkMessage(equipMessage);
|
||||
}
|
||||
|
||||
public void SendUseMessage(Slots slot)
|
||||
{
|
||||
var equipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Use);
|
||||
SendNetworkMessage(equipmessage);
|
||||
}
|
||||
|
||||
public void SendHoverMessage(Slots slot)
|
||||
{
|
||||
SendNetworkMessage(new ClientInventoryMessage(slot, ClientInventoryUpdate.Hover));
|
||||
}
|
||||
|
||||
public void SendOpenStorageUIMessage(Slots slot)
|
||||
{
|
||||
SendNetworkMessage(new OpenSlotStorageUIMessage(slot));
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PlayerAttachedMsg _:
|
||||
InterfaceController.PlayerAttached();
|
||||
_playerAttached = true;
|
||||
break;
|
||||
|
||||
case PlayerDetachedMsg _:
|
||||
InterfaceController.PlayerDetached();
|
||||
_playerAttached = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetSlot(Slots slot, [NotNullWhen(true)] out IEntity? item)
|
||||
{
|
||||
return _slots.TryGetValue(slot, out item);
|
||||
}
|
||||
|
||||
public bool TryFindItemSlots(IEntity item, [NotNullWhen(true)] out Slots? slots)
|
||||
{
|
||||
slots = null;
|
||||
|
||||
foreach (var (slot, entity) in _slots)
|
||||
{
|
||||
if (entity == item)
|
||||
{
|
||||
slots = slot;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IEffectBlocker.CanSlip()
|
||||
{
|
||||
return !TryGetSlot(Slots.SHOES, out var shoes) || shoes == null || EffectBlockerSystem.CanSlip(shoes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,338 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared;
|
||||
using Content.Shared.Prototypes.HUD;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.HUD.Inventory
|
||||
{
|
||||
// Dynamically instantiated by ClientInventoryComponent.
|
||||
[UsedImplicitly]
|
||||
public class HumanInventoryInterfaceController : InventoryInterfaceController
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||
[Dependency] private readonly INetConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private readonly Dictionary<Slots, List<ItemSlotButton>> _inventoryButtons
|
||||
= new();
|
||||
|
||||
private ItemSlotButton _hudButtonPocket1 = default!;
|
||||
private ItemSlotButton _hudButtonPocket2 = default!;
|
||||
private ItemSlotButton _hudButtonShoes = default!;
|
||||
private ItemSlotButton _hudButtonJumpsuit = default!;
|
||||
private ItemSlotButton _hudButtonGloves = default!;
|
||||
private ItemSlotButton _hudButtonNeck = default!;
|
||||
private ItemSlotButton _hudButtonHead = default!;
|
||||
private ItemSlotButton _hudButtonBelt = default!;
|
||||
private ItemSlotButton _hudButtonBack = default!;
|
||||
private ItemSlotButton _hudButtonOClothing = default!;
|
||||
private ItemSlotButton _hudButtonId = default!;
|
||||
private ItemSlotButton _hudButtonMask = default!;
|
||||
private ItemSlotButton _hudButtonEyes = default!;
|
||||
private ItemSlotButton _hudButtonEars = default!;
|
||||
|
||||
private Control _topQuickButtonsContainer = default!;
|
||||
private Control _bottomLeftQuickButtonsContainer = default!;
|
||||
private Control _bottomRightQuickButtonsContainer = default!;
|
||||
|
||||
public HumanInventoryInterfaceController(ClientInventoryComponent owner) : base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_configManager.OnValueChanged(CCVars.HudTheme, UpdateHudTheme, invokeImmediately: true);
|
||||
|
||||
_window = new HumanInventoryWindow(_gameHud);
|
||||
_window.OnClose += () => GameHud.InventoryButtonDown = false;
|
||||
foreach (var (slot, button) in _window.Buttons)
|
||||
{
|
||||
button.OnPressed = (e) => AddToInventory(e, slot);
|
||||
button.OnStoragePressed = (e) => OpenStorage(e, slot);
|
||||
button.OnHover = (_) => RequestItemHover(slot);
|
||||
_inventoryButtons.Add(slot, new List<ItemSlotButton> {button});
|
||||
}
|
||||
|
||||
void AddButton(out ItemSlotButton variable, Slots slot, string textureName)
|
||||
{
|
||||
var texture = _gameHud.GetHudTexture($"{textureName}.png");
|
||||
var storageTexture = _gameHud.GetHudTexture("back.png");
|
||||
variable = new ItemSlotButton(texture, storageTexture, textureName)
|
||||
{
|
||||
OnPressed = (e) => AddToInventory(e, slot),
|
||||
OnStoragePressed = (e) => OpenStorage(e, slot),
|
||||
OnHover = (_) => RequestItemHover(slot)
|
||||
};
|
||||
_inventoryButtons[slot].Add(variable);
|
||||
}
|
||||
|
||||
AddButton(out _hudButtonPocket1, Slots.POCKET1, "pocket");
|
||||
AddButton(out _hudButtonPocket2, Slots.POCKET2, "pocket");
|
||||
AddButton(out _hudButtonId, Slots.IDCARD, "id");
|
||||
|
||||
AddButton(out _hudButtonBack, Slots.BACKPACK, "back");
|
||||
|
||||
AddButton(out _hudButtonBelt, Slots.BELT, "belt");
|
||||
|
||||
AddButton(out _hudButtonShoes, Slots.SHOES, "shoes");
|
||||
AddButton(out _hudButtonJumpsuit, Slots.INNERCLOTHING, "uniform");
|
||||
AddButton(out _hudButtonOClothing, Slots.OUTERCLOTHING, "suit");
|
||||
AddButton(out _hudButtonGloves, Slots.GLOVES, "gloves");
|
||||
AddButton(out _hudButtonNeck, Slots.NECK, "neck");
|
||||
AddButton(out _hudButtonMask, Slots.MASK, "mask");
|
||||
AddButton(out _hudButtonEyes, Slots.EYES, "glasses");
|
||||
AddButton(out _hudButtonEars, Slots.EARS, "ears");
|
||||
AddButton(out _hudButtonHead, Slots.HEAD, "head");
|
||||
|
||||
_topQuickButtonsContainer = new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
_hudButtonShoes,
|
||||
_hudButtonJumpsuit,
|
||||
_hudButtonOClothing,
|
||||
_hudButtonGloves,
|
||||
_hudButtonNeck,
|
||||
_hudButtonMask,
|
||||
_hudButtonEyes,
|
||||
_hudButtonEars,
|
||||
_hudButtonHead
|
||||
},
|
||||
SeparationOverride = 5
|
||||
};
|
||||
|
||||
_bottomRightQuickButtonsContainer = new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
_hudButtonPocket1,
|
||||
_hudButtonPocket2,
|
||||
_hudButtonId,
|
||||
},
|
||||
SeparationOverride = 5
|
||||
};
|
||||
_bottomLeftQuickButtonsContainer = new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
_hudButtonBelt,
|
||||
_hudButtonBack
|
||||
},
|
||||
SeparationOverride = 5
|
||||
};
|
||||
}
|
||||
|
||||
public override SS14Window? Window => _window;
|
||||
private HumanInventoryWindow? _window;
|
||||
|
||||
public override IEnumerable<ItemSlotButton> GetItemSlotButtons(Slots slot)
|
||||
{
|
||||
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
|
||||
{
|
||||
return Enumerable.Empty<ItemSlotButton>();
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
public override void AddToSlot(Slots slot, IEntity entity)
|
||||
{
|
||||
base.AddToSlot(slot, entity);
|
||||
|
||||
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
|
||||
return;
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
_itemSlotManager.SetItemSlot(button, entity);
|
||||
button.OnPressed = (e) => HandleInventoryKeybind(e, slot);
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemoveFromSlot(Slots slot)
|
||||
{
|
||||
base.RemoveFromSlot(slot);
|
||||
|
||||
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
ClearButton(button, slot);
|
||||
}
|
||||
}
|
||||
|
||||
public override void HoverInSlot(Slots slot, IEntity entity, bool fits)
|
||||
{
|
||||
base.HoverInSlot(slot, entity, fits);
|
||||
|
||||
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
_itemSlotManager.HoverInSlot(button, entity, fits);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void HandleInventoryKeybind(GUIBoundKeyEventArgs args, Slots slot)
|
||||
{
|
||||
if (!_inventoryButtons.ContainsKey(slot))
|
||||
return;
|
||||
if (!Owner.TryGetSlot(slot, out var item))
|
||||
return;
|
||||
if (_itemSlotManager.OnButtonPressed(args, item))
|
||||
return;
|
||||
|
||||
base.HandleInventoryKeybind(args, slot);
|
||||
}
|
||||
|
||||
private void ClearButton(ItemSlotButton button, Slots slot)
|
||||
{
|
||||
button.OnPressed = (e) => AddToInventory(e, slot);
|
||||
_itemSlotManager.SetItemSlot(button, null);
|
||||
}
|
||||
|
||||
public override void PlayerAttached()
|
||||
{
|
||||
base.PlayerAttached();
|
||||
|
||||
GameHud.BottomLeftInventoryQuickButtonContainer.AddChild(_bottomLeftQuickButtonsContainer);
|
||||
GameHud.BottomRightInventoryQuickButtonContainer.AddChild(_bottomRightQuickButtonsContainer);
|
||||
GameHud.TopInventoryQuickButtonContainer.AddChild(_topQuickButtonsContainer);
|
||||
|
||||
// Update all the buttons to make sure they check out.
|
||||
|
||||
foreach (var (slot, buttons) in _inventoryButtons)
|
||||
{
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
ClearButton(button, slot);
|
||||
}
|
||||
|
||||
if (Owner.TryGetSlot(slot, out var entity))
|
||||
{
|
||||
AddToSlot(slot, entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void PlayerDetached()
|
||||
{
|
||||
base.PlayerDetached();
|
||||
|
||||
GameHud.BottomRightInventoryQuickButtonContainer.RemoveChild(_bottomRightQuickButtonsContainer);
|
||||
GameHud.BottomLeftInventoryQuickButtonContainer.RemoveChild(_bottomLeftQuickButtonsContainer);
|
||||
GameHud.TopInventoryQuickButtonContainer.RemoveChild(_topQuickButtonsContainer);
|
||||
|
||||
foreach (var (slot, list) in _inventoryButtons)
|
||||
{
|
||||
foreach (var button in list)
|
||||
{
|
||||
ClearButton(button, slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateHudTheme(int idx)
|
||||
{
|
||||
if (!_gameHud.ValidateHudTheme(idx))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (_, list) in _inventoryButtons)
|
||||
{
|
||||
foreach (var button in list)
|
||||
{
|
||||
button.Button.Texture = _gameHud.GetHudTexture($"{button.TextureName}.png");
|
||||
button.StorageButton.TextureNormal = _gameHud.GetHudTexture("back.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class HumanInventoryWindow : SS14Window
|
||||
{
|
||||
private const int ButtonSize = 64;
|
||||
private const int ButtonSeparation = 4;
|
||||
private const int RightSeparation = 2;
|
||||
|
||||
public IReadOnlyDictionary<Slots, ItemSlotButton> Buttons { get; }
|
||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||
|
||||
public HumanInventoryWindow(IGameHud gameHud)
|
||||
{
|
||||
Title = Loc.GetString("Your Inventory");
|
||||
Resizable = false;
|
||||
|
||||
var buttonDict = new Dictionary<Slots, ItemSlotButton>();
|
||||
Buttons = buttonDict;
|
||||
|
||||
const int width = ButtonSize * 4 + ButtonSeparation * 3 + RightSeparation;
|
||||
const int height = ButtonSize * 4 + ButtonSeparation * 3;
|
||||
|
||||
var windowContents = new LayoutContainer {MinSize = (width, height)};
|
||||
Contents.AddChild(windowContents);
|
||||
|
||||
void AddButton(Slots slot, string textureName, Vector2 position)
|
||||
{
|
||||
var texture = gameHud.GetHudTexture($"{textureName}.png");
|
||||
var storageTexture = gameHud.GetHudTexture("back.png");
|
||||
var button = new ItemSlotButton(texture, storageTexture, textureName);
|
||||
|
||||
LayoutContainer.SetPosition(button, position);
|
||||
|
||||
windowContents.AddChild(button);
|
||||
buttonDict.Add(slot, button);
|
||||
}
|
||||
|
||||
const int sizep = (ButtonSize + ButtonSeparation);
|
||||
|
||||
// Left column.
|
||||
AddButton(Slots.EYES, "glasses", (0, 0));
|
||||
AddButton(Slots.NECK, "neck", (0, sizep));
|
||||
AddButton(Slots.INNERCLOTHING, "uniform", (0, 2 * sizep));
|
||||
AddButton(Slots.POCKET1, "pocket", (0, 3 * sizep));
|
||||
|
||||
// Middle column.
|
||||
AddButton(Slots.HEAD, "head", (sizep, 0));
|
||||
AddButton(Slots.MASK, "mask", (sizep, sizep));
|
||||
AddButton(Slots.OUTERCLOTHING, "suit", (sizep, 2 * sizep));
|
||||
AddButton(Slots.SHOES, "shoes", (sizep, 3 * sizep));
|
||||
|
||||
// Right column
|
||||
AddButton(Slots.EARS, "ears", (2 * sizep, 0));
|
||||
AddButton(Slots.IDCARD, "id", (2 * sizep, sizep));
|
||||
AddButton(Slots.GLOVES, "gloves", (2 * sizep, 2 * sizep));
|
||||
AddButton(Slots.POCKET2, "pocket", (2 * sizep, 3 * sizep));
|
||||
|
||||
// Far right column.
|
||||
AddButton(Slots.BACKPACK, "back", (3 * sizep, 0));
|
||||
AddButton(Slots.BELT, "belt", (3 * sizep, sizep));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.HUD.Inventory
|
||||
{
|
||||
public abstract class InventoryInterfaceController : IDisposable
|
||||
{
|
||||
[Dependency] protected readonly IGameHud GameHud = default!;
|
||||
|
||||
protected InventoryInterfaceController(ClientInventoryComponent owner)
|
||||
{
|
||||
Owner = owner;
|
||||
}
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract SS14Window? Window { get; }
|
||||
protected ClientInventoryComponent Owner { get; }
|
||||
|
||||
public virtual void PlayerAttached()
|
||||
{
|
||||
GameHud.InventoryButtonVisible = true;
|
||||
}
|
||||
|
||||
public virtual void PlayerDetached()
|
||||
{
|
||||
GameHud.InventoryButtonVisible = false;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <returns>the button controls associated with the
|
||||
/// specified slot, if any. Empty if none.</returns>
|
||||
public abstract IEnumerable<ItemSlotButton> GetItemSlotButtons(EquipmentSlotDefines.Slots slot);
|
||||
|
||||
public virtual void AddToSlot(EquipmentSlotDefines.Slots slot, IEntity entity)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void HoverInSlot(EquipmentSlotDefines.Slots slot, IEntity entity, bool fits)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void RemoveFromSlot(EquipmentSlotDefines.Slots slot)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void HandleInventoryKeybind(GUIBoundKeyEventArgs args, EquipmentSlotDefines.Slots slot)
|
||||
{
|
||||
if (args.Function == EngineKeyFunctions.UIClick)
|
||||
{
|
||||
UseItemOnInventory(slot);
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddToInventory(GUIBoundKeyEventArgs args, EquipmentSlotDefines.Slots slot)
|
||||
{
|
||||
if (args.Function != EngineKeyFunctions.UIClick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Owner.SendEquipMessage(slot);
|
||||
}
|
||||
|
||||
protected void UseItemOnInventory(EquipmentSlotDefines.Slots slot)
|
||||
{
|
||||
Owner.SendUseMessage(slot);
|
||||
}
|
||||
|
||||
protected void OpenStorage(GUIBoundKeyEventArgs args, EquipmentSlotDefines.Slots slot)
|
||||
{
|
||||
if (args.Function != EngineKeyFunctions.UIClick && args.Function != ContentKeyFunctions.ActivateItemInWorld)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Owner.SendOpenStorageUIMessage(slot);
|
||||
}
|
||||
|
||||
protected void RequestItemHover(EquipmentSlotDefines.Slots slot)
|
||||
{
|
||||
Owner.SendHoverMessage(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.GameObjects.Components.GUI;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.HUD.Inventory
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class StrippableBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
public Dictionary<Slots, string>? Inventory { get; private set; }
|
||||
public Dictionary<string, string>? Hands { get; private set; }
|
||||
public Dictionary<EntityUid, string>? Handcuffs { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
private StrippingMenu? _strippingMenu;
|
||||
|
||||
public StrippableBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_strippingMenu = new StrippingMenu($"{Owner.Owner.Name}'s inventory");
|
||||
|
||||
_strippingMenu.OnClose += Close;
|
||||
_strippingMenu.OpenCentered();
|
||||
UpdateMenu();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_strippingMenu?.Dispose();
|
||||
}
|
||||
|
||||
private void UpdateMenu()
|
||||
{
|
||||
if (_strippingMenu == null) return;
|
||||
|
||||
_strippingMenu.ClearButtons();
|
||||
|
||||
if (Inventory != null)
|
||||
{
|
||||
foreach (var (slot, name) in Inventory)
|
||||
{
|
||||
_strippingMenu.AddButton(SlotNames[slot], name, (ev) =>
|
||||
{
|
||||
SendMessage(new StrippingInventoryButtonPressed(slot));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Hands != null)
|
||||
{
|
||||
foreach (var (hand, name) in Hands)
|
||||
{
|
||||
_strippingMenu.AddButton(hand, name, (ev) =>
|
||||
{
|
||||
SendMessage(new StrippingHandButtonPressed(hand));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Handcuffs != null)
|
||||
{
|
||||
foreach (var (id, name) in Handcuffs)
|
||||
{
|
||||
_strippingMenu.AddButton(Loc.GetString("Restraints"), name, (ev) =>
|
||||
{
|
||||
SendMessage(new StrippingHandcuffButtonPressed(id));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not StrippingBoundUserInterfaceState stripState) return;
|
||||
|
||||
Inventory = stripState.Inventory;
|
||||
Hands = stripState.Hands;
|
||||
Handcuffs = stripState.Handcuffs;
|
||||
|
||||
UpdateMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class HandheldLightComponent : SharedHandheldLightComponent, IItemStatus
|
||||
{
|
||||
[ViewVariables] protected override bool HasCell => _level != null;
|
||||
|
||||
private byte? _level;
|
||||
|
||||
public Control MakeControl()
|
||||
{
|
||||
return new StatusControl(this);
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not HandheldLightComponentState cast)
|
||||
return;
|
||||
|
||||
_level = cast.Charge;
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private const float TimerCycle = 1;
|
||||
|
||||
private readonly HandheldLightComponent _parent;
|
||||
private readonly PanelContainer[] _sections = new PanelContainer[StatusLevels - 1];
|
||||
|
||||
private float _timer;
|
||||
|
||||
private static readonly StyleBoxFlat StyleBoxLit = new()
|
||||
{
|
||||
BackgroundColor = Color.Green
|
||||
};
|
||||
|
||||
private static readonly StyleBoxFlat StyleBoxUnlit = new()
|
||||
{
|
||||
BackgroundColor = Color.Black
|
||||
};
|
||||
|
||||
public StatusControl(HandheldLightComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
|
||||
var wrapper = new HBoxContainer
|
||||
{
|
||||
SeparationOverride = 4,
|
||||
HorizontalAlignment = HAlignment.Center
|
||||
};
|
||||
|
||||
AddChild(wrapper);
|
||||
|
||||
for (var i = 0; i < _sections.Length; i++)
|
||||
{
|
||||
var panel = new PanelContainer {MinSize = (20, 20)};
|
||||
wrapper.AddChild(panel);
|
||||
_sections[i] = panel;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_parent.HasCell)
|
||||
return;
|
||||
|
||||
_timer += args.DeltaSeconds;
|
||||
_timer %= TimerCycle;
|
||||
|
||||
var level = _parent._level;
|
||||
|
||||
for (var i = 0; i < _sections.Length; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
if (level == 0)
|
||||
{
|
||||
_sections[0].PanelOverride = StyleBoxUnlit;
|
||||
}
|
||||
else if (level == 1)
|
||||
{
|
||||
// Flash the last light.
|
||||
_sections[0].PanelOverride = _timer > TimerCycle / 2 ? StyleBoxLit : StyleBoxUnlit;
|
||||
}
|
||||
else
|
||||
{
|
||||
_sections[0].PanelOverride = StyleBoxLit;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
_sections[i].PanelOverride = level >= i + 2 ? StyleBoxLit : StyleBoxUnlit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows a component to provide status tooltips next to the hands interface.
|
||||
/// </summary>
|
||||
public interface IItemStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Called to get a control that represents the status for this component.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The control to render as status.
|
||||
/// </returns>
|
||||
public Control MakeControl();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the item no longer needs this status (say, dropped from hand)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Useful to allow you to drop the control for the GC, if you need to.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Note that this may be called after a second invocation of <see cref="MakeControl"/> (for example if the user switches the item between two hands).
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void DestroyControl(Control control)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.GameObjects.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.GameObjects.SpriteComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.IconSmoothing
|
||||
{
|
||||
// TODO: Potential improvements:
|
||||
// Defer updating of these.
|
||||
// Get told by somebody to use a loop.
|
||||
/// <summary>
|
||||
/// Makes sprites of other grid-aligned entities like us connect.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The system is based on Baystation12's smoothwalling, and thus will work with those.
|
||||
/// To use, set <c>base</c> equal to the prefix of the corner states in the sprite base RSI.
|
||||
/// Any objects with the same <c>key</c> will connect.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
public class IconSmoothComponent : Component
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
[DataField("mode")]
|
||||
private IconSmoothingMode _mode = IconSmoothingMode.Corners;
|
||||
|
||||
public override string Name => "IconSmooth";
|
||||
|
||||
internal ISpriteComponent? Sprite { get; private set; }
|
||||
|
||||
private (GridId, Vector2i) _lastPosition;
|
||||
|
||||
/// <summary>
|
||||
/// We will smooth with other objects with the same key.
|
||||
/// </summary>
|
||||
[DataField("key")]
|
||||
public string? SmoothKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Prepended to the RSI state.
|
||||
/// </summary>
|
||||
[DataField("base")]
|
||||
public string StateBase { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Mode that controls how the icon should be selected.
|
||||
/// </summary>
|
||||
public IconSmoothingMode Mode => _mode;
|
||||
|
||||
/// <summary>
|
||||
/// Used by <see cref="IconSmoothSystem"/> to reduce redundant updates.
|
||||
/// </summary>
|
||||
internal int UpdateGeneration { get; set; }
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Sprite = Owner.GetComponent<ISpriteComponent>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (Owner.Transform.Anchored)
|
||||
{
|
||||
// ensures lastposition initial value is populated on spawn. Just calling
|
||||
// the hook here would cause a dirty event to fire needlessly
|
||||
UpdateLastPosition();
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new IconSmoothDirtyEvent(Owner, null, Mode));
|
||||
}
|
||||
|
||||
if (Sprite != null && Mode == IconSmoothingMode.Corners)
|
||||
{
|
||||
var state0 = $"{StateBase}0";
|
||||
Sprite.LayerMapSet(CornerLayers.SE, Sprite.AddLayerState(state0));
|
||||
Sprite.LayerSetDirOffset(CornerLayers.SE, DirectionOffset.None);
|
||||
Sprite.LayerMapSet(CornerLayers.NE, Sprite.AddLayerState(state0));
|
||||
Sprite.LayerSetDirOffset(CornerLayers.NE, DirectionOffset.CounterClockwise);
|
||||
Sprite.LayerMapSet(CornerLayers.NW, Sprite.AddLayerState(state0));
|
||||
Sprite.LayerSetDirOffset(CornerLayers.NW, DirectionOffset.Flip);
|
||||
Sprite.LayerMapSet(CornerLayers.SW, Sprite.AddLayerState(state0));
|
||||
Sprite.LayerSetDirOffset(CornerLayers.SW, DirectionOffset.Clockwise);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLastPosition()
|
||||
{
|
||||
if (_mapManager.TryGetGrid(Owner.Transform.GridID, out var grid))
|
||||
{
|
||||
_lastPosition = (Owner.Transform.GridID, grid.TileIndicesFor(Owner.Transform.Coordinates));
|
||||
}
|
||||
else
|
||||
{
|
||||
// When this is called during component startup, the transform can end up being with an invalid grid ID.
|
||||
// In that case, use this.
|
||||
_lastPosition = (GridId.Invalid, new Vector2i(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void CalculateNewSprite()
|
||||
{
|
||||
if (!_mapManager.TryGetGrid(Owner.Transform.GridID, out var grid))
|
||||
{
|
||||
Logger.Error($"Failed to calculate IconSmoothComponent sprite in {Owner} because grid {Owner.Transform.GridID} was missing.");
|
||||
return;
|
||||
}
|
||||
CalculateNewSprite(grid);
|
||||
}
|
||||
|
||||
internal virtual void CalculateNewSprite(IMapGrid grid)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case IconSmoothingMode.Corners:
|
||||
CalculateNewSpriteCorners(grid);
|
||||
break;
|
||||
case IconSmoothingMode.CardinalFlags:
|
||||
CalculateNewSpriteCardinal(grid);
|
||||
break;
|
||||
case IconSmoothingMode.NoSprite:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateNewSpriteCardinal(IMapGrid grid)
|
||||
{
|
||||
if (!Owner.Transform.Anchored || Sprite == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dirs = CardinalConnectDirs.None;
|
||||
|
||||
var position = Owner.Transform.Coordinates;
|
||||
if (MatchingEntity(grid.GetInDir(position, Direction.North)))
|
||||
dirs |= CardinalConnectDirs.North;
|
||||
if (MatchingEntity(grid.GetInDir(position, Direction.South)))
|
||||
dirs |= CardinalConnectDirs.South;
|
||||
if (MatchingEntity(grid.GetInDir(position, Direction.East)))
|
||||
dirs |= CardinalConnectDirs.East;
|
||||
if (MatchingEntity(grid.GetInDir(position, Direction.West)))
|
||||
dirs |= CardinalConnectDirs.West;
|
||||
|
||||
Sprite.LayerSetState(0, $"{StateBase}{(int) dirs}");
|
||||
}
|
||||
|
||||
private void CalculateNewSpriteCorners(IMapGrid grid)
|
||||
{
|
||||
if (Sprite == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var (cornerNE, cornerNW, cornerSW, cornerSE) = CalculateCornerFill(grid);
|
||||
|
||||
Sprite.LayerSetState(CornerLayers.NE, $"{StateBase}{(int) cornerNE}");
|
||||
Sprite.LayerSetState(CornerLayers.SE, $"{StateBase}{(int) cornerSE}");
|
||||
Sprite.LayerSetState(CornerLayers.SW, $"{StateBase}{(int) cornerSW}");
|
||||
Sprite.LayerSetState(CornerLayers.NW, $"{StateBase}{(int) cornerNW}");
|
||||
}
|
||||
|
||||
protected (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(IMapGrid grid)
|
||||
{
|
||||
if (!Owner.Transform.Anchored)
|
||||
{
|
||||
return (CornerFill.None, CornerFill.None, CornerFill.None, CornerFill.None);
|
||||
}
|
||||
|
||||
var position = Owner.Transform.Coordinates;
|
||||
var n = MatchingEntity(grid.GetInDir(position, Direction.North));
|
||||
var ne = MatchingEntity(grid.GetInDir(position, Direction.NorthEast));
|
||||
var e = MatchingEntity(grid.GetInDir(position, Direction.East));
|
||||
var se = MatchingEntity(grid.GetInDir(position, Direction.SouthEast));
|
||||
var s = MatchingEntity(grid.GetInDir(position, Direction.South));
|
||||
var sw = MatchingEntity(grid.GetInDir(position, Direction.SouthWest));
|
||||
var w = MatchingEntity(grid.GetInDir(position, Direction.West));
|
||||
var nw = MatchingEntity(grid.GetInDir(position, Direction.NorthWest));
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
var cornerNE = CornerFill.None;
|
||||
var cornerSE = CornerFill.None;
|
||||
var cornerSW = CornerFill.None;
|
||||
var cornerNW = CornerFill.None;
|
||||
// ReSharper restore InconsistentNaming
|
||||
|
||||
if (n)
|
||||
{
|
||||
cornerNE |= CornerFill.CounterClockwise;
|
||||
cornerNW |= CornerFill.Clockwise;
|
||||
}
|
||||
|
||||
if (ne)
|
||||
{
|
||||
cornerNE |= CornerFill.Diagonal;
|
||||
}
|
||||
|
||||
if (e)
|
||||
{
|
||||
cornerNE |= CornerFill.Clockwise;
|
||||
cornerSE |= CornerFill.CounterClockwise;
|
||||
}
|
||||
|
||||
if (se)
|
||||
{
|
||||
cornerSE |= CornerFill.Diagonal;
|
||||
}
|
||||
|
||||
if (s)
|
||||
{
|
||||
cornerSE |= CornerFill.Clockwise;
|
||||
cornerSW |= CornerFill.CounterClockwise;
|
||||
}
|
||||
|
||||
if (sw)
|
||||
{
|
||||
cornerSW |= CornerFill.Diagonal;
|
||||
}
|
||||
|
||||
if (w)
|
||||
{
|
||||
cornerSW |= CornerFill.Clockwise;
|
||||
cornerNW |= CornerFill.CounterClockwise;
|
||||
}
|
||||
|
||||
if (nw)
|
||||
{
|
||||
cornerNW |= CornerFill.Diagonal;
|
||||
}
|
||||
|
||||
switch (Owner.Transform.WorldRotation.GetCardinalDir())
|
||||
{
|
||||
case Direction.North:
|
||||
return (cornerSW, cornerSE, cornerNE, cornerNW);
|
||||
case Direction.West:
|
||||
return (cornerSE, cornerNE, cornerNW, cornerSW);
|
||||
case Direction.South:
|
||||
return (cornerNE, cornerNW, cornerSW, cornerSE);
|
||||
default:
|
||||
return (cornerNW, cornerSW, cornerSE, cornerNE);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
if (Owner.Transform.Anchored)
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new IconSmoothDirtyEvent(Owner, _lastPosition, Mode));
|
||||
}
|
||||
}
|
||||
|
||||
public void SnapGridOnPositionChanged()
|
||||
{
|
||||
if (Owner.Transform.Anchored)
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new IconSmoothDirtyEvent(Owner, _lastPosition, Mode));
|
||||
UpdateLastPosition();
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.Contracts.Pure]
|
||||
protected bool MatchingEntity(IEnumerable<EntityUid> candidates)
|
||||
{
|
||||
foreach (var entity in candidates)
|
||||
{
|
||||
if (!Owner.EntityManager.ComponentManager.TryGetComponent(entity, out IconSmoothComponent? other))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (other.SmoothKey == SmoothKey)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum CardinalConnectDirs : byte
|
||||
{
|
||||
None = 0,
|
||||
North = 1,
|
||||
South = 2,
|
||||
East = 4,
|
||||
West = 8
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CornerFill : byte
|
||||
{
|
||||
// These values are pulled from Baystation12.
|
||||
// I'm too lazy to convert the state names.
|
||||
None = 0,
|
||||
|
||||
// The cardinal tile counter-clockwise of this corner is filled.
|
||||
CounterClockwise = 1,
|
||||
|
||||
// The diagonal tile in the direction of this corner.
|
||||
Diagonal = 2,
|
||||
|
||||
// The cardinal tile clockwise of this corner is filled.
|
||||
Clockwise = 4,
|
||||
}
|
||||
|
||||
public enum CornerLayers : byte
|
||||
{
|
||||
SE,
|
||||
NE,
|
||||
NW,
|
||||
SW,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controls the mode with which icon smoothing is calculated.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public enum IconSmoothingMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Each icon is made up of 4 corners, each of which can get a different state depending on
|
||||
/// adjacent entities clockwise, counter-clockwise and diagonal with the corner.
|
||||
/// </summary>
|
||||
Corners,
|
||||
|
||||
/// <summary>
|
||||
/// There are 16 icons, only one of which is used at once.
|
||||
/// The icon selected is a bit field made up of the cardinal direction flags that have adjacent entities.
|
||||
/// </summary>
|
||||
CardinalFlags,
|
||||
|
||||
/// <summary>
|
||||
/// Where this component contributes to our neighbors being calculated but we do not update our own sprite.
|
||||
/// </summary>
|
||||
NoSprite,
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using Content.Client.Instruments;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Instruments
|
||||
{
|
||||
public class InstrumentBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private InstrumentMenu? _instrumentMenu;
|
||||
|
||||
public InstrumentComponent? Instrument { get; set; }
|
||||
|
||||
public InstrumentBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
if (!Owner.Owner.TryGetComponent<InstrumentComponent>(out var instrument)) return;
|
||||
|
||||
Instrument = instrument;
|
||||
_instrumentMenu = new InstrumentMenu(this);
|
||||
_instrumentMenu.OnClose += Close;
|
||||
|
||||
_instrumentMenu.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
_instrumentMenu?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,484 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Components.Instruments;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Client.Audio.Midi;
|
||||
using Robust.Shared.Audio.Midi;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Instruments
|
||||
{
|
||||
|
||||
[RegisterComponent]
|
||||
public class InstrumentComponent : SharedInstrumentComponent
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Called when a midi song stops playing.
|
||||
/// </summary>
|
||||
public event Action? OnMidiPlaybackEnded;
|
||||
|
||||
[Dependency] private readonly IMidiManager _midiManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
|
||||
private IMidiRenderer? _renderer;
|
||||
|
||||
private InstrumentSystem _instrumentSystem = default!;
|
||||
|
||||
[DataField("program")]
|
||||
private byte _instrumentProgram = 1;
|
||||
|
||||
[DataField("bank")]
|
||||
private byte _instrumentBank;
|
||||
|
||||
private uint _sequenceDelay;
|
||||
|
||||
private uint _sequenceStartTick;
|
||||
|
||||
[DataField("allowPercussion")]
|
||||
private bool _allowPercussion;
|
||||
|
||||
[DataField("allowProgramChange")]
|
||||
private bool _allowProgramChange;
|
||||
|
||||
[DataField("respectMidiLimits")]
|
||||
private bool _respectMidiLimits = true;
|
||||
|
||||
/// <summary>
|
||||
/// A queue of MidiEvents to be sent to the server.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private readonly List<MidiEvent> _midiEventBuffer = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether a midi song will loop or not.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool LoopMidi
|
||||
{
|
||||
get => _renderer?.LoopMidi ?? false;
|
||||
set
|
||||
{
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.LoopMidi = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the instrument the midi renderer will play.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public override byte InstrumentProgram
|
||||
{
|
||||
get => _instrumentProgram;
|
||||
set
|
||||
{
|
||||
_instrumentProgram = value;
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.MidiProgram = _instrumentProgram;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the instrument bank the midi renderer will use.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public override byte InstrumentBank
|
||||
{
|
||||
get => _instrumentBank;
|
||||
set
|
||||
{
|
||||
_instrumentBank = value;
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.MidiBank = _instrumentBank;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public override bool AllowPercussion
|
||||
{
|
||||
get => _allowPercussion;
|
||||
set
|
||||
{
|
||||
_allowPercussion = value;
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.DisablePercussionChannel = !_allowPercussion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public override bool AllowProgramChange
|
||||
{
|
||||
get => _allowProgramChange;
|
||||
set
|
||||
{
|
||||
_allowProgramChange = value;
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.DisableProgramChangeEvent = !_allowProgramChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this instrument is handheld or not.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("handheld")]
|
||||
public bool Handheld { get; set; } // TODO: Replace this by simply checking if the entity has an ItemComponent.
|
||||
|
||||
/// <summary>
|
||||
/// Whether there's a midi song being played or not.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool IsMidiOpen => _renderer?.Status == MidiRendererStatus.File;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the midi renderer is listening for midi input or not.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool IsInputOpen => _renderer?.Status == MidiRendererStatus.Input;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the midi renderer is alive or not.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool IsRendererAlive => _renderer != null;
|
||||
|
||||
[ViewVariables]
|
||||
public int PlayerTotalTick => _renderer?.PlayerTotalTick ?? 0;
|
||||
|
||||
[ViewVariables]
|
||||
public int PlayerTick
|
||||
{
|
||||
get => _renderer?.PlayerTick ?? 0;
|
||||
set
|
||||
{
|
||||
if (!IsRendererAlive || _renderer!.Status != MidiRendererStatus.File) return;
|
||||
|
||||
_midiEventBuffer.Clear();
|
||||
|
||||
_renderer.PlayerTick = value;
|
||||
var tick = _renderer.SequencerTick;
|
||||
|
||||
// We add a "all notes off" message.
|
||||
for (byte i = 0; i < 16; i++)
|
||||
{
|
||||
_midiEventBuffer.Add(new MidiEvent()
|
||||
{
|
||||
Tick = tick, Type = 176,
|
||||
Control = 123, Velocity = 0, Channel = i,
|
||||
});
|
||||
}
|
||||
|
||||
// Now we add a Reset All Controllers message.
|
||||
_midiEventBuffer.Add(new MidiEvent()
|
||||
{
|
||||
Tick = tick, Type = 176,
|
||||
Control = 121, Value = 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
IoCManager.InjectDependencies(this);
|
||||
_instrumentSystem = EntitySystem.Get<InstrumentSystem>();
|
||||
}
|
||||
|
||||
protected virtual void SetupRenderer(bool fromStateChange = false)
|
||||
{
|
||||
if (IsRendererAlive) return;
|
||||
|
||||
_sequenceDelay = 0;
|
||||
_sequenceStartTick = 0;
|
||||
_midiManager.OcclusionCollisionMask = (int) CollisionGroup.Impassable;
|
||||
_renderer = _midiManager.GetNewRenderer();
|
||||
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.MidiBank = _instrumentBank;
|
||||
_renderer.MidiProgram = _instrumentProgram;
|
||||
_renderer.TrackingEntity = Owner;
|
||||
_renderer.DisablePercussionChannel = !_allowPercussion;
|
||||
_renderer.DisableProgramChangeEvent = !_allowProgramChange;
|
||||
_renderer.OnMidiPlayerFinished += () =>
|
||||
{
|
||||
OnMidiPlaybackEnded?.Invoke();
|
||||
EndRenderer(fromStateChange);
|
||||
};
|
||||
}
|
||||
|
||||
if (!fromStateChange)
|
||||
{
|
||||
SendNetworkMessage(new InstrumentStartMidiMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected void EndRenderer(bool fromStateChange = false)
|
||||
{
|
||||
if (IsInputOpen)
|
||||
{
|
||||
CloseInput(fromStateChange);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsMidiOpen)
|
||||
{
|
||||
CloseMidi(fromStateChange);
|
||||
return;
|
||||
}
|
||||
|
||||
_renderer?.StopAllNotes();
|
||||
|
||||
var renderer = _renderer;
|
||||
|
||||
// We dispose of the synth two seconds from now to allow the last notes to stop from playing.
|
||||
Owner.SpawnTimer(2000, () => { renderer?.Dispose(); });
|
||||
_renderer = null;
|
||||
_midiEventBuffer.Clear();
|
||||
|
||||
if (!fromStateChange)
|
||||
{
|
||||
SendNetworkMessage(new InstrumentStopMidiMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
EndRenderer();
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, channel, session);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case InstrumentMidiEventMessage midiEventMessage:
|
||||
if (IsRendererAlive)
|
||||
{
|
||||
// If we're the ones sending the MidiEvents, we ignore this message.
|
||||
if (IsInputOpen || IsMidiOpen) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we haven't started or finished some sequence
|
||||
if (_sequenceStartTick == 0)
|
||||
{
|
||||
// we may have arrived late
|
||||
SetupRenderer(true);
|
||||
}
|
||||
|
||||
// might be our own notes after we already finished playing
|
||||
return;
|
||||
}
|
||||
|
||||
if (_sequenceStartTick <= 0)
|
||||
{
|
||||
_sequenceStartTick = midiEventMessage.MidiEvent
|
||||
.Min(x => x.Tick) - 1;
|
||||
}
|
||||
|
||||
var sqrtLag = MathF.Sqrt(_netManager.ServerChannel!.Ping / 1000f);
|
||||
var delay = (uint) (_renderer!.SequencerTimeScale * (.2 + sqrtLag));
|
||||
var delta = delay - _sequenceStartTick;
|
||||
|
||||
_sequenceDelay = Math.Max(_sequenceDelay, delta);
|
||||
|
||||
var currentTick = _renderer.SequencerTick;
|
||||
|
||||
// ReSharper disable once ForCanBeConvertedToForeach
|
||||
for (var i = 0; i < midiEventMessage.MidiEvent.Length; i++)
|
||||
{
|
||||
var ev = midiEventMessage.MidiEvent[i];
|
||||
var scheduled = ev.Tick + _sequenceDelay;
|
||||
|
||||
if (scheduled <= currentTick)
|
||||
{
|
||||
_sequenceDelay += currentTick - ev.Tick;
|
||||
scheduled = ev.Tick + _sequenceDelay;
|
||||
}
|
||||
|
||||
|
||||
_renderer?.ScheduleMidiEvent(ev, scheduled, true);
|
||||
}
|
||||
|
||||
break;
|
||||
case InstrumentStartMidiMessage _:
|
||||
{
|
||||
SetupRenderer(true);
|
||||
break;
|
||||
}
|
||||
case InstrumentStopMidiMessage _:
|
||||
{
|
||||
EndRenderer(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
if (curState is not InstrumentState state) return;
|
||||
|
||||
if (state.Playing)
|
||||
{
|
||||
SetupRenderer(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EndRenderer(true);
|
||||
}
|
||||
|
||||
AllowPercussion = state.AllowPercussion;
|
||||
AllowProgramChange = state.AllowProgramChange;
|
||||
InstrumentBank = state.InstrumentBank;
|
||||
InstrumentProgram = state.InstrumentProgram;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MidiRenderer.OpenInput"/>
|
||||
public bool OpenInput()
|
||||
{
|
||||
SetupRenderer();
|
||||
|
||||
if (_renderer != null && _renderer.OpenInput())
|
||||
{
|
||||
_renderer.OnMidiEvent += RendererOnMidiEvent;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MidiRenderer.CloseInput"/>
|
||||
public bool CloseInput(bool fromStateChange = false)
|
||||
{
|
||||
if (_renderer == null || !_renderer.CloseInput())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
EndRenderer(fromStateChange);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OpenMidi(ReadOnlySpan<byte> data)
|
||||
{
|
||||
SetupRenderer();
|
||||
|
||||
if (_renderer == null || !_renderer.OpenMidi(data))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_renderer.OnMidiEvent += RendererOnMidiEvent;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MidiRenderer.CloseMidi"/>
|
||||
public bool CloseMidi(bool fromStateChange = false)
|
||||
{
|
||||
if (_renderer == null || !_renderer.CloseMidi())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
EndRenderer(fromStateChange);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the renderer receives a midi event.
|
||||
/// </summary>
|
||||
/// <param name="midiEvent">The received midi event</param>
|
||||
private void RendererOnMidiEvent(MidiEvent midiEvent)
|
||||
{
|
||||
_midiEventBuffer.Add(midiEvent);
|
||||
}
|
||||
|
||||
private TimeSpan _lastMeasured = TimeSpan.MinValue;
|
||||
|
||||
private int _sentWithinASec;
|
||||
|
||||
private static readonly TimeSpan OneSecAgo = TimeSpan.FromSeconds(-1);
|
||||
|
||||
private static readonly Comparer<MidiEvent> SortMidiEventTick
|
||||
= Comparer<MidiEvent>.Create((x, y)
|
||||
=> x.Tick.CompareTo(y.Tick));
|
||||
|
||||
public override void Update(float delta)
|
||||
{
|
||||
if (!IsMidiOpen && !IsInputOpen) return;
|
||||
|
||||
var now = _gameTiming.RealTime;
|
||||
var oneSecAGo = now.Add(OneSecAgo);
|
||||
|
||||
if (_lastMeasured <= oneSecAGo)
|
||||
{
|
||||
_lastMeasured = now;
|
||||
_sentWithinASec = 0;
|
||||
}
|
||||
|
||||
if (_midiEventBuffer.Count == 0) return;
|
||||
|
||||
var max = _respectMidiLimits ?
|
||||
Math.Min(_instrumentSystem.MaxMidiEventsPerBatch, _instrumentSystem.MaxMidiEventsPerSecond - _sentWithinASec)
|
||||
: _midiEventBuffer.Count;
|
||||
|
||||
if (max <= 0)
|
||||
{
|
||||
// hit event/sec limit, have to lag the batch or drop events
|
||||
return;
|
||||
}
|
||||
|
||||
// fix cross-fade events generating retroactive events
|
||||
// also handle any significant backlog of events after midi finished
|
||||
|
||||
_midiEventBuffer.Sort(SortMidiEventTick);
|
||||
var bufferTicks = IsRendererAlive && _renderer!.Status != MidiRendererStatus.None
|
||||
? _renderer.SequencerTimeScale * .2f
|
||||
: 0;
|
||||
var bufferedTick = IsRendererAlive
|
||||
? _renderer!.SequencerTick - bufferTicks
|
||||
: int.MaxValue;
|
||||
|
||||
var events = _midiEventBuffer
|
||||
.TakeWhile(x => x.Tick < bufferedTick)
|
||||
.Take(max)
|
||||
.ToArray();
|
||||
|
||||
var eventCount = events.Length;
|
||||
|
||||
if (eventCount == 0) return;
|
||||
|
||||
SendNetworkMessage(new InstrumentMidiEventMessage(events));
|
||||
|
||||
_sentWithinASec += eventCount;
|
||||
|
||||
_midiEventBuffer.RemoveRange(0, eventCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Interactable
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that represents a handheld expendable light which can be activated and eventually dies over time.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class ExpendableLightComponent : SharedExpendableLightComponent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Interactable;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Interactable
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MultiToolComponent : Component, IItemStatus
|
||||
{
|
||||
private ToolQuality _behavior;
|
||||
[DataField("statusShowBehavior")]
|
||||
private bool _statusShowBehavior = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
|
||||
[ViewVariables] public bool StatusShowBehavior => _statusShowBehavior;
|
||||
[ViewVariables] public ToolQuality? Behavior => _behavior;
|
||||
|
||||
public override string Name => "MultiTool";
|
||||
public override uint? NetID => ContentNetIDs.MULTITOOLS;
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not MultiToolComponentState tool) return;
|
||||
|
||||
_behavior = tool.Quality;
|
||||
_uiUpdateNeeded = true;
|
||||
|
||||
}
|
||||
|
||||
public Control MakeControl() => new StatusControl(this);
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly MultiToolComponent _parent;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
public StatusControl(MultiToolComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}};
|
||||
AddChild(_label);
|
||||
|
||||
parent._uiUpdateNeeded = true;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_parent._uiUpdateNeeded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_parent._uiUpdateNeeded = false;
|
||||
|
||||
_label.SetMarkup(_parent.StatusShowBehavior ? _parent.Behavior.ToString() ?? string.Empty : string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
using System;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Interactable;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Interactable
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class WelderComponent : SharedToolComponent, IItemStatus
|
||||
{
|
||||
public override string Name => "Welder";
|
||||
public override uint? NetID => ContentNetIDs.WELDER;
|
||||
|
||||
private ToolQuality _behavior;
|
||||
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
|
||||
[ViewVariables] public float FuelCapacity { get; private set; }
|
||||
[ViewVariables] public float Fuel { get; private set; }
|
||||
[ViewVariables] public bool Activated { get; private set; }
|
||||
[ViewVariables] public override ToolQuality Qualities => _behavior;
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not WelderComponentState weld)
|
||||
return;
|
||||
|
||||
FuelCapacity = weld.FuelCapacity;
|
||||
Fuel = weld.Fuel;
|
||||
Activated = weld.Activated;
|
||||
_behavior = weld.Quality;
|
||||
_uiUpdateNeeded = true;
|
||||
}
|
||||
|
||||
public Control MakeControl() => new StatusControl(this);
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly WelderComponent _parent;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
public StatusControl(WelderComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}};
|
||||
AddChild(_label);
|
||||
|
||||
parent._uiUpdateNeeded = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_parent._uiUpdateNeeded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_parent._uiUpdateNeeded = false;
|
||||
|
||||
var fuelCap = _parent.FuelCapacity;
|
||||
var fuel = _parent.Fuel;
|
||||
|
||||
_label.SetMarkup(Loc.GetString("Fuel: [color={0}]{1}/{2}[/color]",
|
||||
fuel < fuelCap / 4f ? "darkorange" : "orange", Math.Round(fuel), fuelCap));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class InteractionOutlineComponent : Component
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private const float DefaultWidth = 1;
|
||||
private const string ShaderInRange = "SelectionOutlineInrange";
|
||||
private const string ShaderOutOfRange = "SelectionOutline";
|
||||
|
||||
public override string Name => "InteractionOutline";
|
||||
|
||||
private bool _inRange;
|
||||
private ShaderInstance? _shader;
|
||||
private int _lastRenderScale;
|
||||
|
||||
public void OnMouseEnter(bool inInteractionRange, int renderScale)
|
||||
{
|
||||
_lastRenderScale = renderScale;
|
||||
_inRange = inInteractionRange;
|
||||
if (Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
sprite.PostShader = MakeNewShader(inInteractionRange, renderScale);
|
||||
sprite.RenderOrder = Owner.EntityManager.CurrentTick.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMouseLeave()
|
||||
{
|
||||
if (Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
sprite.PostShader = null;
|
||||
sprite.RenderOrder = 0;
|
||||
}
|
||||
|
||||
_shader?.Dispose();
|
||||
_shader = null;
|
||||
}
|
||||
|
||||
public void UpdateInRange(bool inInteractionRange, int renderScale)
|
||||
{
|
||||
if (Owner.TryGetComponent(out ISpriteComponent? sprite)
|
||||
&& (inInteractionRange != _inRange || _lastRenderScale != renderScale))
|
||||
{
|
||||
_inRange = inInteractionRange;
|
||||
_lastRenderScale = renderScale;
|
||||
_shader = MakeNewShader(_inRange, _lastRenderScale);
|
||||
sprite.PostShader = _shader;
|
||||
}
|
||||
}
|
||||
|
||||
private ShaderInstance MakeNewShader(bool inRange, int renderScale)
|
||||
{
|
||||
var shaderName = inRange ? ShaderInRange : ShaderOutOfRange;
|
||||
|
||||
var instance = _prototypeManager.Index<ShaderPrototype>(shaderName).InstanceUnique();
|
||||
instance.SetParameter("outline_width", DefaultWidth * renderScale);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ItemCabinetVisualizer : AppearanceVisualizer
|
||||
{
|
||||
// TODO proper layering
|
||||
[DataField("fullState", required: true)]
|
||||
private string _fullState = default!;
|
||||
|
||||
[DataField("emptyState", required: true)]
|
||||
private string _emptyState = default!;
|
||||
|
||||
[DataField("closedState", required: true)]
|
||||
private string _closedState = default!;
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.Owner.TryGetComponent<SpriteComponent>(out var sprite)
|
||||
&& component.TryGetData(ItemCabinetVisuals.IsOpen, out bool isOpen))
|
||||
{
|
||||
if (isOpen)
|
||||
{
|
||||
if (component.TryGetData(ItemCabinetVisuals.ContainsItem, out bool contains))
|
||||
{
|
||||
if (contains)
|
||||
{
|
||||
sprite.LayerSetState(0, _fullState);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(0, _emptyState);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(0, _closedState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Client.Animations;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.Components.Storage;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Items
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(ISharedHandsComponent))]
|
||||
[ComponentReference(typeof(SharedHandsComponent))]
|
||||
public class HandsComponent : SharedHandsComponent
|
||||
{
|
||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||
|
||||
private HandsGui? _gui;
|
||||
|
||||
private readonly List<Hand> _hands = new();
|
||||
|
||||
[ViewVariables] public IReadOnlyList<Hand> Hands => _hands;
|
||||
|
||||
[ViewVariables] public string? ActiveIndex { get; private set; }
|
||||
|
||||
[ViewVariables] private ISpriteComponent? _sprite;
|
||||
|
||||
[ViewVariables] public IEntity? ActiveHand => GetEntity(ActiveIndex);
|
||||
|
||||
public override bool IsHolding(IEntity entity)
|
||||
{
|
||||
foreach (var hand in _hands)
|
||||
{
|
||||
if (hand.Entity == entity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddHand(Hand hand)
|
||||
{
|
||||
_sprite?.LayerMapReserveBlank($"hand-{hand.Name}");
|
||||
_hands.Insert(hand.Index, hand);
|
||||
}
|
||||
|
||||
public Hand? GetHand(string? name)
|
||||
{
|
||||
return Hands.FirstOrDefault(hand => hand.Name == name);
|
||||
}
|
||||
|
||||
private bool TryHand(string name, [NotNullWhen(true)] out Hand? hand)
|
||||
{
|
||||
return (hand = GetHand(name)) != null;
|
||||
}
|
||||
|
||||
public IEntity? GetEntity(string? handName)
|
||||
{
|
||||
if (handName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return GetHand(handName)?.Entity;
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
_gui?.Dispose();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (Owner.TryGetComponent(out _sprite))
|
||||
{
|
||||
foreach (var hand in _hands)
|
||||
{
|
||||
_sprite.LayerMapReserveBlank($"hand-{hand.Name}");
|
||||
UpdateHandSprites(hand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var cast = (HandsComponentState) curState;
|
||||
foreach (var sharedHand in cast.Hands)
|
||||
{
|
||||
if (!TryHand(sharedHand.Name, out var hand))
|
||||
{
|
||||
hand = new Hand(this, sharedHand, Owner.EntityManager);
|
||||
AddHand(hand);
|
||||
}
|
||||
else
|
||||
{
|
||||
hand.Location = sharedHand.Location;
|
||||
|
||||
hand.Entity = sharedHand.EntityUid.HasValue
|
||||
? Owner.EntityManager.GetEntity(sharedHand.EntityUid.Value)
|
||||
: null;
|
||||
}
|
||||
|
||||
hand.Enabled = sharedHand.Enabled;
|
||||
|
||||
UpdateHandSprites(hand);
|
||||
}
|
||||
|
||||
foreach (var currentHand in _hands.ToList())
|
||||
{
|
||||
if (cast.Hands.All(newHand => newHand.Name != currentHand.Name))
|
||||
{
|
||||
_hands.Remove(currentHand);
|
||||
_gui?.RemoveHand(currentHand);
|
||||
HideHand(currentHand);
|
||||
}
|
||||
}
|
||||
|
||||
ActiveIndex = cast.ActiveIndex;
|
||||
|
||||
_gui?.UpdateHandIcons();
|
||||
RefreshInHands();
|
||||
}
|
||||
|
||||
private void HideHand(Hand hand)
|
||||
{
|
||||
_sprite?.LayerSetVisible($"hand-{hand.Name}", false);
|
||||
}
|
||||
|
||||
private void UpdateHandSprites(Hand hand)
|
||||
{
|
||||
if (_sprite == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = hand.Entity;
|
||||
var name = hand.Name;
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
if (_sprite.LayerMapTryGet($"hand-{name}", out var layer))
|
||||
{
|
||||
_sprite.LayerSetVisible(layer, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity.TryGetComponent(out SharedItemComponent? item))
|
||||
return;
|
||||
|
||||
if (item.RsiPath == null)
|
||||
{
|
||||
_sprite.LayerSetVisible($"hand-{name}", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var rsi = IoCManager.Resolve<IResourceCache>().GetResource<RSIResource>(SharedSpriteComponent.TextureRoot / item.RsiPath).RSI;
|
||||
|
||||
var handName = hand.Location.ToString().ToLowerInvariant();
|
||||
var prefix = item.EquippedPrefix;
|
||||
var state = prefix != null ? $"{prefix}-inhand-{handName}" : $"inhand-{handName}";
|
||||
|
||||
if (!rsi.TryGetState(state, out _))
|
||||
return;
|
||||
|
||||
var color = item.Color;
|
||||
|
||||
_sprite.LayerSetColor($"hand-{name}", color);
|
||||
_sprite.LayerSetVisible($"hand-{name}", true);
|
||||
_sprite.LayerSetState($"hand-{name}", state, rsi);
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshInHands()
|
||||
{
|
||||
if (!Initialized) return;
|
||||
|
||||
foreach (var hand in _hands)
|
||||
{
|
||||
UpdateHandSprites(hand);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
ActiveIndex = _hands.LastOrDefault()?.Name;
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PlayerAttachedMsg _:
|
||||
if (_gui == null)
|
||||
{
|
||||
_gui = new HandsGui();
|
||||
}
|
||||
else
|
||||
{
|
||||
_gui.Parent?.RemoveChild(_gui);
|
||||
}
|
||||
|
||||
_gameHud.HandsContainer.AddChild(_gui);
|
||||
_gui.UpdateHandIcons();
|
||||
break;
|
||||
case PlayerDetachedMsg _:
|
||||
_gui?.Parent?.RemoveChild(_gui);
|
||||
break;
|
||||
case HandEnabledMsg msg:
|
||||
{
|
||||
var hand = GetHand(msg.Name);
|
||||
|
||||
if (hand?.Button == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hand.Button.Blocked.Visible = false;
|
||||
|
||||
break;
|
||||
}
|
||||
case HandDisabledMsg msg:
|
||||
{
|
||||
var hand = GetHand(msg.Name);
|
||||
|
||||
if (hand?.Button == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hand.Button.Blocked.Visible = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, netChannel, session);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case AnimatePickupEntityMessage msg:
|
||||
{
|
||||
if (Owner.EntityManager.TryGetEntity(msg.EntityId, out var entity))
|
||||
{
|
||||
ReusableAnimations.AnimateEntityPickup(entity, msg.EntityPosition, Owner.Transform.WorldPosition);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SendChangeHand(string index)
|
||||
{
|
||||
SendNetworkMessage(new ClientChangedHandMsg(index));
|
||||
}
|
||||
|
||||
public void AttackByInHand(string index)
|
||||
{
|
||||
SendNetworkMessage(new ClientAttackByInHandMsg(index));
|
||||
}
|
||||
|
||||
public void UseActiveHand()
|
||||
{
|
||||
if (GetEntity(ActiveIndex) != null)
|
||||
{
|
||||
SendNetworkMessage(new UseInHandMsg());
|
||||
}
|
||||
}
|
||||
|
||||
public void ActivateItemInHand(string handIndex)
|
||||
{
|
||||
if (GetEntity(handIndex) == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendNetworkMessage(new ActivateInHandMsg(handIndex));
|
||||
}
|
||||
}
|
||||
|
||||
public class Hand
|
||||
{
|
||||
private bool _enabled = true;
|
||||
|
||||
public Hand(HandsComponent parent, SharedHand hand, IEntityManager manager, HandButton? button = null)
|
||||
{
|
||||
Parent = parent;
|
||||
Index = hand.Index;
|
||||
Name = hand.Name;
|
||||
Location = hand.Location;
|
||||
Button = button;
|
||||
|
||||
if (!hand.EntityUid.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
manager.TryGetEntity(hand.EntityUid.Value, out var entity);
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
private HandsComponent Parent { get; }
|
||||
public int Index { get; }
|
||||
public string Name { get; }
|
||||
public HandLocation Location { get; set; }
|
||||
public IEntity? Entity { get; set; }
|
||||
public HandButton? Button { get; set; }
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
if (_enabled == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_enabled = value;
|
||||
Parent.Dirty();
|
||||
|
||||
var message = value
|
||||
? (ComponentMessage) new HandEnabledMsg(Name)
|
||||
: new HandDisabledMsg(Name);
|
||||
|
||||
Parent.HandleMessage(message, Parent);
|
||||
Parent.Owner.SendMessage(Parent, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#nullable enable
|
||||
using Content.Shared.GameObjects.Components.Storage;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Items
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedItemComponent))]
|
||||
public class ItemComponent : SharedItemComponent
|
||||
{
|
||||
public override bool TryPutInHand(IEntity user)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnEquippedPrefixChange()
|
||||
{
|
||||
if (!Owner.TryGetContainer(out var container))
|
||||
return;
|
||||
|
||||
if (container.Owner.TryGetComponent(out HandsComponent? hands))
|
||||
hands.RefreshInHands();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Items
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ItemStatusComponent : Component
|
||||
{
|
||||
public override string Name => "ItemStatus";
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#nullable enable
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.Kitchen;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Kitchen
|
||||
{
|
||||
[RegisterComponent]
|
||||
internal sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent
|
||||
{
|
||||
public override bool DragDropOn(DragDropEvent eventArgs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Kitchen;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Kitchen
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class MicrowaveBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private MicrowaveMenu? _menu;
|
||||
|
||||
private readonly Dictionary<int, EntityUid> _solids = new();
|
||||
private readonly Dictionary<int, Solution.ReagentQuantity> _reagents =new();
|
||||
|
||||
public MicrowaveBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner,uiKey)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_menu = new MicrowaveMenu(this);
|
||||
_menu.OpenCentered();
|
||||
_menu.OnClose += Close;
|
||||
_menu.StartButton.OnPressed += _ => SendMessage(new SharedMicrowaveComponent.MicrowaveStartCookMessage());
|
||||
_menu.EjectButton.OnPressed += _ => SendMessage(new SharedMicrowaveComponent.MicrowaveEjectMessage());
|
||||
_menu.IngredientsList.OnItemSelected += args =>
|
||||
{
|
||||
SendMessage(new SharedMicrowaveComponent.MicrowaveEjectSolidIndexedMessage(_solids[args.ItemIndex]));
|
||||
|
||||
};
|
||||
|
||||
_menu.IngredientsListReagents.OnItemSelected += args =>
|
||||
{
|
||||
SendMessage(
|
||||
new SharedMicrowaveComponent.MicrowaveVaporizeReagentIndexedMessage(_reagents[args.ItemIndex]));
|
||||
};
|
||||
|
||||
_menu.OnCookTimeSelected += (args,buttonIndex) =>
|
||||
{
|
||||
var actualButton = (MicrowaveMenu.MicrowaveCookTimeButton) args.Button ;
|
||||
SendMessage(new SharedMicrowaveComponent.MicrowaveSelectCookTimeMessage(buttonIndex,actualButton.CookTime));
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (!disposing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_solids.Clear();
|
||||
_menu?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (state is not MicrowaveUpdateUserInterfaceState cState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_menu?.ToggleBusyDisableOverlayPanel(cState.IsMicrowaveBusy);
|
||||
RefreshContentsDisplay(cState.ReagentQuantities, cState.ContainedSolids);
|
||||
|
||||
if (_menu != null)
|
||||
{
|
||||
var currentlySelectedTimeButton = (Button) _menu.CookTimeButtonVbox.GetChild(cState.ActiveButtonIndex);
|
||||
currentlySelectedTimeButton.Pressed = true;
|
||||
var label = cState.ActiveButtonIndex <= 0 ? Loc.GetString("INSTANT") : cState.CurrentCookTime.ToString();
|
||||
_menu.CookTimeInfoLabel.Text = $"{Loc.GetString("COOK TIME")}: {label}";
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshContentsDisplay(Solution.ReagentQuantity[] reagents, EntityUid[] containedSolids)
|
||||
{
|
||||
_reagents.Clear();
|
||||
|
||||
if (_menu == null) return;
|
||||
|
||||
_menu.IngredientsListReagents.Clear();
|
||||
for (var i = 0; i < reagents.Length; i++)
|
||||
{
|
||||
if (_prototypeManager.TryIndex(reagents[i].ReagentId, out ReagentPrototype? proto))
|
||||
{
|
||||
var reagentAdded = _menu.IngredientsListReagents.AddItem($"{reagents[i].Quantity} {proto.Name}");
|
||||
var reagentIndex = _menu.IngredientsListReagents.IndexOf(reagentAdded);
|
||||
_reagents.Add(reagentIndex, reagents[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_solids.Clear();
|
||||
_menu.IngredientsList.Clear();
|
||||
for (var j = 0; j < containedSolids.Length; j++)
|
||||
{
|
||||
if (!_entityManager.TryGetEntity(containedSolids[j], out var entity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity.Deleted)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Texture? texture;
|
||||
if (entity.TryGetComponent(out IconComponent? iconComponent))
|
||||
{
|
||||
texture = iconComponent.Icon?.Default;
|
||||
}
|
||||
else if (entity.TryGetComponent(out SpriteComponent? spriteComponent))
|
||||
{
|
||||
texture = spriteComponent.Icon?.Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var solidItem = _menu.IngredientsList.AddItem(entity.Name, texture);
|
||||
var solidIndex = _menu.IngredientsList.IndexOf(solidItem);
|
||||
_solids.Add(solidIndex, containedSolids[j]);
|
||||
}
|
||||
}
|
||||
|
||||
public class MicrowaveMenu : SS14Window
|
||||
{
|
||||
public class MicrowaveCookTimeButton : Button
|
||||
{
|
||||
public uint CookTime;
|
||||
}
|
||||
|
||||
|
||||
private MicrowaveBoundUserInterface Owner { get; set; }
|
||||
|
||||
public event Action<ButtonEventArgs, int>? OnCookTimeSelected;
|
||||
|
||||
public Button StartButton { get; }
|
||||
public Button EjectButton { get; }
|
||||
|
||||
public PanelContainer TimerFacePlate { get; }
|
||||
|
||||
public ButtonGroup CookTimeButtonGroup { get; }
|
||||
|
||||
public VBoxContainer CookTimeButtonVbox { get; }
|
||||
|
||||
private VBoxContainer ButtonGridContainer { get; }
|
||||
|
||||
private PanelContainer DisableCookingPanelOverlay { get; }
|
||||
|
||||
public ItemList IngredientsList { get; }
|
||||
|
||||
public ItemList IngredientsListReagents { get; }
|
||||
public Label CookTimeInfoLabel { get; }
|
||||
|
||||
public MicrowaveMenu(MicrowaveBoundUserInterface owner)
|
||||
{
|
||||
SetSize = MinSize = (512, 256);
|
||||
|
||||
Owner = owner;
|
||||
Title = Loc.GetString("Microwave");
|
||||
DisableCookingPanelOverlay = new PanelContainer
|
||||
{
|
||||
MouseFilter = MouseFilterMode.Stop,
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.Black.WithAlpha(0.60f)},
|
||||
};
|
||||
|
||||
var hSplit = new HBoxContainer();
|
||||
|
||||
IngredientsListReagents = new ItemList
|
||||
{
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
SelectMode = ItemList.ItemListSelectMode.Button,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
MinSize = (100, 128)
|
||||
};
|
||||
|
||||
IngredientsList = new ItemList
|
||||
{
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
SelectMode = ItemList.ItemListSelectMode.Button,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
MinSize = (100, 128)
|
||||
};
|
||||
|
||||
hSplit.AddChild(IngredientsListReagents);
|
||||
//Padding between the lists.
|
||||
hSplit.AddChild(new Control
|
||||
{
|
||||
MinSize = (0, 5),
|
||||
});
|
||||
|
||||
hSplit.AddChild(IngredientsList);
|
||||
|
||||
var vSplit = new VBoxContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
|
||||
hSplit.AddChild(vSplit);
|
||||
|
||||
ButtonGridContainer = new VBoxContainer
|
||||
{
|
||||
Align = BoxContainer.AlignMode.Center,
|
||||
SizeFlagsStretchRatio = 3
|
||||
};
|
||||
|
||||
StartButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("Start"),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
};
|
||||
|
||||
EjectButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("Eject All Contents"),
|
||||
ToolTip = Loc.GetString("This vaporizes all reagents, but ejects any solids."),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
};
|
||||
|
||||
ButtonGridContainer.AddChild(StartButton);
|
||||
ButtonGridContainer.AddChild(EjectButton);
|
||||
vSplit.AddChild(ButtonGridContainer);
|
||||
|
||||
//Padding
|
||||
vSplit.AddChild(new Control
|
||||
{
|
||||
MinSize = (0, 15),
|
||||
});
|
||||
|
||||
CookTimeButtonGroup = new ButtonGroup();
|
||||
CookTimeButtonVbox = new VBoxContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
Align = BoxContainer.AlignMode.Center,
|
||||
};
|
||||
|
||||
var index = 0;
|
||||
for (var i = 0; i <= 6; i++)
|
||||
{
|
||||
var newButton = new MicrowaveCookTimeButton
|
||||
{
|
||||
Text = index <= 0 ? Loc.GetString("INSTANT") : index.ToString(),
|
||||
CookTime = (uint)index,
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
ToggleMode = true,
|
||||
Group = CookTimeButtonGroup,
|
||||
};
|
||||
CookTimeButtonVbox.AddChild(newButton);
|
||||
newButton.OnToggled += args =>
|
||||
{
|
||||
OnCookTimeSelected?.Invoke(args, newButton.GetPositionInParent());
|
||||
|
||||
};
|
||||
index += 5;
|
||||
}
|
||||
|
||||
var cookTimeOneSecondButton = (Button) CookTimeButtonVbox.GetChild(0);
|
||||
cookTimeOneSecondButton.Pressed = true;
|
||||
|
||||
CookTimeInfoLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString("COOK TIME: 1"),
|
||||
Align = Label.AlignMode.Center,
|
||||
Modulate = Color.White,
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
|
||||
var innerTimerPanel = new PanelContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
ModulateSelfOverride = Color.Red,
|
||||
MinSize = (100, 128),
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.Black.WithAlpha(0.5f)},
|
||||
|
||||
Children =
|
||||
{
|
||||
new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat() {BackgroundColor = Color.Gray.WithAlpha(0.2f)},
|
||||
|
||||
Children =
|
||||
{
|
||||
CookTimeInfoLabel
|
||||
}
|
||||
},
|
||||
|
||||
new ScrollContainer()
|
||||
{
|
||||
VerticalExpand = true,
|
||||
|
||||
Children =
|
||||
{
|
||||
CookTimeButtonVbox,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TimerFacePlate = new PanelContainer()
|
||||
{
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
innerTimerPanel
|
||||
},
|
||||
};
|
||||
|
||||
vSplit.AddChild(TimerFacePlate);
|
||||
Contents.AddChild(hSplit);
|
||||
Contents.AddChild(DisableCookingPanelOverlay);
|
||||
}
|
||||
|
||||
public void ToggleBusyDisableOverlayPanel(bool shouldDisable)
|
||||
{
|
||||
DisableCookingPanelOverlay.Visible = shouldDisable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using Content.Client.GameObjects.Components.Sound;
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using Content.Shared.GameObjects.Components.Sound;
|
||||
using Content.Shared.Kitchen;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Kitchen
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class MicrowaveVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
var loopingSoundComponent = component.Owner.GetComponentOrNull<LoopingSoundComponent>();
|
||||
|
||||
if (!component.TryGetData(PowerDeviceVisuals.VisualState, out MicrowaveVisualState state))
|
||||
{
|
||||
state = MicrowaveVisualState.Idle;
|
||||
}
|
||||
switch (state)
|
||||
{
|
||||
case MicrowaveVisualState.Idle:
|
||||
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_unlit");
|
||||
loopingSoundComponent?.StopAllSounds();
|
||||
break;
|
||||
|
||||
case MicrowaveVisualState.Cooking:
|
||||
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_running_unlit");
|
||||
var audioParams = AudioParams.Default;
|
||||
audioParams.Loop = true;
|
||||
var scheduledSound = new ScheduledSound();
|
||||
scheduledSound.Filename = "/Audio/Machines/microwave_loop.ogg";
|
||||
scheduledSound.AudioParams = audioParams;
|
||||
loopingSoundComponent?.StopAllSounds();
|
||||
loopingSoundComponent?.AddScheduledSound(scheduledSound);
|
||||
break;
|
||||
|
||||
default:
|
||||
Logger.Debug($"Something terrible happened in {this}");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
var glowingPartsVisible = !(component.TryGetData(PowerDeviceVisuals.Powered, out bool powered) && !powered);
|
||||
sprite.LayerSetVisible(MicrowaveVisualizerLayers.BaseUnlit, glowingPartsVisible);
|
||||
}
|
||||
|
||||
private enum MicrowaveVisualizerLayers : byte
|
||||
{
|
||||
Base,
|
||||
BaseUnlit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,306 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Kitchen;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.Chemistry.Solution;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Kitchen
|
||||
{
|
||||
public class ReagentGrinderBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private GrinderMenu? _menu;
|
||||
private readonly Dictionary<int, EntityUid> _chamberVisualContents = new();
|
||||
private readonly Dictionary<int, ReagentQuantity> _beakerVisualContents = new();
|
||||
|
||||
public ReagentGrinderBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) { }
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = new GrinderMenu(this);
|
||||
_menu.OpenCentered();
|
||||
_menu.OnClose += Close;
|
||||
_menu.GrindButton.OnPressed += args => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage());
|
||||
_menu.JuiceButton.OnPressed += args => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderJuiceStartMessage());
|
||||
_menu.ChamberContentBox.EjectButton.OnPressed += args => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage());
|
||||
_menu.BeakerContentBox.EjectButton.OnPressed += args => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectBeakerMessage());
|
||||
_menu.ChamberContentBox.BoxContents.OnItemSelected += args =>
|
||||
{
|
||||
SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberContentMessage(_chamberVisualContents[args.ItemIndex]));
|
||||
};
|
||||
|
||||
_menu.BeakerContentBox.BoxContents.OnItemSelected += args =>
|
||||
{
|
||||
SendMessage(new SharedReagentGrinderComponent.ReagentGrinderVaporizeReagentIndexedMessage(_beakerVisualContents[args.ItemIndex]));
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_chamberVisualContents?.Clear();
|
||||
_beakerVisualContents?.Clear();
|
||||
_menu?.Dispose();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (!(state is ReagentGrinderInterfaceState cState))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_menu.BeakerContentBox.EjectButton.Disabled = !cState.HasBeakerIn;
|
||||
_menu.ChamberContentBox.EjectButton.Disabled = cState.ChamberContents.Length <= 0;
|
||||
_menu.GrindButton.Disabled = !cState.CanGrind || !cState.Powered;
|
||||
_menu.JuiceButton.Disabled = !cState.CanJuice || !cState.Powered;
|
||||
RefreshContentsDisplay(cState.ReagentQuantities, cState.ChamberContents, cState.HasBeakerIn);
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
base.ReceiveMessage(message);
|
||||
|
||||
if (_menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case SharedReagentGrinderComponent.ReagentGrinderWorkStartedMessage workStarted:
|
||||
_menu.GrindButton.Disabled = true;
|
||||
_menu.GrindButton.Modulate = workStarted.GrinderProgram == SharedReagentGrinderComponent.GrinderProgram.Grind ? Color.Green : Color.White;
|
||||
_menu.JuiceButton.Disabled = true;
|
||||
_menu.JuiceButton.Modulate = workStarted.GrinderProgram == SharedReagentGrinderComponent.GrinderProgram.Juice ? Color.Green : Color.White;
|
||||
_menu.BeakerContentBox.EjectButton.Disabled = true;
|
||||
_menu.ChamberContentBox.EjectButton.Disabled = true;
|
||||
break;
|
||||
case SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage doneMessage:
|
||||
_menu.GrindButton.Disabled = false;
|
||||
_menu.JuiceButton.Disabled = false;
|
||||
_menu.GrindButton.Modulate = Color.White;
|
||||
_menu.JuiceButton.Modulate = Color.White;
|
||||
_menu.BeakerContentBox.EjectButton.Disabled = false;
|
||||
_menu.ChamberContentBox.EjectButton.Disabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshContentsDisplay(IList<ReagentQuantity>? reagents, IReadOnlyList<EntityUid> containedSolids, bool isBeakerAttached)
|
||||
{
|
||||
//Refresh chamber contents
|
||||
_chamberVisualContents.Clear();
|
||||
|
||||
if (_menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_menu.ChamberContentBox.BoxContents.Clear();
|
||||
foreach (var uid in containedSolids)
|
||||
{
|
||||
if (!_entityManager.TryGetEntity(uid, out var entity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var texture = entity.GetComponent<SpriteComponent>().Icon?.Default;
|
||||
|
||||
var solidItem = _menu.ChamberContentBox.BoxContents.AddItem(entity.Name, texture);
|
||||
var solidIndex = _menu.ChamberContentBox.BoxContents.IndexOf(solidItem);
|
||||
_chamberVisualContents.Add(solidIndex, uid);
|
||||
}
|
||||
|
||||
//Refresh beaker contents
|
||||
_beakerVisualContents.Clear();
|
||||
_menu.BeakerContentBox.BoxContents.Clear();
|
||||
//if no beaker is attached use this guard to prevent hitting a null reference.
|
||||
if (!isBeakerAttached || reagents == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Looks like we have a beaker attached.
|
||||
if (reagents.Count <= 0)
|
||||
{
|
||||
_menu.BeakerContentBox.BoxContents.AddItem(Loc.GetString("Empty"));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < reagents.Count; i++)
|
||||
{
|
||||
var goodIndex = _prototypeManager.TryIndex(reagents[i].ReagentId, out ReagentPrototype? proto);
|
||||
var reagentName = goodIndex ? Loc.GetString($"{reagents[i].Quantity} {proto!.Name}") : Loc.GetString("???");
|
||||
var reagentAdded = _menu.BeakerContentBox.BoxContents.AddItem(reagentName);
|
||||
var reagentIndex = _menu.BeakerContentBox.BoxContents.IndexOf(reagentAdded);
|
||||
_beakerVisualContents.Add(reagentIndex, reagents[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GrinderMenu : SS14Window
|
||||
{
|
||||
/*The contents of the chamber and beaker will both be vertical scroll rectangles.
|
||||
* Will have a vsplit to split the g/j buttons from the contents menu.
|
||||
* |--------------------------------\
|
||||
* | | Chamber [E] Beaker [E] |
|
||||
* | [G] | | | | | |
|
||||
* | | | | | | |
|
||||
* | | | | | | |
|
||||
* | [J] | |-----| |-----| |
|
||||
* | | |
|
||||
* \---------------------------------/
|
||||
*
|
||||
*/
|
||||
|
||||
private ReagentGrinderBoundUserInterface Owner { get; set; }
|
||||
|
||||
//We'll need 4 buttons, grind, juice, eject beaker, eject the chamber contents.
|
||||
//The other 2 are referenced in the Open function.
|
||||
public Button GrindButton { get; }
|
||||
public Button JuiceButton { get; }
|
||||
|
||||
public LabelledContentBox ChamberContentBox { get; }
|
||||
public LabelledContentBox BeakerContentBox { get; }
|
||||
|
||||
public sealed class LabelledContentBox : VBoxContainer
|
||||
{
|
||||
public string? LabelText { get; set; }
|
||||
|
||||
public ItemList BoxContents { get; set; }
|
||||
|
||||
public Button EjectButton { get; set; }
|
||||
|
||||
private Label _label;
|
||||
|
||||
public LabelledContentBox(string labelText, string buttonText)
|
||||
{
|
||||
_label = new Label
|
||||
{
|
||||
Text = labelText,
|
||||
Align = Label.AlignMode.Center,
|
||||
};
|
||||
|
||||
EjectButton = new Button
|
||||
{
|
||||
Text = buttonText,
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
};
|
||||
|
||||
var vSplit = new HSplitContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
_label,
|
||||
EjectButton
|
||||
}
|
||||
};
|
||||
|
||||
AddChild(vSplit);
|
||||
BoxContents = new ItemList
|
||||
{
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
SelectMode = ItemList.ItemListSelectMode.Button,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
MinSize = (100, 128)
|
||||
};
|
||||
AddChild(BoxContents);
|
||||
}
|
||||
}
|
||||
|
||||
public GrinderMenu(ReagentGrinderBoundUserInterface owner)
|
||||
{
|
||||
SetSize = MinSize = (512, 256);
|
||||
Owner = owner;
|
||||
Title = Loc.GetString("All-In-One Grinder 3000");
|
||||
|
||||
var hSplit = new HBoxContainer();
|
||||
|
||||
var vBoxGrindJuiceButtonPanel = new VBoxContainer
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
|
||||
GrindButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("Grind"),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
MinSize = (64, 64)
|
||||
};
|
||||
|
||||
JuiceButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("Juice"),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
MinSize = (64, 64)
|
||||
};
|
||||
|
||||
vBoxGrindJuiceButtonPanel.AddChild(GrindButton);
|
||||
//inner button padding
|
||||
vBoxGrindJuiceButtonPanel.AddChild(new Control
|
||||
{
|
||||
MinSize = (0, 16),
|
||||
});
|
||||
vBoxGrindJuiceButtonPanel.AddChild(JuiceButton);
|
||||
|
||||
ChamberContentBox = new LabelledContentBox(Loc.GetString("Chamber"), Loc.GetString("Eject Contents"))
|
||||
{
|
||||
//Modulate = Color.Red,
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
|
||||
};
|
||||
|
||||
BeakerContentBox = new LabelledContentBox(Loc.GetString("Beaker"), Loc.GetString("Eject Beaker"))
|
||||
{
|
||||
//Modulate = Color.Blue,
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
};
|
||||
|
||||
hSplit.AddChild(vBoxGrindJuiceButtonPanel);
|
||||
|
||||
//Padding between the g/j buttons panel and the itemlist boxes panel.
|
||||
hSplit.AddChild(new Control
|
||||
{
|
||||
MinSize = (16, 0),
|
||||
});
|
||||
hSplit.AddChild(ChamberContentBox);
|
||||
|
||||
//Padding between the two itemlists.
|
||||
hSplit.AddChild(new Control
|
||||
{
|
||||
MinSize = (8, 0),
|
||||
});
|
||||
hSplit.AddChild(BeakerContentBox);
|
||||
Contents.AddChild(hSplit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using static Content.Shared.Kitchen.SharedReagentGrinderComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Kitchen
|
||||
{
|
||||
public class ReagentGrinderVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
component.TryGetData(ReagentGrinderVisualState.BeakerAttached, out bool hasBeaker);
|
||||
sprite.LayerSetState(0, $"juicer{(hasBeaker ? "1" : "0")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class LanternVisualizer : AppearanceVisualizer
|
||||
{
|
||||
private readonly Animation _radiatingLightAnimation = new()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(5),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(PointLightComponent),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
Property = nameof(PointLightComponent.Radius),
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(3.0f, 0),
|
||||
new AnimationTrackProperty.KeyFrame(2.0f, 1.5f),
|
||||
new AnimationTrackProperty.KeyFrame(3.0f, 3f)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
if (component.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlayAnimation(component);
|
||||
}
|
||||
|
||||
private void PlayAnimation(AppearanceComponent component)
|
||||
{
|
||||
component.Owner.EnsureComponent(out AnimationPlayerComponent animationPlayer);
|
||||
if (animationPlayer.HasRunningAnimation("radiatingLight")) return;
|
||||
animationPlayer.Play(_radiatingLightAnimation, "radiatingLight");
|
||||
animationPlayer.AnimationCompleted += s => animationPlayer.Play(_radiatingLightAnimation, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user