Merge branch 'master' into DecimalReagents
This commit is contained in:
@@ -65,19 +65,11 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -30,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;
|
||||
@@ -161,7 +161,7 @@ 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))
|
||||
@@ -207,7 +207,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
return;
|
||||
|
||||
var beaker = _beakerContainer.ContainedEntity;
|
||||
Solution.SolutionChanged -= HandleSolutionChangedEvent;
|
||||
_beakerContainer.Remove(_beakerContainer.ContainedEntity);
|
||||
UpdateUserInterface();
|
||||
|
||||
@@ -304,7 +303,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
else
|
||||
{
|
||||
_beakerContainer.Insert(activeHandEntity);
|
||||
Solution.SolutionChanged += HandleSolutionChangedEvent;
|
||||
UpdateUserInterface();
|
||||
}
|
||||
}
|
||||
@@ -317,10 +315,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HandleSolutionChangedEvent()
|
||||
{
|
||||
UpdateUserInterface();
|
||||
}
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => UpdateUserInterface();
|
||||
|
||||
private void ClickSound()
|
||||
{
|
||||
@@ -329,5 +324,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
sound.Play("/Audio/machines/machine_switch.ogg", AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,37 @@
|
||||
using Content.Server.Chemistry;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Linq;
|
||||
using Content.Server.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Content.Server.GameObjects.Components.Nutrition;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.Interfaces.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 System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
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;
|
||||
@@ -29,27 +41,178 @@ 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 int _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 int 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 int CurrentVolume => _containedSolution.TotalVolume;
|
||||
|
||||
/// <summary>
|
||||
/// The volume without reagents remaining in the container.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public int 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;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _maxVolume, "maxVol", 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, int 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(int 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(int quantity)
|
||||
{
|
||||
var solutionSplit = _containedSolution.SplitSolution(quantity);
|
||||
OnSolutionChanged(false);
|
||||
return solutionSplit;
|
||||
}
|
||||
|
||||
protected void RecalculateColor()
|
||||
{
|
||||
if (_containedSolution.TotalVolume == 0)
|
||||
{
|
||||
SubstanceColor = Color.Transparent;
|
||||
return;
|
||||
}
|
||||
|
||||
Color mixColor = default;
|
||||
float runningTotalQuantity = 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) * reagent.Quantity);
|
||||
}
|
||||
|
||||
SubstanceColor = mixColor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -118,6 +281,10 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
void IExamine.Examine(FormattedMessage message)
|
||||
{
|
||||
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))
|
||||
@@ -239,7 +406,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
if(!skipReactionCheck)
|
||||
CheckForReaction();
|
||||
OnSolutionChanged();
|
||||
OnSolutionChanged(skipColor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -254,7 +421,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
if(!skipReactionCheck)
|
||||
CheckForReaction();
|
||||
OnSolutionChanged();
|
||||
OnSolutionChanged(skipColor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -322,5 +489,63 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
//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 int quantity)
|
||||
{
|
||||
foreach (var reagent in _containedSolution.Contents)
|
||||
{
|
||||
if (reagent.ReagentId == reagentId)
|
||||
{
|
||||
quantity = reagent.Quantity;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
quantity = 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 = (double)CurrentVolume / MaxVolume;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,16 +47,11 @@ namespace Content.Server.GameObjects.Components.Metabolism
|
||||
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>
|
||||
|
||||
@@ -43,9 +43,6 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
set => _contents.MaxVolume = value;
|
||||
}
|
||||
|
||||
private Solution _initialContents; // This is just for loading from yaml
|
||||
private ReagentUnit _maxVolume;
|
||||
|
||||
private bool _despawnOnFinish;
|
||||
|
||||
private bool _drinking;
|
||||
@@ -64,53 +61,21 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _initialContents, "contents", null);
|
||||
serializer.DataField(ref _maxVolume, "max_volume", ReagentUnit.New(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 = ReagentUnit.New(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);
|
||||
@@ -168,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,22 +68,15 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user