Merge branch 'master' into round_end_screen
@@ -288,7 +288,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);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ 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
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ 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;
|
||||
@@ -134,6 +135,7 @@ namespace Content.Client
|
||||
"Paper",
|
||||
"Write",
|
||||
"Bloodstream",
|
||||
"TransformableContainer",
|
||||
"Mind",
|
||||
"MovementSpeedModifier",
|
||||
"StorageFill"
|
||||
@@ -148,7 +150,7 @@ namespace Content.Client
|
||||
factory.Register<SharedLatheComponent>();
|
||||
factory.Register<SharedSpawnPointComponent>();
|
||||
|
||||
factory.Register<SolutionComponent>();
|
||||
factory.Register<SharedSolutionComponent>();
|
||||
|
||||
factory.Register<SharedVendingMachineComponent>();
|
||||
factory.Register<SharedWiresComponent>();
|
||||
@@ -226,6 +228,14 @@ namespace Content.Client
|
||||
IoCManager.Resolve<IClientPreferencesManager>().Initialize();
|
||||
IoCManager.Resolve<IItemSlotManager>().Initialize();
|
||||
|
||||
_baseClient.RunLevelChanged += (sender, args) =>
|
||||
{
|
||||
if (args.NewLevel == ClientRunLevel.Initialize)
|
||||
{
|
||||
_stateManager.RequestStateChange<MainScreen>();
|
||||
}
|
||||
};
|
||||
|
||||
// Fire off into state dependent on launcher or not.
|
||||
if (_gameController.LaunchState.FromLauncher)
|
||||
{
|
||||
|
||||
@@ -8,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
|
||||
{
|
||||
@@ -17,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;
|
||||
|
||||
@@ -29,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;
|
||||
|
||||
@@ -171,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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,10 +76,6 @@ namespace Content.Client.GameObjects.Components.Mobs
|
||||
|
||||
private void PlayerDetached()
|
||||
{
|
||||
if (!CurrentlyControlled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_ui?.Dispose();
|
||||
_ui = null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
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;
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
break;
|
||||
|
||||
case PlayerDetachedMsg _:
|
||||
_gui.Parent?.RemoveChild(_gui);
|
||||
SetGhostVisibility(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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,5 +1,4 @@
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -14,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
|
||||
|
||||
@@ -43,8 +43,6 @@ namespace Content.Client.State
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_playerManager.LocalPlayer.DetachEntity();
|
||||
|
||||
_inputManager.KeyBindStateChanged -= OnKeyBindStateChanged;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Players;
|
||||
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;
|
||||
@@ -30,10 +31,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,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
@@ -42,7 +42,7 @@ 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>();
|
||||
@@ -122,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)
|
||||
@@ -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))
|
||||
@@ -182,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()
|
||||
@@ -207,7 +207,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
return;
|
||||
|
||||
var beaker = _beakerContainer.ContainedEntity;
|
||||
Solution.SolutionChanged -= HandleSolutionChangedEvent;
|
||||
_beakerContainer.Remove(_beakerContainer.ContainedEntity);
|
||||
UpdateUserInterface();
|
||||
|
||||
@@ -238,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();
|
||||
}
|
||||
@@ -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,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,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 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;
|
||||
|
||||
/// <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 +259,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)
|
||||
@@ -121,6 +274,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))
|
||||
@@ -185,8 +342,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 +358,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 +380,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 +399,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 +414,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
if(!skipReactionCheck)
|
||||
CheckForReaction();
|
||||
OnSolutionChanged();
|
||||
OnSolutionChanged(skipColor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -267,16 +425,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 +457,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);
|
||||
|
||||
|
||||
@@ -38,5 +38,6 @@ namespace Content.Server.GameObjects.Components.Markers
|
||||
Unset = 0,
|
||||
LateJoin,
|
||||
Job,
|
||||
Observer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
break;
|
||||
case PlayerAttachedMsg msg:
|
||||
msg.NewPlayer.VisibilityMask |= (int)VisibilityFlags.Ghost;
|
||||
Dirty();
|
||||
break;
|
||||
case PlayerDetachedMsg msg:
|
||||
msg.OldPlayer.VisibilityMask &= ~(int)VisibilityFlags.Ghost;
|
||||
Timer.Spawn(100, Owner.Delete);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,12 +3,14 @@ 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;
|
||||
@@ -25,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;
|
||||
@@ -138,6 +141,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
if (physics.LinearVelocity != Vector2.Zero)
|
||||
physics.LinearVelocity = Vector2.Zero;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -185,6 +189,11 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
if (!TryGetAttachedComponent(session as IPlayerSession, out IMoverComponent moverComp))
|
||||
return;
|
||||
|
||||
var owner = (session as IPlayerSession)?.AttachedEntity;
|
||||
|
||||
if (owner != null && owner.TryGetComponent(out SpeciesComponent species) && species.CurrentDamageState is DeadState)
|
||||
new Ghost().Execute(null, (IPlayerSession)session, null);
|
||||
|
||||
moverComp.SetVelocityDirection(dir, state);
|
||||
}
|
||||
|
||||
|
||||
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
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
[Flags]
|
||||
public enum VisibilityFlags
|
||||
{
|
||||
Ghost = 2,
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ 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.Maps;
|
||||
@@ -124,6 +125,8 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
Logger.InfoS("ticker", "Restarting round!");
|
||||
|
||||
SendServerMessage("Restarting round...");
|
||||
|
||||
RunLevel = GameRunLevel.PreRoundLobby;
|
||||
_resettingCleanup();
|
||||
_preRoundSetup();
|
||||
@@ -148,6 +151,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();
|
||||
@@ -192,6 +197,14 @@ namespace Content.Server.GameTicking
|
||||
_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;
|
||||
|
||||
@@ -304,7 +317,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))
|
||||
{
|
||||
@@ -330,11 +343,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;
|
||||
|
||||
@@ -350,7 +363,7 @@ namespace Content.Server.GameTicking
|
||||
return location;
|
||||
}
|
||||
|
||||
private GridCoordinates _getJobSpawnPoint(string jobId)
|
||||
public GridCoordinates GetJobSpawnPoint(string jobId)
|
||||
{
|
||||
var location = _spawnPoint;
|
||||
|
||||
@@ -367,6 +380,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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
74
Content.Server/Observer/Ghost.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
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;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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 = name;
|
||||
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
@@ -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,14 +180,14 @@ namespace Content.Shared.Chemistry
|
||||
}
|
||||
|
||||
newSolution = new Solution();
|
||||
var newTotalVolume = 0;
|
||||
var ratio = (float)(TotalVolume - quantity) / TotalVolume;
|
||||
var newTotalVolume = ReagentUnit.New(0M);
|
||||
var ratio = (TotalVolume - quantity).Decimal() / TotalVolume.Decimal();
|
||||
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
{
|
||||
var reagent = _contents[i];
|
||||
|
||||
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);
|
||||
@@ -193,7 +195,7 @@ namespace Content.Shared.Chemistry
|
||||
newTotalVolume += splitQuantity;
|
||||
}
|
||||
|
||||
TotalVolume = (int)Math.Floor(TotalVolume * ratio);
|
||||
TotalVolume = TotalVolume * ratio;
|
||||
newSolution.TotalVolume = newTotalVolume;
|
||||
|
||||
return newSolution;
|
||||
@@ -228,7 +230,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 +248,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -26,8 +26,8 @@ namespace Content.Shared.GameObjects.Components.Chemistry
|
||||
public class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public readonly bool HasBeaker;
|
||||
public readonly int BeakerCurrentVolume;
|
||||
public readonly int BeakerMaxVolume;
|
||||
public readonly ReagentUnit BeakerCurrentVolume;
|
||||
public readonly ReagentUnit BeakerMaxVolume;
|
||||
public readonly string ContainerName;
|
||||
/// <summary>
|
||||
/// A list of the reagents which this dispenser can dispense.
|
||||
@@ -38,10 +38,10 @@ namespace Content.Shared.GameObjects.Components.Chemistry
|
||||
/// </summary>
|
||||
public readonly List<Solution.ReagentQuantity> ContainerReagents;
|
||||
public readonly string DispenserName;
|
||||
public readonly int SelectedDispenseAmount;
|
||||
public readonly ReagentUnit SelectedDispenseAmount;
|
||||
|
||||
public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, int beakerCurrentVolume, int beakerMaxVolume, string containerName,
|
||||
List<ReagentDispenserInventoryEntry> inventory, string dispenserName, List<Solution.ReagentQuantity> containerReagents, int selectedDispenseAmount)
|
||||
public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, ReagentUnit beakerCurrentVolume, ReagentUnit beakerMaxVolume, string containerName,
|
||||
List<ReagentDispenserInventoryEntry> inventory, string dispenserName, List<Solution.ReagentQuantity> containerReagents, ReagentUnit selectedDispenseAmount)
|
||||
{
|
||||
HasBeaker = hasBeaker;
|
||||
BeakerCurrentVolume = beakerCurrentVolume;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Chemistry
|
||||
{
|
||||
public class SharedSolutionComponent : Component
|
||||
{
|
||||
public override string Name => "Solution";
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override uint? NetID => ContentNetIDs.SOLUTION;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class SolutionComponentState : ComponentState
|
||||
{
|
||||
public SolutionComponentState() : base(ContentNetIDs.SOLUTION) { }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new SolutionComponentState();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if(curState == null)
|
||||
return;
|
||||
|
||||
var compState = (SolutionComponentState)curState;
|
||||
|
||||
//TODO: Make me work!
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Chemistry
|
||||
{
|
||||
public class SolutionComponent : Component
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
[ViewVariables]
|
||||
protected Solution _containedSolution = new Solution();
|
||||
protected int _maxVolume;
|
||||
private SolutionCaps _capabilities;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the solution contents change.
|
||||
/// </summary>
|
||||
public event Action SolutionChanged;
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
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 string Name => "Solution";
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override uint? NetID => ContentNetIDs.SOLUTION;
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
RecalculateColor();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_containedSolution.RemoveAllSolution();
|
||||
_containedSolution = new Solution();
|
||||
}
|
||||
|
||||
public void RemoveAllSolution()
|
||||
{
|
||||
_containedSolution.RemoveAllSolution();
|
||||
OnSolutionChanged();
|
||||
}
|
||||
|
||||
public bool TryRemoveReagent(string reagentId, int quantity)
|
||||
{
|
||||
if (!ContainsReagent(reagentId, out var currentQuantity)) return false;
|
||||
|
||||
_containedSolution.RemoveReagent(reagentId, quantity);
|
||||
OnSolutionChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to remove the specified quantity from this solution
|
||||
/// </summary>
|
||||
/// <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)
|
||||
{
|
||||
if (CurrentVolume == 0)
|
||||
return false;
|
||||
|
||||
_containedSolution.RemoveSolution(quantity);
|
||||
OnSolutionChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Solution SplitSolution(int quantity)
|
||||
{
|
||||
var solutionSplit = _containedSolution.SplitSolution(quantity);
|
||||
OnSolutionChanged();
|
||||
return solutionSplit;
|
||||
}
|
||||
|
||||
protected void RecalculateColor()
|
||||
{
|
||||
if(_containedSolution.TotalVolume == 0)
|
||||
SubstanceColor = Color.White;
|
||||
|
||||
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 = BlendRGB(mixColor, proto.SubstanceColor, reagent.Quantity / runningTotalQuantity);
|
||||
}
|
||||
}
|
||||
|
||||
private Color BlendRGB(Color rgb1, Color rgb2, float amount)
|
||||
{
|
||||
var r = (float)Math.Round(rgb1.R + (rgb2.R - rgb1.R) * amount, 1);
|
||||
var g = (float)Math.Round(rgb1.G + (rgb2.G - rgb1.G) * amount, 1);
|
||||
var b = (float)Math.Round(rgb1.B + (rgb2.B - rgb1.B) * amount, 1);
|
||||
var alpha = (float)Math.Round(rgb1.A + (rgb2.A - rgb1.A) * amount, 1);
|
||||
|
||||
return new Color(r, g, b, alpha);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new SolutionComponentState();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if(curState == null)
|
||||
return;
|
||||
|
||||
var compState = (SolutionComponentState)curState;
|
||||
|
||||
//TODO: Make me work!
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class SolutionComponentState : ComponentState
|
||||
{
|
||||
public SolutionComponentState() : base(ContentNetIDs.SOLUTION) { }
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
protected virtual void OnSolutionChanged()
|
||||
{
|
||||
SolutionChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Command
|
||||
{
|
||||
public class SharedCommunicationsConsoleComponent : Component
|
||||
{
|
||||
public override string Name => "CommunicationsConsole";
|
||||
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class CommunicationsConsoleInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public readonly TimeSpan? ExpectedCountdownEnd;
|
||||
public readonly bool CountdownStarted;
|
||||
|
||||
public CommunicationsConsoleInterfaceState(TimeSpan? expectedCountdownEnd = null)
|
||||
{
|
||||
ExpectedCountdownEnd = expectedCountdownEnd;
|
||||
CountdownStarted = expectedCountdownEnd != null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class CommunicationsConsoleCallEmergencyShuttleMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public CommunicationsConsoleCallEmergencyShuttleMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class CommunicationsConsoleRecallEmergencyShuttleMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public CommunicationsConsoleRecallEmergencyShuttleMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum CommunicationsConsoleUiKey
|
||||
{
|
||||
Key
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Observer
|
||||
{
|
||||
public class SharedGhostComponent : Component
|
||||
{
|
||||
public override string Name => "Ghost";
|
||||
public override uint? NetID => ContentNetIDs.GHOST;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class GhostComponentState : ComponentState
|
||||
{
|
||||
public bool CanReturnToBody { get; }
|
||||
|
||||
public GhostComponentState(bool canReturnToBody) : base(ContentNetIDs.GHOST)
|
||||
{
|
||||
CanReturnToBody = canReturnToBody;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class ReturnToBodyComponentMessage : ComponentMessage
|
||||
{
|
||||
public ReturnToBodyComponentMessage() => Directed = true;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,109 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components
|
||||
{
|
||||
public abstract class SharedStackComponent : Component
|
||||
{
|
||||
private const string SerializationCache = "stack";
|
||||
|
||||
public sealed override string Name => "Stack";
|
||||
public sealed override uint? NetID => ContentNetIDs.STACK;
|
||||
|
||||
private int _count;
|
||||
private int _maxCount;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public virtual int Count
|
||||
{
|
||||
get => _count;
|
||||
set
|
||||
{
|
||||
_count = value;
|
||||
if (_count <= 0)
|
||||
{
|
||||
Owner.Delete();
|
||||
}
|
||||
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public int MaxCount
|
||||
{
|
||||
get => _maxCount;
|
||||
private set
|
||||
{
|
||||
_maxCount = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables] public int AvailableSpace => MaxCount - Count;
|
||||
|
||||
[ViewVariables] public object StackType { get; private set; }
|
||||
|
||||
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 override ComponentState GetComponentState()
|
||||
{
|
||||
return new StackComponentState(Count, MaxCount);
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
if (!(curState is StackComponentState cast))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Count = cast.Count;
|
||||
MaxCount = cast.MaxCount;
|
||||
}
|
||||
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class StackComponentState : ComponentState
|
||||
private sealed class StackComponentState : ComponentState
|
||||
{
|
||||
public int Count { get; }
|
||||
public int MaxCount { get; }
|
||||
@@ -22,4 +115,14 @@ namespace Content.Shared.GameObjects.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum StackType
|
||||
{
|
||||
Metal,
|
||||
Glass,
|
||||
Cable,
|
||||
Ointment,
|
||||
Brutepack,
|
||||
FloorTileSteel
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,5 +41,6 @@
|
||||
public const uint HANDHELD_LIGHT = 1036;
|
||||
public const uint PAPER = 1037;
|
||||
public const uint REAGENT_INJECTOR = 1038;
|
||||
public const uint GHOST = 1039;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,6 @@ namespace Content.Shared.Interfaces.Chemistry
|
||||
/// <param name="reagentId">The reagent id</param>
|
||||
/// <param name="tickTime">The time since the last metabolism tick in seconds.</param>
|
||||
/// <returns>The amount of reagent to be removed. The metabolizing organ should handle removing the reagent.</returns>
|
||||
int Metabolize(IEntity solutionEntity, string reagentId, float tickTime);
|
||||
ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime);
|
||||
}
|
||||
}
|
||||
|
||||
155
Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using Content.Shared.Chemistry;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
|
||||
namespace Content.Tests.Shared.Chemistry
|
||||
{
|
||||
[TestFixture, TestOf(typeof(ReagentUnit))]
|
||||
public class ReagentUnit_Tests
|
||||
{
|
||||
[Test]
|
||||
[TestCase(1, "1")]
|
||||
[TestCase(0, "0")]
|
||||
[TestCase(-1, "-1")]
|
||||
public void ReagentUnitIntegerTests(int value, string expected)
|
||||
{
|
||||
var result = ReagentUnit.New(value);
|
||||
Assert.AreEqual(expected, $"{result}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(1.001f, "1")]
|
||||
[TestCase(0.999f, "1")]
|
||||
public void ReagentUnitFloatTests(float value, string expected)
|
||||
{
|
||||
var result = ReagentUnit.New(value);
|
||||
Assert.AreEqual(expected, $"{result}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(1.001d, "1")]
|
||||
[TestCase(0.999d, "1")]
|
||||
public void ReagentUnitDoubleTests(double value, string expected)
|
||||
{
|
||||
var result = ReagentUnit.New(value);
|
||||
Assert.AreEqual(expected, $"{result}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("1.001", "1")]
|
||||
[TestCase("0.999", "1")]
|
||||
public void ReagentUnitDecimalTests(string valueAsString, string expected)
|
||||
{
|
||||
var value = decimal.Parse(valueAsString);
|
||||
var result = ReagentUnit.New(value);
|
||||
Assert.AreEqual(expected, $"{result}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("1.005", "1.01")]
|
||||
[TestCase("0.999", "1")]
|
||||
public void ReagentUnitStringTests(string value, string expected)
|
||||
{
|
||||
var result = ReagentUnit.New(value);
|
||||
Assert.AreEqual(expected, $"{result}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(1.001f, 1.001f, "2")]
|
||||
[TestCase(1.001f, 1.004f, "2")]
|
||||
[TestCase(1f, 1.005f, "2.01")]
|
||||
[TestCase(1f, 2.005f, "3.01")]
|
||||
public void CalculusPlus(float aFloat, float bFloat, string expected)
|
||||
{
|
||||
var a = ReagentUnit.New(aFloat);
|
||||
var b = ReagentUnit.New(bFloat);
|
||||
|
||||
var result = a + b;
|
||||
|
||||
Assert.AreEqual(expected, $"{result}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(1.001f, 1.001f, "0")]
|
||||
[TestCase(1.001f, 1.004f, "0")]
|
||||
[TestCase(1f, 2.005f, "-1.01")]
|
||||
public void CalculusMinus(float aFloat, float bFloat, string expected)
|
||||
{
|
||||
var a = ReagentUnit.New(aFloat);
|
||||
var b = ReagentUnit.New(bFloat);
|
||||
|
||||
var result = a - b;
|
||||
|
||||
Assert.AreEqual(expected, $"{result}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(1.001f, 3f, "0.33")]
|
||||
[TestCase(0.999f, 3f, "0.33")]
|
||||
[TestCase(2.1f, 3f, "0.7")]
|
||||
public void CalculusDivision(float aFloat, float bFloat, string expected)
|
||||
{
|
||||
var a = ReagentUnit.New(aFloat);
|
||||
var b = ReagentUnit.New(bFloat);
|
||||
|
||||
var result = a / b;
|
||||
|
||||
Assert.AreEqual(expected, $"{result}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(1.001f, 0.999f, "1")]
|
||||
[TestCase(0.999f, 3f, "3")]
|
||||
public void CalculusMultiplication(float aFloat, float bFloat, string expected)
|
||||
{
|
||||
var a = ReagentUnit.New(aFloat);
|
||||
var b = ReagentUnit.New(bFloat);
|
||||
|
||||
var result = a * b;
|
||||
|
||||
Assert.AreEqual(expected, $"{result}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(0.995f, 100)]
|
||||
[TestCase(1.005f, 101)]
|
||||
[TestCase(2.005f, 201)]
|
||||
public void FloatRoundingTest(float a, int expected)
|
||||
{
|
||||
var result = (int) Math.Round(a * (float) Math.Pow(10, 2), MidpointRounding.AwayFromZero);
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReagentUnitMin()
|
||||
{
|
||||
var unorderedList = new[]
|
||||
{
|
||||
ReagentUnit.New(5),
|
||||
ReagentUnit.New(3),
|
||||
ReagentUnit.New(1),
|
||||
ReagentUnit.New(2),
|
||||
ReagentUnit.New(4),
|
||||
};
|
||||
var min = ReagentUnit.Min(unorderedList);
|
||||
Assert.AreEqual(ReagentUnit.New(1), min);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(1, 0, false)]
|
||||
[TestCase(0, 0, true)]
|
||||
[TestCase(-1, 0, false)]
|
||||
[TestCase(null, 0, true)]
|
||||
[TestCase(1, 1, true)]
|
||||
[TestCase(0, 1, false)]
|
||||
[TestCase(-1, 1, false)]
|
||||
[TestCase(null, 1, false)]
|
||||
public void ReagentUnitEquals(int a, int b, bool expected)
|
||||
{
|
||||
var parameter = ReagentUnit.New(a);
|
||||
var comparison = ReagentUnit.New(b);
|
||||
Assert.AreEqual(comparison.Equals(parameter), parameter.Equals(comparison));
|
||||
Assert.AreEqual(expected, comparison.Equals(parameter));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,19 +10,19 @@ namespace Content.Tests.Shared.Chemistry
|
||||
public void AddReagentAndGetSolution()
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", 1000);
|
||||
solution.AddReagent("water", ReagentUnit.New(1000));
|
||||
var quantity = solution.GetReagentQuantity("water");
|
||||
|
||||
Assert.That(quantity, Is.EqualTo(1000));
|
||||
Assert.That(quantity.Int(), Is.EqualTo(1000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorAddReagent()
|
||||
{
|
||||
var solution = new Solution("water", 1000);
|
||||
var solution = new Solution("water", ReagentUnit.New(1000));
|
||||
var quantity = solution.GetReagentQuantity("water");
|
||||
|
||||
Assert.That(quantity, Is.EqualTo(1000));
|
||||
Assert.That(quantity.Int(), Is.EqualTo(1000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -31,223 +31,276 @@ namespace Content.Tests.Shared.Chemistry
|
||||
var solution = new Solution();
|
||||
var quantity = solution.GetReagentQuantity("water");
|
||||
|
||||
Assert.That(quantity, Is.EqualTo(0));
|
||||
Assert.That(quantity.Int(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddLessThanZeroReagentReturnsZero()
|
||||
{
|
||||
var solution = new Solution("water", -1000);
|
||||
var solution = new Solution("water", ReagentUnit.New(-1000));
|
||||
var quantity = solution.GetReagentQuantity("water");
|
||||
|
||||
Assert.That(quantity, Is.EqualTo(0));
|
||||
Assert.That(quantity.Int(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddingReagentsSumsProperly()
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", 1000);
|
||||
solution.AddReagent("water", 2000);
|
||||
solution.AddReagent("water", ReagentUnit.New(1000));
|
||||
solution.AddReagent("water", ReagentUnit.New(2000));
|
||||
var quantity = solution.GetReagentQuantity("water");
|
||||
|
||||
Assert.That(quantity, Is.EqualTo(3000));
|
||||
Assert.That(quantity.Int(), Is.EqualTo(3000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReagentQuantitiesStayUnique()
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", 1000);
|
||||
solution.AddReagent("fire", 2000);
|
||||
solution.AddReagent("water", ReagentUnit.New(1000));
|
||||
solution.AddReagent("fire", ReagentUnit.New(2000));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(1000));
|
||||
Assert.That(solution.GetReagentQuantity("fire"), Is.EqualTo(2000));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(1000));
|
||||
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TotalVolumeIsCorrect()
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", 1000);
|
||||
solution.AddReagent("fire", 2000);
|
||||
solution.AddReagent("water", ReagentUnit.New(1000));
|
||||
solution.AddReagent("fire", ReagentUnit.New(2000));
|
||||
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(3000));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(3000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CloningSolutionIsCorrect()
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", 1000);
|
||||
solution.AddReagent("fire", 2000);
|
||||
solution.AddReagent("water", ReagentUnit.New(1000));
|
||||
solution.AddReagent("fire", ReagentUnit.New(2000));
|
||||
|
||||
var newSolution = solution.Clone();
|
||||
|
||||
Assert.That(newSolution.GetReagentQuantity("water"), Is.EqualTo(1000));
|
||||
Assert.That(newSolution.GetReagentQuantity("fire"), Is.EqualTo(2000));
|
||||
Assert.That(newSolution.TotalVolume, Is.EqualTo(3000));
|
||||
Assert.That(newSolution.GetReagentQuantity("water").Int(), Is.EqualTo(1000));
|
||||
Assert.That(newSolution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000));
|
||||
Assert.That(newSolution.TotalVolume.Int(), Is.EqualTo(3000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveSolutionRecalculatesProperly()
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", 1000);
|
||||
solution.AddReagent("fire", 2000);
|
||||
solution.AddReagent("water", ReagentUnit.New(1000));
|
||||
solution.AddReagent("fire", ReagentUnit.New(2000));
|
||||
|
||||
solution.RemoveReagent("water", 500);
|
||||
solution.RemoveReagent("water", ReagentUnit.New(500));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(500));
|
||||
Assert.That(solution.GetReagentQuantity("fire"), Is.EqualTo(2000));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(2500));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(500));
|
||||
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(2500));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveLessThanOneQuantityDoesNothing()
|
||||
{
|
||||
var solution = new Solution("water", 100);
|
||||
var solution = new Solution("water", ReagentUnit.New(100));
|
||||
|
||||
solution.RemoveReagent("water", -100);
|
||||
solution.RemoveReagent("water", ReagentUnit.New(-100));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(100));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(100));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(100));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(100));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveMoreThanTotalRemovesAllReagent()
|
||||
{
|
||||
var solution = new Solution("water", 100);
|
||||
var solution = new Solution("water", ReagentUnit.New(100));
|
||||
|
||||
solution.RemoveReagent("water", 1000);
|
||||
solution.RemoveReagent("water", ReagentUnit.New(1000));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(0));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(0));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(0));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveNonExistReagentDoesNothing()
|
||||
{
|
||||
var solution = new Solution("water", 100);
|
||||
var solution = new Solution("water", ReagentUnit.New(100));
|
||||
|
||||
solution.RemoveReagent("fire", 1000);
|
||||
solution.RemoveReagent("fire", ReagentUnit.New(1000));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(100));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(100));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(100));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(100));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveSolution()
|
||||
{
|
||||
var solution = new Solution("water", 700);
|
||||
var solution = new Solution("water", ReagentUnit.New(700));
|
||||
|
||||
solution.RemoveSolution(500);
|
||||
solution.RemoveSolution(ReagentUnit.New(500));
|
||||
|
||||
//Check that edited solution is correct
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(200));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(200));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(200));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(200));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveSolutionMoreThanTotalRemovesAll()
|
||||
{
|
||||
var solution = new Solution("water", 800);
|
||||
var solution = new Solution("water", ReagentUnit.New(800));
|
||||
|
||||
solution.RemoveSolution(1000);
|
||||
solution.RemoveSolution(ReagentUnit.New(1000));
|
||||
|
||||
//Check that edited solution is correct
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(0));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(0));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(0));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveSolutionRatioPreserved()
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", 1000);
|
||||
solution.AddReagent("fire", 2000);
|
||||
solution.AddReagent("water", ReagentUnit.New(1000));
|
||||
solution.AddReagent("fire", ReagentUnit.New(2000));
|
||||
|
||||
solution.RemoveSolution(1500);
|
||||
solution.RemoveSolution(ReagentUnit.New(1500));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(500));
|
||||
Assert.That(solution.GetReagentQuantity("fire"), Is.EqualTo(1000));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(1500));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(500));
|
||||
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(1000));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(1500));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveSolutionLessThanOneDoesNothing()
|
||||
{
|
||||
var solution = new Solution("water", 800);
|
||||
var solution = new Solution("water", ReagentUnit.New(800));
|
||||
|
||||
solution.RemoveSolution(-200);
|
||||
solution.RemoveSolution(ReagentUnit.New(-200));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(800));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(800));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(800));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(800));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitSolution()
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", 1000);
|
||||
solution.AddReagent("fire", 2000);
|
||||
solution.AddReagent("water", ReagentUnit.New(1000));
|
||||
solution.AddReagent("fire", ReagentUnit.New(2000));
|
||||
|
||||
var splitSolution = solution.SplitSolution(750);
|
||||
var splitSolution = solution.SplitSolution(ReagentUnit.New(750));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(750));
|
||||
Assert.That(solution.GetReagentQuantity("fire"), Is.EqualTo(1500));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(2250));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(750));
|
||||
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(1500));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(2250));
|
||||
|
||||
Assert.That(splitSolution.GetReagentQuantity("water"), Is.EqualTo(250));
|
||||
Assert.That(splitSolution.GetReagentQuantity("fire"), Is.EqualTo(500));
|
||||
Assert.That(splitSolution.TotalVolume, Is.EqualTo(750));
|
||||
Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(250));
|
||||
Assert.That(splitSolution.GetReagentQuantity("fire").Int(), Is.EqualTo(500));
|
||||
Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(750));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitSolutionFractional()
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", ReagentUnit.New(1));
|
||||
solution.AddReagent("fire", ReagentUnit.New(2));
|
||||
|
||||
var splitSolution = solution.SplitSolution(ReagentUnit.New(1));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(0.67f));
|
||||
Assert.That(solution.GetReagentQuantity("fire").Float(), Is.EqualTo(1.33f));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(2));
|
||||
|
||||
Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(0.33f));
|
||||
Assert.That(splitSolution.GetReagentQuantity("fire").Float(), Is.EqualTo(0.67f));
|
||||
Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitSolutionFractionalOpposite()
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", ReagentUnit.New(1));
|
||||
solution.AddReagent("fire", ReagentUnit.New(2));
|
||||
|
||||
var splitSolution = solution.SplitSolution(ReagentUnit.New(2));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(0.33f));
|
||||
Assert.That(solution.GetReagentQuantity("fire").Float(), Is.EqualTo(0.67f));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(1));
|
||||
|
||||
Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(0.67f));
|
||||
Assert.That(splitSolution.GetReagentQuantity("fire").Float(), Is.EqualTo(1.33f));
|
||||
Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(0.03f, 0.01f, 0.02f)]
|
||||
[TestCase(0.03f, 0.02f, 0.01f)]
|
||||
public void SplitSolutionTinyFractionalBigSmall(float initial, float reduce, float remainder)
|
||||
{
|
||||
var solution = new Solution();
|
||||
solution.AddReagent("water", ReagentUnit.New(initial));
|
||||
|
||||
var splitSolution = solution.SplitSolution(ReagentUnit.New(reduce));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(remainder));
|
||||
Assert.That(solution.TotalVolume.Float(), Is.EqualTo(remainder));
|
||||
|
||||
Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(reduce));
|
||||
Assert.That(splitSolution.TotalVolume.Float(), Is.EqualTo(reduce));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitSolutionMoreThanTotalRemovesAll()
|
||||
{
|
||||
var solution = new Solution("water", 800);
|
||||
var solution = new Solution("water", ReagentUnit.New(800));
|
||||
|
||||
var splitSolution = solution.SplitSolution(1000);
|
||||
var splitSolution = solution.SplitSolution(ReagentUnit.New(1000));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(0));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(0));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(0));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(0));
|
||||
|
||||
Assert.That(splitSolution.GetReagentQuantity("water"), Is.EqualTo(800));
|
||||
Assert.That(splitSolution.TotalVolume, Is.EqualTo(800));
|
||||
Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(800));
|
||||
Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(800));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitSolutionLessThanOneDoesNothing()
|
||||
{
|
||||
var solution = new Solution("water", 800);
|
||||
var solution = new Solution("water", ReagentUnit.New(800));
|
||||
|
||||
var splitSolution = solution.SplitSolution(-200);
|
||||
var splitSolution = solution.SplitSolution(ReagentUnit.New(-200));
|
||||
|
||||
Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(800));
|
||||
Assert.That(solution.TotalVolume, Is.EqualTo(800));
|
||||
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(800));
|
||||
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(800));
|
||||
|
||||
Assert.That(splitSolution.GetReagentQuantity("water"), Is.EqualTo(0));
|
||||
Assert.That(splitSolution.TotalVolume, Is.EqualTo(0));
|
||||
Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(0));
|
||||
Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddSolution()
|
||||
{
|
||||
var solutionOne = new Solution();
|
||||
solutionOne.AddReagent("water", 1000);
|
||||
solutionOne.AddReagent("fire", 2000);
|
||||
solutionOne.AddReagent("water", ReagentUnit.New(1000));
|
||||
solutionOne.AddReagent("fire", ReagentUnit.New(2000));
|
||||
|
||||
var solutionTwo = new Solution();
|
||||
solutionTwo.AddReagent("water", 500);
|
||||
solutionTwo.AddReagent("earth", 1000);
|
||||
solutionTwo.AddReagent("water", ReagentUnit.New(500));
|
||||
solutionTwo.AddReagent("earth", ReagentUnit.New(1000));
|
||||
|
||||
solutionOne.AddSolution(solutionTwo);
|
||||
|
||||
Assert.That(solutionOne.GetReagentQuantity("water"), Is.EqualTo(1500));
|
||||
Assert.That(solutionOne.GetReagentQuantity("fire"), Is.EqualTo(2000));
|
||||
Assert.That(solutionOne.GetReagentQuantity("earth"), Is.EqualTo(1000));
|
||||
Assert.That(solutionOne.TotalVolume, Is.EqualTo(4500));
|
||||
Assert.That(solutionOne.GetReagentQuantity("water").Int(), Is.EqualTo(1500));
|
||||
Assert.That(solutionOne.GetReagentQuantity("fire").Int(), Is.EqualTo(2000));
|
||||
Assert.That(solutionOne.GetReagentQuantity("earth").Int(), Is.EqualTo(1000));
|
||||
Assert.That(solutionOne.TotalVolume.Int(), Is.EqualTo(4500));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Resources/Audio/effects/glassbreak1.ogg
Normal file
BIN
Resources/Audio/effects/glassbreak2.ogg
Normal file
BIN
Resources/Audio/effects/glassbreak3.ogg
Normal file
@@ -11,6 +11,7 @@
|
||||
- ooc
|
||||
- observe
|
||||
- toggleready
|
||||
- ghost
|
||||
|
||||
- Index: 50
|
||||
Name: Moderator
|
||||
@@ -26,6 +27,7 @@
|
||||
- showtime
|
||||
- observe
|
||||
- toggleready
|
||||
- ghost
|
||||
- kick
|
||||
- listplayers
|
||||
- loc
|
||||
@@ -44,6 +46,7 @@
|
||||
- aghost
|
||||
- observe
|
||||
- toggleready
|
||||
- ghost
|
||||
- spawn
|
||||
- delete
|
||||
- tp
|
||||
@@ -84,6 +87,7 @@
|
||||
- aghost
|
||||
- observe
|
||||
- toggleready
|
||||
- ghost
|
||||
- spawn
|
||||
- delete
|
||||
- tp
|
||||
|
||||
@@ -18,3 +18,7 @@
|
||||
- chem.Ale
|
||||
- chem.Wine
|
||||
- chem.Ice
|
||||
- chem.Beer
|
||||
- chem.Vodka
|
||||
- chem.Cognac
|
||||
- chem.Kahlua
|
||||
|
||||
@@ -37,3 +37,4 @@
|
||||
- chem.K
|
||||
- chem.Ra
|
||||
- chem.Na
|
||||
- chem.U
|
||||
|
||||
@@ -176,3 +176,8 @@
|
||||
- type: ComputerVisualizer2D
|
||||
key: generic_key
|
||||
screen: comm
|
||||
- type: CommunicationsConsole
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.CommunicationsConsoleUiKey.Key
|
||||
type: CommunicationsConsoleBoundUserInterface
|
||||
|
||||
@@ -19,3 +19,4 @@
|
||||
- chem.Tea
|
||||
- chem.Ice
|
||||
- chem.H2O
|
||||
- chem.Cream
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
description: One sip of this and you just know you're gonna have a good time.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleAbsinthe
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/absinthebottle.rsi
|
||||
- type: Icon
|
||||
@@ -23,12 +17,6 @@
|
||||
description: A bottle of 46 proof Emeraldine Melon Liquor. Sweet and light.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleAlcoClear
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/alco-green.rsi
|
||||
- type: Icon
|
||||
@@ -40,13 +28,12 @@
|
||||
name: Magm-Ale
|
||||
description: A true dorf's drink of choice.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleAle
|
||||
- type: Solution
|
||||
maxVol: 80
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- ReagentId: chem.Ale
|
||||
Quantity: 80
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/alebottle.rsi
|
||||
- type: Icon
|
||||
@@ -59,12 +46,6 @@
|
||||
description: A bottle filled with nothing
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleAlcoClear
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/bottleofnothing.rsi
|
||||
- type: Icon
|
||||
@@ -76,13 +57,12 @@
|
||||
name: Cognac bottle
|
||||
description: A sweet and strongly alchoholic drink, made after numerous distillations and years of maturing. You might as well not scream 'SHITCURITY' this time.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleCognac
|
||||
- type: Solution
|
||||
maxVol: 80
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- ReagentId: chem.Cognac
|
||||
Quantity: 80
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/cognacbottle.rsi
|
||||
- type: Icon
|
||||
@@ -95,12 +75,6 @@
|
||||
description: A bottle of high quality gin, produced in the New London Space Station.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleGin
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/ginbottle.rsi
|
||||
- type: Icon
|
||||
@@ -113,12 +87,6 @@
|
||||
description: 100 proof cinnamon schnapps, made for alcoholic teen girls on spring break.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleGoldschlager
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/goldschlagerbottle.rsi
|
||||
- type: Icon
|
||||
@@ -130,13 +98,12 @@
|
||||
name: Kahlua bottle
|
||||
description: A widely known, Mexican coffee-flavoured liqueur. In production since 1936, HONK
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleKahlua
|
||||
- type: Solution
|
||||
maxVol: 80
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
Quantity: 80
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/kahluabottle.rsi
|
||||
- type: Icon
|
||||
@@ -149,12 +116,6 @@
|
||||
description: Silver laced tequilla, served in space night clubs across the galaxy.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottlePatron
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/patronbottle.rsi
|
||||
- type: Icon
|
||||
@@ -167,12 +128,6 @@
|
||||
description: What a delightful packaging for a surely high quality wine! The vintage must be amazing!
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottlePoisonWine
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/pwinebottle.rsi
|
||||
- type: Icon
|
||||
@@ -185,12 +140,6 @@
|
||||
description: This isn't just rum, oh no. It's practically GRIFF in a bottle.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleRum
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/rumbottle.rsi
|
||||
- type: Icon
|
||||
@@ -203,12 +152,6 @@
|
||||
description: Made from premium petroleum distillates, pure thalidomide and other fine quality ingredients!
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleTequila
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/tequillabottle.rsi
|
||||
- type: Icon
|
||||
@@ -221,12 +164,6 @@
|
||||
description: Sweet, sweet dryness~
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleVermouth
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/vermouthbottle.rsi
|
||||
- type: Icon
|
||||
@@ -238,13 +175,12 @@
|
||||
name: Vodka bottle
|
||||
description: Aah, vodka. Prime choice of drink AND fuel by Russians worldwide.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleVodka
|
||||
- type: Solution
|
||||
maxVol: 80
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- ReagentId: chem.Vodka
|
||||
Quantity: 80
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/vodkabottle.rsi
|
||||
- type: Icon
|
||||
@@ -256,13 +192,12 @@
|
||||
name: Uncle Git's special reserve
|
||||
description: A premium single-malt whiskey, gently matured inside the tunnels of a nuclear shelter. TUNNEL WHISKEY RULES.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleWhiskey
|
||||
- type: Solution
|
||||
maxVol: 80
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- ReagentId: chem.Whiskey
|
||||
Quantity: 80
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/whiskeybottle.rsi
|
||||
- type: Icon
|
||||
@@ -274,13 +209,12 @@
|
||||
name: Doublebearded bearded special wine bottle
|
||||
description: A faint aura of unease and asspainery surrounds the bottle.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
spawn_on_finish: DrinkBottleWine
|
||||
- type: Solution
|
||||
maxVol: 80
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 10
|
||||
- ReagentId: chem.Wine
|
||||
Quantity: 80
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/winebottle.rsi
|
||||
- type: Icon
|
||||
|
||||
@@ -30,11 +30,12 @@
|
||||
name: Space cola
|
||||
description: A refreshing beverage.
|
||||
components:
|
||||
- type: Drink
|
||||
- type: Solution
|
||||
maxVol: 20
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 4
|
||||
- ReagentId: chem.Cola
|
||||
Quantity: 20
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/cola.rsi
|
||||
- type: Icon
|
||||
@@ -62,10 +63,6 @@
|
||||
description: ''
|
||||
components:
|
||||
- type: Drink
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 4
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/ice_tea_can.rsi
|
||||
- type: Icon
|
||||
@@ -93,10 +90,6 @@
|
||||
description: You wanted ORANGE. It gave you Lemon Lime.
|
||||
components:
|
||||
- type: Drink
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 4
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/lemon-lime.rsi
|
||||
- type: Icon
|
||||
@@ -124,10 +117,6 @@
|
||||
description: ''
|
||||
components:
|
||||
- type: Drink
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 4
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/purple_can.rsi
|
||||
- type: Icon
|
||||
@@ -155,10 +144,6 @@
|
||||
description: Blows right through you like a space wind.
|
||||
components:
|
||||
- type: Drink
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 4
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/space_mountain_wind.rsi
|
||||
- type: Icon
|
||||
@@ -186,10 +171,6 @@
|
||||
description: Tastes like a hull breach in your mouth.
|
||||
components:
|
||||
- type: Drink
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 4
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/space-up.rsi
|
||||
- type: Icon
|
||||
@@ -217,10 +198,6 @@
|
||||
description: The taste of a star in liquid form. And, a bit of tuna...?
|
||||
components:
|
||||
- type: Drink
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 4
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/starkist.rsi
|
||||
- type: Icon
|
||||
@@ -248,10 +225,6 @@
|
||||
description: The MBO has advised crew members that consumption of Thirteen Loko may result in seizures, blindness, drunkeness, or even death. Please Drink Responsibly.
|
||||
components:
|
||||
- type: Drink
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 4
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/thirteen_loko.rsi
|
||||
- type: Icon
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
name: Base cup
|
||||
abstract: true
|
||||
components:
|
||||
- type: Solution
|
||||
maxVol: 20
|
||||
- type: Pourable
|
||||
transferAmount: 5
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
despawn_empty: false
|
||||
- type: Sound
|
||||
- type: Sprite
|
||||
@@ -20,8 +23,8 @@
|
||||
name: Golden cup
|
||||
description: A golden cup
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 10
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/golden_cup.rsi
|
||||
- type: Icon
|
||||
@@ -33,8 +36,8 @@
|
||||
name: Insulated pitcher
|
||||
description: A stainless steel insulated pitcher. Everyone's best friend in the morning.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 15
|
||||
- type: Solution
|
||||
maxVol: 15
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/pitcher.rsi
|
||||
state: icon-6
|
||||
@@ -52,8 +55,8 @@
|
||||
name: Mug
|
||||
description: A plain white mug.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/mug.rsi
|
||||
state: icon-3
|
||||
@@ -71,8 +74,8 @@
|
||||
name: Mug Black
|
||||
description: A sleek black mug.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/mug_black.rsi
|
||||
state: icon-3
|
||||
@@ -90,8 +93,8 @@
|
||||
name: Mug Blue
|
||||
description: A blue and black mug.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/mug_blue.rsi
|
||||
state: icon-3
|
||||
@@ -109,8 +112,8 @@
|
||||
name: Mug Green
|
||||
description: A pale green and pink mug.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/mug_green.rsi
|
||||
state: icon-3
|
||||
@@ -128,8 +131,8 @@
|
||||
name: Mug Heart
|
||||
description: A white mug, it prominently features a red heart.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/mug_heart.rsi
|
||||
state: icon-3
|
||||
@@ -147,8 +150,8 @@
|
||||
name: Mug Metal
|
||||
description: A metal mug. You're not sure which metal.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/mug_metal.rsi
|
||||
state: icon-3
|
||||
@@ -166,8 +169,8 @@
|
||||
name: Mug Moebius
|
||||
description: A mug with a Moebius Laboratories logo on it. Not even your morning coffee is safe from corporate advertising.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/mug_moebius.rsi
|
||||
state: icon-3
|
||||
@@ -185,8 +188,8 @@
|
||||
name: "#1 mug"
|
||||
description: "A white mug, it prominently features a #1."
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/mug_one.rsi
|
||||
state: icon-3
|
||||
@@ -204,8 +207,8 @@
|
||||
name: Mug Rainbow
|
||||
description: A rainbow mug. The colors are almost as blinding as a welder.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/mug_rainbow.rsi
|
||||
state: icon-3
|
||||
@@ -223,8 +226,8 @@
|
||||
name: Mug Red
|
||||
description: A red and black mug.
|
||||
components:
|
||||
- type: Drink
|
||||
max_volume: 4
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Sprite
|
||||
sprite: Objects/Drinks/mug_red.rsi
|
||||
state: icon-3
|
||||
|
||||
@@ -10,13 +10,12 @@
|
||||
state: icon
|
||||
- type: Icon
|
||||
state: icon
|
||||
- type: Solution
|
||||
maxVol: 10
|
||||
- type: Pourable
|
||||
transferAmount: 5
|
||||
- type: Drink
|
||||
despawn_empty: false
|
||||
max_volume: 10
|
||||
contents:
|
||||
reagents:
|
||||
- ReagentId: chem.H2O
|
||||
Quantity: 0
|
||||
|
||||
# Containers
|
||||
- type: entity
|
||||
@@ -89,19 +88,6 @@
|
||||
- type: Icon
|
||||
sprite: Objects/TrashDrinks/ginbottle_empty.rsi
|
||||
|
||||
# Couldn't think of a nice place to put this
|
||||
- type: entity
|
||||
name: Empty glass
|
||||
parent: DrinkBottleBase
|
||||
id: DrinkEmptyGlass
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/TrashDrinks/alebottle_empty.rsi
|
||||
- type: Icon
|
||||
sprite: Objects/TrashDrinks/alebottle_empty.rsi
|
||||
- type: Solution
|
||||
max_volume: 4
|
||||
|
||||
- type: entity
|
||||
name: Goldschlager bottle
|
||||
parent: DrinkBottleBase
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
- type: Icon
|
||||
texture: Objects/Chemistry/chemicals.rsi/beaker.png
|
||||
- type: Solution
|
||||
fillingState: beaker
|
||||
maxVol: 50
|
||||
caps: 27
|
||||
- type: Pourable
|
||||
transferAmount: 5
|
||||
transferAmount: 5.0
|
||||
|
||||
- type: entity
|
||||
name: Large Beaker
|
||||
@@ -25,10 +26,11 @@
|
||||
- type: Icon
|
||||
texture: Objects/Chemistry/chemicals.rsi/beakerlarge.png
|
||||
- type: Solution
|
||||
fillingState: beakerlarge
|
||||
maxVol: 100
|
||||
caps: 27
|
||||
- type: Pourable
|
||||
transferAmount: 5
|
||||
transferAmount: 5.0
|
||||
|
||||
- type: entity
|
||||
name: Dropper
|
||||
@@ -41,10 +43,12 @@
|
||||
- type: Icon
|
||||
texture: Objects/Chemistry/chemicals.rsi/dropper.png
|
||||
- type: Solution
|
||||
fillingState: dropper
|
||||
fillingSteps: 2
|
||||
maxVol: 5
|
||||
caps: 19
|
||||
- type: Pourable
|
||||
transferAmount: 5
|
||||
transferAmount: 5.0
|
||||
|
||||
- type: entity
|
||||
name: Syringe
|
||||
@@ -53,10 +57,12 @@
|
||||
id: Syringe
|
||||
components:
|
||||
- type: Sprite
|
||||
texture: Objects/Chemistry/chemicals.rsi/syringeproj.png
|
||||
texture: Objects/Chemistry/syringe.rsi/0.png
|
||||
- type: Icon
|
||||
texture: Objects/Chemistry/chemicals.rsi/syringeproj.png
|
||||
texture: Objects/Chemistry/syringe.rsi/0.png
|
||||
- type: Solution
|
||||
fillingState: syringe
|
||||
fillingSteps: 5
|
||||
maxVol: 15
|
||||
caps: 19
|
||||
- type: Injector
|
||||
|
||||
@@ -14,11 +14,14 @@
|
||||
- type: Hunger
|
||||
- type: Thirst
|
||||
# Organs
|
||||
- type: Stomach
|
||||
maxVolume: 100
|
||||
digestionDelay: 20
|
||||
- type: Solution
|
||||
maxVol: 250
|
||||
- type: Bloodstream
|
||||
maxVolume: 250
|
||||
max_volume: 100
|
||||
- type: Stomach
|
||||
max_volume: 250
|
||||
digestionDelay: 20
|
||||
|
||||
|
||||
- type: Inventory
|
||||
- type: Constructor
|
||||
@@ -147,8 +150,6 @@
|
||||
hands:
|
||||
- left
|
||||
- right
|
||||
# Organs
|
||||
- type: Stomach
|
||||
|
||||
- type: Inventory
|
||||
- type: Sprite
|
||||
|
||||
@@ -15,3 +15,8 @@
|
||||
- type: Examiner
|
||||
DoRangeCheck: false
|
||||
- type: IgnorePause
|
||||
- type: Ghost
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
drawdepth: Mobs
|
||||
texture: Mob/observer.png
|
||||
|
||||
103
Resources/Prototypes/Reactions/drinks.yml
Normal file
@@ -0,0 +1,103 @@
|
||||
- type: reaction
|
||||
id: react.ManlyDorf
|
||||
reactants:
|
||||
chem.Beer:
|
||||
amount: 1
|
||||
chem.Ale:
|
||||
amount: 2
|
||||
products:
|
||||
chem.ManlyDorf: 3
|
||||
|
||||
- type: reaction
|
||||
id: react.CubaLibre
|
||||
reactants:
|
||||
chem.Cola:
|
||||
amount: 1
|
||||
chem.Rum:
|
||||
amount: 2
|
||||
products:
|
||||
chem.CubaLibre: 3
|
||||
|
||||
- type: reaction
|
||||
id: react.IrishCream
|
||||
reactants:
|
||||
chem.Cream:
|
||||
amount: 1
|
||||
chem.Whiskey:
|
||||
amount: 2
|
||||
products:
|
||||
chem.IrishCream: 3
|
||||
|
||||
- type: reaction
|
||||
id: react.IrishCoffee
|
||||
reactants:
|
||||
chem.IrishCream:
|
||||
amount: 2
|
||||
chem.Coffee:
|
||||
amount: 2
|
||||
products:
|
||||
chem.IrishCoffee: 4
|
||||
|
||||
- type: reaction
|
||||
id: react.IrishCarBomb
|
||||
reactants:
|
||||
chem.IrishCream:
|
||||
amount: 1
|
||||
chem.Ale:
|
||||
amount: 1
|
||||
products:
|
||||
chem.IrishCarBomb: 2
|
||||
|
||||
- type: reaction
|
||||
id: react.B52
|
||||
reactants:
|
||||
chem.IrishCarBomb:
|
||||
amount: 1
|
||||
chem.Kahlua:
|
||||
amount: 1
|
||||
chem.Cognac:
|
||||
amount: 1
|
||||
products:
|
||||
chem.B52: 3
|
||||
|
||||
- type: reaction
|
||||
id: react.AtomicBomb
|
||||
reactants:
|
||||
chem.B52:
|
||||
amount: 10
|
||||
chem.U:
|
||||
amount: 1
|
||||
products:
|
||||
chem.AtomicBomb: 11
|
||||
|
||||
- type: reaction
|
||||
id: react.WhiskeyCola
|
||||
reactants:
|
||||
chem.Whiskey:
|
||||
amount: 2
|
||||
chem.Cola:
|
||||
amount: 1
|
||||
products:
|
||||
chem.WhiskeyCola: 3
|
||||
|
||||
- type: reaction
|
||||
id: react.SyndicateBomb
|
||||
reactants:
|
||||
chem.WhiskeyCola:
|
||||
amount: 1
|
||||
chem.Beer:
|
||||
amount: 1
|
||||
products:
|
||||
chem.SyndicateBomb: 2
|
||||
|
||||
- type: reaction
|
||||
id: react.Antifreeze
|
||||
reactants:
|
||||
chem.Vodka:
|
||||
amount: 2
|
||||
chem.Cream:
|
||||
amount: 1
|
||||
chem.Ice:
|
||||
amount: 1
|
||||
products:
|
||||
chem.Antifreeze: 4
|
||||
@@ -2,6 +2,7 @@
|
||||
id: chem.Nutriment
|
||||
name: Nutriment
|
||||
desc: Generic nutrition
|
||||
color: "#664330"
|
||||
metabolism:
|
||||
- !type:DefaultFood
|
||||
rate: 1
|
||||
@@ -10,11 +11,13 @@
|
||||
id: chem.H2SO4
|
||||
name: Sulfuric Acid
|
||||
desc: A highly corrosive, oily, colorless liquid.
|
||||
color: "#BF8C00"
|
||||
|
||||
- type: reagent
|
||||
id: chem.H2O
|
||||
name: Water
|
||||
desc: A tasty colorless liquid.
|
||||
color: "#808080"
|
||||
metabolism:
|
||||
- !type:DefaultDrink
|
||||
rate: 1
|
||||
@@ -29,6 +32,7 @@
|
||||
id: chem.Plasma
|
||||
name: Plasma
|
||||
desc: Funky, space-magic pixie dust. You probably shouldn't eat this, but we both know you will anyways.
|
||||
color: "#500064"
|
||||
|
||||
- type: reagent
|
||||
id: chem.Ethanol
|
||||
|
||||
@@ -2,21 +2,126 @@
|
||||
id: chem.Whiskey
|
||||
name: Whiskey
|
||||
desc: An alcoholic beverage made from fermented grain mash
|
||||
color: "#664300"
|
||||
spritePath: whiskeyglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.Ale
|
||||
name: Ale
|
||||
desc: A type of beer brewed using a warm fermentation method, resulting in a sweet, full-bodied and fruity taste.
|
||||
color: "#664300"
|
||||
spritePath: aleglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.Wine
|
||||
name: Wine
|
||||
desc: An alcoholic drink made from fermented grapes
|
||||
color: "#7E4043"
|
||||
spritePath: wineglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.Beer
|
||||
name: Beer
|
||||
desc: A cold pint of pale lager.
|
||||
color: "#664300"
|
||||
spritePath: beerglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.Vodka
|
||||
name: Vodka
|
||||
desc: The glass contain wodka. Xynta.
|
||||
color: "#664300"
|
||||
|
||||
- type: reagent
|
||||
id: chem.Kahlua
|
||||
name: Kahlua
|
||||
desc: A widely known, Mexican coffee-flavoured liqueur. In production since 1936!
|
||||
color: "#664300"
|
||||
spritePath: kahluaglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.Cognac
|
||||
name: Cognac
|
||||
desc: A sweet and strongly alcoholic drink, twice distilled and left to mature for several years. Classy as fornication.
|
||||
color: "#AB3C05"
|
||||
spritePath: cognacglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.ManlyDorf
|
||||
name: Manly Dorf
|
||||
desc: A dwarfy concoction made from ale and beer. Intended for stout dwarves only.
|
||||
color: "#664300"
|
||||
spritePath: manlydorfglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.CubaLibre
|
||||
name: Cuba Libre
|
||||
desc: A classic mix of rum and cola.
|
||||
color: "#3E1B00"
|
||||
spritePath: cubalibreglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.IrishCarBomb
|
||||
name: Irish Car Bomb
|
||||
desc: A troubling mixture of irish cream and ale.
|
||||
color: "#2E6671"
|
||||
spritePath: irishcarbomb.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.IrishCoffee
|
||||
name: Irish Coffee
|
||||
desc: Coffee served with irish cream. Regular cream just isn't the same!
|
||||
color: "#664300"
|
||||
spritePath: irishcoffeeglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.IrishCream
|
||||
name: Irish Cream
|
||||
desc: Whiskey-imbued cream. What else could you expect from the Irish.
|
||||
color: "#664300"
|
||||
spritePath: irishcreamglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.B52
|
||||
name: B-52
|
||||
desc: Coffee, irish cream, and cognac. You will get bombed.
|
||||
color: "#664300"
|
||||
spritePath: b52glass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.AtomicBomb
|
||||
name: Atomic Bomb
|
||||
desc: Nuclear proliferation never tasted so good.
|
||||
color: "#666300"
|
||||
spritePath: atomicbombglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.WhiskeyCola
|
||||
name: Whiskey Cola
|
||||
desc: An innocent-looking mixture of cola and whiskey. Delicious.
|
||||
color: "#3E1B00"
|
||||
spritePath: whiskeycolaglass.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.SyndicateBomb
|
||||
name: Syndicate Bomb
|
||||
desc: Somebody set us up the bomb!
|
||||
color: "#2E6671"
|
||||
spritePath: syndicatebomb.rsi
|
||||
|
||||
- type: reagent
|
||||
id: chem.Antifreeze
|
||||
name: Antifreeze
|
||||
desc: The ultimate refreshment.
|
||||
color: "#664300"
|
||||
spritePath: antifreeze.rsi
|
||||
|
||||
|
||||
- type: reagent
|
||||
id: chem.Cola
|
||||
name: Cola
|
||||
desc: A sweet, carbonated soft drink. Caffeine free.
|
||||
color: "#100800"
|
||||
metabolism:
|
||||
- !type:DefaultDrink
|
||||
rate: 1
|
||||
@@ -25,6 +130,7 @@
|
||||
id: chem.Coffee
|
||||
name: Coffee
|
||||
desc: A drink made from brewed coffee beans. Contains a moderate amount of caffeine.
|
||||
color: "#664300"
|
||||
metabolism:
|
||||
- !type:DefaultDrink
|
||||
rate: 1
|
||||
@@ -33,6 +139,25 @@
|
||||
id: chem.Tea
|
||||
name: Tea
|
||||
desc: A made by boiling leaves of the tea tree, Camellia sinensis.
|
||||
color: "#101000"
|
||||
metabolism:
|
||||
- !type:DefaultDrink
|
||||
rate: 1
|
||||
|
||||
- type: reagent
|
||||
id: chem.Cream
|
||||
name: Cream
|
||||
desc: The fatty, still liquid part of milk. Why don't you mix this with sum scotch, eh?
|
||||
color: "#DFD7AF"
|
||||
metabolism:
|
||||
- !type:DefaultDrink
|
||||
rate: 1
|
||||
|
||||
- type: reagent
|
||||
id: chem.Milk
|
||||
name: Milk
|
||||
desc: An opaque white liquid produced by the mammary glands of mammals.
|
||||
color: "#DFDFDF"
|
||||
metabolism:
|
||||
- !type:DefaultDrink
|
||||
rate: 1
|
||||
@@ -2,11 +2,13 @@
|
||||
id: chem.H
|
||||
name: Hydrogen
|
||||
desc: A light, flammable gas.
|
||||
color: "#808080"
|
||||
|
||||
- type: reagent
|
||||
id: chem.O
|
||||
name: Oxygen
|
||||
desc: An oxidizing, colorless gas.
|
||||
color: "#808080"
|
||||
|
||||
- type: reagent
|
||||
id: chem.S
|
||||
@@ -36,6 +38,7 @@
|
||||
id: chem.N
|
||||
name: Nitrogen
|
||||
desc: A colorless, odorless unreactive gas. Highly stable.
|
||||
color: "#808080"
|
||||
|
||||
- type: reagent
|
||||
id: chem.Fe
|
||||
@@ -47,6 +50,7 @@
|
||||
id: chem.F
|
||||
name: Fluorine
|
||||
desc: A highly toxic pale yellow gas. Extremely reactive.
|
||||
color: "#808080"
|
||||
|
||||
- type: reagent
|
||||
id: chem.Si
|
||||
@@ -95,3 +99,9 @@
|
||||
name: Sodium
|
||||
desc: A silvery-white alkali metal. Highly reactive in it's pure form.
|
||||
color: "#c6c8cc"
|
||||
|
||||
- type: reagent
|
||||
id: chem.U
|
||||
name: Uranium
|
||||
desc: A silvery-white metallic chemical element in the actinide series, weakly radioactive.
|
||||
color: "#00ff06"
|
||||
6
Resources/Prototypes/SoundCollections/glassbreak.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
- type: sound_collection
|
||||
id: glassbreak
|
||||
files:
|
||||
- /Audio/effects/glassbreak1.ogg
|
||||
- /Audio/effects/glassbreak2.ogg
|
||||
- /Audio/effects/glassbreak3.ogg
|
||||
BIN
Resources/Textures/Objects/Chemistry/fillings.rsi/backpack1.png
Normal file
|
After Width: | Height: | Size: 127 B |
BIN
Resources/Textures/Objects/Chemistry/fillings.rsi/backpack2.png
Normal file
|
After Width: | Height: | Size: 149 B |
|
After Width: | Height: | Size: 164 B |
|
After Width: | Height: | Size: 173 B |
BIN
Resources/Textures/Objects/Chemistry/fillings.rsi/beaker1.png
Normal file
|
After Width: | Height: | Size: 137 B |
BIN
Resources/Textures/Objects/Chemistry/fillings.rsi/beaker2.png
Normal file
|
After Width: | Height: | Size: 145 B |
BIN
Resources/Textures/Objects/Chemistry/fillings.rsi/beaker3.png
Normal file
|
After Width: | Height: | Size: 151 B |
BIN
Resources/Textures/Objects/Chemistry/fillings.rsi/beaker4.png
Normal file
|
After Width: | Height: | Size: 162 B |
BIN
Resources/Textures/Objects/Chemistry/fillings.rsi/beaker5.png
Normal file
|
After Width: | Height: | Size: 160 B |
BIN
Resources/Textures/Objects/Chemistry/fillings.rsi/beaker6.png
Normal file
|
After Width: | Height: | Size: 167 B |
|
After Width: | Height: | Size: 129 B |
|
After Width: | Height: | Size: 146 B |
|
After Width: | Height: | Size: 166 B |
|
After Width: | Height: | Size: 164 B |