Merge branch 'master' into prediction
This commit is contained in:
@@ -40,8 +40,8 @@ csharp_space_between_method_declaration_parameter_list_parentheses=false
|
||||
csharp_space_between_parentheses=false
|
||||
csharp_space_between_square_brackets=false
|
||||
csharp_style_expression_bodied_accessors=true:suggestion
|
||||
csharp_style_expression_bodied_constructors=true:suggestion
|
||||
csharp_style_expression_bodied_methods=true:suggestion
|
||||
csharp_style_expression_bodied_constructors=false:suggestion
|
||||
csharp_style_expression_bodied_methods=false:suggestion
|
||||
csharp_style_expression_bodied_properties=true:suggestion
|
||||
csharp_style_var_elsewhere=true:suggestion
|
||||
csharp_style_var_for_built_in_types=true:suggestion
|
||||
|
||||
@@ -184,6 +184,9 @@ namespace Content.Client.Chat
|
||||
case ChatChannel.OOC:
|
||||
color = Color.LightSkyBlue;
|
||||
break;
|
||||
case ChatChannel.Dead:
|
||||
color = Color.MediumPurple;
|
||||
break;
|
||||
}
|
||||
|
||||
_currentChatBox?.AddLine(messageText, message.Channel, color);
|
||||
@@ -288,7 +291,7 @@ namespace Content.Client.Chat
|
||||
WriteChatMessage(storedMessage);
|
||||
|
||||
// Local messages that have an entity attached get a speech bubble.
|
||||
if (msg.Channel == ChatChannel.Local && msg.SenderEntity != default)
|
||||
if ((msg.Channel == ChatChannel.Local || msg.Channel == ChatChannel.Dead) && msg.SenderEntity != default)
|
||||
{
|
||||
AddSpeechBubble(msg);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,11 @@ using Content.Client.Interfaces.Parallax;
|
||||
using Content.Client.Parallax;
|
||||
using Content.Client.Sandbox;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.Chemistry;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client
|
||||
@@ -27,6 +30,7 @@ namespace Content.Client
|
||||
IoCManager.Register<IModuleManager, ClientModuleManager>();
|
||||
IoCManager.Register<IClientPreferencesManager, ClientPreferencesManager>();
|
||||
IoCManager.Register<IItemSlotManager, ItemSlotManager>();
|
||||
IoCManager.Register<IStylesheetManager, StylesheetManager>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
80
Content.Client/Command/CommunicationsConsoleMenu.cs
Normal file
80
Content.Client/Command/CommunicationsConsoleMenu.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System.Threading;
|
||||
using Content.Client.GameObjects.Components.Command;
|
||||
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.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
|
||||
namespace Content.Client.Command
|
||||
{
|
||||
public class CommunicationsConsoleMenu : SS14Window
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
protected override Vector2? CustomSize => new Vector2(600, 400);
|
||||
|
||||
private CommunicationsConsoleBoundUserInterface Owner { get; set; }
|
||||
private readonly CancellationTokenSource _timerCancelTokenSource = new CancellationTokenSource();
|
||||
private readonly Button _emergencyShuttleButton;
|
||||
private readonly RichTextLabel _countdownLabel;
|
||||
|
||||
public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Title = _localizationManager.GetString("Communications Console");
|
||||
Owner = owner;
|
||||
|
||||
_countdownLabel = new RichTextLabel(){CustomMinimumSize = new Vector2(0, 200)};
|
||||
_emergencyShuttleButton = new Button();
|
||||
_emergencyShuttleButton.OnPressed += (e) => Owner.EmergencyShuttleButtonPressed();
|
||||
|
||||
var vbox = new VBoxContainer() {SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsVertical = SizeFlags.FillExpand};
|
||||
|
||||
vbox.AddChild(_countdownLabel);
|
||||
vbox.AddChild(_emergencyShuttleButton);
|
||||
|
||||
var hbox = new HBoxContainer() {SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsVertical = SizeFlags.FillExpand};
|
||||
hbox.AddChild(new Control(){CustomMinimumSize = new Vector2(100,0), SizeFlagsHorizontal = SizeFlags.FillExpand});
|
||||
hbox.AddChild(vbox);
|
||||
hbox.AddChild(new Control(){CustomMinimumSize = new Vector2(100,0), SizeFlagsHorizontal = SizeFlags.FillExpand});
|
||||
|
||||
Contents.AddChild(hbox);
|
||||
|
||||
UpdateCountdown();
|
||||
Timer.SpawnRepeating(1000, UpdateCountdown, _timerCancelTokenSource.Token);
|
||||
}
|
||||
|
||||
public void UpdateCountdown()
|
||||
{
|
||||
if (!Owner.CountdownStarted)
|
||||
{
|
||||
_countdownLabel.SetMessage("");
|
||||
_emergencyShuttleButton.Text = _localizationManager.GetString("Call emergency shuttle");
|
||||
return;
|
||||
}
|
||||
|
||||
_emergencyShuttleButton.Text = _localizationManager.GetString("Recall emergency shuttle");
|
||||
_countdownLabel.SetMessage($"Time remaining\n{Owner.Countdown.ToString()}s");
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
|
||||
_timerCancelTokenSource.Cancel();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing)
|
||||
_timerCancelTokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,18 @@ using Content.Client.Parallax;
|
||||
using Content.Client.Sandbox;
|
||||
using Content.Client.State;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Cargo;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Markers;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.GameObjects.Components.VendingMachines;
|
||||
using Robust.Client;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Interfaces.State;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
@@ -35,6 +37,8 @@ namespace Content.Client
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
[Dependency] private readonly IBaseClient _baseClient;
|
||||
[Dependency] private readonly IEscapeMenuOwner _escapeMenuOwner;
|
||||
[Dependency] private readonly IGameController _gameController;
|
||||
[Dependency] private readonly IStateManager _stateManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void Init()
|
||||
@@ -131,6 +135,7 @@ namespace Content.Client
|
||||
"Paper",
|
||||
"Write",
|
||||
"Bloodstream",
|
||||
"TransformableContainer",
|
||||
"Mind",
|
||||
"MovementSpeedModifier",
|
||||
"StorageFill"
|
||||
@@ -145,7 +150,7 @@ namespace Content.Client
|
||||
factory.Register<SharedLatheComponent>();
|
||||
factory.Register<SharedSpawnPointComponent>();
|
||||
|
||||
factory.Register<SolutionComponent>();
|
||||
factory.Register<SharedSolutionComponent>();
|
||||
|
||||
factory.Register<SharedVendingMachineComponent>();
|
||||
factory.Register<SharedWiresComponent>();
|
||||
@@ -167,10 +172,7 @@ namespace Content.Client
|
||||
|
||||
IoCManager.Resolve<IParallaxManager>().LoadParallax();
|
||||
IoCManager.Resolve<IBaseClient>().PlayerJoinedServer += SubscribePlayerAttachmentEvents;
|
||||
|
||||
var stylesheet = new NanoStyle();
|
||||
|
||||
IoCManager.Resolve<IUserInterfaceManager>().Stylesheet = stylesheet.Stylesheet;
|
||||
IoCManager.Resolve<IStylesheetManager>().Initialize();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
@@ -225,6 +227,37 @@ namespace Content.Client
|
||||
IoCManager.Resolve<ISandboxManager>().Initialize();
|
||||
IoCManager.Resolve<IClientPreferencesManager>().Initialize();
|
||||
IoCManager.Resolve<IItemSlotManager>().Initialize();
|
||||
|
||||
_baseClient.RunLevelChanged += (sender, args) =>
|
||||
{
|
||||
if (args.NewLevel == ClientRunLevel.Initialize)
|
||||
{
|
||||
SwitchToDefaultState(args.OldLevel == ClientRunLevel.Connected ||
|
||||
args.OldLevel == ClientRunLevel.InGame);
|
||||
}
|
||||
};
|
||||
|
||||
SwitchToDefaultState();
|
||||
}
|
||||
|
||||
private void SwitchToDefaultState(bool disconnected = false)
|
||||
{
|
||||
// Fire off into state dependent on launcher or not.
|
||||
|
||||
if (_gameController.LaunchState.FromLauncher)
|
||||
{
|
||||
_stateManager.RequestStateChange<LauncherConnecting>();
|
||||
var state = (LauncherConnecting) _stateManager.CurrentState;
|
||||
|
||||
if (disconnected)
|
||||
{
|
||||
state.SetDisconnected();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_stateManager.RequestStateChange<MainScreen>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(ModUpdateLevel level, FrameEventArgs frameEventArgs)
|
||||
|
||||
@@ -5,7 +5,6 @@ using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.Placement;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Interfaces.State;
|
||||
using Robust.Client.State.States;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Client.GameObjects.Components.Mobs;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -68,7 +69,7 @@ namespace Content.Client.GameObjects.Components.Actor
|
||||
(SubText = new Label
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.None,
|
||||
StyleClasses = {NanoStyle.StyleClassLabelSubText}
|
||||
StyleClasses = {StyleNano.StyleClassLabelSubText}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
@@ -7,6 +8,7 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Content.Shared.Chemistry;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Chemistry
|
||||
{
|
||||
@@ -16,8 +18,8 @@ namespace Content.Client.GameObjects.Components.Chemistry
|
||||
[RegisterComponent]
|
||||
public class InjectorComponent : SharedInjectorComponent, IItemStatus
|
||||
{
|
||||
[ViewVariables] private int CurrentVolume { get; set; }
|
||||
[ViewVariables] private int TotalVolume { get; set; }
|
||||
[ViewVariables] private ReagentUnit CurrentVolume { get; set; }
|
||||
[ViewVariables] private ReagentUnit TotalVolume { get; set; }
|
||||
[ViewVariables] private InjectorToggleMode CurrentMode { get; set; }
|
||||
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
|
||||
|
||||
@@ -28,7 +30,7 @@ namespace Content.Client.GameObjects.Components.Chemistry
|
||||
//Handle net updates
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
var cast = (InjectorComponentState)curState;
|
||||
var cast = (InjectorComponentState) curState;
|
||||
if (cast != null)
|
||||
{
|
||||
CurrentVolume = cast.CurrentVolume;
|
||||
@@ -49,7 +51,7 @@ namespace Content.Client.GameObjects.Components.Chemistry
|
||||
public StatusControl(InjectorComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel { StyleClasses = { NanoStyle.StyleClassItemStatus } };
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
AddChild(_label);
|
||||
|
||||
parent._uiUpdateNeeded = true;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
@@ -170,7 +171,7 @@ namespace Content.Client.GameObjects.Components.Chemistry
|
||||
Title = castState.DispenserName;
|
||||
UpdateContainerInfo(castState);
|
||||
|
||||
switch (castState.SelectedDispenseAmount)
|
||||
switch (castState.SelectedDispenseAmount.Int())
|
||||
{
|
||||
case 1:
|
||||
DispenseButton1.Pressed = true;
|
||||
@@ -218,7 +219,7 @@ namespace Content.Client.GameObjects.Components.Chemistry
|
||||
new Label
|
||||
{
|
||||
Text = $"{state.BeakerCurrentVolume}/{state.BeakerMaxVolume}",
|
||||
StyleClasses = {NanoStyle.StyleClassLabelSecondaryColor}
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -247,12 +248,12 @@ namespace Content.Client.GameObjects.Components.Chemistry
|
||||
new Label
|
||||
{
|
||||
Text = $"{name}: ",
|
||||
StyleClasses = {NanoStyle.StyleClassPowerStateGood}
|
||||
StyleClasses = {StyleNano.StyleClassPowerStateGood}
|
||||
},
|
||||
new Label
|
||||
{
|
||||
Text = $"{reagent.Quantity}u",
|
||||
StyleClasses = {NanoStyle.StyleClassPowerStateGood}
|
||||
StyleClasses = {StyleNano.StyleClassPowerStateGood}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -267,7 +268,7 @@ namespace Content.Client.GameObjects.Components.Chemistry
|
||||
new Label
|
||||
{
|
||||
Text = $"{reagent.Quantity}u",
|
||||
StyleClasses = {NanoStyle.StyleClassLabelSecondaryColor}
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using Content.Client.Command;
|
||||
using Content.Shared.GameObjects.Components.Command;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Command
|
||||
{
|
||||
public class CommunicationsConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private CommunicationsConsoleMenu _menu;
|
||||
|
||||
[Dependency] private IGameTiming _gameTiming;
|
||||
|
||||
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 CallShuttle()
|
||||
{
|
||||
SendMessage(new CommunicationsConsoleCallEmergencyShuttleMessage());
|
||||
}
|
||||
|
||||
public void RecallShuttle()
|
||||
{
|
||||
SendMessage(new CommunicationsConsoleRecallEmergencyShuttleMessage());
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
if (!(state is CommunicationsConsoleInterfaceState commsState))
|
||||
return;
|
||||
|
||||
_expectedCountdownTime = commsState.ExpectedCountdownEnd;
|
||||
CountdownStarted = commsState.CountdownStarted;
|
||||
_menu?.UpdateCountdown();
|
||||
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
_menu?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects.Components.Instruments;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Client.Audio.Midi;
|
||||
using Robust.Shared.Audio.Midi;
|
||||
@@ -28,6 +29,7 @@ namespace Content.Client.GameObjects.Components.Instruments
|
||||
[Dependency] private readonly IGameTiming _timing;
|
||||
#pragma warning restore 649
|
||||
|
||||
[CanBeNull]
|
||||
private IMidiRenderer _renderer;
|
||||
private int _instrumentProgram = 1;
|
||||
|
||||
@@ -42,8 +44,14 @@ namespace Content.Client.GameObjects.Components.Instruments
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool LoopMidi
|
||||
{
|
||||
get => _renderer.LoopMidi;
|
||||
set => _renderer.LoopMidi = value;
|
||||
get => _renderer?.LoopMidi ?? false;
|
||||
set
|
||||
{
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.LoopMidi = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -53,9 +61,13 @@ namespace Content.Client.GameObjects.Components.Instruments
|
||||
public int InstrumentProgram
|
||||
{
|
||||
get => _instrumentProgram;
|
||||
set {
|
||||
set
|
||||
{
|
||||
_instrumentProgram = value;
|
||||
_renderer.MidiProgram = _instrumentProgram;
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.MidiProgram = _instrumentProgram;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,22 +75,26 @@ namespace Content.Client.GameObjects.Components.Instruments
|
||||
/// Whether there's a midi song being played or not.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool IsMidiOpen => _renderer.Status == MidiRendererStatus.File;
|
||||
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;
|
||||
public bool IsInputOpen => _renderer?.Status == MidiRendererStatus.Input;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
IoCManager.InjectDependencies(this);
|
||||
_renderer = _midiManager.GetNewRenderer();
|
||||
_renderer.MidiProgram = _instrumentProgram;
|
||||
_renderer.TrackingEntity = Owner;
|
||||
_renderer.OnMidiPlayerFinished += () => { OnMidiPlaybackEnded?.Invoke(); };
|
||||
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.MidiProgram = _instrumentProgram;
|
||||
_renderer.TrackingEntity = Owner;
|
||||
_renderer.OnMidiPlayerFinished += () => { OnMidiPlaybackEnded?.Invoke(); };
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
@@ -93,9 +109,16 @@ namespace Content.Client.GameObjects.Components.Instruments
|
||||
serializer.DataField(ref _instrumentProgram, "program", 1);
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
|
||||
IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
if (_renderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case InstrumentMidiEventMessage midiEventMessage:
|
||||
@@ -107,8 +130,8 @@ namespace Content.Client.GameObjects.Components.Instruments
|
||||
|
||||
case InstrumentStopMidiMessage _:
|
||||
_renderer.StopAllNotes();
|
||||
if(IsInputOpen) CloseInput();
|
||||
if(IsMidiOpen) CloseMidi();
|
||||
if (IsInputOpen) CloseInput();
|
||||
if (IsMidiOpen) CloseMidi();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -116,7 +139,7 @@ namespace Content.Client.GameObjects.Components.Instruments
|
||||
/// <inheritdoc cref="MidiRenderer.OpenInput"/>
|
||||
public bool OpenInput()
|
||||
{
|
||||
if (_renderer.OpenInput())
|
||||
if (_renderer != null && _renderer.OpenInput())
|
||||
{
|
||||
_renderer.OnMidiEvent += RendererOnMidiEvent;
|
||||
return true;
|
||||
@@ -128,28 +151,37 @@ namespace Content.Client.GameObjects.Components.Instruments
|
||||
/// <inheritdoc cref="MidiRenderer.CloseInput"/>
|
||||
public bool CloseInput()
|
||||
{
|
||||
if (!_renderer.CloseInput()) return false;
|
||||
if (_renderer == null || !_renderer.CloseInput())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_renderer.OnMidiEvent -= RendererOnMidiEvent;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MidiRenderer.OpenMidi(string)"/>
|
||||
public bool OpenMidi(string filename)
|
||||
{
|
||||
if (!_renderer.OpenMidi(filename)) return false;
|
||||
if (_renderer == null || !_renderer.OpenMidi(filename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_renderer.OnMidiEvent += RendererOnMidiEvent;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MidiRenderer.CloseMidi"/>
|
||||
public bool CloseMidi()
|
||||
{
|
||||
if (!_renderer.CloseMidi()) return false;
|
||||
if (_renderer == null || !_renderer.CloseMidi())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_renderer.OnMidiEvent -= RendererOnMidiEvent;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared.Preferences.Appearance;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
@@ -126,9 +127,9 @@ namespace Content.Client.GameObjects.Components
|
||||
var vBox = new VBoxContainer();
|
||||
AddChild(vBox);
|
||||
|
||||
vBox.AddChild(_colorSliderR = new ColorSlider(NanoStyle.StyleClassSliderRed));
|
||||
vBox.AddChild(_colorSliderG = new ColorSlider(NanoStyle.StyleClassSliderGreen));
|
||||
vBox.AddChild(_colorSliderB = new ColorSlider(NanoStyle.StyleClassSliderBlue));
|
||||
vBox.AddChild(_colorSliderR = new ColorSlider(StyleNano.StyleClassSliderRed));
|
||||
vBox.AddChild(_colorSliderG = new ColorSlider(StyleNano.StyleClassSliderGreen));
|
||||
vBox.AddChild(_colorSliderB = new ColorSlider(StyleNano.StyleClassSliderBlue));
|
||||
|
||||
Action colorValueChanged = ColorValueChanged;
|
||||
_colorSliderR.OnValueChanged += colorValueChanged;
|
||||
|
||||
@@ -76,10 +76,6 @@ namespace Content.Client.GameObjects.Components.Mobs
|
||||
|
||||
private void PlayerDetached()
|
||||
{
|
||||
if (!CurrentlyControlled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_ui?.Dispose();
|
||||
_ui = null;
|
||||
}
|
||||
|
||||
108
Content.Client/GameObjects/Components/Observer/GhostComponent.cs
Normal file
108
Content.Client/GameObjects/Components/Observer/GhostComponent.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.GameObjects.Components.Observer;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Observer
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GhostComponent : SharedGhostComponent
|
||||
{
|
||||
private GhostGui _gui;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool CanReturnToBody { get; private set; } = true;
|
||||
|
||||
private bool _isAttached;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IGameHud _gameHud;
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
[Dependency] private IComponentManager _componentManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
_gui?.Dispose();
|
||||
|
||||
// PlayerDetachedMsg might not fire due to deletion order so...
|
||||
if (_isAttached)
|
||||
{
|
||||
SetGhostVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SetGhostVisibility(bool visibility)
|
||||
{
|
||||
foreach (var ghost in _componentManager.GetAllComponents(typeof(GhostComponent)))
|
||||
{
|
||||
if (ghost.Owner.TryGetComponent(out SpriteComponent component))
|
||||
component.Visible = visibility;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent component))
|
||||
component.Visible = _playerManager.LocalPlayer.ControlledEntity?.HasComponent<GhostComponent>() ?? false;
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
|
||||
IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PlayerAttachedMsg _:
|
||||
if (_gui == null)
|
||||
{
|
||||
_gui = new GhostGui(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gui.Orphan();
|
||||
}
|
||||
|
||||
_gameHud.HandsContainer.AddChild(_gui);
|
||||
SetGhostVisibility(true);
|
||||
_isAttached = true;
|
||||
|
||||
break;
|
||||
|
||||
case PlayerDetachedMsg _:
|
||||
_gui.Parent?.RemoveChild(_gui);
|
||||
SetGhostVisibility(false);
|
||||
_isAttached = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendReturnToBodyMessage() => SendNetworkMessage(new ReturnToBodyComponentMessage());
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (!(curState is GhostComponentState state)) return;
|
||||
|
||||
CanReturnToBody = state.CanReturnToBody;
|
||||
|
||||
if (Owner == _playerManager.LocalPlayer.ControlledEntity)
|
||||
{
|
||||
_gui?.Update();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
@@ -47,15 +48,15 @@ namespace Content.Client.GameObjects.Components.Power
|
||||
{
|
||||
case ApcExternalPowerState.None:
|
||||
_externalPowerStateLabel.Text = "None";
|
||||
_externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateNone);
|
||||
_externalPowerStateLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateNone);
|
||||
break;
|
||||
case ApcExternalPowerState.Low:
|
||||
_externalPowerStateLabel.Text = "Low";
|
||||
_externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateLow);
|
||||
_externalPowerStateLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateLow);
|
||||
break;
|
||||
case ApcExternalPowerState.Good:
|
||||
_externalPowerStateLabel.Text = "Good";
|
||||
_externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateGood);
|
||||
_externalPowerStateLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
@@ -140,7 +141,7 @@ namespace Content.Client.GameObjects.Components.Power
|
||||
var externalStatus = new HBoxContainer();
|
||||
var externalStatusLabel = new Label {Text = "External Power: "};
|
||||
ExternalPowerStateLabel = new Label {Text = "Good"};
|
||||
ExternalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateGood);
|
||||
ExternalPowerStateLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood);
|
||||
externalStatus.AddChild(externalStatusLabel);
|
||||
externalStatus.AddChild(ExternalPowerStateLabel);
|
||||
rows.AddChild(externalStatus);
|
||||
|
||||
@@ -17,9 +17,9 @@ namespace Content.Client.GameObjects.Components.Research
|
||||
private IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore
|
||||
[ViewVariables]
|
||||
private LatheMenu menu;
|
||||
private LatheMenu _menu;
|
||||
[ViewVariables]
|
||||
private LatheQueueMenu queueMenu;
|
||||
private LatheQueueMenu _queueMenu;
|
||||
|
||||
public MaterialStorageComponent Storage { get; private set; }
|
||||
public SharedLatheComponent Lathe { get; private set; }
|
||||
@@ -48,30 +48,30 @@ namespace Content.Client.GameObjects.Components.Research
|
||||
Lathe = lathe;
|
||||
Database = database;
|
||||
|
||||
menu = new LatheMenu(this);
|
||||
queueMenu = new LatheQueueMenu { Owner = this };
|
||||
_menu = new LatheMenu(this);
|
||||
_queueMenu = new LatheQueueMenu { Owner = this };
|
||||
|
||||
menu.OnClose += Close;
|
||||
_menu.OnClose += Close;
|
||||
|
||||
menu.Populate();
|
||||
menu.PopulateMaterials();
|
||||
_menu.Populate();
|
||||
_menu.PopulateMaterials();
|
||||
|
||||
menu.QueueButton.OnPressed += (args) => { queueMenu.OpenCentered(); };
|
||||
_menu.QueueButton.OnPressed += (args) => { _queueMenu.OpenCentered(); };
|
||||
|
||||
menu.ServerConnectButton.OnPressed += (args) =>
|
||||
_menu.ServerConnectButton.OnPressed += (args) =>
|
||||
{
|
||||
SendMessage(new SharedLatheComponent.LatheServerSelectionMessage());
|
||||
};
|
||||
|
||||
menu.ServerSyncButton.OnPressed += (args) =>
|
||||
_menu.ServerSyncButton.OnPressed += (args) =>
|
||||
{
|
||||
SendMessage(new SharedLatheComponent.LatheServerSyncMessage());
|
||||
};
|
||||
|
||||
storage.OnMaterialStorageChanged += menu.PopulateDisabled;
|
||||
storage.OnMaterialStorageChanged += menu.PopulateMaterials;
|
||||
storage.OnMaterialStorageChanged += _menu.PopulateDisabled;
|
||||
storage.OnMaterialStorageChanged += _menu.PopulateMaterials;
|
||||
|
||||
menu.OpenCentered();
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
public void Queue(LatheRecipePrototype recipe, int quantity = 1)
|
||||
@@ -85,10 +85,10 @@ namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
case SharedLatheComponent.LatheProducingRecipeMessage msg:
|
||||
if (!_prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype recipe)) break;
|
||||
queueMenu?.SetInfo(recipe);
|
||||
_queueMenu?.SetInfo(recipe);
|
||||
break;
|
||||
case SharedLatheComponent.LatheStoppedProducingRecipeMessage _:
|
||||
queueMenu?.ClearInfo();
|
||||
_queueMenu?.ClearInfo();
|
||||
break;
|
||||
case SharedLatheComponent.LatheFullQueueMessage msg:
|
||||
_queuedRecipes.Clear();
|
||||
@@ -97,7 +97,7 @@ namespace Content.Client.GameObjects.Components.Research
|
||||
if (!_prototypeManager.TryIndex(id, out LatheRecipePrototype recipePrototype)) break;
|
||||
_queuedRecipes.Enqueue(recipePrototype);
|
||||
}
|
||||
queueMenu?.PopulateList();
|
||||
_queueMenu?.PopulateList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -106,8 +106,8 @@ namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
menu?.Dispose();
|
||||
queueMenu?.Dispose();
|
||||
_menu?.Dispose();
|
||||
_queueMenu?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -13,21 +13,19 @@ namespace Content.Client.GameObjects.Components
|
||||
[RegisterComponent]
|
||||
public class StackComponent : SharedStackComponent, IItemStatus
|
||||
{
|
||||
[ViewVariables] public int Count { get; private set; }
|
||||
[ViewVariables] public int MaxCount { get; private set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
|
||||
|
||||
public Control MakeControl() => new StatusControl(this);
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
public override int Count
|
||||
{
|
||||
if (!(curState is StackComponentState cast))
|
||||
return;
|
||||
get => base.Count;
|
||||
set
|
||||
{
|
||||
base.Count = value;
|
||||
|
||||
Count = cast.Count;
|
||||
MaxCount = cast.MaxCount;
|
||||
_uiUpdateNeeded = true;
|
||||
_uiUpdateNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
@@ -38,7 +36,7 @@ namespace Content.Client.GameObjects.Components
|
||||
public StatusControl(StackComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel {StyleClasses = {NanoStyle.StyleClassItemStatus}};
|
||||
_label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}};
|
||||
AddChild(_label);
|
||||
|
||||
parent._uiUpdateNeeded = true;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Content.Client.Animations;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||
@@ -174,7 +175,7 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
(_noMagazineLabel = new Label
|
||||
{
|
||||
Text = "No Magazine!",
|
||||
StyleClasses = {NanoStyle.StyleClassItemStatus}
|
||||
StyleClasses = {StyleNano.StyleClassItemStatus}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
@@ -46,7 +47,7 @@ namespace Content.Client.GameObjects.Components
|
||||
public StatusControl(WelderComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel {StyleClasses = {NanoStyle.StyleClassItemStatus}};
|
||||
_label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}};
|
||||
AddChild(_label);
|
||||
|
||||
parent._uiUpdateNeeded = true;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Content.Client.Interfaces;
|
||||
using Content.Client.State;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared;
|
||||
using Robust.Client.Interfaces.State;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
@@ -35,10 +36,13 @@ namespace Content.Client.GameTicking
|
||||
_netManager.RegisterNetMessage<MsgTickerJoinGame>(nameof(MsgTickerJoinGame), JoinGame);
|
||||
_netManager.RegisterNetMessage<MsgTickerLobbyStatus>(nameof(MsgTickerLobbyStatus), LobbyStatus);
|
||||
_netManager.RegisterNetMessage<MsgTickerLobbyInfo>(nameof(MsgTickerLobbyInfo), LobbyInfo);
|
||||
_netManager.RegisterNetMessage<MsgRoundEndMessage>(nameof(MsgRoundEndMessage), RoundEnd);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void JoinLobby(MsgTickerJoinLobby message)
|
||||
{
|
||||
_stateManager.RequestStateChange<LobbyState>();
|
||||
@@ -64,5 +68,13 @@ namespace Content.Client.GameTicking
|
||||
{
|
||||
_stateManager.RequestStateChange<GameScreen>();
|
||||
}
|
||||
|
||||
private void RoundEnd(MsgRoundEndMessage message)
|
||||
{
|
||||
|
||||
//This is not ideal at all, but I don't see an immediately better fit anywhere else.
|
||||
var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundDuration, message.AllPlayersEndInfo);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using Content.Client.GameObjects.Components.Instruments;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Robust.Client.Audio.Midi;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
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.Log;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -27,7 +30,7 @@ namespace Content.Client.Instruments
|
||||
public InstrumentMenu(InstrumentBoundUserInterface owner)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
Title = "Instrument";
|
||||
Title = Loc.GetString("Instrument");
|
||||
|
||||
_owner = owner;
|
||||
|
||||
@@ -55,7 +58,7 @@ namespace Content.Client.Instruments
|
||||
|
||||
midiInputButton = new Button()
|
||||
{
|
||||
Text = "MIDI Input",
|
||||
Text = Loc.GetString("MIDI Input"),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
@@ -73,7 +76,7 @@ namespace Content.Client.Instruments
|
||||
|
||||
var midiFileButton = new Button()
|
||||
{
|
||||
Text = "Open File",
|
||||
Text = Loc.GetString("Play MIDI File"),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
@@ -91,7 +94,7 @@ namespace Content.Client.Instruments
|
||||
|
||||
midiLoopButton = new Button()
|
||||
{
|
||||
Text = "Loop",
|
||||
Text = Loc.GetString("Loop"),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
@@ -110,7 +113,7 @@ namespace Content.Client.Instruments
|
||||
|
||||
midiStopButton = new Button()
|
||||
{
|
||||
Text = "Stop",
|
||||
Text = Loc.GetString("Stop"),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
@@ -132,6 +135,26 @@ namespace Content.Client.Instruments
|
||||
|
||||
margin.AddChild(vBox);
|
||||
|
||||
if (!_midiManager.IsAvailable)
|
||||
{
|
||||
margin.AddChild(new PanelContainer
|
||||
{
|
||||
MouseFilter = MouseFilterMode.Stop,
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.Black.WithAlpha(0.90f)},
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Align = Label.AlignMode.Center,
|
||||
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
|
||||
StyleClasses = {StyleNano.StyleClassLabelBig},
|
||||
Text = Loc.GetString("MIDI support is currently\nnot available on your platform.")
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Contents.AddChild(margin);
|
||||
}
|
||||
|
||||
@@ -148,7 +171,8 @@ namespace Content.Client.Instruments
|
||||
|
||||
private async void MidiFileButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
var filename = await _fileDialogManager.OpenFile();
|
||||
var filters = new FileDialogFilters(new FileDialogFilters.Group("mid", "midi"));
|
||||
var filename = await _fileDialogManager.OpenFile(filters);
|
||||
|
||||
if (filename == null) return;
|
||||
|
||||
@@ -160,7 +184,7 @@ namespace Content.Client.Instruments
|
||||
|
||||
if (!_owner.Instrument.OpenMidi(filename)) return;
|
||||
MidiPlaybackSetButtonsDisabled(false);
|
||||
if(midiInputButton.Pressed)
|
||||
if (midiInputButton.Pressed)
|
||||
midiInputButton.Pressed = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ namespace Content.Client.State
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_playerManager.LocalPlayer.DetachEntity();
|
||||
|
||||
_inputManager.KeyBindStateChanged -= OnKeyBindStateChanged;
|
||||
}
|
||||
|
||||
|
||||
308
Content.Client/State/LauncherConnecting.cs
Normal file
308
Content.Client/State/LauncherConnecting.cs
Normal file
@@ -0,0 +1,308 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using static Content.Client.StaticIoC;
|
||||
|
||||
namespace Content.Client.State
|
||||
{
|
||||
public class LauncherConnecting : Robust.Client.State.State
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
|
||||
[Dependency] private readonly IStylesheetManager _stylesheetManager;
|
||||
[Dependency] private readonly IClientNetManager _clientNetManager;
|
||||
[Dependency] private readonly IGameController _gameController;
|
||||
[Dependency] private readonly IBaseClient _baseClient;
|
||||
#pragma warning restore 649
|
||||
|
||||
private Control _control;
|
||||
private Label _connectStatus;
|
||||
|
||||
private Control _connectingStatus;
|
||||
private Control _connectFail;
|
||||
private Label _connectFailReason;
|
||||
private Control _disconnected;
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
var panelTex = ResC.GetTexture("/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
{
|
||||
Texture = panelTex,
|
||||
Modulate = new Color(32, 32, 48),
|
||||
};
|
||||
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
|
||||
Button exitButton;
|
||||
Button reconnectButton;
|
||||
Button retryButton;
|
||||
|
||||
var address = _gameController.LaunchState.Ss14Address ?? _gameController.LaunchState.ConnectAddress;
|
||||
|
||||
VBoxContainer disconnected;
|
||||
_control = new Control
|
||||
{
|
||||
Stylesheet = _stylesheetManager.SheetSpace,
|
||||
Children =
|
||||
{
|
||||
new PanelContainer
|
||||
{
|
||||
PanelOverride = back
|
||||
},
|
||||
new VBoxContainer
|
||||
{
|
||||
SeparationOverride = 0,
|
||||
CustomMinimumSize = (300, 200),
|
||||
Children =
|
||||
{
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new MarginContainer
|
||||
{
|
||||
MarginLeftOverride = 8,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("Space Station 14"),
|
||||
StyleClasses = {StyleBase.StyleClassLabelHeading},
|
||||
VAlign = Label.VAlignMode.Center
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
(exitButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("Exit"),
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ShrinkEnd | Control.SizeFlags.Expand
|
||||
}),
|
||||
}
|
||||
},
|
||||
|
||||
// Line
|
||||
new HighDivider(),
|
||||
|
||||
new MarginContainer
|
||||
{
|
||||
SizeFlagsVertical = Control.SizeFlags.FillExpand,
|
||||
MarginLeftOverride = 4,
|
||||
MarginRightOverride = 4,
|
||||
MarginTopOverride = 4,
|
||||
Children =
|
||||
{
|
||||
new VBoxContainer
|
||||
{
|
||||
SeparationOverride = 0,
|
||||
Children =
|
||||
{
|
||||
new Control
|
||||
{
|
||||
SizeFlagsVertical = Control.SizeFlags.FillExpand,
|
||||
Children =
|
||||
{
|
||||
(_connectingStatus = new VBoxContainer
|
||||
{
|
||||
SeparationOverride = 0,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("Connecting to server..."),
|
||||
Align = Label.AlignMode.Center,
|
||||
},
|
||||
|
||||
(_connectStatus = new Label
|
||||
{
|
||||
StyleClasses = {StyleBase.StyleClassLabelSubText},
|
||||
Align = Label.AlignMode.Center,
|
||||
}),
|
||||
}
|
||||
}),
|
||||
(_connectFail = new VBoxContainer
|
||||
{
|
||||
Visible = false,
|
||||
SeparationOverride = 0,
|
||||
Children =
|
||||
{
|
||||
(_connectFailReason = new Label
|
||||
{
|
||||
Align = Label.AlignMode.Center
|
||||
}),
|
||||
|
||||
(retryButton = new Button
|
||||
{
|
||||
Text = "Retry",
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter,
|
||||
SizeFlagsVertical =
|
||||
Control.SizeFlags.Expand |
|
||||
Control.SizeFlags.ShrinkEnd
|
||||
})
|
||||
}
|
||||
}),
|
||||
|
||||
(_disconnected = new VBoxContainer
|
||||
{
|
||||
SeparationOverride = 0,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = "Disconnected from server:",
|
||||
Align = Label.AlignMode.Center
|
||||
},
|
||||
new Label
|
||||
{
|
||||
Text = _baseClient.LastDisconnectReason,
|
||||
Align = Label.AlignMode.Center
|
||||
},
|
||||
(reconnectButton = new Button
|
||||
{
|
||||
Text = "Reconnect",
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter,
|
||||
SizeFlagsVertical =
|
||||
Control.SizeFlags.Expand |
|
||||
Control.SizeFlags.ShrinkEnd
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// Padding.
|
||||
new Control {CustomMinimumSize = (0, 8)},
|
||||
|
||||
new Label
|
||||
{
|
||||
Text = address,
|
||||
StyleClasses = {StyleBase.StyleClassLabelSubText},
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// Line
|
||||
new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = Color.FromHex("#444"),
|
||||
ContentMarginTopOverride = 2
|
||||
},
|
||||
},
|
||||
|
||||
new MarginContainer
|
||||
{
|
||||
MarginLeftOverride = 12,
|
||||
MarginRightOverride = 4,
|
||||
Children =
|
||||
{
|
||||
new HBoxContainer
|
||||
{
|
||||
SizeFlagsVertical = Control.SizeFlags.ShrinkEnd,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("Don't die!"),
|
||||
StyleClasses = {StyleBase.StyleClassLabelSubText}
|
||||
},
|
||||
new Label
|
||||
{
|
||||
Text = "ver 0.1",
|
||||
SizeFlagsHorizontal =
|
||||
Control.SizeFlags.Expand | Control.SizeFlags.ShrinkEnd,
|
||||
StyleClasses = {StyleBase.StyleClassLabelSubText}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
_userInterfaceManager.StateRoot.AddChild(_control);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(_control, LayoutContainer.LayoutPreset.Center);
|
||||
LayoutContainer.SetGrowHorizontal(_control, LayoutContainer.GrowDirection.Both);
|
||||
LayoutContainer.SetGrowVertical(_control, LayoutContainer.GrowDirection.Both);
|
||||
|
||||
exitButton.OnPressed += args =>
|
||||
{
|
||||
_gameController.Shutdown("Exit button pressed");
|
||||
};
|
||||
|
||||
void Retry(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
_baseClient.ConnectToServer(_gameController.LaunchState.ConnectEndpoint);
|
||||
SetActivePage(Page.Connecting);
|
||||
}
|
||||
|
||||
reconnectButton.OnPressed += Retry;
|
||||
retryButton.OnPressed += Retry;
|
||||
|
||||
_clientNetManager.ConnectFailed += (sender, args) =>
|
||||
{
|
||||
_connectFailReason.Text = Loc.GetString("Failed to connect to server:\n{0}", args.Reason);
|
||||
SetActivePage(Page.ConnectFailed);
|
||||
};
|
||||
|
||||
_clientNetManager.ClientConnectStateChanged += ConnectStateChanged;
|
||||
|
||||
SetActivePage(Page.Connecting);
|
||||
|
||||
ConnectStateChanged(_clientNetManager.ClientConnectState);
|
||||
}
|
||||
|
||||
private void ConnectStateChanged(ClientConnectionState state)
|
||||
{
|
||||
_connectStatus.Text = Loc.GetString(state switch
|
||||
{
|
||||
ClientConnectionState.NotConnecting => "You should not be seeing this",
|
||||
ClientConnectionState.ResolvingHost => "Resolving server address...",
|
||||
ClientConnectionState.EstablishingConnection => "Establishing initial connection...",
|
||||
ClientConnectionState.Handshake => "Doing handshake...",
|
||||
ClientConnectionState.Connected => "Synchronizing game state...",
|
||||
_ => state.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_control.Dispose();
|
||||
}
|
||||
|
||||
public void SetDisconnected()
|
||||
{
|
||||
SetActivePage(Page.Disconnected);
|
||||
}
|
||||
|
||||
private void SetActivePage(Page page)
|
||||
{
|
||||
_connectingStatus.Visible = page == Page.Connecting;
|
||||
_connectFail.Visible = page == Page.ConnectFailed;
|
||||
_disconnected.Visible = page == Page.Disconnected;
|
||||
}
|
||||
|
||||
private enum Page
|
||||
{
|
||||
Connecting,
|
||||
ConnectFailed,
|
||||
Disconnected,
|
||||
}
|
||||
}
|
||||
}
|
||||
329
Content.Client/State/MainMenu.cs
Normal file
329
Content.Client/State/MainMenu.cs
Normal file
@@ -0,0 +1,329 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using Robust.Client;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.State
|
||||
{
|
||||
/// <summary>
|
||||
/// Main menu screen that is the first screen to be displayed when the game starts.
|
||||
/// </summary>
|
||||
// Instantiated dynamically through the StateManager, Dependencies will be resolved.
|
||||
public class MainScreen : Robust.Client.State.State
|
||||
{
|
||||
private const string PublicServerAddress = "server.spacestation14.io";
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IBaseClient _client;
|
||||
[Dependency] private readonly IClientNetManager _netManager;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager;
|
||||
[Dependency] private readonly IGameController _controllerProxy;
|
||||
[Dependency] private readonly ILocalizationManager _loc;
|
||||
[Dependency] private readonly IResourceCache _resourceCache;
|
||||
[Dependency] private readonly IUserInterfaceManager userInterfaceManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private MainMenuControl _mainMenuControl;
|
||||
private OptionsMenu OptionsMenu;
|
||||
private bool _isConnecting;
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private static readonly Regex IPv6Regex = new Regex(@"\[(.*:.*:.*)](?::(\d+))?");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Startup()
|
||||
{
|
||||
_mainMenuControl = new MainMenuControl(_resourceCache, _configurationManager);
|
||||
userInterfaceManager.StateRoot.AddChild(_mainMenuControl);
|
||||
|
||||
_mainMenuControl.QuitButton.OnPressed += QuitButtonPressed;
|
||||
_mainMenuControl.OptionsButton.OnPressed += OptionsButtonPressed;
|
||||
_mainMenuControl.DirectConnectButton.OnPressed += DirectConnectButtonPressed;
|
||||
_mainMenuControl.JoinPublicServerButton.OnPressed += JoinPublicServerButtonPressed;
|
||||
_mainMenuControl.AddressBox.OnTextEntered += AddressBoxEntered;
|
||||
|
||||
_client.RunLevelChanged += RunLevelChanged;
|
||||
|
||||
OptionsMenu = new OptionsMenu(_configurationManager);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Shutdown()
|
||||
{
|
||||
_client.RunLevelChanged -= RunLevelChanged;
|
||||
_netManager.ConnectFailed -= _onConnectFailed;
|
||||
|
||||
_mainMenuControl.Dispose();
|
||||
OptionsMenu.Dispose();
|
||||
}
|
||||
|
||||
private void QuitButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
_controllerProxy.Shutdown();
|
||||
}
|
||||
|
||||
private void OptionsButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
OptionsMenu.OpenCentered();
|
||||
}
|
||||
|
||||
private void DirectConnectButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
var input = _mainMenuControl.AddressBox;
|
||||
TryConnect(input.Text);
|
||||
}
|
||||
|
||||
private void JoinPublicServerButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
TryConnect(PublicServerAddress);
|
||||
}
|
||||
|
||||
private void AddressBoxEntered(LineEdit.LineEditEventArgs args)
|
||||
{
|
||||
if (_isConnecting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TryConnect(args.Text);
|
||||
}
|
||||
|
||||
private void TryConnect(string address)
|
||||
{
|
||||
var inputName = _mainMenuControl.UserNameBox.Text.Trim();
|
||||
var (nameValid, invalidReason) = UsernameHelpers.IsNameValid(inputName);
|
||||
if (!nameValid)
|
||||
{
|
||||
invalidReason = _loc.GetString(invalidReason);
|
||||
userInterfaceManager.Popup(
|
||||
_loc.GetString("Invalid username:\n{0}", invalidReason),
|
||||
_loc.GetString("Invalid Username"));
|
||||
return;
|
||||
}
|
||||
|
||||
var configName = _configurationManager.GetCVar<string>("player.name");
|
||||
if (_mainMenuControl.UserNameBox.Text != configName)
|
||||
{
|
||||
_configurationManager.SetCVar("player.name", inputName);
|
||||
_configurationManager.SaveToFile();
|
||||
}
|
||||
|
||||
_setConnectingState(true);
|
||||
_netManager.ConnectFailed += _onConnectFailed;
|
||||
try
|
||||
{
|
||||
ParseAddress(address, out var ip, out var port);
|
||||
_client.ConnectToServer(ip, port);
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
userInterfaceManager.Popup($"Unable to connect: {e.Message}", "Connection error.");
|
||||
Logger.Warning(e.ToString());
|
||||
_netManager.ConnectFailed -= _onConnectFailed;
|
||||
}
|
||||
}
|
||||
|
||||
private void RunLevelChanged(object obj, RunLevelChangedEventArgs args)
|
||||
{
|
||||
if (args.NewLevel == ClientRunLevel.Initialize)
|
||||
{
|
||||
_setConnectingState(false);
|
||||
_netManager.ConnectFailed -= _onConnectFailed;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseAddress(string address, out string ip, out ushort port)
|
||||
{
|
||||
var match6 = IPv6Regex.Match(address);
|
||||
if (match6 != Match.Empty)
|
||||
{
|
||||
ip = match6.Groups[1].Value;
|
||||
if (!match6.Groups[2].Success)
|
||||
{
|
||||
port = _client.DefaultPort;
|
||||
}
|
||||
else if (!ushort.TryParse(match6.Groups[2].Value, out port))
|
||||
{
|
||||
throw new ArgumentException("Not a valid port.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// See if the IP includes a port.
|
||||
var split = address.Split(':');
|
||||
ip = address;
|
||||
port = _client.DefaultPort;
|
||||
if (split.Length > 2)
|
||||
{
|
||||
throw new ArgumentException("Not a valid Address.");
|
||||
}
|
||||
|
||||
// IP:port format.
|
||||
if (split.Length == 2)
|
||||
{
|
||||
ip = split[0];
|
||||
if (!ushort.TryParse(split[1], out port))
|
||||
{
|
||||
throw new ArgumentException("Not a valid port.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _onConnectFailed(object _, NetConnectFailArgs args)
|
||||
{
|
||||
userInterfaceManager.Popup($"Failed to connect:\n{args.Reason}");
|
||||
_netManager.ConnectFailed -= _onConnectFailed;
|
||||
_setConnectingState(false);
|
||||
}
|
||||
|
||||
private void _setConnectingState(bool state)
|
||||
{
|
||||
_isConnecting = state;
|
||||
_mainMenuControl.DirectConnectButton.Disabled = state;
|
||||
#if FULL_RELEASE
|
||||
_mainMenuControl.JoinPublicServerButton.Disabled = state;
|
||||
#endif
|
||||
}
|
||||
|
||||
private sealed class MainMenuControl : Control
|
||||
{
|
||||
private readonly IResourceCache _resourceCache;
|
||||
private readonly IConfigurationManager _configurationManager;
|
||||
|
||||
public LineEdit UserNameBox { get; private set; }
|
||||
public Button JoinPublicServerButton { get; private set; }
|
||||
public LineEdit AddressBox { get; private set; }
|
||||
public Button DirectConnectButton { get; private set; }
|
||||
public Button OptionsButton { get; private set; }
|
||||
public Button QuitButton { get; private set; }
|
||||
public Label VersionLabel { get; private set; }
|
||||
|
||||
public MainMenuControl(IResourceCache resCache, IConfigurationManager configMan)
|
||||
{
|
||||
_resourceCache = resCache;
|
||||
_configurationManager = configMan;
|
||||
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
private void PerformLayout()
|
||||
{
|
||||
LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
|
||||
|
||||
var layout = new LayoutContainer();
|
||||
AddChild(layout);
|
||||
|
||||
var vBox = new VBoxContainer
|
||||
{
|
||||
StyleIdentifier = "mainMenuVBox"
|
||||
};
|
||||
|
||||
layout.AddChild(vBox);
|
||||
LayoutContainer.SetAnchorPreset(vBox, LayoutContainer.LayoutPreset.TopRight);
|
||||
LayoutContainer.SetMarginRight(vBox, -25);
|
||||
LayoutContainer.SetMarginTop(vBox, 30);
|
||||
LayoutContainer.SetGrowHorizontal(vBox, LayoutContainer.GrowDirection.Begin);
|
||||
|
||||
var logoTexture = _resourceCache.GetResource<TextureResource>("/Textures/Logo/logo.png");
|
||||
var logo = new TextureRect
|
||||
{
|
||||
Texture = logoTexture,
|
||||
Stretch = TextureRect.StretchMode.KeepCentered,
|
||||
};
|
||||
vBox.AddChild(logo);
|
||||
|
||||
var userNameHBox = new HBoxContainer {SeparationOverride = 4};
|
||||
vBox.AddChild(userNameHBox);
|
||||
userNameHBox.AddChild(new Label {Text = "Username:"});
|
||||
|
||||
var currentUserName = _configurationManager.GetCVar<string>("player.name");
|
||||
UserNameBox = new LineEdit
|
||||
{
|
||||
Text = currentUserName, PlaceHolder = "Username",
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand
|
||||
};
|
||||
|
||||
userNameHBox.AddChild(UserNameBox);
|
||||
|
||||
JoinPublicServerButton = new Button
|
||||
{
|
||||
Text = "Join Public Server",
|
||||
StyleIdentifier = "mainMenu",
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
#if !FULL_RELEASE
|
||||
Disabled = true,
|
||||
ToolTip = "Cannot connect to public server with a debug build."
|
||||
#endif
|
||||
};
|
||||
|
||||
vBox.AddChild(JoinPublicServerButton);
|
||||
|
||||
// Separator.
|
||||
vBox.AddChild(new Control {CustomMinimumSize = (0, 2)});
|
||||
|
||||
AddressBox = new LineEdit
|
||||
{
|
||||
Text = "localhost",
|
||||
PlaceHolder = "server address:port",
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand
|
||||
};
|
||||
|
||||
vBox.AddChild(AddressBox);
|
||||
|
||||
DirectConnectButton = new Button
|
||||
{
|
||||
Text = "Direct Connect",
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
StyleIdentifier = "mainMenu",
|
||||
};
|
||||
|
||||
vBox.AddChild(DirectConnectButton);
|
||||
|
||||
// Separator.
|
||||
vBox.AddChild(new Control {CustomMinimumSize = (0, 2)});
|
||||
|
||||
OptionsButton = new Button
|
||||
{
|
||||
Text = "Options",
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
StyleIdentifier = "mainMenu",
|
||||
};
|
||||
|
||||
vBox.AddChild(OptionsButton);
|
||||
|
||||
QuitButton = new Button
|
||||
{
|
||||
Text = "Quit",
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
StyleIdentifier = "mainMenu",
|
||||
};
|
||||
|
||||
vBox.AddChild(QuitButton);
|
||||
|
||||
VersionLabel = new Label
|
||||
{
|
||||
Text = $"v0.1"
|
||||
};
|
||||
|
||||
LayoutContainer.SetAnchorPreset(VersionLabel, LayoutContainer.LayoutPreset.BottomRight);
|
||||
LayoutContainer.SetGrowHorizontal(VersionLabel, LayoutContainer.GrowDirection.Begin);
|
||||
LayoutContainer.SetGrowVertical(VersionLabel, LayoutContainer.GrowDirection.Begin);
|
||||
layout.AddChild(VersionLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
|
||||
namespace Content.Client.UserInterface.Cargo
|
||||
{
|
||||
@@ -57,7 +58,7 @@ namespace Content.Client.UserInterface.Cargo
|
||||
var accountName = new HBoxContainer();
|
||||
var accountNameLabel = new Label {
|
||||
Text = _loc.GetString("Account Name: "),
|
||||
StyleClasses = { NanoStyle.StyleClassLabelKeyText }
|
||||
StyleClasses = { StyleNano.StyleClassLabelKeyText }
|
||||
};
|
||||
_accountNameLabel = new Label {
|
||||
Text = "None" //Owner.Bank.Account.Name
|
||||
@@ -70,7 +71,7 @@ namespace Content.Client.UserInterface.Cargo
|
||||
var pointsLabel = new Label
|
||||
{
|
||||
Text = _loc.GetString("Points: "),
|
||||
StyleClasses = { NanoStyle.StyleClassLabelKeyText }
|
||||
StyleClasses = { StyleNano.StyleClassLabelKeyText }
|
||||
};
|
||||
_pointsLabel = new Label
|
||||
{
|
||||
@@ -84,7 +85,7 @@ namespace Content.Client.UserInterface.Cargo
|
||||
var shuttleStatusLabel = new Label
|
||||
{
|
||||
Text = _loc.GetString("Shuttle Status: "),
|
||||
StyleClasses = { NanoStyle.StyleClassLabelKeyText }
|
||||
StyleClasses = { StyleNano.StyleClassLabelKeyText }
|
||||
};
|
||||
_shuttleStatusLabel = new Label
|
||||
{
|
||||
@@ -410,13 +411,13 @@ namespace Content.Client.UserInterface.Cargo
|
||||
ProductName = new Label
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
StyleClasses = { NanoStyle.StyleClassLabelSubText },
|
||||
StyleClasses = { StyleNano.StyleClassLabelSubText },
|
||||
ClipText = true
|
||||
};
|
||||
Description = new Label
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
StyleClasses = { NanoStyle.StyleClassLabelSubText },
|
||||
StyleClasses = { StyleNano.StyleClassLabelSubText },
|
||||
ClipText = true
|
||||
};
|
||||
vBox.AddChild(ProductName);
|
||||
@@ -426,14 +427,14 @@ namespace Content.Client.UserInterface.Cargo
|
||||
Approve = new Button
|
||||
{
|
||||
Text = "Approve",
|
||||
StyleClasses = { NanoStyle.StyleClassLabelSubText }
|
||||
StyleClasses = { StyleNano.StyleClassLabelSubText }
|
||||
};
|
||||
hBox.AddChild(Approve);
|
||||
|
||||
Cancel = new Button
|
||||
{
|
||||
Text = "Cancel",
|
||||
StyleClasses = { NanoStyle.StyleClassLabelSubText }
|
||||
StyleClasses = { StyleNano.StyleClassLabelSubText }
|
||||
};
|
||||
hBox.AddChild(Cancel);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using Content.Client.GameObjects.Components.Mobs;
|
||||
using Content.Client.Interfaces;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.Jobs;
|
||||
using Content.Shared.Preferences;
|
||||
@@ -67,7 +68,7 @@ namespace Content.Client.UserInterface
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.Expand | SizeFlags.ShrinkEnd,
|
||||
Text = Loc.GetString("Save and close"),
|
||||
StyleClasses = {NanoStyle.StyleClassButtonBig}
|
||||
StyleClasses = {StyleNano.StyleClassButtonBig}
|
||||
};
|
||||
|
||||
var topHBox = new HBoxContainer
|
||||
@@ -83,7 +84,7 @@ namespace Content.Client.UserInterface
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("Character Setup"),
|
||||
StyleClasses = {NanoStyle.StyleClassLabelHeadingBigger},
|
||||
StyleClasses = {StyleNano.StyleClassLabelHeadingBigger},
|
||||
VAlign = Label.VAlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.Expand | SizeFlags.ShrinkCenter
|
||||
}
|
||||
@@ -99,7 +100,7 @@ namespace Content.Client.UserInterface
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = NanoStyle.NanoGold,
|
||||
BackgroundColor = StyleNano.NanoGold,
|
||||
ContentMarginTopOverride = 2
|
||||
}
|
||||
});
|
||||
@@ -146,7 +147,7 @@ namespace Content.Client.UserInterface
|
||||
|
||||
hBox.AddChild(new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = NanoStyle.NanoGold},
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = StyleNano.NanoGold},
|
||||
CustomMinimumSize = (2, 0)
|
||||
});
|
||||
_humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager);
|
||||
|
||||
14
Content.Client/UserInterface/Controls/HighDivider.cs
Normal file
14
Content.Client/UserInterface/Controls/HighDivider.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls
|
||||
{
|
||||
public sealed class HighDivider : Control
|
||||
{
|
||||
public HighDivider()
|
||||
{
|
||||
Children.Add(new PanelContainer {StyleClasses = {StyleBase.ClassHighDivider}});
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Content.Client/UserInterface/GhostGui.cs
Normal file
34
Content.Client/UserInterface/GhostGui.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Data;
|
||||
using Content.Client.GameObjects.Components.Observer;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.UserInterface
|
||||
{
|
||||
public class GhostGui : Control
|
||||
{
|
||||
public Button ReturnToBody = new Button(){Text = "Return to body"};
|
||||
private GhostComponent _owner;
|
||||
|
||||
public GhostGui(GhostComponent owner)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_owner = owner;
|
||||
|
||||
MouseFilter = MouseFilterMode.Ignore;
|
||||
|
||||
ReturnToBody.OnPressed += (args) => { owner.SendReturnToBodyMessage(); };
|
||||
|
||||
AddChild(ReturnToBody);
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
ReturnToBody.Disabled = !_owner.CanReturnToBody;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.GameObjects.Components;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -57,7 +58,7 @@ namespace Content.Client.UserInterface
|
||||
(_itemNameLabel = new Label
|
||||
{
|
||||
ClipText = true,
|
||||
StyleClasses = {NanoStyle.StyleClassItemStatus}
|
||||
StyleClasses = {StyleNano.StyleClassItemStatus}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Client.Chat;
|
||||
using Content.Client.Interfaces;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
@@ -69,7 +70,7 @@ namespace Content.Client.UserInterface
|
||||
new Label
|
||||
{
|
||||
Text = Loc.GetString("Lobby"),
|
||||
StyleClasses = {NanoStyle.StyleClassLabelHeadingBigger},
|
||||
StyleClasses = {StyleNano.StyleClassLabelHeadingBigger},
|
||||
/*MarginBottom = 40,
|
||||
MarginLeft = 8,*/
|
||||
VAlign = Label.VAlignMode.Center
|
||||
@@ -78,7 +79,7 @@ namespace Content.Client.UserInterface
|
||||
},
|
||||
(ServerName = new Label
|
||||
{
|
||||
StyleClasses = {NanoStyle.StyleClassLabelHeadingBigger},
|
||||
StyleClasses = {StyleNano.StyleClassLabelHeadingBigger},
|
||||
/*MarginBottom = 40,
|
||||
GrowHorizontal = GrowDirection.Both,*/
|
||||
VAlign = Label.VAlignMode.Center,
|
||||
@@ -88,7 +89,7 @@ namespace Content.Client.UserInterface
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.ShrinkEnd,
|
||||
Text = Loc.GetString("Leave"),
|
||||
StyleClasses = {NanoStyle.StyleClassButtonBig},
|
||||
StyleClasses = {StyleNano.StyleClassButtonBig},
|
||||
//GrowHorizontal = GrowDirection.Begin
|
||||
})
|
||||
}
|
||||
@@ -100,7 +101,7 @@ namespace Content.Client.UserInterface
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = NanoStyle.NanoGold,
|
||||
BackgroundColor = StyleNano.NanoGold,
|
||||
ContentMarginTopOverride = 2
|
||||
},
|
||||
});
|
||||
@@ -146,20 +147,20 @@ namespace Content.Client.UserInterface
|
||||
(ObserveButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("Observe"),
|
||||
StyleClasses = {NanoStyle.StyleClassButtonBig}
|
||||
StyleClasses = {StyleNano.StyleClassButtonBig}
|
||||
}),
|
||||
(StartTime = new Label
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
Align = Label.AlignMode.Right,
|
||||
FontColorOverride = Color.DarkGray,
|
||||
StyleClasses = {NanoStyle.StyleClassLabelBig}
|
||||
StyleClasses = {StyleNano.StyleClassLabelBig}
|
||||
}),
|
||||
(ReadyButton = new Button
|
||||
{
|
||||
ToggleMode = true,
|
||||
Text = Loc.GetString("Ready Up"),
|
||||
StyleClasses = {NanoStyle.StyleClassButtonBig}
|
||||
StyleClasses = {StyleNano.StyleClassButtonBig}
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -188,7 +189,7 @@ namespace Content.Client.UserInterface
|
||||
|
||||
hBox.AddChild(new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = NanoStyle.NanoGold}, CustomMinimumSize = (2, 0)
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = StyleNano.NanoGold}, CustomMinimumSize = (2, 0)
|
||||
});
|
||||
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -14,7 +15,7 @@ namespace Content.Client.UserInterface
|
||||
{
|
||||
Children = {(_label = new Label
|
||||
{
|
||||
StyleClasses = {NanoStyle.StyleClassLabelHeading}
|
||||
StyleClasses = {StyleNano.StyleClassLabelHeading}
|
||||
})}
|
||||
};
|
||||
AddChild(_panel);
|
||||
|
||||
101
Content.Client/UserInterface/RoundEndSummaryWindow.cs
Normal file
101
Content.Client/UserInterface/RoundEndSummaryWindow.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Content.Client.Utility;
|
||||
using Robust.Client.Player;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using static Robust.Client.UserInterface.Controls.ItemList;
|
||||
using static Content.Shared.SharedGameTicker;
|
||||
using System;
|
||||
|
||||
namespace Content.Client.UserInterface
|
||||
{
|
||||
public sealed class RoundEndSummaryWindow : SS14Window
|
||||
{
|
||||
private VBoxContainer RoundEndSummaryTab { get; }
|
||||
private VBoxContainer PlayerManifestoTab { get; }
|
||||
private TabContainer RoundEndWindowTabs { get; }
|
||||
protected override Vector2? CustomSize => (520, 580);
|
||||
|
||||
public RoundEndSummaryWindow(string gm, TimeSpan roundTimeSpan, List<RoundEndPlayerInfo> info )
|
||||
{
|
||||
|
||||
Title = Loc.GetString("Round End Summary");
|
||||
|
||||
//Round End Window is split into two tabs, one about the round stats
|
||||
//and the other is a list of RoundEndPlayerInfo for each player.
|
||||
//This tab would be a good place for things like: "x many people died.",
|
||||
//"clown slipped the crew x times.", "x shots were fired this round.", etc.
|
||||
//Also good for serious info.
|
||||
RoundEndSummaryTab = new VBoxContainer()
|
||||
{
|
||||
Name = Loc.GetString("Round Information")
|
||||
};
|
||||
|
||||
//Tab for listing unique info per player.
|
||||
PlayerManifestoTab = new VBoxContainer()
|
||||
{
|
||||
Name = Loc.GetString("Player Manifesto")
|
||||
};
|
||||
|
||||
RoundEndWindowTabs = new TabContainer();
|
||||
RoundEndWindowTabs.AddChild(RoundEndSummaryTab);
|
||||
RoundEndWindowTabs.AddChild(PlayerManifestoTab);
|
||||
|
||||
Contents.AddChild(RoundEndWindowTabs);
|
||||
|
||||
//Gamemode Name
|
||||
var gamemodeLabel = new RichTextLabel();
|
||||
gamemodeLabel.SetMarkup(Loc.GetString("Round of [color=white]{0}[/color] has ended.", gm));
|
||||
RoundEndSummaryTab.AddChild(gamemodeLabel);
|
||||
|
||||
//Duration
|
||||
var roundTimeLabel = new RichTextLabel();
|
||||
roundTimeLabel.SetMarkup(Loc.GetString("It lasted for [color=yellow]{0} hours, {1} minutes, and {2} seconds.",
|
||||
roundTimeSpan.Hours,roundTimeSpan.Minutes,roundTimeSpan.Seconds));
|
||||
RoundEndSummaryTab.AddChild(roundTimeLabel);
|
||||
|
||||
//Initialize what will be the list of players display.
|
||||
var scrollContainer = new ScrollContainer();
|
||||
scrollContainer.SizeFlagsVertical = SizeFlags.FillExpand;
|
||||
var innerScrollContainer = new VBoxContainer();
|
||||
|
||||
//Put antags on top of the list.
|
||||
var manifestSortedList = info.OrderBy(p => !p.Antag);
|
||||
//Create labels for each player info.
|
||||
foreach (var plyinfo in manifestSortedList)
|
||||
{
|
||||
|
||||
var playerInfoText = new RichTextLabel()
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.Fill
|
||||
};
|
||||
|
||||
//TODO: On Hover display a popup detailing more play info.
|
||||
//For example: their antag goals and if they completed them sucessfully.
|
||||
var icNameColor = plyinfo.Antag ? "red" : "white";
|
||||
playerInfoText.SetMarkup(
|
||||
Loc.GetString($"[color=gray]{plyinfo.PlayerOOCName}[/color] was [color={icNameColor}]{plyinfo.PlayerICName}[/color] playing role of [color=orange]{plyinfo.Role}[/color]."));
|
||||
innerScrollContainer.AddChild(playerInfoText);
|
||||
}
|
||||
|
||||
scrollContainer.AddChild(innerScrollContainer);
|
||||
//Attach the entire ScrollContainer that holds all the playerinfo.
|
||||
PlayerManifestoTab.AddChild(scrollContainer);
|
||||
|
||||
//Finally, display the window.
|
||||
OpenCentered();
|
||||
MoveToFront();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.UserInterface.Stylesheets
|
||||
{
|
||||
public interface IStylesheetManager
|
||||
{
|
||||
Stylesheet SheetNano { get; }
|
||||
Stylesheet SheetSpace { get; }
|
||||
|
||||
void Initialize();
|
||||
}
|
||||
}
|
||||
47
Content.Client/UserInterface/Stylesheets/StyleBase.cs
Normal file
47
Content.Client/UserInterface/Stylesheets/StyleBase.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Content.Client.Utility;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.UserInterface.Stylesheets
|
||||
{
|
||||
public abstract class StyleBase
|
||||
{
|
||||
public const string ClassHighDivider = "HighDivider";
|
||||
public const string StyleClassLabelHeading = "LabelHeading";
|
||||
public const string StyleClassLabelSubText = "LabelSubText";
|
||||
|
||||
public abstract Stylesheet Stylesheet { get; }
|
||||
|
||||
protected StyleRule[] BaseRules { get; }
|
||||
|
||||
protected StyleBoxTexture BaseButton { get; }
|
||||
|
||||
protected StyleBase(IResourceCache resCache)
|
||||
{
|
||||
var notoSans12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 12);
|
||||
|
||||
// Button styles.
|
||||
var buttonTex = resCache.GetTexture("/Nano/button.svg.96dpi.png");
|
||||
BaseButton = new StyleBoxTexture
|
||||
{
|
||||
Texture = buttonTex,
|
||||
};
|
||||
BaseButton.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
BaseButton.SetPadding(StyleBox.Margin.All, 1);
|
||||
BaseButton.SetContentMarginOverride(StyleBox.Margin.Vertical, 2);
|
||||
BaseButton.SetContentMarginOverride(StyleBox.Margin.Horizontal, 14);
|
||||
|
||||
BaseRules = new[]
|
||||
{
|
||||
// Default font.
|
||||
new StyleRule(
|
||||
new SelectorElement(null, null, null, null),
|
||||
new[]
|
||||
{
|
||||
new StyleProperty("font", notoSans12),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,29 @@
|
||||
using Content.Client.GameObjects.EntitySystems;
|
||||
using System.Linq;
|
||||
using Content.Client.GameObjects.EntitySystems;
|
||||
using Content.Client.Utility;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.UserInterface
|
||||
namespace Content.Client.UserInterface.Stylesheets
|
||||
{
|
||||
public sealed class NanoStyle
|
||||
public sealed class StyleNano : StyleBase
|
||||
{
|
||||
public const string StyleClassSliderRed = "Red";
|
||||
public const string StyleClassSliderGreen = "Green";
|
||||
public const string StyleClassSliderBlue = "Blue";
|
||||
|
||||
public const string StyleClassLabelHeading = "LabelHeading";
|
||||
public const string StyleClassLabelHeadingBigger = "LabelHeadingBigger";
|
||||
public const string StyleClassLabelSubText = "LabelSubText";
|
||||
public const string StyleClassLabelKeyText = "LabelKeyText";
|
||||
public const string StyleClassLabelSecondaryColor = "LabelSecondaryColor";
|
||||
public const string StyleClassLabelBig = "LabelBig";
|
||||
public const string StyleClassButtonBig = "ButtonBig";
|
||||
|
||||
public static readonly Color NanoGold = Color.FromHex("#A88B5E");
|
||||
|
||||
public static readonly Color ButtonColorDefault = Color.FromHex("#464966");
|
||||
public static readonly Color ButtonColorHovered = Color.FromHex("#575b7f");
|
||||
public static readonly Color ButtonColorPressed = Color.FromHex("#3e6c45");
|
||||
@@ -36,12 +36,10 @@ namespace Content.Client.UserInterface
|
||||
|
||||
public const string StyleClassItemStatus = "ItemStatus";
|
||||
|
||||
public Stylesheet Stylesheet { get; }
|
||||
public override Stylesheet Stylesheet { get; }
|
||||
|
||||
public NanoStyle()
|
||||
public StyleNano(IResourceCache resCache) : base(resCache)
|
||||
{
|
||||
var resCache = IoCManager.Resolve<IResourceCache>();
|
||||
var notoSans8 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 8);
|
||||
var notoSans10 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 10);
|
||||
var notoSans12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 12);
|
||||
var notoSansBold12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Bold.ttf", 12);
|
||||
@@ -69,16 +67,10 @@ namespace Content.Client.UserInterface
|
||||
var textureInvertedTriangle = resCache.GetTexture("/Nano/inverted_triangle.svg.png");
|
||||
|
||||
// Button styles.
|
||||
var buttonTex = resCache.GetTexture("/Nano/button.svg.96dpi.png");
|
||||
var buttonNormal = new StyleBoxTexture
|
||||
var buttonNormal = new StyleBoxTexture(BaseButton)
|
||||
{
|
||||
Texture = buttonTex,
|
||||
Modulate = ButtonColorDefault
|
||||
};
|
||||
buttonNormal.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
buttonNormal.SetPadding(StyleBox.Margin.All, 1);
|
||||
buttonNormal.SetContentMarginOverride(StyleBox.Margin.Vertical, 2);
|
||||
buttonNormal.SetContentMarginOverride(StyleBox.Margin.Horizontal, 14);
|
||||
|
||||
var buttonHover = new StyleBoxTexture(buttonNormal)
|
||||
{
|
||||
@@ -248,16 +240,8 @@ namespace Content.Client.UserInterface
|
||||
var sliderFillRed = new StyleBoxTexture(sliderFillBox) {Modulate = Color.Red};
|
||||
var sliderFillBlue = new StyleBoxTexture(sliderFillBox) {Modulate = Color.Blue};
|
||||
|
||||
Stylesheet = new Stylesheet(new[]
|
||||
Stylesheet = new Stylesheet(BaseRules.Concat(new[]
|
||||
{
|
||||
// Default font.
|
||||
new StyleRule(
|
||||
new SelectorElement(null, null, null, null),
|
||||
new[]
|
||||
{
|
||||
new StyleProperty("font", notoSans12),
|
||||
}),
|
||||
|
||||
// Window title.
|
||||
new StyleRule(
|
||||
new SelectorElement(typeof(Label), new[] {SS14Window.StyleClassWindowTitle}, null, null),
|
||||
@@ -721,7 +705,12 @@ namespace Content.Client.UserInterface
|
||||
{
|
||||
new StyleProperty(Label.StylePropertyAlignMode, Label.AlignMode.Center),
|
||||
}),
|
||||
});
|
||||
|
||||
new StyleRule(new SelectorElement(typeof(PanelContainer), new []{ ClassHighDivider}, null, null), new []
|
||||
{
|
||||
new StyleProperty(PanelContainer.StylePropertyPanel, new StyleBoxFlat { BackgroundColor = NanoGold, ContentMarginBottomOverride = 2, ContentMarginLeftOverride = 2}),
|
||||
})
|
||||
}).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Content.Client/UserInterface/Stylesheets/StyleSpace.cs
Normal file
107
Content.Client/UserInterface/Stylesheets/StyleSpace.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Utility;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.UserInterface.Stylesheets
|
||||
{
|
||||
public class StyleSpace : StyleBase
|
||||
{
|
||||
public static readonly Color SpaceRed = Color.FromHex("#9b2236");
|
||||
|
||||
public static readonly Color ButtonColorDefault = Color.FromHex("#464966");
|
||||
public static readonly Color ButtonColorHovered = Color.FromHex("#575b7f");
|
||||
public static readonly Color ButtonColorPressed = Color.FromHex("#3e6c45");
|
||||
public static readonly Color ButtonColorDisabled = Color.FromHex("#30313c");
|
||||
|
||||
public override Stylesheet Stylesheet { get; }
|
||||
|
||||
public StyleSpace(IResourceCache resCache) : base(resCache)
|
||||
{
|
||||
var notoSans10 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 10);
|
||||
var notoSansBold16 = resCache.GetFont("/Nano/NotoSans/NotoSans-Bold.ttf", 16);
|
||||
|
||||
// Button styles.
|
||||
var buttonNormal = new StyleBoxTexture(BaseButton)
|
||||
{
|
||||
Modulate = ButtonColorDefault
|
||||
};
|
||||
|
||||
var buttonHover = new StyleBoxTexture(buttonNormal)
|
||||
{
|
||||
Modulate = ButtonColorHovered
|
||||
};
|
||||
|
||||
var buttonPressed = new StyleBoxTexture(buttonNormal)
|
||||
{
|
||||
Modulate = ButtonColorPressed
|
||||
};
|
||||
|
||||
var buttonDisabled = new StyleBoxTexture(buttonNormal)
|
||||
{
|
||||
Modulate = ButtonColorDisabled
|
||||
};
|
||||
|
||||
|
||||
Stylesheet = new Stylesheet(BaseRules.Concat(new StyleRule[]
|
||||
{
|
||||
// Big Label
|
||||
new StyleRule(new SelectorElement(typeof(Label), new[] {StyleClassLabelHeading}, null, null), new[]
|
||||
{
|
||||
new StyleProperty(Label.StylePropertyFont, notoSansBold16),
|
||||
new StyleProperty(Label.StylePropertyFontColor, SpaceRed),
|
||||
}),
|
||||
|
||||
// Small Label
|
||||
new StyleRule(new SelectorElement(typeof(Label), new[] {StyleClassLabelSubText}, null, null), new[]
|
||||
{
|
||||
new StyleProperty(Label.StylePropertyFont, notoSans10),
|
||||
new StyleProperty(Label.StylePropertyFontColor, Color.DarkGray),
|
||||
}),
|
||||
|
||||
new StyleRule(new SelectorElement(typeof(PanelContainer), new[] {ClassHighDivider}, null, null), new[]
|
||||
{
|
||||
new StyleProperty(PanelContainer.StylePropertyPanel,
|
||||
new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = SpaceRed, ContentMarginBottomOverride = 2, ContentMarginLeftOverride = 2
|
||||
}),
|
||||
}),
|
||||
|
||||
// Regular buttons!
|
||||
new StyleRule(new SelectorElement(typeof(ContainerButton), new[] { ContainerButton.StyleClassButton }, null, new[] {ContainerButton.StylePseudoClassNormal}), new[]
|
||||
{
|
||||
new StyleProperty(ContainerButton.StylePropertyStyleBox, buttonNormal),
|
||||
}),
|
||||
new StyleRule(new SelectorElement(typeof(ContainerButton), new[] { ContainerButton.StyleClassButton }, null, new[] {ContainerButton.StylePseudoClassHover}), new[]
|
||||
{
|
||||
new StyleProperty(ContainerButton.StylePropertyStyleBox, buttonHover),
|
||||
}),
|
||||
new StyleRule(new SelectorElement(typeof(ContainerButton), new[] { ContainerButton.StyleClassButton }, null, new[] {ContainerButton.StylePseudoClassPressed}), new[]
|
||||
{
|
||||
new StyleProperty(ContainerButton.StylePropertyStyleBox, buttonPressed),
|
||||
}),
|
||||
new StyleRule(new SelectorElement(typeof(ContainerButton), new[] { ContainerButton.StyleClassButton }, null, new[] {ContainerButton.StylePseudoClassDisabled}), new[]
|
||||
{
|
||||
new StyleProperty(ContainerButton.StylePropertyStyleBox, buttonDisabled),
|
||||
}),
|
||||
|
||||
new StyleRule(new SelectorElement(typeof(Label), new[] { Button.StyleClassButton }, null, null), new[]
|
||||
{
|
||||
new StyleProperty(Label.StylePropertyAlignMode, Label.AlignMode.Center),
|
||||
}),
|
||||
|
||||
new StyleRule(new SelectorChild(
|
||||
new SelectorElement(typeof(Button), null, null, new[] {ContainerButton.StylePseudoClassDisabled}),
|
||||
new SelectorElement(typeof(Label), null, null, null)),
|
||||
new[]
|
||||
{
|
||||
new StyleProperty("font-color", Color.FromHex("#E5E5E581")),
|
||||
}),
|
||||
}).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.UserInterface.Stylesheets
|
||||
{
|
||||
public sealed class StylesheetManager : IStylesheetManager
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
|
||||
[Dependency] private readonly IResourceCache _resourceCache;
|
||||
#pragma warning restore 649
|
||||
|
||||
public Stylesheet SheetNano { get; private set; }
|
||||
public Stylesheet SheetSpace { get; private set; }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
SheetNano = new StyleNano(_resourceCache).Stylesheet;
|
||||
SheetSpace = new StyleSpace(_resourceCache).Stylesheet;
|
||||
|
||||
_userInterfaceManager.Stylesheet = SheetNano;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using Content.Server.GameTicking;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Content.Shared;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.IntegrationTests
|
||||
@@ -54,6 +55,10 @@ namespace Content.IntegrationTests
|
||||
{
|
||||
}
|
||||
|
||||
public GridCoordinates GetLateJoinSpawnPoint() => GridCoordinates.InvalidGrid;
|
||||
public GridCoordinates GetJobSpawnPoint(string jobId) => GridCoordinates.InvalidGrid;
|
||||
public GridCoordinates GetObserverSpawnPoint() => GridCoordinates.InvalidGrid;
|
||||
|
||||
public T AddGameRule<T>() where T : GameRule, new()
|
||||
{
|
||||
return new T();
|
||||
|
||||
81
Content.IntegrationTests/Tests/EntityTest.cs
Normal file
81
Content.IntegrationTests/Tests/EntityTest.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Server.Interfaces.Maps;
|
||||
using Robust.Server.Interfaces.Timing;
|
||||
|
||||
namespace Content.IntegrationTests.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(Robust.Shared.GameObjects.Entity))]
|
||||
public class EntityTest : ContentIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
var server = StartServerDummyTicker();
|
||||
await server.WaitIdleAsync();
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var entityMan = server.ResolveDependency<IEntityManager>();
|
||||
var prototypeMan = server.ResolveDependency<IPrototypeManager>();
|
||||
var mapLoader = server.ResolveDependency<IMapLoader>();
|
||||
var pauseMan = server.ResolveDependency<IPauseManager>();
|
||||
var prototypes = new List<EntityPrototype>();
|
||||
IMapGrid grid = default;
|
||||
IEntity testEntity = null;
|
||||
|
||||
//Build up test environment
|
||||
server.Post(() =>
|
||||
{
|
||||
var mapId = mapMan.CreateMap();
|
||||
pauseMan.AddUninitializedMap(mapId);
|
||||
grid = mapLoader.LoadBlueprint(mapId, "Maps/stationstation.yml");
|
||||
});
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
var testLocation = new GridCoordinates(new Robust.Shared.Maths.Vector2(0, 0), grid);
|
||||
|
||||
//Generate list of non-abstract prototypes to test
|
||||
foreach (var prototype in prototypeMan.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (prototype.Abstract)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
prototypes.Add(prototype);
|
||||
}
|
||||
|
||||
//Iterate list of prototypes to spawn
|
||||
foreach (var prototype in prototypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogS(LogLevel.Debug, "EntityTest", "Testing: " + prototype.Name);
|
||||
testEntity = entityMan.SpawnEntity(prototype.ID, testLocation);
|
||||
server.RunTicks(2);
|
||||
Assert.That(testEntity.Initialized);
|
||||
entityMan.DeleteEntity(testEntity.Uid);
|
||||
}
|
||||
|
||||
//Fail any exceptions thrown on spawn
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogS(LogLevel.Error, "EntityTest", "Entity '" + testEntity.Name + "' threw: " + e.Message);
|
||||
Assert.Fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
using Content.Server.Players;
|
||||
using System.Timers;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.Players;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
|
||||
namespace Content.Server.Administration
|
||||
{
|
||||
@@ -30,10 +33,14 @@ namespace Content.Server.Administration
|
||||
}
|
||||
else
|
||||
{
|
||||
var canReturn = mind.CurrentEntity != null && !mind.CurrentEntity.HasComponent<GhostComponent>();
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
var ghost = entityManager.SpawnEntity("AdminObserver", player.AttachedEntity.Transform.GridPosition);
|
||||
|
||||
mind.Visit(ghost);
|
||||
if(canReturn)
|
||||
mind.Visit(ghost);
|
||||
else
|
||||
mind.TransferTo(ghost);
|
||||
ghost.GetComponent<GhostComponent>().CanReturnToBody = canReturn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.Mobs;
|
||||
using Content.Server.Players;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
@@ -55,9 +56,14 @@ namespace Content.Server.Administration
|
||||
return;
|
||||
}
|
||||
|
||||
var oldEntity = mind.CurrentEntity;
|
||||
|
||||
mindComponent.Mind?.TransferTo(null);
|
||||
mind.TransferTo(target);
|
||||
|
||||
if(oldEntity.HasComponent<GhostComponent>())
|
||||
oldEntity.Delete();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Content.Server.Interfaces.Chat;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.Interfaces.Chat;
|
||||
using Content.Server.Observer;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Enums;
|
||||
@@ -24,7 +26,10 @@ namespace Content.Server.Chat
|
||||
|
||||
var message = string.Join(" ", args);
|
||||
|
||||
chat.EntitySay(player.AttachedEntity, message);
|
||||
if (player.AttachedEntity.HasComponent<GhostComponent>())
|
||||
chat.SendDeadChat(player, message);
|
||||
else
|
||||
chat.EntitySay(player.AttachedEntity, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.Chat;
|
||||
using Content.Server.Observer;
|
||||
using Content.Server.Players;
|
||||
using Content.Shared.Chat;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Content.Server.Chat
|
||||
{
|
||||
@@ -20,6 +25,7 @@ namespace Content.Server.Chat
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNetManager _netManager;
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
[Dependency] private readonly IMoMMILink _mommiLink;
|
||||
#pragma warning restore 649
|
||||
|
||||
@@ -93,6 +99,18 @@ namespace Content.Server.Chat
|
||||
_mommiLink.SendOOCMessage(player.SessionId.ToString(), message);
|
||||
}
|
||||
|
||||
public void SendDeadChat(IPlayerSession player, string message)
|
||||
{
|
||||
var clients = _playerManager.GetPlayersBy(x => x.AttachedEntity != null && x.AttachedEntity.HasComponent<GhostComponent>()).Select(p => p.ConnectedClient);;
|
||||
|
||||
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
|
||||
msg.Channel = ChatChannel.Dead;
|
||||
msg.Message = message;
|
||||
msg.MessageWrap = $"{_localizationManager.GetString("DEAD")}: {player.AttachedEntity.Name}: {{0}}";
|
||||
msg.SenderEntity = player.AttachedEntityUid.GetValueOrDefault();
|
||||
_netManager.ServerSendToMany(msg, clients.ToList());
|
||||
}
|
||||
|
||||
public void SendHookOOC(string sender, string message)
|
||||
{
|
||||
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Content.Server.GameObjects.Components.Nutrition;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Interfaces.Chemistry;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
@@ -14,8 +15,8 @@ namespace Content.Server.Chemistry.Metabolism
|
||||
class DefaultDrink : IMetabolizable
|
||||
{
|
||||
//Rate of metabolism in units / second
|
||||
private int _metabolismRate;
|
||||
public int MetabolismRate => _metabolismRate;
|
||||
private ReagentUnit _metabolismRate;
|
||||
public ReagentUnit MetabolismRate => _metabolismRate;
|
||||
|
||||
//How much thirst is satiated when 1u of the reagent is metabolized
|
||||
private float _hydrationFactor;
|
||||
@@ -23,16 +24,16 @@ namespace Content.Server.Chemistry.Metabolism
|
||||
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _metabolismRate, "rate", 1);
|
||||
serializer.DataField(ref _metabolismRate, "rate", ReagentUnit.New(1));
|
||||
serializer.DataField(ref _hydrationFactor, "nutrimentFactor", 30.0f);
|
||||
}
|
||||
|
||||
//Remove reagent at set rate, satiate thirst if a ThirstComponent can be found
|
||||
int IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime)
|
||||
ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime)
|
||||
{
|
||||
int metabolismAmount = (int)Math.Round(MetabolismRate * tickTime);
|
||||
var metabolismAmount = MetabolismRate * tickTime;
|
||||
if (solutionEntity.TryGetComponent(out ThirstComponent thirst))
|
||||
thirst.UpdateThirst(metabolismAmount * HydrationFactor);
|
||||
thirst.UpdateThirst(metabolismAmount.Float() * HydrationFactor);
|
||||
|
||||
//Return amount of reagent to be removed, remove reagent regardless of ThirstComponent presence
|
||||
return metabolismAmount;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using Content.Server.GameObjects.Components.Nutrition;
|
||||
using Content.Server.GameObjects.Components.Nutrition;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Interfaces.Chemistry;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Chemistry.Metabolism
|
||||
@@ -14,8 +15,8 @@ namespace Content.Server.Chemistry.Metabolism
|
||||
class DefaultFood : IMetabolizable
|
||||
{
|
||||
//Rate of metabolism in units / second
|
||||
private int _metabolismRate;
|
||||
public int MetabolismRate => _metabolismRate;
|
||||
private ReagentUnit _metabolismRate;
|
||||
public ReagentUnit MetabolismRate => _metabolismRate;
|
||||
|
||||
//How much hunger is satiated when 1u of the reagent is metabolized
|
||||
private float _nutritionFactor;
|
||||
@@ -23,16 +24,16 @@ namespace Content.Server.Chemistry.Metabolism
|
||||
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _metabolismRate, "rate", 1);
|
||||
serializer.DataField(ref _metabolismRate, "rate", ReagentUnit.New(1M));
|
||||
serializer.DataField(ref _nutritionFactor, "nutrimentFactor", 30.0f);
|
||||
}
|
||||
|
||||
//Remove reagent at set rate, satiate hunger if a HungerComponent can be found
|
||||
int IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime)
|
||||
ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime)
|
||||
{
|
||||
int metabolismAmount = (int)Math.Round(MetabolismRate * tickTime);
|
||||
var metabolismAmount = MetabolismRate * tickTime;
|
||||
if (solutionEntity.TryGetComponent(out HungerComponent hunger))
|
||||
hunger.UpdateFood(metabolismAmount * NutritionFactor);
|
||||
hunger.UpdateFood(metabolismAmount.Float() * NutritionFactor);
|
||||
|
||||
//Return amount of reagent to be removed, remove reagent regardless of HungerComponent presence
|
||||
return metabolismAmount;
|
||||
|
||||
@@ -35,9 +35,9 @@ namespace Content.Server.Chemistry.ReactionEffects
|
||||
serializer.DataField(ref _maxScale, "maxScale", 1);
|
||||
}
|
||||
|
||||
public void React(IEntity solutionEntity, int intensity)
|
||||
public void React(IEntity solutionEntity, decimal intensity)
|
||||
{
|
||||
float floatIntensity = intensity; //Use float to avoid truncation in scaling
|
||||
float floatIntensity = (float)intensity;
|
||||
if (solutionEntity == null)
|
||||
return;
|
||||
if(!solutionEntity.TryGetComponent(out SolutionComponent solution))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -16,7 +17,7 @@ namespace Content.Server.Chemistry
|
||||
private string _id;
|
||||
private string _name;
|
||||
private Dictionary<string, ReactantPrototype> _reactants;
|
||||
private Dictionary<string, uint> _products;
|
||||
private Dictionary<string, ReagentUnit> _products;
|
||||
private List<IReactionEffect> _effects;
|
||||
|
||||
public string ID => _id;
|
||||
@@ -28,7 +29,7 @@ namespace Content.Server.Chemistry
|
||||
/// <summary>
|
||||
/// Reagents created when the reaction occurs.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, uint> Products => _products;
|
||||
public IReadOnlyDictionary<string, ReagentUnit> Products => _products;
|
||||
/// <summary>
|
||||
/// Effects to be triggered when the reaction occurs.
|
||||
/// </summary>
|
||||
@@ -41,7 +42,7 @@ namespace Content.Server.Chemistry
|
||||
serializer.DataField(ref _id, "id", string.Empty);
|
||||
serializer.DataField(ref _name, "name", string.Empty);
|
||||
serializer.DataField(ref _reactants, "reactants", new Dictionary<string, ReactantPrototype>());
|
||||
serializer.DataField(ref _products, "products", new Dictionary<string, uint>());
|
||||
serializer.DataField(ref _products, "products", new Dictionary<string, ReagentUnit>());
|
||||
serializer.DataField(ref _effects, "effects", new List<IReactionEffect>());
|
||||
}
|
||||
}
|
||||
@@ -51,13 +52,13 @@ namespace Content.Server.Chemistry
|
||||
/// </summary>
|
||||
public class ReactantPrototype : IExposeData
|
||||
{
|
||||
private int _amount;
|
||||
private ReagentUnit _amount;
|
||||
private bool _catalyst;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum amount of the reactant needed for the reaction to occur.
|
||||
/// </summary>
|
||||
public int Amount => _amount;
|
||||
public ReagentUnit Amount => _amount;
|
||||
/// <summary>
|
||||
/// Whether or not the reactant is a catalyst. Catalysts aren't removed when a reaction occurs.
|
||||
/// </summary>
|
||||
@@ -65,7 +66,7 @@ namespace Content.Server.Chemistry
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _amount, "amount", 1);
|
||||
serializer.DataField(ref _amount, "amount", ReagentUnit.New(1));
|
||||
serializer.DataField(ref _catalyst, "catalyst", false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.Cargo;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Components.Cargo;
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Content.Shared.Prototypes.Cargo;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
@@ -40,6 +41,9 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
|
||||
private bool _requestOnly = false;
|
||||
|
||||
private PowerDeviceComponent _powerDevice;
|
||||
private bool Powered => _powerDevice.Powered;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -47,6 +51,7 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
Orders = Owner.GetComponent<CargoOrderDatabaseComponent>();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>().GetBoundUserInterface(CargoConsoleUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
_powerDevice = Owner.GetComponent<PowerDeviceComponent>();
|
||||
_galacticBankManager.AddComponent(this);
|
||||
BankId = 0;
|
||||
}
|
||||
@@ -66,6 +71,8 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
var message = serverMsg.Message;
|
||||
if (!Orders.ConnectedToDatabase)
|
||||
return;
|
||||
if (!Powered)
|
||||
return;
|
||||
switch (message)
|
||||
{
|
||||
case CargoConsoleAddOrderMessage msg:
|
||||
@@ -119,6 +126,8 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Powered)
|
||||
return;
|
||||
|
||||
_userInterface.Open(actor.playerSession);
|
||||
}
|
||||
|
||||
@@ -37,13 +37,13 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// attempt to inject it's entire contents upon use.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private int _transferAmount;
|
||||
private ReagentUnit _transferAmount;
|
||||
|
||||
/// <summary>
|
||||
/// Initial storage volume of the injector
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private int _initialMaxVolume;
|
||||
private ReagentUnit _initialMaxVolume;
|
||||
|
||||
/// <summary>
|
||||
/// The state of the injector. Determines it's attack behavior. Containers must have the
|
||||
@@ -62,22 +62,14 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _injectOnly, "injectOnly", false);
|
||||
serializer.DataField(ref _initialMaxVolume, "initialMaxVolume", 15);
|
||||
serializer.DataField(ref _transferAmount, "transferAmount", 5);
|
||||
serializer.DataField(ref _initialMaxVolume, "initialMaxVolume", ReagentUnit.New(15));
|
||||
serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(5));
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
//Create and setup internal storage
|
||||
_internalContents = new SolutionComponent();
|
||||
_internalContents.InitializeFromPrototype();
|
||||
_internalContents.Init();
|
||||
_internalContents.MaxVolume = _initialMaxVolume;
|
||||
_internalContents.Owner = Owner; //Manually set owner to avoid crash when VV'ing this
|
||||
base.Startup();
|
||||
_internalContents = Owner.GetComponent<SolutionComponent>();
|
||||
_internalContents.Capabilities |= SolutionCaps.Injector;
|
||||
|
||||
//Set _toggleState based on prototype
|
||||
_toggleState = _injectOnly ? InjectorToggleMode.Inject : InjectorToggleMode.Draw;
|
||||
}
|
||||
@@ -165,7 +157,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
|
||||
//Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
int realTransferAmount = Math.Min(_transferAmount, targetBloodstream.EmptyVolume);
|
||||
var realTransferAmount = ReagentUnit.Min(_transferAmount, targetBloodstream.EmptyVolume);
|
||||
if (realTransferAmount <= 0)
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user,
|
||||
@@ -193,7 +185,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
|
||||
//Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
int realTransferAmount = Math.Min(_transferAmount, targetSolution.EmptyVolume);
|
||||
var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.EmptyVolume);
|
||||
if (realTransferAmount <= 0)
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user,
|
||||
@@ -221,7 +213,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
|
||||
//Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
int realTransferAmount = Math.Min(_transferAmount, targetSolution.CurrentVolume);
|
||||
var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.CurrentVolume);
|
||||
if (realTransferAmount <= 0)
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user,
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Text;
|
||||
using Content.Server.GameObjects.Components.Nutrition;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.Chemistry;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
@@ -28,13 +29,13 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
public override string Name => "Pourable";
|
||||
|
||||
private int _transferAmount;
|
||||
private ReagentUnit _transferAmount;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of solution to be transferred from this solution when clicking on other solutions with it.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public int TransferAmount
|
||||
public ReagentUnit TransferAmount
|
||||
{
|
||||
get => _transferAmount;
|
||||
set => _transferAmount = value;
|
||||
@@ -43,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _transferAmount, "transferAmount", 5);
|
||||
serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(5.0M));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,7 +70,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
return false;
|
||||
|
||||
//Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
int realTransferAmount = Math.Min(attackPourable.TransferAmount, targetSolution.EmptyVolume);
|
||||
var realTransferAmount = ReagentUnit.Min(attackPourable.TransferAmount, targetSolution.EmptyVolume);
|
||||
if (realTransferAmount <= 0) //Special message if container is full
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Sound;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Shared.Chemistry;
|
||||
@@ -29,7 +30,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IAttackBy))]
|
||||
public class ReagentDispenserComponent : SharedReagentDispenserComponent, IActivate, IAttackBy
|
||||
public class ReagentDispenserComponent : SharedReagentDispenserComponent, IActivate, IAttackBy, ISolutionChange
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
@@ -41,11 +42,16 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[ViewVariables] private string _packPrototypeId;
|
||||
|
||||
[ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null;
|
||||
[ViewVariables] private int DispenseAmount = 10;
|
||||
[ViewVariables] private ReagentUnit _dispenseAmount = ReagentUnit.New(10);
|
||||
|
||||
[ViewVariables]
|
||||
private SolutionComponent Solution => _beakerContainer.ContainedEntity.GetComponent<SolutionComponent>();
|
||||
|
||||
///implementing PowerDeviceComponent
|
||||
private PowerDeviceComponent _powerDevice;
|
||||
private bool Powered => _powerDevice.Powered;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Shows the serializer how to save/load this components yaml prototype.
|
||||
/// </summary>
|
||||
@@ -70,6 +76,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
_beakerContainer =
|
||||
ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-reagentContainerContainer", Owner);
|
||||
_powerDevice = Owner.GetComponent<PowerDeviceComponent>();
|
||||
|
||||
InitializeFromPrototype();
|
||||
UpdateUserInterface();
|
||||
@@ -115,22 +122,22 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
TryClear();
|
||||
break;
|
||||
case UiButton.SetDispenseAmount1:
|
||||
DispenseAmount = 1;
|
||||
_dispenseAmount = ReagentUnit.New(1);
|
||||
break;
|
||||
case UiButton.SetDispenseAmount5:
|
||||
DispenseAmount = 5;
|
||||
_dispenseAmount = ReagentUnit.New(5);
|
||||
break;
|
||||
case UiButton.SetDispenseAmount10:
|
||||
DispenseAmount = 10;
|
||||
_dispenseAmount = ReagentUnit.New(10);
|
||||
break;
|
||||
case UiButton.SetDispenseAmount25:
|
||||
DispenseAmount = 25;
|
||||
_dispenseAmount = ReagentUnit.New(25);
|
||||
break;
|
||||
case UiButton.SetDispenseAmount50:
|
||||
DispenseAmount = 50;
|
||||
_dispenseAmount = ReagentUnit.New(50);
|
||||
break;
|
||||
case UiButton.SetDispenseAmount100:
|
||||
DispenseAmount = 100;
|
||||
_dispenseAmount = ReagentUnit.New(100);
|
||||
break;
|
||||
case UiButton.Dispense:
|
||||
if (HasBeaker)
|
||||
@@ -154,11 +161,14 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
private bool PlayerCanUseDispenser(IEntity playerEntity)
|
||||
{
|
||||
//Need player entity to check if they are still able to use the dispenser
|
||||
if (playerEntity == null)
|
||||
if (playerEntity == null)
|
||||
return false;
|
||||
//Check if player can interact in their current state
|
||||
if (!ActionBlockerSystem.CanInteract(playerEntity) || !ActionBlockerSystem.CanUse(playerEntity))
|
||||
return false;
|
||||
//Check if device is powered
|
||||
if (!Powered)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -172,13 +182,13 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
var beaker = _beakerContainer.ContainedEntity;
|
||||
if (beaker == null)
|
||||
{
|
||||
return new ReagentDispenserBoundUserInterfaceState(false, 0, 0,
|
||||
"", Inventory, Owner.Name, null, DispenseAmount);
|
||||
return new ReagentDispenserBoundUserInterfaceState(false, ReagentUnit.New(0), ReagentUnit.New(0),
|
||||
"", Inventory, Owner.Name, null, _dispenseAmount);
|
||||
}
|
||||
|
||||
var solution = beaker.GetComponent<SolutionComponent>();
|
||||
return new ReagentDispenserBoundUserInterfaceState(true, solution.CurrentVolume, solution.MaxVolume,
|
||||
beaker.Name, Inventory, Owner.Name, solution.ReagentList.ToList(), DispenseAmount);
|
||||
beaker.Name, Inventory, Owner.Name, solution.ReagentList.ToList(), _dispenseAmount);
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
@@ -197,7 +207,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
return;
|
||||
|
||||
var beaker = _beakerContainer.ContainedEntity;
|
||||
Solution.SolutionChanged -= HandleSolutionChangedEvent;
|
||||
_beakerContainer.Remove(_beakerContainer.ContainedEntity);
|
||||
UpdateUserInterface();
|
||||
|
||||
@@ -228,7 +237,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
if (!HasBeaker) return;
|
||||
|
||||
var solution = _beakerContainer.ContainedEntity.GetComponent<SolutionComponent>();
|
||||
solution.TryAddReagent(Inventory[dispenseIndex].ID, DispenseAmount, out _);
|
||||
solution.TryAddReagent(Inventory[dispenseIndex].ID, _dispenseAmount, out _);
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
@@ -251,6 +260,9 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Powered)
|
||||
return;
|
||||
|
||||
var activeHandEntity = hands.GetActiveHand?.Owner;
|
||||
if (activeHandEntity == null)
|
||||
{
|
||||
@@ -291,7 +303,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
else
|
||||
{
|
||||
_beakerContainer.Insert(activeHandEntity);
|
||||
Solution.SolutionChanged += HandleSolutionChangedEvent;
|
||||
UpdateUserInterface();
|
||||
}
|
||||
}
|
||||
@@ -304,10 +315,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HandleSolutionChangedEvent()
|
||||
{
|
||||
UpdateUserInterface();
|
||||
}
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => UpdateUserInterface();
|
||||
|
||||
private void ClickSound()
|
||||
{
|
||||
@@ -316,5 +324,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
sound.Play("/Audio/machines/machine_switch.ogg", AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using Content.Server.Chemistry;
|
||||
using Content.Server.GameObjects.Components.Nutrition;
|
||||
using Content.Server.Chemistry;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Chemistry
|
||||
{
|
||||
/// <summary>
|
||||
/// Shared ECS component that manages a liquid solution of reagents.
|
||||
/// ECS component that manages a liquid solution of reagents.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
internal class SolutionComponent : Shared.GameObjects.Components.Chemistry.SolutionComponent, IExamine
|
||||
internal class SolutionComponent : SharedSolutionComponent, IExamine
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
@@ -31,27 +34,180 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
private IEnumerable<ReactionPrototype> _reactions;
|
||||
private AudioSystem _audioSystem;
|
||||
private ChemistrySystem _chemistrySystem;
|
||||
|
||||
private SpriteComponent _spriteComponent;
|
||||
|
||||
private Solution _containedSolution = new Solution();
|
||||
private ReagentUnit _maxVolume;
|
||||
private SolutionCaps _capabilities;
|
||||
private string _fillInitState;
|
||||
private int _fillInitSteps;
|
||||
private string _fillPathString = "Objects/Chemistry/fillings.rsi";
|
||||
private ResourcePath _fillPath;
|
||||
private SpriteSpecifier _fillSprite;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum volume of the container.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => _maxVolume;
|
||||
set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The total volume of all the of the reagents in the container.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public ReagentUnit CurrentVolume => _containedSolution.TotalVolume;
|
||||
|
||||
/// <summary>
|
||||
/// The volume without reagents remaining in the container.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public ReagentUnit EmptyVolume => MaxVolume - CurrentVolume;
|
||||
|
||||
/// <summary>
|
||||
/// The current blended color of all the reagents in the container.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Color SubstanceColor { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current capabilities of this container (is the top open to pour? can I inject it into another object?).
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public SolutionCaps Capabilities
|
||||
{
|
||||
get => _capabilities;
|
||||
set => _capabilities = value;
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public Solution Solution
|
||||
{
|
||||
get => _containedSolution;
|
||||
set => _containedSolution = value;
|
||||
}
|
||||
|
||||
public IReadOnlyList<Solution.ReagentQuantity> ReagentList => _containedSolution.Contents;
|
||||
|
||||
/// <summary>
|
||||
/// Shortcut for Capabilities PourIn flag to avoid binary operators.
|
||||
/// </summary>
|
||||
public bool CanPourIn => (Capabilities & SolutionCaps.PourIn) != 0;
|
||||
/// <summary>
|
||||
/// Shortcut for Capabilities PourOut flag to avoid binary operators.
|
||||
/// </summary>
|
||||
public bool CanPourOut => (Capabilities & SolutionCaps.PourOut) != 0;
|
||||
/// <summary>
|
||||
/// Shortcut for Capabilities Injectable flag
|
||||
/// </summary>
|
||||
public bool Injectable => (Capabilities & SolutionCaps.Injectable) != 0;
|
||||
/// <summary>
|
||||
/// Shortcut for Capabilities Injector flag
|
||||
/// </summary>
|
||||
public bool Injector => (Capabilities & SolutionCaps.Injector) != 0;
|
||||
|
||||
public bool NoExamine => (Capabilities & SolutionCaps.NoExamine) != 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _maxVolume, "maxVol", ReagentUnit.New(0));
|
||||
serializer.DataField(ref _containedSolution, "contents", _containedSolution);
|
||||
serializer.DataField(ref _capabilities, "caps", SolutionCaps.None);
|
||||
serializer.DataField(ref _fillInitState, "fillingState", "");
|
||||
serializer.DataField(ref _fillInitSteps, "fillingSteps", 7);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_audioSystem = _entitySystemManager.GetEntitySystem<AudioSystem>();
|
||||
_chemistrySystem = _entitySystemManager.GetEntitySystem<ChemistrySystem>();
|
||||
_reactions = _prototypeManager.EnumeratePrototypes<ReactionPrototype>();
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
Init();
|
||||
RecalculateColor();
|
||||
if (!string.IsNullOrEmpty(_fillInitState))
|
||||
{
|
||||
_spriteComponent = Owner.GetComponent<SpriteComponent>();
|
||||
_fillPath = new ResourcePath(_fillPathString);
|
||||
_fillSprite = new SpriteSpecifier.Rsi(_fillPath, _fillInitState + (_fillInitSteps - 1));
|
||||
_spriteComponent.AddLayerWithSprite(_fillSprite);
|
||||
UpdateFillIcon();
|
||||
}
|
||||
}
|
||||
|
||||
public void Init()
|
||||
public void RemoveAllSolution()
|
||||
{
|
||||
_reactions = _prototypeManager.EnumeratePrototypes<ReactionPrototype>();
|
||||
_audioSystem = _entitySystemManager.GetEntitySystem<AudioSystem>();
|
||||
_containedSolution.RemoveAllSolution();
|
||||
OnSolutionChanged(false);
|
||||
}
|
||||
|
||||
public bool TryRemoveReagent(string reagentId, ReagentUnit quantity)
|
||||
{
|
||||
if (!ContainsReagent(reagentId, out var currentQuantity)) return false;
|
||||
|
||||
_containedSolution.RemoveReagent(reagentId, quantity);
|
||||
OnSolutionChanged(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the SolutionComponent if it doesn't have an owner
|
||||
/// Attempt to remove the specified quantity from this solution
|
||||
/// </summary>
|
||||
public void InitializeFromPrototype()
|
||||
/// <param name="quantity">Quantity of this solution to remove</param>
|
||||
/// <returns>Whether or not the solution was successfully removed</returns>
|
||||
public bool TryRemoveSolution(ReagentUnit quantity)
|
||||
{
|
||||
// Because Initialize needs an Owner, Startup isn't called, etc.
|
||||
IoCManager.InjectDependencies(this);
|
||||
_reactions = _prototypeManager.EnumeratePrototypes<ReactionPrototype>();
|
||||
if (CurrentVolume == 0)
|
||||
return false;
|
||||
|
||||
_containedSolution.RemoveSolution(quantity);
|
||||
OnSolutionChanged(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public Solution SplitSolution(ReagentUnit quantity)
|
||||
{
|
||||
var solutionSplit = _containedSolution.SplitSolution(quantity);
|
||||
OnSolutionChanged(false);
|
||||
return solutionSplit;
|
||||
}
|
||||
|
||||
protected void RecalculateColor()
|
||||
{
|
||||
if (_containedSolution.TotalVolume == 0)
|
||||
{
|
||||
SubstanceColor = Color.Transparent;
|
||||
return;
|
||||
}
|
||||
|
||||
Color mixColor = default;
|
||||
var runningTotalQuantity = ReagentUnit.New(0);
|
||||
|
||||
foreach (var reagent in _containedSolution)
|
||||
{
|
||||
runningTotalQuantity += reagent.Quantity;
|
||||
|
||||
if(!_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype proto))
|
||||
continue;
|
||||
if (mixColor == default)
|
||||
mixColor = proto.SubstanceColor;
|
||||
mixColor = Color.InterpolateBetween(mixColor, proto.SubstanceColor,
|
||||
(1 / runningTotalQuantity.Float()) * reagent.Quantity.Float());
|
||||
}
|
||||
|
||||
SubstanceColor = mixColor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -105,8 +261,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
if ((handSolutionComp.Capabilities & SolutionCaps.PourOut) == 0 || (component.Capabilities & SolutionCaps.PourIn) == 0)
|
||||
return;
|
||||
|
||||
var transferQuantity = Math.Min(component.MaxVolume - component.CurrentVolume, handSolutionComp.CurrentVolume);
|
||||
transferQuantity = Math.Min(transferQuantity, 10);
|
||||
var transferQuantity = ReagentUnit.Min(component.MaxVolume - component.CurrentVolume, handSolutionComp.CurrentVolume, ReagentUnit.New(10));
|
||||
|
||||
// nothing to transfer
|
||||
if (transferQuantity <= 0)
|
||||
@@ -120,7 +275,16 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
void IExamine.Examine(FormattedMessage message)
|
||||
{
|
||||
if (NoExamine)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
message.AddText(_loc.GetString("Contains:\n"));
|
||||
if (ReagentList.Count == 0)
|
||||
{
|
||||
message.AddText("Nothing.\n");
|
||||
}
|
||||
foreach (var reagent in ReagentList)
|
||||
{
|
||||
if (_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype proto))
|
||||
@@ -185,8 +349,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
if ((handSolutionComp.Capabilities & SolutionCaps.PourIn) == 0 || (component.Capabilities & SolutionCaps.PourOut) == 0)
|
||||
return;
|
||||
|
||||
var transferQuantity = Math.Min(handSolutionComp.MaxVolume - handSolutionComp.CurrentVolume, component.CurrentVolume);
|
||||
transferQuantity = Math.Min(transferQuantity, 10);
|
||||
var transferQuantity = ReagentUnit.Min(handSolutionComp.MaxVolume - handSolutionComp.CurrentVolume, component.CurrentVolume, ReagentUnit.New(10));
|
||||
|
||||
// pulling from an empty container, pointless to continue
|
||||
if (transferQuantity <= 0)
|
||||
@@ -202,10 +365,11 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
bool checkForNewReaction = false;
|
||||
while (true)
|
||||
{
|
||||
//TODO: make a hashmap at startup and then look up reagents in the contents for a reaction
|
||||
//Check the solution for every reaction
|
||||
foreach (var reaction in _reactions)
|
||||
{
|
||||
if (SolutionValidReaction(reaction, out int unitReactions))
|
||||
if (SolutionValidReaction(reaction, out var unitReactions))
|
||||
{
|
||||
PerformReaction(reaction, unitReactions);
|
||||
checkForNewReaction = true;
|
||||
@@ -223,11 +387,12 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryAddReagent(string reagentId, int quantity, out int acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false)
|
||||
public bool TryAddReagent(string reagentId, ReagentUnit quantity, out ReagentUnit acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false)
|
||||
{
|
||||
if (quantity > _maxVolume - _containedSolution.TotalVolume)
|
||||
var toAcceptQuantity = MaxVolume - _containedSolution.TotalVolume;
|
||||
if (quantity > toAcceptQuantity)
|
||||
{
|
||||
acceptedQuantity = _maxVolume - _containedSolution.TotalVolume;
|
||||
acceptedQuantity = toAcceptQuantity;
|
||||
if (acceptedQuantity == 0) return false;
|
||||
}
|
||||
else
|
||||
@@ -241,13 +406,13 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
if(!skipReactionCheck)
|
||||
CheckForReaction();
|
||||
OnSolutionChanged();
|
||||
OnSolutionChanged(skipColor);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryAddSolution(Solution solution, bool skipReactionCheck = false, bool skipColor = false)
|
||||
{
|
||||
if (solution.TotalVolume > (_maxVolume - _containedSolution.TotalVolume))
|
||||
if (solution.TotalVolume > (MaxVolume - _containedSolution.TotalVolume))
|
||||
return false;
|
||||
|
||||
_containedSolution.AddSolution(solution);
|
||||
@@ -256,7 +421,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
if(!skipReactionCheck)
|
||||
CheckForReaction();
|
||||
OnSolutionChanged();
|
||||
OnSolutionChanged(skipColor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -267,16 +432,16 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// <param name="reaction">The reaction whose reactants will be checked for in the solution.</param>
|
||||
/// <param name="unitReactions">The number of times the reaction can occur with the given solution.</param>
|
||||
/// <returns></returns>
|
||||
private bool SolutionValidReaction(ReactionPrototype reaction, out int unitReactions)
|
||||
private bool SolutionValidReaction(ReactionPrototype reaction, out ReagentUnit unitReactions)
|
||||
{
|
||||
unitReactions = int.MaxValue; //Set to some impossibly large number initially
|
||||
unitReactions = ReagentUnit.MaxValue; //Set to some impossibly large number initially
|
||||
foreach (var reactant in reaction.Reactants)
|
||||
{
|
||||
if (!ContainsReagent(reactant.Key, out int reagentQuantity))
|
||||
if (!ContainsReagent(reactant.Key, out ReagentUnit reagentQuantity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int currentUnitReactions = reagentQuantity / reactant.Value.Amount;
|
||||
var currentUnitReactions = reagentQuantity / reactant.Value.Amount;
|
||||
if (currentUnitReactions < unitReactions)
|
||||
{
|
||||
unitReactions = currentUnitReactions;
|
||||
@@ -299,30 +464,88 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// <param name="solution">Solution to be reacted.</param>
|
||||
/// <param name="reaction">Reaction to occur.</param>
|
||||
/// <param name="unitReactions">The number of times to cause this reaction.</param>
|
||||
private void PerformReaction(ReactionPrototype reaction, int unitReactions)
|
||||
private void PerformReaction(ReactionPrototype reaction, ReagentUnit unitReactions)
|
||||
{
|
||||
//Remove non-catalysts
|
||||
foreach (var reactant in reaction.Reactants)
|
||||
{
|
||||
if (!reactant.Value.Catalyst)
|
||||
{
|
||||
int amountToRemove = unitReactions * reactant.Value.Amount;
|
||||
var amountToRemove = unitReactions * reactant.Value.Amount;
|
||||
TryRemoveReagent(reactant.Key, amountToRemove);
|
||||
}
|
||||
}
|
||||
//Add products
|
||||
foreach (var product in reaction.Products)
|
||||
{
|
||||
TryAddReagent(product.Key, (int)(unitReactions * product.Value), out int acceptedQuantity, true);
|
||||
TryAddReagent(product.Key, product.Value * unitReactions, out var acceptedQuantity, true);
|
||||
}
|
||||
//Trigger reaction effects
|
||||
foreach (var effect in reaction.Effects)
|
||||
{
|
||||
effect.React(Owner, unitReactions);
|
||||
effect.React(Owner, unitReactions.Decimal());
|
||||
}
|
||||
|
||||
//Play reaction sound client-side
|
||||
_audioSystem.Play("/Audio/effects/chemistry/bubbles.ogg", Owner.Transform.GridPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the solution contains the specified reagent.
|
||||
/// </summary>
|
||||
/// <param name="reagentId">The reagent to check for.</param>
|
||||
/// <param name="quantity">Output the quantity of the reagent if it is contained, 0 if it isn't.</param>
|
||||
/// <returns>Return true if the solution contains the reagent.</returns>
|
||||
public bool ContainsReagent(string reagentId, out ReagentUnit quantity)
|
||||
{
|
||||
foreach (var reagent in _containedSolution.Contents)
|
||||
{
|
||||
if (reagent.ReagentId == reagentId)
|
||||
{
|
||||
quantity = reagent.Quantity;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
quantity = ReagentUnit.New(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetMajorReagentId()
|
||||
{
|
||||
if (_containedSolution.Contents.Count == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
var majorReagent = _containedSolution.Contents.OrderByDescending(reagent => reagent.Quantity).First();;
|
||||
return majorReagent.ReagentId;
|
||||
}
|
||||
|
||||
protected void UpdateFillIcon()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_fillInitState)) return;
|
||||
|
||||
var percentage = (CurrentVolume / MaxVolume).Double();
|
||||
var level = ContentHelpers.RoundToLevels(percentage * 100, 100, _fillInitSteps);
|
||||
|
||||
//Transformed glass uses special fancy sprites so we don't bother
|
||||
if (level == 0 || Owner.TryGetComponent<TransformableContainerComponent>(out var transformableContainerComponent)
|
||||
&& transformableContainerComponent.Transformed)
|
||||
{
|
||||
_spriteComponent.LayerSetColor(1, Color.Transparent);
|
||||
return;
|
||||
}
|
||||
_fillSprite = new SpriteSpecifier.Rsi(_fillPath, _fillInitState+level);
|
||||
_spriteComponent.LayerSetSprite(1, _fillSprite);
|
||||
_spriteComponent.LayerSetColor(1,SubstanceColor);
|
||||
}
|
||||
|
||||
protected virtual void OnSolutionChanged(bool skipColor)
|
||||
{
|
||||
if (!skipColor)
|
||||
RecalculateColor();
|
||||
|
||||
UpdateFillIcon();
|
||||
_chemistrySystem.HandleSolutionChange(Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Chemistry;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Chemistry
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class TransformableContainerComponent : Component, ISolutionChange
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override string Name => "TransformableContainer";
|
||||
|
||||
private bool _transformed = false;
|
||||
public bool Transformed { get => _transformed; }
|
||||
|
||||
private SpriteSpecifier _initialSprite;
|
||||
private string _initialName;
|
||||
private string _initialDescription;
|
||||
private SpriteComponent _sprite;
|
||||
|
||||
private ReagentPrototype _currentReagent;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_sprite = Owner.GetComponent<SpriteComponent>();
|
||||
_initialSprite = new SpriteSpecifier.Rsi(new ResourcePath(_sprite.BaseRSIPath), "icon");
|
||||
_initialName = Owner.Name;
|
||||
_initialDescription = Owner.Description;
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
Owner.GetComponent<SolutionComponent>().Capabilities |= SolutionCaps.FitsInDispenser;;
|
||||
}
|
||||
|
||||
public void CancelTransformation()
|
||||
{
|
||||
_currentReagent = null;
|
||||
_transformed = false;
|
||||
_sprite.LayerSetSprite(0, _initialSprite);
|
||||
Owner.Name = _initialName;
|
||||
Owner.Description = _initialDescription;
|
||||
}
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs)
|
||||
{
|
||||
var solution = eventArgs.Owner.GetComponent<SolutionComponent>();
|
||||
//Transform container into initial state when emptied
|
||||
if (_currentReagent != null && solution.ReagentList.Count == 0)
|
||||
{
|
||||
CancelTransformation();
|
||||
}
|
||||
|
||||
//the biggest reagent in the solution decides the appearance
|
||||
var reagentId = solution.GetMajorReagentId();
|
||||
|
||||
//If biggest reagent didn't changed - don't change anything at all
|
||||
if (_currentReagent != null && _currentReagent.ID == reagentId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Only reagents with spritePath property can change appearance of transformable containers!
|
||||
if (!string.IsNullOrWhiteSpace(reagentId) &&
|
||||
_prototypeManager.TryIndex(reagentId, out ReagentPrototype proto) &&
|
||||
!string.IsNullOrWhiteSpace(proto.SpriteReplacementPath))
|
||||
{
|
||||
var spriteSpec = new SpriteSpecifier.Rsi(new ResourcePath("Objects/Drinks/" + proto.SpriteReplacementPath),"icon");
|
||||
_sprite.LayerSetSprite(0, spriteSpec);
|
||||
Owner.Name = proto.Name + " glass";
|
||||
Owner.Description = proto.Description;
|
||||
_currentReagent = proto;
|
||||
_transformed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Content.Shared.GameObjects.Components.Command;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Command
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class CommunicationsConsoleComponent : SharedCommunicationsConsoleComponent, IActivate
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IEntitySystemManager _entitySystemManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private BoundUserInterface _userInterface;
|
||||
private PowerDeviceComponent _powerDevice;
|
||||
private bool Powered => _powerDevice.Powered;
|
||||
private RoundEndSystem RoundEndSystem => _entitySystemManager.GetEntitySystem<RoundEndSystem>();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>().GetBoundUserInterface(CommunicationsConsoleUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
_powerDevice = Owner.GetComponent<PowerDeviceComponent>();
|
||||
|
||||
RoundEndSystem.OnRoundEndCountdownStarted += UpdateBoundInterface;
|
||||
RoundEndSystem.OnRoundEndCountdownCancelled += UpdateBoundInterface;
|
||||
RoundEndSystem.OnRoundEndCountdownFinished += UpdateBoundInterface;
|
||||
}
|
||||
|
||||
private void UpdateBoundInterface()
|
||||
{
|
||||
_userInterface.SetState(new CommunicationsConsoleInterfaceState(RoundEndSystem.ExpectedCountdownEnd));
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
switch (obj.Message)
|
||||
{
|
||||
case CommunicationsConsoleCallEmergencyShuttleMessage _:
|
||||
RoundEndSystem.RequestRoundEnd();
|
||||
break;
|
||||
|
||||
case CommunicationsConsoleRecallEmergencyShuttleMessage _:
|
||||
RoundEndSystem.CancelRoundEndCountdown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenUserInterface(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Open(session);
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
return;
|
||||
|
||||
if (!Powered)
|
||||
{
|
||||
return;
|
||||
}
|
||||
OpenUserInterface(actor.playerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Content.Server.GameObjects.Components.Stack;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.Construction;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
@@ -14,7 +15,6 @@ using Robust.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.Construction.ConstructionStepMaterial;
|
||||
using static Content.Shared.Construction.ConstructionStepTool;
|
||||
@@ -114,7 +114,7 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
{
|
||||
Sprite.AddLayerWithSprite(prototype.Icon);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,13 @@ namespace Content.Server.GameObjects
|
||||
{
|
||||
if (damageType == DamageType.Total)
|
||||
{
|
||||
throw new ArgumentException("Cannot take damage for DamageType.Total");
|
||||
foreach (DamageType e in Enum.GetValues(typeof(DamageType)))
|
||||
{
|
||||
if (e == damageType) continue;
|
||||
TakeDamage(e, amount, source, sourceMob);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
InitializeDamageType(damageType);
|
||||
|
||||
|
||||
@@ -150,8 +150,8 @@ namespace Content.Server.GameObjects.Components
|
||||
if(!entity.Transform.IsMapTransform)
|
||||
continue;
|
||||
|
||||
// only items that can be stored in an inventory, or a player, can be eaten by a locker
|
||||
if(!entity.HasComponent<StoreableComponent>() && !entity.HasComponent<IActorComponent>())
|
||||
// only items that can be stored in an inventory, or a mob, can be eaten by a locker
|
||||
if (!entity.HasComponent<StoreableComponent>() && !entity.HasComponent<SpeciesComponent>())
|
||||
continue;
|
||||
|
||||
if (!AddToContents(entity))
|
||||
@@ -207,7 +207,8 @@ namespace Content.Server.GameObjects.Components
|
||||
private bool AddToContents(IEntity entity)
|
||||
{
|
||||
var collidableComponent = Owner.GetComponent<ICollidableComponent>();
|
||||
if(entity.TryGetComponent<ICollidableComponent>(out var entityCollidableComponent))
|
||||
ICollidableComponent entityCollidableComponent;
|
||||
if (entity.TryGetComponent(out entityCollidableComponent))
|
||||
{
|
||||
if(MaxSize < entityCollidableComponent.WorldAABB.Size.X
|
||||
|| MaxSize < entityCollidableComponent.WorldAABB.Size.Y)
|
||||
@@ -247,6 +248,10 @@ namespace Content.Server.GameObjects.Components
|
||||
}
|
||||
Contents.Insert(entity);
|
||||
entity.Transform.WorldPosition = worldPos;
|
||||
if (entityCollidableComponent != null)
|
||||
{
|
||||
entityCollidableComponent.CollisionEnabled = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -256,7 +261,13 @@ namespace Content.Server.GameObjects.Components
|
||||
{
|
||||
foreach (var contained in Contents.ContainedEntities.ToArray())
|
||||
{
|
||||
Contents.Remove(contained);
|
||||
if(Contents.Remove(contained))
|
||||
{
|
||||
if (contained.TryGetComponent<ICollidableComponent>(out var entityCollidableComponent))
|
||||
{
|
||||
entityCollidableComponent.CollisionEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,5 +38,6 @@ namespace Content.Server.GameObjects.Components.Markers
|
||||
Unset = 0,
|
||||
LateJoin,
|
||||
Job,
|
||||
Observer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Medical;
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
@@ -24,6 +25,10 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
private readonly Vector2 _ejectOffset = new Vector2(-0.5f, 0f);
|
||||
public bool IsOccupied => _bodyContainer.ContainedEntity != null;
|
||||
|
||||
///implementing PowerDeviceComponent
|
||||
private PowerDeviceComponent _powerDevice;
|
||||
private bool Powered => _powerDevice.Powered;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -31,6 +36,7 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(MedicalScannerUiKey.Key);
|
||||
_bodyContainer = ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-bodyContainer", Owner);
|
||||
_powerDevice = Owner.GetComponent<PowerDeviceComponent>();
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
@@ -79,6 +85,8 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
if (!Powered)
|
||||
return;
|
||||
var newState = GetUserInterfaceState();
|
||||
_userInterface.SetState(newState);
|
||||
}
|
||||
@@ -112,6 +120,8 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Powered)
|
||||
return;
|
||||
_userInterface.Open(actor.playerSession);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,29 +34,24 @@ namespace Content.Server.GameObjects.Components.Metabolism
|
||||
/// Max volume of internal solution storage
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private int _initialMaxVolume;
|
||||
private ReagentUnit _initialMaxVolume;
|
||||
|
||||
/// <summary>
|
||||
/// Empty volume of internal solution
|
||||
/// </summary>
|
||||
public int EmptyVolume => _internalSolution.EmptyVolume;
|
||||
public ReagentUnit EmptyVolume => _internalSolution.EmptyVolume;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _initialMaxVolume, "maxVolume", 250);
|
||||
serializer.DataField(ref _initialMaxVolume, "maxVolume", ReagentUnit.New(250));
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
//Create and setup internal solution storage
|
||||
_internalSolution = new SolutionComponent();
|
||||
_internalSolution.InitializeFromPrototype();
|
||||
_internalSolution.Init();
|
||||
base.Startup();
|
||||
_internalSolution = Owner.GetComponent<SolutionComponent>();
|
||||
_internalSolution.MaxVolume = _initialMaxVolume;
|
||||
_internalSolution.Owner = Owner; //Manually set owner to avoid crash when VV'ing this
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -98,7 +93,7 @@ namespace Content.Server.GameObjects.Components.Metabolism
|
||||
//Run metabolism code for each reagent
|
||||
foreach (var metabolizable in proto.Metabolism)
|
||||
{
|
||||
int reagentDelta = metabolizable.Metabolize(Owner, reagent.ReagentId, tickTime);
|
||||
var reagentDelta = metabolizable.Metabolize(Owner, reagent.ReagentId, tickTime);
|
||||
_internalSolution.TryRemoveReagent(reagent.ReagentId, reagentDelta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Mobs;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.Mobs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -46,13 +47,31 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
Mind = value;
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.OnRemove();
|
||||
base.Shutdown();
|
||||
|
||||
if (HasMind)
|
||||
{
|
||||
Mind.TransferTo(null);
|
||||
var visiting = Mind.VisitingEntity;
|
||||
if (visiting != null)
|
||||
{
|
||||
if (visiting.TryGetComponent(out GhostComponent ghost))
|
||||
{
|
||||
ghost.CanReturnToBody = false;
|
||||
}
|
||||
|
||||
Mind.TransferTo(visiting);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ghost = Owner.EntityManager.SpawnEntity("MobObserver", Owner.Transform.GridPosition);
|
||||
ghost.Name = Mind.CharacterName;
|
||||
|
||||
var ghostComponent = ghost.GetComponent<GhostComponent>();
|
||||
ghostComponent.CanReturnToBody = false;
|
||||
Mind.TransferTo(ghost);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@ using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Movement;
|
||||
using Content.Server.Observer;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
@@ -15,7 +18,7 @@ using Robust.Shared.Serialization;
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class SpeciesComponent : SharedSpeciesComponent, IActionBlocker, IOnDamageBehavior, IExAct
|
||||
public class SpeciesComponent : SharedSpeciesComponent, IActionBlocker, IOnDamageBehavior, IExAct, IRelayMoveInput
|
||||
{
|
||||
/// <summary>
|
||||
/// Damagestates are reached by reaching a certain damage threshold, they will block actions after being reached
|
||||
@@ -198,6 +201,14 @@ namespace Content.Server.GameObjects
|
||||
Owner.GetComponent<DamageableComponent>().TakeDamage(DamageType.Brute, bruteDamage, null);
|
||||
Owner.GetComponent<DamageableComponent>().TakeDamage(DamageType.Heat, burnDamage, null);
|
||||
}
|
||||
|
||||
void IRelayMoveInput.MoveInputPressed(IPlayerSession session)
|
||||
{
|
||||
if (CurrentDamageState is DeadState)
|
||||
{
|
||||
new Ghost().Execute(null, session, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -56,18 +56,6 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
serializer.DataField(ref _visionRadius, "vision", 8.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Movement speed (m/s) that the entity walks, before modifiers
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseWalkSpeed { get; set; } = PlayerInputMoverComponent.DefaultBaseWalkSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Movement speed (m/s) that the entity sprints, before modifiers
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseSprintSpeed { get; set; } = PlayerInputMoverComponent.DefaultBaseSprintSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Movement speed (m/s) that the entity walks, after modifiers
|
||||
/// </summary>
|
||||
@@ -76,14 +64,15 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
get
|
||||
{
|
||||
float speed = BaseWalkSpeed;
|
||||
if (Owner.TryGetComponent<MovementSpeedModifierComponent>(out MovementSpeedModifierComponent component))
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent component))
|
||||
{
|
||||
speed *= component.WalkSpeedModifier;
|
||||
return component.CurrentWalkSpeed;
|
||||
}
|
||||
return speed;
|
||||
|
||||
return MovementSpeedModifierComponent.DefaultBaseWalkSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Movement speed (m/s) that the entity walks, after modifiers
|
||||
/// </summary>
|
||||
@@ -92,12 +81,12 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
get
|
||||
{
|
||||
float speed = BaseSprintSpeed;
|
||||
if (Owner.TryGetComponent<MovementSpeedModifierComponent>(out MovementSpeedModifierComponent component))
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent component))
|
||||
{
|
||||
speed *= component.SprintSpeedModifier;
|
||||
return component.CurrentSprintSpeed;
|
||||
}
|
||||
return speed;
|
||||
|
||||
return MovementSpeedModifierComponent.DefaultBaseSprintSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MovementSpeedModifierComponent : Component
|
||||
{
|
||||
public const float DefaultBaseWalkSpeed = 4.0f;
|
||||
public const float DefaultBaseSprintSpeed = 7.0f;
|
||||
|
||||
public override string Name => "MovementSpeedModifier";
|
||||
|
||||
private float _cachedWalkSpeedModifier = 1.0f;
|
||||
[ViewVariables]
|
||||
public float WalkSpeedModifier
|
||||
{
|
||||
get
|
||||
@@ -18,6 +24,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
}
|
||||
}
|
||||
private float _cachedSprintSpeedModifier;
|
||||
[ViewVariables]
|
||||
public float SprintSpeedModifier
|
||||
{
|
||||
get
|
||||
@@ -27,6 +34,16 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseWalkSpeed { get; set; }
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseSprintSpeed { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public float CurrentWalkSpeed => WalkSpeedModifier * BaseWalkSpeed;
|
||||
[ViewVariables]
|
||||
public float CurrentSprintSpeed => SprintSpeedModifier * BaseSprintSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// set to warn us that a component's movespeed modifier has changed
|
||||
/// </summary>
|
||||
@@ -37,6 +54,14 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
_movespeedModifiersNeedRefresh = true;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(this, p => p.BaseWalkSpeed, "baseWalkSpeed", 4);
|
||||
serializer.DataField(this, p => p.BaseSprintSpeed, "baseSprintSpeed", 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculate movement speed with current modifiers, or return early if no change
|
||||
/// </summary>
|
||||
|
||||
@@ -22,9 +22,6 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[ComponentReference(typeof(IMoverComponent))]
|
||||
public class PlayerInputMoverComponent : Component, IMoverComponent, ICollideSpecial
|
||||
{
|
||||
public const float DefaultBaseWalkSpeed = 4.0f;
|
||||
public const float DefaultBaseSprintSpeed = 7.0f;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager;
|
||||
#pragma warning restore 649
|
||||
@@ -37,19 +34,6 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
/// <inheritdoc />
|
||||
public override string Name => "PlayerInputMover";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Movement speed (m/s) that the entity walks, before modifiers
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseWalkSpeed { get; set; } = DefaultBaseWalkSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Movement speed (m/s) that the entity sprints, before modifiers
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseSprintSpeed { get; set; } = DefaultBaseSprintSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Movement speed (m/s) that the entity walks, after modifiers
|
||||
/// </summary>
|
||||
@@ -58,12 +42,12 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
get
|
||||
{
|
||||
float speed = BaseWalkSpeed;
|
||||
if (Owner.TryGetComponent<MovementSpeedModifierComponent>(out MovementSpeedModifierComponent component))
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent component))
|
||||
{
|
||||
speed *= component.WalkSpeedModifier;
|
||||
return component.CurrentWalkSpeed;
|
||||
}
|
||||
return speed;
|
||||
|
||||
return MovementSpeedModifierComponent.DefaultBaseWalkSpeed;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
@@ -74,12 +58,12 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
get
|
||||
{
|
||||
float speed = BaseSprintSpeed;
|
||||
if (Owner.TryGetComponent<MovementSpeedModifierComponent>(out MovementSpeedModifierComponent component))
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent component))
|
||||
{
|
||||
speed *= component.SprintSpeedModifier;
|
||||
return component.CurrentSprintSpeed;
|
||||
}
|
||||
return speed;
|
||||
|
||||
return MovementSpeedModifierComponent.DefaultBaseSprintSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,12 +88,6 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
/// </summary>
|
||||
[ViewVariables] public bool DiagonalMovementEnabled => _configurationManager.GetCVar<bool>("game.diagonalmovement");
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_configurationManager.RegisterCVar("game.diagonalmovement", true, CVar.ARCHIVE);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAdd()
|
||||
{
|
||||
@@ -120,20 +98,6 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
base.OnAdd();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
//only save the base speeds - the current speeds are transient.
|
||||
serializer.DataReadWriteFunction("wspd", DefaultBaseWalkSpeed, value => BaseWalkSpeed = value, () => BaseWalkSpeed);
|
||||
serializer.DataReadWriteFunction("rspd", DefaultBaseSprintSpeed, value => BaseSprintSpeed = value, () => BaseSprintSpeed);
|
||||
|
||||
// The velocity and moving directions is usually set from player or AI input,
|
||||
// so we don't want to save/load these derived fields.
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Toggles one of the four cardinal directions. Each of the four directions are
|
||||
/// composed into a single direction vector, <see cref="VelocityDir"/>. Enabling
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Nutrition;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Maths;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
@@ -32,19 +33,16 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
[ViewVariables]
|
||||
private string _finishPrototype;
|
||||
|
||||
public int TransferAmount => _transferAmount;
|
||||
public ReagentUnit TransferAmount => _transferAmount;
|
||||
[ViewVariables]
|
||||
private int _transferAmount = 2;
|
||||
private ReagentUnit _transferAmount = ReagentUnit.New(2);
|
||||
|
||||
public int MaxVolume
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => _contents.MaxVolume;
|
||||
set => _contents.MaxVolume = value;
|
||||
}
|
||||
|
||||
private Solution _initialContents; // This is just for loading from yaml
|
||||
private int _maxVolume;
|
||||
|
||||
private bool _despawnOnFinish;
|
||||
|
||||
private bool _drinking;
|
||||
@@ -56,60 +54,28 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return Math.Max(1, _contents.CurrentVolume / _transferAmount);
|
||||
return Math.Max(1, (int)Math.Ceiling((_contents.CurrentVolume / _transferAmount).Float()));
|
||||
}
|
||||
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _initialContents, "contents", null);
|
||||
serializer.DataField(ref _maxVolume, "max_volume", 0);
|
||||
serializer.DataField(ref _useSound, "use_sound", "/Audio/items/drink.ogg");
|
||||
// E.g. cola can when done or clear bottle, whatever
|
||||
// Currently this will enforce it has the same volume but this may change.
|
||||
serializer.DataField(ref _despawnOnFinish, "despawn_empty", true);
|
||||
// Currently this will enforce it has the same volume but this may change. - TODO: this should be implemented in a separate component
|
||||
serializer.DataField(ref _despawnOnFinish, "despawn_empty", false);
|
||||
serializer.DataField(ref _finishPrototype, "spawn_on_finish", null);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
if (_contents == null)
|
||||
{
|
||||
if (Owner.TryGetComponent(out SolutionComponent solutionComponent))
|
||||
{
|
||||
_contents = solutionComponent;
|
||||
}
|
||||
else
|
||||
{
|
||||
_contents = Owner.AddComponent<SolutionComponent>();
|
||||
//Ensure SolutionComponent supports click transferring if custom one not set
|
||||
_contents.Capabilities = SolutionCaps.PourIn
|
||||
| SolutionCaps.PourOut
|
||||
| SolutionCaps.Injectable;
|
||||
|
||||
var pourable = Owner.AddComponent<PourableComponent>();
|
||||
pourable.TransferAmount = 5;
|
||||
}
|
||||
}
|
||||
|
||||
_drinking = false;
|
||||
if (_maxVolume != 0)
|
||||
_contents.MaxVolume = _maxVolume;
|
||||
else
|
||||
_contents.MaxVolume = _initialContents.TotalVolume;
|
||||
_contents.SolutionChanged += HandleSolutionChangedEvent;
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
if (_initialContents != null)
|
||||
{
|
||||
_contents.TryAddSolution(_initialContents, true, true);
|
||||
}
|
||||
_initialContents = null;
|
||||
_contents = Owner.GetComponent<SolutionComponent>();
|
||||
_contents.Capabilities = SolutionCaps.PourIn
|
||||
| SolutionCaps.PourOut
|
||||
| SolutionCaps.Injectable;
|
||||
_drinking = false;
|
||||
Owner.TryGetComponent(out AppearanceComponent appearance);
|
||||
_appearanceComponent = appearance;
|
||||
_appearanceComponent?.SetData(SharedFoodComponent.FoodVisuals.MaxUses, MaxVolume);
|
||||
@@ -149,7 +115,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
if (user.TryGetComponent(out StomachComponent stomachComponent))
|
||||
{
|
||||
_drinking = true;
|
||||
var transferAmount = Math.Min(_transferAmount, _contents.CurrentVolume);
|
||||
var transferAmount = ReagentUnit.Min(_transferAmount, _contents.CurrentVolume);
|
||||
var split = _contents.SplitSolution(transferAmount);
|
||||
if (stomachComponent.TryTransferSolution(split))
|
||||
{
|
||||
@@ -167,54 +133,6 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
}
|
||||
_drinking = false;
|
||||
}
|
||||
|
||||
Finish(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger finish behavior in the drink if applicable.
|
||||
/// Depending on the drink this will either delete it,
|
||||
/// or convert it to another entity, like an empty variant.
|
||||
/// </summary>
|
||||
/// <param name="user">The entity that is using the drink</param>
|
||||
public void Finish(IEntity user)
|
||||
{
|
||||
// Drink containers are mostly transient.
|
||||
if (_drinking || !_despawnOnFinish || UsesLeft() > 0)
|
||||
return;
|
||||
|
||||
var gridPos = Owner.Transform.GridPosition;
|
||||
_contents.SolutionChanged -= HandleSolutionChangedEvent;
|
||||
Owner.Delete();
|
||||
|
||||
if (_finishPrototype == null || user == null)
|
||||
return;
|
||||
|
||||
var finisher = Owner.EntityManager.SpawnEntity(_finishPrototype, Owner.Transform.GridPosition);
|
||||
if (user.TryGetComponent(out HandsComponent handsComponent) && finisher.TryGetComponent(out ItemComponent itemComponent))
|
||||
{
|
||||
if (handsComponent.CanPutInHand(itemComponent))
|
||||
{
|
||||
handsComponent.PutInHand(itemComponent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
finisher.Transform.GridPosition = gridPos;
|
||||
if (finisher.TryGetComponent(out DrinkComponent drinkComponent))
|
||||
{
|
||||
drinkComponent.MaxVolume = MaxVolume;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates drink state when the solution is changed by something other
|
||||
/// than this component. Without this some drinks won't properly delete
|
||||
/// themselves without additional clicks/uses after them being emptied.
|
||||
/// </summary>
|
||||
private void HandleSolutionChangedEvent()
|
||||
{
|
||||
Finish(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
[ViewVariables]
|
||||
private SolutionComponent _contents;
|
||||
[ViewVariables]
|
||||
private int _transferAmount;
|
||||
private ReagentUnit _transferAmount;
|
||||
|
||||
private Solution _initialContents; // This is just for loading from yaml
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
serializer.DataField(ref _initialContents, "contents", null);
|
||||
serializer.DataField(ref _useSound, "use_sound", "/Audio/items/eatfood.ogg");
|
||||
// Default is transfer 30 units
|
||||
serializer.DataField(ref _transferAmount, "transfer_amount", 5);
|
||||
serializer.DataField(ref _transferAmount, "transfer_amount", ReagentUnit.New(5));
|
||||
// E.g. empty chip packet when done
|
||||
serializer.DataField(ref _finishPrototype, "spawn_on_finish", null);
|
||||
}
|
||||
@@ -78,7 +78,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
_initialContents = null;
|
||||
if (_contents.CurrentVolume == 0)
|
||||
{
|
||||
_contents.TryAddReagent("chem.Nutriment", 5, out _);
|
||||
_contents.TryAddReagent("chem.Nutriment", ReagentUnit.New(5), out _);
|
||||
}
|
||||
Owner.TryGetComponent(out AppearanceComponent appearance);
|
||||
_appearanceComponent = appearance;
|
||||
@@ -99,7 +99,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return Math.Max(1, _contents.CurrentVolume / _transferAmount);
|
||||
return Math.Max(1, (int)Math.Ceiling((_contents.CurrentVolume / _transferAmount).Float()));
|
||||
}
|
||||
|
||||
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
|
||||
@@ -130,7 +130,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
// TODO: Add putting food back in boxes here?
|
||||
if (user.TryGetComponent(out StomachComponent stomachComponent))
|
||||
{
|
||||
var transferAmount = Math.Min(_transferAmount, _contents.CurrentVolume);
|
||||
var transferAmount = ReagentUnit.Min(_transferAmount, _contents.CurrentVolume);
|
||||
var split = _contents.SplitSolution(transferAmount);
|
||||
if (stomachComponent.TryTransferSolution(split))
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
/// <summary>
|
||||
/// Max volume of internal solution storage
|
||||
/// </summary>
|
||||
public int MaxVolume
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => _stomachContents.MaxVolume;
|
||||
set => _stomachContents.MaxVolume = value;
|
||||
@@ -43,7 +43,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
/// Initial internal solution storage volume
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private int _initialMaxVolume;
|
||||
private ReagentUnit _initialMaxVolume;
|
||||
|
||||
/// <summary>
|
||||
/// Time in seconds between reagents being ingested and them being transferred to <see cref="BloodstreamComponent"/>
|
||||
@@ -64,26 +64,19 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _initialMaxVolume, "maxVolume", 100);
|
||||
serializer.DataField(ref _initialMaxVolume, "maxVolume", ReagentUnit.New(100));
|
||||
serializer.DataField(ref _digestionDelay, "digestionDelay", 20);
|
||||
}
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Initialize();
|
||||
//Doesn't use Owner.AddComponent<>() to avoid cross-contamination (e.g. with blood or whatever they holds other solutions)
|
||||
_stomachContents = new SolutionComponent();
|
||||
_stomachContents.InitializeFromPrototype();
|
||||
_stomachContents = Owner.GetComponent<SolutionComponent>();
|
||||
_stomachContents.MaxVolume = _initialMaxVolume;
|
||||
_stomachContents.Owner = Owner; //Manually set owner to avoid crash when VV'ing this
|
||||
|
||||
//Ensure bloodstream in present
|
||||
if (!Owner.TryGetComponent<BloodstreamComponent>(out _bloodstream))
|
||||
{
|
||||
Logger.Warning(_localizationManager.GetString(
|
||||
"StomachComponent entity does not have a BloodstreamComponent, which is required for it to function. Owner entity name: {0}",
|
||||
Owner.Name));
|
||||
"StomachComponent entity does not have a BloodstreamComponent, which is required for it to function. Owner entity name: {0}",
|
||||
Owner.Name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,10 +134,10 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
private class ReagentDelta
|
||||
{
|
||||
public readonly string ReagentId;
|
||||
public readonly int Quantity;
|
||||
public readonly ReagentUnit Quantity;
|
||||
public float Lifetime { get; private set; }
|
||||
|
||||
public ReagentDelta(string reagentId, int quantity)
|
||||
public ReagentDelta(string reagentId, ReagentUnit quantity)
|
||||
{
|
||||
ReagentId = reagentId;
|
||||
Quantity = quantity;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Players;
|
||||
using Content.Shared.GameObjects.Components.Observer;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Observer
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GhostComponent : SharedGhostComponent, IActionBlocker
|
||||
{
|
||||
private bool _canReturnToBody = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool CanReturnToBody
|
||||
{
|
||||
get => _canReturnToBody;
|
||||
set
|
||||
{
|
||||
_canReturnToBody = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Owner.EnsureComponent<VisibilityComponent>().Layer = (int)VisibilityFlags.Ghost;
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState() => new GhostComponentState(CanReturnToBody);
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
|
||||
IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case ReturnToBodyComponentMessage reenter:
|
||||
if (!Owner.TryGetComponent(out IActorComponent actor) || !CanReturnToBody) break;
|
||||
if (netChannel == null || netChannel == actor.playerSession.ConnectedClient)
|
||||
{
|
||||
actor.playerSession.ContentData().Mind.UnVisit();
|
||||
Owner.Delete();
|
||||
}
|
||||
break;
|
||||
case PlayerAttachedMsg msg:
|
||||
msg.NewPlayer.VisibilityMask |= (int)VisibilityFlags.Ghost;
|
||||
Dirty();
|
||||
break;
|
||||
case PlayerDetachedMsg msg:
|
||||
msg.OldPlayer.VisibilityMask &= ~(int)VisibilityFlags.Ghost;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanInteract() => false;
|
||||
public bool CanUse() => false;
|
||||
public bool CanThrow() => false;
|
||||
public bool CanDrop() => false;
|
||||
public bool CanPickup() => false;
|
||||
public bool CanEmote() => false;
|
||||
public bool CanAttack() => false;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
using System;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Audio;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -24,9 +32,14 @@ namespace Content.Server.GameObjects.Components.Power
|
||||
/// Component that represents a light bulb. Can be broken, or burned, which turns them mostly useless.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class LightBulbComponent : Component
|
||||
public class LightBulbComponent : Component, ILand
|
||||
{
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly IRobustRandom _random;
|
||||
#pragma warning restore 649
|
||||
|
||||
/// <summary>
|
||||
/// Invoked whenever the state of the light bulb changes.
|
||||
/// </summary>
|
||||
@@ -104,5 +117,18 @@ namespace Content.Server.GameObjects.Components.Power
|
||||
base.Initialize();
|
||||
UpdateColor();
|
||||
}
|
||||
|
||||
public void Land(LandEventArgs eventArgs)
|
||||
{
|
||||
if (State == LightBulbState.Broken)
|
||||
return;
|
||||
|
||||
var soundCollection = _prototypeManager.Index<SoundCollectionPrototype>("glassbreak");
|
||||
var file = _random.Pick(soundCollection.PickFiles);
|
||||
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>().Play(file, Owner);
|
||||
|
||||
State = LightBulbState.Broken;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,9 @@ namespace Content.Server.GameObjects.Components.Research
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
{
|
||||
if (!Powered)
|
||||
return;
|
||||
|
||||
switch (message.Message)
|
||||
{
|
||||
case LatheQueueRecipeMessage msg:
|
||||
@@ -87,13 +90,15 @@ namespace Content.Server.GameObjects.Components.Research
|
||||
|
||||
case LatheServerSyncMessage msg:
|
||||
if (!Owner.TryGetComponent(out TechnologyDatabaseComponent database)
|
||||
|| !Owner.TryGetComponent(out ProtolatheDatabaseComponent protoDatabase)) return;
|
||||
|| !Owner.TryGetComponent(out ProtolatheDatabaseComponent protoDatabase)) return;
|
||||
|
||||
if(database.SyncWithServer())
|
||||
if (database.SyncWithServer())
|
||||
protoDatabase.Sync();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal bool Produce(LatheRecipePrototype recipe)
|
||||
|
||||
@@ -46,6 +46,8 @@ namespace Content.Server.GameObjects.Components.Research
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out TechnologyDatabaseComponent database)) return;
|
||||
if (!Powered)
|
||||
return;
|
||||
|
||||
switch (message.Message)
|
||||
{
|
||||
|
||||
@@ -3,11 +3,9 @@ using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timers;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -23,34 +21,19 @@ namespace Content.Server.GameObjects.Components.Stack
|
||||
[Dependency] private readonly ISharedNotifyManager _sharedNotifyManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private const string SerializationCache = "stack";
|
||||
private int _count = 50;
|
||||
private int _maxCount = 50;
|
||||
private bool _throwIndividually = false;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int Count
|
||||
public override int Count
|
||||
{
|
||||
get => _count;
|
||||
get => base.Count;
|
||||
set
|
||||
{
|
||||
_count = value;
|
||||
if (_count <= 0)
|
||||
base.Count = value;
|
||||
|
||||
if (Count <= 0)
|
||||
{
|
||||
Owner.Delete();
|
||||
}
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public int MaxCount
|
||||
{
|
||||
get => _maxCount;
|
||||
private set
|
||||
{
|
||||
_maxCount = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,12 +48,6 @@ namespace Content.Server.GameObjects.Components.Stack
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public int AvailableSpace => MaxCount - Count;
|
||||
|
||||
[ViewVariables]
|
||||
public object StackType { get; private set; }
|
||||
|
||||
public void Add(int amount)
|
||||
{
|
||||
Count += amount;
|
||||
@@ -91,42 +68,6 @@ namespace Content.Server.GameObjects.Components.Stack
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataFieldCached(ref _maxCount, "max", 50);
|
||||
serializer.DataFieldCached(ref _count, "count", MaxCount);
|
||||
|
||||
if (!serializer.Reading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (serializer.TryGetCacheData(SerializationCache, out object stackType))
|
||||
{
|
||||
StackType = stackType;
|
||||
return;
|
||||
}
|
||||
|
||||
if (serializer.TryReadDataFieldCached("stacktype", out string raw))
|
||||
{
|
||||
var refl = IoCManager.Resolve<IReflectionManager>();
|
||||
if (refl.TryParseEnumReference(raw, out var @enum))
|
||||
{
|
||||
stackType = @enum;
|
||||
}
|
||||
else
|
||||
{
|
||||
stackType = raw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stackType = Owner.Prototype.ID;
|
||||
}
|
||||
serializer.SetCacheData(SerializationCache, stackType);
|
||||
StackType = stackType;
|
||||
}
|
||||
|
||||
public bool AttackBy(AttackByEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.AttackWith.TryGetComponent<StackComponent>(out var stack))
|
||||
@@ -175,20 +116,5 @@ namespace Content.Server.GameObjects.Components.Stack
|
||||
"There is [color=lightgray]1[/color] thing in the stack",
|
||||
"There are [color=lightgray]{0}[/color] things in the stack.", Count, Count));
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new StackComponentState(Count, MaxCount);
|
||||
}
|
||||
}
|
||||
|
||||
public enum StackType
|
||||
{
|
||||
Metal,
|
||||
Glass,
|
||||
Cable,
|
||||
Ointment,
|
||||
Brutepack,
|
||||
FloorTileSteel
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ namespace Content.Server.GameObjects.Components.VendingMachines
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Powered)
|
||||
return;
|
||||
|
||||
var wires = Owner.GetComponent<WiresComponent>();
|
||||
if (wires.IsPanelOpen)
|
||||
@@ -121,6 +123,9 @@ namespace Content.Server.GameObjects.Components.VendingMachines
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg)
|
||||
{
|
||||
if (!Powered)
|
||||
return;
|
||||
|
||||
var message = serverMsg.Message;
|
||||
switch (message)
|
||||
{
|
||||
|
||||
@@ -5,23 +5,23 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
public interface IActionBlocker
|
||||
{
|
||||
bool CanMove();
|
||||
bool CanMove() => true;
|
||||
|
||||
bool CanInteract();
|
||||
bool CanInteract() => true;
|
||||
|
||||
bool CanUse();
|
||||
bool CanUse() => true;
|
||||
|
||||
bool CanThrow();
|
||||
bool CanThrow() => true;
|
||||
|
||||
bool CanSpeak();
|
||||
bool CanSpeak() => true;
|
||||
|
||||
bool CanDrop();
|
||||
bool CanDrop() => true;
|
||||
|
||||
bool CanPickup();
|
||||
bool CanPickup() => true;
|
||||
|
||||
bool CanEmote();
|
||||
bool CanEmote() => true;
|
||||
|
||||
bool CanAttack();
|
||||
bool CanAttack() => true;
|
||||
}
|
||||
|
||||
public class ActionBlockerSystem : EntitySystem
|
||||
|
||||
42
Content.Server/GameObjects/EntitySystems/ChemistrySystem.cs
Normal file
42
Content.Server/GameObjects/EntitySystems/ChemistrySystem.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface gives components behavior on whether entities solution (implying SolutionComponent is in place) is changed
|
||||
/// </summary>
|
||||
public interface ISolutionChange
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when solution is mixed with some other solution, or when some part of the solution is removed
|
||||
/// </summary>
|
||||
void SolutionChanged(SolutionChangeEventArgs eventArgs);
|
||||
}
|
||||
|
||||
public class SolutionChangeEventArgs : EventArgs
|
||||
{
|
||||
public IEntity Owner { get; set; }
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public class ChemistrySystem : EntitySystem
|
||||
{
|
||||
public void HandleSolutionChange(IEntity owner)
|
||||
{
|
||||
var eventArgs = new SolutionChangeEventArgs
|
||||
{
|
||||
Owner = owner,
|
||||
};
|
||||
var solutionChangeArgs = owner.GetAllComponents<ISolutionChange>().ToList();
|
||||
|
||||
foreach (var solutionChangeArg in solutionChangeArgs)
|
||||
{
|
||||
solutionChangeArg.SolutionChanged(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,22 @@ using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.Components.Sound;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Movement;
|
||||
using Content.Server.Observer;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.Maps;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Server.Interfaces.Timing;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
@@ -23,6 +27,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
@@ -38,6 +43,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private AudioSystem _audioSystem;
|
||||
@@ -78,6 +84,8 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
SubscribeLocalEvent<PlayerDetachedSystemMessage>(PlayerDetached);
|
||||
|
||||
_audioSystem = EntitySystemManager.GetEntitySystem<AudioSystem>();
|
||||
|
||||
_configurationManager.RegisterCVar("game.diagonalmovement", true, CVar.ARCHIVE);
|
||||
}
|
||||
|
||||
private static void PlayerAttached(PlayerAttachSystemMessage ev)
|
||||
@@ -133,6 +141,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
if (physics.LinearVelocity != Vector2.Zero)
|
||||
physics.LinearVelocity = Vector2.Zero;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -177,9 +186,20 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
|
||||
private static void HandleDirChange(ICommonSession session, Direction dir, bool state)
|
||||
{
|
||||
if (!TryGetAttachedComponent(session as IPlayerSession, out IMoverComponent moverComp))
|
||||
var playerSes = session as IPlayerSession;
|
||||
if (!TryGetAttachedComponent(playerSes, out IMoverComponent moverComp))
|
||||
return;
|
||||
|
||||
var owner = playerSes?.AttachedEntity;
|
||||
|
||||
if (owner != null)
|
||||
{
|
||||
foreach (var comp in owner.GetAllComponents<IRelayMoveInput>())
|
||||
{
|
||||
comp.MoveInputPressed(playerSes);
|
||||
}
|
||||
}
|
||||
|
||||
moverComp.SetVelocityDirection(dir, state);
|
||||
}
|
||||
|
||||
|
||||
65
Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs
Normal file
65
Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
public class RoundEndSystem : EntitySystem
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IGameTicker _gameTicker;
|
||||
[Dependency] private IGameTiming _gameTiming;
|
||||
#pragma warning restore 649
|
||||
|
||||
private CancellationTokenSource _roundEndCancellationTokenSource = new CancellationTokenSource();
|
||||
public bool IsRoundEndCountdownStarted { get; private set; }
|
||||
public TimeSpan RoundEndCountdownTime { get; set; } = TimeSpan.FromMinutes(4);
|
||||
public TimeSpan? ExpectedCountdownEnd = null;
|
||||
|
||||
public delegate void RoundEndCountdownStarted();
|
||||
public event RoundEndCountdownStarted OnRoundEndCountdownStarted;
|
||||
|
||||
public delegate void RoundEndCountdownCancelled();
|
||||
public event RoundEndCountdownCancelled OnRoundEndCountdownCancelled;
|
||||
|
||||
public delegate void RoundEndCountdownFinished();
|
||||
public event RoundEndCountdownFinished OnRoundEndCountdownFinished;
|
||||
|
||||
public void RequestRoundEnd()
|
||||
{
|
||||
if (IsRoundEndCountdownStarted)
|
||||
return;
|
||||
|
||||
IsRoundEndCountdownStarted = true;
|
||||
|
||||
ExpectedCountdownEnd = _gameTiming.CurTime + RoundEndCountdownTime;
|
||||
Timer.Spawn(RoundEndCountdownTime, EndRound, _roundEndCancellationTokenSource.Token);
|
||||
OnRoundEndCountdownStarted?.Invoke();
|
||||
}
|
||||
|
||||
public void CancelRoundEndCountdown()
|
||||
{
|
||||
if (!IsRoundEndCountdownStarted)
|
||||
return;
|
||||
|
||||
IsRoundEndCountdownStarted = false;
|
||||
|
||||
_roundEndCancellationTokenSource.Cancel();
|
||||
_roundEndCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
ExpectedCountdownEnd = null;
|
||||
|
||||
OnRoundEndCountdownCancelled?.Invoke();
|
||||
}
|
||||
|
||||
private void EndRound()
|
||||
{
|
||||
OnRoundEndCountdownFinished?.Invoke();
|
||||
_gameTicker.EndRound();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Content.Server/GameObjects/VisibilityFlags.cs
Normal file
10
Content.Server/GameObjects/VisibilityFlags.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
[Flags]
|
||||
public enum VisibilityFlags
|
||||
{
|
||||
Ghost = 2,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Content.Server.GameTicking
|
||||
namespace Content.Server.GameTicking
|
||||
{
|
||||
/// <summary>
|
||||
/// A round-start setup preset, such as which antagonists to spawn.
|
||||
@@ -6,6 +6,7 @@ namespace Content.Server.GameTicking
|
||||
public abstract class GamePreset
|
||||
{
|
||||
public abstract void Start();
|
||||
public virtual string ModeTitle => "Sandbox";
|
||||
public virtual string Description => "Secret!";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Server.GameTicking.GameRules;
|
||||
using Content.Server.GameTicking.GameRules;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Content.Server.GameTicking.GamePresets
|
||||
_gameTicker.AddGameRule<RuleDeathMatch>();
|
||||
}
|
||||
|
||||
public override string Description => "Deathmatch, go and kill everybody else to win!";
|
||||
public override string ModeTitle => "Deathmatch";
|
||||
public override string Description => "Kill anything that moves!";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Server.Sandbox;
|
||||
using Content.Server.Sandbox;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.GameTicking.GamePresets
|
||||
@@ -14,6 +14,7 @@ namespace Content.Server.GameTicking.GamePresets
|
||||
_sandboxManager.IsSandboxEnabled = true;
|
||||
}
|
||||
|
||||
public override string Description => "Sandbox, go and build something!";
|
||||
public override string ModeTitle => "Sandbox";
|
||||
public override string Description => "No stress, build something!";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Content.Server.GameObjects;
|
||||
using Content.Server.GameObjects.Components.Access;
|
||||
using Content.Server.GameObjects.Components.Markers;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.GameTicking.GamePresets;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.Chat;
|
||||
@@ -13,11 +15,14 @@ using Content.Server.Mobs;
|
||||
using Content.Server.Mobs.Roles;
|
||||
using Content.Server.Players;
|
||||
using Content.Shared;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Jobs;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Server.Interfaces;
|
||||
using Robust.Server.Interfaces.Maps;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Server.ServerStatus;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -34,19 +39,22 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timers;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
|
||||
namespace Content.Server.GameTicking
|
||||
{
|
||||
public partial class GameTicker : SharedGameTicker, IGameTicker
|
||||
{
|
||||
private static readonly TimeSpan UpdateRestartDelay = TimeSpan.FromSeconds(20);
|
||||
|
||||
private const string PlayerPrototypeName = "HumanMob_Content";
|
||||
private const string ObserverPrototypeName = "MobObserver";
|
||||
private const string MapFile = "Maps/stationstation.yml";
|
||||
private static TimeSpan _roundStartTimeSpan;
|
||||
|
||||
[ViewVariables] private readonly List<GameRule> _gameRules = new List<GameRule>();
|
||||
[ViewVariables] private readonly List<ManifestEntry> _manifest = new List<ManifestEntry>();
|
||||
@@ -66,6 +74,10 @@ namespace Content.Server.GameTicking
|
||||
|
||||
[ViewVariables] private bool LobbyEnabled => _configurationManager.GetCVar<bool>("game.lobbyenabled");
|
||||
|
||||
[ViewVariables] private bool _updateOnRoundEnd;
|
||||
private CancellationTokenSource _updateShutdownCts;
|
||||
|
||||
|
||||
[ViewVariables]
|
||||
public GameRunLevel RunLevel
|
||||
{
|
||||
@@ -100,6 +112,7 @@ namespace Content.Server.GameTicking
|
||||
_netManager.RegisterNetMessage<MsgTickerJoinGame>(nameof(MsgTickerJoinGame));
|
||||
_netManager.RegisterNetMessage<MsgTickerLobbyStatus>(nameof(MsgTickerLobbyStatus));
|
||||
_netManager.RegisterNetMessage<MsgTickerLobbyInfo>(nameof(MsgTickerLobbyInfo));
|
||||
_netManager.RegisterNetMessage<MsgRoundEndMessage>(nameof(MsgRoundEndMessage));
|
||||
|
||||
SetStartPreset(_configurationManager.GetCVar<string>("game.defaultpreset"));
|
||||
|
||||
@@ -108,6 +121,16 @@ namespace Content.Server.GameTicking
|
||||
_initialized = true;
|
||||
|
||||
JobControllerInit();
|
||||
|
||||
_watchdogApi.UpdateReceived += WatchdogApiOnUpdateReceived;
|
||||
}
|
||||
|
||||
private void WatchdogApiOnUpdateReceived()
|
||||
{
|
||||
_chatManager.DispatchServerAnnouncement(Loc.GetString(
|
||||
"Update has been received, server will automatically restart for update at the end of this round."));
|
||||
_updateOnRoundEnd = true;
|
||||
ServerEmptyUpdateRestartCheck();
|
||||
}
|
||||
|
||||
public void Update(FrameEventArgs frameEventArgs)
|
||||
@@ -121,8 +144,17 @@ namespace Content.Server.GameTicking
|
||||
|
||||
public void RestartRound()
|
||||
{
|
||||
if (_updateOnRoundEnd)
|
||||
{
|
||||
_baseServer.Shutdown(
|
||||
Loc.GetString("Server is shutting down for update and will automatically restart."));
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.InfoS("ticker", "Restarting round!");
|
||||
|
||||
SendServerMessage("Restarting round...");
|
||||
|
||||
RunLevel = GameRunLevel.PreRoundLobby;
|
||||
_resettingCleanup();
|
||||
_preRoundSetup();
|
||||
@@ -147,6 +179,8 @@ namespace Content.Server.GameTicking
|
||||
DebugTools.Assert(RunLevel == GameRunLevel.PreRoundLobby);
|
||||
Logger.InfoS("ticker", "Starting round!");
|
||||
|
||||
SendServerMessage("The round is starting now...");
|
||||
|
||||
RunLevel = GameRunLevel.InRound;
|
||||
|
||||
var preset = MakeGamePreset();
|
||||
@@ -188,9 +222,18 @@ namespace Content.Server.GameTicking
|
||||
SpawnPlayer(player, job, false);
|
||||
}
|
||||
|
||||
_roundStartTimeSpan = IoCManager.Resolve<IGameTiming>().RealTime;
|
||||
_sendStatusToAll();
|
||||
}
|
||||
|
||||
private void SendServerMessage(string message)
|
||||
{
|
||||
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
|
||||
msg.Channel = ChatChannel.Server;
|
||||
msg.Message = message;
|
||||
IoCManager.Resolve<IServerNetManager>().ServerSendToAll(msg);
|
||||
}
|
||||
|
||||
private HumanoidCharacterProfile GetPlayerProfile(IPlayerSession p) =>
|
||||
(HumanoidCharacterProfile) _prefsManager.GetPreferences(p.SessionId.Username).SelectedCharacter;
|
||||
|
||||
@@ -200,6 +243,34 @@ namespace Content.Server.GameTicking
|
||||
Logger.InfoS("ticker", "Ending round!");
|
||||
|
||||
RunLevel = GameRunLevel.PostRound;
|
||||
|
||||
//Tell every client the round has ended.
|
||||
var roundEndMessage = _netManager.CreateNetMessage<MsgRoundEndMessage>();
|
||||
roundEndMessage.GamemodeTitle = MakeGamePreset().ModeTitle;
|
||||
|
||||
//Get the timespan of the round.
|
||||
roundEndMessage.RoundDuration = IoCManager.Resolve<IGameTiming>().RealTime.Subtract(_roundStartTimeSpan);
|
||||
|
||||
//Generate a list of basic player info to display in the end round summary.
|
||||
var listOfPlayerInfo = new List<RoundEndPlayerInfo>();
|
||||
foreach(var ply in _playerManager.GetAllPlayers().OrderBy(p => p.Name))
|
||||
{
|
||||
if(ply.AttachedEntity.TryGetComponent<MindComponent>(out var mindComponent)
|
||||
&& mindComponent.HasMind)
|
||||
{
|
||||
var playerEndRoundInfo = new RoundEndPlayerInfo()
|
||||
{
|
||||
PlayerOOCName = ply.Name,
|
||||
PlayerICName = mindComponent.Mind.CurrentEntity.Name,
|
||||
Role = mindComponent.Mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("Unkown"),
|
||||
Antag = false
|
||||
};
|
||||
listOfPlayerInfo.Add(playerEndRoundInfo);
|
||||
}
|
||||
}
|
||||
|
||||
roundEndMessage.AllPlayersEndInfo = listOfPlayerInfo;
|
||||
_netManager.ServerSendToAll(roundEndMessage);
|
||||
}
|
||||
|
||||
public void Respawn(IPlayerSession targetPlayer)
|
||||
@@ -273,7 +344,7 @@ namespace Content.Server.GameTicking
|
||||
|
||||
private IEntity _spawnPlayerMob(Job job, bool lateJoin = true)
|
||||
{
|
||||
GridCoordinates coordinates = lateJoin ? _getLateJoinSpawnPoint() : _getJobSpawnPoint(job.Prototype.ID);
|
||||
GridCoordinates coordinates = lateJoin ? GetLateJoinSpawnPoint() : GetJobSpawnPoint(job.Prototype.ID);
|
||||
var entity = _entityManager.SpawnEntity(PlayerPrototypeName, coordinates);
|
||||
if (entity.TryGetComponent(out InventoryComponent inventory))
|
||||
{
|
||||
@@ -299,11 +370,11 @@ namespace Content.Server.GameTicking
|
||||
|
||||
private IEntity _spawnObserverMob()
|
||||
{
|
||||
GridCoordinates coordinates = _getLateJoinSpawnPoint();
|
||||
var coordinates = GetObserverSpawnPoint();
|
||||
return _entityManager.SpawnEntity(ObserverPrototypeName, coordinates);
|
||||
}
|
||||
|
||||
private GridCoordinates _getLateJoinSpawnPoint()
|
||||
public GridCoordinates GetLateJoinSpawnPoint()
|
||||
{
|
||||
var location = _spawnPoint;
|
||||
|
||||
@@ -319,7 +390,7 @@ namespace Content.Server.GameTicking
|
||||
return location;
|
||||
}
|
||||
|
||||
private GridCoordinates _getJobSpawnPoint(string jobId)
|
||||
public GridCoordinates GetJobSpawnPoint(string jobId)
|
||||
{
|
||||
var location = _spawnPoint;
|
||||
|
||||
@@ -336,6 +407,23 @@ namespace Content.Server.GameTicking
|
||||
return location;
|
||||
}
|
||||
|
||||
public GridCoordinates GetObserverSpawnPoint()
|
||||
{
|
||||
var location = _spawnPoint;
|
||||
|
||||
var possiblePoints = new List<GridCoordinates>();
|
||||
foreach (var entity in _entityManager.GetEntities(new TypeEntityQuery(typeof(SpawnPointComponent))))
|
||||
{
|
||||
var point = entity.GetComponent<SpawnPointComponent>();
|
||||
if (point.SpawnType == SpawnPointType.Observer)
|
||||
possiblePoints.Add(entity.Transform.GridPosition);
|
||||
}
|
||||
|
||||
if (possiblePoints.Count != 0) location = _robustRandom.Pick(possiblePoints);
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup that has to run to clear up anything from the previous round.
|
||||
/// Stuff like wiping the previous map clean.
|
||||
@@ -389,6 +477,11 @@ namespace Content.Server.GameTicking
|
||||
|
||||
switch (args.NewStatus)
|
||||
{
|
||||
case SessionStatus.Connecting:
|
||||
// Cancel shutdown update timer in progress.
|
||||
_updateShutdownCts?.Cancel();
|
||||
break;
|
||||
|
||||
case SessionStatus.Connected:
|
||||
{
|
||||
// Always make sure the client has player data. Mind gets assigned on spawn.
|
||||
@@ -443,11 +536,43 @@ namespace Content.Server.GameTicking
|
||||
if (_playersInLobby.ContainsKey(session)) _playersInLobby.Remove(session);
|
||||
|
||||
_chatManager.DispatchServerAnnouncement($"Player {args.Session.SessionId} left server!");
|
||||
ServerEmptyUpdateRestartCheck();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether there are still players on the server,
|
||||
/// and if not starts a timer to automatically reboot the server if an update is available.
|
||||
/// </summary>
|
||||
private void ServerEmptyUpdateRestartCheck()
|
||||
{
|
||||
// Can't simple check the current connected player count since that doesn't update
|
||||
// before PlayerStatusChanged gets fired.
|
||||
// So in the disconnect handler we'd still see a single player otherwise.
|
||||
var playersOnline = _playerManager.GetAllPlayers().Any(p => p.Status != SessionStatus.Disconnected);
|
||||
if (playersOnline || !_updateOnRoundEnd)
|
||||
{
|
||||
// Still somebody online.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_updateShutdownCts != null && !_updateShutdownCts.IsCancellationRequested)
|
||||
{
|
||||
// Do nothing because I guess we already have a timer running..?
|
||||
return;
|
||||
}
|
||||
|
||||
_updateShutdownCts = new CancellationTokenSource();
|
||||
|
||||
Timer.Spawn(UpdateRestartDelay, () =>
|
||||
{
|
||||
_baseServer.Shutdown(
|
||||
Loc.GetString("Server is shutting down for update and will automatically restart."));
|
||||
}, _updateShutdownCts.Token);
|
||||
}
|
||||
|
||||
private void SpawnPlayer(IPlayerSession session, string jobId = null, bool lateJoin = true)
|
||||
{
|
||||
var character = (HumanoidCharacterProfile) _prefsManager
|
||||
@@ -458,7 +583,10 @@ namespace Content.Server.GameTicking
|
||||
|
||||
var data = session.ContentData();
|
||||
data.WipeMind();
|
||||
data.Mind = new Mind(session.SessionId);
|
||||
data.Mind = new Mind(session.SessionId)
|
||||
{
|
||||
CharacterName = character.Name
|
||||
};
|
||||
|
||||
if (jobId == null)
|
||||
{
|
||||
@@ -506,12 +634,18 @@ namespace Content.Server.GameTicking
|
||||
|
||||
private void _spawnObserver(IPlayerSession session)
|
||||
{
|
||||
var name = _prefsManager
|
||||
.GetPreferences(session.SessionId.Username)
|
||||
.SelectedCharacter.Name;
|
||||
|
||||
_playerJoinGame(session);
|
||||
var data = session.ContentData();
|
||||
data.WipeMind();
|
||||
data.Mind = new Mind(session.SessionId);
|
||||
|
||||
var mob = _spawnObserverMob();
|
||||
mob.Name = name;
|
||||
mob.GetComponent<GhostComponent>().CanReturnToBody = false;
|
||||
data.Mind.TransferTo(mob);
|
||||
}
|
||||
|
||||
@@ -559,10 +693,12 @@ namespace Content.Server.GameTicking
|
||||
|
||||
private string GetInfoText()
|
||||
{
|
||||
var gameMode = MakeGamePreset().Description;
|
||||
var gmTitle = MakeGamePreset().ModeTitle;
|
||||
var desc = MakeGamePreset().Description;
|
||||
return _localization.GetString(@"Hi and welcome to [color=white]Space Station 14![/color]
|
||||
|
||||
The current game mode is [color=white]{0}[/color]", gameMode);
|
||||
The current game mode is: [color=white]{0}[/color].
|
||||
[color=yellow]{1}[/color]", gmTitle, desc );
|
||||
}
|
||||
|
||||
private void UpdateInfoText()
|
||||
@@ -591,6 +727,8 @@ The current game mode is [color=white]{0}[/color]", gameMode);
|
||||
[Dependency] private readonly ILocalizationManager _localization;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom;
|
||||
[Dependency] private readonly IServerPreferencesManager _prefsManager;
|
||||
[Dependency] private readonly IBaseServer _baseServer;
|
||||
[Dependency] private readonly IWatchdogApi _watchdogApi;
|
||||
#pragma warning restore 649
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.GameObjects;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Nutrition;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.Players;
|
||||
using Content.Shared.GameObjects;
|
||||
using Robust.Server.Console;
|
||||
@@ -41,9 +42,13 @@ namespace Content.Server.GlobalVerbs
|
||||
{
|
||||
var userMind = user.GetComponent<IActorComponent>().playerSession.ContentData().Mind;
|
||||
var targetMind = target.GetComponent<MindComponent>();
|
||||
var oldEntity = userMind.CurrentEntity;
|
||||
|
||||
targetMind.Mind?.TransferTo(null);
|
||||
userMind.TransferTo(target);
|
||||
|
||||
if(oldEntity.HasComponent<GhostComponent>())
|
||||
oldEntity.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Content.Server.Interfaces.Chat
|
||||
void EntityMe(IEntity source, string action);
|
||||
|
||||
void SendOOC(IPlayerSession player, string message);
|
||||
void SendDeadChat(IPlayerSession player, string message);
|
||||
|
||||
void SendHookOOC(string sender, string message);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ namespace Content.Shared.Interfaces
|
||||
/// </summary>
|
||||
public interface IReactionEffect : IExposeData
|
||||
{
|
||||
void React(IEntity solutionEntity, int intensity);
|
||||
void React(IEntity solutionEntity, decimal intensity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using Robust.Server.Interfaces.Player;
|
||||
|
||||
namespace Content.Server.Interfaces.GameObjects.Components.Movement
|
||||
{
|
||||
public interface IRelayMoveInput
|
||||
{
|
||||
void MoveInputPressed(IPlayerSession session);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameTicking;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Interfaces.GameTicking
|
||||
@@ -27,6 +28,10 @@ namespace Content.Server.Interfaces.GameTicking
|
||||
void MakeJoinGame(IPlayerSession player);
|
||||
void ToggleReady(IPlayerSession player, bool ready);
|
||||
|
||||
GridCoordinates GetLateJoinSpawnPoint();
|
||||
GridCoordinates GetJobSpawnPoint(string jobId);
|
||||
GridCoordinates GetObserverSpawnPoint();
|
||||
|
||||
// GameRule system.
|
||||
T AddGameRule<T>() where T : GameRule, new();
|
||||
void RemoveGameRule(GameRule rule);
|
||||
|
||||
@@ -88,21 +88,15 @@ namespace Content.Server
|
||||
}
|
||||
|
||||
var password = _configurationManager.GetCVar<string>("status.mommipassword");
|
||||
OOCPostMessage message;
|
||||
|
||||
using (var streamReader = new StreamReader(request.Body, EncodingHelpers.UTF8))
|
||||
using (var jsonReader = new JsonTextReader(streamReader))
|
||||
OOCPostMessage message = null;
|
||||
try
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
try
|
||||
{
|
||||
message = serializer.Deserialize<OOCPostMessage>(jsonReader);
|
||||
}
|
||||
catch (JsonSerializationException)
|
||||
{
|
||||
response.StatusCode = (int) HttpStatusCode.BadRequest;
|
||||
return true;
|
||||
}
|
||||
message = request.GetFromJson<OOCPostMessage>();
|
||||
}
|
||||
catch (JsonSerializationException)
|
||||
{
|
||||
// message null so enters block down below.
|
||||
}
|
||||
|
||||
if (message == null)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.Players;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -48,6 +49,9 @@ namespace Content.Server.Mobs
|
||||
|
||||
[ViewVariables] public IEntity CurrentEntity => VisitingEntity ?? OwnedEntity;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string CharacterName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The component currently owned by this mind.
|
||||
/// Can be null.
|
||||
@@ -139,6 +143,7 @@ namespace Content.Server.Mobs
|
||||
public void TransferTo(IEntity entity)
|
||||
{
|
||||
MindComponent component = null;
|
||||
bool alreadyAttached = false;
|
||||
if (entity != null)
|
||||
{
|
||||
if (!entity.TryGetComponent(out component))
|
||||
@@ -150,6 +155,17 @@ namespace Content.Server.Mobs
|
||||
// TODO: Kick them out, maybe?
|
||||
throw new ArgumentException("That entity already has a mind.", nameof(entity));
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out IActorComponent actor))
|
||||
{
|
||||
// Happens when transferring to your currently visited entity.
|
||||
if (actor.playerSession != Session)
|
||||
{
|
||||
throw new ArgumentException("Visit target already has a session.", nameof(entity));
|
||||
}
|
||||
|
||||
alreadyAttached = true;
|
||||
}
|
||||
}
|
||||
|
||||
OwnedMob?.InternalEjectMind();
|
||||
@@ -157,7 +173,7 @@ namespace Content.Server.Mobs
|
||||
OwnedMob?.InternalAssignMind(this);
|
||||
|
||||
// Player is CURRENTLY connected.
|
||||
if (Session != null && OwnedMob != null)
|
||||
if (Session != null && OwnedMob != null && !alreadyAttached)
|
||||
{
|
||||
Session.AttachToEntity(entity);
|
||||
}
|
||||
|
||||
77
Content.Server/Observer/Ghost.cs
Normal file
77
Content.Server/Observer/Ghost.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using Content.Server.GameObjects;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Content.Server.Players;
|
||||
using Content.Shared.GameObjects;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timers;
|
||||
|
||||
namespace Content.Server.Observer
|
||||
{
|
||||
public class Ghost : IClientCommand
|
||||
{
|
||||
public string Command => "ghost";
|
||||
public string Description => "Give up on life and become a ghost.";
|
||||
public string Help => "ghost";
|
||||
|
||||
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
|
||||
{
|
||||
if (player == null)
|
||||
{
|
||||
shell.SendText((IPlayerSession) null, "Nah");
|
||||
return;
|
||||
}
|
||||
|
||||
var mind = player.ContentData().Mind;
|
||||
var canReturn = player.AttachedEntity != null;
|
||||
var name = player.AttachedEntity?.Name ?? player.Name;
|
||||
|
||||
if (player.AttachedEntity != null && player.AttachedEntity.HasComponent<GhostComponent>())
|
||||
return;
|
||||
|
||||
if (mind.VisitingEntity != null)
|
||||
{
|
||||
mind.UnVisit();
|
||||
mind.VisitingEntity.Delete();
|
||||
}
|
||||
|
||||
var position = player.AttachedEntity?.Transform.GridPosition ?? IoCManager.Resolve<IGameTicker>().GetObserverSpawnPoint();
|
||||
|
||||
if (canReturn && player.AttachedEntity.TryGetComponent(out SpeciesComponent species))
|
||||
{
|
||||
switch (species.CurrentDamageState)
|
||||
{
|
||||
case DeadState _:
|
||||
canReturn = true;
|
||||
break;
|
||||
case CriticalState _:
|
||||
canReturn = true;
|
||||
if (!player.AttachedEntity.TryGetComponent(out DamageableComponent damageable)) break;
|
||||
damageable.TakeDamage(DamageType.Total, 100); // TODO: Use airloss/oxyloss instead
|
||||
break;
|
||||
default:
|
||||
canReturn = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
var ghost = entityManager.SpawnEntity("MobObserver", position);
|
||||
ghost.Name = mind.CharacterName;
|
||||
|
||||
var ghostComponent = ghost.GetComponent<GhostComponent>();
|
||||
ghostComponent.CanReturnToBody = canReturn;
|
||||
|
||||
if(canReturn)
|
||||
mind.Visit(ghost);
|
||||
else
|
||||
mind.TransferTo(ghost);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Server.Cargo;
|
||||
using Content.Server.Cargo;
|
||||
using Content.Server.Chat;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Interfaces;
|
||||
@@ -7,7 +7,9 @@ using Content.Server.Interfaces.GameTicking;
|
||||
using Content.Server.Preferences;
|
||||
using Content.Server.Sandbox;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.Chemistry;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Content.Shared.Chat
|
||||
/// Represents chat channels that the player can filter chat tabs by.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ChatChannel : byte
|
||||
public enum ChatChannel : short
|
||||
{
|
||||
None = 0,
|
||||
|
||||
@@ -46,9 +46,14 @@ namespace Content.Shared.Chat
|
||||
/// </summary>
|
||||
Emotes = 64,
|
||||
|
||||
/// <summary>
|
||||
/// Deadchat
|
||||
/// </summary>
|
||||
Dead = 128,
|
||||
|
||||
/// <summary>
|
||||
/// Unspecified.
|
||||
/// </summary>
|
||||
Unspecified = 128,
|
||||
Unspecified = 256,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Content.Shared.Chat
|
||||
|
||||
/// <summary>
|
||||
/// The sending entity.
|
||||
/// Only applies to <see cref="ChatChannel.Local"/> and <see cref="ChatChannel.Emotes"/>.
|
||||
/// Only applies to <see cref="ChatChannel.Local"/>, <see cref="ChatChannel.Dead"/> and <see cref="ChatChannel.Emotes"/>.
|
||||
/// </summary>
|
||||
public EntityUid SenderEntity { get; set; }
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace Content.Shared.Chat
|
||||
switch (Channel)
|
||||
{
|
||||
case ChatChannel.Local:
|
||||
case ChatChannel.Dead:
|
||||
case ChatChannel.Emotes:
|
||||
SenderEntity = buffer.ReadEntityUid();
|
||||
break;
|
||||
@@ -63,6 +64,7 @@ namespace Content.Shared.Chat
|
||||
switch (Channel)
|
||||
{
|
||||
case ChatChannel.Local:
|
||||
case ChatChannel.Dead:
|
||||
case ChatChannel.Emotes:
|
||||
buffer.Write(SenderEntity);
|
||||
break;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using Content.Shared.Interfaces.Chemistry;
|
||||
using Content.Shared.Interfaces.Chemistry;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Chemistry
|
||||
@@ -10,18 +10,17 @@ namespace Content.Shared.Chemistry
|
||||
class DefaultMetabolizable : IMetabolizable
|
||||
{
|
||||
//Rate of metabolism in units / second
|
||||
private int _metabolismRate = 1;
|
||||
public int MetabolismRate => _metabolismRate;
|
||||
private decimal _metabolismRate = 1;
|
||||
public decimal MetabolismRate => _metabolismRate;
|
||||
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _metabolismRate, "rate", 1);
|
||||
}
|
||||
|
||||
int IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime)
|
||||
ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime)
|
||||
{
|
||||
int metabolismAmount = (int)Math.Round(MetabolismRate * tickTime);
|
||||
return metabolismAmount;
|
||||
return ReagentUnit.New(MetabolismRate * (decimal)tickTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Content.Shared.Chemistry
|
||||
private string _description;
|
||||
private Color _substanceColor;
|
||||
private List<IMetabolizable> _metabolism;
|
||||
private string _spritePath;
|
||||
|
||||
public string ID => _id;
|
||||
public string Name => _name;
|
||||
@@ -29,6 +30,8 @@ namespace Content.Shared.Chemistry
|
||||
//List of metabolism effects this reagent has, should really only be used server-side.
|
||||
public List<IMetabolizable> Metabolism => _metabolism;
|
||||
|
||||
public string SpriteReplacementPath => _spritePath;
|
||||
|
||||
public ReagentPrototype()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -42,6 +45,7 @@ namespace Content.Shared.Chemistry
|
||||
serializer.DataField(ref _name, "name", string.Empty);
|
||||
serializer.DataField(ref _description, "desc", string.Empty);
|
||||
serializer.DataField(ref _substanceColor, "color", Color.White);
|
||||
serializer.DataField(ref _spritePath, "spritePath", string.Empty);
|
||||
|
||||
if (_moduleManager.IsServerModule)
|
||||
serializer.DataField(ref _metabolism, "metabolism", new List<IMetabolizable> {new DefaultMetabolizable()});
|
||||
|
||||
225
Content.Shared/Chemistry/ReagentUnit.cs
Normal file
225
Content.Shared/Chemistry/ReagentUnit.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Serialization;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Shared.Chemistry
|
||||
{
|
||||
[Serializable]
|
||||
public struct ReagentUnit : ISelfSerialize, IComparable<ReagentUnit>, IEquatable<ReagentUnit>
|
||||
{
|
||||
private int _value;
|
||||
private static readonly int Shift = 2;
|
||||
|
||||
public static ReagentUnit MaxValue => new ReagentUnit(int.MaxValue);
|
||||
|
||||
private double ShiftDown()
|
||||
{
|
||||
return _value / Math.Pow(10, Shift);
|
||||
}
|
||||
|
||||
private ReagentUnit(int value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public static ReagentUnit New(int value)
|
||||
{
|
||||
return new ReagentUnit(value * (int) Math.Pow(10, Shift));
|
||||
}
|
||||
|
||||
public static ReagentUnit New(decimal value)
|
||||
{
|
||||
return new ReagentUnit((int) Math.Round(value * (decimal) Math.Pow(10, Shift), MidpointRounding.AwayFromZero));
|
||||
}
|
||||
|
||||
public static ReagentUnit New(float value)
|
||||
{
|
||||
return new ReagentUnit(FromFloat(value));
|
||||
}
|
||||
|
||||
private static int FromFloat(float value)
|
||||
{
|
||||
return (int) Math.Round(value * (float) Math.Pow(10, Shift), MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
public static ReagentUnit New(double value)
|
||||
{
|
||||
return new ReagentUnit((int) Math.Round(value * Math.Pow(10, Shift), MidpointRounding.AwayFromZero));
|
||||
}
|
||||
|
||||
public static ReagentUnit New(string value)
|
||||
{
|
||||
return New(FloatFromString(value));
|
||||
}
|
||||
|
||||
private static float FloatFromString(string value)
|
||||
{
|
||||
return float.Parse(value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static ReagentUnit operator +(ReagentUnit a) => a;
|
||||
|
||||
public static ReagentUnit operator -(ReagentUnit a) => new ReagentUnit(-a._value);
|
||||
|
||||
public static ReagentUnit operator +(ReagentUnit a, ReagentUnit b)
|
||||
=> new ReagentUnit(a._value + b._value);
|
||||
|
||||
public static ReagentUnit operator -(ReagentUnit a, ReagentUnit b)
|
||||
=> a + -b;
|
||||
|
||||
public static ReagentUnit operator *(ReagentUnit a, ReagentUnit b)
|
||||
{
|
||||
var aD = a.ShiftDown();
|
||||
var bD = b.ShiftDown();
|
||||
return New(aD * bD);
|
||||
}
|
||||
|
||||
public static ReagentUnit operator *(ReagentUnit a, float b)
|
||||
{
|
||||
var aD = (float) a.ShiftDown();
|
||||
return New(aD * b);
|
||||
}
|
||||
|
||||
public static ReagentUnit operator *(ReagentUnit a, decimal b)
|
||||
{
|
||||
var aD = (decimal) a.ShiftDown();
|
||||
return New(aD * b);
|
||||
}
|
||||
|
||||
public static ReagentUnit operator *(ReagentUnit a, double b)
|
||||
{
|
||||
var aD = a.ShiftDown();
|
||||
return New(aD * b);
|
||||
}
|
||||
|
||||
public static ReagentUnit operator *(ReagentUnit a, int b)
|
||||
{
|
||||
return new ReagentUnit(a._value * b);
|
||||
}
|
||||
|
||||
public static ReagentUnit operator /(ReagentUnit a, ReagentUnit b)
|
||||
{
|
||||
if (b._value == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
var aD = a.ShiftDown();
|
||||
var bD = b.ShiftDown();
|
||||
return New(aD / bD);
|
||||
}
|
||||
|
||||
public static bool operator <=(ReagentUnit a, int b)
|
||||
{
|
||||
return a.ShiftDown() <= b;
|
||||
}
|
||||
|
||||
public static bool operator >=(ReagentUnit a, int b)
|
||||
{
|
||||
return a.ShiftDown() >= b;
|
||||
}
|
||||
|
||||
public static bool operator ==(ReagentUnit a, int b)
|
||||
{
|
||||
return a.ShiftDown() == b;
|
||||
}
|
||||
|
||||
public static bool operator !=(ReagentUnit a, int b)
|
||||
{
|
||||
return a.ShiftDown() != b;
|
||||
}
|
||||
|
||||
public static bool operator <=(ReagentUnit a, ReagentUnit b)
|
||||
{
|
||||
return a._value <= b._value;
|
||||
}
|
||||
|
||||
public static bool operator >=(ReagentUnit a, ReagentUnit b)
|
||||
{
|
||||
return a._value >= b._value;
|
||||
}
|
||||
|
||||
public static bool operator <(ReagentUnit a, ReagentUnit b)
|
||||
{
|
||||
return a._value < b._value;
|
||||
}
|
||||
|
||||
public static bool operator >(ReagentUnit a, ReagentUnit b)
|
||||
{
|
||||
return a._value > b._value;
|
||||
}
|
||||
|
||||
public float Float()
|
||||
{
|
||||
return (float) ShiftDown();
|
||||
}
|
||||
|
||||
public decimal Decimal()
|
||||
{
|
||||
return (decimal) ShiftDown();
|
||||
}
|
||||
|
||||
public double Double()
|
||||
{
|
||||
return ShiftDown();
|
||||
}
|
||||
|
||||
public int Int()
|
||||
{
|
||||
return (int) ShiftDown();
|
||||
}
|
||||
|
||||
public static ReagentUnit Min(params ReagentUnit[] reagentUnits)
|
||||
{
|
||||
return reagentUnits.Min();
|
||||
}
|
||||
|
||||
public static ReagentUnit Min(ReagentUnit a, ReagentUnit b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ReagentUnit unit &&
|
||||
_value == unit._value;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_value);
|
||||
}
|
||||
|
||||
public void Deserialize(string value)
|
||||
{
|
||||
_value = FromFloat(FloatFromString(value));
|
||||
}
|
||||
|
||||
public override string ToString() => $"{ShiftDown().ToString(CultureInfo.InvariantCulture)}";
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
return ToString();
|
||||
}
|
||||
|
||||
public bool Equals([AllowNull] ReagentUnit other)
|
||||
{
|
||||
return _value == other._value;
|
||||
}
|
||||
|
||||
public int CompareTo([AllowNull] ReagentUnit other)
|
||||
{
|
||||
if(other._value > _value)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if(other._value < _value)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Interfaces.Chemistry;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Content.Shared.Chemistry
|
||||
{
|
||||
@@ -23,7 +25,7 @@ namespace Content.Shared.Chemistry
|
||||
/// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker).
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public int TotalVolume { get; private set; }
|
||||
public ReagentUnit TotalVolume { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an empty solution (ex. an empty beaker).
|
||||
@@ -35,7 +37,7 @@ namespace Content.Shared.Chemistry
|
||||
/// </summary>
|
||||
/// <param name="reagentId">The prototype ID of the reagent to add.</param>
|
||||
/// <param name="quantity">The quantity in milli-units.</param>
|
||||
public Solution(string reagentId, int quantity)
|
||||
public Solution(string reagentId, ReagentUnit quantity)
|
||||
{
|
||||
AddReagent(reagentId, quantity);
|
||||
}
|
||||
@@ -47,7 +49,7 @@ namespace Content.Shared.Chemistry
|
||||
|
||||
if (serializer.Reading)
|
||||
{
|
||||
TotalVolume = 0;
|
||||
TotalVolume = ReagentUnit.New(0);
|
||||
foreach (var reagent in _contents)
|
||||
{
|
||||
TotalVolume += reagent.Quantity;
|
||||
@@ -60,9 +62,9 @@ namespace Content.Shared.Chemistry
|
||||
/// </summary>
|
||||
/// <param name="reagentId">The prototype ID of the reagent to add.</param>
|
||||
/// <param name="quantity">The quantity in milli-units.</param>
|
||||
public void AddReagent(string reagentId, int quantity)
|
||||
public void AddReagent(string reagentId, ReagentUnit quantity)
|
||||
{
|
||||
if(quantity <= 0)
|
||||
if (quantity <= 0)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
@@ -85,7 +87,7 @@ namespace Content.Shared.Chemistry
|
||||
/// </summary>
|
||||
/// <param name="reagentId">The prototype ID of the reagent to add.</param>
|
||||
/// <returns>The quantity in milli-units.</returns>
|
||||
public int GetReagentQuantity(string reagentId)
|
||||
public ReagentUnit GetReagentQuantity(string reagentId)
|
||||
{
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
{
|
||||
@@ -93,10 +95,10 @@ namespace Content.Shared.Chemistry
|
||||
return _contents[i].Quantity;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ReagentUnit.New(0);
|
||||
}
|
||||
|
||||
public void RemoveReagent(string reagentId, int quantity)
|
||||
public void RemoveReagent(string reagentId, ReagentUnit quantity)
|
||||
{
|
||||
if(quantity <= 0)
|
||||
return;
|
||||
@@ -129,12 +131,12 @@ namespace Content.Shared.Chemistry
|
||||
/// Remove the specified quantity from this solution.
|
||||
/// </summary>
|
||||
/// <param name="quantity">The quantity of this solution to remove</param>
|
||||
public void RemoveSolution(int quantity)
|
||||
public void RemoveSolution(ReagentUnit quantity)
|
||||
{
|
||||
if(quantity <= 0)
|
||||
return;
|
||||
|
||||
var ratio = (float)(TotalVolume - quantity) / TotalVolume;
|
||||
var ratio = (TotalVolume - quantity).Decimal() / TotalVolume.Decimal();
|
||||
|
||||
if (ratio <= 0)
|
||||
{
|
||||
@@ -149,21 +151,21 @@ namespace Content.Shared.Chemistry
|
||||
|
||||
// quantity taken is always a little greedy, so fractional quantities get rounded up to the nearest
|
||||
// whole unit. This should prevent little bits of chemical remaining because of float rounding errors.
|
||||
var newQuantity = (int)Math.Floor(oldQuantity * ratio);
|
||||
var newQuantity = oldQuantity * ratio;
|
||||
|
||||
_contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
|
||||
}
|
||||
|
||||
TotalVolume = (int)Math.Floor(TotalVolume * ratio);
|
||||
TotalVolume = TotalVolume * ratio;
|
||||
}
|
||||
|
||||
public void RemoveAllSolution()
|
||||
{
|
||||
_contents.Clear();
|
||||
TotalVolume = 0;
|
||||
TotalVolume = ReagentUnit.New(0);
|
||||
}
|
||||
|
||||
public Solution SplitSolution(int quantity)
|
||||
public Solution SplitSolution(ReagentUnit quantity)
|
||||
{
|
||||
if (quantity <= 0)
|
||||
return new Solution();
|
||||
@@ -178,23 +180,26 @@ namespace Content.Shared.Chemistry
|
||||
}
|
||||
|
||||
newSolution = new Solution();
|
||||
var newTotalVolume = 0;
|
||||
var ratio = (float)(TotalVolume - quantity) / TotalVolume;
|
||||
var newTotalVolume = ReagentUnit.New(0M);
|
||||
var remainingVolume = TotalVolume;
|
||||
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
{
|
||||
var reagent = _contents[i];
|
||||
var ratio = (remainingVolume - quantity).Decimal() / remainingVolume.Decimal();
|
||||
remainingVolume -= reagent.Quantity;
|
||||
|
||||
var newQuantity = (int)Math.Floor(reagent.Quantity * ratio);
|
||||
var newQuantity = reagent.Quantity * ratio;
|
||||
var splitQuantity = reagent.Quantity - newQuantity;
|
||||
|
||||
_contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
|
||||
newSolution._contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity));
|
||||
newTotalVolume += splitQuantity;
|
||||
quantity -= splitQuantity;
|
||||
}
|
||||
|
||||
TotalVolume = (int)Math.Floor(TotalVolume * ratio);
|
||||
newSolution.TotalVolume = newTotalVolume;
|
||||
TotalVolume -= newTotalVolume;
|
||||
|
||||
return newSolution;
|
||||
}
|
||||
@@ -228,7 +233,7 @@ namespace Content.Shared.Chemistry
|
||||
|
||||
public Solution Clone()
|
||||
{
|
||||
var volume = 0;
|
||||
var volume = ReagentUnit.New(0);
|
||||
var newSolution = new Solution();
|
||||
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
@@ -246,9 +251,9 @@ namespace Content.Shared.Chemistry
|
||||
public readonly struct ReagentQuantity
|
||||
{
|
||||
public readonly string ReagentId;
|
||||
public readonly int Quantity;
|
||||
public readonly ReagentUnit Quantity;
|
||||
|
||||
public ReagentQuantity(string reagentId, int quantity)
|
||||
public ReagentQuantity(string reagentId, ReagentUnit quantity)
|
||||
{
|
||||
ReagentId = reagentId;
|
||||
Quantity = quantity;
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace Content.Shared.Chemistry
|
||||
/// <para>Allows us to have obscenely large containers that are harder to abuse in chem dispensers
|
||||
/// since they can't be placed directly in them.</para>
|
||||
/// </summary>
|
||||
FitsInDispenser = 16,
|
||||
FitsInDispenser = 16,
|
||||
|
||||
NoExamine = 32,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Content.Shared.Chemistry;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
@@ -18,11 +19,11 @@ namespace Content.Shared.GameObjects.Components.Chemistry
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class InjectorComponentState : ComponentState
|
||||
{
|
||||
public int CurrentVolume { get; }
|
||||
public int TotalVolume { get; }
|
||||
public ReagentUnit CurrentVolume { get; }
|
||||
public ReagentUnit TotalVolume { get; }
|
||||
public InjectorToggleMode CurrentMode { get; }
|
||||
|
||||
public InjectorComponentState(int currentVolume, int totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR)
|
||||
public InjectorComponentState(ReagentUnit currentVolume, ReagentUnit totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR)
|
||||
{
|
||||
CurrentVolume = currentVolume;
|
||||
TotalVolume = totalVolume;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user