Re-organize all projects (#4166)

This commit is contained in:
DrSmugleaf
2021-06-09 22:19:39 +02:00
committed by GitHub
parent 9f50e4061b
commit ff1a2d97ea
1773 changed files with 5258 additions and 5508 deletions

View File

@@ -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));
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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?
}
}
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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);
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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 { }
}

View File

@@ -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
{
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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,
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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}
}
}
});
}
}
}
}
}

View File

@@ -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;
}
}
}
}
}

View File

@@ -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
{
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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
})
}
});
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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();
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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}");
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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,
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}
}
}
}

View File

@@ -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}");
}
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -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,
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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
{
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -1,12 +0,0 @@
using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Items
{
[RegisterComponent]
public class ItemStatusComponent : Component
{
public override string Name => "ItemStatus";
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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")}");
}
}
}

View File

@@ -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