From 6905394e8a39bcd3dbcc2c769d7c8c345e594737 Mon Sep 17 00:00:00 2001 From: zumorica Date: Tue, 3 Mar 2020 18:04:16 +0100 Subject: [PATCH 01/54] Add placeholder ghost UI, ghost component --- Content.Client/Observer/GhostComponent.cs | 53 +++++++++++++++++++ Content.Client/UserInterface/GhostGui.cs | 18 +++++++ .../Observer/SharedGhostComponent.cs | 9 ++++ .../Prototypes/Entities/mobs/observer.yml | 1 + 4 files changed, 81 insertions(+) create mode 100644 Content.Client/Observer/GhostComponent.cs create mode 100644 Content.Client/UserInterface/GhostGui.cs create mode 100644 Content.Shared/Observer/SharedGhostComponent.cs diff --git a/Content.Client/Observer/GhostComponent.cs b/Content.Client/Observer/GhostComponent.cs new file mode 100644 index 0000000000..31f45d84b8 --- /dev/null +++ b/Content.Client/Observer/GhostComponent.cs @@ -0,0 +1,53 @@ +using Content.Client.UserInterface; +using Content.Shared.Observer; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Network; +using Robust.Shared.IoC; + +namespace Content.Client.Observer +{ + [RegisterComponent] + public class GhostComponent : SharedGhostComponent + { + private GhostGui _gui; + +#pragma warning disable 649 + [Dependency] private readonly IGameHud _gameHud; +#pragma warning restore 649 + + public override void OnRemove() + { + base.OnRemove(); + + _gui?.Dispose(); + } + + 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(); + } + else + { + _gui.Parent?.RemoveChild(_gui); + } + + _gameHud.HandsContainer.AddChild(_gui); + break; + + case PlayerDetachedMsg _: + _gui.Parent?.RemoveChild(_gui); + break; + } + } + } +} diff --git a/Content.Client/UserInterface/GhostGui.cs b/Content.Client/UserInterface/GhostGui.cs new file mode 100644 index 0000000000..63f7a870ba --- /dev/null +++ b/Content.Client/UserInterface/GhostGui.cs @@ -0,0 +1,18 @@ +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.IoC; + +namespace Content.Client.UserInterface +{ + public class GhostGui : Control + { + public GhostGui() + { + IoCManager.InjectDependencies(this); + + MouseFilter = MouseFilterMode.Ignore; + + AddChild(new Label(){Text = "YES THIS IS GHOST WHOOOOOO"}); + } + } +} diff --git a/Content.Shared/Observer/SharedGhostComponent.cs b/Content.Shared/Observer/SharedGhostComponent.cs new file mode 100644 index 0000000000..dca348b285 --- /dev/null +++ b/Content.Shared/Observer/SharedGhostComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameObjects; + +namespace Content.Shared.Observer +{ + public class SharedGhostComponent : Component + { + public override string Name => "Ghost"; + } +} diff --git a/Resources/Prototypes/Entities/mobs/observer.yml b/Resources/Prototypes/Entities/mobs/observer.yml index c99257b300..8fc3adb84f 100644 --- a/Resources/Prototypes/Entities/mobs/observer.yml +++ b/Resources/Prototypes/Entities/mobs/observer.yml @@ -15,3 +15,4 @@ - type: Examiner DoRangeCheck: false - type: IgnorePause + - type: Ghost From 055f09d5015602781b60e57bcd70221bef79050b Mon Sep 17 00:00:00 2001 From: zumorica Date: Tue, 3 Mar 2020 19:10:07 +0100 Subject: [PATCH 02/54] Button to return to body --- Content.Client/Observer/GhostComponent.cs | 25 ++++++++- Content.Client/UserInterface/GhostGui.cs | 12 +++- Content.Server/Observer/GhostComponent.cs | 56 +++++++++++++++++++ Content.Shared/GameObjects/ContentNetIDs.cs | 1 + .../Observer/SharedGhostComponent.cs | 23 ++++++++ 5 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 Content.Server/Observer/GhostComponent.cs diff --git a/Content.Client/Observer/GhostComponent.cs b/Content.Client/Observer/GhostComponent.cs index 31f45d84b8..f1b05fcc29 100644 --- a/Content.Client/Observer/GhostComponent.cs +++ b/Content.Client/Observer/GhostComponent.cs @@ -5,6 +5,8 @@ using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.ViewVariables; namespace Content.Client.Observer { @@ -12,6 +14,14 @@ namespace Content.Client.Observer public class GhostComponent : SharedGhostComponent { private GhostGui _gui; + private bool _canReturnToBody = true; + + [ViewVariables(VVAccess.ReadOnly)] + public override bool CanReturnToBody + { + get => _canReturnToBody; + set {} + } #pragma warning disable 649 [Dependency] private readonly IGameHud _gameHud; @@ -34,7 +44,7 @@ namespace Content.Client.Observer case PlayerAttachedMsg _: if (_gui == null) { - _gui = new GhostGui(); + _gui = new GhostGui(this); } else { @@ -49,5 +59,18 @@ namespace Content.Client.Observer 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 (_gui == null) return; + _gui.ReturnToBody.Disabled = !_canReturnToBody; + } } } diff --git a/Content.Client/UserInterface/GhostGui.cs b/Content.Client/UserInterface/GhostGui.cs index 63f7a870ba..2a73c6f0fd 100644 --- a/Content.Client/UserInterface/GhostGui.cs +++ b/Content.Client/UserInterface/GhostGui.cs @@ -1,3 +1,4 @@ +using Content.Client.Observer; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.IoC; @@ -6,13 +7,20 @@ namespace Content.Client.UserInterface { public class GhostGui : Control { - public GhostGui() + public Button ReturnToBody = new Button(){Text = "Return to body"}; + private GhostComponent _owner; + + public GhostGui(GhostComponent owner) { IoCManager.InjectDependencies(this); + _owner = owner; + MouseFilter = MouseFilterMode.Ignore; - AddChild(new Label(){Text = "YES THIS IS GHOST WHOOOOOO"}); + ReturnToBody.OnPressed += (args) => { owner.SendReturnToBodyMessage(); }; + + AddChild(ReturnToBody); } } } diff --git a/Content.Server/Observer/GhostComponent.cs b/Content.Server/Observer/GhostComponent.cs new file mode 100644 index 0000000000..d3ea271c52 --- /dev/null +++ b/Content.Server/Observer/GhostComponent.cs @@ -0,0 +1,56 @@ +using System.Threading; +using Content.Server.Players; +using Content.Shared.Observer; +using Robust.Server.GameObjects; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Network; +using Robust.Shared.Log; +using Robust.Shared.ViewVariables; +using Timer = Robust.Shared.Timers.Timer; + + +namespace Content.Server.Observer +{ + [RegisterComponent] + public class GhostComponent : SharedGhostComponent + { + private bool _canReturnToBody = true; + + [ViewVariables(VVAccess.ReadWrite)] + public override bool CanReturnToBody + { + get => _canReturnToBody; + set + { + _canReturnToBody = value; + Dirty(); + } + } + + 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 PlayerDetachedMsg _: + Timer.Spawn(100, Owner.Delete); + break; + default: + break; + } + } + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index eeecefda41..ef69972f4d 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -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; } } diff --git a/Content.Shared/Observer/SharedGhostComponent.cs b/Content.Shared/Observer/SharedGhostComponent.cs index dca348b285..b768b1e7a5 100644 --- a/Content.Shared/Observer/SharedGhostComponent.cs +++ b/Content.Shared/Observer/SharedGhostComponent.cs @@ -1,9 +1,32 @@ +using System; +using Content.Shared.GameObjects; using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; namespace Content.Shared.Observer { public class SharedGhostComponent : Component { public override string Name => "Ghost"; + public override uint? NetID => ContentNetIDs.GHOST; + + public virtual bool CanReturnToBody { get; set; } = true; + } + + [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; } } From 7f19381bec2225d3ee0a89147bdf904da9e59dcf Mon Sep 17 00:00:00 2001 From: zumorica Date: Tue, 3 Mar 2020 20:37:26 +0100 Subject: [PATCH 03/54] Ghost command, some other stuff --- Content.Client/Observer/GhostComponent.cs | 3 +- Content.Client/UserInterface/GhostGui.cs | 8 +++ Content.IntegrationTests/DummyGameTicker.cs | 5 ++ .../Components/Damage/DamageableComponent.cs | 8 ++- .../Components/Markers/SpawnPointComponent.cs | 1 + Content.Server/GameTicking/GameTicker.cs | 25 +++++-- .../Interfaces/GameTicking/IGameTicker.cs | 5 ++ Content.Server/Observer/Ghost.cs | 67 +++++++++++++++++++ Resources/Groups/groups.yml | 4 ++ 9 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 Content.Server/Observer/Ghost.cs diff --git a/Content.Client/Observer/GhostComponent.cs b/Content.Client/Observer/GhostComponent.cs index f1b05fcc29..a35059dd48 100644 --- a/Content.Client/Observer/GhostComponent.cs +++ b/Content.Client/Observer/GhostComponent.cs @@ -69,8 +69,7 @@ namespace Content.Client.Observer if (!(curState is GhostComponentState state)) return; _canReturnToBody = state.CanReturnToBody; - if (_gui == null) return; - _gui.ReturnToBody.Disabled = !_canReturnToBody; + _gui?.Update(); } } } diff --git a/Content.Client/UserInterface/GhostGui.cs b/Content.Client/UserInterface/GhostGui.cs index 2a73c6f0fd..13d41aea69 100644 --- a/Content.Client/UserInterface/GhostGui.cs +++ b/Content.Client/UserInterface/GhostGui.cs @@ -1,3 +1,4 @@ +using System.Data; using Content.Client.Observer; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; @@ -21,6 +22,13 @@ namespace Content.Client.UserInterface ReturnToBody.OnPressed += (args) => { owner.SendReturnToBodyMessage(); }; AddChild(ReturnToBody); + + Update(); + } + + public void Update() + { + ReturnToBody.Disabled = !_owner.CanReturnToBody; } } } diff --git a/Content.IntegrationTests/DummyGameTicker.cs b/Content.IntegrationTests/DummyGameTicker.cs index 5732e413ea..33bc84ac24 100644 --- a/Content.IntegrationTests/DummyGameTicker.cs +++ b/Content.IntegrationTests/DummyGameTicker.cs @@ -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() where T : GameRule, new() { return new T(); diff --git a/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs b/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs index 76d34a39af..d341520159 100644 --- a/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs +++ b/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs @@ -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); diff --git a/Content.Server/GameObjects/Components/Markers/SpawnPointComponent.cs b/Content.Server/GameObjects/Components/Markers/SpawnPointComponent.cs index e502a08f1a..77b01c34a1 100644 --- a/Content.Server/GameObjects/Components/Markers/SpawnPointComponent.cs +++ b/Content.Server/GameObjects/Components/Markers/SpawnPointComponent.cs @@ -38,5 +38,6 @@ namespace Content.Server.GameObjects.Components.Markers Unset = 0, LateJoin, Job, + Observer, } } diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index e8c654b2c3..5f6542172c 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -273,7 +273,7 @@ namespace Content.Server.GameTicking private IEntity _spawnPlayerMob(Job job, bool lateJoin = true) { - GridCoordinates coordinates = lateJoin ? _getLateJoinSpawnPoint() : _getJobSpawnPoint(job.Prototype.ID); + GridCoordinates coordinates = lateJoin ? GetLateJoinSpawnPoint() : GetJobSpawnPoint(job.Prototype.ID); var entity = _entityManager.SpawnEntity(PlayerPrototypeName, coordinates); if (entity.TryGetComponent(out InventoryComponent inventory)) { @@ -299,11 +299,11 @@ namespace Content.Server.GameTicking private IEntity _spawnObserverMob() { - GridCoordinates coordinates = _getLateJoinSpawnPoint(); + var coordinates = GetObserverSpawnPoint(); return _entityManager.SpawnEntity(ObserverPrototypeName, coordinates); } - private GridCoordinates _getLateJoinSpawnPoint() + public GridCoordinates GetLateJoinSpawnPoint() { var location = _spawnPoint; @@ -319,7 +319,7 @@ namespace Content.Server.GameTicking return location; } - private GridCoordinates _getJobSpawnPoint(string jobId) + public GridCoordinates GetJobSpawnPoint(string jobId) { var location = _spawnPoint; @@ -336,6 +336,23 @@ namespace Content.Server.GameTicking return location; } + public GridCoordinates GetObserverSpawnPoint() + { + var location = _spawnPoint; + + var possiblePoints = new List(); + foreach (var entity in _entityManager.GetEntities(new TypeEntityQuery(typeof(SpawnPointComponent)))) + { + var point = entity.GetComponent(); + if (point.SpawnType == SpawnPointType.Observer) + possiblePoints.Add(entity.Transform.GridPosition); + } + + if (possiblePoints.Count != 0) location = _robustRandom.Pick(possiblePoints); + + return location; + } + /// /// Cleanup that has to run to clear up anything from the previous round. /// Stuff like wiping the previous map clean. diff --git a/Content.Server/Interfaces/GameTicking/IGameTicker.cs b/Content.Server/Interfaces/GameTicking/IGameTicker.cs index e3754e1a7d..67bf2857d2 100644 --- a/Content.Server/Interfaces/GameTicking/IGameTicker.cs +++ b/Content.Server/Interfaces/GameTicking/IGameTicker.cs @@ -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() where T : GameRule, new(); void RemoveGameRule(GameRule rule); diff --git a/Content.Server/Observer/Ghost.cs b/Content.Server/Observer/Ghost.cs new file mode 100644 index 0000000000..3ee16adf73 --- /dev/null +++ b/Content.Server/Observer/Ghost.cs @@ -0,0 +1,67 @@ +using Content.Server.GameObjects; +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.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; + GridCoordinates position; + var canReturn = player.AttachedEntity != null; + + if (mind.VisitingEntity != null) + { + mind.UnVisit(); + } + + position = player.AttachedEntity?.Transform.GridPosition ?? IoCManager.Resolve().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(); + var ghost = entityManager.SpawnEntity("MobObserver", position); + var ghostComponent = ghost.GetComponent(); + ghostComponent.CanReturnToBody = canReturn; + + if(canReturn) + mind.Visit(ghost); + else + mind.TransferTo(ghost); + } + } +} diff --git a/Resources/Groups/groups.yml b/Resources/Groups/groups.yml index d1fe708455..50b84be17f 100644 --- a/Resources/Groups/groups.yml +++ b/Resources/Groups/groups.yml @@ -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 From bea34f7f347ebb8795986e4d3be2b4348cfb4c3d Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sat, 7 Mar 2020 18:30:10 +0100 Subject: [PATCH 04/54] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5abfcb8979..0c20a5b39f 100644 --- a/.gitignore +++ b/.gitignore @@ -280,3 +280,4 @@ BuildFiles/Windows/Godot/* # Working on the tools scripts is getting annoying okay? .mypy_cache/ +RobustToolbox From b9f9eb6651ce8d4f44b4f989836e97917c56d341 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sat, 7 Mar 2020 18:33:03 +0100 Subject: [PATCH 05/54] Revert "Update .gitignore" This reverts commit bea34f7f347ebb8795986e4d3be2b4348cfb4c3d. --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0c20a5b39f..5abfcb8979 100644 --- a/.gitignore +++ b/.gitignore @@ -280,4 +280,3 @@ BuildFiles/Windows/Godot/* # Working on the tools scripts is getting annoying okay? .mypy_cache/ -RobustToolbox From f05fdfb5fca78f5c5af4593cbfa139d9b75fd4ee Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sat, 14 Mar 2020 12:55:07 +0100 Subject: [PATCH 06/54] Changed all int and some float things in Reagent code to Decimals --- .../Components/Chemistry/InjectorComponent.cs | 8 ++--- .../Chemistry/Metabolism/DefaultDrink.cs | 2 +- .../Chemistry/Metabolism/DefaultFood.cs | 19 ++++++----- .../ExplosionReactionEffect.cs | 4 +-- .../Components/Chemistry/InjectorComponent.cs | 8 ++--- .../Components/Chemistry/PourableComponent.cs | 2 +- .../Components/Chemistry/SolutionComponent.cs | 25 ++++++++------ .../Metabolism/BloodstreamComponent.cs | 4 +-- .../Components/Nutrition/DrinkComponent.cs | 9 ++--- .../Components/Nutrition/FoodComponent.cs | 2 +- .../Components/Nutrition/StomachComponent.cs | 6 ++-- .../Interfaces/Chemistry/IReactionEffect.cs | 2 +- .../Chemistry/DefaultMetabolizable.cs | 9 ++--- Content.Shared/Chemistry/Solution.cs | 34 ++++++++++--------- .../Chemistry/SharedInjectorComponent.cs | 6 ++-- .../SharedReagentDispenserComponent.cs | 10 +++--- .../Components/Chemistry/SolutionComponent.cs | 18 +++++----- .../Interfaces/Chemistry/IMetabolizable.cs | 2 +- Content.Shared/Maths/Rounders.cs | 12 +++++++ 19 files changed, 101 insertions(+), 81 deletions(-) create mode 100644 Content.Shared/Maths/Rounders.cs diff --git a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs index ab1bb415d1..23e8c1a6ad 100644 --- a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -16,8 +16,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 decimal CurrentVolume { get; set; } + [ViewVariables] private decimal TotalVolume { get; set; } [ViewVariables] private InjectorToggleMode CurrentMode { get; set; } [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; @@ -28,9 +28,9 @@ namespace Content.Client.GameObjects.Components.Chemistry //Handle net updates public override void HandleComponentState(ComponentState curState, ComponentState nextState) { - var cast = (InjectorComponentState)curState; - if (cast != null) + if(curState.GetType() == typeof(InjectorComponentState)) { + var cast = (InjectorComponentState) curState; CurrentVolume = cast.CurrentVolume; TotalVolume = cast.TotalVolume; CurrentMode = cast.CurrentMode; diff --git a/Content.Server/Chemistry/Metabolism/DefaultDrink.cs b/Content.Server/Chemistry/Metabolism/DefaultDrink.cs index e43a1d7dd3..0839d6f164 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultDrink.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultDrink.cs @@ -28,7 +28,7 @@ namespace Content.Server.Chemistry.Metabolism } //Remove reagent at set rate, satiate thirst if a ThirstComponent can be found - int IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) + decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { int metabolismAmount = (int)Math.Round(MetabolismRate * tickTime); if (solutionEntity.TryGetComponent(out ThirstComponent thirst)) diff --git a/Content.Server/Chemistry/Metabolism/DefaultFood.cs b/Content.Server/Chemistry/Metabolism/DefaultFood.cs index 824721acfd..4de983702a 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultFood.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultFood.cs @@ -1,6 +1,7 @@ using System; using Content.Server.GameObjects.Components.Nutrition; using Content.Shared.Interfaces.Chemistry; +using Content.Shared.Maths; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Serialization; @@ -14,25 +15,25 @@ namespace Content.Server.Chemistry.Metabolism class DefaultFood : IMetabolizable { //Rate of metabolism in units / second - private int _metabolismRate; - public int MetabolismRate => _metabolismRate; + private decimal _metabolismRate; + public decimal MetabolismRate => _metabolismRate; //How much hunger is satiated when 1u of the reagent is metabolized - private float _nutritionFactor; - public float NutritionFactor => _nutritionFactor; + private decimal _nutritionFactor; + public decimal NutritionFactor => _nutritionFactor; void IExposeData.ExposeData(ObjectSerializer serializer) { - serializer.DataField(ref _metabolismRate, "rate", 1); - serializer.DataField(ref _nutritionFactor, "nutrimentFactor", 30.0f); + serializer.DataField(ref _metabolismRate, "rate", 1M); + serializer.DataField(ref _nutritionFactor, "nutrimentFactor", 30.0M); } //Remove reagent at set rate, satiate hunger if a HungerComponent can be found - int IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) + decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - int metabolismAmount = (int)Math.Round(MetabolismRate * tickTime); + var metabolismAmount = (MetabolismRate * (decimal) tickTime).RoundForReagents(); if (solutionEntity.TryGetComponent(out HungerComponent hunger)) - hunger.UpdateFood(metabolismAmount * NutritionFactor); + hunger.UpdateFood((float)(metabolismAmount * NutritionFactor)); //Return amount of reagent to be removed, remove reagent regardless of HungerComponent presence return metabolismAmount; diff --git a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs index 7382a7bdc2..2bdff9b1c2 100644 --- a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs @@ -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)) diff --git a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs index d4140d31f4..b9f0527793 100644 --- a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -37,7 +37,7 @@ namespace Content.Server.GameObjects.Components.Chemistry /// attempt to inject it's entire contents upon use. /// [ViewVariables] - private int _transferAmount; + private decimal _transferAmount; /// /// Initial storage volume of the injector @@ -165,7 +165,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); + decimal realTransferAmount = Math.Min(_transferAmount, targetBloodstream.EmptyVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, @@ -193,7 +193,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); + decimal realTransferAmount = Math.Min(_transferAmount, targetSolution.EmptyVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, @@ -221,7 +221,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); + decimal realTransferAmount = Math.Min(_transferAmount, targetSolution.CurrentVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, diff --git a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs index b0f47233b0..1ad4386634 100644 --- a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs @@ -69,7 +69,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); + decimal realTransferAmount = Math.Min(attackPourable.TransferAmount, targetSolution.EmptyVolume); if (realTransferAmount <= 0) //Special message if container is full { _notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User, diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 98b064bfc2..b612c4052d 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -7,6 +7,7 @@ using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Shared.Chemistry; using Content.Shared.GameObjects; +using Content.Shared.Maths; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -205,7 +206,7 @@ namespace Content.Server.GameObjects.Components.Chemistry //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 +224,13 @@ 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, decimal quantity, out decimal acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) { - if (quantity > _maxVolume - _containedSolution.TotalVolume) + quantity = quantity.RoundForReagents(); + var toAcceptQuantity = (_maxVolume - _containedSolution.TotalVolume).RoundForReagents(); + if (quantity > toAcceptQuantity) { - acceptedQuantity = _maxVolume - _containedSolution.TotalVolume; + acceptedQuantity = toAcceptQuantity; if (acceptedQuantity == 0) return false; } else @@ -267,16 +270,16 @@ namespace Content.Server.GameObjects.Components.Chemistry /// The reaction whose reactants will be checked for in the solution. /// The number of times the reaction can occur with the given solution. /// - private bool SolutionValidReaction(ReactionPrototype reaction, out int unitReactions) + private bool SolutionValidReaction(ReactionPrototype reaction, out decimal unitReactions) { - unitReactions = int.MaxValue; //Set to some impossibly large number initially + unitReactions = decimal.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 decimal reagentQuantity)) { return false; } - int currentUnitReactions = reagentQuantity / reactant.Value.Amount; + var currentUnitReactions = (reagentQuantity / reactant.Value.Amount).RoundForReagents(); if (currentUnitReactions < unitReactions) { unitReactions = currentUnitReactions; @@ -299,21 +302,21 @@ namespace Content.Server.GameObjects.Components.Chemistry /// Solution to be reacted. /// Reaction to occur. /// The number of times to cause this reaction. - private void PerformReaction(ReactionPrototype reaction, int unitReactions) + private void PerformReaction(ReactionPrototype reaction, decimal 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).RoundForReagents(); 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, (int)(unitReactions * product.Value), out var acceptedQuantity, true); } //Trigger reaction effects foreach (var effect in reaction.Effects) diff --git a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs index 0fa862ce82..ef4579331d 100644 --- a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs +++ b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs @@ -39,7 +39,7 @@ namespace Content.Server.GameObjects.Components.Metabolism /// /// Empty volume of internal solution /// - public int EmptyVolume => _internalSolution.EmptyVolume; + public decimal EmptyVolume => _internalSolution.EmptyVolume; public override void ExposeData(ObjectSerializer serializer) { @@ -98,7 +98,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); } } diff --git a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs index ab6f259b24..26d552f71f 100644 --- a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs @@ -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,11 +33,11 @@ namespace Content.Server.GameObjects.Components.Nutrition [ViewVariables] private string _finishPrototype; - public int TransferAmount => _transferAmount; + public decimal TransferAmount => _transferAmount; [ViewVariables] - private int _transferAmount = 2; + private decimal _transferAmount = 2; - public int MaxVolume + public decimal MaxVolume { get => _contents.MaxVolume; set => _contents.MaxVolume = value; @@ -56,7 +57,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)); } diff --git a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs index df990b392c..a528c223b6 100644 --- a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs @@ -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)); } bool IUse.UseEntity(UseEntityEventArgs eventArgs) diff --git a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs index 95353351f9..294fc20fcb 100644 --- a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs @@ -27,7 +27,7 @@ namespace Content.Server.GameObjects.Components.Nutrition /// /// Max volume of internal solution storage /// - public int MaxVolume + public decimal MaxVolume { get => _stomachContents.MaxVolume; set => _stomachContents.MaxVolume = value; @@ -141,10 +141,10 @@ namespace Content.Server.GameObjects.Components.Nutrition private class ReagentDelta { public readonly string ReagentId; - public readonly int Quantity; + public readonly decimal Quantity; public float Lifetime { get; private set; } - public ReagentDelta(string reagentId, int quantity) + public ReagentDelta(string reagentId, decimal quantity) { ReagentId = reagentId; Quantity = quantity; diff --git a/Content.Server/Interfaces/Chemistry/IReactionEffect.cs b/Content.Server/Interfaces/Chemistry/IReactionEffect.cs index bd01799557..9e5e28071b 100644 --- a/Content.Server/Interfaces/Chemistry/IReactionEffect.cs +++ b/Content.Server/Interfaces/Chemistry/IReactionEffect.cs @@ -8,6 +8,6 @@ namespace Content.Shared.Interfaces /// public interface IReactionEffect : IExposeData { - void React(IEntity solutionEntity, int intensity); + void React(IEntity solutionEntity, decimal intensity); } } diff --git a/Content.Shared/Chemistry/DefaultMetabolizable.cs b/Content.Shared/Chemistry/DefaultMetabolizable.cs index ab5816d9f0..7ceb3372c4 100644 --- a/Content.Shared/Chemistry/DefaultMetabolizable.cs +++ b/Content.Shared/Chemistry/DefaultMetabolizable.cs @@ -1,5 +1,6 @@ using System; using Content.Shared.Interfaces.Chemistry; +using Content.Shared.Maths; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Serialization; @@ -10,17 +11,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) + decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - int metabolismAmount = (int)Math.Round(MetabolismRate * tickTime); + var metabolismAmount = (MetabolismRate * (decimal)tickTime).RoundForReagents(); return metabolismAmount; } } diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index bbb9937249..e10d99f389 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using Content.Shared.Maths; using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -23,7 +24,7 @@ namespace Content.Shared.Chemistry /// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker). /// [ViewVariables] - public int TotalVolume { get; private set; } + public decimal TotalVolume { get; private set; } /// /// Constructs an empty solution (ex. an empty beaker). @@ -60,9 +61,10 @@ namespace Content.Shared.Chemistry /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public void AddReagent(string reagentId, int quantity) + public void AddReagent(string reagentId, decimal quantity) { - if(quantity <= 0) + quantity = quantity.RoundForReagents(); + if (quantity <= 0) return; for (var i = 0; i < _contents.Count; i++) @@ -85,7 +87,7 @@ namespace Content.Shared.Chemistry /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public int GetReagentQuantity(string reagentId) + public decimal GetReagentQuantity(string reagentId) { for (var i = 0; i < _contents.Count; i++) { @@ -96,7 +98,7 @@ namespace Content.Shared.Chemistry return 0; } - public void RemoveReagent(string reagentId, int quantity) + public void RemoveReagent(string reagentId, decimal quantity) { if(quantity <= 0) return; @@ -109,7 +111,7 @@ namespace Content.Shared.Chemistry var curQuantity = reagent.Quantity; - var newQuantity = curQuantity - quantity; + var newQuantity = (curQuantity - quantity).RoundForReagents(); if (newQuantity <= 0) { _contents.RemoveSwap(i); @@ -129,12 +131,12 @@ namespace Content.Shared.Chemistry /// Remove the specified quantity from this solution. /// /// The quantity of this solution to remove - public void RemoveSolution(int quantity) + public void RemoveSolution(decimal quantity) { if(quantity <= 0) return; - var ratio = (float)(TotalVolume - quantity) / TotalVolume; + var ratio = (TotalVolume - quantity).RoundForReagents() / TotalVolume; if (ratio <= 0) { @@ -149,12 +151,12 @@ 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).RoundForReagents(); _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); } - TotalVolume = (int)Math.Floor(TotalVolume * ratio); + TotalVolume = (TotalVolume * ratio).RoundForReagents(); } public void RemoveAllSolution() @@ -163,7 +165,7 @@ namespace Content.Shared.Chemistry TotalVolume = 0; } - public Solution SplitSolution(int quantity) + public Solution SplitSolution(decimal quantity) { if (quantity <= 0) return new Solution(); @@ -178,8 +180,8 @@ namespace Content.Shared.Chemistry } newSolution = new Solution(); - var newTotalVolume = 0; - var ratio = (float)(TotalVolume - quantity) / TotalVolume; + var newTotalVolume = 0M; + var ratio = (TotalVolume - quantity) / TotalVolume; for (var i = 0; i < _contents.Count; i++) { @@ -228,7 +230,7 @@ namespace Content.Shared.Chemistry public Solution Clone() { - var volume = 0; + var volume = 0M; 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 decimal Quantity; - public ReagentQuantity(string reagentId, int quantity) + public ReagentQuantity(string reagentId, decimal quantity) { ReagentId = reagentId; Quantity = quantity; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs index ebbb4b11e4..8bf232b96f 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs @@ -18,11 +18,11 @@ namespace Content.Shared.GameObjects.Components.Chemistry [Serializable, NetSerializable] protected sealed class InjectorComponentState : ComponentState { - public int CurrentVolume { get; } - public int TotalVolume { get; } + public decimal CurrentVolume { get; } + public decimal TotalVolume { get; } public InjectorToggleMode CurrentMode { get; } - public InjectorComponentState(int currentVolume, int totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR) + public InjectorComponentState(decimal currentVolume, decimal totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR) { CurrentVolume = currentVolume; TotalVolume = totalVolume; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs index 0066dc3d50..9907fb3550 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs @@ -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 decimal BeakerCurrentVolume; + public readonly decimal BeakerMaxVolume; public readonly string ContainerName; /// /// A list of the reagents which this dispenser can dispense. @@ -38,10 +38,10 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// public readonly List ContainerReagents; public readonly string DispenserName; - public readonly int SelectedDispenseAmount; + public readonly decimal SelectedDispenseAmount; - public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, int beakerCurrentVolume, int beakerMaxVolume, string containerName, - List inventory, string dispenserName, List containerReagents, int selectedDispenseAmount) + public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, decimal beakerCurrentVolume, decimal beakerMaxVolume, string containerName, + List inventory, string dispenserName, List containerReagents, decimal selectedDispenseAmount) { HasBeaker = hasBeaker; BeakerCurrentVolume = beakerCurrentVolume; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs index 426daa0639..71ba3c6c91 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -18,7 +18,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry [ViewVariables] protected Solution _containedSolution = new Solution(); - protected int _maxVolume; + protected decimal _maxVolume; private SolutionCaps _capabilities; /// @@ -30,7 +30,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The maximum volume of the container. /// [ViewVariables(VVAccess.ReadWrite)] - public int MaxVolume + public decimal MaxVolume { get => _maxVolume; set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced. @@ -40,13 +40,13 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The total volume of all the of the reagents in the container. /// [ViewVariables] - public int CurrentVolume => _containedSolution.TotalVolume; + public decimal CurrentVolume => _containedSolution.TotalVolume; /// /// The volume without reagents remaining in the container. /// [ViewVariables] - public int EmptyVolume => MaxVolume - CurrentVolume; + public decimal EmptyVolume => MaxVolume - CurrentVolume; /// /// The current blended color of all the reagents in the container. @@ -122,7 +122,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry OnSolutionChanged(); } - public bool TryRemoveReagent(string reagentId, int quantity) + public bool TryRemoveReagent(string reagentId, decimal quantity) { if (!ContainsReagent(reagentId, out var currentQuantity)) return false; @@ -146,7 +146,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry return true; } - public Solution SplitSolution(int quantity) + public Solution SplitSolution(decimal quantity) { var solutionSplit = _containedSolution.SplitSolution(quantity); OnSolutionChanged(); @@ -159,7 +159,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry SubstanceColor = Color.White; Color mixColor = default; - float runningTotalQuantity = 0; + var runningTotalQuantity = 0M; foreach (var reagent in _containedSolution) { @@ -171,7 +171,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry if (mixColor == default) mixColor = proto.SubstanceColor; - mixColor = BlendRGB(mixColor, proto.SubstanceColor, reagent.Quantity / runningTotalQuantity); + mixColor = BlendRGB(mixColor, proto.SubstanceColor, (float) (reagent.Quantity / runningTotalQuantity)); } } @@ -216,7 +216,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The reagent to check for. /// Output the quantity of the reagent if it is contained, 0 if it isn't. /// Return true if the solution contains the reagent. - public bool ContainsReagent(string reagentId, out int quantity) + public bool ContainsReagent(string reagentId, out decimal quantity) { foreach (var reagent in _containedSolution.Contents) { diff --git a/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs b/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs index 4b03ef51e2..3a55de645f 100644 --- a/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs +++ b/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs @@ -19,6 +19,6 @@ namespace Content.Shared.Interfaces.Chemistry /// The reagent id /// The time since the last metabolism tick in seconds. /// The amount of reagent to be removed. The metabolizing organ should handle removing the reagent. - int Metabolize(IEntity solutionEntity, string reagentId, float tickTime); + decimal Metabolize(IEntity solutionEntity, string reagentId, float tickTime); } } diff --git a/Content.Shared/Maths/Rounders.cs b/Content.Shared/Maths/Rounders.cs new file mode 100644 index 0000000000..4b07d84026 --- /dev/null +++ b/Content.Shared/Maths/Rounders.cs @@ -0,0 +1,12 @@ +using System; + +namespace Content.Shared.Maths +{ + public static class Rounders + { + public static decimal RoundForReagents(this decimal me) + { + return Math.Round(me, 2); + } + } +} From dc666218042ca474012c15bd987fc43b09cd7ee6 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sat, 14 Mar 2020 14:04:08 +0100 Subject: [PATCH 07/54] Replaced static Rounders with an impleneted interface --- Content.Client/ClientContentIoC.cs | 3 ++ .../Chemistry/Metabolism/DefaultFood.cs | 10 +++--- .../Components/Chemistry/SolutionComponent.cs | 26 +++++++------- Content.Server/ServerContentIoC.cs | 5 ++- .../Chemistry/DefaultMetabolizable.cs | 10 +++--- .../Chemistry/RounderForReagents.cs | 14 ++++++++ Content.Shared/Chemistry/Solution.cs | 24 +++++++------ .../Components/Chemistry/SolutionComponent.cs | 36 +++++++++---------- .../Chemistry/IRounderForReagents.cs | 7 ++++ Content.Shared/Maths/Rounders.cs | 12 ------- .../Prototypes/Entities/items/chemistry.yml | 14 ++++---- 11 files changed, 91 insertions(+), 70 deletions(-) create mode 100644 Content.Shared/Chemistry/RounderForReagents.cs create mode 100644 Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs delete mode 100644 Content.Shared/Maths/Rounders.cs diff --git a/Content.Client/ClientContentIoC.cs b/Content.Client/ClientContentIoC.cs index 6a8e9ea10a..d26587104c 100644 --- a/Content.Client/ClientContentIoC.cs +++ b/Content.Client/ClientContentIoC.cs @@ -7,7 +7,9 @@ using Content.Client.Parallax; using Content.Client.Sandbox; using Content.Client.UserInterface; using Content.Client.Utility; +using Content.Shared.Chemistry; using Content.Shared.Interfaces; +using Content.Shared.Interfaces.Chemistry; using Robust.Shared.IoC; namespace Content.Client @@ -27,6 +29,7 @@ namespace Content.Client IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); } } } diff --git a/Content.Server/Chemistry/Metabolism/DefaultFood.cs b/Content.Server/Chemistry/Metabolism/DefaultFood.cs index 4de983702a..aca26e2628 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultFood.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultFood.cs @@ -1,9 +1,8 @@ -using System; -using Content.Server.GameObjects.Components.Nutrition; +using Content.Server.GameObjects.Components.Nutrition; using Content.Shared.Interfaces.Chemistry; -using Content.Shared.Maths; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; +using Robust.Shared.IoC; using Robust.Shared.Serialization; namespace Content.Server.Chemistry.Metabolism @@ -14,6 +13,9 @@ namespace Content.Server.Chemistry.Metabolism /// class DefaultFood : IMetabolizable { +#pragma warning disable 649 + [Dependency] private readonly IRounderForReagents _rounder; +#pragma warning restore 649 //Rate of metabolism in units / second private decimal _metabolismRate; public decimal MetabolismRate => _metabolismRate; @@ -31,7 +33,7 @@ namespace Content.Server.Chemistry.Metabolism //Remove reagent at set rate, satiate hunger if a HungerComponent can be found decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - var metabolismAmount = (MetabolismRate * (decimal) tickTime).RoundForReagents(); + var metabolismAmount = _rounder.Round(MetabolismRate * (decimal) tickTime); if (solutionEntity.TryGetComponent(out HungerComponent hunger)) hunger.UpdateFood((float)(metabolismAmount * NutritionFactor)); diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index b612c4052d..c5a815752a 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -1,13 +1,8 @@ -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.Maths; +using Content.Shared.Interfaces.Chemistry; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -15,6 +10,8 @@ using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Prototypes; using Robust.Shared.Utility; +using System; +using System.Collections.Generic; namespace Content.Server.GameObjects.Components.Chemistry { @@ -25,6 +22,7 @@ namespace Content.Server.GameObjects.Components.Chemistry internal class SolutionComponent : Shared.GameObjects.Components.Chemistry.SolutionComponent, IExamine { #pragma warning disable 649 + [Dependency] private readonly IRounderForReagents _rounder; [Dependency] private readonly IPrototypeManager _prototypeManager; [Dependency] private readonly ILocalizationManager _loc; [Dependency] private readonly IEntitySystemManager _entitySystemManager; @@ -226,8 +224,8 @@ namespace Content.Server.GameObjects.Components.Chemistry public bool TryAddReagent(string reagentId, decimal quantity, out decimal acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) { - quantity = quantity.RoundForReagents(); - var toAcceptQuantity = (_maxVolume - _containedSolution.TotalVolume).RoundForReagents(); + quantity = _rounder.Round(quantity); + var toAcceptQuantity = _rounder.Round(MaxVolume - ContainedSolution.TotalVolume); if (quantity > toAcceptQuantity) { acceptedQuantity = toAcceptQuantity; @@ -238,7 +236,7 @@ namespace Content.Server.GameObjects.Components.Chemistry acceptedQuantity = quantity; } - _containedSolution.AddReagent(reagentId, acceptedQuantity); + ContainedSolution.AddReagent(reagentId, acceptedQuantity); if (!skipColor) { RecalculateColor(); } @@ -250,10 +248,10 @@ namespace Content.Server.GameObjects.Components.Chemistry 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); + ContainedSolution.AddSolution(solution); if (!skipColor) { RecalculateColor(); } @@ -279,7 +277,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { return false; } - var currentUnitReactions = (reagentQuantity / reactant.Value.Amount).RoundForReagents(); + var currentUnitReactions = _rounder.Round(reagentQuantity / reactant.Value.Amount); if (currentUnitReactions < unitReactions) { unitReactions = currentUnitReactions; @@ -309,7 +307,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { if (!reactant.Value.Catalyst) { - var amountToRemove = (unitReactions * reactant.Value.Amount).RoundForReagents(); + var amountToRemove = _rounder.Round(unitReactions * reactant.Value.Amount); TryRemoveReagent(reactant.Key, amountToRemove); } } diff --git a/Content.Server/ServerContentIoC.cs b/Content.Server/ServerContentIoC.cs index ec9ff2a876..1c5937c0fe 100644 --- a/Content.Server/ServerContentIoC.cs +++ b/Content.Server/ServerContentIoC.cs @@ -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 @@ -26,6 +28,7 @@ namespace Content.Server IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); } } } diff --git a/Content.Shared/Chemistry/DefaultMetabolizable.cs b/Content.Shared/Chemistry/DefaultMetabolizable.cs index 7ceb3372c4..31d91d351b 100644 --- a/Content.Shared/Chemistry/DefaultMetabolizable.cs +++ b/Content.Shared/Chemistry/DefaultMetabolizable.cs @@ -1,8 +1,7 @@ -using System; -using Content.Shared.Interfaces.Chemistry; -using Content.Shared.Maths; +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,6 +9,9 @@ namespace Content.Shared.Chemistry //Default metabolism for reagents. Metabolizes the reagent with no effects class DefaultMetabolizable : IMetabolizable { +#pragma warning disable 649 + [Dependency] private readonly IRounderForReagents _rounder; +#pragma warning restore 649 //Rate of metabolism in units / second private decimal _metabolismRate = 1; public decimal MetabolismRate => _metabolismRate; @@ -21,7 +23,7 @@ namespace Content.Shared.Chemistry decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - var metabolismAmount = (MetabolismRate * (decimal)tickTime).RoundForReagents(); + var metabolismAmount = _rounder.Round(MetabolismRate * (decimal)tickTime); return metabolismAmount; } } diff --git a/Content.Shared/Chemistry/RounderForReagents.cs b/Content.Shared/Chemistry/RounderForReagents.cs new file mode 100644 index 0000000000..d7c6ccce11 --- /dev/null +++ b/Content.Shared/Chemistry/RounderForReagents.cs @@ -0,0 +1,14 @@ +using Content.Shared.Interfaces.Chemistry; +using System; + +namespace Content.Shared.Chemistry +{ + + public class RounderForReagents : IRounderForReagents + { + public decimal Round(decimal value) + { + return Math.Round(value, 2); + } + } +} diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index e10d99f389..109b780108 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -1,12 +1,13 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Content.Shared.Maths; +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 { @@ -15,6 +16,9 @@ namespace Content.Shared.Chemistry /// public class Solution : IExposeData, IEnumerable { +#pragma warning disable 649 + [Dependency] private readonly IRounderForReagents _rounder; +#pragma warning restore 649 // Most objects on the station hold only 1 or 2 reagents [ViewVariables] private List _contents = new List(2); @@ -63,7 +67,7 @@ namespace Content.Shared.Chemistry /// The quantity in milli-units. public void AddReagent(string reagentId, decimal quantity) { - quantity = quantity.RoundForReagents(); + quantity = _rounder.Round(quantity); if (quantity <= 0) return; @@ -111,7 +115,7 @@ namespace Content.Shared.Chemistry var curQuantity = reagent.Quantity; - var newQuantity = (curQuantity - quantity).RoundForReagents(); + var newQuantity = _rounder.Round(curQuantity - quantity); if (newQuantity <= 0) { _contents.RemoveSwap(i); @@ -136,7 +140,7 @@ namespace Content.Shared.Chemistry if(quantity <= 0) return; - var ratio = (TotalVolume - quantity).RoundForReagents() / TotalVolume; + var ratio = _rounder.Round(TotalVolume - quantity) / TotalVolume; if (ratio <= 0) { @@ -151,12 +155,12 @@ 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 = (oldQuantity * ratio).RoundForReagents(); + var newQuantity = _rounder.Round(oldQuantity * ratio); _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); } - TotalVolume = (TotalVolume * ratio).RoundForReagents(); + TotalVolume = _rounder.Round(TotalVolume * ratio); } public void RemoveAllSolution() diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs index 71ba3c6c91..8a1f2b8a58 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -1,12 +1,12 @@ -using System; -using System.Collections.Generic; -using Content.Shared.Chemistry; +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; +using System; +using System.Collections.Generic; namespace Content.Shared.GameObjects.Components.Chemistry { @@ -17,8 +17,8 @@ namespace Content.Shared.GameObjects.Components.Chemistry #pragma warning restore 649 [ViewVariables] - protected Solution _containedSolution = new Solution(); - protected decimal _maxVolume; + protected Solution ContainedSolution = new Solution(); + private decimal _maxVolume; private SolutionCaps _capabilities; /// @@ -40,7 +40,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The total volume of all the of the reagents in the container. /// [ViewVariables] - public decimal CurrentVolume => _containedSolution.TotalVolume; + public decimal CurrentVolume => ContainedSolution.TotalVolume; /// /// The volume without reagents remaining in the container. @@ -64,7 +64,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry set => _capabilities = value; } - public IReadOnlyList ReagentList => _containedSolution.Contents; + public IReadOnlyList ReagentList => ContainedSolution.Contents; /// /// Shortcut for Capabilities PourIn flag to avoid binary operators. @@ -94,8 +94,8 @@ namespace Content.Shared.GameObjects.Components.Chemistry { base.ExposeData(serializer); - serializer.DataField(ref _maxVolume, "maxVol", 0); - serializer.DataField(ref _containedSolution, "contents", _containedSolution); + serializer.DataField(ref _maxVolume, "maxVol", 0M); + serializer.DataField(ref ContainedSolution, "contents", ContainedSolution); serializer.DataField(ref _capabilities, "caps", SolutionCaps.None); } @@ -112,13 +112,13 @@ namespace Content.Shared.GameObjects.Components.Chemistry { base.Shutdown(); - _containedSolution.RemoveAllSolution(); - _containedSolution = new Solution(); + ContainedSolution.RemoveAllSolution(); + ContainedSolution = new Solution(); } public void RemoveAllSolution() { - _containedSolution.RemoveAllSolution(); + ContainedSolution.RemoveAllSolution(); OnSolutionChanged(); } @@ -126,7 +126,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry { if (!ContainsReagent(reagentId, out var currentQuantity)) return false; - _containedSolution.RemoveReagent(reagentId, quantity); + ContainedSolution.RemoveReagent(reagentId, quantity); OnSolutionChanged(); return true; } @@ -141,27 +141,27 @@ namespace Content.Shared.GameObjects.Components.Chemistry if (CurrentVolume == 0) return false; - _containedSolution.RemoveSolution(quantity); + ContainedSolution.RemoveSolution(quantity); OnSolutionChanged(); return true; } public Solution SplitSolution(decimal quantity) { - var solutionSplit = _containedSolution.SplitSolution(quantity); + var solutionSplit = ContainedSolution.SplitSolution(quantity); OnSolutionChanged(); return solutionSplit; } protected void RecalculateColor() { - if(_containedSolution.TotalVolume == 0) + if(ContainedSolution.TotalVolume == 0) SubstanceColor = Color.White; Color mixColor = default; var runningTotalQuantity = 0M; - foreach (var reagent in _containedSolution) + foreach (var reagent in ContainedSolution) { runningTotalQuantity += reagent.Quantity; @@ -218,7 +218,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// Return true if the solution contains the reagent. public bool ContainsReagent(string reagentId, out decimal quantity) { - foreach (var reagent in _containedSolution.Contents) + foreach (var reagent in ContainedSolution.Contents) { if (reagent.ReagentId == reagentId) { diff --git a/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs b/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs new file mode 100644 index 0000000000..da8a1871c7 --- /dev/null +++ b/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs @@ -0,0 +1,7 @@ +namespace Content.Shared.Interfaces.Chemistry +{ + public interface IRounderForReagents + { + decimal Round(decimal value); + } +} diff --git a/Content.Shared/Maths/Rounders.cs b/Content.Shared/Maths/Rounders.cs deleted file mode 100644 index 4b07d84026..0000000000 --- a/Content.Shared/Maths/Rounders.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Content.Shared.Maths -{ - public static class Rounders - { - public static decimal RoundForReagents(this decimal me) - { - return Math.Round(me, 2); - } - } -} diff --git a/Resources/Prototypes/Entities/items/chemistry.yml b/Resources/Prototypes/Entities/items/chemistry.yml index cca0065844..cf5e63916f 100644 --- a/Resources/Prototypes/Entities/items/chemistry.yml +++ b/Resources/Prototypes/Entities/items/chemistry.yml @@ -9,10 +9,10 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/beaker.png - type: Solution - maxVol: 50 + maxVol: 50.0 caps: 27 - type: Pourable - transferAmount: 5 + transferAmount: 5.0 - type: entity name: Large Beaker @@ -25,10 +25,10 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/beakerlarge.png - type: Solution - maxVol: 100 + maxVol: 100.0 caps: 27 - type: Pourable - transferAmount: 5 + transferAmount: 5.0 - type: entity name: Dropper @@ -41,10 +41,10 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/dropper.png - type: Solution - maxVol: 5 + maxVol: 5.0 caps: 19 - type: Pourable - transferAmount: 5 + transferAmount: 5.0 - type: entity name: Syringe @@ -57,7 +57,7 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/syringeproj.png - type: Solution - maxVol: 15 + maxVol: 15.0 caps: 19 - type: Injector injectOnly: false From 539214b1ad33a41e6e735cd8ec65eeef02c79359 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sat, 21 Mar 2020 16:22:35 +0100 Subject: [PATCH 08/54] Some more int -> decimal conversions. Changed the use of the Solution constructor. --- .../Components/Chemistry/PourableComponent.cs | 6 +++--- .../Chemistry/ReagentDispenserComponent.cs | 20 +++++++++---------- .../Components/Chemistry/SolutionComponent.cs | 3 ++- .../Components/Nutrition/StomachComponent.cs | 2 +- Content.Shared/Chemistry/Solution.cs | 6 +++--- .../Components/Chemistry/SolutionComponent.cs | 13 ++++++++---- 6 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs index 1ad4386634..439fc69ac9 100644 --- a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs @@ -28,13 +28,13 @@ namespace Content.Server.GameObjects.Components.Chemistry public override string Name => "Pourable"; - private int _transferAmount; + private decimal _transferAmount; /// /// The amount of solution to be transferred from this solution when clicking on other solutions with it. /// [ViewVariables] - public int TransferAmount + public decimal TransferAmount { get => _transferAmount; set => _transferAmount = value; @@ -43,7 +43,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", 5.0M); } /// diff --git a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs index fa3a65e6c5..1f8f13625e 100644 --- a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs @@ -41,7 +41,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 decimal _dispenseAmount = 10; [ViewVariables] private SolutionComponent Solution => _beakerContainer.ContainedEntity.GetComponent(); @@ -115,22 +115,22 @@ namespace Content.Server.GameObjects.Components.Chemistry TryClear(); break; case UiButton.SetDispenseAmount1: - DispenseAmount = 1; + _dispenseAmount = 1; break; case UiButton.SetDispenseAmount5: - DispenseAmount = 5; + _dispenseAmount = 5; break; case UiButton.SetDispenseAmount10: - DispenseAmount = 10; + _dispenseAmount = 10; break; case UiButton.SetDispenseAmount25: - DispenseAmount = 25; + _dispenseAmount = 25; break; case UiButton.SetDispenseAmount50: - DispenseAmount = 50; + _dispenseAmount = 50; break; case UiButton.SetDispenseAmount100: - DispenseAmount = 100; + _dispenseAmount = 100; break; case UiButton.Dispense: if (HasBeaker) @@ -173,12 +173,12 @@ namespace Content.Server.GameObjects.Components.Chemistry if (beaker == null) { return new ReagentDispenserBoundUserInterfaceState(false, 0, 0, - "", Inventory, Owner.Name, null, DispenseAmount); + "", Inventory, Owner.Name, null, _dispenseAmount); } var solution = beaker.GetComponent(); 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() @@ -228,7 +228,7 @@ namespace Content.Server.GameObjects.Components.Chemistry if (!HasBeaker) return; var solution = _beakerContainer.ContainedEntity.GetComponent(); - solution.TryAddReagent(Inventory[dispenseIndex].ID, DispenseAmount, out _); + solution.TryAddReagent(Inventory[dispenseIndex].ID, _dispenseAmount, out _); UpdateUserInterface(); } diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index c5a815752a..9bb574a002 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -37,8 +37,9 @@ namespace Content.Server.GameObjects.Components.Chemistry Init(); } - public void Init() + public override void Init() { + base.Init(); _reactions = _prototypeManager.EnumeratePrototypes(); _audioSystem = _entitySystemManager.GetEntitySystem(); } diff --git a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs index 294fc20fcb..9923767503 100644 --- a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs @@ -119,7 +119,7 @@ namespace Content.Server.GameObjects.Components.Nutrition } //Add reagents ready for transfer to bloodstream to transferSolution - var transferSolution = new Solution(); + var transferSolution = IoCManager.InjectDependencies(new Solution()); foreach (var delta in _reagentDeltas.ToList()) //Use ToList here to remove entries while iterating { //Increment lifetime of reagents diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index 109b780108..365372f91f 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -172,7 +172,7 @@ namespace Content.Shared.Chemistry public Solution SplitSolution(decimal quantity) { if (quantity <= 0) - return new Solution(); + return IoCManager.InjectDependencies(new Solution()); Solution newSolution; @@ -183,7 +183,7 @@ namespace Content.Shared.Chemistry return newSolution; } - newSolution = new Solution(); + newSolution = IoCManager.InjectDependencies(new Solution()); var newTotalVolume = 0M; var ratio = (TotalVolume - quantity) / TotalVolume; @@ -235,7 +235,7 @@ namespace Content.Shared.Chemistry public Solution Clone() { var volume = 0M; - var newSolution = new Solution(); + var newSolution = IoCManager.InjectDependencies(new Solution()); for (var i = 0; i < _contents.Count; i++) { diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs index 8a1f2b8a58..81a91b3d38 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -17,7 +17,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry #pragma warning restore 649 [ViewVariables] - protected Solution ContainedSolution = new Solution(); + protected Solution ContainedSolution; private decimal _maxVolume; private SolutionCaps _capabilities; @@ -95,15 +95,20 @@ namespace Content.Shared.GameObjects.Components.Chemistry base.ExposeData(serializer); serializer.DataField(ref _maxVolume, "maxVol", 0M); - serializer.DataField(ref ContainedSolution, "contents", ContainedSolution); + serializer.DataField(ref ContainedSolution, "contents", IoCManager.InjectDependencies(new Solution())); serializer.DataField(ref _capabilities, "caps", SolutionCaps.None); } + public virtual void Init() + { + ContainedSolution = IoCManager.InjectDependencies(new Solution()); + } + /// protected override void Startup() { base.Startup(); - + RecalculateColor(); } @@ -113,7 +118,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry base.Shutdown(); ContainedSolution.RemoveAllSolution(); - ContainedSolution = new Solution(); + ContainedSolution = IoCManager.InjectDependencies(new Solution()); } public void RemoveAllSolution() From 16353a89e6b2541ad67d5a1eb9487021a4832d1a Mon Sep 17 00:00:00 2001 From: zumorica Date: Mon, 30 Mar 2020 01:15:03 +0200 Subject: [PATCH 09/54] Default interface implementation for IActionBlocker (defaults to true) --- .../EntitySystems/ActionBlockerSystem.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs b/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs index ce3205bb01..36de02bb79 100644 --- a/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs @@ -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 From aba3b0217e3143369dc94c7918c701f2f941d87e Mon Sep 17 00:00:00 2001 From: zumorica Date: Mon, 30 Mar 2020 01:15:23 +0200 Subject: [PATCH 10/54] Moving when dead ghosts you --- Content.Server/GameObjects/EntitySystems/MoverSystem.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs index 5860ac4e35..4b2038d855 100644 --- a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs @@ -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); } From a07c407f2ecbb195e11948099e7938a15d58b7e2 Mon Sep 17 00:00:00 2001 From: zumorica Date: Mon, 30 Mar 2020 01:15:43 +0200 Subject: [PATCH 11/54] Deadchat --- Content.Server/Chat/ChatCommands.cs | 6 +++++- Content.Server/Chat/ChatManager.cs | 12 ++++++++++++ Content.Server/Interfaces/Chat/IChatManager.cs | 1 + Content.Shared/Chat/ChatChannel.cs | 9 +++++++-- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Content.Server/Chat/ChatCommands.cs b/Content.Server/Chat/ChatCommands.cs index 04fdaeef94..4a88fab4b9 100644 --- a/Content.Server/Chat/ChatCommands.cs +++ b/Content.Server/Chat/ChatCommands.cs @@ -1,4 +1,5 @@ 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 +25,10 @@ namespace Content.Server.Chat var message = string.Join(" ", args); - chat.EntitySay(player.AttachedEntity, message); + if (player.AttachedEntity.HasComponent()) + chat.SendDeadChat(player, message); + else + chat.EntitySay(player.AttachedEntity, message); } } diff --git a/Content.Server/Chat/ChatManager.cs b/Content.Server/Chat/ChatManager.cs index 218fc31dcd..14ae50d395 100644 --- a/Content.Server/Chat/ChatManager.cs +++ b/Content.Server/Chat/ChatManager.cs @@ -2,6 +2,7 @@ using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Server.Interfaces.Chat; +using Content.Server.Observer; using Content.Shared.Chat; using Robust.Server.Interfaces.Player; using Robust.Shared.Interfaces.GameObjects; @@ -93,6 +94,17 @@ 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()).Select(p => p.ConnectedClient);; + + var msg = _netManager.CreateNetMessage(); + msg.Channel = ChatChannel.Dead; + msg.Message = message; + msg.MessageWrap = $"DEAD: {player.AttachedEntity.Name}: {{0}}"; + _netManager.ServerSendToMany(msg, clients.ToList()); + } + public void SendHookOOC(string sender, string message) { var msg = _netManager.CreateNetMessage(); diff --git a/Content.Server/Interfaces/Chat/IChatManager.cs b/Content.Server/Interfaces/Chat/IChatManager.cs index 7cfe1b4f44..26af31827a 100644 --- a/Content.Server/Interfaces/Chat/IChatManager.cs +++ b/Content.Server/Interfaces/Chat/IChatManager.cs @@ -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); } diff --git a/Content.Shared/Chat/ChatChannel.cs b/Content.Shared/Chat/ChatChannel.cs index 0aac182734..4c9da79f0b 100644 --- a/Content.Shared/Chat/ChatChannel.cs +++ b/Content.Shared/Chat/ChatChannel.cs @@ -6,7 +6,7 @@ namespace Content.Shared.Chat /// Represents chat channels that the player can filter chat tabs by. /// [Flags] - public enum ChatChannel : byte + public enum ChatChannel : short { None = 0, @@ -46,9 +46,14 @@ namespace Content.Shared.Chat /// Emotes = 64, + /// + /// Deadchat + /// + Dead = 128, + /// /// Unspecified. /// - Unspecified = 128, + Unspecified = 256, } } From 24c5909c43a5b011385fb7b432f5329871dc6806 Mon Sep 17 00:00:00 2001 From: zumorica Date: Mon, 30 Mar 2020 01:16:44 +0200 Subject: [PATCH 12/54] Ghosts retain their prior name, before defaulting to username. --- Content.Server/Observer/Ghost.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Content.Server/Observer/Ghost.cs b/Content.Server/Observer/Ghost.cs index 3ee16adf73..66410bff7f 100644 --- a/Content.Server/Observer/Ghost.cs +++ b/Content.Server/Observer/Ghost.cs @@ -1,4 +1,5 @@ using Content.Server.GameObjects; +using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces.GameTicking; using Content.Server.Players; using Content.Shared.GameObjects; @@ -25,15 +26,18 @@ namespace Content.Server.Observer } var mind = player.ContentData().Mind; - GridCoordinates position; var canReturn = player.AttachedEntity != null; + var name = player.AttachedEntity?.Name ?? player.Name; + + if (player.AttachedEntity != null && player.AttachedEntity.HasComponent()) + return; if (mind.VisitingEntity != null) { mind.UnVisit(); } - position = player.AttachedEntity?.Transform.GridPosition ?? IoCManager.Resolve().GetObserverSpawnPoint(); + var position = player.AttachedEntity?.Transform.GridPosition ?? IoCManager.Resolve().GetObserverSpawnPoint(); if (canReturn && player.AttachedEntity.TryGetComponent(out SpeciesComponent species)) { @@ -55,6 +59,7 @@ namespace Content.Server.Observer var entityManager = IoCManager.Resolve(); var ghost = entityManager.SpawnEntity("MobObserver", position); + ghost.Name = name; var ghostComponent = ghost.GetComponent(); ghostComponent.CanReturnToBody = canReturn; From d28af07fce7764bce34d0946fbad9fbf2ff6e899 Mon Sep 17 00:00:00 2001 From: zumorica Date: Mon, 30 Mar 2020 01:18:28 +0200 Subject: [PATCH 13/54] Ghost component implements IActionBlocker --- Content.Server/Observer/GhostComponent.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Content.Server/Observer/GhostComponent.cs b/Content.Server/Observer/GhostComponent.cs index d3ea271c52..029e7814ed 100644 --- a/Content.Server/Observer/GhostComponent.cs +++ b/Content.Server/Observer/GhostComponent.cs @@ -1,4 +1,5 @@ using System.Threading; +using Content.Server.GameObjects.EntitySystems; using Content.Server.Players; using Content.Shared.Observer; using Robust.Server.GameObjects; @@ -14,7 +15,7 @@ using Timer = Robust.Shared.Timers.Timer; namespace Content.Server.Observer { [RegisterComponent] - public class GhostComponent : SharedGhostComponent + public class GhostComponent : SharedGhostComponent, IActionBlocker { private bool _canReturnToBody = true; @@ -52,5 +53,14 @@ namespace Content.Server.Observer 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; } } From a0d114c67235344a3782b6d9561e88f4f04a9115 Mon Sep 17 00:00:00 2001 From: zumorica Date: Sun, 5 Apr 2020 02:29:04 +0200 Subject: [PATCH 14/54] Ghost sprites and a bunch of fixes --- .../Components}/Observer/GhostComponent.cs | 37 +++++++++++++++++-- Content.Client/UserInterface/GhostGui.cs | 2 +- Content.Server/Administration/AGhost.cs | 11 ++++-- Content.Server/Chat/ChatCommands.cs | 3 +- Content.Server/Chat/ChatManager.cs | 1 + .../Components}/Observer/GhostComponent.cs | 10 ++--- Content.Server/Observer/Ghost.cs | 2 + .../Observer/SharedGhostComponent.cs | 3 +- .../Prototypes/Entities/mobs/observer.yml | 4 ++ 9 files changed, 57 insertions(+), 16 deletions(-) rename Content.Client/{ => GameObjects/Components}/Observer/GhostComponent.cs (62%) rename Content.Server/{ => GameObjects/Components}/Observer/GhostComponent.cs (90%) rename Content.Shared/{ => GameObjects/Components}/Observer/SharedGhostComponent.cs (92%) diff --git a/Content.Client/Observer/GhostComponent.cs b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs similarity index 62% rename from Content.Client/Observer/GhostComponent.cs rename to Content.Client/GameObjects/Components/Observer/GhostComponent.cs index a35059dd48..c71310845a 100644 --- a/Content.Client/Observer/GhostComponent.cs +++ b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs @@ -1,14 +1,14 @@ using Content.Client.UserInterface; -using Content.Shared.Observer; +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.Log; using Robust.Shared.ViewVariables; -namespace Content.Client.Observer +namespace Content.Client.GameObjects.Components.Observer { [RegisterComponent] public class GhostComponent : SharedGhostComponent @@ -25,6 +25,8 @@ namespace Content.Client.Observer #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() @@ -34,6 +36,25 @@ namespace Content.Client.Observer _gui?.Dispose(); } + + private void SetGhostVisibility(bool visibility) + { + // So, for now this is a client-side hack... Please, PLEASE someone make this work server-side. + 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() ?? false; + } + public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) { @@ -52,10 +73,13 @@ namespace Content.Client.Observer } _gameHud.HandsContainer.AddChild(_gui); + SetGhostVisibility(true); + break; case PlayerDetachedMsg _: _gui.Parent?.RemoveChild(_gui); + SetGhostVisibility(false); break; } } @@ -69,7 +93,12 @@ namespace Content.Client.Observer if (!(curState is GhostComponentState state)) return; _canReturnToBody = state.CanReturnToBody; - _gui?.Update(); + + if (Owner == _playerManager.LocalPlayer.ControlledEntity) + { + _gui?.Update(); + } + } } } diff --git a/Content.Client/UserInterface/GhostGui.cs b/Content.Client/UserInterface/GhostGui.cs index 13d41aea69..959b3beff6 100644 --- a/Content.Client/UserInterface/GhostGui.cs +++ b/Content.Client/UserInterface/GhostGui.cs @@ -1,5 +1,5 @@ using System.Data; -using Content.Client.Observer; +using Content.Client.GameObjects.Components.Observer; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.IoC; diff --git a/Content.Server/Administration/AGhost.cs b/Content.Server/Administration/AGhost.cs index 57d5882480..40547197e1 100644 --- a/Content.Server/Administration/AGhost.cs +++ b/Content.Server/Administration/AGhost.cs @@ -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(); var entityManager = IoCManager.Resolve(); var ghost = entityManager.SpawnEntity("AdminObserver", player.AttachedEntity.Transform.GridPosition); - - mind.Visit(ghost); + if(canReturn) + mind.Visit(ghost); + else + mind.TransferTo(ghost); + ghost.GetComponent().CanReturnToBody = canReturn; } } } diff --git a/Content.Server/Chat/ChatCommands.cs b/Content.Server/Chat/ChatCommands.cs index 4a88fab4b9..00ca6a4124 100644 --- a/Content.Server/Chat/ChatCommands.cs +++ b/Content.Server/Chat/ChatCommands.cs @@ -1,4 +1,5 @@ -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; diff --git a/Content.Server/Chat/ChatManager.cs b/Content.Server/Chat/ChatManager.cs index 14ae50d395..ebd06862ee 100644 --- a/Content.Server/Chat/ChatManager.cs +++ b/Content.Server/Chat/ChatManager.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Server.GameObjects.Components.Observer; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Server.Interfaces.Chat; diff --git a/Content.Server/Observer/GhostComponent.cs b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs similarity index 90% rename from Content.Server/Observer/GhostComponent.cs rename to Content.Server/GameObjects/Components/Observer/GhostComponent.cs index 029e7814ed..11eb1d40db 100644 --- a/Content.Server/Observer/GhostComponent.cs +++ b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs @@ -1,18 +1,16 @@ -using System.Threading; using Content.Server.GameObjects.EntitySystems; using Content.Server.Players; -using Content.Shared.Observer; +using Content.Shared.GameObjects.Components.Observer; using Robust.Server.GameObjects; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; -using Robust.Shared.Log; using Robust.Shared.ViewVariables; using Timer = Robust.Shared.Timers.Timer; -namespace Content.Server.Observer +namespace Content.Server.GameObjects.Components.Observer { [RegisterComponent] public class GhostComponent : SharedGhostComponent, IActionBlocker @@ -46,6 +44,9 @@ namespace Content.Server.Observer actor.playerSession.ContentData().Mind.UnVisit(); } break; + case PlayerAttachedMsg _: + Dirty(); + break; case PlayerDetachedMsg _: Timer.Spawn(100, Owner.Delete); break; @@ -54,7 +55,6 @@ namespace Content.Server.Observer } } - public bool CanInteract() => false; public bool CanUse() => false; public bool CanThrow() => false; diff --git a/Content.Server/Observer/Ghost.cs b/Content.Server/Observer/Ghost.cs index 66410bff7f..712d673c5a 100644 --- a/Content.Server/Observer/Ghost.cs +++ b/Content.Server/Observer/Ghost.cs @@ -1,4 +1,5 @@ using Content.Server.GameObjects; +using Content.Server.GameObjects.Components.Observer; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces.GameTicking; using Content.Server.Players; @@ -7,6 +8,7 @@ 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 diff --git a/Content.Shared/Observer/SharedGhostComponent.cs b/Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs similarity index 92% rename from Content.Shared/Observer/SharedGhostComponent.cs rename to Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs index b768b1e7a5..f7a302bcba 100644 --- a/Content.Shared/Observer/SharedGhostComponent.cs +++ b/Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs @@ -1,9 +1,8 @@ using System; -using Content.Shared.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; -namespace Content.Shared.Observer +namespace Content.Shared.GameObjects.Components.Observer { public class SharedGhostComponent : Component { diff --git a/Resources/Prototypes/Entities/mobs/observer.yml b/Resources/Prototypes/Entities/mobs/observer.yml index 8fc3adb84f..26d817f212 100644 --- a/Resources/Prototypes/Entities/mobs/observer.yml +++ b/Resources/Prototypes/Entities/mobs/observer.yml @@ -16,3 +16,7 @@ DoRangeCheck: false - type: IgnorePause - type: Ghost + - type: Sprite + netsync: false + drawdepth: Mobs + texture: Mob/observer.png From 4e0242d47c44bbe2e254f21cce18988a8257c939 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sun, 5 Apr 2020 11:36:12 +0200 Subject: [PATCH 15/54] Replace decimal with ReagentUnit --- Content.Client/ClientContentIoC.cs | 1 - .../Components/Chemistry/InjectorComponent.cs | 5 +- .../Chemistry/ReagentDispenserWindow.cs | 2 +- .../Chemistry/Metabolism/DefaultDrink.cs | 13 +- .../Chemistry/Metabolism/DefaultFood.cs | 22 +-- Content.Server/Chemistry/ReactionPrototype.cs | 13 +- .../Components/Chemistry/InjectorComponent.cs | 14 +- .../Components/Chemistry/PourableComponent.cs | 9 +- .../Chemistry/ReagentDispenserComponent.cs | 16 +- .../Components/Chemistry/SolutionComponent.cs | 28 ++- .../Metabolism/BloodstreamComponent.cs | 6 +- .../Components/Nutrition/DrinkComponent.cs | 16 +- .../Components/Nutrition/FoodComponent.cs | 10 +- .../Components/Nutrition/StomachComponent.cs | 12 +- Content.Server/ServerContentIoC.cs | 1 - .../Chemistry/DefaultMetabolizable.cs | 8 +- Content.Shared/Chemistry/ReagentUnit.cs | 162 ++++++++++++++++ .../Chemistry/RounderForReagents.cs | 14 -- Content.Shared/Chemistry/Solution.cs | 52 +++--- .../Chemistry/SharedInjectorComponent.cs | 7 +- .../SharedReagentDispenserComponent.cs | 10 +- .../Components/Chemistry/SolutionComponent.cs | 30 +-- .../Interfaces/Chemistry/IMetabolizable.cs | 2 +- .../Chemistry/IRounderForReagents.cs | 7 - .../Shared/Chemistry/ReagentUnit_Tests.cs | 113 ++++++++++++ .../Shared/Chemistry/Solution_Tests.cs | 174 +++++++++--------- RobustToolbox | 2 +- 27 files changed, 496 insertions(+), 253 deletions(-) create mode 100644 Content.Shared/Chemistry/ReagentUnit.cs delete mode 100644 Content.Shared/Chemistry/RounderForReagents.cs delete mode 100644 Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs create mode 100644 Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs diff --git a/Content.Client/ClientContentIoC.cs b/Content.Client/ClientContentIoC.cs index d26587104c..24f1738d00 100644 --- a/Content.Client/ClientContentIoC.cs +++ b/Content.Client/ClientContentIoC.cs @@ -29,7 +29,6 @@ namespace Content.Client IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); - IoCManager.Register(); } } } diff --git a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs index 23e8c1a6ad..1d1a7f5dcb 100644 --- a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -7,6 +7,7 @@ using Robust.Client.UserInterface.Controls; using Robust.Shared.GameObjects; using Robust.Shared.Localization; using Robust.Shared.ViewVariables; +using Content.Shared.Chemistry; namespace Content.Client.GameObjects.Components.Chemistry { @@ -16,8 +17,8 @@ namespace Content.Client.GameObjects.Components.Chemistry [RegisterComponent] public class InjectorComponent : SharedInjectorComponent, IItemStatus { - [ViewVariables] private decimal CurrentVolume { get; set; } - [ViewVariables] private decimal 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; diff --git a/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs b/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs index d35b18346c..e9c8903de3 100644 --- a/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs +++ b/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs @@ -170,7 +170,7 @@ namespace Content.Client.GameObjects.Components.Chemistry Title = castState.DispenserName; UpdateContainerInfo(castState); - switch (castState.SelectedDispenseAmount) + switch (castState.SelectedDispenseAmount.Int()) { case 1: DispenseButton1.Pressed = true; diff --git a/Content.Server/Chemistry/Metabolism/DefaultDrink.cs b/Content.Server/Chemistry/Metabolism/DefaultDrink.cs index 0839d6f164..232ea1e108 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultDrink.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultDrink.cs @@ -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 - decimal 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; diff --git a/Content.Server/Chemistry/Metabolism/DefaultFood.cs b/Content.Server/Chemistry/Metabolism/DefaultFood.cs index aca26e2628..d220bb1805 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultFood.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultFood.cs @@ -1,4 +1,5 @@ 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; @@ -13,29 +14,26 @@ namespace Content.Server.Chemistry.Metabolism /// class DefaultFood : IMetabolizable { -#pragma warning disable 649 - [Dependency] private readonly IRounderForReagents _rounder; -#pragma warning restore 649 //Rate of metabolism in units / second - private decimal _metabolismRate; - public decimal MetabolismRate => _metabolismRate; + private ReagentUnit _metabolismRate; + public ReagentUnit MetabolismRate => _metabolismRate; //How much hunger is satiated when 1u of the reagent is metabolized - private decimal _nutritionFactor; - public decimal NutritionFactor => _nutritionFactor; + private float _nutritionFactor; + public float NutritionFactor => _nutritionFactor; void IExposeData.ExposeData(ObjectSerializer serializer) { - serializer.DataField(ref _metabolismRate, "rate", 1M); - serializer.DataField(ref _nutritionFactor, "nutrimentFactor", 30.0M); + 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 - decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) + ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - var metabolismAmount = _rounder.Round(MetabolismRate * (decimal) tickTime); + var metabolismAmount = MetabolismRate * tickTime; if (solutionEntity.TryGetComponent(out HungerComponent hunger)) - hunger.UpdateFood((float)(metabolismAmount * NutritionFactor)); + hunger.UpdateFood(metabolismAmount.Float() * NutritionFactor); //Return amount of reagent to be removed, remove reagent regardless of HungerComponent presence return metabolismAmount; diff --git a/Content.Server/Chemistry/ReactionPrototype.cs b/Content.Server/Chemistry/ReactionPrototype.cs index f5c9c8974f..36e2304b16 100644 --- a/Content.Server/Chemistry/ReactionPrototype.cs +++ b/Content.Server/Chemistry/ReactionPrototype.cs @@ -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 _reactants; - private Dictionary _products; + private Dictionary _products; private List _effects; public string ID => _id; @@ -28,7 +29,7 @@ namespace Content.Server.Chemistry /// /// Reagents created when the reaction occurs. /// - public IReadOnlyDictionary Products => _products; + public IReadOnlyDictionary Products => _products; /// /// Effects to be triggered when the reaction occurs. /// @@ -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()); - serializer.DataField(ref _products, "products", new Dictionary()); + serializer.DataField(ref _products, "products", new Dictionary()); serializer.DataField(ref _effects, "effects", new List()); } } @@ -51,13 +52,13 @@ namespace Content.Server.Chemistry /// public class ReactantPrototype : IExposeData { - private int _amount; + private ReagentUnit _amount; private bool _catalyst; /// /// Minimum amount of the reactant needed for the reaction to occur. /// - public int Amount => _amount; + public ReagentUnit Amount => _amount; /// /// Whether or not the reactant is a catalyst. Catalysts aren't removed when a reaction occurs. /// @@ -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); } } diff --git a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs index b9f0527793..865191b580 100644 --- a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -37,13 +37,13 @@ namespace Content.Server.GameObjects.Components.Chemistry /// attempt to inject it's entire contents upon use. /// [ViewVariables] - private decimal _transferAmount; + private ReagentUnit _transferAmount; /// /// Initial storage volume of the injector /// [ViewVariables] - private int _initialMaxVolume; + private ReagentUnit _initialMaxVolume; /// /// The state of the injector. Determines it's attack behavior. Containers must have the @@ -62,8 +62,8 @@ 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() @@ -165,7 +165,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } //Get transfer amount. May be smaller than _transferAmount if not enough room - decimal realTransferAmount = Math.Min(_transferAmount, targetBloodstream.EmptyVolume); + var realTransferAmount = ReagentUnit.Min(_transferAmount, targetBloodstream.EmptyVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, @@ -193,7 +193,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } //Get transfer amount. May be smaller than _transferAmount if not enough room - decimal realTransferAmount = Math.Min(_transferAmount, targetSolution.EmptyVolume); + var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.EmptyVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, @@ -221,7 +221,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } //Get transfer amount. May be smaller than _transferAmount if not enough room - decimal realTransferAmount = Math.Min(_transferAmount, targetSolution.CurrentVolume); + var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.CurrentVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, diff --git a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs index 439fc69ac9..f129e30321 100644 --- a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs @@ -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 decimal _transferAmount; + private ReagentUnit _transferAmount; /// /// The amount of solution to be transferred from this solution when clicking on other solutions with it. /// [ViewVariables] - public decimal 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.0M); + serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(5.0M)); } /// @@ -69,7 +70,7 @@ namespace Content.Server.GameObjects.Components.Chemistry return false; //Get transfer amount. May be smaller than _transferAmount if not enough room - decimal 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, diff --git a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs index 1f8f13625e..78d22c0f65 100644 --- a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs @@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.Components.Chemistry [ViewVariables] private string _packPrototypeId; [ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null; - [ViewVariables] private decimal _dispenseAmount = 10; + [ViewVariables] private ReagentUnit _dispenseAmount = ReagentUnit.New(10); [ViewVariables] private SolutionComponent Solution => _beakerContainer.ContainedEntity.GetComponent(); @@ -115,22 +115,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) @@ -172,7 +172,7 @@ namespace Content.Server.GameObjects.Components.Chemistry var beaker = _beakerContainer.ContainedEntity; if (beaker == null) { - return new ReagentDispenserBoundUserInterfaceState(false, 0, 0, + return new ReagentDispenserBoundUserInterfaceState(false, ReagentUnit.New(0), ReagentUnit.New(0), "", Inventory, Owner.Name, null, _dispenseAmount); } diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 9bb574a002..0328dcae67 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -22,7 +22,6 @@ namespace Content.Server.GameObjects.Components.Chemistry internal class SolutionComponent : Shared.GameObjects.Components.Chemistry.SolutionComponent, IExamine { #pragma warning disable 649 - [Dependency] private readonly IRounderForReagents _rounder; [Dependency] private readonly IPrototypeManager _prototypeManager; [Dependency] private readonly ILocalizationManager _loc; [Dependency] private readonly IEntitySystemManager _entitySystemManager; @@ -105,8 +104,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) @@ -185,8 +183,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) @@ -223,10 +220,9 @@ namespace Content.Server.GameObjects.Components.Chemistry } } - public bool TryAddReagent(string reagentId, decimal quantity, out decimal acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) + public bool TryAddReagent(string reagentId, ReagentUnit quantity, out ReagentUnit acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) { - quantity = _rounder.Round(quantity); - var toAcceptQuantity = _rounder.Round(MaxVolume - ContainedSolution.TotalVolume); + var toAcceptQuantity = MaxVolume - ContainedSolution.TotalVolume; if (quantity > toAcceptQuantity) { acceptedQuantity = toAcceptQuantity; @@ -269,16 +265,16 @@ namespace Content.Server.GameObjects.Components.Chemistry /// The reaction whose reactants will be checked for in the solution. /// The number of times the reaction can occur with the given solution. /// - private bool SolutionValidReaction(ReactionPrototype reaction, out decimal unitReactions) + private bool SolutionValidReaction(ReactionPrototype reaction, out ReagentUnit unitReactions) { - unitReactions = decimal.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 decimal reagentQuantity)) + if (!ContainsReagent(reactant.Key, out ReagentUnit reagentQuantity)) { return false; } - var currentUnitReactions = _rounder.Round(reagentQuantity / reactant.Value.Amount); + var currentUnitReactions = reagentQuantity / reactant.Value.Amount; if (currentUnitReactions < unitReactions) { unitReactions = currentUnitReactions; @@ -301,26 +297,26 @@ namespace Content.Server.GameObjects.Components.Chemistry /// Solution to be reacted. /// Reaction to occur. /// The number of times to cause this reaction. - private void PerformReaction(ReactionPrototype reaction, decimal unitReactions) + private void PerformReaction(ReactionPrototype reaction, ReagentUnit unitReactions) { //Remove non-catalysts foreach (var reactant in reaction.Reactants) { if (!reactant.Value.Catalyst) { - var amountToRemove = _rounder.Round(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 var 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 diff --git a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs index ef4579331d..b384b525b8 100644 --- a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs +++ b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs @@ -34,17 +34,17 @@ namespace Content.Server.GameObjects.Components.Metabolism /// Max volume of internal solution storage /// [ViewVariables] - private int _initialMaxVolume; + private ReagentUnit _initialMaxVolume; /// /// Empty volume of internal solution /// - public decimal 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() diff --git a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs index 26d552f71f..76dfe6147d 100644 --- a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs @@ -33,18 +33,18 @@ namespace Content.Server.GameObjects.Components.Nutrition [ViewVariables] private string _finishPrototype; - public decimal TransferAmount => _transferAmount; + public ReagentUnit TransferAmount => _transferAmount; [ViewVariables] - private decimal _transferAmount = 2; + private ReagentUnit _transferAmount = ReagentUnit.New(2); - public decimal 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 ReagentUnit _maxVolume; private bool _despawnOnFinish; @@ -57,7 +57,7 @@ namespace Content.Server.GameObjects.Components.Nutrition { return 0; } - return Math.Max(1, (int)Math.Ceiling(_contents.CurrentVolume / _transferAmount)); + return Math.Max(1, (int)Math.Ceiling((_contents.CurrentVolume / _transferAmount).Float())); } @@ -65,7 +65,7 @@ namespace Content.Server.GameObjects.Components.Nutrition { base.ExposeData(serializer); serializer.DataField(ref _initialContents, "contents", null); - serializer.DataField(ref _maxVolume, "max_volume", 0); + serializer.DataField(ref _maxVolume, "max_volume", ReagentUnit.New(0)); serializer.DataField(ref _useSound, "use_sound", "/Audio/items/drink.ogg"); // E.g. cola can when done or clear bottle, whatever // Currently this will enforce it has the same volume but this may change. @@ -91,7 +91,7 @@ namespace Content.Server.GameObjects.Components.Nutrition | SolutionCaps.Injectable; var pourable = Owner.AddComponent(); - pourable.TransferAmount = 5; + pourable.TransferAmount = ReagentUnit.New(5); } } @@ -150,7 +150,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)) { diff --git a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs index a528c223b6..a0039b14f9 100644 --- a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs @@ -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, (int)Math.Ceiling(_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)) { diff --git a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs index 9923767503..83f07710dd 100644 --- a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs @@ -27,7 +27,7 @@ namespace Content.Server.GameObjects.Components.Nutrition /// /// Max volume of internal solution storage /// - public decimal 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 /// [ViewVariables] - private int _initialMaxVolume; + private ReagentUnit _initialMaxVolume; /// /// Time in seconds between reagents being ingested and them being transferred to @@ -64,7 +64,7 @@ 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); } @@ -119,7 +119,7 @@ namespace Content.Server.GameObjects.Components.Nutrition } //Add reagents ready for transfer to bloodstream to transferSolution - var transferSolution = IoCManager.InjectDependencies(new Solution()); + var transferSolution = new Solution(); foreach (var delta in _reagentDeltas.ToList()) //Use ToList here to remove entries while iterating { //Increment lifetime of reagents @@ -141,10 +141,10 @@ namespace Content.Server.GameObjects.Components.Nutrition private class ReagentDelta { public readonly string ReagentId; - public readonly decimal Quantity; + public readonly ReagentUnit Quantity; public float Lifetime { get; private set; } - public ReagentDelta(string reagentId, decimal quantity) + public ReagentDelta(string reagentId, ReagentUnit quantity) { ReagentId = reagentId; Quantity = quantity; diff --git a/Content.Server/ServerContentIoC.cs b/Content.Server/ServerContentIoC.cs index 1c5937c0fe..f4bb04b9cc 100644 --- a/Content.Server/ServerContentIoC.cs +++ b/Content.Server/ServerContentIoC.cs @@ -28,7 +28,6 @@ namespace Content.Server IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); - IoCManager.Register(); } } } diff --git a/Content.Shared/Chemistry/DefaultMetabolizable.cs b/Content.Shared/Chemistry/DefaultMetabolizable.cs index 31d91d351b..05686ccf07 100644 --- a/Content.Shared/Chemistry/DefaultMetabolizable.cs +++ b/Content.Shared/Chemistry/DefaultMetabolizable.cs @@ -9,9 +9,6 @@ namespace Content.Shared.Chemistry //Default metabolism for reagents. Metabolizes the reagent with no effects class DefaultMetabolizable : IMetabolizable { -#pragma warning disable 649 - [Dependency] private readonly IRounderForReagents _rounder; -#pragma warning restore 649 //Rate of metabolism in units / second private decimal _metabolismRate = 1; public decimal MetabolismRate => _metabolismRate; @@ -21,10 +18,9 @@ namespace Content.Shared.Chemistry serializer.DataField(ref _metabolismRate, "rate", 1); } - decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) + ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - var metabolismAmount = _rounder.Round(MetabolismRate * (decimal)tickTime); - return metabolismAmount; + return ReagentUnit.New(MetabolismRate * (decimal)tickTime); } } } diff --git a/Content.Shared/Chemistry/ReagentUnit.cs b/Content.Shared/Chemistry/ReagentUnit.cs new file mode 100644 index 0000000000..a9350c5b3c --- /dev/null +++ b/Content.Shared/Chemistry/ReagentUnit.cs @@ -0,0 +1,162 @@ +using System; +using System.Linq; + +namespace Content.Shared.Chemistry +{ + [Serializable] + public struct ReagentUnit + { + private int _value; + private static readonly int Shift = 2; + + public static ReagentUnit MaxValue => new ReagentUnit(int.MaxValue); + + private decimal ShiftDown() + { + return _value / (decimal)Math.Pow(10, Shift); + } + + private decimal ShiftUp() + { + return _value * (decimal)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))); + } + + public static ReagentUnit New(float value) + { + return new ReagentUnit((int) Math.Round(value * (float) Math.Pow(10, Shift))); + } + + public static ReagentUnit New(double value) + { + return new ReagentUnit((int) Math.Round(value * Math.Pow(10, Shift))); + } + + 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 = a.ShiftDown(); + return New(aD * 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 override string ToString() => $"{ShiftDown()}"; + + public float Float() + { + return (float) ShiftDown(); + } + + public decimal Decimal() + { + return (decimal) ShiftDown(); + } + + public int Int() + { + return (int) ShiftDown(); + } + + public static ReagentUnit Min(params ReagentUnit[] reagentUnits) + { + return reagentUnits.OrderBy(x => x._value).First(); + } + + public override bool Equals(object obj) + { + return obj is ReagentUnit unit && + _value == unit._value; + } + + public override int GetHashCode() + { + return HashCode.Combine(_value); + } + } +} diff --git a/Content.Shared/Chemistry/RounderForReagents.cs b/Content.Shared/Chemistry/RounderForReagents.cs deleted file mode 100644 index d7c6ccce11..0000000000 --- a/Content.Shared/Chemistry/RounderForReagents.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Content.Shared.Interfaces.Chemistry; -using System; - -namespace Content.Shared.Chemistry -{ - - public class RounderForReagents : IRounderForReagents - { - public decimal Round(decimal value) - { - return Math.Round(value, 2); - } - } -} diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index 365372f91f..13b15d94b4 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -16,9 +16,6 @@ namespace Content.Shared.Chemistry /// public class Solution : IExposeData, IEnumerable { -#pragma warning disable 649 - [Dependency] private readonly IRounderForReagents _rounder; -#pragma warning restore 649 // Most objects on the station hold only 1 or 2 reagents [ViewVariables] private List _contents = new List(2); @@ -28,7 +25,7 @@ namespace Content.Shared.Chemistry /// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker). /// [ViewVariables] - public decimal TotalVolume { get; private set; } + public ReagentUnit TotalVolume { get; private set; } /// /// Constructs an empty solution (ex. an empty beaker). @@ -40,7 +37,7 @@ namespace Content.Shared.Chemistry /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public Solution(string reagentId, int quantity) + public Solution(string reagentId, ReagentUnit quantity) { AddReagent(reagentId, quantity); } @@ -52,7 +49,7 @@ namespace Content.Shared.Chemistry if (serializer.Reading) { - TotalVolume = 0; + TotalVolume = ReagentUnit.New(0); foreach (var reagent in _contents) { TotalVolume += reagent.Quantity; @@ -65,9 +62,8 @@ namespace Content.Shared.Chemistry /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public void AddReagent(string reagentId, decimal quantity) + public void AddReagent(string reagentId, ReagentUnit quantity) { - quantity = _rounder.Round(quantity); if (quantity <= 0) return; @@ -91,7 +87,7 @@ namespace Content.Shared.Chemistry /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public decimal GetReagentQuantity(string reagentId) + public ReagentUnit GetReagentQuantity(string reagentId) { for (var i = 0; i < _contents.Count; i++) { @@ -99,10 +95,10 @@ namespace Content.Shared.Chemistry return _contents[i].Quantity; } - return 0; + return ReagentUnit.New(0); } - public void RemoveReagent(string reagentId, decimal quantity) + public void RemoveReagent(string reagentId, ReagentUnit quantity) { if(quantity <= 0) return; @@ -115,7 +111,7 @@ namespace Content.Shared.Chemistry var curQuantity = reagent.Quantity; - var newQuantity = _rounder.Round(curQuantity - quantity); + var newQuantity = curQuantity - quantity; if (newQuantity <= 0) { _contents.RemoveSwap(i); @@ -135,12 +131,12 @@ namespace Content.Shared.Chemistry /// Remove the specified quantity from this solution. /// /// The quantity of this solution to remove - public void RemoveSolution(decimal quantity) + public void RemoveSolution(ReagentUnit quantity) { if(quantity <= 0) return; - var ratio = _rounder.Round(TotalVolume - quantity) / TotalVolume; + var ratio = (TotalVolume - quantity).Decimal() / TotalVolume.Decimal(); if (ratio <= 0) { @@ -155,24 +151,24 @@ 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 = _rounder.Round(oldQuantity * ratio); + var newQuantity = oldQuantity * ratio; _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); } - TotalVolume = _rounder.Round(TotalVolume * ratio); + TotalVolume = TotalVolume * ratio; } public void RemoveAllSolution() { _contents.Clear(); - TotalVolume = 0; + TotalVolume = ReagentUnit.New(0); } - public Solution SplitSolution(decimal quantity) + public Solution SplitSolution(ReagentUnit quantity) { if (quantity <= 0) - return IoCManager.InjectDependencies(new Solution()); + return new Solution(); Solution newSolution; @@ -183,15 +179,15 @@ namespace Content.Shared.Chemistry return newSolution; } - newSolution = IoCManager.InjectDependencies(new Solution()); - var newTotalVolume = 0M; - var ratio = (TotalVolume - quantity) / TotalVolume; + newSolution = new Solution(); + 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); @@ -199,7 +195,7 @@ namespace Content.Shared.Chemistry newTotalVolume += splitQuantity; } - TotalVolume = (int)Math.Floor(TotalVolume * ratio); + TotalVolume = TotalVolume * ratio; newSolution.TotalVolume = newTotalVolume; return newSolution; @@ -234,8 +230,8 @@ namespace Content.Shared.Chemistry public Solution Clone() { - var volume = 0M; - var newSolution = IoCManager.InjectDependencies(new Solution()); + var volume = ReagentUnit.New(0); + var newSolution = new Solution(); for (var i = 0; i < _contents.Count; i++) { @@ -252,9 +248,9 @@ namespace Content.Shared.Chemistry public readonly struct ReagentQuantity { public readonly string ReagentId; - public readonly decimal Quantity; + public readonly ReagentUnit Quantity; - public ReagentQuantity(string reagentId, decimal quantity) + public ReagentQuantity(string reagentId, ReagentUnit quantity) { ReagentId = reagentId; Quantity = quantity; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs index 8bf232b96f..4fbef4ff26 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs @@ -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 decimal CurrentVolume { get; } - public decimal TotalVolume { get; } + public ReagentUnit CurrentVolume { get; } + public ReagentUnit TotalVolume { get; } public InjectorToggleMode CurrentMode { get; } - public InjectorComponentState(decimal currentVolume, decimal totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR) + public InjectorComponentState(ReagentUnit currentVolume, ReagentUnit totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR) { CurrentVolume = currentVolume; TotalVolume = totalVolume; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs index 9907fb3550..d9ff0581dd 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs @@ -26,8 +26,8 @@ namespace Content.Shared.GameObjects.Components.Chemistry public class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState { public readonly bool HasBeaker; - public readonly decimal BeakerCurrentVolume; - public readonly decimal BeakerMaxVolume; + public readonly ReagentUnit BeakerCurrentVolume; + public readonly ReagentUnit BeakerMaxVolume; public readonly string ContainerName; /// /// A list of the reagents which this dispenser can dispense. @@ -38,10 +38,10 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// public readonly List ContainerReagents; public readonly string DispenserName; - public readonly decimal SelectedDispenseAmount; + public readonly ReagentUnit SelectedDispenseAmount; - public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, decimal beakerCurrentVolume, decimal beakerMaxVolume, string containerName, - List inventory, string dispenserName, List containerReagents, decimal selectedDispenseAmount) + public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, ReagentUnit beakerCurrentVolume, ReagentUnit beakerMaxVolume, string containerName, + List inventory, string dispenserName, List containerReagents, ReagentUnit selectedDispenseAmount) { HasBeaker = hasBeaker; BeakerCurrentVolume = beakerCurrentVolume; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs index 81a91b3d38..615c1d85b0 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -18,7 +18,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry [ViewVariables] protected Solution ContainedSolution; - private decimal _maxVolume; + private ReagentUnit _maxVolume; private SolutionCaps _capabilities; /// @@ -30,7 +30,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The maximum volume of the container. /// [ViewVariables(VVAccess.ReadWrite)] - public decimal MaxVolume + public ReagentUnit MaxVolume { get => _maxVolume; set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced. @@ -40,13 +40,13 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The total volume of all the of the reagents in the container. /// [ViewVariables] - public decimal CurrentVolume => ContainedSolution.TotalVolume; + public ReagentUnit CurrentVolume => ContainedSolution.TotalVolume; /// /// The volume without reagents remaining in the container. /// [ViewVariables] - public decimal EmptyVolume => MaxVolume - CurrentVolume; + public ReagentUnit EmptyVolume => MaxVolume - CurrentVolume; /// /// The current blended color of all the reagents in the container. @@ -94,14 +94,14 @@ namespace Content.Shared.GameObjects.Components.Chemistry { base.ExposeData(serializer); - serializer.DataField(ref _maxVolume, "maxVol", 0M); - serializer.DataField(ref ContainedSolution, "contents", IoCManager.InjectDependencies(new Solution())); + serializer.DataField(ref _maxVolume, "maxVol", ReagentUnit.New(0M)); + serializer.DataField(ref ContainedSolution, "contents", new Solution()); serializer.DataField(ref _capabilities, "caps", SolutionCaps.None); } public virtual void Init() { - ContainedSolution = IoCManager.InjectDependencies(new Solution()); + ContainedSolution = new Solution(); } /// @@ -118,7 +118,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry base.Shutdown(); ContainedSolution.RemoveAllSolution(); - ContainedSolution = IoCManager.InjectDependencies(new Solution()); + ContainedSolution = new Solution(); } public void RemoveAllSolution() @@ -127,7 +127,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry OnSolutionChanged(); } - public bool TryRemoveReagent(string reagentId, decimal quantity) + public bool TryRemoveReagent(string reagentId, ReagentUnit quantity) { if (!ContainsReagent(reagentId, out var currentQuantity)) return false; @@ -141,7 +141,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// /// Quantity of this solution to remove /// Whether or not the solution was successfully removed - public bool TryRemoveSolution(int quantity) + public bool TryRemoveSolution(ReagentUnit quantity) { if (CurrentVolume == 0) return false; @@ -151,7 +151,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry return true; } - public Solution SplitSolution(decimal quantity) + public Solution SplitSolution(ReagentUnit quantity) { var solutionSplit = ContainedSolution.SplitSolution(quantity); OnSolutionChanged(); @@ -164,7 +164,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry SubstanceColor = Color.White; Color mixColor = default; - var runningTotalQuantity = 0M; + var runningTotalQuantity = ReagentUnit.New(0M); foreach (var reagent in ContainedSolution) { @@ -176,7 +176,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry if (mixColor == default) mixColor = proto.SubstanceColor; - mixColor = BlendRGB(mixColor, proto.SubstanceColor, (float) (reagent.Quantity / runningTotalQuantity)); + mixColor = BlendRGB(mixColor, proto.SubstanceColor, reagent.Quantity.Float() / runningTotalQuantity.Float()); } } @@ -221,7 +221,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The reagent to check for. /// Output the quantity of the reagent if it is contained, 0 if it isn't. /// Return true if the solution contains the reagent. - public bool ContainsReagent(string reagentId, out decimal quantity) + public bool ContainsReagent(string reagentId, out ReagentUnit quantity) { foreach (var reagent in ContainedSolution.Contents) { @@ -231,7 +231,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry return true; } } - quantity = 0; + quantity = ReagentUnit.New(0); return false; } diff --git a/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs b/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs index 3a55de645f..1973012d52 100644 --- a/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs +++ b/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs @@ -19,6 +19,6 @@ namespace Content.Shared.Interfaces.Chemistry /// The reagent id /// The time since the last metabolism tick in seconds. /// The amount of reagent to be removed. The metabolizing organ should handle removing the reagent. - decimal Metabolize(IEntity solutionEntity, string reagentId, float tickTime); + ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime); } } diff --git a/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs b/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs deleted file mode 100644 index da8a1871c7..0000000000 --- a/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Content.Shared.Interfaces.Chemistry -{ - public interface IRounderForReagents - { - decimal Round(decimal value); - } -} diff --git a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs new file mode 100644 index 0000000000..f2f11b4fdb --- /dev/null +++ b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs @@ -0,0 +1,113 @@ +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.001f, 1.001f, "2")] + [TestCase(1.001f, 1.004f, "2")] + [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, 100)] + [TestCase(2.005f, 201)] + public void FloatRoundingTest(float a, int expected) + { + var result = (int) Math.Round(a * (float) Math.Pow(10, 2)); + Assert.AreEqual(expected, result); + } + } +} diff --git a/Content.Tests/Shared/Chemistry/Solution_Tests.cs b/Content.Tests/Shared/Chemistry/Solution_Tests.cs index cada149224..e3024d3a07 100644 --- a/Content.Tests/Shared/Chemistry/Solution_Tests.cs +++ b/Content.Tests/Shared/Chemistry/Solution_Tests.cs @@ -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,223 @@ 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 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)); } } } diff --git a/RobustToolbox b/RobustToolbox index 0a306514a2..ec52102d02 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 0a306514a2c927d3e19f2f6fcec9eee64b488ce4 +Subproject commit ec52102d0279281a00cc1c6811330a13ddaf975b From f8b73fdc3bbbdf707985798f724be92821f7b4f8 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sun, 5 Apr 2020 11:45:23 +0200 Subject: [PATCH 16/54] Fix null-state handling for InjectorComponent --- .../GameObjects/Components/Chemistry/InjectorComponent.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs index 272a67d964..4d46ddf5f0 100644 --- a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -30,6 +30,10 @@ namespace Content.Client.GameObjects.Components.Chemistry //Handle net updates public override void HandleComponentState(ComponentState curState, ComponentState nextState) { + if(curState == null) + { + return; + } if(curState.GetType() == typeof(InjectorComponentState)) { var cast = (InjectorComponentState) curState; From d400f77129d07c480d3e6ce1c40368ad369ac079 Mon Sep 17 00:00:00 2001 From: Injazz Date: Wed, 8 Apr 2020 15:53:15 +0500 Subject: [PATCH 17/54] Chemistry revamp and bartending features -Ability to mix drinks to create cocktails with shiny icons -New Chemistry System which can relay chemistry events to corresponding components -moved some solution logic from Shared to Server -fixed some weird stuff with DrinkComponent --- Content.Client/EntryPoint.cs | 3 +- .../Components/Chemistry/InjectorComponent.cs | 14 +- .../Chemistry/ReagentDispenserComponent.cs | 13 +- .../Components/Chemistry/SolutionComponent.cs | 216 ++++- .../TransformableContainerComponent.cs | 80 ++ .../Metabolism/BloodstreamComponent.cs | 11 +- .../Components/Nutrition/DrinkComponent.cs | 70 +- .../Components/Nutrition/StomachComponent.cs | 15 +- .../EntitySystems/ChemistrySystem.cs | 42 + Content.Shared/Chemistry/ReagentPrototype.cs | 4 + .../Chemistry/SharedSolutionComponent.cs | 46 + .../Components/Chemistry/SolutionComponent.cs | 238 ----- .../Entities/buildings/booze_dispenser.yml | 4 + .../Entities/buildings/chem_dispenser.yml | 1 + .../Entities/buildings/soda_dispenser.yml | 1 + .../Entities/items/Consumables/drinks.yml | 889 +++--------------- .../items/Consumables/drinks_bottles.yml | 112 +-- .../items/Consumables/drinks_cans.yml | 35 +- .../items/Consumables/drinks_cups.yml | 53 +- .../items/Consumables/trash_drinks.yml | 22 +- Resources/Prototypes/Entities/mobs/human.yml | 11 +- Resources/Prototypes/Reactions/drinks.yml | 103 ++ Resources/Prototypes/Reagents/drinks.yml | 103 ++ Resources/Prototypes/Reagents/elements.yml | 6 + 24 files changed, 822 insertions(+), 1270 deletions(-) create mode 100644 Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs create mode 100644 Content.Server/GameObjects/EntitySystems/ChemistrySystem.cs create mode 100644 Content.Shared/GameObjects/Components/Chemistry/SharedSolutionComponent.cs delete mode 100644 Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs create mode 100644 Resources/Prototypes/Reactions/drinks.yml diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index e1b50b11e8..1cc3c720a0 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -134,6 +134,7 @@ namespace Content.Client "Paper", "Write", "Bloodstream", + "TransformableContainer", "Mind", "MovementSpeedModifier", "StorageFill" @@ -148,7 +149,7 @@ namespace Content.Client factory.Register(); factory.Register(); - factory.Register(); + factory.Register(); factory.Register(); factory.Register(); diff --git a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs index d4140d31f4..b0126dbd10 100644 --- a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -65,19 +65,11 @@ namespace Content.Server.GameObjects.Components.Chemistry serializer.DataField(ref _initialMaxVolume, "initialMaxVolume", 15); serializer.DataField(ref _transferAmount, "transferAmount", 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(); _internalContents.Capabilities |= SolutionCaps.Injector; - //Set _toggleState based on prototype _toggleState = _injectOnly ? InjectorToggleMode.Inject : InjectorToggleMode.Draw; } diff --git a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs index 6fb12aa337..ab39fcad69 100644 --- a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs @@ -30,7 +30,7 @@ namespace Content.Server.GameObjects.Components.Chemistry [RegisterComponent] [ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IAttackBy))] - public class ReagentDispenserComponent : SharedReagentDispenserComponent, IActivate, IAttackBy + public class ReagentDispenserComponent : SharedReagentDispenserComponent, IActivate, IAttackBy, ISolutionChange { #pragma warning disable 649 [Dependency] private readonly IServerNotifyManager _notifyManager; @@ -161,7 +161,7 @@ namespace Content.Server.GameObjects.Components.Chemistry private bool PlayerCanUseDispenser(IEntity playerEntity) { //Need player entity to check if they are still able to use the dispenser - if (playerEntity == null) + if (playerEntity == null) return false; //Check if player can interact in their current state if (!ActionBlockerSystem.CanInteract(playerEntity) || !ActionBlockerSystem.CanUse(playerEntity)) @@ -207,7 +207,6 @@ namespace Content.Server.GameObjects.Components.Chemistry return; var beaker = _beakerContainer.ContainedEntity; - Solution.SolutionChanged -= HandleSolutionChangedEvent; _beakerContainer.Remove(_beakerContainer.ContainedEntity); UpdateUserInterface(); @@ -304,7 +303,6 @@ namespace Content.Server.GameObjects.Components.Chemistry else { _beakerContainer.Insert(activeHandEntity); - Solution.SolutionChanged += HandleSolutionChangedEvent; UpdateUserInterface(); } } @@ -317,10 +315,7 @@ namespace Content.Server.GameObjects.Components.Chemistry return true; } - private void HandleSolutionChangedEvent() - { - UpdateUserInterface(); - } + void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => UpdateUserInterface(); private void ClickSound() { @@ -329,5 +324,7 @@ namespace Content.Server.GameObjects.Components.Chemistry sound.Play("/Audio/machines/machine_switch.ogg", AudioParams.Default.WithVolume(-2f)); } } + + } } diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 98b064bfc2..87c15bd061 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.ComponentModel.Design; +using System.Linq; using Content.Server.Chemistry; +using Content.Shared.GameObjects.Components.Chemistry; using Content.Server.GameObjects.Components.Nutrition; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; @@ -12,16 +14,19 @@ 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; namespace Content.Server.GameObjects.Components.Chemistry { /// - /// Shared ECS component that manages a liquid solution of reagents. + /// ECS component that manages a liquid solution of reagents. /// [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,17 +36,93 @@ namespace Content.Server.GameObjects.Components.Chemistry private IEnumerable _reactions; private AudioSystem _audioSystem; + private ChemistrySystem _chemistrySystem; + + [ViewVariables] + protected Solution _containedSolution = new Solution(); + protected int _maxVolume; + private SolutionCaps _capabilities; + + /// + /// The maximum volume of the container. + /// + [ViewVariables(VVAccess.ReadWrite)] + public int MaxVolume + { + get => _maxVolume; + set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced. + } + + /// + /// The total volume of all the of the reagents in the container. + /// + [ViewVariables] + public int CurrentVolume => _containedSolution.TotalVolume; + + /// + /// The volume without reagents remaining in the container. + /// + [ViewVariables] + public int EmptyVolume => MaxVolume - CurrentVolume; + + /// + /// The current blended color of all the reagents in the container. + /// + [ViewVariables(VVAccess.ReadWrite)] + public Color SubstanceColor { get; private set; } + + /// + /// The current capabilities of this container (is the top open to pour? can I inject it into another object?). + /// + [ViewVariables(VVAccess.ReadWrite)] + public SolutionCaps Capabilities + { + get => _capabilities; + set => _capabilities = value; + } + + public IReadOnlyList ReagentList => _containedSolution.Contents; + + /// + /// Shortcut for Capabilities PourIn flag to avoid binary operators. + /// + public bool CanPourIn => (Capabilities & SolutionCaps.PourIn) != 0; + /// + /// Shortcut for Capabilities PourOut flag to avoid binary operators. + /// + public bool CanPourOut => (Capabilities & SolutionCaps.PourOut) != 0; + /// + /// Shortcut for Capabilities Injectable flag + /// + public bool Injectable => (Capabilities & SolutionCaps.Injectable) != 0; + /// + /// Shortcut for Capabilities Injector flag + /// + public bool Injector => (Capabilities & SolutionCaps.Injector) != 0; + + /// + 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); + } + + public override void Initialize() + { + base.Initialize(); + _audioSystem = _entitySystemManager.GetEntitySystem(); + _chemistrySystem = _entitySystemManager.GetEntitySystem(); + _reactions = _prototypeManager.EnumeratePrototypes(); + + } protected override void Startup() { base.Startup(); - Init(); - } - - public void Init() - { - _reactions = _prototypeManager.EnumeratePrototypes(); - _audioSystem = _entitySystemManager.GetEntitySystem(); + RecalculateColor(); } /// @@ -54,6 +135,85 @@ namespace Content.Server.GameObjects.Components.Chemistry _reactions = _prototypeManager.EnumeratePrototypes(); } + /// + 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; + } + + /// + /// Attempt to remove the specified quantity from this solution + /// + /// Quantity of this solution to remove + /// Whether or not the solution was successfully removed + 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); + } + + /// /// Transfers solution from the held container to the target container. /// @@ -121,6 +281,10 @@ namespace Content.Server.GameObjects.Components.Chemistry void IExamine.Examine(FormattedMessage message) { message.AddText(_loc.GetString("Contains:\n")); + if (ReagentList.Count == 0) + { + message.AddText("Nothing.\n"); + } foreach (var reagent in ReagentList) { if (_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype proto)) @@ -256,7 +420,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } if(!skipReactionCheck) CheckForReaction(); - OnSolutionChanged(); + _chemistrySystem.HandleSolutionChange(Owner); return true; } @@ -324,5 +488,37 @@ namespace Content.Server.GameObjects.Components.Chemistry //Play reaction sound client-side _audioSystem.Play("/Audio/effects/chemistry/bubbles.ogg", Owner.Transform.GridPosition); } + + /// + /// Check if the solution contains the specified reagent. + /// + /// The reagent to check for. + /// Output the quantity of the reagent if it is contained, 0 if it isn't. + /// Return true if the solution contains the reagent. + public bool ContainsReagent(string reagentId, out int quantity) + { + foreach (var reagent in _containedSolution.Contents) + { + if (reagent.ReagentId == reagentId) + { + quantity = reagent.Quantity; + return true; + } + } + quantity = 0; + return false; + } + + public string GetMajorReagentId() + { + if (_containedSolution.Contents.Count == 0) + { + return ""; + } + var majorReagent = _containedSolution.Contents.OrderByDescending(reagent => reagent.Quantity).First();; + return majorReagent.ReagentId; + } + + protected virtual void OnSolutionChanged() => _chemistrySystem.HandleSolutionChange(Owner); } } diff --git a/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs b/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs new file mode 100644 index 0000000000..f08dba169b --- /dev/null +++ b/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs @@ -0,0 +1,80 @@ +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 SpriteSpecifier _initialSprite; + private string _initialName; + private string _initialDescription; + private SpriteComponent _sprite; + + private ReagentPrototype _currentReagent; + + public override void Initialize() + { + base.Initialize(); + + _sprite = Owner.GetComponent(); + _initialSprite = new SpriteSpecifier.Rsi(new ResourcePath(_sprite.BaseRSIPath), "icon"); + _initialName = Owner.Name; + _initialDescription = Owner.Description; + } + + public void CancelTransformation() + { + _currentReagent = null; + _sprite.LayerSetSprite(0, _initialSprite); + Owner.Name = _initialName; + Owner.Description = _initialDescription; + } + + void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) + { + var solution = eventArgs.Owner.GetComponent(); + + //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; + Owner.Description = proto.Description; + _currentReagent = proto; + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs index 0fa862ce82..9ce1dffacb 100644 --- a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs +++ b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs @@ -47,16 +47,11 @@ namespace Content.Server.GameObjects.Components.Metabolism serializer.DataField(ref _initialMaxVolume, "maxVolume", 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(); _internalSolution.MaxVolume = _initialMaxVolume; - _internalSolution.Owner = Owner; //Manually set owner to avoid crash when VV'ing this } /// diff --git a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs index ab6f259b24..51be393f2c 100644 --- a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs @@ -16,7 +16,7 @@ using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Nutrition { [RegisterComponent] - public class DrinkComponent : Component, IAfterAttack, IUse + public class DrinkComponent : Component, IAfterAttack, IUse, ISolutionChange { #pragma warning disable 649 [Dependency] private readonly ILocalizationManager _localizationManager; @@ -42,9 +42,6 @@ namespace Content.Server.GameObjects.Components.Nutrition set => _contents.MaxVolume = value; } - private Solution _initialContents; // This is just for loading from yaml - private int _maxVolume; - private bool _despawnOnFinish; private bool _drinking; @@ -63,53 +60,21 @@ namespace Content.Server.GameObjects.Components.Nutrition public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); - serializer.DataField(ref _initialContents, "contents", null); - serializer.DataField(ref _maxVolume, "max_volume", 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(); - //Ensure SolutionComponent supports click transferring if custom one not set - _contents.Capabilities = SolutionCaps.PourIn - | SolutionCaps.PourOut - | SolutionCaps.Injectable; - - var pourable = Owner.AddComponent(); - 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(); + _contents.Capabilities = SolutionCaps.PourIn + | SolutionCaps.PourOut + | SolutionCaps.Injectable; + _drinking = false; Owner.TryGetComponent(out AppearanceComponent appearance); _appearanceComponent = appearance; _appearanceComponent?.SetData(SharedFoodComponent.FoodVisuals.MaxUses, MaxVolume); @@ -168,7 +133,7 @@ namespace Content.Server.GameObjects.Components.Nutrition _drinking = false; } - Finish(user); + //Finish(user); } /// @@ -177,14 +142,15 @@ namespace Content.Server.GameObjects.Components.Nutrition /// or convert it to another entity, like an empty variant. /// /// The entity that is using the drink - public void Finish(IEntity user) + /* + public void Finish(IEntity user) { // Drink containers are mostly transient. + // are you sure about that if (_drinking || !_despawnOnFinish || UsesLeft() > 0) return; var gridPos = Owner.Transform.GridPosition; - _contents.SolutionChanged -= HandleSolutionChangedEvent; Owner.Delete(); if (_finishPrototype == null || user == null) @@ -205,16 +171,8 @@ namespace Content.Server.GameObjects.Components.Nutrition { drinkComponent.MaxVolume = MaxVolume; } - } + }*/ - /// - /// 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. - /// - private void HandleSolutionChangedEvent() - { - Finish(null); - } + void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) { } //Finish(null); } } diff --git a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs index 95353351f9..d8eb1a899c 100644 --- a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs @@ -68,22 +68,15 @@ namespace Content.Server.GameObjects.Components.Nutrition serializer.DataField(ref _digestionDelay, "digestionDelay", 20); } - - public override void Initialize() + protected override void Startup() { - base.Initialize(); - //Doesn't use Owner.AddComponent<>() to avoid cross-contamination (e.g. with blood or whatever they holds other solutions) - _stomachContents = new SolutionComponent(); - _stomachContents.InitializeFromPrototype(); + _stomachContents = Owner.GetComponent(); _stomachContents.MaxVolume = _initialMaxVolume; - _stomachContents.Owner = Owner; //Manually set owner to avoid crash when VV'ing this - - //Ensure bloodstream in present if (!Owner.TryGetComponent(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)); } } diff --git a/Content.Server/GameObjects/EntitySystems/ChemistrySystem.cs b/Content.Server/GameObjects/EntitySystems/ChemistrySystem.cs new file mode 100644 index 0000000000..69bd555460 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/ChemistrySystem.cs @@ -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 +{ + /// + /// This interface gives components behavior on whether entities solution (implying SolutionComponent is in place) is changed + /// + public interface ISolutionChange + { + /// + /// Called when solution is mixed with some other solution, or when some part of the solution is removed + /// + 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().ToList(); + + foreach (var solutionChangeArg in solutionChangeArgs) + { + solutionChangeArg.SolutionChanged(eventArgs); + } + } + } +} diff --git a/Content.Shared/Chemistry/ReagentPrototype.cs b/Content.Shared/Chemistry/ReagentPrototype.cs index 65b4756047..2840ca4855 100644 --- a/Content.Shared/Chemistry/ReagentPrototype.cs +++ b/Content.Shared/Chemistry/ReagentPrototype.cs @@ -21,6 +21,7 @@ namespace Content.Shared.Chemistry private string _description; private Color _substanceColor; private List _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 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 {new DefaultMetabolizable()}); diff --git a/Content.Shared/GameObjects/Components/Chemistry/SharedSolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SharedSolutionComponent.cs new file mode 100644 index 0000000000..232bea635e --- /dev/null +++ b/Content.Shared/GameObjects/Components/Chemistry/SharedSolutionComponent.cs @@ -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"; + + /// + public sealed override uint? NetID => ContentNetIDs.SOLUTION; + + [Serializable, NetSerializable] + public class SolutionComponentState : ComponentState + { + public SolutionComponentState() : base(ContentNetIDs.SOLUTION) { } + } + + /// + public override ComponentState GetComponentState() + { + return new SolutionComponentState(); + } + + /// + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + base.HandleComponentState(curState, nextState); + + if(curState == null) + return; + + var compState = (SolutionComponentState)curState; + + //TODO: Make me work! + } + + } +} diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs deleted file mode 100644 index 426daa0639..0000000000 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ /dev/null @@ -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; - - /// - /// Triggered when the solution contents change. - /// - public event Action SolutionChanged; - - /// - /// The maximum volume of the container. - /// - [ViewVariables(VVAccess.ReadWrite)] - public int MaxVolume - { - get => _maxVolume; - set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced. - } - - /// - /// The total volume of all the of the reagents in the container. - /// - [ViewVariables] - public int CurrentVolume => _containedSolution.TotalVolume; - - /// - /// The volume without reagents remaining in the container. - /// - [ViewVariables] - public int EmptyVolume => MaxVolume - CurrentVolume; - - /// - /// The current blended color of all the reagents in the container. - /// - [ViewVariables(VVAccess.ReadWrite)] - public Color SubstanceColor { get; private set; } - - /// - /// The current capabilities of this container (is the top open to pour? can I inject it into another object?). - /// - [ViewVariables(VVAccess.ReadWrite)] - public SolutionCaps Capabilities - { - get => _capabilities; - set => _capabilities = value; - } - - public IReadOnlyList ReagentList => _containedSolution.Contents; - - /// - /// Shortcut for Capabilities PourIn flag to avoid binary operators. - /// - public bool CanPourIn => (Capabilities & SolutionCaps.PourIn) != 0; - /// - /// Shortcut for Capabilities PourOut flag to avoid binary operators. - /// - public bool CanPourOut => (Capabilities & SolutionCaps.PourOut) != 0; - /// - /// Shortcut for Capabilities Injectable flag - /// - public bool Injectable => (Capabilities & SolutionCaps.Injectable) != 0; - /// - /// Shortcut for Capabilities Injector flag - /// - public bool Injector => (Capabilities & SolutionCaps.Injector) != 0; - - /// - public override string Name => "Solution"; - - /// - public sealed override uint? NetID => ContentNetIDs.SOLUTION; - - /// - 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); - } - - /// - protected override void Startup() - { - base.Startup(); - - RecalculateColor(); - } - - /// - 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; - } - - /// - /// Attempt to remove the specified quantity from this solution - /// - /// Quantity of this solution to remove - /// Whether or not the solution was successfully removed - 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); - } - - /// - public override ComponentState GetComponentState() - { - return new SolutionComponentState(); - } - - /// - 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) { } - } - - /// - /// Check if the solution contains the specified reagent. - /// - /// The reagent to check for. - /// Output the quantity of the reagent if it is contained, 0 if it isn't. - /// Return true if the solution contains the reagent. - 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(); - } - } -} diff --git a/Resources/Prototypes/Entities/buildings/booze_dispenser.yml b/Resources/Prototypes/Entities/buildings/booze_dispenser.yml index 28010fe2b4..8e7f3ae2e8 100644 --- a/Resources/Prototypes/Entities/buildings/booze_dispenser.yml +++ b/Resources/Prototypes/Entities/buildings/booze_dispenser.yml @@ -18,3 +18,7 @@ - chem.Ale - chem.Wine - chem.Ice + - chem.Beer + - chem.Vodka + - chem.Cognac + - chem.Kahlua diff --git a/Resources/Prototypes/Entities/buildings/chem_dispenser.yml b/Resources/Prototypes/Entities/buildings/chem_dispenser.yml index a72263b277..21ba174a64 100644 --- a/Resources/Prototypes/Entities/buildings/chem_dispenser.yml +++ b/Resources/Prototypes/Entities/buildings/chem_dispenser.yml @@ -37,3 +37,4 @@ - chem.K - chem.Ra - chem.Na + - chem.U diff --git a/Resources/Prototypes/Entities/buildings/soda_dispenser.yml b/Resources/Prototypes/Entities/buildings/soda_dispenser.yml index e52868a841..ce0bcebb75 100644 --- a/Resources/Prototypes/Entities/buildings/soda_dispenser.yml +++ b/Resources/Prototypes/Entities/buildings/soda_dispenser.yml @@ -19,3 +19,4 @@ - chem.Tea - chem.Ice - chem.H2O + - chem.Cream diff --git a/Resources/Prototypes/Entities/items/Consumables/drinks.yml b/Resources/Prototypes/Entities/items/Consumables/drinks.yml index cbae0e107f..6451742780 100644 --- a/Resources/Prototypes/Entities/items/Consumables/drinks.yml +++ b/Resources/Prototypes/Entities/items/Consumables/drinks.yml @@ -6,93 +6,36 @@ id: DrinkBase abstract: true components: + - type: Solution + maxVol: 50 + - type: Pourable + transferAmount: 5 - type: Drink - despawn_empty: true + despawn_empty: false - type: Sound - type: Sprite state: icon - netsync: false - type: Icon state: icon -# Drinks below here +# Transformable container - normal glass - type: entity + name: Drinking glass parent: DrinkBase - id: DrinkAbsintheglass - name: Absinthe glass - description: A anise-flavoured spirit derived from botanicals. + id: DrinkGlass components: - - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite - sprite: Objects/Drinks/absintheglass.rsi + sprite: Objects/Drinks/glass_clear.rsi - type: Icon - sprite: Objects/Drinks/absintheglass.rsi - -- type: entity - parent: DrinkBase - id: DrinkAcidspitGlass - name: Acidspit glass - description: A drink for the daring, can be deadly if incorrectly prepared! - components: - - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - - type: Sprite - sprite: Objects/Drinks/acidspitglass.rsi - - type: Icon - sprite: Objects/Drinks/acidspitglass.rsi - -- type: entity - parent: DrinkBase - id: DrinkAlco-blue - name: Miss Blue Curacao - description: A fruity, exceptionally azure drink. Does not allow the imbiber to use the fifth magic. - components: - - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - spawn_on_finish: TrashAlcoClear - - type: Sprite - sprite: Objects/Drinks/alco-blue.rsi - - type: Icon - sprite: Objects/Drinks/alco-blue.rsi - -#- type: entity -# parent: DrinkBase -# id: DrinkAlco-red -# name: Alco-red -# description: '' -# components: -# - type: Drink# -# contents: -# - water: 1 -# spawn_on_finish: TrashAlcoClear -# - type: Sprite -# sprite: Objects/Drinks/alco-red.rsi -# - type: Icon -# sprite: Objects/Drinks/alco-red.rsi -# -#- type: entity -# parent: DrinkBase -# id: DrinkAlco-white -# name: Alco-white -# description: '' -# components: -# - type: Drink# -# contents: -# - water: 1 -# - type: Sprite -# sprite: Objects/Drinks/alco-white.rsi -# - type: Icon -# sprite: Objects/Drinks/alco-white.rsi + sprite: Objects/Drinks/glass_clear.rsi + - type: Solution + maxVol: 50 + caps: 16 + - type: Drink + despawn_empty: false + - type: Pourable + transferAmount: 5 + - type: TransformableContainer - type: entity parent: DrinkBase @@ -100,91 +43,30 @@ name: Ale description: A dark alchoholic beverage made by malted barley and yeast. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Ale + Quantity: 20 - type: Sprite sprite: Objects/Drinks/aleglass.rsi - type: Icon sprite: Objects/Drinks/aleglass.rsi -- type: entity - parent: DrinkBase - id: DrinkAlliescocktail - name: Allies cocktail - description: A drink made from your allies, not as sweet as when made from your enemies. - components: - - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - - type: Sprite - sprite: Objects/Drinks/alliescocktail.rsi - - type: Icon - sprite: Objects/Drinks/alliescocktail.rsi - -- type: entity - parent: DrinkBase - id: DrinkAloe - name: Aloe - description: So very, very, very good. - components: - - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - - type: Sprite - sprite: Objects/Drinks/aloe.rsi - - type: Icon - sprite: Objects/Drinks/aloe.rsi - -- type: entity - parent: DrinkBase - id: DrinkAmasecglass - name: Amasec glass - description: Official drink of the Gun Club! - components: - - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - - type: Sprite - sprite: Objects/Drinks/amasecglass.rsi - - type: Icon - sprite: Objects/Drinks/amasecglass.rsi - -- type: entity - parent: DrinkBase - id: DrinkAndalusia - name: Andalusia - description: A nice, strangely named drink. - components: - - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - - type: Sprite - sprite: Objects/Drinks/andalusia.rsi - - type: Icon - sprite: Objects/Drinks/andalusia.rsi - - type: entity parent: DrinkBase id: DrinkAntifreeze name: Anti-freeze description: Ultimate refreshment. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Antifreeze + Quantity: 20 + - type: Drink - type: Sprite sprite: Objects/Drinks/antifreeze.rsi - type: Icon @@ -196,80 +78,36 @@ name: Atomic bomb glass description: Nuclear proliferation never tasted so good. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.AtomicBomb + Quantity: 20 + - type: Drink - type: Sprite sprite: Objects/Drinks/atomicbombglass.rsi - type: Icon sprite: Objects/Drinks/atomicbombglass.rsi -- type: entity - parent: DrinkBase - id: DrinkBarefootAndPregnant - name: Barefoot - description: Barefoot and pregnant - components: - - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - - type: Sprite - sprite: Objects/Drinks/b&p.rsi - - type: Icon - sprite: Objects/Drinks/b&p.rsi - - type: entity parent: DrinkBase id: DrinkB52Glass name: B-52 glass description: Coffee, Irish Cream, and cognac. You will get bombed. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.B52 + Quantity: 20 + - type: Drink - type: Sprite sprite: Objects/Drinks/b52glass.rsi - type: Icon sprite: Objects/Drinks/b52glass.rsi -- type: entity - parent: DrinkBase - id: DrinkBahamaMama - name: Bahama mama - description: Tropical cocktail. - components: - - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - - type: Sprite - sprite: Objects/Drinks/bahama_mama.rsi - - type: Icon - sprite: Objects/Drinks/bahama_mama.rsi - -- type: entity - parent: DrinkBase - id: DrinkBananaJuice - name: Banana juice - description: The raw essence of a banana. - components: - - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - - type: Sprite - sprite: Objects/Drinks/banana.rsi - - type: Icon - sprite: Objects/Drinks/banana.rsi - - type: entity parent: DrinkBase id: DrinkBananahonkglass @@ -277,10 +115,6 @@ description: A drink from Clown Heaven. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/bananahonkglass.rsi - type: Icon @@ -307,10 +141,6 @@ description: Deny drinking this and prepare for THE LAW. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/beepskysmashglass.rsi - type: Icon @@ -322,11 +152,12 @@ name: Beer # Beer it is. Coffee. Beer? COFF-EE? BE-ER? C-O... B-E description: An alcoholic beverage made from malted grains, hops, yeast, and water. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Beer + Quantity: 20 - type: Sprite sprite: Objects/Drinks/beer.rsi - type: Icon @@ -338,11 +169,12 @@ name: Beer glass description: An alcoholic beverage made from malted grains, hops, yeast, and water. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Beer + Quantity: 20 - type: Sprite sprite: Objects/Drinks/beerglass.rsi - type: Icon @@ -355,10 +187,6 @@ description: A delicious blend of several different kinds of berries. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/berryjuice.rsi - type: Icon @@ -385,10 +213,6 @@ description: For the lactose-intolerant. Still as classy as a White Russian. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/blackrussianglass.rsi - type: Icon @@ -401,10 +225,6 @@ description: A strange yet pleasurable mixture made of vodka, tomato and lime juice. Tastes like liquid murder components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/bloodymaryglass.rsi - type: Icon @@ -417,10 +237,6 @@ description: Ewww... components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/booger.rsi - type: Icon @@ -433,10 +249,6 @@ description: It's just as effective as Dutch-Courage! components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/bravebullglass.rsi - type: Icon @@ -463,10 +275,6 @@ description: It's not what it sounds like... components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/brownstar.rsi - type: Icon @@ -479,10 +287,6 @@ description: A nice, strong and tasty beverage while you are reading. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/cafe_latte.rsi - type: Icon @@ -495,10 +299,6 @@ description: A handled glass pitcher. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/carafe.rsi state: icon-10 @@ -517,10 +317,6 @@ description: Has a uniquely sweet flavour of concentrated carrots. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/carrotjuice.rsi - type: Icon @@ -533,10 +329,6 @@ description: You take a tiny sip and feel a burning sensation... components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/changelingsting.rsi - type: Icon @@ -549,10 +341,6 @@ description: A heated drink consisting melted chocolate and heated milk. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/chocolateglass.rsi - type: Icon @@ -564,11 +352,12 @@ name: Coffee description: Coffee is a brewed drink prepared from roasted seeds, commonly called coffee beans, of the coffee plant. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Coffee + Quantity: 20 - type: Sprite sprite: Objects/Drinks/coffee.rsi - type: Icon @@ -580,11 +369,12 @@ name: Cognac glass description: A sweet and strongly alchoholic drink, made after numerous distillations and years of maturing. Classy as fornication. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Cognac + Quantity: 20 - type: Sprite sprite: Objects/Drinks/cognacglass.rsi - type: Icon @@ -596,11 +386,12 @@ name: Space cola bottle description: Cola. in space components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Cola + Quantity: 20 - type: Sprite sprite: Objects/Drinks/colabottle.rsi - type: Icon @@ -612,11 +403,12 @@ name: Cream description: Dairy product composed of the higher-fat layer skimmed from the top of milk before homogenization. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Cream + Quantity: 20 - type: Sprite sprite: Objects/Drinks/cream.rsi - type: Icon @@ -628,11 +420,12 @@ name: Cuba libre glass description: Rum, mixed with cola. Viva la revolucion. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.CubaLibre + Quantity: 20 - type: Sprite sprite: Objects/Drinks/cubalibreglass.rsi - type: Icon @@ -645,10 +438,6 @@ description: Exotically blue, fruity drink, distilled from oranges. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/curacaoglass.rsi - type: Icon @@ -661,10 +450,6 @@ description: AHHHH!!!! # AAHHHHHH components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/demonsblood.rsi - type: Icon @@ -677,10 +462,6 @@ description: A metal flask with a leather band and golden badge belonging to the inspector. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/detflask.rsi - type: Icon @@ -693,10 +474,6 @@ description: Creepy time! components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/devilskiss.rsi - type: Icon @@ -709,10 +486,6 @@ description: A gulp a day keeps the MediBot away. That's probably for the best. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/doctorsdelightglass.rsi - type: Icon @@ -725,10 +498,6 @@ description: Only for the experienced. You think you see sand floating in the glass. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/driestmartiniglass.rsi - type: Icon @@ -741,10 +510,6 @@ description: A delicious blend of 42 different flavours components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/dr_gibb.rsi - type: Icon @@ -757,10 +522,6 @@ description: A delicious blend of 42 different flavours components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/dr_gibb_glass.rsi - type: Icon @@ -773,10 +534,6 @@ description: A relatively sweet and fruity 46 proof liquor. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/emeraldglass.rsi - type: Icon @@ -789,10 +546,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/energy_drink.rsi - type: Icon @@ -805,10 +558,6 @@ description: The surprise is, it's green! components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/erikasurprise.rsi - type: Icon @@ -821,10 +570,6 @@ description: A metal flask belonging to the captain components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/flask.rsi - type: Icon @@ -837,10 +582,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/flask_old.rsi - type: Icon @@ -853,10 +594,6 @@ description: Whoah, this stuff looks volatile! components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/gargleblasterglass.rsi - type: Icon @@ -869,10 +606,6 @@ description: Refreshingly lemony, deliciously dry. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/ginfizzglass.rsi - type: Icon @@ -885,10 +618,6 @@ description: An all time classic, mild cocktail. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/gintonicglass.rsi - type: Icon @@ -901,10 +630,6 @@ description: 100 proof cinnamon schnapps, made for alcoholic teen girls on spring break. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/ginvodkaglass.rsi - type: Icon @@ -917,10 +642,6 @@ description: This appears to be beer mixed with milk. Disgusting. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/glass_brown.rsi - type: Icon @@ -933,10 +654,6 @@ description: Either someone's failure at cocktail making or attempt in alchohol production. In any case, do you really want to drink that? components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/glass_brown2.rsi - type: Icon @@ -949,10 +666,6 @@ description: You've really hit rock bottom now... your liver packed its bags and left last night. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/glass_clear.rsi - type: Icon @@ -965,10 +678,6 @@ description: The sweet-sour juice of limes. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/glass_green.rsi - type: Icon @@ -981,10 +690,6 @@ description: Liquid extract of the orange tree fruit, produced by squeezing or reaming oranges. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/glass_orange.rsi - type: Icon @@ -997,10 +702,6 @@ description: Juice made from tomatoes, usually used as a beverage, either plain or in cocktails components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/glass_red.rsi - type: Icon @@ -1012,11 +713,12 @@ name: Milk description: An opaque white liquid produced by the mammary glands of mammals. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Milk + Quantity: 20 - type: Sprite sprite: Objects/Drinks/glass_white.rsi - type: Icon @@ -1030,10 +732,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/glass_yellow.rsi - type: Icon @@ -1046,10 +744,6 @@ description: 100 proof cinnamon schnapps, made for alcoholic teen girls on spring break. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/goldschlagerglass.rsi - type: Icon @@ -1062,10 +756,6 @@ description: The juice is often sold in stores or fermented and made into wine, brandy, or vinegar. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/grapejuice.rsi - type: Icon @@ -1078,10 +768,6 @@ description: Sweetened drink with a grape flavor and a deep purple color. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/grapesoda.rsi - type: Icon @@ -1094,10 +780,6 @@ description: Sweet and tangy, a bar syrup used to add color or flavor to drinks. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/grenadinebottle.rsi - type: Icon @@ -1110,10 +792,6 @@ description: Made in the modern day with proper pomegranate substitute. Who uses real fruit, anyways? components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/grenadineglass.rsi - type: Icon @@ -1126,10 +804,6 @@ description: Watered-down rum, pirate approved! components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/grogglass.rsi - type: Icon @@ -1142,10 +816,6 @@ description: Sweetened drink with a grape flavor and a deep purple color. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/gsodaglass.rsi - type: Icon @@ -1158,10 +828,6 @@ description: The noodles are boiled, the flavors are artificial, just like being back in school. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/hell.rsi - type: Icon @@ -1174,10 +840,6 @@ description: You just don't get it maaaan. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/hippiesdelightglass.rsi - type: Icon @@ -1190,10 +852,6 @@ description: A heated drink consisting melted chocolate and heated milk. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/hot_coco.rsi - type: Icon @@ -1205,11 +863,12 @@ name: Coffee description: Coffee is a brewed drink prepared from roasted seeds, commonly called coffee beans, of the coffee plant. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Coffee + Quantity: 20 - type: Sprite sprite: Objects/Drinks/hot_coffee.rsi - type: Icon @@ -1222,10 +881,6 @@ description: Coffee and ice, refreshing and cool. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/icedcoffeeglass.rsi - type: Icon @@ -1238,10 +893,6 @@ description: The liquor cabinet, brought together in a delicious mix. Intended for middle-aged alcoholic women only. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/icedteaglass.rsi - type: Icon @@ -1254,10 +905,6 @@ description: A beer which is so cold the air around it freezes. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/iced_beerglass.rsi - type: Icon @@ -1269,11 +916,12 @@ name: Ice glass description: Water frozen into a solid state. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 1 + - ReagentId: chem.Ice + Quantity: 20 - type: Sprite sprite: Objects/Drinks/iceglass.rsi - type: Icon @@ -1285,11 +933,12 @@ name: Irish car bomb description: Mmm, tastes like chocolate cake... components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.IrishCarBomb + Quantity: 20 - type: Sprite sprite: Objects/Drinks/irishcarbomb.rsi - type: Icon @@ -1301,11 +950,12 @@ name: Irish coffee glass description: Coffee, and alcohol. More fun than a Mimosa to drink in the morning. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.IrishCoffee + Quantity: 20 - type: Sprite sprite: Objects/Drinks/irishcoffeeglass.rsi - type: Icon @@ -1317,11 +967,12 @@ name: Irish cream glass description: Whiskey-imbued cream, what else would you expect from the Irish. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.IrishCream + Quantity: 20 - type: Sprite sprite: Objects/Drinks/irishcreamglass.rsi - type: Icon @@ -1334,10 +985,6 @@ description: The hipster's cup components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/jar.rsi - type: Icon @@ -1350,10 +997,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/jar_metroid.rsi - type: Icon @@ -1366,10 +1009,6 @@ description: You can't really tell what this is. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/jar_what.rsi - type: Icon @@ -1381,11 +1020,12 @@ name: Kahlua glass description: A widely known, Mexican coffee-flavoured liqueur. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Kahlua + Quantity: 20 - type: Sprite sprite: Objects/Drinks/kahluaglass.rsi - type: Icon @@ -1398,10 +1038,6 @@ description: Long live the guy who everyone had mistaken for a girl. Baka! components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/kiraspecial.rsi - type: Icon @@ -1414,10 +1050,6 @@ description: Drink using lemon juice, water, and a sweetener such as cane sugar or honey. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/lemonade.rsi - type: Icon @@ -1430,10 +1062,6 @@ description: Drink using lemon juice, water, and a sweetener such as cane sugar or honey. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/lemonadeglass.rsi - type: Icon @@ -1446,10 +1074,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/lemonglass.rsi - type: Icon @@ -1462,10 +1086,6 @@ description: Used to make lemonade, soft drinks, and cocktails. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/lemonjuice.rsi - type: Icon @@ -1478,10 +1098,6 @@ description: A tangy substance made of lime and lemon. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/lemonlime.rsi - type: Icon @@ -1494,10 +1110,6 @@ description: The sweet-sour juice of limes. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/limejuice.rsi - type: Icon @@ -1510,10 +1122,6 @@ description: A flask with a Lithium Atom symbol on it. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/lithiumflask.rsi - type: Icon @@ -1526,10 +1134,6 @@ description: The liquor cabinet, brought together in a delicious mix. Intended for middle-aged alcoholic women only. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/longislandicedteaglass.rsi - type: Icon @@ -1542,10 +1146,6 @@ description: The Detective's undercover drink of choice. He never could stomach gin... components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/manhattanglass.rsi - type: Icon @@ -1557,11 +1157,12 @@ name: The manly dorf glass description: Beer and Ale, brought together in a delicious mix. Intended for true men only. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.ManlyDorf + Quantity: 20 - type: Sprite sprite: Objects/Drinks/manlydorfglass.rsi - type: Icon @@ -1574,10 +1175,6 @@ description: On the rocks with salt on the rim. Arriba~! components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/margaritaglass.rsi - type: Icon @@ -1590,10 +1187,6 @@ description: Vodka with Gin. Not quite how 007 enjoyed it, but still delicious. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/martiniglass.rsi - type: Icon @@ -1606,10 +1199,6 @@ description: A Viking's drink, though a cheap one. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/meadglass.rsi - type: Icon @@ -1621,11 +1210,12 @@ name: Milk jug description: An opaque white liquid produced by the mammary glands of mammals. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Milk + Quantity: 20 - type: Sprite sprite: Objects/Drinks/milk.rsi - type: Icon @@ -1638,10 +1228,6 @@ description: Sweet, cold beverage that is usually made from milk components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/milkshake.rsi - type: Icon @@ -1654,10 +1240,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/mojito.rsi - type: Icon @@ -1670,10 +1252,6 @@ description: A strong neurotoxin that puts the subject into a death-like state. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/neurotoxinglass.rsi - type: Icon @@ -1686,10 +1264,6 @@ description: Absolutely nothing. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/nothing.rsi - type: Icon @@ -1702,10 +1276,6 @@ description: Fortified dessert wine made from cabernet sauvignon, saperavi and other grapes. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/ntcahors.rsi - type: Icon @@ -1718,10 +1288,6 @@ description: Cola, cola never changes. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/nuka_colaglass.rsi - type: Icon @@ -1734,10 +1300,6 @@ description: Liquid extract of the orange tree fruit, produced by squeezing or reaming oranges. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/orangejuice.rsi - type: Icon @@ -1750,10 +1312,6 @@ description: Tequila with silver in it, a favorite of alcoholic women in the club scene. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/patronglass.rsi - type: Icon @@ -1766,10 +1324,6 @@ description: A tasty juice blended from various kinds of very deadly and toxic berries. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/poisonberryjuice.rsi - type: Icon @@ -1782,10 +1336,6 @@ description: A scientist's drink of choice, for pondering ways to blow up the station. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/proj_manhattanglass.rsi - type: Icon @@ -1798,10 +1348,6 @@ description: Is this even wine? Toxic! Hallucinogenic! Probably consumed in boatloads by your superiors! components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/pwineglass.rsi - type: Icon @@ -1814,10 +1360,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/rag.rsi - type: Icon @@ -1830,10 +1372,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/rag_lit.rsi - type: Icon @@ -1846,10 +1384,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/rag_small.rsi - type: Icon @@ -1862,10 +1396,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/rag_small_lit.rsi - type: Icon @@ -1878,10 +1408,6 @@ description: Just add 10ml water, self heats! A taste that reminds you of your school years. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/ramen.rsi - type: Icon @@ -1894,10 +1420,6 @@ description: The true Viking's drink! Even though it has a strange red color. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/red_meadglass.rsi - type: Icon @@ -1910,10 +1432,6 @@ description: The secret of the sanctuary of the Libarian... components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/rewriter.rsi - type: Icon @@ -1926,10 +1444,6 @@ description: Distilled alcoholic drink made from saltwater. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/rumglass.rsi - type: Icon @@ -1942,10 +1456,6 @@ description: A spicy Vodka! Might be a little hot for the little guys! components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/sbitenglass.rsi - type: Icon @@ -1958,10 +1468,6 @@ description: Vodka, mixed with plain ol' orange juice. The result is surprisingly delicious. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/screwdriverglass.rsi - type: Icon @@ -1974,10 +1480,6 @@ description: 'Comprised of: White soda, blue curacao, melon liquor.' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/sdreamglass.rsi - type: Icon @@ -1990,10 +1492,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/shake-blue.rsi - type: Icon @@ -2007,10 +1505,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/shake-empty.rsi - type: Icon @@ -2023,10 +1517,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/shake-meat.rsi - type: Icon @@ -2039,10 +1529,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/shake-robo.rsi - type: Icon @@ -2055,10 +1541,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/shake-white.rsi - type: Icon @@ -2072,10 +1554,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/shaker.rsi - type: Icon @@ -2088,10 +1566,6 @@ description: A shiny metal flask. It appears to have a Greek symbol inscribed on it. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/shinyflask.rsi - type: Icon @@ -2104,10 +1578,6 @@ description: '' components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/shotglass.rsi - type: Icon @@ -2120,10 +1590,6 @@ description: A drink from Mime Heaven. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/silencerglass.rsi - type: Icon @@ -2136,10 +1602,6 @@ description: A blue-space beverage! components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/singulo.rsi - type: Icon @@ -2152,10 +1614,6 @@ description: A cold refreshment components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/snowwhite.rsi - type: Icon @@ -2168,10 +1626,6 @@ description: Water containing dissolved carbon dioxide gas. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/sodawater.rsi - type: Icon @@ -2184,10 +1638,6 @@ description: An opaque white liquid made from soybeans. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/soymilk.rsi - type: Icon @@ -2200,10 +1650,6 @@ description: A coffee drink made with espresso and steamed soy milk. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/soy_latte.rsi - type: Icon @@ -2216,10 +1662,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_bottle.rsi - type: Icon @@ -2232,10 +1674,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_glass.rsi - type: Icon @@ -2248,10 +1686,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_bottle.rsi - type: Icon @@ -2264,10 +1698,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_glass.rsi - type: Icon @@ -2279,11 +1709,12 @@ name: Syndicate bomb description: Tastes like terrorism! components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.SyndicateBomb + Quantity: 20 - type: Sprite sprite: Objects/Drinks/syndicatebomb.rsi - type: Icon @@ -2295,11 +1726,12 @@ name: Teacup description: A plain white porcelain teacup. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Tea + Quantity: 20 - type: Sprite sprite: Objects/Drinks/teacup.rsi state: icon-1 @@ -2317,11 +1749,12 @@ name: Tea glass description: Tasty black tea. Contains caffeine. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Tea + Quantity: 20 - type: Sprite sprite: Objects/Drinks/teaglass.rsi - type: Icon @@ -2333,11 +1766,12 @@ name: Teapot # Short and stout description: An elegant teapot. It simply oozes class. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Tea + Quantity: 20 - type: Sprite sprite: Objects/Drinks/teapot.rsi - type: Icon @@ -2350,10 +1784,6 @@ description: A strong and mildly flavoured, mexican produced spirit. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/tequillaglass.rsi - type: Icon @@ -2366,10 +1796,6 @@ description: Tequila and orange juice. Much like a Screwdriver, only Mexican~ components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/tequillasunriseglass.rsi - type: Icon @@ -2382,10 +1808,6 @@ description: A potent mixture of caffeine and alcohol. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/thirteen_loko_glass.rsi - type: Icon @@ -2398,10 +1820,6 @@ description: Made for a woman, strong enough for a man. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/threemileislandglass.rsi - type: Icon @@ -2414,10 +1832,6 @@ description: Juice made from tomatoes, usually used as a beverage, either plain or in cocktails components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/tomatojuice.rsi - type: Icon @@ -2430,10 +1844,6 @@ description: A carbonated soft drink in which quinine is dissolved. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/tonic.rsi - type: Icon @@ -2446,10 +1856,6 @@ description: This thing is ON FIRE! CALL THE DAMN SHUTTLE!" components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/toxinsspecialglass.rsi - type: Icon @@ -2462,10 +1868,6 @@ description: Keeping your drinks at the perfect temperature since 1892. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/vacuumflask.rsi - type: Icon @@ -2478,10 +1880,6 @@ description: Aromatized, fortified white wine flavored with various botanicals. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/vermouthglass.rsi - type: Icon @@ -2494,10 +1892,6 @@ description: For when a gin and tonic isn't russian enough. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/vodkatonicglass.rsi - type: Icon @@ -2510,10 +1904,6 @@ description: Stay hydrated components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/water.rsi - type: Icon @@ -2526,10 +1916,6 @@ description: Simple clean water of unknown origin. You think that maybe you dont want to know it. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/waterbottle.rsi - type: Icon @@ -2542,10 +1928,6 @@ description: Delicious juice made from watermelon. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/watermelon.rsi - type: Icon @@ -2558,10 +1940,6 @@ description: A paper water cup. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/water_cup.rsi state: icon-1 @@ -2579,11 +1957,12 @@ name: Whiskey cola glass description: Whiskey, mixed with cola. Surprisingly refreshing. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.WhiskeyCola + Quantity: 20 - type: Sprite sprite: Objects/Drinks/whiskeycolaglass.rsi - type: Icon @@ -2595,11 +1974,12 @@ name: Special blend whiskey glass description: Just when you thought regular station whiskey was good... This silky, amber goodness has to come along and ruin everything. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Whiskey + Quantity: 20 - type: Sprite sprite: Objects/Drinks/whiskeyglass.rsi - type: Icon @@ -2612,10 +1992,6 @@ description: For the more refined griffon. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/whiskeysodaglass.rsi - type: Icon @@ -2628,10 +2004,6 @@ description: For the more refined griffon. components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/whiskeysodaglass2.rsi - type: Icon @@ -2644,10 +2016,6 @@ description: That's just, like, your opinion, man... components: - type: Drink - contents: - reagents: - - ReagentId: chem.H2O - Quantity: 4 - type: Sprite sprite: Objects/Drinks/whiterussianglass.rsi - type: Icon @@ -2659,11 +2027,12 @@ name: Wine glass description: An premium alchoholic beverage made from distilled grape juice. components: - - type: Drink + - type: Solution + maxVol: 20 contents: reagents: - - ReagentId: chem.H2O - Quantity: 4 + - ReagentId: chem.Wine + Quantity: 20 - type: Sprite sprite: Objects/Drinks/wineglass.rsi - type: Icon diff --git a/Resources/Prototypes/Entities/items/Consumables/drinks_bottles.yml b/Resources/Prototypes/Entities/items/Consumables/drinks_bottles.yml index bfc997e01b..c719d7e7f5 100644 --- a/Resources/Prototypes/Entities/items/Consumables/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/items/Consumables/drinks_bottles.yml @@ -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 diff --git a/Resources/Prototypes/Entities/items/Consumables/drinks_cans.yml b/Resources/Prototypes/Entities/items/Consumables/drinks_cans.yml index 4d776b2446..ba497f7751 100644 --- a/Resources/Prototypes/Entities/items/Consumables/drinks_cans.yml +++ b/Resources/Prototypes/Entities/items/Consumables/drinks_cans.yml @@ -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 diff --git a/Resources/Prototypes/Entities/items/Consumables/drinks_cups.yml b/Resources/Prototypes/Entities/items/Consumables/drinks_cups.yml index 904dd9dc1d..0b29b551fa 100644 --- a/Resources/Prototypes/Entities/items/Consumables/drinks_cups.yml +++ b/Resources/Prototypes/Entities/items/Consumables/drinks_cups.yml @@ -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 diff --git a/Resources/Prototypes/Entities/items/Consumables/trash_drinks.yml b/Resources/Prototypes/Entities/items/Consumables/trash_drinks.yml index e180de55d2..cdba3d3ad6 100644 --- a/Resources/Prototypes/Entities/items/Consumables/trash_drinks.yml +++ b/Resources/Prototypes/Entities/items/Consumables/trash_drinks.yml @@ -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 diff --git a/Resources/Prototypes/Entities/mobs/human.yml b/Resources/Prototypes/Entities/mobs/human.yml index 50f789e8e0..1b49e65c8a 100644 --- a/Resources/Prototypes/Entities/mobs/human.yml +++ b/Resources/Prototypes/Entities/mobs/human.yml @@ -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 diff --git a/Resources/Prototypes/Reactions/drinks.yml b/Resources/Prototypes/Reactions/drinks.yml new file mode 100644 index 0000000000..d3dda3f121 --- /dev/null +++ b/Resources/Prototypes/Reactions/drinks.yml @@ -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 \ No newline at end of file diff --git a/Resources/Prototypes/Reagents/drinks.yml b/Resources/Prototypes/Reagents/drinks.yml index eb804d8d8a..7c79af86b5 100644 --- a/Resources/Prototypes/Reagents/drinks.yml +++ b/Resources/Prototypes/Reagents/drinks.yml @@ -2,16 +2,103 @@ id: chem.Whiskey name: Whiskey desc: An alcoholic beverage made from fermented grain mash + 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. + spritePath: aleglass.rsi - type: reagent id: chem.Wine name: Wine desc: An alcoholic drink made from fermented grapes + spritePath: wineglass.rsi + +- type: reagent + id: chem.Beer + name: Beer + desc: A cold pint of pale lager. + spritePath: beerglass.rsi + +- type: reagent + id: chem.Vodka + name: Vodka + desc: The glass contain wodka. Xynta. + +- type: reagent + id: chem.Kahlua + name: Kahlua + desc: A widely known, Mexican coffee-flavoured liqueur. In production since 1936! + 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. + 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. + spritePath: manlydorfglass.rsi + +- type: reagent + id: chem.CubaLibre + name: Cuba Libre + desc: A classic mix of rum and cola. + spritePath: cubalibreglass.rsi + +- type: reagent + id: chem.IrishCarBomb + name: Irish Car Bomb + desc: A troubling mixture of irish cream and ale. + spritePath: irishcarbomb.rsi + +- type: reagent + id: chem.IrishCoffee + name: Irish Coffee + desc: Coffee served with irish cream. Regular cream just isn't the same! + spritePath: irishcoffeeglass.rsi + +- type: reagent + id: chem.IrishCream + name: Irish Cream + desc: Whiskey-imbued cream. What else could you expect from the Irish. + spritePath: irishcreamglass.rsi + +- type: reagent + id: chem.B52 + name: B-52 + desc: Coffee, irish cream, and cognac. You will get bombed. + spritePath: b52glass.rsi + +- type: reagent + id: chem.AtomicBomb + name: Atomic Bomb + desc: Nuclear proliferation never tasted so good. + spritePath: atomicbombglass.rsi + +- type: reagent + id: chem.WhiskeyCola + name: Whiskey Cola + desc: An innocent-looking mixture of cola and whiskey. Delicious. + spritePath: whiskeycolaglass.rsi + +- type: reagent + id: chem.SyndicateBomb + name: Syndicate Bomb + desc: Somebody set us up the bomb! + spritePath: syndicatebomb.rsi + +- type: reagent + id: chem.Antifreeze + name: Antifreeze + desc: The ultimate refreshment. + spritePath: antifreeze.rsi + - type: reagent id: chem.Cola @@ -36,3 +123,19 @@ 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? + metabolism: + - !type:DefaultDrink + rate: 1 + +- type: reagent + id: chem.Milk + name: Milk + desc: An opaque white liquid produced by the mammary glands of mammals. + metabolism: + - !type:DefaultDrink + rate: 1 \ No newline at end of file diff --git a/Resources/Prototypes/Reagents/elements.yml b/Resources/Prototypes/Reagents/elements.yml index 938a27a4c7..0465c71cc1 100644 --- a/Resources/Prototypes/Reagents/elements.yml +++ b/Resources/Prototypes/Reagents/elements.yml @@ -95,3 +95,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" \ No newline at end of file From 1b8215ee4981a59a9dd93aaa8b90f8c71303374f Mon Sep 17 00:00:00 2001 From: Injazz Date: Wed, 8 Apr 2020 16:06:24 +0500 Subject: [PATCH 18/54] commented out description setter --- .../Components/Chemistry/TransformableContainerComponent.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs b/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs index f08dba169b..532f404629 100644 --- a/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs @@ -42,7 +42,7 @@ namespace Content.Server.GameObjects.Components.Chemistry _currentReagent = null; _sprite.LayerSetSprite(0, _initialSprite); Owner.Name = _initialName; - Owner.Description = _initialDescription; + //Owner.Description = _initialDescription; } void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) @@ -72,7 +72,7 @@ namespace Content.Server.GameObjects.Components.Chemistry var spriteSpec = new SpriteSpecifier.Rsi(new ResourcePath("Objects/Drinks/" + proto.SpriteReplacementPath),"icon"); _sprite.LayerSetSprite(0, spriteSpec); Owner.Name = proto.Name; - Owner.Description = proto.Description; + //Owner.Description = proto.Description; _currentReagent = proto; } } From 4174891c8701c837d66059a45b1be7a1ee3f09a7 Mon Sep 17 00:00:00 2001 From: Injazz Date: Wed, 8 Apr 2020 17:12:00 +0500 Subject: [PATCH 19/54] some fixes removes unused interface from DrinkComponent adds capability to fit inside dispensers for TransformableContainer removes stomach component from character setup dummy --- .../TransformableContainerComponent.cs | 9 +++- .../Components/Nutrition/DrinkComponent.cs | 43 +------------------ Resources/Prototypes/Entities/mobs/human.yml | 2 - 3 files changed, 8 insertions(+), 46 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs b/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs index 532f404629..27e2d338bc 100644 --- a/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs @@ -37,6 +37,12 @@ namespace Content.Server.GameObjects.Components.Chemistry _initialDescription = Owner.Description; } + protected override void Startup() + { + base.Startup(); + Owner.GetComponent().Capabilities |= SolutionCaps.FitsInDispenser;; + } + public void CancelTransformation() { _currentReagent = null; @@ -48,7 +54,6 @@ namespace Content.Server.GameObjects.Components.Chemistry void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) { var solution = eventArgs.Owner.GetComponent(); - //Transform container into initial state when emptied if (_currentReagent != null && solution.ReagentList.Count == 0) { @@ -71,7 +76,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { var spriteSpec = new SpriteSpecifier.Rsi(new ResourcePath("Objects/Drinks/" + proto.SpriteReplacementPath),"icon"); _sprite.LayerSetSprite(0, spriteSpec); - Owner.Name = proto.Name; + Owner.Name = proto.Name + " glass"; //Owner.Description = proto.Description; _currentReagent = proto; } diff --git a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs index 51be393f2c..14356f8cea 100644 --- a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs @@ -16,7 +16,7 @@ using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Nutrition { [RegisterComponent] - public class DrinkComponent : Component, IAfterAttack, IUse, ISolutionChange + public class DrinkComponent : Component, IAfterAttack, IUse { #pragma warning disable 649 [Dependency] private readonly ILocalizationManager _localizationManager; @@ -132,47 +132,6 @@ namespace Content.Server.GameObjects.Components.Nutrition } _drinking = false; } - - //Finish(user); } - - /// - /// 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. - /// - /// The entity that is using the drink - /* - public void Finish(IEntity user) - { - // Drink containers are mostly transient. - // are you sure about that - if (_drinking || !_despawnOnFinish || UsesLeft() > 0) - return; - - var gridPos = Owner.Transform.GridPosition; - 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; - } - }*/ - - void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) { } //Finish(null); } } diff --git a/Resources/Prototypes/Entities/mobs/human.yml b/Resources/Prototypes/Entities/mobs/human.yml index 1b49e65c8a..fbe6939007 100644 --- a/Resources/Prototypes/Entities/mobs/human.yml +++ b/Resources/Prototypes/Entities/mobs/human.yml @@ -150,8 +150,6 @@ hands: - left - right - # Organs - - type: Stomach - type: Inventory - type: Sprite From e7d121b6b0fea3ea551fc64c2d382f0bff066d22 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 16:08:16 +0200 Subject: [PATCH 20/54] Update InjectorComponent.cs --- .../GameObjects/Components/Chemistry/InjectorComponent.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs index 4d46ddf5f0..359dbeb282 100644 --- a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -30,13 +30,9 @@ namespace Content.Client.GameObjects.Components.Chemistry //Handle net updates public override void HandleComponentState(ComponentState curState, ComponentState nextState) { - if(curState == null) + var cast = (InjectorComponentState) curState; + if (cast != null) { - return; - } - if(curState.GetType() == typeof(InjectorComponentState)) - { - var cast = (InjectorComponentState) curState; CurrentVolume = cast.CurrentVolume; TotalVolume = cast.TotalVolume; CurrentMode = cast.CurrentMode; From a484b6fd52b9c40391b1ad17396722e0fc902144 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 19:07:33 +0200 Subject: [PATCH 21/54] Implementation of ISelfSerialize --- .../Components/Chemistry/SolutionComponent.cs | 1 + Content.Shared/Chemistry/ReagentUnit.cs | 40 ++++++++++++++----- .../Shared/Chemistry/ReagentUnit_Tests.cs | 9 +++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 0328dcae67..4cd0a7d1ed 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -199,6 +199,7 @@ 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) { diff --git a/Content.Shared/Chemistry/ReagentUnit.cs b/Content.Shared/Chemistry/ReagentUnit.cs index a9350c5b3c..184f7aa5d7 100644 --- a/Content.Shared/Chemistry/ReagentUnit.cs +++ b/Content.Shared/Chemistry/ReagentUnit.cs @@ -1,10 +1,12 @@ -using System; +using Robust.Shared.Interfaces.Serialization; +using Robust.Shared.Serialization; +using System; using System.Linq; namespace Content.Shared.Chemistry { [Serializable] - public struct ReagentUnit + public struct ReagentUnit : ISelfSerialize { private int _value; private static readonly int Shift = 2; @@ -16,11 +18,6 @@ namespace Content.Shared.Chemistry return _value / (decimal)Math.Pow(10, Shift); } - private decimal ShiftUp() - { - return _value * (decimal)Math.Pow(10, Shift); - } - private ReagentUnit(int value) { _value = value; @@ -38,7 +35,12 @@ namespace Content.Shared.Chemistry public static ReagentUnit New(float value) { - return new ReagentUnit((int) Math.Round(value * (float) Math.Pow(10, Shift))); + return new ReagentUnit(FromFloat(value)); + } + + private static int FromFloat(float value) + { + return (int) Math.Round(value * (float) Math.Pow(10, Shift)); } public static ReagentUnit New(double value) @@ -46,6 +48,16 @@ namespace Content.Shared.Chemistry return new ReagentUnit((int) Math.Round(value * Math.Pow(10, Shift))); } + public static ReagentUnit New(string value) + { + return New(FloatFromString(value)); + } + + private static float FloatFromString(string value) + { + return float.Parse(value); + } + public static ReagentUnit operator +(ReagentUnit a) => a; public static ReagentUnit operator -(ReagentUnit a) => new ReagentUnit(-a._value); @@ -135,7 +147,7 @@ namespace Content.Shared.Chemistry public decimal Decimal() { - return (decimal) ShiftDown(); + return ShiftDown(); } public int Int() @@ -158,5 +170,15 @@ namespace Content.Shared.Chemistry { return HashCode.Combine(_value); } + + public void Deserialize(string value) + { + _value = FromFloat(FloatFromString(value)); + } + + public string Serialize() + { + return ToString(); + } } } diff --git a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs index f2f11b4fdb..1cae6e1db2 100644 --- a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs +++ b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs @@ -45,6 +45,15 @@ namespace Content.Tests.Shared.Chemistry Assert.AreEqual(expected, $"{result}"); } + [Test] + [TestCase("1.005", "1")] + [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")] From 919469408567c41fb26a7d2895578876e0130427 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 19:43:16 +0200 Subject: [PATCH 22/54] Fix initialization change --- .../GameObjects/Components/Chemistry/SolutionComponent.cs | 3 +-- .../GameObjects/Components/Chemistry/SolutionComponent.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 4cd0a7d1ed..aaf78b9723 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -36,9 +36,8 @@ namespace Content.Server.GameObjects.Components.Chemistry Init(); } - public override void Init() + public void Init() { - base.Init(); _reactions = _prototypeManager.EnumeratePrototypes(); _audioSystem = _entitySystemManager.GetEntitySystem(); } diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs index 615c1d85b0..1b65016975 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -17,7 +17,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry #pragma warning restore 649 [ViewVariables] - protected Solution ContainedSolution; + protected Solution ContainedSolution = new Solution(); private ReagentUnit _maxVolume; private SolutionCaps _capabilities; From 66c6a9e3a68996374231a5283e50f0ef88bcd3dc Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 22:39:46 +0200 Subject: [PATCH 23/54] ah yes a much need whitespace --- Content.Shared/EntryPoint.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Shared/EntryPoint.cs b/Content.Shared/EntryPoint.cs index a9df20ff2a..5401603602 100644 --- a/Content.Shared/EntryPoint.cs +++ b/Content.Shared/EntryPoint.cs @@ -24,6 +24,7 @@ { base.PostInit(); + _initTileDefinitions(); } From 282ee61e99c5dbf4a496a1b911f8b20feadf0154 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 22:39:59 +0200 Subject: [PATCH 24/54] not so needed afterall --- Content.Shared/EntryPoint.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Content.Shared/EntryPoint.cs b/Content.Shared/EntryPoint.cs index 5401603602..a9df20ff2a 100644 --- a/Content.Shared/EntryPoint.cs +++ b/Content.Shared/EntryPoint.cs @@ -24,7 +24,6 @@ { base.PostInit(); - _initTileDefinitions(); } From 92e25524401f0d6c159698e4ce23517c875a6e81 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 22:40:29 +0200 Subject: [PATCH 25/54] ree travis --- Content.Shared/EntryPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/EntryPoint.cs b/Content.Shared/EntryPoint.cs index a9df20ff2a..0ada486216 100644 --- a/Content.Shared/EntryPoint.cs +++ b/Content.Shared/EntryPoint.cs @@ -17,7 +17,7 @@ public override void Init() { - IoCManager.InjectDependencies(this); + IoCManager.InjectDepe ndencies(this); } public override void PostInit() From a507a06916e803f770fd6971e6c22022977c4d54 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 22:40:43 +0200 Subject: [PATCH 26/54] fix compile error --- Content.Shared/EntryPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/EntryPoint.cs b/Content.Shared/EntryPoint.cs index 0ada486216..a9df20ff2a 100644 --- a/Content.Shared/EntryPoint.cs +++ b/Content.Shared/EntryPoint.cs @@ -17,7 +17,7 @@ public override void Init() { - IoCManager.InjectDepe ndencies(this); + IoCManager.InjectDependencies(this); } public override void PostInit() From 64eafde0c3c8cbc3d33ba233dc14a8e2041823a2 Mon Sep 17 00:00:00 2001 From: zumorica Date: Thu, 9 Apr 2020 00:28:56 +0200 Subject: [PATCH 27/54] Adds working communications console that ends the round --- .../Command/CommunicationsConsoleMenu.cs | 78 +++++++++++++++++++ ...CommunicationsConsoleBoundUserInterface.cs | 66 ++++++++++++++++ .../Research/LatheBoundUserInterface.cs | 36 ++++----- .../Command/CommunicationsConsoleComponent.cs | 72 +++++++++++++++++ .../EntitySystems/RoundEndSystem.cs | 58 ++++++++++++++ Content.Server/GameTicking/GameTicker.cs | 15 ++++ .../SharedCommunicationsConsoleComponent.cs | 41 ++++++++++ .../Entities/buildings/computers.yml | 5 ++ 8 files changed, 353 insertions(+), 18 deletions(-) create mode 100644 Content.Client/Command/CommunicationsConsoleMenu.cs create mode 100644 Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs create mode 100644 Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs create mode 100644 Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs create mode 100644 Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs diff --git a/Content.Client/Command/CommunicationsConsoleMenu.cs b/Content.Client/Command/CommunicationsConsoleMenu.cs new file mode 100644 index 0000000000..2213c84dbb --- /dev/null +++ b/Content.Client/Command/CommunicationsConsoleMenu.cs @@ -0,0 +1,78 @@ +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.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 RichTextLabel _countdownLabel; + + public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner) + { + IoCManager.InjectDependencies(this); + + Title = _localizationManager.GetString("Communications Console"); + Owner = owner; + + _countdownLabel = new RichTextLabel(){CustomMinimumSize = new Vector2(0, 200)}; + var emergencyShuttleButton = new Button() {Text = _localizationManager.GetString("Call emergency shuttle")}; + + emergencyShuttleButton.OnPressed += (e) => Owner.CallShuttle(); + + 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); + } + + private void UpdateCountdown() + { + if (!Owner.CountdownStarted) + { + _countdownLabel.SetMessage(""); + return; + } + + _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(); + } + } +} diff --git a/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs b/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs new file mode 100644 index 0000000000..e123e5ac4d --- /dev/null +++ b/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs @@ -0,0 +1,66 @@ +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.ViewVariables; + +namespace Content.Client.GameObjects.Components.Command +{ + public class CommunicationsConsoleBoundUserInterface : BoundUserInterface + { + [ViewVariables] + private CommunicationsConsoleMenu _menu; + + public bool CountdownStarted { get; private set; } + + public int Countdown => _expectedCountdownTime == null + ? 0 : Math.Max((int)(_expectedCountdownTime.Value.Subtract(DateTime.Now)).TotalSeconds, 0); + private DateTime? _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 CallShuttle() + { + SendMessage(new CommunicationsConsoleCallEmergencyShuttleMessage()); + } + + 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; + + + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) return; + _menu?.Dispose(); + } + } +} diff --git a/Content.Client/GameObjects/Components/Research/LatheBoundUserInterface.cs b/Content.Client/GameObjects/Components/Research/LatheBoundUserInterface.cs index 3273cb79f3..9e318f04a6 100644 --- a/Content.Client/GameObjects/Components/Research/LatheBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/Research/LatheBoundUserInterface.cs @@ -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(); } } } diff --git a/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs b/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs new file mode 100644 index 0000000000..4ce6a83883 --- /dev/null +++ b/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs @@ -0,0 +1,72 @@ +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(); + + public override void Initialize() + { + base.Initialize(); + + _userInterface = Owner.GetComponent().GetBoundUserInterface(CommunicationsConsoleUiKey.Key); + _userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage; + _powerDevice = Owner.GetComponent(); + + 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; + } + } + + 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); + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs new file mode 100644 index 0000000000..90bb676f16 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading; +using Content.Server.Interfaces.GameTicking; +using Robust.Shared.GameObjects.Systems; +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; +#pragma warning restore 649 + + private CancellationTokenSource _roundEndCancellationTokenSource = new CancellationTokenSource(); + public bool IsRoundEndCountdownStarted { get; private set; } + public int RoundEndCountdownTime { get; set; } = 5000; + public DateTime? 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 = DateTime.Now.AddMilliseconds(RoundEndCountdownTime); + Timer.Spawn(RoundEndCountdownTime, EndRound, _roundEndCancellationTokenSource.Token); + OnRoundEndCountdownStarted?.Invoke(); + } + + public void CancelRoundEndCountdown() + { + _roundEndCancellationTokenSource.Cancel(); + _roundEndCancellationTokenSource = new CancellationTokenSource(); + + ExpectedCountdownEnd = null; + + OnRoundEndCountdownCancelled?.Invoke(); + } + + private void EndRound() + { + OnRoundEndCountdownFinished?.Invoke(); + _gameTicker.EndRound(); + } + } +} diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index e8c654b2c3..39905c6737 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -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; @@ -123,6 +124,8 @@ namespace Content.Server.GameTicking { Logger.InfoS("ticker", "Restarting round!"); + SendServerMessage("Restarting round..."); + RunLevel = GameRunLevel.PreRoundLobby; _resettingCleanup(); _preRoundSetup(); @@ -147,6 +150,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(); @@ -191,6 +196,14 @@ namespace Content.Server.GameTicking _sendStatusToAll(); } + private void SendServerMessage(string message) + { + var msg = _netManager.CreateNetMessage(); + msg.Channel = ChatChannel.Server; + msg.Message = message; + IoCManager.Resolve().ServerSendToAll(msg); + } + private HumanoidCharacterProfile GetPlayerProfile(IPlayerSession p) => (HumanoidCharacterProfile) _prefsManager.GetPreferences(p.SessionId.Username).SelectedCharacter; @@ -200,6 +213,8 @@ namespace Content.Server.GameTicking Logger.InfoS("ticker", "Ending round!"); RunLevel = GameRunLevel.PostRound; + + SendServerMessage("The round has ended!"); } public void Respawn(IPlayerSession targetPlayer) diff --git a/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs b/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs new file mode 100644 index 0000000000..b5ad45c115 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs @@ -0,0 +1,41 @@ +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 DateTime? ExpectedCountdownEnd; + public readonly bool CountdownStarted; + + public CommunicationsConsoleInterfaceState(DateTime? expectedCountdownEnd = null) + { + ExpectedCountdownEnd = expectedCountdownEnd; + CountdownStarted = expectedCountdownEnd != null; + + } + } + + [Serializable, NetSerializable] + public class CommunicationsConsoleCallEmergencyShuttleMessage : BoundUserInterfaceMessage + { + public CommunicationsConsoleCallEmergencyShuttleMessage() + { + } + } + + [Serializable, NetSerializable] + public enum CommunicationsConsoleUiKey + { + Key + } +} diff --git a/Resources/Prototypes/Entities/buildings/computers.yml b/Resources/Prototypes/Entities/buildings/computers.yml index fff70844a8..bc8dbf9fe4 100644 --- a/Resources/Prototypes/Entities/buildings/computers.yml +++ b/Resources/Prototypes/Entities/buildings/computers.yml @@ -176,3 +176,8 @@ - type: ComputerVisualizer2D key: generic_key screen: comm + - type: CommunicationsConsole + - type: UserInterface + interfaces: + - key: enum.CommunicationsConsoleUiKey.Key + type: CommunicationsConsoleBoundUserInterface From 83e96881339b93856a6d436233bb2f166ea9a3ac Mon Sep 17 00:00:00 2001 From: zumorica Date: Thu, 9 Apr 2020 01:43:28 +0200 Subject: [PATCH 28/54] You can now cancel the timer (recall) --- Content.Client/Command/CommunicationsConsoleMenu.cs | 11 ++++++----- .../CommunicationsConsoleBoundUserInterface.cs | 13 +++++++++++++ .../Command/CommunicationsConsoleComponent.cs | 4 ++++ .../GameObjects/EntitySystems/RoundEndSystem.cs | 5 +++++ .../Command/SharedCommunicationsConsoleComponent.cs | 8 ++++++++ 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/Content.Client/Command/CommunicationsConsoleMenu.cs b/Content.Client/Command/CommunicationsConsoleMenu.cs index 2213c84dbb..426ae84977 100644 --- a/Content.Client/Command/CommunicationsConsoleMenu.cs +++ b/Content.Client/Command/CommunicationsConsoleMenu.cs @@ -20,7 +20,7 @@ namespace Content.Client.Command private CommunicationsConsoleBoundUserInterface Owner { get; set; } private readonly CancellationTokenSource _timerCancelTokenSource = new CancellationTokenSource(); - + private readonly Button _emergencyShuttleButton; private readonly RichTextLabel _countdownLabel; public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner) @@ -31,14 +31,13 @@ namespace Content.Client.Command Owner = owner; _countdownLabel = new RichTextLabel(){CustomMinimumSize = new Vector2(0, 200)}; - var emergencyShuttleButton = new Button() {Text = _localizationManager.GetString("Call emergency shuttle")}; - - emergencyShuttleButton.OnPressed += (e) => Owner.CallShuttle(); + _emergencyShuttleButton = new Button(); + _emergencyShuttleButton.OnPressed += (e) => Owner.EmergencyShuttleButtonPressed(); var vbox = new VBoxContainer() {SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsVertical = SizeFlags.FillExpand}; vbox.AddChild(_countdownLabel); - vbox.AddChild(emergencyShuttleButton); + 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}); @@ -56,9 +55,11 @@ namespace Content.Client.Command 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"); } diff --git a/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs b/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs index e123e5ac4d..5401ac8537 100644 --- a/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs @@ -33,11 +33,24 @@ namespace Content.Client.GameObjects.Components.Command _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) diff --git a/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs b/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs index 4ce6a83883..a3ce594242 100644 --- a/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs +++ b/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs @@ -49,6 +49,10 @@ namespace Content.Server.GameObjects.Components.Command case CommunicationsConsoleCallEmergencyShuttleMessage _: RoundEndSystem.RequestRoundEnd(); break; + + case CommunicationsConsoleRecallEmergencyShuttleMessage _: + RoundEndSystem.CancelRoundEndCountdown(); + break; } } diff --git a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs index 90bb676f16..e1b75529c6 100644 --- a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs @@ -41,6 +41,11 @@ namespace Content.Server.GameObjects.EntitySystems public void CancelRoundEndCountdown() { + if (!IsRoundEndCountdownStarted) + return; + + IsRoundEndCountdownStarted = false; + _roundEndCancellationTokenSource.Cancel(); _roundEndCancellationTokenSource = new CancellationTokenSource(); diff --git a/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs b/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs index b5ad45c115..00d73272b1 100644 --- a/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs +++ b/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs @@ -33,6 +33,14 @@ namespace Content.Shared.GameObjects.Components.Command } } + [Serializable, NetSerializable] + public class CommunicationsConsoleRecallEmergencyShuttleMessage : BoundUserInterfaceMessage + { + public CommunicationsConsoleRecallEmergencyShuttleMessage() + { + } + } + [Serializable, NetSerializable] public enum CommunicationsConsoleUiKey { From 612790840c27307c262117ebbb8c854f7ec24fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= <6766154+Zumorica@users.noreply.github.com> Date: Thu, 9 Apr 2020 02:59:20 +0200 Subject: [PATCH 29/54] Update Content.Client/GameObjects/Components/Observer/GhostComponent.cs Co-Authored-By: Pieter-Jan Briers --- .../GameObjects/Components/Observer/GhostComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Client/GameObjects/Components/Observer/GhostComponent.cs b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs index c71310845a..244dd7a5d6 100644 --- a/Content.Client/GameObjects/Components/Observer/GhostComponent.cs +++ b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs @@ -69,7 +69,7 @@ namespace Content.Client.GameObjects.Components.Observer } else { - _gui.Parent?.RemoveChild(_gui); + _gui.Orphan(); } _gameHud.HandsContainer.AddChild(_gui); From c0bdfdf123294970f02b3e38746cca51fa10a2d0 Mon Sep 17 00:00:00 2001 From: zumorica Date: Thu, 9 Apr 2020 03:01:56 +0200 Subject: [PATCH 30/54] Remove CanReturnToBody property from SharedGhostComponent --- .../GameObjects/Components/Observer/GhostComponent.cs | 9 ++------- .../GameObjects/Components/Observer/GhostComponent.cs | 2 +- .../Components/Observer/SharedGhostComponent.cs | 2 -- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/Content.Client/GameObjects/Components/Observer/GhostComponent.cs b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs index 244dd7a5d6..67b81a6345 100644 --- a/Content.Client/GameObjects/Components/Observer/GhostComponent.cs +++ b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs @@ -14,14 +14,9 @@ namespace Content.Client.GameObjects.Components.Observer public class GhostComponent : SharedGhostComponent { private GhostGui _gui; - private bool _canReturnToBody = true; [ViewVariables(VVAccess.ReadOnly)] - public override bool CanReturnToBody - { - get => _canReturnToBody; - set {} - } + public bool CanReturnToBody { get; private set; } = true; #pragma warning disable 649 [Dependency] private readonly IGameHud _gameHud; @@ -92,7 +87,7 @@ namespace Content.Client.GameObjects.Components.Observer if (!(curState is GhostComponentState state)) return; - _canReturnToBody = state.CanReturnToBody; + CanReturnToBody = state.CanReturnToBody; if (Owner == _playerManager.LocalPlayer.ControlledEntity) { diff --git a/Content.Server/GameObjects/Components/Observer/GhostComponent.cs b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs index 11eb1d40db..4853900fed 100644 --- a/Content.Server/GameObjects/Components/Observer/GhostComponent.cs +++ b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs @@ -18,7 +18,7 @@ namespace Content.Server.GameObjects.Components.Observer private bool _canReturnToBody = true; [ViewVariables(VVAccess.ReadWrite)] - public override bool CanReturnToBody + public bool CanReturnToBody { get => _canReturnToBody; set diff --git a/Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs b/Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs index f7a302bcba..b838c3b63e 100644 --- a/Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs +++ b/Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs @@ -8,8 +8,6 @@ namespace Content.Shared.GameObjects.Components.Observer { public override string Name => "Ghost"; public override uint? NetID => ContentNetIDs.GHOST; - - public virtual bool CanReturnToBody { get; set; } = true; } [Serializable, NetSerializable] From 50273679885d36b577c82272d245e8193413ce46 Mon Sep 17 00:00:00 2001 From: zumorica Date: Thu, 9 Apr 2020 03:08:06 +0200 Subject: [PATCH 31/54] Add localization to deadchat --- Content.Server/Chat/ChatManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Content.Server/Chat/ChatManager.cs b/Content.Server/Chat/ChatManager.cs index ebd06862ee..12d8f27efa 100644 --- a/Content.Server/Chat/ChatManager.cs +++ b/Content.Server/Chat/ChatManager.cs @@ -9,6 +9,7 @@ using Robust.Server.Interfaces.Player; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; using Robust.Shared.IoC; +using Robust.Shared.Localization; namespace Content.Server.Chat { @@ -22,6 +23,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 @@ -102,7 +104,7 @@ namespace Content.Server.Chat var msg = _netManager.CreateNetMessage(); msg.Channel = ChatChannel.Dead; msg.Message = message; - msg.MessageWrap = $"DEAD: {player.AttachedEntity.Name}: {{0}}"; + msg.MessageWrap = $"{_localizationManager.GetString("DEAD")}: {player.AttachedEntity.Name}: {{0}}"; _netManager.ServerSendToMany(msg, clients.ToList()); } From 3ba2e5de807252713d28a01faf9114d71eb36363 Mon Sep 17 00:00:00 2001 From: zumorica Date: Thu, 9 Apr 2020 03:31:40 +0200 Subject: [PATCH 32/54] Speech bubbles! --- Content.Client/Chat/ChatManager.cs | 2 +- Content.Server/Chat/ChatManager.cs | 3 +++ Content.Shared/Chat/MsgChatMessage.cs | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Content.Client/Chat/ChatManager.cs b/Content.Client/Chat/ChatManager.cs index a2a3e04ae7..dd8f437142 100644 --- a/Content.Client/Chat/ChatManager.cs +++ b/Content.Client/Chat/ChatManager.cs @@ -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); } diff --git a/Content.Server/Chat/ChatManager.cs b/Content.Server/Chat/ChatManager.cs index 12d8f27efa..f4f3df49e0 100644 --- a/Content.Server/Chat/ChatManager.cs +++ b/Content.Server/Chat/ChatManager.cs @@ -4,12 +4,14 @@ 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 { @@ -105,6 +107,7 @@ namespace Content.Server.Chat 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()); } diff --git a/Content.Shared/Chat/MsgChatMessage.cs b/Content.Shared/Chat/MsgChatMessage.cs index de2a5f980c..10e73d881e 100644 --- a/Content.Shared/Chat/MsgChatMessage.cs +++ b/Content.Shared/Chat/MsgChatMessage.cs @@ -35,7 +35,7 @@ namespace Content.Shared.Chat /// /// The sending entity. - /// Only applies to and . + /// Only applies to , and . /// 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; From 8f580ecc1bcf7acab88c66514a82544363b23b37 Mon Sep 17 00:00:00 2001 From: Injazz Date: Thu, 9 Apr 2020 16:43:56 +0500 Subject: [PATCH 33/54] Visually updates contents of the beaker also adds new syringe sprite from eris also adds colors to ALL reagents --- .../Components/Chemistry/SolutionComponent.cs | 102 +++++++++++------- .../TransformableContainerComponent.cs | 5 + .../Entities/items/Consumables/drinks.yml | 1 + .../Prototypes/Entities/items/chemistry.yml | 10 +- Resources/Prototypes/Reagents/chemicals.yml | 4 + Resources/Prototypes/Reagents/drinks.yml | 22 ++++ Resources/Prototypes/Reagents/elements.yml | 4 + .../Chemistry/fillings.rsi/backpack1.png | Bin 0 -> 127 bytes .../Chemistry/fillings.rsi/backpack2.png | Bin 0 -> 149 bytes .../Chemistry/fillings.rsi/backpackmob1.png | Bin 0 -> 164 bytes .../Chemistry/fillings.rsi/backpackmob2.png | Bin 0 -> 173 bytes .../Chemistry/fillings.rsi/beaker1.png | Bin 0 -> 137 bytes .../Chemistry/fillings.rsi/beaker2.png | Bin 0 -> 145 bytes .../Chemistry/fillings.rsi/beaker3.png | Bin 0 -> 151 bytes .../Chemistry/fillings.rsi/beaker4.png | Bin 0 -> 162 bytes .../Chemistry/fillings.rsi/beaker5.png | Bin 0 -> 160 bytes .../Chemistry/fillings.rsi/beaker6.png | Bin 0 -> 167 bytes .../Chemistry/fillings.rsi/beakerlarge1.png | Bin 0 -> 129 bytes .../Chemistry/fillings.rsi/beakerlarge2.png | Bin 0 -> 146 bytes .../Chemistry/fillings.rsi/beakerlarge3.png | Bin 0 -> 166 bytes .../Chemistry/fillings.rsi/beakerlarge4.png | Bin 0 -> 164 bytes .../Chemistry/fillings.rsi/beakerlarge5.png | Bin 0 -> 171 bytes .../Chemistry/fillings.rsi/beakerlarge6.png | Bin 0 -> 164 bytes .../Chemistry/fillings.rsi/bottle-1-1.png | Bin 0 -> 109 bytes .../Chemistry/fillings.rsi/bottle-1-2.png | Bin 0 -> 121 bytes .../Chemistry/fillings.rsi/bottle-1-3.png | Bin 0 -> 134 bytes .../Chemistry/fillings.rsi/bottle-1-4.png | Bin 0 -> 136 bytes .../Chemistry/fillings.rsi/bottle-1-5.png | Bin 0 -> 136 bytes .../Chemistry/fillings.rsi/bottle-1-6.png | Bin 0 -> 142 bytes .../Chemistry/fillings.rsi/bottle-2-1.png | Bin 0 -> 109 bytes .../Chemistry/fillings.rsi/bottle-2-2.png | Bin 0 -> 126 bytes .../Chemistry/fillings.rsi/bottle-2-3.png | Bin 0 -> 126 bytes .../Chemistry/fillings.rsi/bottle-2-4.png | Bin 0 -> 134 bytes .../Chemistry/fillings.rsi/bottle-2-5.png | Bin 0 -> 153 bytes .../Chemistry/fillings.rsi/bottle-2-6.png | Bin 0 -> 151 bytes .../Chemistry/fillings.rsi/bottle-3-1.png | Bin 0 -> 109 bytes .../Chemistry/fillings.rsi/bottle-3-2.png | Bin 0 -> 101 bytes .../Chemistry/fillings.rsi/bottle-3-3.png | Bin 0 -> 109 bytes .../Chemistry/fillings.rsi/bottle-3-4.png | Bin 0 -> 112 bytes .../Chemistry/fillings.rsi/bottle-3-5.png | Bin 0 -> 127 bytes .../Chemistry/fillings.rsi/bottle-3-6.png | Bin 0 -> 124 bytes .../Chemistry/fillings.rsi/bottle-4-1.png | Bin 0 -> 109 bytes .../Chemistry/fillings.rsi/bottle-4-2.png | Bin 0 -> 125 bytes .../Chemistry/fillings.rsi/bottle-4-3.png | Bin 0 -> 142 bytes .../Chemistry/fillings.rsi/bottle-4-4.png | Bin 0 -> 147 bytes .../Chemistry/fillings.rsi/bottle-4-5.png | Bin 0 -> 146 bytes .../Chemistry/fillings.rsi/bottle-4-6.png | Bin 0 -> 155 bytes .../Chemistry/fillings.rsi/dropper1.png | Bin 0 -> 113 bytes .../Objects/Chemistry/fillings.rsi/glass1.png | Bin 0 -> 131 bytes .../Objects/Chemistry/fillings.rsi/glass2.png | Bin 0 -> 152 bytes .../Objects/Chemistry/fillings.rsi/glass3.png | Bin 0 -> 152 bytes .../Objects/Chemistry/fillings.rsi/glass4.png | Bin 0 -> 164 bytes .../Objects/Chemistry/fillings.rsi/glass5.png | Bin 0 -> 155 bytes .../Objects/Chemistry/fillings.rsi/glass6.png | Bin 0 -> 154 bytes .../Chemistry/fillings.rsi/largebottle1.png | Bin 0 -> 141 bytes .../Chemistry/fillings.rsi/largebottle2.png | Bin 0 -> 168 bytes .../Chemistry/fillings.rsi/largebottle3.png | Bin 0 -> 177 bytes .../Chemistry/fillings.rsi/largebottle4.png | Bin 0 -> 180 bytes .../Chemistry/fillings.rsi/largebottle5.png | Bin 0 -> 193 bytes .../Chemistry/fillings.rsi/largebottle6.png | Bin 0 -> 213 bytes .../Objects/Chemistry/fillings.rsi/meta.json | 1 + .../Chemistry/fillings.rsi/smallbottle1.png | Bin 0 -> 123 bytes .../Chemistry/fillings.rsi/smallbottle2.png | Bin 0 -> 144 bytes .../Chemistry/fillings.rsi/smallbottle3.png | Bin 0 -> 163 bytes .../Chemistry/fillings.rsi/smallbottle4.png | Bin 0 -> 165 bytes .../Chemistry/fillings.rsi/smallbottle5.png | Bin 0 -> 160 bytes .../Chemistry/fillings.rsi/smallbottle6.png | Bin 0 -> 163 bytes .../Chemistry/fillings.rsi/syringe1.png | Bin 0 -> 131 bytes .../Chemistry/fillings.rsi/syringe2.png | Bin 0 -> 131 bytes .../Chemistry/fillings.rsi/syringe3.png | Bin 0 -> 131 bytes .../Chemistry/fillings.rsi/syringe4.png | Bin 0 -> 153 bytes .../Objects/Chemistry/fillings.rsi/vial1.png | Bin 0 -> 102 bytes .../Objects/Chemistry/fillings.rsi/vial2.png | Bin 0 -> 104 bytes .../Objects/Chemistry/fillings.rsi/vial3.png | Bin 0 -> 105 bytes .../Objects/Chemistry/fillings.rsi/vial4.png | Bin 0 -> 106 bytes .../Objects/Chemistry/fillings.rsi/vial5.png | Bin 0 -> 105 bytes .../Objects/Chemistry/fillings.rsi/vial6.png | Bin 0 -> 106 bytes .../Objects/Chemistry/syringe.rsi/0.png | Bin 0 -> 331 bytes .../Objects/Chemistry/syringe.rsi/1.png | Bin 0 -> 331 bytes .../Objects/Chemistry/syringe.rsi/10.png | Bin 0 -> 330 bytes .../Objects/Chemistry/syringe.rsi/15.png | Bin 0 -> 284 bytes .../Objects/Chemistry/syringe.rsi/5.png | Bin 0 -> 338 bytes .../Chemistry/syringe.rsi/autoinjector.png | Bin 0 -> 301 bytes .../Chemistry/syringe.rsi/autoinjector0.png | Bin 0 -> 381 bytes .../syringe.rsi/autoinjector_black.png | Bin 0 -> 304 bytes .../syringe.rsi/autoinjector_black0.png | Bin 0 -> 370 bytes .../syringe.rsi/autoinjector_red.png | Bin 0 -> 304 bytes .../syringe.rsi/autoinjector_red0.png | Bin 0 -> 376 bytes .../Chemistry/syringe.rsi/borghypo.png | Bin 0 -> 587 bytes .../Chemistry/syringe.rsi/borghypo_s.png | Bin 0 -> 517 bytes .../Objects/Chemistry/syringe.rsi/broken.png | Bin 0 -> 341 bytes .../Chemistry/syringe.rsi/combat_hypo.png | Bin 0 -> 665 bytes .../Objects/Chemistry/syringe.rsi/draw.png | Bin 0 -> 181 bytes .../Objects/Chemistry/syringe.rsi/hypo.png | Bin 0 -> 429 bytes .../Objects/Chemistry/syringe.rsi/inject.png | Bin 0 -> 175 bytes .../Objects/Chemistry/syringe.rsi/meta.json | 1 + 96 files changed, 107 insertions(+), 43 deletions(-) create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/backpack1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/backpack2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/backpackmob1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/backpackmob2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beaker1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beaker2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beaker3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beaker4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beaker5.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beaker6.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge5.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge6.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-5.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-6.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-5.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-6.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-5.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-6.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-5.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-6.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/dropper1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/glass1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/glass2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/glass3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/glass4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/glass5.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/glass6.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/largebottle1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/largebottle2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/largebottle3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/largebottle4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/largebottle5.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/largebottle6.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/meta.json create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle5.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle6.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/syringe1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/syringe2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/syringe3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/syringe4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/vial1.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/vial2.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/vial3.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/vial4.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/vial5.png create mode 100644 Resources/Textures/Objects/Chemistry/fillings.rsi/vial6.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/0.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/1.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/10.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/15.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/5.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/autoinjector.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/autoinjector0.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/autoinjector_black.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/autoinjector_black0.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/autoinjector_red.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/autoinjector_red0.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/borghypo.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/borghypo_s.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/broken.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/combat_hypo.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/draw.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/hypo.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/inject.png create mode 100644 Resources/Textures/Objects/Chemistry/syringe.rsi/meta.json diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 87c15bd061..66843b3b54 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -9,6 +9,8 @@ using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Shared.Chemistry; using Content.Shared.GameObjects; +using Content.Shared.Utility; +using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -38,11 +40,17 @@ namespace Content.Server.GameObjects.Components.Chemistry private AudioSystem _audioSystem; private ChemistrySystem _chemistrySystem; + private SpriteComponent _spriteComponent; + [ViewVariables] protected Solution _containedSolution = new Solution(); protected int _maxVolume; private SolutionCaps _capabilities; - + private string _fillInitState; + private int _fillInitSteps; + private string _fillPathString = "Objects/Chemistry/fillings.rsi"; + private ResourcePath _fillPath; + private SpriteSpecifier _fillSprite; /// /// The maximum volume of the container. /// @@ -108,6 +116,8 @@ namespace Content.Server.GameObjects.Components.Chemistry serializer.DataField(ref _maxVolume, "maxVol", 0); serializer.DataField(ref _containedSolution, "contents", _containedSolution); serializer.DataField(ref _capabilities, "caps", SolutionCaps.None); + serializer.DataField(ref _fillInitState, "fillingState", ""); + serializer.DataField(ref _fillInitSteps, "fillingSteps", 7); } public override void Initialize() @@ -116,23 +126,20 @@ namespace Content.Server.GameObjects.Components.Chemistry _audioSystem = _entitySystemManager.GetEntitySystem(); _chemistrySystem = _entitySystemManager.GetEntitySystem(); _reactions = _prototypeManager.EnumeratePrototypes(); - } protected override void Startup() { base.Startup(); RecalculateColor(); - } - - /// - /// Initializes the SolutionComponent if it doesn't have an owner - /// - public void InitializeFromPrototype() - { - // Because Initialize needs an Owner, Startup isn't called, etc. - IoCManager.InjectDependencies(this); - _reactions = _prototypeManager.EnumeratePrototypes(); + if (!string.IsNullOrEmpty(_fillInitState)) + { + _spriteComponent = Owner.GetComponent(); + _fillPath = new ResourcePath(_fillPathString); + _fillSprite = new SpriteSpecifier.Rsi(_fillPath, _fillInitState + (_fillInitSteps - 1)); + _spriteComponent.AddLayerWithSprite(_fillSprite); + UpdateFillIcon(); + } } /// @@ -147,7 +154,7 @@ namespace Content.Server.GameObjects.Components.Chemistry public void RemoveAllSolution() { _containedSolution.RemoveAllSolution(); - OnSolutionChanged(); + OnSolutionChanged(false); } public bool TryRemoveReagent(string reagentId, int quantity) @@ -155,7 +162,7 @@ namespace Content.Server.GameObjects.Components.Chemistry if (!ContainsReagent(reagentId, out var currentQuantity)) return false; _containedSolution.RemoveReagent(reagentId, quantity); - OnSolutionChanged(); + OnSolutionChanged(false); return true; } @@ -170,21 +177,24 @@ namespace Content.Server.GameObjects.Components.Chemistry return false; _containedSolution.RemoveSolution(quantity); - OnSolutionChanged(); + OnSolutionChanged(false); return true; } public Solution SplitSolution(int quantity) { var solutionSplit = _containedSolution.SplitSolution(quantity); - OnSolutionChanged(); + OnSolutionChanged(false); return solutionSplit; } protected void RecalculateColor() { - if(_containedSolution.TotalVolume == 0) - SubstanceColor = Color.White; + if (_containedSolution.TotalVolume == 0) + { + SubstanceColor = Color.Transparent; + return; + } Color mixColor = default; float runningTotalQuantity = 0; @@ -195,25 +205,15 @@ namespace Content.Server.GameObjects.Components.Chemistry if(!_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype proto)) continue; - if (mixColor == default) mixColor = proto.SubstanceColor; - - mixColor = BlendRGB(mixColor, proto.SubstanceColor, reagent.Quantity / runningTotalQuantity); + mixColor = Color.InterpolateBetween(mixColor, proto.SubstanceColor, + (1 / runningTotalQuantity) * reagent.Quantity); } + + SubstanceColor = mixColor; } - 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); - } - - /// /// Transfers solution from the held container to the target container. /// @@ -400,12 +400,9 @@ namespace Content.Server.GameObjects.Components.Chemistry } _containedSolution.AddReagent(reagentId, acceptedQuantity); - if (!skipColor) { - RecalculateColor(); - } if(!skipReactionCheck) CheckForReaction(); - OnSolutionChanged(); + OnSolutionChanged(skipColor); return true; } @@ -415,12 +412,9 @@ namespace Content.Server.GameObjects.Components.Chemistry return false; _containedSolution.AddSolution(solution); - if (!skipColor) { - RecalculateColor(); - } if(!skipReactionCheck) CheckForReaction(); - _chemistrySystem.HandleSolutionChange(Owner); + OnSolutionChanged(skipColor); return true; } @@ -519,6 +513,32 @@ namespace Content.Server.GameObjects.Components.Chemistry return majorReagent.ReagentId; } - protected virtual void OnSolutionChanged() => _chemistrySystem.HandleSolutionChange(Owner); + protected void UpdateFillIcon() + { + if (string.IsNullOrEmpty(_fillInitState)) return; + + var percentage = (double)CurrentVolume / MaxVolume; + var level = ContentHelpers.RoundToLevels(percentage * 100, 100, _fillInitSteps); + + //Transformed glass uses special fancy sprites so we don't bother + if (level == 0 || Owner.TryGetComponent(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); + } } } diff --git a/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs b/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs index 27e2d338bc..380ac24bff 100644 --- a/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs @@ -20,6 +20,9 @@ namespace Content.Server.GameObjects.Components.Chemistry public override string Name => "TransformableContainer"; + private bool _transformed = false; + public bool Transformed { get => _transformed; } + private SpriteSpecifier _initialSprite; private string _initialName; private string _initialDescription; @@ -46,6 +49,7 @@ namespace Content.Server.GameObjects.Components.Chemistry public void CancelTransformation() { _currentReagent = null; + _transformed = false; _sprite.LayerSetSprite(0, _initialSprite); Owner.Name = _initialName; //Owner.Description = _initialDescription; @@ -79,6 +83,7 @@ namespace Content.Server.GameObjects.Components.Chemistry Owner.Name = proto.Name + " glass"; //Owner.Description = proto.Description; _currentReagent = proto; + _transformed = true; } } } diff --git a/Resources/Prototypes/Entities/items/Consumables/drinks.yml b/Resources/Prototypes/Entities/items/Consumables/drinks.yml index 6451742780..bb5562b769 100644 --- a/Resources/Prototypes/Entities/items/Consumables/drinks.yml +++ b/Resources/Prototypes/Entities/items/Consumables/drinks.yml @@ -29,6 +29,7 @@ - type: Icon sprite: Objects/Drinks/glass_clear.rsi - type: Solution + fillingState: glass maxVol: 50 caps: 16 - type: Drink diff --git a/Resources/Prototypes/Entities/items/chemistry.yml b/Resources/Prototypes/Entities/items/chemistry.yml index cca0065844..be043ad008 100644 --- a/Resources/Prototypes/Entities/items/chemistry.yml +++ b/Resources/Prototypes/Entities/items/chemistry.yml @@ -9,6 +9,7 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/beaker.png - type: Solution + fillingState: beaker maxVol: 50 caps: 27 - type: Pourable @@ -25,6 +26,7 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/beakerlarge.png - type: Solution + fillingState: beakerlarge maxVol: 100 caps: 27 - type: Pourable @@ -41,6 +43,8 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/dropper.png - type: Solution + fillingState: dropper + fillingSteps: 2 maxVol: 5 caps: 19 - type: Pourable @@ -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 diff --git a/Resources/Prototypes/Reagents/chemicals.yml b/Resources/Prototypes/Reagents/chemicals.yml index a5e29b8487..e8d0b18dd3 100644 --- a/Resources/Prototypes/Reagents/chemicals.yml +++ b/Resources/Prototypes/Reagents/chemicals.yml @@ -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 diff --git a/Resources/Prototypes/Reagents/drinks.yml b/Resources/Prototypes/Reagents/drinks.yml index 7c79af86b5..4cffd3be62 100644 --- a/Resources/Prototypes/Reagents/drinks.yml +++ b/Resources/Prototypes/Reagents/drinks.yml @@ -2,101 +2,118 @@ 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 @@ -104,6 +121,7 @@ id: chem.Cola name: Cola desc: A sweet, carbonated soft drink. Caffeine free. + color: "#100800" metabolism: - !type:DefaultDrink rate: 1 @@ -112,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 @@ -120,6 +139,7 @@ id: chem.Tea name: Tea desc: A made by boiling leaves of the tea tree, Camellia sinensis. + color: "#101000" metabolism: - !type:DefaultDrink rate: 1 @@ -128,6 +148,7 @@ 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 @@ -136,6 +157,7 @@ id: chem.Milk name: Milk desc: An opaque white liquid produced by the mammary glands of mammals. + color: "#DFDFDF" metabolism: - !type:DefaultDrink rate: 1 \ No newline at end of file diff --git a/Resources/Prototypes/Reagents/elements.yml b/Resources/Prototypes/Reagents/elements.yml index 0465c71cc1..f60bb120d0 100644 --- a/Resources/Prototypes/Reagents/elements.yml +++ b/Resources/Prototypes/Reagents/elements.yml @@ -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 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/backpack1.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/backpack1.png new file mode 100644 index 0000000000000000000000000000000000000000..87af9277bb94955ea26ce84437bcc8a785ae9e39 GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzH%}MGkcv5P&u!!dO1K1?PUCs< zZ)&pv6Z4J+)b7tiHr%IQ?@~56?n0!xW!K!s<7Pm!7^D{DB Zm;4&SBe3-{>vo`t44$rjF6*2UngAwEDr5iv literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/backpack2.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/backpack2.png new file mode 100644 index 0000000000000000000000000000000000000000..b77569d5ed307778d7c061d70afecd181c699dc8 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ7*7|+kcv6U2@z4X&WazW?%yXNl8hOf0NbyPZ+g#N= t-J_FdbDucZ_9V)J%k7{B$gm5H46OV44+Z~N69BY_!PC{xWt~$(697Z!FE9WA literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/backpackmob1.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/backpackmob1.png new file mode 100644 index 0000000000000000000000000000000000000000..1cc753cd78155e4702ce9e446d8fbec896c0e10e GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=*`6+rAr*7p-rUIBV8FxT;5=8* z$iXe)f0v_?6Wf9X?n*99Q+A*0{aUrNn1zAiK+Cl4%wMwBzTJB5gr&Pz=Igtg`*^;( zb5&R$5MDav-DP&5i9pcN@%ew9Y5mQ)Z}$J=>o=%n1d1J~;S1Ll^I7{WV>U>Mr>mdK II;Vst00ka8!T8wk$X_HuUelwOfB_e||ovCFiwVV)VADGbd}w kE}pXFxwul&9cBjc*JtDdZ`fuo1FC26boFyt=akR{03I4I#sB~S literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/beaker2.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/beaker2.png new file mode 100644 index 0000000000000000000000000000000000000000..40209c2f669e0275956f906aefc600b6b2a7517a GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ2u~Nskcv5PFKpy(NZ@F>csb&q z@`nFDJO#=Zl2@zE^7y-V(i6TW7NE*MA)#|;*3L~$3lH6&x=&_ufXj=W7rrg=`mItD sn-;xIRB$o#wm`34wFzrY-!V1_Cm&b*|7vOBSD;P?Pgg&ebxsLQ0JQ`*^8f$< literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/beaker3.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/beaker3.png new file mode 100644 index 0000000000000000000000000000000000000000..62c4528ae1e6cd2147e1a9b4d9ea41479208dd6e GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJI8PVHkcv5PFKpyIV8G*Y(c9yt zpiRT~`Nx>=aJ-v)D&dk!n1aBM^+HT@TU3C`|M+IFy&m;D_WjDoXB8)aZillU+qCoF7v{r%I;R9#urv65_@uq&)ygCPfLa+mUHx3vIVCg!08-;T AqyPW_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/beaker4.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/beaker4.png new file mode 100644 index 0000000000000000000000000000000000000000..489b48b78a089734fbe7238fb8189f1a0f27d57d GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJOivfbkcv5PFC64NV8Fo|5M6bK zL-N4i<%JFQP36^G9*V5GU%uSe6x`RPbP0l+XkK=6OL& literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/beaker5.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/beaker5.png new file mode 100644 index 0000000000000000000000000000000000000000..ca719e883afb18d9c3bd5913c33b4154f8494eb5 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJbWaz@kcv5PFD>LfV8G*Y@%}0U z<%)(k_LJQ?@&i@0gATGz`69=)v3L`28W7Z%uV78?u{Q4HU@#JH=vc5-?fdR4O;@F! zd}b(RvfdWr4g18WtBI__u6Ikb7yl{I&Zr+P?4Ng1s0RCNVUW z-QVQ!e&>mAtozgoT%^#rDFPWmR1#>fe5;)9`tJ^z1XB O#SEUVelF{r5}E)*GeLF$ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge4.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge4.png new file mode 100644 index 0000000000000000000000000000000000000000..c16054a21ac87e9683dc4342f96067a837643148 GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJY)==*kcv5PFFA5G81S$h5ViTn zS?loc|1ulJa$8QJ9M2}MN9Qb44sT-+6coHTea6kQ=Q0dyrYeVs?*0C-XI0zxrTiT% z=lZIjFv!oyOgPEXd+*z-#%oa-?cmJ#zp6 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge5.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge5.png new file mode 100644 index 0000000000000000000000000000000000000000..363e3a1bfc02caba22ec6151120c13a83587119b GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJB2O2`kcv5PFCFA;FyLXmpkMKZ zLsH?l|7Auar}tGsSs9ADS{?soZ%=6PXHZg7TEw_?if+*KV!o@42dp-?%ye`)XKuXv z%9Z-<`WGIt|7w+se{h*oN4dGm{f3vqQju4-Qyc6LT^F+Wq4KJ6mDSbB9sjb~`wr`c USUtRN544@Z)78&qol`;+0He=8x&QzG literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge6.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/beakerlarge6.png new file mode 100644 index 0000000000000000000000000000000000000000..64126589dae45658feee3fa70772277a345a6d0b GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJY)==*kcv5PFFSG_FyLT0p!?;8 za81Mi`pNzu?#*Lzyfr~YXUe-ds(Rim9UUDn-fx@wu)~7kz}l9nWbH zbwj#Un{$HrGl!>)c6Sdf_MZ`Js-RRl-yr^AHm}Lf&bo(hehLbjsvk_zlDKASp9Hj) N!PC{xWt~$(6967eJx>4t literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-1.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-1.png new file mode 100644 index 0000000000000000000000000000000000000000..aaed076693827a751d2ee7294c4553cbc1c3362b GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz6Hgb%kcv5P&n@I-P~dU7_{dt| zw>H}=2Au$B0giiS4Tl=oT;P(OpGtDnm{ Hr-UW|Ey*E= literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-2.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-2.png new file mode 100644 index 0000000000000000000000000000000000000000..2243651f0dfb6b6ef9155f2d0ea563bdf4662bbd GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz2TvErkcv5P&lw6b7;rcTM#*wG zIQ{h(ZDL{BuzBk1WA(}JCUtWF6)uZj-`cUmw7A@G^JU8o)=RxY>(4M82;l6=VUKwC SxkwkNgu&C*&t;ucLK6V8IVLaw literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-3.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-3.png new file mode 100644 index 0000000000000000000000000000000000000000..a58b538a8125d9e118af76bb1f36a4601981be0a GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzUr!gukcv5P&u!#wFyLXm$Q)SJ z!gJ`KXmbS%zXNO6i$`tG;xb>JW}l?u`RnF3s}-xXjTt49_k8cLux4~mH?e1{XtMhD ht>_c)B$c&07&m)x#axn3JqR?I!PC{xWt~$(69A5eEwlgt literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-4.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-4.png new file mode 100644 index 0000000000000000000000000000000000000000..a71c865d5835e4ca8cae70fa9372c93ba3ec1e16 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdze@_?3kcv5PFKpy(aNu#i$e+~1 zQ=q(|zO*@|g~y?kS5{Dn_5asty3Hz{m+t5KGl!ZQ3paEyC>Tp7&zZ|utzNRdtwbhY iWSzT}UA@Dk62|qZyqn%K9_RoX&EVbP0l+XkKF0e0( literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-6.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-1-6.png new file mode 100644 index 0000000000000000000000000000000000000000..21e86f3d5fb2efe81d0aff8f3c05e6c56fc62e21 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJP)`@fkcv5PFRm7BaNu#h*y^~f zMa1cU7I&rU9;OQ%EH`J&-Fe*gi9Jxujj3-ltJht#v~G(m{NwxMmX*@GQw&SFJ)|bZ p6^5SXHrhO=y42*(FD8Ztb`M{1rk0lJqCg86JYD@<);T3K0RUWLF$Vwu literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-1.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-1.png new file mode 100644 index 0000000000000000000000000000000000000000..ac1ecd5cffc02b42a52c371bfdb2688f3bd876f6 GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz6Hgb%kcv5P&lw6bDDW^J;QMjC zb%NIl29YmS1wH?hjZ_2~7z!4yDw|$@H|4L@#F_fP8ICai+alRCrUEu*LWEI@FsruV)$^5B&<@rzdiB7n3>TH(6>|MJV7!sIw X>4>=U?g=acs$=kU^>bP0l+XkK)kr9u literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-4.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-4.png new file mode 100644 index 0000000000000000000000000000000000000000..925bf0a4228e604a787bdd582e3ea7c00bc5a099 GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzUr!gukcv5PFCF9rO0+)gI9BjK zQtBY1%t1!0GEF6Jr62nje|n&}jD>;WgK+u!%{y1|m23Z86Qr)R@Xl7Iv~SxSzA&Yl g8~kp5!3wlR-b%zh=Z5(k-=_&#n@7Cc)o>!ChhXFE`vne~)_v~S;UpUK;LV5ildU%y>VHH@oW yJXDvN2NafTevfQ4Ja&)i72CR-4yEj`fExJcew3fYz_SEoC4;A{pUXO@geCw$Av9|M literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-6.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-2-6.png new file mode 100644 index 0000000000000000000000000000000000000000..d82ea80c1f4acef86f631112c8547d57ac8a3213 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJI8PVHkcv5PFKy&)FyL{y$YI#_ zeZH!41w(%UbHK%!P0Czn>qA9Oemr?jf`Q?HN~YJW`1_W3p53kVZdmuQ%k#$B_rK3< zaf;A>;nEViUB@Bxuk?Yr2YzmQ@KSzH}=2Au$B0giiS4Tl=oT;P(OpGtDnm{ Hr-UW|Ey*E= literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-2.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-2.png new file mode 100644 index 0000000000000000000000000000000000000000..3704a1363d492ec199155f1c7012a722666cb086 GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz9Zwg>kcv5P&lw6b81Nj~!1J(w zb%UIP#*)2#mtLFpFyLX{5b*1N ztgQxn$$ODWD;0X#nq?Uo4&)ns4%xKIbhhz!>!l}?8REoe?C1I%eEd%}P(OpGtDnm{ Hr-UW|GJGP0 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-4.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-4.png new file mode 100644 index 0000000000000000000000000000000000000000..ee9ef2c432b496290fa6f55c57bfc4521f488da5 GIT binary patch literal 112 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzb59q?kcv5PFBoz$DDW^JQ2O^j z)@}uVLHVk(f}PULjT1N+81zz~tx5@9Y`*;c?}nd2uVVNPWHJZFa(&%ynK&P41cRrm KpUXO@geCx?MkCY! literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-5.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-3-5.png new file mode 100644 index 0000000000000000000000000000000000000000..6cb238455c40322d6c4242090d960ff1c597aa97 GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzH%}MGkcv5PFBozGC0Y}^-T1Hk zUC#G}F-n=kiFNw_+ahvyB8&_S8rN(TjiaBhS!KMu@9~f9@2ii$IxH}=2Au$B0giiS4Tl=oT;P(OpGtDnm{ Hr-UW|Ey*E= literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-2.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-2.png new file mode 100644 index 0000000000000000000000000000000000000000..6e62bae22cada2937cf1f82e86d121327feea7c5 GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz7f%<*kcv5PFKiS9N;qH4IP35( z-jYMvph5UVY);OyWl<;oH$WHU( V+E{;R%`Tui22WQ%mvv4FO#q4uDEj~a literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-3.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-3.png new file mode 100644 index 0000000000000000000000000000000000000000..dd1a77ce62c0d632cfd8d138b35e5206720e7eb1 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJP)`@fkcv5PFYe}TFyLXh$Q+o) z!hh&rDBDR6xdx`diN_p{@4X~{bCcsFm6z|Qc@?I8Gh|~BEIhsa+P7Cs(OVPEe)Mj= qRS^3?d#U(=pKXzUPX1K&WMGfo#K#%l*8dV{0fVQjpUXO@geCxtb~Ic7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-4.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/bottle-4-4.png new file mode 100644 index 0000000000000000000000000000000000000000..379c84c5bdbc29c471eaee71651bcc7a2c88a354 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJC{Gv1kcv5PFKy&)aNuybxPIdv zL5U-Owb>I}qz{~u@YUH7lk_>Bn}w-C#q-knXTbkxyhCB5i%!=wgg${65i3@W7I&bzBXbXdyi?*YKK&GCV$froE44OwYnmI-5(ZCKKbLh*2~7ZE6>upZ-A7EIyVvz@<^3tELKkgT7=;-JW+4;NZ`_|v*ZU&|upD0`Ld+W^DsZ1+* zT`W8&{0UjJ)uDy?z?{vKz8h>}jy;#{uO8&gQu&X%Q~lo FCII@gKs*2d literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/dropper1.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/dropper1.png new file mode 100644 index 0000000000000000000000000000000000000000..999d333ab8435e12d73dd3a080706d31c840719a GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz3r`ovkcv5P&l(CcDDW^l6a;hr zm@g2-DSF_X;YNcG_Rl7HXEPi~{+kvOT72H<^Nrq)!q1Ehf07xU)7hEso%?+bXa<9) LtDnm{r-UW|wwffO literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/glass1.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/glass1.png new file mode 100644 index 0000000000000000000000000000000000000000..d9ee584491978f604a738640c9685cbb7204c090 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzFHaZ8kcv5P&u!#AV8G!LI3dP} zsk!3+DQSZPx|6hAy_CK6KE>C6I15zzq5Pg?=+&bCe#V!TlHT2un%nUGA?I2KPqmjG d3=A>f8QyVmZfH8Cd;qAF!PC{xWt~$(695HyEW`i+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/glass2.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/glass2.png new file mode 100644 index 0000000000000000000000000000000000000000..6d5dc6152c0e6879dedbd2bc50fa3b6dd6230148 GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJcuyC{kcv5PFBtMQIB>8WxayHv z%4`4df4{-?j~ss*zGgmsCd6HC<~(9zLx;=b;gfcJmbta7(k`nG+ynHmGbmTPUd z?yj^gVfgmo?kS#`H>W&i_}O^I#;7=oLvW&hY;G9uM;qJtvp}mDJYD@<);T3K0RW}5 BI~o80 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/glass3.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/glass3.png new file mode 100644 index 0000000000000000000000000000000000000000..229015775568a4103757e776f905ebdaed5e1939 GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJcuyC{kcv5PFBtMQIB>8ym~0C( zG1=4n-~2oCVfzov*Rq~gah~-nSJ|E;ps1vzl(kJ^zR~A3tFGH9eOn(X9pvER5}KcH z++6g4>BXVtrCz7_FRLfV8G*YF*`I$ zBxw?-Rl|4ndkUxIUNR}BI@-D#?PS+|`o)n!P*Bjb%%H#O{dcW2-HS$aK?lYv37a!%;h zEz4yBY74e)&q-up{>LYgyoGhjzJ8iwuAh>T@6Nw({Z!_5qp`k}p<-U;ku$Gyo^h5l%n;B0{r@tb Z`LsT#hZnCU{sp>$!PC{xWt~$(699+0M5q7& literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/largebottle4.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/largebottle4.png new file mode 100644 index 0000000000000000000000000000000000000000..61fd62c4dbe566cd2a7df8fcbc530cbfbdcb60ea GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJYEKu(kcv5PFCFAOpuod=;r^v$ zw*m(Dg8!41EgalVFxZOZOLc^JJ=~woRQHwH#l_{zM&(U+qE>6Fmb-^eEY^!=`Xaz! zz$Ii`(>e3%)SG9%hH(f+?)oVi`R@Gt)=y<_HyFzrWPgOSH$0!|erC*;znmFnGH9xvXRxE6hI&9%+8S6O!do^0e0%$DY>$^54F{;7tW rhR?(pc7{tYG)}n2f9v({>E>@d`LlMp|JtRu- z4xR%X@9JNHD4qu=k7W7i7=C1t=qKh~wHEDYuO$2ZU9)mIP{WXRb&NhDQ$dx9F!Rt8U3 KKbLh*2~7aIAUvi3 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle4.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle4.png new file mode 100644 index 0000000000000000000000000000000000000000..149f77ad9aa36ae9e8f72b40677126122130dc78 GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ98VX=kcv5Pubky=>PGC zppk=H!T(_Q7YwqB%oRd^95*}N@ds|cF zHi-W@G{pUd@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle5.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle5.png new file mode 100644 index 0000000000000000000000000000000000000000..78ebfcc3a44c89ba50ea42dd6d0406eff9e994aa GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJbWaz@kcv5PuWaOPFc4sNXqGt4 zoWk;Ly~IQI4+h7W~@Ey|9;jSMh~Y; zE1zv(tN5IdUoz|0{rIczJXks(eCAl=%93+BqrPlffC~%58q<#o#}A}hRsgMJ@O1Ta JS?83{1OPyZI}HE; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle6.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/smallbottle6.png new file mode 100644 index 0000000000000000000000000000000000000000..4f392a2288b20f85e6c9a9a667202155b2848de3 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJEKe85kcv5PukPh-aNuBluzX_* z3tIxq_w_Q3j1rBEYrg8dyO7~?FMQI6$$OX@Iy&skLT4Ej?_Sk?!pbUpoy>PA& zv0LT5mwJZIZe8{39%H%KJVPe80~}ua85r*T{lI@F=Lf%lAmaf(Z*OJQt^o6sKwB9+ MUHx3vIVCg!0OV3RfdBvi literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/syringe1.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/syringe1.png new file mode 100644 index 0000000000000000000000000000000000000000..74cfa72ab4831e7514d2eeb8180dae0d69d6f858 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzFHaZ8kcv5PFIaOOFyLVc)L#B2 z@_2i6xngXH1XE(%;%C) edB))RYbh`D9%%C) edB))RYbh`D9%s^|QhL?nuNm|X=T83g%r6~iDubu1pUXO@geCwUv@Q?; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/syringe4.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/syringe4.png new file mode 100644 index 0000000000000000000000000000000000000000..d8b7e8a701b086365b192639d62dc4ce25b167f9 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ1Wy;okcv5PuN>q(puofIuzGhY z`}tWs)Ay|inCI%d@)EtJuRSXOXt5s}`|K75+PYdH`h~aT%`za^B_Zv_vgQu&X%Q~loCIC=e B9nt^* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/vial3.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/vial3.png new file mode 100644 index 0000000000000000000000000000000000000000..997d2dae2f9271d4b600f0ea413ba74155328149 GIT binary patch literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz15X#nkcv5P&pGlk81Nj~koBig z)TvRXq49>@)EtJuRSXO_v^>xI>@PdlFUh=`ok36a2;1Mk6Pf#fdKo-j{an^LB{Ts5 DlsF%S literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/fillings.rsi/vial4.png b/Resources/Textures/Objects/Chemistry/fillings.rsi/vial4.png new file mode 100644 index 0000000000000000000000000000000000000000..fdd0af6277b8a14252b8c7d6c9e7eacbb8b7a725 GIT binary patch literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzLr)jSkcv5P&pGlk81Nj~koBig z)TvRXq49>@)EtJuRSXQLVZwn1#)e+*@3QRdYftti05vmsy85}Sb4q9e E0ONEaUH||9 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/syringe.rsi/0.png b/Resources/Textures/Objects/Chemistry/syringe.rsi/0.png new file mode 100644 index 0000000000000000000000000000000000000000..9cf92274d4b7ad8e567d67704611d6289cfab32f GIT binary patch literal 331 zcmV-R0kr;!P)!T( zgw&lEvuwgGixtf<$(ha`pu$Yt!{hl~10R)uSvD;fb0q>?g6&G)gO(OKw?<@2%-zod z7bXs_6asgqv!v0a!U3S!>JqbTDg+JyuD_w!MA}`?qfFoNVHcoYS8+Op03?6AQd3>R dOL(cj)D?jnmNS`0sXoEc&NaGU)CxWv8L z7~J)iX{{5uEDX+0;gJJCw{JY`_`GrBDnE=VYvPyiQe$}9AEBxW0E1Lj0UJRf5}N7; d-oP8xQa3#`lRRa-eTe`7002ovPDHLkV1jz0k5m8v literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/syringe.rsi/10.png b/Resources/Textures/Objects/Chemistry/syringe.rsi/10.png new file mode 100644 index 0000000000000000000000000000000000000000..35e22c64542f76398d4c4148f9e54714b041cfa3 GIT binary patch literal 330 zcmV-Q0k!^#P)e|>Wg zIHsEekS!-<2G!GmzPZPLJ1Y~0lA=bUqJ-i&L-u8SL;%B~V`mudKYBuRdZ0K26y;Z<}aUz6;#KILD;0cfJg%iya+kgXTYNrVx zD)Bsc@Q7CS69;x5J^tUo)Qu*gKvXuoeCi^Df`S|a1B2Ut1qC@+7Nidt&=fvu`N*?; c6pTm!09vzpQTH|xaR2}S07*qoM6N<$f)#0l`v3p{ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/syringe.rsi/15.png b/Resources/Textures/Objects/Chemistry/syringe.rsi/15.png new file mode 100644 index 0000000000000000000000000000000000000000..834ae95f9f67cc4e4216a6bbb38dab01db3a127d GIT binary patch literal 284 zcmV+%0ptFOP)e|>Wg zIHsEekS!-<2G!GmzPZPLJ1Y~0lA=bUqJ-i&L-u8SL;%B~V`mudKYBuRdZ1dO*?si* z|EUw^GT2#}Ff3fL0WMD0gxS|W10&uEr30#_fuh0;l6^bi1rbf+XFv<5;RymlQ>b5z zLIF{U@WF#ew6dQ#u>0uo{|2URGzkTws^iP2E;1-6$T2W5xcyg9kYm`edJg?kHchJ0 iQOidy9|fZT2><}8?_osFS+RQn0000UtP)e|>Wg zIHsEekS!-<2G!GmzPZPLJ1Y~0lA=bUqJ-i&L-u8SL;%B~V`mudKYBuRdZ0K26y;={|pREY32xY%V9LB4j?NE z0zy;%Z&*Et8EV1bizjz5lVc890GrQ|*bGod(I)Q)gr@xO>z@G+Zm8ufG*4{kj=<$} znp%p^$Cl!01`JHyFf5{LuCGhPC zgu(KD$mS^yg~4_u0PyXIP#7{}G#ZUY^BX(>^&?va@GCQX00000NkvXXu0mjfF5`cs literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/syringe.rsi/autoinjector0.png b/Resources/Textures/Objects/Chemistry/syringe.rsi/autoinjector0.png new file mode 100644 index 0000000000000000000000000000000000000000..c19b268183007cbf9b7daba6536b07e09db1dbae GIT binary patch literal 381 zcmV-@0fPRCP)1u{hYrp~Bp|V15TS*Zii2BmlG3rIiJ7S{4qNHxx{uEo!%zDFqoe}d?3sN{e~ bqtSc;wR3R?Tf>Kf00000NkvXXu0mjfmFJ}d literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/syringe.rsi/autoinjector_black.png b/Resources/Textures/Objects/Chemistry/syringe.rsi/autoinjector_black.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c55e28ca5729a71f26dcf3be6c9372598a0046 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ@18D>Ar*6y6C_v{Cy4Zv9PsE& z3zuH}zw6Yif`4mTPkWm6w+5XSo_tvX1lGk_oj!2jz=!!E?h~FefYdz{4CK)c*Gb+{ z^-|~XDbdqlW$)~3oHj+Kv@=*WxE`H*e7(n|<1V`FclK9LUL&Azn-QqK`L~sgQkP?3 za4KtmM`7!(J$;@G*RTPgGpxHhtQBkG`$c6@3UOaCFh)%H!l|y3dMg%lW9IbI|_BfjO**0yVUbmWd!Jk zNv+-hz;hjdFs%qF0Rz8}=Q=#x-$wOYka~ucfFz4w@%lbW)wi$}|pOtL`ajECZhFuzwgWmT_Qd z0RUH*7eM5IZkXu1B3l9FN>!#ENdFXv#@+!NTiZ5y2%05(MoPt!%9Y8^ulp7h+-O2JCENvIZfo_(>D6Q2ZcThzqbX#6k>W zml+9ybGix4%bh!i0fj>G@5E#_S1Scy&*p>ma5N4*FCqK$cBc!#%5?zzupxv3R4WC) z(QILv1}oRWG7SI@$8+de!VP@#x{B@T8fstsTLBKcwNL}X0W8x9PTgp>(6f2;Ogg#% z?*h&jj&K9Q4zxSnm-&=S#o&8n4!qkh(}0wOz5W1rHW0Z35IjCo2suz&T&Sm}04!%V zn_Ch*jmdo<%~R}p8o8Mbz~+|3^)!(&3WY+U_zfQ5_gJnt8_~1?0000P#oNfW9OD`rI0`1=HL&>=HkcX4{+(^&^CjC4lTyE zSP?ooRRv2NNEED~Tho@vM-{Q0Zzk;=fIVDd7RazLR_D82x~ WR&S||AC@`*0000kdUjOQzyK%XKjn4w2YH!J9FGE+NT~t)4x!BpAAQ_EemuYfDx` z2IJt0*mP0{OKY{n?ULBWDS-^v#d^rjS#n!>({H(dy7ztW-H8JtA|fIpB9@@lYROf_ zUJLp_N-4XBu`2yw1HT4?m5BpD%y02SwJJ9f3G9?g=-W2UTb6}%I!&#Ld_M0R+vS0G z;CI%UREqjt!$2;Vqt?Y+EpIG8Gqmt|_fP_WUaO*S+cSayaMEa?-RUgMWLwwq?&#>A z5davRrSS4#q%=Vlc(VV7vgzA4o@&}muy&_|LZL9Tc|0Cd|LesC^}TxsXDNL8et=p{ zla)&4o(VGWar6T1ZzH^Y_FI|7wyvYuY$~&N*Hsr*du?M6iz_fQx$OVmH4MBGf?5@p zWl_IeE(hNM74>>u0>DnGgfFM3G(Wn!!bT!NtqMQUr40bU&&x}B(rDoQ@o@|QIOlSB z7pL|3`VgjRE+wX7O$gEZn+>LE#^UQk{OF&@-0{DP6P$Bj6Z3aN2q6I=nJtF%MkKSv zo)Dt9nb``g2IrhaH#1xRZ@?9Gs}N)&^z6O3CX(5r(nRRlW9~I%jA;OHdvmR+K4y$z zYEZhDFmyQQ&Q!#in-4wvg$N;>sgN@rlVI3qzp#v)bB8g8$z+1zUHtjOM~`uPbB(Y| Z{|6q|*2fsk`QHEl002ovPDHLkV1lnT6~q7l literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/syringe.rsi/borghypo_s.png b/Resources/Textures/Objects/Chemistry/syringe.rsi/borghypo_s.png new file mode 100644 index 0000000000000000000000000000000000000000..4adc13c448a40cf02e1cbe10c87b518aee3c501b GIT binary patch literal 517 zcmV+g0{Z=lP)BdF16XyFAEr~? z>-TZTW7K|c#+6dowrye+o6V;7?1DkLQ}?Xxt1HvquWsEtITez8JJ8am2-6&cr|$L`@YL_-i$^jR-u$K?d^8kTmx0i zW-|@oj>n{rk7oS$^E17E-^42FM9($=@OiP&Ns{p6{@wx*LTEQl&3x**L{W5>SQR}f zOAM{3Dh@u#!@)ZszmGYzQHY=*@io z{{{-tA`49>TEBgn!o)HNDowP0dyf8wj^lU$4@!Aei;M66G6-~0gsmflhz}z1(Rk~( zKZ%qwJ_yB!z7cHu*`G|K5F&OQhb+r*)AZw8+vY(j+D`oob|uWwd*bsY00000NkvXX Hu0mjfRqp;x literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/syringe.rsi/broken.png b/Resources/Textures/Objects/Chemistry/syringe.rsi/broken.png new file mode 100644 index 0000000000000000000000000000000000000000..aee78e21a54b12442025cd688b6154d3be0eed0a GIT binary patch literal 341 zcmV-b0jmCqP)Yx-H9HnEHf}2ZEAnxKWb#?FtE{=t6g;o@o;sGqhbND-GOVEnd znpCh4N|Pnu%lrQ%K&gLMMKnOwbc)Bp))vlt3QwB@fFjRMx~h2>_}Pe=VUpu*NY(V@ zlV*U2zgpw#J)mkj^}210N7GCt$z3ykm%{;r>+f-lPMP~5cLL3N4FE8+CUE^ddfj$n zOi0~%F~cNWQ>>_(PS$+!02OB99v=VSHSkdhm|@a-wN!3^bFj7LU1(`L=f=315_9&m zz=er}D}}(V`66jFsc-;jb^62%lL~Ymu||mgb5({6BGH_&U(^CtG& zwjJ8L-EM6BsR+2P8$2o$0NC$zcvG&iY-K|Gm)R zr4#@UmX^3PKTp5k=eg&x8IvQHK(y_??{nn)juU>@8;uDcZPx^Py&m_w-P<{Ym)9wf zxf}p8m*X^bkAwPFGR1}GJa z!P4?FN-0{c)>JQStPmahK1Ii2w^rj_tHmbZ7Ybt}Ow&XuH6}v0MnL1_~34VFrMo{7UZ&muzb;00000NkvXXu0mjfmd!4R literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/syringe.rsi/draw.png b/Resources/Textures/Objects/Chemistry/syringe.rsi/draw.png new file mode 100644 index 0000000000000000000000000000000000000000..1bc212413e4e6d8542a89e38cd4c136a07a2b7ae GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ8c!F;kcv6U2@)?G1m`c=xAS;? z&LeUEcL!GYiyUzBaP*J2<=Vew#l)yGV*?Nn3ak(u6{1-oD!M1wNA3KCfK@OyN14*acLhe8pS2u6K!AlTtz)^8?@(wyGbjj2ls!*s^kPHfK zl-450rHRslm~*5K=3P!Q9v!LZNcCNbC6)7-1FY9- z#BrRNkCKG@;Shk~<7+N+Fz5jx1a;dck`jP!6lvAU6+x;n46XOsjPc8hkvZguU{T%q zpPNdH+;iQfPK%^mO$46ns;1*`E+tLJq2+m4mUaSqJ+Rwu18_87mC84I#sC*VKnNTL zfwr*`Knl@lH0Var>LVysD%rDTSw>^1y#wWH6)?-+2;jG*4WN|DPQsM}qzz}%ct!y5 zc@^Ic2F4&-0GmS4@Aug`yEZb{2Dt6@D3?myJdD{sy`trL+M}R-87*$T$yrdS(4Xo9 XF~@m1n0(H*00000NkvXXu0mjf=b6JS literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/syringe.rsi/inject.png b/Resources/Textures/Objects/Chemistry/syringe.rsi/inject.png new file mode 100644 index 0000000000000000000000000000000000000000..e03bd1d144af40012c4f254d36c4f68979bc594c GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJGEW!Bkcv6U2@*sT|I1n1fA8(8=DgY`Uo-9SBZ)N0yS05eihFgA&*Z57^-9vK zOG;92-kpOUzopr00A{XiU0rr literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Chemistry/syringe.rsi/meta.json b/Resources/Textures/Objects/Chemistry/syringe.rsi/meta.json new file mode 100644 index 0000000000..a07d2954f6 --- /dev/null +++ b/Resources/Textures/Objects/Chemistry/syringe.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "states": [{"name": "0", "directions": 1, "delays": [[1.0]]}, {"name": "1", "directions": 1, "delays": [[1.0]]}, {"name": "10", "directions": 1, "delays": [[1.0]]}, {"name": "15", "directions": 1, "delays": [[1.0]]}, {"name": "5", "directions": 1, "delays": [[1.0]]}, {"name": "autoinjector", "directions": 1, "delays": [[1.0]]}, {"name": "autoinjector0", "directions": 1, "delays": [[1.0]]}, {"name": "autoinjector_black", "directions": 1, "delays": [[1.0]]}, {"name": "autoinjector_black0", "directions": 1, "delays": [[1.0]]}, {"name": "autoinjector_red", "directions": 1, "delays": [[1.0]]}, {"name": "autoinjector_red0", "directions": 1, "delays": [[1.0]]}, {"name": "borghypo", "directions": 1, "delays": [[1.0]]}, {"name": "borghypo_s", "directions": 1, "delays": [[1.0]]}, {"name": "broken", "directions": 1, "delays": [[1.0]]}, {"name": "combat_hypo", "directions": 1, "delays": [[1.0]]}, {"name": "draw", "directions": 1, "delays": [[1.0]]}, {"name": "hypo", "directions": 1, "delays": [[1.0]]}, {"name": "inject", "directions": 1, "delays": [[1.0]]}]} \ No newline at end of file From 6a08647375d4eb95bc7e81f31e76cd650ca73e81 Mon Sep 17 00:00:00 2001 From: Injazz Date: Thu, 9 Apr 2020 16:49:19 +0500 Subject: [PATCH 34/54] uncomments setter --- .../Components/Chemistry/TransformableContainerComponent.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs b/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs index 380ac24bff..dec60171f8 100644 --- a/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/TransformableContainerComponent.cs @@ -52,7 +52,7 @@ namespace Content.Server.GameObjects.Components.Chemistry _transformed = false; _sprite.LayerSetSprite(0, _initialSprite); Owner.Name = _initialName; - //Owner.Description = _initialDescription; + Owner.Description = _initialDescription; } void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) @@ -81,7 +81,7 @@ namespace Content.Server.GameObjects.Components.Chemistry 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; + Owner.Description = proto.Description; _currentReagent = proto; _transformed = true; } From a6e6edab0ef2aa882d9c6725de3849969c3f7f00 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 9 Apr 2020 16:57:10 +0200 Subject: [PATCH 35/54] Update submodule --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index ec52102d02..1cdb279319 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit ec52102d0279281a00cc1c6811330a13ddaf975b +Subproject commit 1cdb279319bdb16efdc9671d0d4e0e5947b0493f From b73b8cf17267f4d5a4c8c1370ba167296e7faf1b Mon Sep 17 00:00:00 2001 From: Injazz Date: Thu, 9 Apr 2020 20:38:31 +0500 Subject: [PATCH 36/54] fixes --- .../Components/Chemistry/SolutionComponent.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 66843b3b54..81f8656a0e 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -42,15 +42,15 @@ namespace Content.Server.GameObjects.Components.Chemistry private SpriteComponent _spriteComponent; - [ViewVariables] - protected Solution _containedSolution = new Solution(); - protected int _maxVolume; + private Solution _containedSolution = new Solution(); + private int _maxVolume; private SolutionCaps _capabilities; private string _fillInitState; private int _fillInitSteps; private string _fillPathString = "Objects/Chemistry/fillings.rsi"; private ResourcePath _fillPath; private SpriteSpecifier _fillSprite; + /// /// The maximum volume of the container. /// @@ -89,6 +89,13 @@ namespace Content.Server.GameObjects.Components.Chemistry set => _capabilities = value; } + [ViewVariables] + public Solution Solution + { + get => _containedSolution; + set => _containedSolution = value; + } + public IReadOnlyList ReagentList => _containedSolution.Contents; /// @@ -142,15 +149,6 @@ namespace Content.Server.GameObjects.Components.Chemistry } } - /// - protected override void Shutdown() - { - base.Shutdown(); - - _containedSolution.RemoveAllSolution(); - _containedSolution = new Solution(); - } - public void RemoveAllSolution() { _containedSolution.RemoveAllSolution(); From 3c795ad283dcadc2328ac4b49975cbabd9466c3f Mon Sep 17 00:00:00 2001 From: zumorica Date: Thu, 9 Apr 2020 20:28:22 +0200 Subject: [PATCH 37/54] Use TimeSpan instead of DateTime in RoundEnd and Comms Console. --- .../Command/CommunicationsConsoleBoundUserInterface.cs | 8 ++++++-- .../GameObjects/EntitySystems/RoundEndSystem.cs | 6 ++++-- .../Command/SharedCommunicationsConsoleComponent.cs | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs b/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs index 5401ac8537..9e90200515 100644 --- a/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs @@ -3,6 +3,8 @@ 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 @@ -12,11 +14,13 @@ namespace Content.Client.GameObjects.Components.Command [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(DateTime.Now)).TotalSeconds, 0); - private DateTime? _expectedCountdownTime; + ? 0 : Math.Max((int)_expectedCountdownTime.Value.Subtract(_gameTiming.CurTime).TotalSeconds, 0); + private TimeSpan? _expectedCountdownTime; public CommunicationsConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) { diff --git a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs index e1b75529c6..62dfc01069 100644 --- a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs @@ -2,6 +2,7 @@ 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; @@ -11,12 +12,13 @@ namespace Content.Server.GameObjects.EntitySystems { #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 int RoundEndCountdownTime { get; set; } = 5000; - public DateTime? ExpectedCountdownEnd = null; + public TimeSpan? ExpectedCountdownEnd = null; public delegate void RoundEndCountdownStarted(); public event RoundEndCountdownStarted OnRoundEndCountdownStarted; @@ -34,7 +36,7 @@ namespace Content.Server.GameObjects.EntitySystems IsRoundEndCountdownStarted = true; - ExpectedCountdownEnd = DateTime.Now.AddMilliseconds(RoundEndCountdownTime); + ExpectedCountdownEnd = _gameTiming.CurTime.Add(new TimeSpan(0,0,0,0,RoundEndCountdownTime)); Timer.Spawn(RoundEndCountdownTime, EndRound, _roundEndCancellationTokenSource.Token); OnRoundEndCountdownStarted?.Invoke(); } diff --git a/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs b/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs index 00d73272b1..88fb0168fc 100644 --- a/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs +++ b/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs @@ -14,10 +14,10 @@ namespace Content.Shared.GameObjects.Components.Command [Serializable, NetSerializable] public class CommunicationsConsoleInterfaceState : BoundUserInterfaceState { - public readonly DateTime? ExpectedCountdownEnd; + public readonly TimeSpan? ExpectedCountdownEnd; public readonly bool CountdownStarted; - public CommunicationsConsoleInterfaceState(DateTime? expectedCountdownEnd = null) + public CommunicationsConsoleInterfaceState(TimeSpan? expectedCountdownEnd = null) { ExpectedCountdownEnd = expectedCountdownEnd; CountdownStarted = expectedCountdownEnd != null; From c55a4ffbf4d4eb29df35fdef71dc27e1f0effaa7 Mon Sep 17 00:00:00 2001 From: zumorica Date: Thu, 9 Apr 2020 20:31:28 +0200 Subject: [PATCH 38/54] Countdown is now 4 minutes --- Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs index 62dfc01069..1a206d5c50 100644 --- a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs @@ -17,7 +17,7 @@ namespace Content.Server.GameObjects.EntitySystems private CancellationTokenSource _roundEndCancellationTokenSource = new CancellationTokenSource(); public bool IsRoundEndCountdownStarted { get; private set; } - public int RoundEndCountdownTime { get; set; } = 5000; + public int RoundEndCountdownTime { get; set; } = 240000; public TimeSpan? ExpectedCountdownEnd = null; public delegate void RoundEndCountdownStarted(); From 4e67f09488aede6959c8c526d2dc9b820396db22 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Thu, 9 Apr 2020 20:40:59 +0200 Subject: [PATCH 39/54] Fixed comments ReagentUnit now implements IComparable and IEquateable. ReagentUnit now uses double internally. Added multiplication without shifting for ints. InvariantCulture for some string things. Added units tests for Equals and CompareTo. Added unit tests to Solution that deals with nasty fractionals. --- Content.Shared/Chemistry/ReagentUnit.cs | 55 ++++++++++++++++--- .../Shared/Chemistry/ReagentUnit_Tests.cs | 32 +++++++++++ .../Shared/Chemistry/Solution_Tests.cs | 35 ++++++++++++ 3 files changed, 115 insertions(+), 7 deletions(-) diff --git a/Content.Shared/Chemistry/ReagentUnit.cs b/Content.Shared/Chemistry/ReagentUnit.cs index 184f7aa5d7..63385fe297 100644 --- a/Content.Shared/Chemistry/ReagentUnit.cs +++ b/Content.Shared/Chemistry/ReagentUnit.cs @@ -1,21 +1,23 @@ 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 + public struct ReagentUnit : ISelfSerialize, IComparable, IEquatable { private int _value; private static readonly int Shift = 2; public static ReagentUnit MaxValue => new ReagentUnit(int.MaxValue); - private decimal ShiftDown() + private double ShiftDown() { - return _value / (decimal)Math.Pow(10, Shift); + return _value / Math.Pow(10, Shift); } private ReagentUnit(int value) @@ -55,7 +57,7 @@ namespace Content.Shared.Chemistry private static float FloatFromString(string value) { - return float.Parse(value); + return float.Parse(value, CultureInfo.InvariantCulture); } public static ReagentUnit operator +(ReagentUnit a) => a; @@ -82,11 +84,22 @@ namespace Content.Shared.Chemistry } 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) @@ -138,14 +151,17 @@ namespace Content.Shared.Chemistry return a._value > b._value; } - public override string ToString() => $"{ShiftDown()}"; - public float Float() { return (float) ShiftDown(); } public decimal Decimal() + { + return (decimal) ShiftDown(); + } + + public double Double() { return ShiftDown(); } @@ -157,7 +173,12 @@ namespace Content.Shared.Chemistry public static ReagentUnit Min(params ReagentUnit[] reagentUnits) { - return reagentUnits.OrderBy(x => x._value).First(); + return reagentUnits.Min(); + } + + public static ReagentUnit Min(ReagentUnit a, ReagentUnit b) + { + return a < b ? a : b; } public override bool Equals(object obj) @@ -176,9 +197,29 @@ namespace Content.Shared.Chemistry _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; + } } } diff --git a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs index 1cae6e1db2..46d391459b 100644 --- a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs +++ b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs @@ -118,5 +118,37 @@ namespace Content.Tests.Shared.Chemistry var result = (int) Math.Round(a * (float) Math.Pow(10, 2)); 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)); + } } } diff --git a/Content.Tests/Shared/Chemistry/Solution_Tests.cs b/Content.Tests/Shared/Chemistry/Solution_Tests.cs index e3024d3a07..2714ce2252 100644 --- a/Content.Tests/Shared/Chemistry/Solution_Tests.cs +++ b/Content.Tests/Shared/Chemistry/Solution_Tests.cs @@ -203,6 +203,41 @@ namespace Content.Tests.Shared.Chemistry 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] + [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() { From 4aa6c5cc69d52068ad7ec321fe5f36c23de93cc7 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Thu, 9 Apr 2020 20:43:45 +0200 Subject: [PATCH 40/54] Extra unit test --- .../Shared/Chemistry/Solution_Tests.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Content.Tests/Shared/Chemistry/Solution_Tests.cs b/Content.Tests/Shared/Chemistry/Solution_Tests.cs index 2714ce2252..f149d66043 100644 --- a/Content.Tests/Shared/Chemistry/Solution_Tests.cs +++ b/Content.Tests/Shared/Chemistry/Solution_Tests.cs @@ -221,6 +221,24 @@ namespace Content.Tests.Shared.Chemistry 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)] From c18f318b806ceaf4e2d2deff4796c96c47337cdf Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Thu, 9 Apr 2020 21:29:34 +0200 Subject: [PATCH 41/54] MidpointRounding --- Content.Shared/Chemistry/ReagentUnit.cs | 6 +++--- Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Content.Shared/Chemistry/ReagentUnit.cs b/Content.Shared/Chemistry/ReagentUnit.cs index 63385fe297..43c2354f03 100644 --- a/Content.Shared/Chemistry/ReagentUnit.cs +++ b/Content.Shared/Chemistry/ReagentUnit.cs @@ -32,7 +32,7 @@ namespace Content.Shared.Chemistry public static ReagentUnit New(decimal value) { - return new ReagentUnit((int) Math.Round(value * (decimal) Math.Pow(10, Shift))); + return new ReagentUnit((int) Math.Round(value * (decimal) Math.Pow(10, Shift), MidpointRounding.AwayFromZero)); } public static ReagentUnit New(float value) @@ -42,12 +42,12 @@ namespace Content.Shared.Chemistry private static int FromFloat(float value) { - return (int) Math.Round(value * (float) Math.Pow(10, Shift)); + 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))); + return new ReagentUnit((int) Math.Round(value * Math.Pow(10, Shift), MidpointRounding.AwayFromZero)); } public static ReagentUnit New(string value) diff --git a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs index 46d391459b..003ecf4663 100644 --- a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs +++ b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs @@ -46,7 +46,7 @@ namespace Content.Tests.Shared.Chemistry } [Test] - [TestCase("1.005", "1")] + [TestCase("1.005", "1.01")] [TestCase("0.999", "1")] public void ReagentUnitStringTests(string value, string expected) { @@ -57,6 +57,7 @@ namespace Content.Tests.Shared.Chemistry [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) { @@ -111,11 +112,11 @@ namespace Content.Tests.Shared.Chemistry [Test] [TestCase(0.995f, 100)] - [TestCase(1.005f, 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)); + var result = (int) Math.Round(a * (float) Math.Pow(10, 2), MidpointRounding.AwayFromZero); Assert.AreEqual(expected, result); } From 503a2a8acfbbb348ec8ee8d664bdbeb583086dc1 Mon Sep 17 00:00:00 2001 From: Injazz Date: Fri, 10 Apr 2020 12:08:33 +0500 Subject: [PATCH 42/54] updates submodule --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index ec52102d02..1cdb279319 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit ec52102d0279281a00cc1c6811330a13ddaf975b +Subproject commit 1cdb279319bdb16efdc9671d0d4e0e5947b0493f From 47c3258861e645f2429ec135629ce537e8f68b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= <6766154+Zumorica@users.noreply.github.com> Date: Fri, 10 Apr 2020 13:28:28 +0200 Subject: [PATCH 43/54] Update Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs Co-Authored-By: Pieter-Jan Briers --- Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs index 1a206d5c50..ddbbcde98d 100644 --- a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs @@ -36,7 +36,7 @@ namespace Content.Server.GameObjects.EntitySystems IsRoundEndCountdownStarted = true; - ExpectedCountdownEnd = _gameTiming.CurTime.Add(new TimeSpan(0,0,0,0,RoundEndCountdownTime)); + ExpectedCountdownEnd = _gameTiming.CurTime + TimeSpan.FromMilliseconds(RoundEndCountdownTime); Timer.Spawn(RoundEndCountdownTime, EndRound, _roundEndCancellationTokenSource.Token); OnRoundEndCountdownStarted?.Invoke(); } From 05e9d39e8928ecebdb5fdad19a049077cb19cd7f Mon Sep 17 00:00:00 2001 From: zumorica Date: Fri, 10 Apr 2020 13:36:30 +0200 Subject: [PATCH 44/54] Fix bug where comms console sometimes took a second to update after a countdown has started --- Content.Client/Command/CommunicationsConsoleMenu.cs | 3 ++- .../Command/CommunicationsConsoleBoundUserInterface.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Content.Client/Command/CommunicationsConsoleMenu.cs b/Content.Client/Command/CommunicationsConsoleMenu.cs index 426ae84977..0a6804264c 100644 --- a/Content.Client/Command/CommunicationsConsoleMenu.cs +++ b/Content.Client/Command/CommunicationsConsoleMenu.cs @@ -5,6 +5,7 @@ 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; @@ -50,7 +51,7 @@ namespace Content.Client.Command Timer.SpawnRepeating(1000, UpdateCountdown, _timerCancelTokenSource.Token); } - private void UpdateCountdown() + public void UpdateCountdown() { if (!Owner.CountdownStarted) { diff --git a/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs b/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs index 9e90200515..c82ded4c7a 100644 --- a/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs @@ -69,7 +69,7 @@ namespace Content.Client.GameObjects.Components.Command _expectedCountdownTime = commsState.ExpectedCountdownEnd; CountdownStarted = commsState.CountdownStarted; - + _menu?.UpdateCountdown(); } From c19035fccc746e50cbcfc36fbb7d88ed799a9975 Mon Sep 17 00:00:00 2001 From: zumorica Date: Fri, 10 Apr 2020 13:37:13 +0200 Subject: [PATCH 45/54] Use TimeSpan instead of an int for the countdown time --- Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs index ddbbcde98d..9077e94184 100644 --- a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs @@ -17,7 +17,7 @@ namespace Content.Server.GameObjects.EntitySystems private CancellationTokenSource _roundEndCancellationTokenSource = new CancellationTokenSource(); public bool IsRoundEndCountdownStarted { get; private set; } - public int RoundEndCountdownTime { get; set; } = 240000; + public TimeSpan RoundEndCountdownTime { get; set; } = TimeSpan.FromMinutes(4); public TimeSpan? ExpectedCountdownEnd = null; public delegate void RoundEndCountdownStarted(); @@ -36,7 +36,7 @@ namespace Content.Server.GameObjects.EntitySystems IsRoundEndCountdownStarted = true; - ExpectedCountdownEnd = _gameTiming.CurTime + TimeSpan.FromMilliseconds(RoundEndCountdownTime); + ExpectedCountdownEnd = _gameTiming.CurTime + RoundEndCountdownTime; Timer.Spawn(RoundEndCountdownTime, EndRound, _roundEndCancellationTokenSource.Token); OnRoundEndCountdownStarted?.Invoke(); } From 683644eec5a2b64e95a85f3c3c186986d3af84f2 Mon Sep 17 00:00:00 2001 From: zumorica Date: Fri, 10 Apr 2020 16:28:14 +0200 Subject: [PATCH 46/54] Ghosts now make use of the new "entity visibility" engine functionality --- .../Components/Observer/GhostComponent.cs | 1 - .../Components/Observer/GhostComponent.cs | 14 ++++++++++++-- Content.Server/GameObjects/VisibilityFlags.cs | 12 ++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 Content.Server/GameObjects/VisibilityFlags.cs diff --git a/Content.Client/GameObjects/Components/Observer/GhostComponent.cs b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs index 67b81a6345..21403b3a76 100644 --- a/Content.Client/GameObjects/Components/Observer/GhostComponent.cs +++ b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs @@ -34,7 +34,6 @@ namespace Content.Client.GameObjects.Components.Observer private void SetGhostVisibility(bool visibility) { - // So, for now this is a client-side hack... Please, PLEASE someone make this work server-side. foreach (var ghost in _componentManager.GetAllComponents(typeof(GhostComponent))) { if (ghost.Owner.TryGetComponent(out SpriteComponent component)) diff --git a/Content.Server/GameObjects/Components/Observer/GhostComponent.cs b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs index 4853900fed..6adc21ed33 100644 --- a/Content.Server/GameObjects/Components/Observer/GhostComponent.cs +++ b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs @@ -2,6 +2,7 @@ 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; @@ -28,6 +29,13 @@ namespace Content.Server.GameObjects.Components.Observer } } + public override void Initialize() + { + base.Initialize(); + + Owner.EnsureComponent().Layer = (int)VisibilityFlags.Ghost; + } + public override ComponentState GetComponentState() => new GhostComponentState(CanReturnToBody); public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, @@ -44,10 +52,12 @@ namespace Content.Server.GameObjects.Components.Observer actor.playerSession.ContentData().Mind.UnVisit(); } break; - case PlayerAttachedMsg _: + case PlayerAttachedMsg msg: + msg.NewPlayer.VisibilityMask |= (int)VisibilityFlags.Ghost; Dirty(); break; - case PlayerDetachedMsg _: + case PlayerDetachedMsg msg: + msg.OldPlayer.VisibilityMask &= ~(int)VisibilityFlags.Ghost; Timer.Spawn(100, Owner.Delete); break; default: diff --git a/Content.Server/GameObjects/VisibilityFlags.cs b/Content.Server/GameObjects/VisibilityFlags.cs new file mode 100644 index 0000000000..d5e3ccb7fa --- /dev/null +++ b/Content.Server/GameObjects/VisibilityFlags.cs @@ -0,0 +1,12 @@ +using System; + +namespace Content.Server.GameObjects +{ + [Flags] + public enum VisibilityFlags + { + None = 0, + Normal = 1, + Ghost = 2, + } +} From c41eb3ba10ca08d7266a2cbf83cc3977c663af28 Mon Sep 17 00:00:00 2001 From: zumorica Date: Fri, 10 Apr 2020 17:13:55 +0200 Subject: [PATCH 47/54] Remove unused visibility flags --- Content.Server/GameObjects/VisibilityFlags.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Content.Server/GameObjects/VisibilityFlags.cs b/Content.Server/GameObjects/VisibilityFlags.cs index d5e3ccb7fa..4acb9c988a 100644 --- a/Content.Server/GameObjects/VisibilityFlags.cs +++ b/Content.Server/GameObjects/VisibilityFlags.cs @@ -5,8 +5,6 @@ namespace Content.Server.GameObjects [Flags] public enum VisibilityFlags { - None = 0, - Normal = 1, Ghost = 2, } } From 5c6d045f3e627be280d5ff6d3de9ef1f257a9ab8 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 12 Apr 2020 00:58:06 +0200 Subject: [PATCH 48/54] Update submodule --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 1cdb279319..3d6a3a6216 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 1cdb279319bdb16efdc9671d0d4e0e5947b0493f +Subproject commit 3d6a3a6216fa752f422a0f1720e02fec8e8e145e From 56d67200263aea4751879693c15f0dcb7200a97e Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 12 Apr 2020 01:15:16 +0200 Subject: [PATCH 49/54] Move part of stack code to shared. Meaning that ExposeData is in shared, fixing #809 --- .../GameObjects/Components/StackComponent.cs | 19 ++-- .../Construction/ConstructionComponent.cs | 4 +- .../Components/Stack/StackComponent.cs | 84 +------------- .../Components/SharedStackComponent.cs | 105 +++++++++++++++++- 4 files changed, 119 insertions(+), 93 deletions(-) diff --git a/Content.Client/GameObjects/Components/StackComponent.cs b/Content.Client/GameObjects/Components/StackComponent.cs index c24b1787a5..af5dfe3e8d 100644 --- a/Content.Client/GameObjects/Components/StackComponent.cs +++ b/Content.Client/GameObjects/Components/StackComponent.cs @@ -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 diff --git a/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs b/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs index f7f3b81bbd..be64cfb14f 100644 --- a/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs +++ b/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs @@ -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); } - + } diff --git a/Content.Server/GameObjects/Components/Stack/StackComponent.cs b/Content.Server/GameObjects/Components/Stack/StackComponent.cs index 9aaa605f1c..3b4bae15c5 100644 --- a/Content.Server/GameObjects/Components/Stack/StackComponent.cs +++ b/Content.Server/GameObjects/Components/Stack/StackComponent.cs @@ -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(); - 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(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 } } diff --git a/Content.Shared/GameObjects/Components/SharedStackComponent.cs b/Content.Shared/GameObjects/Components/SharedStackComponent.cs index 3a141d4d4d..9ceb2f69b7 100644 --- a/Content.Shared/GameObjects/Components/SharedStackComponent.cs +++ b/Content.Shared/GameObjects/Components/SharedStackComponent.cs @@ -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(); + 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 + } } From d261a21b5772087c15aded0831fac78d7b7bc90b Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sun, 12 Apr 2020 14:44:14 +0200 Subject: [PATCH 50/54] Fix up SolutionComponent --- .../Components/Chemistry/SolutionComponent.cs | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index e6a9713029..7e56d748ab 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -1,15 +1,8 @@ using Content.Server.Chemistry; -using System; -using System.Collections.Generic; -using System.ComponentModel.Design; -using System.Linq; -using Content.Server.Chemistry; -using Content.Shared.GameObjects.Components.Chemistry; -using Content.Server.GameObjects.Components.Nutrition; using Content.Server.GameObjects.EntitySystems; using Content.Shared.Chemistry; using Content.Shared.GameObjects; -using Content.Shared.Interfaces.Chemistry; +using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.Utility; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; @@ -21,9 +14,9 @@ using Robust.Shared.Maths; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; -using System; -using System.Collections.Generic; using Robust.Shared.ViewVariables; +using System.Collections.Generic; +using System.Linq; namespace Content.Server.GameObjects.Components.Chemistry { @@ -46,7 +39,7 @@ namespace Content.Server.GameObjects.Components.Chemistry private SpriteComponent _spriteComponent; private Solution _containedSolution = new Solution(); - private int _maxVolume; + private ReagentUnit _maxVolume; private SolutionCaps _capabilities; private string _fillInitState; private int _fillInitSteps; @@ -58,7 +51,7 @@ namespace Content.Server.GameObjects.Components.Chemistry /// The maximum volume of the container. /// [ViewVariables(VVAccess.ReadWrite)] - public int MaxVolume + public ReagentUnit MaxVolume { get => _maxVolume; set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced. @@ -68,13 +61,13 @@ namespace Content.Server.GameObjects.Components.Chemistry /// The total volume of all the of the reagents in the container. /// [ViewVariables] - public int CurrentVolume => _containedSolution.TotalVolume; + public ReagentUnit CurrentVolume => _containedSolution.TotalVolume; /// /// The volume without reagents remaining in the container. /// [ViewVariables] - public int EmptyVolume => MaxVolume - CurrentVolume; + public ReagentUnit EmptyVolume => MaxVolume - CurrentVolume; /// /// The current blended color of all the reagents in the container. @@ -123,7 +116,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { base.ExposeData(serializer); - serializer.DataField(ref _maxVolume, "maxVol", 0); + 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", ""); @@ -158,7 +151,7 @@ namespace Content.Server.GameObjects.Components.Chemistry OnSolutionChanged(false); } - public bool TryRemoveReagent(string reagentId, int quantity) + public bool TryRemoveReagent(string reagentId, ReagentUnit quantity) { if (!ContainsReagent(reagentId, out var currentQuantity)) return false; @@ -172,7 +165,7 @@ namespace Content.Server.GameObjects.Components.Chemistry /// /// Quantity of this solution to remove /// Whether or not the solution was successfully removed - public bool TryRemoveSolution(int quantity) + public bool TryRemoveSolution(ReagentUnit quantity) { if (CurrentVolume == 0) return false; @@ -182,7 +175,7 @@ namespace Content.Server.GameObjects.Components.Chemistry return true; } - public Solution SplitSolution(int quantity) + public Solution SplitSolution(ReagentUnit quantity) { var solutionSplit = _containedSolution.SplitSolution(quantity); OnSolutionChanged(false); @@ -198,7 +191,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } Color mixColor = default; - float runningTotalQuantity = 0; + var runningTotalQuantity = ReagentUnit.New(0); foreach (var reagent in _containedSolution) { @@ -209,7 +202,7 @@ namespace Content.Server.GameObjects.Components.Chemistry if (mixColor == default) mixColor = proto.SubstanceColor; mixColor = Color.InterpolateBetween(mixColor, proto.SubstanceColor, - (1 / runningTotalQuantity) * reagent.Quantity); + (1 / runningTotalQuantity.Float()) * reagent.Quantity.Float()); } SubstanceColor = mixColor; @@ -389,7 +382,7 @@ namespace Content.Server.GameObjects.Components.Chemistry public bool TryAddReagent(string reagentId, ReagentUnit quantity, out ReagentUnit acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) { - var toAcceptQuantity = MaxVolume - ContainedSolution.TotalVolume; + var toAcceptQuantity = MaxVolume - _containedSolution.TotalVolume; if (quantity > toAcceptQuantity) { acceptedQuantity = toAcceptQuantity; @@ -400,7 +393,7 @@ namespace Content.Server.GameObjects.Components.Chemistry acceptedQuantity = quantity; } - ContainedSolution.AddReagent(reagentId, acceptedQuantity); + _containedSolution.AddReagent(reagentId, acceptedQuantity); if (!skipColor) { RecalculateColor(); } @@ -412,10 +405,10 @@ namespace Content.Server.GameObjects.Components.Chemistry 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); + _containedSolution.AddSolution(solution); if (!skipColor) { RecalculateColor(); } @@ -496,7 +489,7 @@ namespace Content.Server.GameObjects.Components.Chemistry /// The reagent to check for. /// Output the quantity of the reagent if it is contained, 0 if it isn't. /// Return true if the solution contains the reagent. - public bool ContainsReagent(string reagentId, out int quantity) + public bool ContainsReagent(string reagentId, out ReagentUnit quantity) { foreach (var reagent in _containedSolution.Contents) { @@ -506,7 +499,7 @@ namespace Content.Server.GameObjects.Components.Chemistry return true; } } - quantity = 0; + quantity = ReagentUnit.New(0); return false; } @@ -524,7 +517,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { if (string.IsNullOrEmpty(_fillInitState)) return; - var percentage = (double)CurrentVolume / MaxVolume; + var percentage = (CurrentVolume / MaxVolume).Double(); var level = ContentHelpers.RoundToLevels(percentage * 100, 100, _fillInitSteps); //Transformed glass uses special fancy sprites so we don't bother From e9100db219b5f7294b44562f883bd86ccfa7be66 Mon Sep 17 00:00:00 2001 From: zumorica Date: Mon, 13 Apr 2020 01:44:05 +0200 Subject: [PATCH 51/54] LightBulbs now break when thrown --- .../Components/Power/LightBulbComponent.cs | 28 +++++++++++++++++- Resources/Audio/effects/glassbreak1.ogg | Bin 0 -> 19725 bytes Resources/Audio/effects/glassbreak2.ogg | Bin 0 -> 15999 bytes Resources/Audio/effects/glassbreak3.ogg | Bin 0 -> 27378 bytes .../SoundCollections/glassbreak.yml | 6 ++++ 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 Resources/Audio/effects/glassbreak1.ogg create mode 100644 Resources/Audio/effects/glassbreak2.ogg create mode 100644 Resources/Audio/effects/glassbreak3.ogg create mode 100644 Resources/Prototypes/SoundCollections/glassbreak.yml diff --git a/Content.Server/GameObjects/Components/Power/LightBulbComponent.cs b/Content.Server/GameObjects/Components/Power/LightBulbComponent.cs index 9316dd6be9..f8fde7b236 100644 --- a/Content.Server/GameObjects/Components/Power/LightBulbComponent.cs +++ b/Content.Server/GameObjects/Components/Power/LightBulbComponent.cs @@ -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. /// [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 + /// /// Invoked whenever the state of the light bulb changes. /// @@ -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("glassbreak"); + var file = _random.Pick(soundCollection.PickFiles); + + IoCManager.Resolve().GetEntitySystem().Play(file, Owner); + + State = LightBulbState.Broken; + } } } diff --git a/Resources/Audio/effects/glassbreak1.ogg b/Resources/Audio/effects/glassbreak1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..fe14cdeda3415a8934a11ae50bba11b14f4f016a GIT binary patch literal 19725 zcmagG1zc56w=g`1?gr^l8bP`n3`$zMOS(fO4g!jZNJ&d~HwXwvxOVdgWj27nQ>}GE3e%k<( zFS-4Jk~i${PXkQ#w&#DP+n%?O6)oRTf`>Q%kJO9&43O)hndq&pysg~m-Je@IIJmRjQh}lVgM>|1?imbVKrKAV z^qugLC~W`;05D}_!;7_6V9SlmWPOns_vE(L$^AAl?rkTzMFdaRzYBVPOHu$(0Gt&w zGJ93QZb-z2oF&pNL&Wx}a6UbHxcZ_9X4?_7g;QQlp1so$8xsbu9x8x1B>jXTltkmU zFb`vRO%4@rsNH%P@>AWg*b6dVVe$6R2P^Y#3CU9d3jEG`RgTy8Z`LpHG~>`4>dXoH6wrhuYBsi zaB`dfPMsbGGVY1QFSBLcrOf&%l7$@dx&#SW-m-)qGv+Q^OtwOvwOyHQO5O9~I>-L1 z`~6ioe{TUW=$6}*VVj)y|H{^S@mBx$Ed9KP9Y{mG>~^K@c4d61!PxC~AN?y%0AvILF)sblfGT+*PI^ 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 FA^;qSh8q9? literal 0 HcmV?d00001 diff --git a/Resources/Audio/effects/glassbreak2.ogg b/Resources/Audio/effects/glassbreak2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..79163acc19e94abe74eb14b1a9d6d28ecfbd6514 GIT binary patch literal 15999 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 T%fajXWGAL{fa1ZTDENN>;$eTG literal 0 HcmV?d00001 diff --git a/Resources/Audio/effects/glassbreak3.ogg b/Resources/Audio/effects/glassbreak3.ogg new file mode 100644 index 0000000000000000000000000000000000000000..bc45ab333240d76ab1edfab8073f668286b515da GIT binary patch literal 27378 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 literal 0 HcmV?d00001 diff --git a/Resources/Prototypes/SoundCollections/glassbreak.yml b/Resources/Prototypes/SoundCollections/glassbreak.yml new file mode 100644 index 0000000000..ee3d9278bb --- /dev/null +++ b/Resources/Prototypes/SoundCollections/glassbreak.yml @@ -0,0 +1,6 @@ +- type: sound_collection + id: glassbreak + files: + - /Audio/effects/glassbreak1.ogg + - /Audio/effects/glassbreak2.ogg + - /Audio/effects/glassbreak3.ogg From 7def5e7d6ce1b3cad5c15538adb6cdc7287d995b Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 13 Apr 2020 22:26:05 +0200 Subject: [PATCH 52/54] Remove seemingly-unecessary DetachEntity() in GameScreenBase. --- Content.Client/State/GameScreenBase.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Content.Client/State/GameScreenBase.cs b/Content.Client/State/GameScreenBase.cs index 4cb8896e26..3e277799dd 100644 --- a/Content.Client/State/GameScreenBase.cs +++ b/Content.Client/State/GameScreenBase.cs @@ -43,8 +43,6 @@ namespace Content.Client.State public override void Shutdown() { - _playerManager.LocalPlayer.DetachEntity(); - _inputManager.KeyBindStateChanged -= OnKeyBindStateChanged; } From a4092acf5b313ace94a6910c9a15252e9c42b2be Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 13 Apr 2020 22:27:47 +0200 Subject: [PATCH 53/54] Fix not returning to main menu on disconnect correctly. --- Content.Client/EntryPoint.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 1cc3c720a0..ff63ab58ad 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -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; @@ -227,6 +228,14 @@ namespace Content.Client IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); + _baseClient.RunLevelChanged += (sender, args) => + { + if (args.NewLevel == ClientRunLevel.Initialize) + { + _stateManager.RequestStateChange(); + } + }; + // Fire off into state dependent on launcher or not. if (_gameController.LaunchState.FromLauncher) { From ec314545ef373d5cde5913b392dbc98f5a77576e Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 13 Apr 2020 22:28:05 +0200 Subject: [PATCH 54/54] Fix ClientStatusEffectsComponent not cleaning up GUI on shutdown correctly. --- .../Components/Mobs/ClientStatusEffectsComponent.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs b/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs index 0e915772a4..e3edf3d34b 100644 --- a/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs +++ b/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs @@ -76,10 +76,6 @@ namespace Content.Client.GameObjects.Components.Mobs private void PlayerDetached() { - if (!CurrentlyControlled) - { - return; - } _ui?.Dispose(); _ui = null; }