From 6905394e8a39bcd3dbcc2c769d7c8c345e594737 Mon Sep 17 00:00:00 2001 From: zumorica Date: Tue, 3 Mar 2020 18:04:16 +0100 Subject: [PATCH 001/103] 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 002/103] 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 003/103] 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 004/103] 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 005/103] 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 006/103] 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 007/103] 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 008/103] 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 d645b1470e483620e84bd4c332a870da52024687 Mon Sep 17 00:00:00 2001 From: JiimBob <44332255+JiimBob@users.noreply.github.com> Date: Sat, 21 Mar 2020 15:37:22 -0400 Subject: [PATCH 009/103] Added power checks Added power checks to: Reagent Dispenser Medical Scanner Lathes Research Console Vending Machines Added power device component to: chem dispenser prototype medical scanner prototype Fixes for #706 --- .../Chemistry/ReagentDispenserComponent.cs | 13 ++++ .../Medical/MedicalScannerComponent.cs | 10 ++++ .../Components/Research/LatheComponent.cs | 60 ++++++++++--------- .../Research/ResearchConsoleComponent.cs | 2 + .../VendingMachineComponent.cs | 5 ++ .../Entities/buildings/chem_dispenser.yml | 1 + .../Entities/buildings/medical_scanner.yml | 2 +- 7 files changed, 64 insertions(+), 29 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs index fa3a65e6c5..6fb12aa337 100644 --- a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs @@ -2,6 +2,7 @@ using System.Linq; using Content.Server.GameObjects.Components.Sound; using Content.Server.GameObjects.EntitySystems; +using Content.Server.GameObjects.Components.Power; using Content.Server.Interfaces; using Content.Server.Interfaces.GameObjects; using Content.Shared.Chemistry; @@ -46,6 +47,11 @@ namespace Content.Server.GameObjects.Components.Chemistry [ViewVariables] private SolutionComponent Solution => _beakerContainer.ContainedEntity.GetComponent(); + ///implementing PowerDeviceComponent + private PowerDeviceComponent _powerDevice; + private bool Powered => _powerDevice.Powered; + + /// /// Shows the serializer how to save/load this components yaml prototype. /// @@ -70,6 +76,7 @@ namespace Content.Server.GameObjects.Components.Chemistry _beakerContainer = ContainerManagerComponent.Ensure($"{Name}-reagentContainerContainer", Owner); + _powerDevice = Owner.GetComponent(); InitializeFromPrototype(); UpdateUserInterface(); @@ -159,6 +166,9 @@ namespace Content.Server.GameObjects.Components.Chemistry //Check if player can interact in their current state if (!ActionBlockerSystem.CanInteract(playerEntity) || !ActionBlockerSystem.CanUse(playerEntity)) return false; + //Check if device is powered + if (!Powered) + return false; return true; } @@ -251,6 +261,9 @@ namespace Content.Server.GameObjects.Components.Chemistry return; } + if (!Powered) + return; + var activeHandEntity = hands.GetActiveHand?.Owner; if (activeHandEntity == null) { diff --git a/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs b/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs index f27144bb0b..2ea653ff69 100644 --- a/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs +++ b/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Medical; +using Content.Server.GameObjects.Components.Power; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.Container; using Robust.Server.GameObjects.Components.UserInterface; @@ -24,6 +25,10 @@ namespace Content.Server.GameObjects.Components.Medical private readonly Vector2 _ejectOffset = new Vector2(-0.5f, 0f); public bool IsOccupied => _bodyContainer.ContainedEntity != null; + ///implementing PowerDeviceComponent + private PowerDeviceComponent _powerDevice; + private bool Powered => _powerDevice.Powered; + public override void Initialize() { base.Initialize(); @@ -31,6 +36,7 @@ namespace Content.Server.GameObjects.Components.Medical _userInterface = Owner.GetComponent() .GetBoundUserInterface(MedicalScannerUiKey.Key); _bodyContainer = ContainerManagerComponent.Ensure($"{Name}-bodyContainer", Owner); + _powerDevice = Owner.GetComponent(); UpdateUserInterface(); } @@ -79,6 +85,8 @@ namespace Content.Server.GameObjects.Components.Medical private void UpdateUserInterface() { + if (!Powered) + return; var newState = GetUserInterfaceState(); _userInterface.SetState(newState); } @@ -112,6 +120,8 @@ namespace Content.Server.GameObjects.Components.Medical { return; } + if (!Powered) + return; _userInterface.Open(actor.playerSession); } diff --git a/Content.Server/GameObjects/Components/Research/LatheComponent.cs b/Content.Server/GameObjects/Components/Research/LatheComponent.cs index f1020eb091..f06967b592 100644 --- a/Content.Server/GameObjects/Components/Research/LatheComponent.cs +++ b/Content.Server/GameObjects/Components/Research/LatheComponent.cs @@ -62,38 +62,42 @@ namespace Content.Server.GameObjects.Components.Research private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message) { - switch (message.Message) - { - case LatheQueueRecipeMessage msg: - _prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype recipe); - if (recipe != null) - for (var i = 0; i < msg.Quantity; i++) - { - Queue.Enqueue(recipe); - _userInterface.SendMessage(new LatheFullQueueMessage(GetIDQueue())); - } - break; - case LatheSyncRequestMessage msg: - if (!Owner.TryGetComponent(out MaterialStorageComponent storage)) return; - _userInterface.SendMessage(new LatheFullQueueMessage(GetIDQueue())); - if (_producingRecipe != null) - _userInterface.SendMessage(new LatheProducingRecipeMessage(_producingRecipe.ID)); - break; + if (!Powered) + return; + switch (message.Message) + { + case LatheQueueRecipeMessage msg: + _prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype recipe); + if (recipe != null) + for (var i = 0; i < msg.Quantity; i++) + { + Queue.Enqueue(recipe); + _userInterface.SendMessage(new LatheFullQueueMessage(GetIDQueue())); + } + break; + case LatheSyncRequestMessage msg: + if (!Owner.TryGetComponent(out MaterialStorageComponent storage)) return; + _userInterface.SendMessage(new LatheFullQueueMessage(GetIDQueue())); + if (_producingRecipe != null) + _userInterface.SendMessage(new LatheProducingRecipeMessage(_producingRecipe.ID)); + break; - case LatheServerSelectionMessage msg: - if (!Owner.TryGetComponent(out ResearchClientComponent researchClient)) return; - researchClient.OpenUserInterface(message.Session); - break; + case LatheServerSelectionMessage msg: + if (!Owner.TryGetComponent(out ResearchClientComponent researchClient)) return; + researchClient.OpenUserInterface(message.Session); + break; - case LatheServerSyncMessage msg: - if (!Owner.TryGetComponent(out TechnologyDatabaseComponent database) - || !Owner.TryGetComponent(out ProtolatheDatabaseComponent protoDatabase)) return; + case LatheServerSyncMessage msg: + if (!Owner.TryGetComponent(out TechnologyDatabaseComponent database) + || !Owner.TryGetComponent(out ProtolatheDatabaseComponent protoDatabase)) return; - if(database.SyncWithServer()) - protoDatabase.Sync(); + if (database.SyncWithServer()) + protoDatabase.Sync(); + + break; + } + - break; - } } internal bool Produce(LatheRecipePrototype recipe) diff --git a/Content.Server/GameObjects/Components/Research/ResearchConsoleComponent.cs b/Content.Server/GameObjects/Components/Research/ResearchConsoleComponent.cs index e3a2d4be72..9b10ad832a 100644 --- a/Content.Server/GameObjects/Components/Research/ResearchConsoleComponent.cs +++ b/Content.Server/GameObjects/Components/Research/ResearchConsoleComponent.cs @@ -46,6 +46,8 @@ namespace Content.Server.GameObjects.Components.Research private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message) { if (!Owner.TryGetComponent(out TechnologyDatabaseComponent database)) return; + if (!Powered) + return; switch (message.Message) { diff --git a/Content.Server/GameObjects/Components/VendingMachines/VendingMachineComponent.cs b/Content.Server/GameObjects/Components/VendingMachines/VendingMachineComponent.cs index 2284d14596..1a9823d8ae 100644 --- a/Content.Server/GameObjects/Components/VendingMachines/VendingMachineComponent.cs +++ b/Content.Server/GameObjects/Components/VendingMachines/VendingMachineComponent.cs @@ -47,6 +47,8 @@ namespace Content.Server.GameObjects.Components.VendingMachines { return; } + if (!Powered) + return; var wires = Owner.GetComponent(); if (wires.IsPanelOpen) @@ -121,6 +123,9 @@ namespace Content.Server.GameObjects.Components.VendingMachines private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg) { + if (!Powered) + return; + var message = serverMsg.Message; switch (message) { diff --git a/Resources/Prototypes/Entities/buildings/chem_dispenser.yml b/Resources/Prototypes/Entities/buildings/chem_dispenser.yml index da79842a68..a72263b277 100644 --- a/Resources/Prototypes/Entities/buildings/chem_dispenser.yml +++ b/Resources/Prototypes/Entities/buildings/chem_dispenser.yml @@ -10,6 +10,7 @@ texture: Buildings/chemicals.rsi/industrial_dispenser.png - type: ReagentDispenser pack: ChemDispenserStandardInventory + - type: PowerDevice - type: reagentDispenserInventory id: ChemDispenserStandardInventory diff --git a/Resources/Prototypes/Entities/buildings/medical_scanner.yml b/Resources/Prototypes/Entities/buildings/medical_scanner.yml index c583a1a12a..9ad614ce7b 100644 --- a/Resources/Prototypes/Entities/buildings/medical_scanner.yml +++ b/Resources/Prototypes/Entities/buildings/medical_scanner.yml @@ -11,7 +11,7 @@ map: ["enum.MedicalScannerVisualLayers.Machine"] - state: scanner_terminal_blue map: ["enum.MedicalScannerVisualLayers.Terminal"] - + - type: PowerDevice - type: Icon sprite: Buildings/medical_scanner.rsi state: scanner_open From 5536ef2d2b7bdd1f34e39e4647db4d4dbc1af4a8 Mon Sep 17 00:00:00 2001 From: JiimBob <44332255+JiimBob@users.noreply.github.com> Date: Sun, 22 Mar 2020 19:34:38 -0400 Subject: [PATCH 010/103] Update CargoConsoleComponent.cs --- .../Components/Cargo/CargoConsoleComponent.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs b/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs index b5e3fd4564..3b555c725f 100644 --- a/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs +++ b/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs @@ -1,6 +1,7 @@ using Content.Server.Cargo; using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects.Components.Cargo; +using Content.Server.GameObjects.Components.Power; using Content.Shared.Prototypes.Cargo; using Robust.Server.GameObjects.Components.UserInterface; using Robust.Server.Interfaces.GameObjects; @@ -40,6 +41,9 @@ namespace Content.Server.GameObjects.Components.Cargo private bool _requestOnly = false; + private PowerDeviceComponent _powerDevice; + private bool Powered => _powerDevice.Powered; + public override void Initialize() { base.Initialize(); @@ -47,6 +51,7 @@ namespace Content.Server.GameObjects.Components.Cargo Orders = Owner.GetComponent(); _userInterface = Owner.GetComponent().GetBoundUserInterface(CargoConsoleUiKey.Key); _userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage; + _powerDevice = Owner.GetComponent(); _galacticBankManager.AddComponent(this); BankId = 0; } @@ -66,6 +71,8 @@ namespace Content.Server.GameObjects.Components.Cargo var message = serverMsg.Message; if (!Orders.ConnectedToDatabase) return; + if (!Powered) + return; switch (message) { case CargoConsoleAddOrderMessage msg: @@ -119,6 +126,8 @@ namespace Content.Server.GameObjects.Components.Cargo { return; } + if (!Powered) + return; _userInterface.Open(actor.playerSession); } From 65d1681cb3fb7a67bbb5e8615487fc667397fedf Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Wed, 25 Mar 2020 22:21:29 +0100 Subject: [PATCH 011/103] Fix error about game.diagonalmovement being registered multiple times. --- .../Components/Movement/PlayerInputMoverComponent.cs | 6 ------ Content.Server/GameObjects/EntitySystems/MoverSystem.cs | 5 +++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs b/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs index 26d056ac17..969b071bd8 100644 --- a/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs @@ -104,12 +104,6 @@ namespace Content.Server.GameObjects.Components.Movement /// [ViewVariables] public bool DiagonalMovementEnabled => _configurationManager.GetCVar("game.diagonalmovement"); - public override void Initialize() - { - base.Initialize(); - _configurationManager.RegisterCVar("game.diagonalmovement", true, CVar.ARCHIVE); - } - /// public override void OnAdd() { diff --git a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs index a2524b6204..5860ac4e35 100644 --- a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs @@ -11,10 +11,12 @@ using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Timing; +using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components.Transform; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Input; +using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects.Components; using Robust.Shared.Interfaces.Map; @@ -38,6 +40,7 @@ namespace Content.Server.GameObjects.EntitySystems [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager; [Dependency] private readonly IMapManager _mapManager; [Dependency] private readonly IRobustRandom _robustRandom; + [Dependency] private readonly IConfigurationManager _configurationManager; #pragma warning restore 649 private AudioSystem _audioSystem; @@ -78,6 +81,8 @@ namespace Content.Server.GameObjects.EntitySystems SubscribeLocalEvent(PlayerDetached); _audioSystem = EntitySystemManager.GetEntitySystem(); + + _configurationManager.RegisterCVar("game.diagonalmovement", true, CVar.ARCHIVE); } private static void PlayerAttached(PlayerAttachSystemMessage ev) From 006d0685ac6ddb0f1c101d2fde6133c6ee692588 Mon Sep 17 00:00:00 2001 From: JiimBob <44332255+JiimBob@users.noreply.github.com> Date: Wed, 25 Mar 2020 19:58:59 -0400 Subject: [PATCH 012/103] LatheComponent indentation fix Corrected indentation for the switch case. --- .../Components/Research/LatheComponent.cs | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/Content.Server/GameObjects/Components/Research/LatheComponent.cs b/Content.Server/GameObjects/Components/Research/LatheComponent.cs index f06967b592..b97cc3a7cb 100644 --- a/Content.Server/GameObjects/Components/Research/LatheComponent.cs +++ b/Content.Server/GameObjects/Components/Research/LatheComponent.cs @@ -64,38 +64,39 @@ namespace Content.Server.GameObjects.Components.Research { if (!Powered) return; - switch (message.Message) - { - case LatheQueueRecipeMessage msg: - _prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype recipe); - if (recipe != null) - for (var i = 0; i < msg.Quantity; i++) - { - Queue.Enqueue(recipe); - _userInterface.SendMessage(new LatheFullQueueMessage(GetIDQueue())); - } - break; - case LatheSyncRequestMessage msg: - if (!Owner.TryGetComponent(out MaterialStorageComponent storage)) return; - _userInterface.SendMessage(new LatheFullQueueMessage(GetIDQueue())); - if (_producingRecipe != null) - _userInterface.SendMessage(new LatheProducingRecipeMessage(_producingRecipe.ID)); - break; - case LatheServerSelectionMessage msg: - if (!Owner.TryGetComponent(out ResearchClientComponent researchClient)) return; - researchClient.OpenUserInterface(message.Session); - break; + switch (message.Message) + { + case LatheQueueRecipeMessage msg: + _prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype recipe); + if (recipe != null) + for (var i = 0; i < msg.Quantity; i++) + { + Queue.Enqueue(recipe); + _userInterface.SendMessage(new LatheFullQueueMessage(GetIDQueue())); + } + break; + case LatheSyncRequestMessage msg: + if (!Owner.TryGetComponent(out MaterialStorageComponent storage)) return; + _userInterface.SendMessage(new LatheFullQueueMessage(GetIDQueue())); + if (_producingRecipe != null) + _userInterface.SendMessage(new LatheProducingRecipeMessage(_producingRecipe.ID)); + break; - case LatheServerSyncMessage msg: - if (!Owner.TryGetComponent(out TechnologyDatabaseComponent database) - || !Owner.TryGetComponent(out ProtolatheDatabaseComponent protoDatabase)) return; + case LatheServerSelectionMessage msg: + if (!Owner.TryGetComponent(out ResearchClientComponent researchClient)) return; + researchClient.OpenUserInterface(message.Session); + break; - if (database.SyncWithServer()) - protoDatabase.Sync(); + case LatheServerSyncMessage msg: + if (!Owner.TryGetComponent(out TechnologyDatabaseComponent database) + || !Owner.TryGetComponent(out ProtolatheDatabaseComponent protoDatabase)) return; - break; - } + if (database.SyncWithServer()) + protoDatabase.Sync(); + + break; + } } From 3b06ec8126583cd0c0e59c2a6007cb9a33d0a2df Mon Sep 17 00:00:00 2001 From: Swept Date: Wed, 25 Mar 2020 23:16:38 -0700 Subject: [PATCH 013/103] Fixed pixel aspect ratio on SMES --- .../Textures/Buildings/smes.rsi/smes-oc0.png | Bin 162 -> 127 bytes .../Textures/Buildings/smes.rsi/smes-oc1.png | Bin 171 -> 134 bytes .../Textures/Buildings/smes.rsi/smes-oc2.png | Bin 171 -> 134 bytes .../Textures/Buildings/smes.rsi/smes-og1.png | Bin 146 -> 120 bytes .../Textures/Buildings/smes.rsi/smes-og2.png | Bin 181 -> 157 bytes .../Textures/Buildings/smes.rsi/smes-og3.png | Bin 198 -> 179 bytes .../Textures/Buildings/smes.rsi/smes-og4.png | Bin 230 -> 220 bytes .../Textures/Buildings/smes.rsi/smes-og5.png | Bin 255 -> 249 bytes .../Textures/Buildings/smes.rsi/smes-op0.png | Bin 146 -> 115 bytes .../Textures/Buildings/smes.rsi/smes-op1.png | Bin 192 -> 171 bytes .../Textures/Buildings/smes.rsi/smes-op2.png | Bin 146 -> 127 bytes 11 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Textures/Buildings/smes.rsi/smes-oc0.png b/Resources/Textures/Buildings/smes.rsi/smes-oc0.png index 2a8a9cb961aa4fe32964442c33884b99de43533a..2dd8b598ef0d91a97f32cd06eeae71a4c1f78cf3 100644 GIT binary patch delta 105 zcmV-v0G9uv0e_GjV*qn{dh`GQ07gkfK~z|U?b4wQ03ZxR(F>bkIzm@rc9x+SECB-~ zsZ>5-|8adaI?+)1Ue86$59{>P!y$B5Ain4(=00000 LNkvXXu0mjfM{X$Y delta 141 zcmb2D~W-DfwRCPvbaTnfr){kzMb*(fp2O+!4OXu$B>A_$q5qQ9_*ia z^Z-fi^{A7=3^PEZKGnIY2C6zI@e#M2Sjm9y+(f+XwW1fY=MrD;LTi7}r{*77ar s-7)*WsAxW4L~~yN2T--a0|tgq-I7;t_$>w+&%ofx?&{~V&MBb@0LXJTTmS$7 diff --git a/Resources/Textures/Buildings/smes.rsi/smes-oc1.png b/Resources/Textures/Buildings/smes.rsi/smes-oc1.png index 66095d016b8ac1538c24a3c6459c2dc7b796448e..32bfb2c017f491741ce2b2f0a96a7af460a44912 100644 GIT binary patch delta 113 zcmZ3@*v2?PCW~Rw+`1M91_obG7srr{#7Wsl_R5$W z#&1p6>)J5_l_u!#y;QaI?6>TvhO57NRnOl&>59&wYmEOa><)a}R|izETq@-@f9#xj R7iIuC44$rjF6*2UngDs|ES3NO delta 151 zcmV;I0BHY)0jmL!9Df1Tnobe`000SaNLh1yZ~y`T0Dp%8HU&2y00018Nkl|eG zAJ*2Rr7mIt0D!^20I##CGWy|7Wsl_R5$W z#&1p6>)J5_l_u!#y;QaI?6>TvhO57NRnOl&>59&wYmEOa><)a}R|izETq@-@f9#xj R7iIuC44$rjF6*2UngDs|ES3NO delta 151 zcmV;I0BHY)0jmL!9Df1Tnobe`000SaNLh1&pa22@0Dp%8ZY)^~00018Nkl|eG zAJ*2Rr7mIt0D!^20I##CGWy|;28I+*7srr@*0+~7@-`UoxLlNPJIN%Hb3lXVs76l15vDZB z%L_C@Cmjl!Rqd;FKQhe01q9T*CnnZS@cwYp+GsP&bZKq2h`m1EZ&=N~3Dp$4rhPxH pX#ark-+p1efT?Q7{xzInpU*71hvhWd6!?-R3}fr9CtE{-7)hm#W|B0kis z9jwHB8YnNYF)g*=nT*>ov4C0PS51~X4dRi6V9i9kCU NJYD@<);T3K0RT}cHnRW# diff --git a/Resources/Textures/Buildings/smes.rsi/smes-og3.png b/Resources/Textures/Buildings/smes.rsi/smes-og3.png index 30ea3f0722405732cf8a06f99e210b9eddd15201..0716a2643b58848da832b73a5728f6b5652faea4 100644 GIT binary patch delta 159 zcmV;Q0AT;d0kZ*+9De|FdV2H#004SPL_t(oh3(U^4S*mNgyElMZ~zN8&`li5O>_ej z9YB{k8$;~0c+r^L*HCef8~|o!W@cs|OOas`xdbQn$m~bMwIB(dYrfwV-M~m&f;*;H z51^h5Y}CLCK$V93Ylv;2xim08YanhoeM({g000SaNLh1&-~a*u0Dp%8N3omc0001ZNklaGhj!I)`+8E6pVsVFjxUqTV2SFKSoA|Tg2#Lp)0C)b}=X*@dm&Fbpss* g3`kKxWC)A`0KS18{C3gB-v9sr07*qoM6N<$f=ZJ^vj6}9 diff --git a/Resources/Textures/Buildings/smes.rsi/smes-og4.png b/Resources/Textures/Buildings/smes.rsi/smes-og4.png index 6ea122e0f47c45a8a5d55e7f82c1794363435439..ce6b70e2f0c88e1ab3e0a7c1a298669c2dfd2d93 100644 GIT binary patch delta 200 zcmV;(05|{U0o(zQ9De|FdV2H#005&&L_t(oh3%3d4gx_8Mc<&&Bq-dVPzWT3z)etG z0mUu234{xfEDAMTfCP>Im|Zf88PTw&&$Oof%}c>?9LI4S$LX11x$JNs#jfAaUXPlY zfpK_RPId`M`7ysRHE?+j*ImCFzt5Jti%3K@NJ4G>zX6^28bJsfstlMNpe+$d%z+yK z({QBth_(h6nSpDa0lRRB4M?E2_V5E)XrQd$aR5FEJTN)pB9^TH0000eM({g000SaNLh1&&;SAe0Dp%8!hFb^0001(Nkl7CNPc$ z;wTse(1P$k2$P>1&j54c8SM2Dak+68gdXU@BwYslv z5u=BNnhvlO`KExxrN0B5FMT0d4^Z`f0Fm`9!K;;&BLDyZ M07*qoM6N<$f~S8~l>h($ diff --git a/Resources/Textures/Buildings/smes.rsi/smes-og5.png b/Resources/Textures/Buildings/smes.rsi/smes-og5.png index 63579e63abe6ec99aaa4222ee4a08df8887cce00..5b58c2358d1c1c78c3aaffd76e3beca32b49a30e 100644 GIT binary patch delta 229 zcmV%t9De|FdV2H#006*AL_t(oh3%A~3IahCMbA{RDwu4u$z-qy7STU3 z`3ok0!Q`JP7QrH{$+R|;pjE8bvM$?p!!V2RPI*)2z2VHA0mCp1!!V47aY4HYU+R1d z+Pl{96d{yXU%e@?f$^&~`>ju%&pzq_*4M5FvPpl*@xB2_R$hZ=igc}Y%1qqbb1~O0@ f>WKXl8vn`{AoM$)Y88Q+00000NkvXXu0mjf41Z)~ delta 235 zcmVeM({g000SaNLh1&-~a*u0Dp%8N3omc00027NklMsf_m z&@~E1!4L&!fZ5>;_S%4`ywJ=p2BvEnUXhubfwupr#03nqC}a)-kRurWk0ypuFbYP& z-~`ldb0D{!7#VI6qlJZ<_S?MSQh>3KFnpuG0~-D?!PzcE>Y=IwoG*PLSr1hmz)&~9 li4a)fsv#MCYc!7&0|3e=Edu1Ts#5>}002ovPDHLkV1oWLV0r)m diff --git a/Resources/Textures/Buildings/smes.rsi/smes-op0.png b/Resources/Textures/Buildings/smes.rsi/smes-op0.png index 7a6e915b535e57260aa6e1b980104f9f5947ebe1..50bdab855c144a43a240b8864018e4df5682e8ad 100644 GIT binary patch delta 93 zcmV-j0HXhr0dtTXR{(Q*dh`GQ06IxTK~z|U?ao0B03j5_(0ckF)(rm7!)1d5G9l?R z(1IjMk|bRgn85Vn>q~}ICIa)vI|FB;71t9s`DqfMQsd0DRS*p2Mw+ e_H;0F`f35~qY|KuLYGDW00000044HL_t(&f$h_=4Z|=DM9~kglalZXnZg@n7cFMH z%siLIt-A;k;Qt07j|UJWNs=T4w5vY9LNs%z;3v?Bz>-c9D09Of5JzmoEeyVgGDG+fv+OPls002ovPDHLk FV1nnnKLr2) delta 172 zcmV;d08{^~0l)!}9Df1Tnobe`000SaNLh1&@Bjh;0Dp%8I|{1J0001TNkl>t%1<_AdlB|=7%}mA&U=@PRJrGMPIq8jsOaLCOn87yz-R>9W1YraK^dzV($oPFc emut((7%B%S=n_)Pi&ql>0000 Date: Fri, 27 Mar 2020 00:32:24 -0400 Subject: [PATCH 014/103] Added entity integration testing Added integration test that spawns all non-abstract entities to see if anything breaks. --- Content.IntegrationTests/Tests/EntityTest.cs | 81 ++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 Content.IntegrationTests/Tests/EntityTest.cs diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs new file mode 100644 index 0000000000..be5f5c83c5 --- /dev/null +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -0,0 +1,81 @@ +using NUnit.Framework; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Robust.Shared.Log; +using Robust.Server.Interfaces.Maps; +using Robust.Server.Interfaces.Timing; + +namespace Content.IntegrationTests.Tests +{ + [TestFixture] + [TestOf(typeof(Robust.Shared.GameObjects.Entity))] + public class EntityTest : ContentIntegrationTest + { + [Test] + public async Task Test() + { + var server = StartServerDummyTicker(); + await server.WaitIdleAsync(); + var mapMan = server.ResolveDependency(); + var entityMan = server.ResolveDependency(); + var prototypeMan = server.ResolveDependency(); + var mapLoader = server.ResolveDependency(); + var pauseMan = server.ResolveDependency(); + var prototypes = new List(); + IMapGrid grid = default; + IEntity testEntity = null; + + //Build up test environment + server.Post(() => + { + var mapId = mapMan.CreateMap(); + pauseMan.AddUninitializedMap(mapId); + grid = mapLoader.LoadBlueprint(mapId, "Maps/stationstation.yml"); + }); + + server.Assert(() => + { + var testLocation = new GridCoordinates(new Robust.Shared.Maths.Vector2(0, 0), grid); + + //Generate list of non-abstract prototypes to test + foreach (var prototype in prototypeMan.EnumeratePrototypes()) + { + if (prototype.Abstract) + { + continue; + } + prototypes.Add(prototype); + } + + //Iterate list of prototypes to spawn + foreach (var prototype in prototypes) + { + try + { + Logger.LogS(LogLevel.Debug, "EntityTest", "Testing: " + prototype.Name); + testEntity = entityMan.SpawnEntity(prototype.ID, testLocation); + server.RunTicks(2); + Assert.That(testEntity.Initialized); + entityMan.DeleteEntity(testEntity.Uid); + } + + //Fail any exceptions thrown on spawn + catch (Exception e) + { + Logger.LogS(LogLevel.Error, "EntityTest", "Entity '" + testEntity.Name + "' threw: " + e.Message); + Assert.Fail(); + } + } + }); + + await server.WaitIdleAsync(); + } + + } +} From 16353a89e6b2541ad67d5a1eb9487021a4832d1a Mon Sep 17 00:00:00 2001 From: zumorica Date: Mon, 30 Mar 2020 01:15:03 +0200 Subject: [PATCH 015/103] 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 016/103] 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 017/103] 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 018/103] 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 019/103] 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 c282f12edf75a4db4a4896ffd496c59c691acb1f Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 30 Mar 2020 18:34:36 +0200 Subject: [PATCH 020/103] Update submodule. --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index a21bad1f1b..7d6bc63439 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit a21bad1f1b423cd1d000eb6b69293c70614f9f15 +Subproject commit 7d6bc63439382e8c265ff29ed504bd88d2126c52 From ebe5ffe33edf5de2cb9bfb4220102a0712fd4aeb Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 30 Mar 2020 18:34:57 +0200 Subject: [PATCH 021/103] Soundfont is a word. --- SpaceStation14.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 582298cec4..ee76b7776b 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -52,5 +52,6 @@ True True True + True True True From ff36b2dcc7ce5f19490ba89477dce75030e4a31d Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 30 Mar 2020 18:36:58 +0200 Subject: [PATCH 022/103] Re-enable instruments with fallback when MIDI isn't available. --- .../Instruments/InstrumentComponent.cs | 70 ++++++--- Content.Client/Instruments/InstrumentMenu.cs | 38 ++++- .../Entities/buildings/instruments.yml | 140 +++++++++--------- 3 files changed, 152 insertions(+), 96 deletions(-) diff --git a/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs b/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs index 29511ceb4d..80fc60360c 100644 --- a/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs +++ b/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Content.Shared.GameObjects.Components.Instruments; +using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Client.Audio.Midi; using Robust.Shared.Audio.Midi; @@ -28,6 +29,7 @@ namespace Content.Client.GameObjects.Components.Instruments [Dependency] private readonly IGameTiming _timing; #pragma warning restore 649 + [CanBeNull] private IMidiRenderer _renderer; private int _instrumentProgram = 1; @@ -42,8 +44,14 @@ namespace Content.Client.GameObjects.Components.Instruments [ViewVariables(VVAccess.ReadWrite)] public bool LoopMidi { - get => _renderer.LoopMidi; - set => _renderer.LoopMidi = value; + get => _renderer?.LoopMidi ?? false; + set + { + if (_renderer != null) + { + _renderer.LoopMidi = value; + } + } } /// @@ -53,9 +61,13 @@ namespace Content.Client.GameObjects.Components.Instruments public int InstrumentProgram { get => _instrumentProgram; - set { + set + { _instrumentProgram = value; - _renderer.MidiProgram = _instrumentProgram; + if (_renderer != null) + { + _renderer.MidiProgram = _instrumentProgram; + } } } @@ -63,22 +75,26 @@ namespace Content.Client.GameObjects.Components.Instruments /// Whether there's a midi song being played or not. /// [ViewVariables] - public bool IsMidiOpen => _renderer.Status == MidiRendererStatus.File; + public bool IsMidiOpen => _renderer?.Status == MidiRendererStatus.File; /// /// Whether the midi renderer is listening for midi input or not. /// [ViewVariables] - public bool IsInputOpen => _renderer.Status == MidiRendererStatus.Input; + public bool IsInputOpen => _renderer?.Status == MidiRendererStatus.Input; public override void Initialize() { base.Initialize(); IoCManager.InjectDependencies(this); _renderer = _midiManager.GetNewRenderer(); - _renderer.MidiProgram = _instrumentProgram; - _renderer.TrackingEntity = Owner; - _renderer.OnMidiPlayerFinished += () => { OnMidiPlaybackEnded?.Invoke(); }; + + if (_renderer != null) + { + _renderer.MidiProgram = _instrumentProgram; + _renderer.TrackingEntity = Owner; + _renderer.OnMidiPlayerFinished += () => { OnMidiPlaybackEnded?.Invoke(); }; + } } protected override void Shutdown() @@ -93,9 +109,16 @@ namespace Content.Client.GameObjects.Components.Instruments serializer.DataField(ref _instrumentProgram, "program", 1); } - public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) + public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, + IComponent component = null) { base.HandleMessage(message, netChannel, component); + + if (_renderer == null) + { + return; + } + switch (message) { case InstrumentMidiEventMessage midiEventMessage: @@ -107,8 +130,8 @@ namespace Content.Client.GameObjects.Components.Instruments case InstrumentStopMidiMessage _: _renderer.StopAllNotes(); - if(IsInputOpen) CloseInput(); - if(IsMidiOpen) CloseMidi(); + if (IsInputOpen) CloseInput(); + if (IsMidiOpen) CloseMidi(); break; } } @@ -116,7 +139,7 @@ namespace Content.Client.GameObjects.Components.Instruments /// public bool OpenInput() { - if (_renderer.OpenInput()) + if (_renderer != null && _renderer.OpenInput()) { _renderer.OnMidiEvent += RendererOnMidiEvent; return true; @@ -128,28 +151,37 @@ namespace Content.Client.GameObjects.Components.Instruments /// public bool CloseInput() { - if (!_renderer.CloseInput()) return false; + if (_renderer == null || !_renderer.CloseInput()) + { + return false; + } + _renderer.OnMidiEvent -= RendererOnMidiEvent; return true; - } /// public bool OpenMidi(string filename) { - if (!_renderer.OpenMidi(filename)) return false; + if (_renderer == null || !_renderer.OpenMidi(filename)) + { + return false; + } + _renderer.OnMidiEvent += RendererOnMidiEvent; return true; - } /// public bool CloseMidi() { - if (!_renderer.CloseMidi()) return false; + if (_renderer == null || !_renderer.CloseMidi()) + { + return false; + } + _renderer.OnMidiEvent -= RendererOnMidiEvent; return true; - } /// diff --git a/Content.Client/Instruments/InstrumentMenu.cs b/Content.Client/Instruments/InstrumentMenu.cs index b0880fbb48..0fb39bde2e 100644 --- a/Content.Client/Instruments/InstrumentMenu.cs +++ b/Content.Client/Instruments/InstrumentMenu.cs @@ -1,10 +1,13 @@ using Content.Client.GameObjects.Components.Instruments; +using Content.Client.UserInterface; using Robust.Client.Audio.Midi; +using Robust.Client.Graphics.Drawing; using Robust.Client.Interfaces.UserInterface; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.IoC; +using Robust.Shared.Localization; using Robust.Shared.Log; using Robust.Shared.Maths; @@ -27,7 +30,7 @@ namespace Content.Client.Instruments public InstrumentMenu(InstrumentBoundUserInterface owner) { IoCManager.InjectDependencies(this); - Title = "Instrument"; + Title = Loc.GetString("Instrument"); _owner = owner; @@ -55,7 +58,7 @@ namespace Content.Client.Instruments midiInputButton = new Button() { - Text = "MIDI Input", + Text = Loc.GetString("MIDI Input"), TextAlign = Label.AlignMode.Center, SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsStretchRatio = 1, @@ -73,7 +76,7 @@ namespace Content.Client.Instruments var midiFileButton = new Button() { - Text = "Open File", + Text = Loc.GetString("Play MIDI File"), TextAlign = Label.AlignMode.Center, SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsStretchRatio = 1, @@ -91,7 +94,7 @@ namespace Content.Client.Instruments midiLoopButton = new Button() { - Text = "Loop", + Text = Loc.GetString("Loop"), TextAlign = Label.AlignMode.Center, SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsStretchRatio = 1, @@ -110,7 +113,7 @@ namespace Content.Client.Instruments midiStopButton = new Button() { - Text = "Stop", + Text = Loc.GetString("Stop"), TextAlign = Label.AlignMode.Center, SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsStretchRatio = 1, @@ -132,6 +135,26 @@ namespace Content.Client.Instruments margin.AddChild(vBox); + if (!_midiManager.IsAvailable) + { + margin.AddChild(new PanelContainer + { + MouseFilter = MouseFilterMode.Stop, + PanelOverride = new StyleBoxFlat {BackgroundColor = Color.Black.WithAlpha(0.90f)}, + Children = + { + new Label + { + Align = Label.AlignMode.Center, + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SizeFlagsHorizontal = SizeFlags.ShrinkCenter, + StyleClasses = {NanoStyle.StyleClassLabelBig}, + Text = Loc.GetString("MIDI support is currently\nnot available on your platform.") + } + } + }); + } + Contents.AddChild(margin); } @@ -148,7 +171,8 @@ namespace Content.Client.Instruments private async void MidiFileButtonOnOnPressed(BaseButton.ButtonEventArgs obj) { - var filename = await _fileDialogManager.OpenFile(); + var filters = new FileDialogFilters(new FileDialogFilters.Group("mid", "midi")); + var filename = await _fileDialogManager.OpenFile(filters); if (filename == null) return; @@ -160,7 +184,7 @@ namespace Content.Client.Instruments if (!_owner.Instrument.OpenMidi(filename)) return; MidiPlaybackSetButtonsDisabled(false); - if(midiInputButton.Pressed) + if (midiInputButton.Pressed) midiInputButton.Pressed = false; } diff --git a/Resources/Prototypes/Entities/buildings/instruments.yml b/Resources/Prototypes/Entities/buildings/instruments.yml index ce8cc881b9..2c6edf0355 100644 --- a/Resources/Prototypes/Entities/buildings/instruments.yml +++ b/Resources/Prototypes/Entities/buildings/instruments.yml @@ -1,70 +1,70 @@ -# - type: entity -# name: BaseInstrument -# id: BaseInstrument -# abstract: true -# components: -# - type: Instrument -# handheld: false -# -# - type: Clickable -# - type: InteractionOutline -# -# - type: Collidable -# shapes: -# - !type:PhysShapeAabb -# layer: 31 -# -# - type: SnapGrid -# offset: Center -# -# - type: Damageable -# - type: Destructible -# thresholdvalue: 50 -# -# - type: UserInterface -# interfaces: -# - key: enum.InstrumentUiKey.Key -# type: InstrumentBoundUserInterface -# -# - type: entity -# name: Piano -# parent: BaseInstrument -# id: PianoInstrument -# description: Play Needles Piano Now. -# components: -# - type: Instrument -# program: 1 -# - type: Sprite -# sprite: Objects/Instruments/musician.rsi -# state: piano -# - type: Icon -# sprite: Objects/Instruments/musician.rsi -# state: piano -# -# - type: entity -# name: Minimoog -# parent: BaseInstrument -# id: MinimoogInstrument -# components: -# - type: Instrument -# program: 7 -# - type: Sprite -# sprite: Objects/Instruments/musician.rsi -# state: minimoog -# - type: Icon -# sprite: Objects/Instruments/musician.rsi -# state: minimoog -# -# - type: entity -# name: Xylophone -# parent: BaseInstrument -# id: XylophoneInstrument -# components: -# - type: Instrument -# program: 13 -# - type: Sprite -# sprite: Objects/Instruments/musician.rsi -# state: xylophone -# - type: Icon -# sprite: Objects/Instruments/musician.rsi -# state: xylophone + - type: entity + name: BaseInstrument + id: BaseInstrument + abstract: true + components: + - type: Instrument + handheld: false + + - type: Clickable + - type: InteractionOutline + + - type: Collidable + shapes: + - !type:PhysShapeAabb + layer: 31 + + - type: SnapGrid + offset: Center + + - type: Damageable + - type: Destructible + thresholdvalue: 50 + + - type: UserInterface + interfaces: + - key: enum.InstrumentUiKey.Key + type: InstrumentBoundUserInterface + + - type: entity + name: Piano + parent: BaseInstrument + id: PianoInstrument + description: Play Needles Piano Now. + components: + - type: Instrument + program: 1 + - type: Sprite + sprite: Objects/Instruments/musician.rsi + state: piano + - type: Icon + sprite: Objects/Instruments/musician.rsi + state: piano + + - type: entity + name: Minimoog + parent: BaseInstrument + id: MinimoogInstrument + components: + - type: Instrument + program: 7 + - type: Sprite + sprite: Objects/Instruments/musician.rsi + state: minimoog + - type: Icon + sprite: Objects/Instruments/musician.rsi + state: minimoog + + - type: entity + name: Xylophone + parent: BaseInstrument + id: XylophoneInstrument + components: + - type: Instrument + program: 13 + - type: Sprite + sprite: Objects/Instruments/musician.rsi + state: xylophone + - type: Icon + sprite: Objects/Instruments/musician.rsi + state: xylophone From 5edfa2db34b4f152b35115d1c1db7fb226bff9d6 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 4 Apr 2020 15:10:51 +0200 Subject: [PATCH 023/103] Re-organize style sheets and add StyleSpace. --- Content.Client/ClientContentIoC.cs | 2 + Content.Client/EntryPoint.cs | 8 +- Content.Client/EscapeMenuOwner.cs | 1 - .../Actor/CharacterInfoComponent.cs | 3 +- .../Components/Chemistry/InjectorComponent.cs | 3 +- .../Chemistry/ReagentDispenserWindow.cs | 9 +- .../MagicMirrorBoundUserInterface.cs | 7 +- .../Components/Power/ApcBoundUserInterface.cs | 9 +- .../GameObjects/Components/StackComponent.cs | 3 +- .../BallisticMagazineWeaponComponent.cs | 3 +- .../GameObjects/Components/WelderComponent.cs | 3 +- Content.Client/Instruments/InstrumentMenu.cs | 4 +- .../UserInterface/Cargo/CargoConsoleMenu.cs | 15 +-- .../UserInterface/CharacterSetupGui.cs | 9 +- .../UserInterface/Controls/HighDivider.cs | 14 +++ .../UserInterface/ItemStatusPanel.cs | 3 +- Content.Client/UserInterface/LobbyGui.cs | 17 +-- Content.Client/UserInterface/NanoHeading.cs | 3 +- .../Stylesheets/IStylesheetManager.cs | 12 ++ .../UserInterface/Stylesheets/StyleBase.cs | 47 ++++++++ .../StyleNano.cs} | 43 +++---- .../UserInterface/Stylesheets/StyleSpace.cs | 107 ++++++++++++++++++ .../Stylesheets/StylesheetManager.cs | 26 +++++ 23 files changed, 279 insertions(+), 72 deletions(-) create mode 100644 Content.Client/UserInterface/Controls/HighDivider.cs create mode 100644 Content.Client/UserInterface/Stylesheets/IStylesheetManager.cs create mode 100644 Content.Client/UserInterface/Stylesheets/StyleBase.cs rename Content.Client/UserInterface/{NanoStyle.cs => Stylesheets/StyleNano.cs} (96%) create mode 100644 Content.Client/UserInterface/Stylesheets/StyleSpace.cs create mode 100644 Content.Client/UserInterface/Stylesheets/StylesheetManager.cs diff --git a/Content.Client/ClientContentIoC.cs b/Content.Client/ClientContentIoC.cs index 6a8e9ea10a..05dfbfa957 100644 --- a/Content.Client/ClientContentIoC.cs +++ b/Content.Client/ClientContentIoC.cs @@ -6,6 +6,7 @@ using Content.Client.Interfaces.Parallax; using Content.Client.Parallax; using Content.Client.Sandbox; using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Content.Shared.Interfaces; using Robust.Shared.IoC; @@ -27,6 +28,7 @@ namespace Content.Client IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); } } } diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 8eedfcc852..5077657039 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -1,4 +1,4 @@ -using System; +using System; using Content.Client.GameObjects.Components.Actor; using Content.Client.Input; using Content.Client.Interfaces; @@ -8,6 +8,7 @@ using Content.Client.Parallax; using Content.Client.Sandbox; using Content.Client.State; using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components.Cargo; using Content.Shared.GameObjects.Components.Chemistry; @@ -167,10 +168,7 @@ namespace Content.Client IoCManager.Resolve().LoadParallax(); IoCManager.Resolve().PlayerJoinedServer += SubscribePlayerAttachmentEvents; - - var stylesheet = new NanoStyle(); - - IoCManager.Resolve().Stylesheet = stylesheet.Stylesheet; + IoCManager.Resolve().Initialize(); IoCManager.InjectDependencies(this); diff --git a/Content.Client/EscapeMenuOwner.cs b/Content.Client/EscapeMenuOwner.cs index 519d6e0692..5cbd33c1d8 100644 --- a/Content.Client/EscapeMenuOwner.cs +++ b/Content.Client/EscapeMenuOwner.cs @@ -5,7 +5,6 @@ using Robust.Client.Interfaces.Input; using Robust.Client.Interfaces.Placement; using Robust.Client.Interfaces.ResourceManagement; using Robust.Client.Interfaces.State; -using Robust.Client.State.States; using Robust.Shared.Input; using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.Map; diff --git a/Content.Client/GameObjects/Components/Actor/CharacterInfoComponent.cs b/Content.Client/GameObjects/Components/Actor/CharacterInfoComponent.cs index 18b939d90b..dfb7e3c796 100644 --- a/Content.Client/GameObjects/Components/Actor/CharacterInfoComponent.cs +++ b/Content.Client/GameObjects/Components/Actor/CharacterInfoComponent.cs @@ -1,5 +1,6 @@ using Content.Client.GameObjects.Components.Mobs; using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Robust.Client.Interfaces.GameObjects.Components; using Robust.Client.Interfaces.ResourceManagement; using Robust.Client.UserInterface; @@ -68,7 +69,7 @@ namespace Content.Client.GameObjects.Components.Actor (SubText = new Label { SizeFlagsVertical = SizeFlags.None, - StyleClasses = {NanoStyle.StyleClassLabelSubText} + StyleClasses = {StyleNano.StyleClassLabelSubText} }) } } diff --git a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs index ab1bb415d1..bc064e32e6 100644 --- a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -1,4 +1,5 @@ using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Robust.Shared.Timing; using Content.Shared.GameObjects.Components.Chemistry; @@ -49,7 +50,7 @@ namespace Content.Client.GameObjects.Components.Chemistry public StatusControl(InjectorComponent parent) { _parent = parent; - _label = new RichTextLabel { StyleClasses = { NanoStyle.StyleClassItemStatus } }; + _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; AddChild(_label); parent._uiUpdateNeeded = true; diff --git a/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs b/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs index d35b18346c..8461d0ac8b 100644 --- a/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs +++ b/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Content.Shared.Chemistry; using Content.Shared.GameObjects.Components.Chemistry; using Robust.Client.Graphics.Drawing; @@ -218,7 +219,7 @@ namespace Content.Client.GameObjects.Components.Chemistry new Label { Text = $"{state.BeakerCurrentVolume}/{state.BeakerMaxVolume}", - StyleClasses = {NanoStyle.StyleClassLabelSecondaryColor} + StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} } } }); @@ -247,12 +248,12 @@ namespace Content.Client.GameObjects.Components.Chemistry new Label { Text = $"{name}: ", - StyleClasses = {NanoStyle.StyleClassPowerStateGood} + StyleClasses = {StyleNano.StyleClassPowerStateGood} }, new Label { Text = $"{reagent.Quantity}u", - StyleClasses = {NanoStyle.StyleClassPowerStateGood} + StyleClasses = {StyleNano.StyleClassPowerStateGood} } } }); @@ -267,7 +268,7 @@ namespace Content.Client.GameObjects.Components.Chemistry new Label { Text = $"{reagent.Quantity}u", - StyleClasses = {NanoStyle.StyleClassLabelSecondaryColor} + StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} } } }); diff --git a/Content.Client/GameObjects/Components/MagicMirrorBoundUserInterface.cs b/Content.Client/GameObjects/Components/MagicMirrorBoundUserInterface.cs index 4c9245b9b3..d6fcca29fd 100644 --- a/Content.Client/GameObjects/Components/MagicMirrorBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/MagicMirrorBoundUserInterface.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Content.Shared.Preferences.Appearance; using JetBrains.Annotations; using Robust.Client.GameObjects.Components.UserInterface; @@ -126,9 +127,9 @@ namespace Content.Client.GameObjects.Components var vBox = new VBoxContainer(); AddChild(vBox); - vBox.AddChild(_colorSliderR = new ColorSlider(NanoStyle.StyleClassSliderRed)); - vBox.AddChild(_colorSliderG = new ColorSlider(NanoStyle.StyleClassSliderGreen)); - vBox.AddChild(_colorSliderB = new ColorSlider(NanoStyle.StyleClassSliderBlue)); + vBox.AddChild(_colorSliderR = new ColorSlider(StyleNano.StyleClassSliderRed)); + vBox.AddChild(_colorSliderG = new ColorSlider(StyleNano.StyleClassSliderGreen)); + vBox.AddChild(_colorSliderB = new ColorSlider(StyleNano.StyleClassSliderBlue)); Action colorValueChanged = ColorValueChanged; _colorSliderR.OnValueChanged += colorValueChanged; diff --git a/Content.Client/GameObjects/Components/Power/ApcBoundUserInterface.cs b/Content.Client/GameObjects/Components/Power/ApcBoundUserInterface.cs index 0abeb69183..0d89ecfddb 100644 --- a/Content.Client/GameObjects/Components/Power/ApcBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/Power/ApcBoundUserInterface.cs @@ -1,5 +1,6 @@ using System; using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Content.Shared.GameObjects.Components.Power; using Robust.Client.GameObjects.Components.UserInterface; using Robust.Client.Graphics.Drawing; @@ -47,15 +48,15 @@ namespace Content.Client.GameObjects.Components.Power { case ApcExternalPowerState.None: _externalPowerStateLabel.Text = "None"; - _externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateNone); + _externalPowerStateLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateNone); break; case ApcExternalPowerState.Low: _externalPowerStateLabel.Text = "Low"; - _externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateLow); + _externalPowerStateLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateLow); break; case ApcExternalPowerState.Good: _externalPowerStateLabel.Text = "Good"; - _externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateGood); + _externalPowerStateLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood); break; default: throw new ArgumentOutOfRangeException(); @@ -140,7 +141,7 @@ namespace Content.Client.GameObjects.Components.Power var externalStatus = new HBoxContainer(); var externalStatusLabel = new Label {Text = "External Power: "}; ExternalPowerStateLabel = new Label {Text = "Good"}; - ExternalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateGood); + ExternalPowerStateLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood); externalStatus.AddChild(externalStatusLabel); externalStatus.AddChild(ExternalPowerStateLabel); rows.AddChild(externalStatus); diff --git a/Content.Client/GameObjects/Components/StackComponent.cs b/Content.Client/GameObjects/Components/StackComponent.cs index 717372091a..c24b1787a5 100644 --- a/Content.Client/GameObjects/Components/StackComponent.cs +++ b/Content.Client/GameObjects/Components/StackComponent.cs @@ -1,4 +1,5 @@ using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Content.Shared.GameObjects.Components; using Robust.Client.UserInterface; @@ -38,7 +39,7 @@ namespace Content.Client.GameObjects.Components public StatusControl(StackComponent parent) { _parent = parent; - _label = new RichTextLabel {StyleClasses = {NanoStyle.StyleClassItemStatus}}; + _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}}; AddChild(_label); parent._uiUpdateNeeded = true; diff --git a/Content.Client/GameObjects/Components/Weapons/Ranged/BallisticMagazineWeaponComponent.cs b/Content.Client/GameObjects/Components/Weapons/Ranged/BallisticMagazineWeaponComponent.cs index 1917dbcd65..d800e7cb5e 100644 --- a/Content.Client/GameObjects/Components/Weapons/Ranged/BallisticMagazineWeaponComponent.cs +++ b/Content.Client/GameObjects/Components/Weapons/Ranged/BallisticMagazineWeaponComponent.cs @@ -1,6 +1,7 @@ using System; using Content.Client.Animations; using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Weapons.Ranged; @@ -174,7 +175,7 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged (_noMagazineLabel = new Label { Text = "No Magazine!", - StyleClasses = {NanoStyle.StyleClassItemStatus} + StyleClasses = {StyleNano.StyleClassItemStatus} }) } }, diff --git a/Content.Client/GameObjects/Components/WelderComponent.cs b/Content.Client/GameObjects/Components/WelderComponent.cs index dc7387b559..e5a64d5ccb 100644 --- a/Content.Client/GameObjects/Components/WelderComponent.cs +++ b/Content.Client/GameObjects/Components/WelderComponent.cs @@ -1,5 +1,6 @@ using System; using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components; @@ -46,7 +47,7 @@ namespace Content.Client.GameObjects.Components public StatusControl(WelderComponent parent) { _parent = parent; - _label = new RichTextLabel {StyleClasses = {NanoStyle.StyleClassItemStatus}}; + _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}}; AddChild(_label); parent._uiUpdateNeeded = true; diff --git a/Content.Client/Instruments/InstrumentMenu.cs b/Content.Client/Instruments/InstrumentMenu.cs index 0fb39bde2e..171263e811 100644 --- a/Content.Client/Instruments/InstrumentMenu.cs +++ b/Content.Client/Instruments/InstrumentMenu.cs @@ -1,5 +1,5 @@ using Content.Client.GameObjects.Components.Instruments; -using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; using Robust.Client.Audio.Midi; using Robust.Client.Graphics.Drawing; using Robust.Client.Interfaces.UserInterface; @@ -148,7 +148,7 @@ namespace Content.Client.Instruments Align = Label.AlignMode.Center, SizeFlagsVertical = SizeFlags.ShrinkCenter, SizeFlagsHorizontal = SizeFlags.ShrinkCenter, - StyleClasses = {NanoStyle.StyleClassLabelBig}, + StyleClasses = {StyleNano.StyleClassLabelBig}, Text = Loc.GetString("MIDI support is currently\nnot available on your platform.") } } diff --git a/Content.Client/UserInterface/Cargo/CargoConsoleMenu.cs b/Content.Client/UserInterface/Cargo/CargoConsoleMenu.cs index 30b8238d20..9de68993e6 100644 --- a/Content.Client/UserInterface/Cargo/CargoConsoleMenu.cs +++ b/Content.Client/UserInterface/Cargo/CargoConsoleMenu.cs @@ -9,6 +9,7 @@ using Robust.Shared.Localization; using Robust.Shared.Maths; using System; using System.Collections.Generic; +using Content.Client.UserInterface.Stylesheets; namespace Content.Client.UserInterface.Cargo { @@ -57,7 +58,7 @@ namespace Content.Client.UserInterface.Cargo var accountName = new HBoxContainer(); var accountNameLabel = new Label { Text = _loc.GetString("Account Name: "), - StyleClasses = { NanoStyle.StyleClassLabelKeyText } + StyleClasses = { StyleNano.StyleClassLabelKeyText } }; _accountNameLabel = new Label { Text = "None" //Owner.Bank.Account.Name @@ -70,7 +71,7 @@ namespace Content.Client.UserInterface.Cargo var pointsLabel = new Label { Text = _loc.GetString("Points: "), - StyleClasses = { NanoStyle.StyleClassLabelKeyText } + StyleClasses = { StyleNano.StyleClassLabelKeyText } }; _pointsLabel = new Label { @@ -84,7 +85,7 @@ namespace Content.Client.UserInterface.Cargo var shuttleStatusLabel = new Label { Text = _loc.GetString("Shuttle Status: "), - StyleClasses = { NanoStyle.StyleClassLabelKeyText } + StyleClasses = { StyleNano.StyleClassLabelKeyText } }; _shuttleStatusLabel = new Label { @@ -410,13 +411,13 @@ namespace Content.Client.UserInterface.Cargo ProductName = new Label { SizeFlagsHorizontal = SizeFlags.FillExpand, - StyleClasses = { NanoStyle.StyleClassLabelSubText }, + StyleClasses = { StyleNano.StyleClassLabelSubText }, ClipText = true }; Description = new Label { SizeFlagsHorizontal = SizeFlags.FillExpand, - StyleClasses = { NanoStyle.StyleClassLabelSubText }, + StyleClasses = { StyleNano.StyleClassLabelSubText }, ClipText = true }; vBox.AddChild(ProductName); @@ -426,14 +427,14 @@ namespace Content.Client.UserInterface.Cargo Approve = new Button { Text = "Approve", - StyleClasses = { NanoStyle.StyleClassLabelSubText } + StyleClasses = { StyleNano.StyleClassLabelSubText } }; hBox.AddChild(Approve); Cancel = new Button { Text = "Cancel", - StyleClasses = { NanoStyle.StyleClassLabelSubText } + StyleClasses = { StyleNano.StyleClassLabelSubText } }; hBox.AddChild(Cancel); diff --git a/Content.Client/UserInterface/CharacterSetupGui.cs b/Content.Client/UserInterface/CharacterSetupGui.cs index 3f4291deeb..be82a1668d 100644 --- a/Content.Client/UserInterface/CharacterSetupGui.cs +++ b/Content.Client/UserInterface/CharacterSetupGui.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Client.GameObjects.Components.Mobs; using Content.Client.Interfaces; +using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Content.Shared.Jobs; using Content.Shared.Preferences; @@ -67,7 +68,7 @@ namespace Content.Client.UserInterface { SizeFlagsHorizontal = SizeFlags.Expand | SizeFlags.ShrinkEnd, Text = Loc.GetString("Save and close"), - StyleClasses = {NanoStyle.StyleClassButtonBig} + StyleClasses = {StyleNano.StyleClassButtonBig} }; var topHBox = new HBoxContainer @@ -83,7 +84,7 @@ namespace Content.Client.UserInterface new Label { Text = Loc.GetString("Character Setup"), - StyleClasses = {NanoStyle.StyleClassLabelHeadingBigger}, + StyleClasses = {StyleNano.StyleClassLabelHeadingBigger}, VAlign = Label.VAlignMode.Center, SizeFlagsHorizontal = SizeFlags.Expand | SizeFlags.ShrinkCenter } @@ -99,7 +100,7 @@ namespace Content.Client.UserInterface { PanelOverride = new StyleBoxFlat { - BackgroundColor = NanoStyle.NanoGold, + BackgroundColor = StyleNano.NanoGold, ContentMarginTopOverride = 2 } }); @@ -146,7 +147,7 @@ namespace Content.Client.UserInterface hBox.AddChild(new PanelContainer { - PanelOverride = new StyleBoxFlat {BackgroundColor = NanoStyle.NanoGold}, + PanelOverride = new StyleBoxFlat {BackgroundColor = StyleNano.NanoGold}, CustomMinimumSize = (2, 0) }); _humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager); diff --git a/Content.Client/UserInterface/Controls/HighDivider.cs b/Content.Client/UserInterface/Controls/HighDivider.cs new file mode 100644 index 0000000000..29bdaabaee --- /dev/null +++ b/Content.Client/UserInterface/Controls/HighDivider.cs @@ -0,0 +1,14 @@ +using Content.Client.UserInterface.Stylesheets; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.UserInterface.Controls +{ + public sealed class HighDivider : Control + { + public HighDivider() + { + Children.Add(new PanelContainer {StyleClasses = {StyleBase.ClassHighDivider}}); + } + } +} diff --git a/Content.Client/UserInterface/ItemStatusPanel.cs b/Content.Client/UserInterface/ItemStatusPanel.cs index e53a1cd522..fe94573a17 100644 --- a/Content.Client/UserInterface/ItemStatusPanel.cs +++ b/Content.Client/UserInterface/ItemStatusPanel.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Content.Client.GameObjects.Components; +using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Robust.Client.Graphics.Drawing; using Robust.Client.UserInterface; @@ -57,7 +58,7 @@ namespace Content.Client.UserInterface (_itemNameLabel = new Label { ClipText = true, - StyleClasses = {NanoStyle.StyleClassItemStatus} + StyleClasses = {StyleNano.StyleClassItemStatus} }) } } diff --git a/Content.Client/UserInterface/LobbyGui.cs b/Content.Client/UserInterface/LobbyGui.cs index 29624b8ced..4904eb6322 100644 --- a/Content.Client/UserInterface/LobbyGui.cs +++ b/Content.Client/UserInterface/LobbyGui.cs @@ -1,5 +1,6 @@ using Content.Client.Chat; using Content.Client.Interfaces; +using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Robust.Client.Graphics.Drawing; using Robust.Client.Interfaces.ResourceManagement; @@ -69,7 +70,7 @@ namespace Content.Client.UserInterface new Label { Text = Loc.GetString("Lobby"), - StyleClasses = {NanoStyle.StyleClassLabelHeadingBigger}, + StyleClasses = {StyleNano.StyleClassLabelHeadingBigger}, /*MarginBottom = 40, MarginLeft = 8,*/ VAlign = Label.VAlignMode.Center @@ -78,7 +79,7 @@ namespace Content.Client.UserInterface }, (ServerName = new Label { - StyleClasses = {NanoStyle.StyleClassLabelHeadingBigger}, + StyleClasses = {StyleNano.StyleClassLabelHeadingBigger}, /*MarginBottom = 40, GrowHorizontal = GrowDirection.Both,*/ VAlign = Label.VAlignMode.Center, @@ -88,7 +89,7 @@ namespace Content.Client.UserInterface { SizeFlagsHorizontal = SizeFlags.ShrinkEnd, Text = Loc.GetString("Leave"), - StyleClasses = {NanoStyle.StyleClassButtonBig}, + StyleClasses = {StyleNano.StyleClassButtonBig}, //GrowHorizontal = GrowDirection.Begin }) } @@ -100,7 +101,7 @@ namespace Content.Client.UserInterface { PanelOverride = new StyleBoxFlat { - BackgroundColor = NanoStyle.NanoGold, + BackgroundColor = StyleNano.NanoGold, ContentMarginTopOverride = 2 }, }); @@ -146,20 +147,20 @@ namespace Content.Client.UserInterface (ObserveButton = new Button { Text = Loc.GetString("Observe"), - StyleClasses = {NanoStyle.StyleClassButtonBig} + StyleClasses = {StyleNano.StyleClassButtonBig} }), (StartTime = new Label { SizeFlagsHorizontal = SizeFlags.FillExpand, Align = Label.AlignMode.Right, FontColorOverride = Color.DarkGray, - StyleClasses = {NanoStyle.StyleClassLabelBig} + StyleClasses = {StyleNano.StyleClassLabelBig} }), (ReadyButton = new Button { ToggleMode = true, Text = Loc.GetString("Ready Up"), - StyleClasses = {NanoStyle.StyleClassButtonBig} + StyleClasses = {StyleNano.StyleClassButtonBig} }), } } @@ -188,7 +189,7 @@ namespace Content.Client.UserInterface hBox.AddChild(new PanelContainer { - PanelOverride = new StyleBoxFlat {BackgroundColor = NanoStyle.NanoGold}, CustomMinimumSize = (2, 0) + PanelOverride = new StyleBoxFlat {BackgroundColor = StyleNano.NanoGold}, CustomMinimumSize = (2, 0) }); { diff --git a/Content.Client/UserInterface/NanoHeading.cs b/Content.Client/UserInterface/NanoHeading.cs index 30dde27fc5..d0ac77e9e7 100644 --- a/Content.Client/UserInterface/NanoHeading.cs +++ b/Content.Client/UserInterface/NanoHeading.cs @@ -1,3 +1,4 @@ +using Content.Client.UserInterface.Stylesheets; using Robust.Client.UserInterface.Controls; using Robust.Shared.Maths; @@ -14,7 +15,7 @@ namespace Content.Client.UserInterface { Children = {(_label = new Label { - StyleClasses = {NanoStyle.StyleClassLabelHeading} + StyleClasses = {StyleNano.StyleClassLabelHeading} })} }; AddChild(_panel); diff --git a/Content.Client/UserInterface/Stylesheets/IStylesheetManager.cs b/Content.Client/UserInterface/Stylesheets/IStylesheetManager.cs new file mode 100644 index 0000000000..2082ffb216 --- /dev/null +++ b/Content.Client/UserInterface/Stylesheets/IStylesheetManager.cs @@ -0,0 +1,12 @@ +using Robust.Client.UserInterface; + +namespace Content.Client.UserInterface.Stylesheets +{ + public interface IStylesheetManager + { + Stylesheet SheetNano { get; } + Stylesheet SheetSpace { get; } + + void Initialize(); + } +} diff --git a/Content.Client/UserInterface/Stylesheets/StyleBase.cs b/Content.Client/UserInterface/Stylesheets/StyleBase.cs new file mode 100644 index 0000000000..10562fad2f --- /dev/null +++ b/Content.Client/UserInterface/Stylesheets/StyleBase.cs @@ -0,0 +1,47 @@ +using Content.Client.Utility; +using Robust.Client.Graphics.Drawing; +using Robust.Client.Interfaces.ResourceManagement; +using Robust.Client.UserInterface; + +namespace Content.Client.UserInterface.Stylesheets +{ + public abstract class StyleBase + { + public const string ClassHighDivider = "HighDivider"; + public const string StyleClassLabelHeading = "LabelHeading"; + public const string StyleClassLabelSubText = "LabelSubText"; + + public abstract Stylesheet Stylesheet { get; } + + protected StyleRule[] BaseRules { get; } + + protected StyleBoxTexture BaseButton { get; } + + protected StyleBase(IResourceCache resCache) + { + var notoSans12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 12); + + // Button styles. + var buttonTex = resCache.GetTexture("/Nano/button.svg.96dpi.png"); + BaseButton = new StyleBoxTexture + { + Texture = buttonTex, + }; + BaseButton.SetPatchMargin(StyleBox.Margin.All, 10); + BaseButton.SetPadding(StyleBox.Margin.All, 1); + BaseButton.SetContentMarginOverride(StyleBox.Margin.Vertical, 2); + BaseButton.SetContentMarginOverride(StyleBox.Margin.Horizontal, 14); + + BaseRules = new[] + { + // Default font. + new StyleRule( + new SelectorElement(null, null, null, null), + new[] + { + new StyleProperty("font", notoSans12), + }), + }; + } + } +} diff --git a/Content.Client/UserInterface/NanoStyle.cs b/Content.Client/UserInterface/Stylesheets/StyleNano.cs similarity index 96% rename from Content.Client/UserInterface/NanoStyle.cs rename to Content.Client/UserInterface/Stylesheets/StyleNano.cs index 97cdd91310..e182012a55 100644 --- a/Content.Client/UserInterface/NanoStyle.cs +++ b/Content.Client/UserInterface/Stylesheets/StyleNano.cs @@ -1,29 +1,29 @@ -using Content.Client.GameObjects.EntitySystems; +using System.Linq; +using Content.Client.GameObjects.EntitySystems; using Content.Client.Utility; using Robust.Client.Graphics.Drawing; using Robust.Client.Interfaces.ResourceManagement; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; -using Robust.Shared.IoC; using Robust.Shared.Maths; -namespace Content.Client.UserInterface +namespace Content.Client.UserInterface.Stylesheets { - public sealed class NanoStyle + public sealed class StyleNano : StyleBase { public const string StyleClassSliderRed = "Red"; public const string StyleClassSliderGreen = "Green"; public const string StyleClassSliderBlue = "Blue"; - public const string StyleClassLabelHeading = "LabelHeading"; public const string StyleClassLabelHeadingBigger = "LabelHeadingBigger"; - public const string StyleClassLabelSubText = "LabelSubText"; public const string StyleClassLabelKeyText = "LabelKeyText"; public const string StyleClassLabelSecondaryColor = "LabelSecondaryColor"; public const string StyleClassLabelBig = "LabelBig"; public const string StyleClassButtonBig = "ButtonBig"; + public static readonly Color NanoGold = Color.FromHex("#A88B5E"); + public static readonly Color ButtonColorDefault = Color.FromHex("#464966"); public static readonly Color ButtonColorHovered = Color.FromHex("#575b7f"); public static readonly Color ButtonColorPressed = Color.FromHex("#3e6c45"); @@ -36,12 +36,10 @@ namespace Content.Client.UserInterface public const string StyleClassItemStatus = "ItemStatus"; - public Stylesheet Stylesheet { get; } + public override Stylesheet Stylesheet { get; } - public NanoStyle() + public StyleNano(IResourceCache resCache) : base(resCache) { - var resCache = IoCManager.Resolve(); - var notoSans8 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 8); var notoSans10 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 10); var notoSans12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 12); var notoSansBold12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Bold.ttf", 12); @@ -69,16 +67,10 @@ namespace Content.Client.UserInterface var textureInvertedTriangle = resCache.GetTexture("/Nano/inverted_triangle.svg.png"); // Button styles. - var buttonTex = resCache.GetTexture("/Nano/button.svg.96dpi.png"); - var buttonNormal = new StyleBoxTexture + var buttonNormal = new StyleBoxTexture(BaseButton) { - Texture = buttonTex, Modulate = ButtonColorDefault }; - buttonNormal.SetPatchMargin(StyleBox.Margin.All, 10); - buttonNormal.SetPadding(StyleBox.Margin.All, 1); - buttonNormal.SetContentMarginOverride(StyleBox.Margin.Vertical, 2); - buttonNormal.SetContentMarginOverride(StyleBox.Margin.Horizontal, 14); var buttonHover = new StyleBoxTexture(buttonNormal) { @@ -248,16 +240,8 @@ namespace Content.Client.UserInterface var sliderFillRed = new StyleBoxTexture(sliderFillBox) {Modulate = Color.Red}; var sliderFillBlue = new StyleBoxTexture(sliderFillBox) {Modulate = Color.Blue}; - Stylesheet = new Stylesheet(new[] + Stylesheet = new Stylesheet(BaseRules.Concat(new[] { - // Default font. - new StyleRule( - new SelectorElement(null, null, null, null), - new[] - { - new StyleProperty("font", notoSans12), - }), - // Window title. new StyleRule( new SelectorElement(typeof(Label), new[] {SS14Window.StyleClassWindowTitle}, null, null), @@ -721,7 +705,12 @@ namespace Content.Client.UserInterface { new StyleProperty(Label.StylePropertyAlignMode, Label.AlignMode.Center), }), - }); + + new StyleRule(new SelectorElement(typeof(PanelContainer), new []{ ClassHighDivider}, null, null), new [] + { + new StyleProperty(PanelContainer.StylePropertyPanel, new StyleBoxFlat { BackgroundColor = NanoGold, ContentMarginBottomOverride = 2, ContentMarginLeftOverride = 2}), + }) + }).ToList()); } } } diff --git a/Content.Client/UserInterface/Stylesheets/StyleSpace.cs b/Content.Client/UserInterface/Stylesheets/StyleSpace.cs new file mode 100644 index 0000000000..9e4b318c6c --- /dev/null +++ b/Content.Client/UserInterface/Stylesheets/StyleSpace.cs @@ -0,0 +1,107 @@ +using System.Linq; +using Content.Client.Utility; +using Robust.Client.Graphics.Drawing; +using Robust.Client.Interfaces.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Maths; + +namespace Content.Client.UserInterface.Stylesheets +{ + public class StyleSpace : StyleBase + { + public static readonly Color SpaceRed = Color.FromHex("#9b2236"); + + public static readonly Color ButtonColorDefault = Color.FromHex("#464966"); + public static readonly Color ButtonColorHovered = Color.FromHex("#575b7f"); + public static readonly Color ButtonColorPressed = Color.FromHex("#3e6c45"); + public static readonly Color ButtonColorDisabled = Color.FromHex("#30313c"); + + public override Stylesheet Stylesheet { get; } + + public StyleSpace(IResourceCache resCache) : base(resCache) + { + var notoSans10 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 10); + var notoSansBold16 = resCache.GetFont("/Nano/NotoSans/NotoSans-Bold.ttf", 16); + + // Button styles. + var buttonNormal = new StyleBoxTexture(BaseButton) + { + Modulate = ButtonColorDefault + }; + + var buttonHover = new StyleBoxTexture(buttonNormal) + { + Modulate = ButtonColorHovered + }; + + var buttonPressed = new StyleBoxTexture(buttonNormal) + { + Modulate = ButtonColorPressed + }; + + var buttonDisabled = new StyleBoxTexture(buttonNormal) + { + Modulate = ButtonColorDisabled + }; + + + Stylesheet = new Stylesheet(BaseRules.Concat(new StyleRule[] + { + // Big Label + new StyleRule(new SelectorElement(typeof(Label), new[] {StyleClassLabelHeading}, null, null), new[] + { + new StyleProperty(Label.StylePropertyFont, notoSansBold16), + new StyleProperty(Label.StylePropertyFontColor, SpaceRed), + }), + + // Small Label + new StyleRule(new SelectorElement(typeof(Label), new[] {StyleClassLabelSubText}, null, null), new[] + { + new StyleProperty(Label.StylePropertyFont, notoSans10), + new StyleProperty(Label.StylePropertyFontColor, Color.DarkGray), + }), + + new StyleRule(new SelectorElement(typeof(PanelContainer), new[] {ClassHighDivider}, null, null), new[] + { + new StyleProperty(PanelContainer.StylePropertyPanel, + new StyleBoxFlat + { + BackgroundColor = SpaceRed, ContentMarginBottomOverride = 2, ContentMarginLeftOverride = 2 + }), + }), + + // Regular buttons! + new StyleRule(new SelectorElement(typeof(ContainerButton), new[] { ContainerButton.StyleClassButton }, null, new[] {ContainerButton.StylePseudoClassNormal}), new[] + { + new StyleProperty(ContainerButton.StylePropertyStyleBox, buttonNormal), + }), + new StyleRule(new SelectorElement(typeof(ContainerButton), new[] { ContainerButton.StyleClassButton }, null, new[] {ContainerButton.StylePseudoClassHover}), new[] + { + new StyleProperty(ContainerButton.StylePropertyStyleBox, buttonHover), + }), + new StyleRule(new SelectorElement(typeof(ContainerButton), new[] { ContainerButton.StyleClassButton }, null, new[] {ContainerButton.StylePseudoClassPressed}), new[] + { + new StyleProperty(ContainerButton.StylePropertyStyleBox, buttonPressed), + }), + new StyleRule(new SelectorElement(typeof(ContainerButton), new[] { ContainerButton.StyleClassButton }, null, new[] {ContainerButton.StylePseudoClassDisabled}), new[] + { + new StyleProperty(ContainerButton.StylePropertyStyleBox, buttonDisabled), + }), + + new StyleRule(new SelectorElement(typeof(Label), new[] { Button.StyleClassButton }, null, null), new[] + { + new StyleProperty(Label.StylePropertyAlignMode, Label.AlignMode.Center), + }), + + new StyleRule(new SelectorChild( + new SelectorElement(typeof(Button), null, null, new[] {ContainerButton.StylePseudoClassDisabled}), + new SelectorElement(typeof(Label), null, null, null)), + new[] + { + new StyleProperty("font-color", Color.FromHex("#E5E5E581")), + }), + }).ToList()); + } + } +} diff --git a/Content.Client/UserInterface/Stylesheets/StylesheetManager.cs b/Content.Client/UserInterface/Stylesheets/StylesheetManager.cs new file mode 100644 index 0000000000..49147d841e --- /dev/null +++ b/Content.Client/UserInterface/Stylesheets/StylesheetManager.cs @@ -0,0 +1,26 @@ +using Robust.Client.Interfaces.ResourceManagement; +using Robust.Client.Interfaces.UserInterface; +using Robust.Client.UserInterface; +using Robust.Shared.IoC; + +namespace Content.Client.UserInterface.Stylesheets +{ + public sealed class StylesheetManager : IStylesheetManager + { +#pragma warning disable 649 + [Dependency] private readonly IUserInterfaceManager _userInterfaceManager; + [Dependency] private readonly IResourceCache _resourceCache; +#pragma warning restore 649 + + public Stylesheet SheetNano { get; private set; } + public Stylesheet SheetSpace { get; private set; } + + public void Initialize() + { + SheetNano = new StyleNano(_resourceCache).Stylesheet; + SheetSpace = new StyleSpace(_resourceCache).Stylesheet; + + _userInterfaceManager.Stylesheet = SheetNano; + } + } +} From 264891ff86811339810f41fd75f5136d48bfbfd6 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 4 Apr 2020 15:11:03 +0200 Subject: [PATCH 024/103] Update submodule. --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 7d6bc63439..ec52102d02 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 7d6bc63439382e8c265ff29ed504bd88d2126c52 +Subproject commit ec52102d0279281a00cc1c6811330a13ddaf975b From 45e9be43ef8f384f17c0059c03d1ab42813b0714 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 4 Apr 2020 15:11:28 +0200 Subject: [PATCH 025/103] Content now owns main menu and launcher connecting. --- Content.Client/EntryPoint.cs | 16 +- Content.Client/State/LauncherConnecting.cs | 230 ++++++++++++++ Content.Client/State/MainMenu.cs | 329 +++++++++++++++++++++ 3 files changed, 573 insertions(+), 2 deletions(-) create mode 100644 Content.Client/State/LauncherConnecting.cs create mode 100644 Content.Client/State/MainMenu.cs diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 5077657039..e1b50b11e8 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -1,4 +1,4 @@ -using System; +using System; using Content.Client.GameObjects.Components.Actor; using Content.Client.Input; using Content.Client.Interfaces; @@ -18,7 +18,7 @@ using Content.Shared.GameObjects.Components.VendingMachines; using Robust.Client.Interfaces; using Robust.Client.Interfaces.Graphics.Overlays; using Robust.Client.Interfaces.Input; -using Robust.Client.Interfaces.UserInterface; +using Robust.Client.Interfaces.State; using Robust.Client.Player; using Robust.Shared.ContentPack; using Robust.Shared.Interfaces.GameObjects; @@ -36,6 +36,8 @@ namespace Content.Client [Dependency] private readonly IPlayerManager _playerManager; [Dependency] private readonly IBaseClient _baseClient; [Dependency] private readonly IEscapeMenuOwner _escapeMenuOwner; + [Dependency] private readonly IGameController _gameController; + [Dependency] private readonly IStateManager _stateManager; #pragma warning restore 649 public override void Init() @@ -223,6 +225,16 @@ namespace Content.Client IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); + + // Fire off into state dependent on launcher or not. + if (_gameController.LaunchState.FromLauncher) + { + _stateManager.RequestStateChange(); + } + else + { + _stateManager.RequestStateChange(); + } } public override void Update(ModUpdateLevel level, FrameEventArgs frameEventArgs) diff --git a/Content.Client/State/LauncherConnecting.cs b/Content.Client/State/LauncherConnecting.cs new file mode 100644 index 0000000000..df1af3c6dc --- /dev/null +++ b/Content.Client/State/LauncherConnecting.cs @@ -0,0 +1,230 @@ +using Content.Client.UserInterface.Controls; +using Content.Client.UserInterface.Stylesheets; +using Content.Client.Utility; +using Robust.Client.Graphics.Drawing; +using Robust.Client.Interfaces; +using Robust.Client.Interfaces.UserInterface; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Interfaces.Network; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using static Content.Client.StaticIoC; + +namespace Content.Client.State +{ + public class LauncherConnecting : Robust.Client.State.State + { +#pragma warning disable 649 + [Dependency] private readonly IUserInterfaceManager _userInterfaceManager; + [Dependency] private readonly IStylesheetManager _stylesheetManager; + [Dependency] private readonly IClientNetManager _clientNetManager; + [Dependency] private readonly IGameController _gameController; +#pragma warning restore 649 + + private Control _control; + private Label _connectStatus; + + public override void Startup() + { + var panelTex = ResC.GetTexture("/Nano/button.svg.96dpi.png"); + var back = new StyleBoxTexture + { + Texture = panelTex, + Modulate = new Color(32, 32, 48), + }; + back.SetPatchMargin(StyleBox.Margin.All, 10); + + Button exitButton; + Label connectFailReason; + Control connectingStatus; + + var address = _gameController.LaunchState.Ss14Address ?? _gameController.LaunchState.ConnectAddress; + + _control = new Control + { + Stylesheet = _stylesheetManager.SheetSpace, + Children = + { + new PanelContainer + { + PanelOverride = back + }, + new VBoxContainer + { + SeparationOverride = 0, + CustomMinimumSize = (300, 200), + Children = + { + new HBoxContainer + { + Children = + { + new MarginContainer + { + MarginLeftOverride = 8, + Children = + { + new Label + { + Text = Loc.GetString("Space Station 14"), + StyleClasses = {StyleBase.StyleClassLabelHeading}, + VAlign = Label.VAlignMode.Center + }, + } + }, + + (exitButton = new Button + { + Text = Loc.GetString("Exit"), + SizeFlagsHorizontal = Control.SizeFlags.ShrinkEnd | Control.SizeFlags.Expand + }), + } + }, + + // Line + new HighDivider(), + + new MarginContainer + { + SizeFlagsVertical = Control.SizeFlags.FillExpand, + MarginLeftOverride = 4, + MarginRightOverride = 4, + MarginTopOverride = 4, + Children = + { + new VBoxContainer + { + SeparationOverride = 0, + Children = + { + new Control + { + Children = + { + (connectingStatus = new VBoxContainer + { + SeparationOverride = 0, + Children = + { + new Label + { + Text = Loc.GetString("Connecting to server..."), + Align = Label.AlignMode.Center, + }, + + (_connectStatus = new Label + { + StyleClasses = {StyleBase.StyleClassLabelSubText}, + Align = Label.AlignMode.Center, + }), + } + }), + (connectFailReason = new Label + { + Align = Label.AlignMode.Center + }) + } + }, + + // Padding. + new Control {CustomMinimumSize = (0, 8)}, + + new Label + { + Text = address, + StyleClasses = {StyleBase.StyleClassLabelSubText}, + SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter, + SizeFlagsVertical = + Control.SizeFlags.ShrinkEnd | Control.SizeFlags.Expand, + } + } + }, + } + }, + + // Line + new PanelContainer + { + PanelOverride = new StyleBoxFlat + { + BackgroundColor = Color.FromHex("#444"), + ContentMarginTopOverride = 2 + }, + }, + + new MarginContainer + { + MarginLeftOverride = 12, + MarginRightOverride = 4, + Children = + { + new HBoxContainer + { + SizeFlagsVertical = Control.SizeFlags.ShrinkEnd, + Children = + { + new Label + { + Text = Loc.GetString("Don't die!"), + StyleClasses = {StyleBase.StyleClassLabelSubText} + }, + new Label + { + Text = "ver 0.1", + SizeFlagsHorizontal = + Control.SizeFlags.Expand | Control.SizeFlags.ShrinkEnd, + StyleClasses = {StyleBase.StyleClassLabelSubText} + } + } + } + } + }, + } + }, + } + }; + + _userInterfaceManager.StateRoot.AddChild(_control); + + LayoutContainer.SetAnchorPreset(_control, LayoutContainer.LayoutPreset.Center); + LayoutContainer.SetGrowHorizontal(_control, LayoutContainer.GrowDirection.Both); + LayoutContainer.SetGrowVertical(_control, LayoutContainer.GrowDirection.Both); + + exitButton.OnPressed += args => + { + _gameController.Shutdown("Exit button pressed"); + }; + + _clientNetManager.ConnectFailed += (sender, args) => + { + connectFailReason.Text = Loc.GetString("Failed to connect to server:\n{0}", args.Reason); + connectingStatus.Visible = false; + connectFailReason.Visible = true; + }; + + _clientNetManager.ClientConnectStateChanged += ConnectStateChanged; + + ConnectStateChanged(_clientNetManager.ClientConnectState); + } + + private void ConnectStateChanged(ClientConnectionState state) + { + _connectStatus.Text = Loc.GetString(state switch + { + ClientConnectionState.NotConnecting => "Not connecting?", + ClientConnectionState.ResolvingHost => "Resolving server address...", + ClientConnectionState.EstablishingConnection => "Establishing initial connection...", + ClientConnectionState.Handshake => "Doing handshake...", + ClientConnectionState.Connected => "Synchronizing game state...", + _ => state.ToString() + }); + } + + public override void Shutdown() + { + _control.Dispose(); + } + } +} diff --git a/Content.Client/State/MainMenu.cs b/Content.Client/State/MainMenu.cs new file mode 100644 index 0000000000..1157e9907b --- /dev/null +++ b/Content.Client/State/MainMenu.cs @@ -0,0 +1,329 @@ +using System; +using System.Text.RegularExpressions; +using Robust.Client; +using Robust.Client.Interfaces; +using Robust.Client.Interfaces.ResourceManagement; +using Robust.Client.Interfaces.UserInterface; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Interfaces.Configuration; +using Robust.Shared.Interfaces.Network; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Log; +using Robust.Shared.Network; +using Robust.Shared.Utility; + +namespace Content.Client.State +{ + /// + /// Main menu screen that is the first screen to be displayed when the game starts. + /// + // Instantiated dynamically through the StateManager, Dependencies will be resolved. + public class MainScreen : Robust.Client.State.State + { + private const string PublicServerAddress = "server.spacestation14.io"; + +#pragma warning disable 649 + [Dependency] private readonly IBaseClient _client; + [Dependency] private readonly IClientNetManager _netManager; + [Dependency] private readonly IConfigurationManager _configurationManager; + [Dependency] private readonly IGameController _controllerProxy; + [Dependency] private readonly ILocalizationManager _loc; + [Dependency] private readonly IResourceCache _resourceCache; + [Dependency] private readonly IUserInterfaceManager userInterfaceManager; +#pragma warning restore 649 + + private MainMenuControl _mainMenuControl; + private OptionsMenu OptionsMenu; + private bool _isConnecting; + + // ReSharper disable once InconsistentNaming + private static readonly Regex IPv6Regex = new Regex(@"\[(.*:.*:.*)](?::(\d+))?"); + + /// + public override void Startup() + { + _mainMenuControl = new MainMenuControl(_resourceCache, _configurationManager); + userInterfaceManager.StateRoot.AddChild(_mainMenuControl); + + _mainMenuControl.QuitButton.OnPressed += QuitButtonPressed; + _mainMenuControl.OptionsButton.OnPressed += OptionsButtonPressed; + _mainMenuControl.DirectConnectButton.OnPressed += DirectConnectButtonPressed; + _mainMenuControl.JoinPublicServerButton.OnPressed += JoinPublicServerButtonPressed; + _mainMenuControl.AddressBox.OnTextEntered += AddressBoxEntered; + + _client.RunLevelChanged += RunLevelChanged; + + OptionsMenu = new OptionsMenu(_configurationManager); + } + + /// + public override void Shutdown() + { + _client.RunLevelChanged -= RunLevelChanged; + _netManager.ConnectFailed -= _onConnectFailed; + + _mainMenuControl.Dispose(); + OptionsMenu.Dispose(); + } + + private void QuitButtonPressed(BaseButton.ButtonEventArgs args) + { + _controllerProxy.Shutdown(); + } + + private void OptionsButtonPressed(BaseButton.ButtonEventArgs args) + { + OptionsMenu.OpenCentered(); + } + + private void DirectConnectButtonPressed(BaseButton.ButtonEventArgs args) + { + var input = _mainMenuControl.AddressBox; + TryConnect(input.Text); + } + + private void JoinPublicServerButtonPressed(BaseButton.ButtonEventArgs args) + { + TryConnect(PublicServerAddress); + } + + private void AddressBoxEntered(LineEdit.LineEditEventArgs args) + { + if (_isConnecting) + { + return; + } + + TryConnect(args.Text); + } + + private void TryConnect(string address) + { + var inputName = _mainMenuControl.UserNameBox.Text.Trim(); + var (nameValid, invalidReason) = UsernameHelpers.IsNameValid(inputName); + if (!nameValid) + { + invalidReason = _loc.GetString(invalidReason); + userInterfaceManager.Popup( + _loc.GetString("Invalid username:\n{0}", invalidReason), + _loc.GetString("Invalid Username")); + return; + } + + var configName = _configurationManager.GetCVar("player.name"); + if (_mainMenuControl.UserNameBox.Text != configName) + { + _configurationManager.SetCVar("player.name", inputName); + _configurationManager.SaveToFile(); + } + + _setConnectingState(true); + _netManager.ConnectFailed += _onConnectFailed; + try + { + ParseAddress(address, out var ip, out var port); + _client.ConnectToServer(ip, port); + } + catch (ArgumentException e) + { + userInterfaceManager.Popup($"Unable to connect: {e.Message}", "Connection error."); + Logger.Warning(e.ToString()); + _netManager.ConnectFailed -= _onConnectFailed; + } + } + + private void RunLevelChanged(object obj, RunLevelChangedEventArgs args) + { + if (args.NewLevel == ClientRunLevel.Initialize) + { + _setConnectingState(false); + _netManager.ConnectFailed -= _onConnectFailed; + } + } + + private void ParseAddress(string address, out string ip, out ushort port) + { + var match6 = IPv6Regex.Match(address); + if (match6 != Match.Empty) + { + ip = match6.Groups[1].Value; + if (!match6.Groups[2].Success) + { + port = _client.DefaultPort; + } + else if (!ushort.TryParse(match6.Groups[2].Value, out port)) + { + throw new ArgumentException("Not a valid port."); + } + + return; + } + + // See if the IP includes a port. + var split = address.Split(':'); + ip = address; + port = _client.DefaultPort; + if (split.Length > 2) + { + throw new ArgumentException("Not a valid Address."); + } + + // IP:port format. + if (split.Length == 2) + { + ip = split[0]; + if (!ushort.TryParse(split[1], out port)) + { + throw new ArgumentException("Not a valid port."); + } + } + } + + private void _onConnectFailed(object _, NetConnectFailArgs args) + { + userInterfaceManager.Popup($"Failed to connect:\n{args.Reason}"); + _netManager.ConnectFailed -= _onConnectFailed; + _setConnectingState(false); + } + + private void _setConnectingState(bool state) + { + _isConnecting = state; + _mainMenuControl.DirectConnectButton.Disabled = state; +#if FULL_RELEASE + _mainMenuControl.JoinPublicServerButton.Disabled = state; +#endif + } + + private sealed class MainMenuControl : Control + { + private readonly IResourceCache _resourceCache; + private readonly IConfigurationManager _configurationManager; + + public LineEdit UserNameBox { get; private set; } + public Button JoinPublicServerButton { get; private set; } + public LineEdit AddressBox { get; private set; } + public Button DirectConnectButton { get; private set; } + public Button OptionsButton { get; private set; } + public Button QuitButton { get; private set; } + public Label VersionLabel { get; private set; } + + public MainMenuControl(IResourceCache resCache, IConfigurationManager configMan) + { + _resourceCache = resCache; + _configurationManager = configMan; + + PerformLayout(); + } + + private void PerformLayout() + { + LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide); + + var layout = new LayoutContainer(); + AddChild(layout); + + var vBox = new VBoxContainer + { + StyleIdentifier = "mainMenuVBox" + }; + + layout.AddChild(vBox); + LayoutContainer.SetAnchorPreset(vBox, LayoutContainer.LayoutPreset.TopRight); + LayoutContainer.SetMarginRight(vBox, -25); + LayoutContainer.SetMarginTop(vBox, 30); + LayoutContainer.SetGrowHorizontal(vBox, LayoutContainer.GrowDirection.Begin); + + var logoTexture = _resourceCache.GetResource("/Textures/Logo/logo.png"); + var logo = new TextureRect + { + Texture = logoTexture, + Stretch = TextureRect.StretchMode.KeepCentered, + }; + vBox.AddChild(logo); + + var userNameHBox = new HBoxContainer {SeparationOverride = 4}; + vBox.AddChild(userNameHBox); + userNameHBox.AddChild(new Label {Text = "Username:"}); + + var currentUserName = _configurationManager.GetCVar("player.name"); + UserNameBox = new LineEdit + { + Text = currentUserName, PlaceHolder = "Username", + SizeFlagsHorizontal = SizeFlags.FillExpand + }; + + userNameHBox.AddChild(UserNameBox); + + JoinPublicServerButton = new Button + { + Text = "Join Public Server", + StyleIdentifier = "mainMenu", + TextAlign = Label.AlignMode.Center, +#if !FULL_RELEASE + Disabled = true, + ToolTip = "Cannot connect to public server with a debug build." +#endif + }; + + vBox.AddChild(JoinPublicServerButton); + + // Separator. + vBox.AddChild(new Control {CustomMinimumSize = (0, 2)}); + + AddressBox = new LineEdit + { + Text = "localhost", + PlaceHolder = "server address:port", + SizeFlagsHorizontal = SizeFlags.FillExpand + }; + + vBox.AddChild(AddressBox); + + DirectConnectButton = new Button + { + Text = "Direct Connect", + TextAlign = Label.AlignMode.Center, + StyleIdentifier = "mainMenu", + }; + + vBox.AddChild(DirectConnectButton); + + // Separator. + vBox.AddChild(new Control {CustomMinimumSize = (0, 2)}); + + OptionsButton = new Button + { + Text = "Options", + TextAlign = Label.AlignMode.Center, + StyleIdentifier = "mainMenu", + }; + + vBox.AddChild(OptionsButton); + + QuitButton = new Button + { + Text = "Quit", + TextAlign = Label.AlignMode.Center, + StyleIdentifier = "mainMenu", + }; + + vBox.AddChild(QuitButton); + + VersionLabel = new Label + { + Text = $"v0.1" + }; + + LayoutContainer.SetAnchorPreset(VersionLabel, LayoutContainer.LayoutPreset.BottomRight); + LayoutContainer.SetGrowHorizontal(VersionLabel, LayoutContainer.GrowDirection.Begin); + LayoutContainer.SetGrowVertical(VersionLabel, LayoutContainer.GrowDirection.Begin); + layout.AddChild(VersionLabel); + } + } + } +} From a0d114c67235344a3782b6d9561e88f4f04a9115 Mon Sep 17 00:00:00 2001 From: zumorica Date: Sun, 5 Apr 2020 02:29:04 +0200 Subject: [PATCH 026/103] 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 027/103] 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 028/103] 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 029/103] 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 030/103] 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 02f9c5259c0d71471e5213069027d95bd9a51757 Mon Sep 17 00:00:00 2001 From: scuffedjays Date: Wed, 8 Apr 2020 06:07:54 -0500 Subject: [PATCH 031/103] Add foundation for Round End Summary Screen. Adjust GamePreset class, added title alongside description. --- .../GameTicking/ClientGameTicker.cs | 12 ++++ .../UserInterface/RoundEndSummaryWindow.cs | 56 +++++++++++++++++++ Content.Server/GameTicking/GamePreset.cs | 3 +- .../GamePresets/PresetDeathMatch.cs | 5 +- .../GameTicking/GamePresets/PresetSandbox.cs | 5 +- Content.Server/GameTicking/GameTicker.cs | 14 ++++- Content.Shared/SharedGameTicker.cs | 33 ++++++++++- 7 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 Content.Client/UserInterface/RoundEndSummaryWindow.cs diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs index 77ca609371..ad8bc976c8 100644 --- a/Content.Client/GameTicking/ClientGameTicker.cs +++ b/Content.Client/GameTicking/ClientGameTicker.cs @@ -1,6 +1,7 @@ using System; using Content.Client.Interfaces; using Content.Client.State; +using Content.Client.UserInterface; using Content.Shared; using Robust.Client.Interfaces.State; using Robust.Shared.Interfaces.Network; @@ -35,10 +36,13 @@ namespace Content.Client.GameTicking _netManager.RegisterNetMessage(nameof(MsgTickerJoinGame), JoinGame); _netManager.RegisterNetMessage(nameof(MsgTickerLobbyStatus), LobbyStatus); _netManager.RegisterNetMessage(nameof(MsgTickerLobbyInfo), LobbyInfo); + _netManager.RegisterNetMessage(nameof(MsgRoundEndMessage), RoundEnd); _initialized = true; } + + private void JoinLobby(MsgTickerJoinLobby message) { _stateManager.RequestStateChange(); @@ -64,5 +68,13 @@ namespace Content.Client.GameTicking { _stateManager.RequestStateChange(); } + + private void RoundEnd(MsgRoundEndMessage message) + { + + //This is not ideal at all, but I don't see an immediately better fit anywhere else. + var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.DurationInHours); + + } } } diff --git a/Content.Client/UserInterface/RoundEndSummaryWindow.cs b/Content.Client/UserInterface/RoundEndSummaryWindow.cs new file mode 100644 index 0000000000..b3abe84b69 --- /dev/null +++ b/Content.Client/UserInterface/RoundEndSummaryWindow.cs @@ -0,0 +1,56 @@ +using Robust.Client.Graphics; +using Robust.Client.Interfaces.Input; +using Robust.Client.Interfaces.ResourceManagement; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Content.Client.Utility; + +namespace Content.Client.UserInterface +{ + public sealed class RoundEndSummaryWindow : SS14Window + { + private readonly int _headerFontSize = 14; + private VBoxContainer VBox { get; } + + protected override Vector2? CustomSize => (520, 580); + +#pragma warning disable 649 + [Dependency] private readonly IResourceCache _resourceCache; + [Dependency] private readonly ILocalizationManager _loc; + [Dependency] private readonly IInputManager _inputManager; +#pragma warning restore 649 + + public RoundEndSummaryWindow(string gm, uint duration) + { + Title = "Round End Summary"; + + //Get section header font + _loc = IoCManager.Resolve(); + var cache = IoCManager.Resolve(); + var inputManager = IoCManager.Resolve(); + Font headerFont = new VectorFont(cache.GetResource("/Nano/NotoSans/NotoSans-Regular.ttf"), _headerFontSize); + + var scrollContainer = new ScrollContainer(); + scrollContainer.AddChild(VBox = new VBoxContainer()); + Contents.AddChild(scrollContainer); + + //Gamemode Name + var gamemodeLabel = new RichTextLabel(); + gamemodeLabel.SetMarkup(_loc.GetString("Round of: [color=white]{0}[/color] has ended.", gm)); + VBox.AddChild(gamemodeLabel); + + //Duration + var roundDurationInfo = new RichTextLabel(); + roundDurationInfo.SetMarkup(_loc.GetString("The round lasted for [color=yellow]{0}[/color] hours.", duration)); + VBox.AddChild(roundDurationInfo); + + OpenCentered(); + MoveToFront(); + + } + } +} diff --git a/Content.Server/GameTicking/GamePreset.cs b/Content.Server/GameTicking/GamePreset.cs index cf7981b725..b880b162a0 100644 --- a/Content.Server/GameTicking/GamePreset.cs +++ b/Content.Server/GameTicking/GamePreset.cs @@ -1,4 +1,4 @@ -namespace Content.Server.GameTicking +namespace Content.Server.GameTicking { /// /// A round-start setup preset, such as which antagonists to spawn. @@ -6,6 +6,7 @@ namespace Content.Server.GameTicking public abstract class GamePreset { public abstract void Start(); + public virtual string ModeTitle => "Sandbox"; public virtual string Description => "Secret!"; } } diff --git a/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs b/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs index 11c716442d..5b866dc8fa 100644 --- a/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs +++ b/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs @@ -1,4 +1,4 @@ -using Content.Server.GameTicking.GameRules; +using Content.Server.GameTicking.GameRules; using Content.Server.Interfaces.GameTicking; using Robust.Shared.IoC; @@ -15,6 +15,7 @@ namespace Content.Server.GameTicking.GamePresets _gameTicker.AddGameRule(); } - public override string Description => "Deathmatch, go and kill everybody else to win!"; + public override string ModeTitle => "Deathmatch"; + public override string Description => "Kill anything that moves!"; } } diff --git a/Content.Server/GameTicking/GamePresets/PresetSandbox.cs b/Content.Server/GameTicking/GamePresets/PresetSandbox.cs index f80b41ebd0..05f15c6972 100644 --- a/Content.Server/GameTicking/GamePresets/PresetSandbox.cs +++ b/Content.Server/GameTicking/GamePresets/PresetSandbox.cs @@ -1,4 +1,4 @@ -using Content.Server.Sandbox; +using Content.Server.Sandbox; using Robust.Shared.IoC; namespace Content.Server.GameTicking.GamePresets @@ -14,6 +14,7 @@ namespace Content.Server.GameTicking.GamePresets _sandboxManager.IsSandboxEnabled = true; } - public override string Description => "Sandbox, go and build something!"; + public override string ModeTitle => "Sandbox"; + public override string Description => "No stress, build something!"; } } diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index e8c654b2c3..3df9f50c4a 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -100,6 +100,7 @@ namespace Content.Server.GameTicking _netManager.RegisterNetMessage(nameof(MsgTickerJoinGame)); _netManager.RegisterNetMessage(nameof(MsgTickerLobbyStatus)); _netManager.RegisterNetMessage(nameof(MsgTickerLobbyInfo)); + _netManager.RegisterNetMessage(nameof(MsgRoundEndMessage)); SetStartPreset(_configurationManager.GetCVar("game.defaultpreset")); @@ -200,6 +201,13 @@ namespace Content.Server.GameTicking Logger.InfoS("ticker", "Ending round!"); RunLevel = GameRunLevel.PostRound; + + //Tell every client the round has ended. + var roundEndMessage = _netManager.CreateNetMessage(); + roundEndMessage.GamemodeTitle = MakeGamePreset().ModeTitle; + //TODO:Grab actual timespan of round. + roundEndMessage.DurationInHours = 1337; + _netManager.ServerSendToAll(roundEndMessage); } public void Respawn(IPlayerSession targetPlayer) @@ -559,10 +567,12 @@ namespace Content.Server.GameTicking private string GetInfoText() { - var gameMode = MakeGamePreset().Description; + var gmTitle = MakeGamePreset().ModeTitle; + var desc = MakeGamePreset().Description; return _localization.GetString(@"Hi and welcome to [color=white]Space Station 14![/color] -The current game mode is [color=white]{0}[/color]", gameMode); +The current game mode is: [color=white]{0}[/color]. +[color=yellow]{1}[/color]", gmTitle, desc ); } private void UpdateInfoText() diff --git a/Content.Shared/SharedGameTicker.cs b/Content.Shared/SharedGameTicker.cs index 126a44e0e9..616e53ef1c 100644 --- a/Content.Shared/SharedGameTicker.cs +++ b/Content.Shared/SharedGameTicker.cs @@ -1,4 +1,4 @@ -using System; +using System; using Lidgren.Network; using Robust.Shared.Interfaces.Network; using Robust.Shared.Network; @@ -114,5 +114,36 @@ namespace Content.Shared buffer.Write(TextBlob); } } + + protected class MsgRoundEndMessage : NetMessage + { + + #region REQUIRED + + public const MsgGroups GROUP = MsgGroups.Command; + public const string NAME = nameof(MsgRoundEndMessage); + public MsgRoundEndMessage(INetChannel channel) : base(NAME, GROUP) { } + + #endregion + + public string GamemodeTitle; + //TODO: Change to a more detailed measurement of time. + public uint DurationInHours; + + public override void ReadFromBuffer(NetIncomingMessage buffer) + { + GamemodeTitle = buffer.ReadString(); + DurationInHours = buffer.ReadUInt32(); + } + + public override void WriteToBuffer(NetOutgoingMessage buffer) + { + buffer.Write(GamemodeTitle); + buffer.Write(DurationInHours); + + } + + } } } + From 4174891c8701c837d66059a45b1be7a1ee3f09a7 Mon Sep 17 00:00:00 2001 From: Injazz Date: Wed, 8 Apr 2020 17:12:00 +0500 Subject: [PATCH 032/103] 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 033/103] 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 034/103] 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 035/103] 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 036/103] 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 037/103] 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 038/103] 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 039/103] 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 040/103] 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 041/103] 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 febe9aa46ca951815139f5b5084587da0b66da88 Mon Sep 17 00:00:00 2001 From: scuffedjays Date: Wed, 8 Apr 2020 19:09:02 -0500 Subject: [PATCH 042/103] Removed unused. Use Loc instead. --- .../UserInterface/RoundEndSummaryWindow.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Content.Client/UserInterface/RoundEndSummaryWindow.cs b/Content.Client/UserInterface/RoundEndSummaryWindow.cs index b3abe84b69..4c12d01b90 100644 --- a/Content.Client/UserInterface/RoundEndSummaryWindow.cs +++ b/Content.Client/UserInterface/RoundEndSummaryWindow.cs @@ -18,18 +18,10 @@ namespace Content.Client.UserInterface protected override Vector2? CustomSize => (520, 580); -#pragma warning disable 649 - [Dependency] private readonly IResourceCache _resourceCache; - [Dependency] private readonly ILocalizationManager _loc; - [Dependency] private readonly IInputManager _inputManager; -#pragma warning restore 649 - public RoundEndSummaryWindow(string gm, uint duration) { - Title = "Round End Summary"; + Title = Loc.GetString("Round End Summary"); - //Get section header font - _loc = IoCManager.Resolve(); var cache = IoCManager.Resolve(); var inputManager = IoCManager.Resolve(); Font headerFont = new VectorFont(cache.GetResource("/Nano/NotoSans/NotoSans-Regular.ttf"), _headerFontSize); @@ -40,12 +32,12 @@ namespace Content.Client.UserInterface //Gamemode Name var gamemodeLabel = new RichTextLabel(); - gamemodeLabel.SetMarkup(_loc.GetString("Round of: [color=white]{0}[/color] has ended.", gm)); + gamemodeLabel.SetMarkup(Loc.GetString("Round of: [color=white]{0}[/color] has ended.", gm)); VBox.AddChild(gamemodeLabel); //Duration var roundDurationInfo = new RichTextLabel(); - roundDurationInfo.SetMarkup(_loc.GetString("The round lasted for [color=yellow]{0}[/color] hours.", duration)); + roundDurationInfo.SetMarkup(Loc.GetString("The round lasted for [color=yellow]{0}[/color] hours.", duration)); VBox.AddChild(roundDurationInfo); OpenCentered(); 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 043/103] 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 044/103] 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 045/103] 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 046/103] 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 c47368231bc3af106aa3acabc14fd6544bb8b0fb Mon Sep 17 00:00:00 2001 From: scuffedjays Date: Wed, 8 Apr 2020 23:51:49 -0500 Subject: [PATCH 047/103] Add scrollable player list to round end menu with OOC Usernames. --- .../UserInterface/RoundEndSummaryWindow.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Content.Client/UserInterface/RoundEndSummaryWindow.cs b/Content.Client/UserInterface/RoundEndSummaryWindow.cs index 4c12d01b90..a12c9076ba 100644 --- a/Content.Client/UserInterface/RoundEndSummaryWindow.cs +++ b/Content.Client/UserInterface/RoundEndSummaryWindow.cs @@ -8,13 +8,22 @@ using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Maths; using Content.Client.Utility; +using Robust.Client.Player; +using System.Linq; +using static Robust.Client.UserInterface.Controls.ItemList; namespace Content.Client.UserInterface { public sealed class RoundEndSummaryWindow : SS14Window { + + #pragma warning disable 649 + [Dependency] private IPlayerManager _playerManager; +#pragma warning restore 649 + private readonly int _headerFontSize = 14; private VBoxContainer VBox { get; } + private ItemList _playerList; protected override Vector2? CustomSize => (520, 580); @@ -32,7 +41,7 @@ namespace Content.Client.UserInterface //Gamemode Name var gamemodeLabel = new RichTextLabel(); - gamemodeLabel.SetMarkup(Loc.GetString("Round of: [color=white]{0}[/color] has ended.", gm)); + gamemodeLabel.SetMarkup(Loc.GetString("Round of [color=white]{0}[/color] has ended.", gm)); VBox.AddChild(gamemodeLabel); //Duration @@ -40,9 +49,27 @@ namespace Content.Client.UserInterface roundDurationInfo.SetMarkup(Loc.GetString("The round lasted for [color=yellow]{0}[/color] hours.", duration)); VBox.AddChild(roundDurationInfo); + + //Populate list of players. + _playerManager = IoCManager.Resolve(); + _playerList = new ItemList() + { + SizeFlagsStretchRatio = 8, + SizeFlagsVertical = SizeFlags.FillExpand, + SelectMode = ItemList.ItemListSelectMode.Button + }; + + foreach (var session in _playerManager.Sessions.OrderBy(s => s.Name)) + { + var playerOOCName = session.SessionId.Username; + //No Mind data so no ICName/Job/Role, etc. + _playerList.AddItem(playerOOCName); + } + VBox.AddChild(_playerList); OpenCentered(); MoveToFront(); } } + } From 8f580ecc1bcf7acab88c66514a82544363b23b37 Mon Sep 17 00:00:00 2001 From: Injazz Date: Thu, 9 Apr 2020 16:43:56 +0500 Subject: [PATCH 048/103] 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 049/103] 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 050/103] 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 051/103] 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 052/103] 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 053/103] 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 054/103] 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 055/103] 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 056/103] 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 3ce50879b7510fa054cad4b42e4df627ec94b332 Mon Sep 17 00:00:00 2001 From: scuffedjays Date: Fri, 10 Apr 2020 01:37:14 -0500 Subject: [PATCH 057/103] New struct for basic round end info per player. --- .../GameTicking/ClientGameTicker.cs | 2 +- .../UserInterface/RoundEndSummaryWindow.cs | 15 +++++--- Content.Server/GameTicking/GameTicker.cs | 22 +++++++++++ Content.Shared/SharedGameTicker.cs | 38 +++++++++++++++++++ 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs index ad8bc976c8..621be8e936 100644 --- a/Content.Client/GameTicking/ClientGameTicker.cs +++ b/Content.Client/GameTicking/ClientGameTicker.cs @@ -73,7 +73,7 @@ namespace Content.Client.GameTicking { //This is not ideal at all, but I don't see an immediately better fit anywhere else. - var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.DurationInHours); + var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.DurationInHours, message.AllPlayersEndInfo); } } diff --git a/Content.Client/UserInterface/RoundEndSummaryWindow.cs b/Content.Client/UserInterface/RoundEndSummaryWindow.cs index a12c9076ba..b393edfddd 100644 --- a/Content.Client/UserInterface/RoundEndSummaryWindow.cs +++ b/Content.Client/UserInterface/RoundEndSummaryWindow.cs @@ -10,7 +10,9 @@ using Robust.Shared.Maths; using Content.Client.Utility; using Robust.Client.Player; using System.Linq; +using System.Collections.Generic; using static Robust.Client.UserInterface.Controls.ItemList; +using static Content.Shared.SharedGameTicker; namespace Content.Client.UserInterface { @@ -27,7 +29,7 @@ namespace Content.Client.UserInterface protected override Vector2? CustomSize => (520, 580); - public RoundEndSummaryWindow(string gm, uint duration) + public RoundEndSummaryWindow(string gm, uint duration, List info ) { Title = Loc.GetString("Round End Summary"); @@ -59,12 +61,15 @@ namespace Content.Client.UserInterface SelectMode = ItemList.ItemListSelectMode.Button }; - foreach (var session in _playerManager.Sessions.OrderBy(s => s.Name)) + foreach(var plyinfo in info) { - var playerOOCName = session.SessionId.Username; - //No Mind data so no ICName/Job/Role, etc. - _playerList.AddItem(playerOOCName); + var oocName = plyinfo.PlayerOOCName; + var icName = plyinfo.PlayerICName; + var role = plyinfo.Role; + var wasAntag = plyinfo.Antag; + _playerList.AddItem($"{oocName} was {icName} playing role of {role}."); } + VBox.AddChild(_playerList); OpenCentered(); MoveToFront(); diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 3df9f50c4a..a7b2b9bf61 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -205,8 +205,30 @@ namespace Content.Server.GameTicking //Tell every client the round has ended. var roundEndMessage = _netManager.CreateNetMessage(); roundEndMessage.GamemodeTitle = MakeGamePreset().ModeTitle; + //TODO:Grab actual timespan of round. roundEndMessage.DurationInHours = 1337; + + //Generate a list of basic player info to display in the end round summary. + var listOfPlayerInfo = new List(); + foreach(var ply in _playerManager.GetAllPlayers().OrderBy(p => p.Name)) + { + if (ply == null) continue; + if(ply.AttachedEntity.TryGetComponent(out var mindComponent) + && mindComponent.HasMind) + { + var playerEndRoundInfo = new RoundEndPlayerInfo() + { + PlayerOOCName = ply.Name, + PlayerICName = mindComponent.Mind.CurrentEntity.Name, + Role = mindComponent.Mind.AllRoles.First().Name, + Antag = false + }; + listOfPlayerInfo.Add(playerEndRoundInfo); + } + } + + roundEndMessage.AllPlayersEndInfo = listOfPlayerInfo; _netManager.ServerSendToAll(roundEndMessage); } diff --git a/Content.Shared/SharedGameTicker.cs b/Content.Shared/SharedGameTicker.cs index 616e53ef1c..41a1651d12 100644 --- a/Content.Shared/SharedGameTicker.cs +++ b/Content.Shared/SharedGameTicker.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using Lidgren.Network; +using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; using Robust.Shared.Network; @@ -114,6 +116,14 @@ namespace Content.Shared buffer.Write(TextBlob); } } + public struct RoundEndPlayerInfo + { + public string PlayerOOCName; + public string PlayerICName; + public string Role; + public bool Antag; + + } protected class MsgRoundEndMessage : NetMessage { @@ -130,10 +140,30 @@ namespace Content.Shared //TODO: Change to a more detailed measurement of time. public uint DurationInHours; + public uint PlayerCount; + + public List AllPlayersEndInfo; + public override void ReadFromBuffer(NetIncomingMessage buffer) { GamemodeTitle = buffer.ReadString(); DurationInHours = buffer.ReadUInt32(); + + PlayerCount = buffer.ReadUInt32(); + AllPlayersEndInfo = new List(); + for(var i = 0; i < PlayerCount + 1; i++) + { + var readPlayerData = new RoundEndPlayerInfo + { + PlayerOOCName = buffer.ReadString(), + PlayerICName = buffer.ReadString(), + Role = buffer.ReadString(), + Antag = buffer.ReadBoolean() + }; + + AllPlayersEndInfo.Add(readPlayerData); + } + } public override void WriteToBuffer(NetOutgoingMessage buffer) @@ -141,6 +171,14 @@ namespace Content.Shared buffer.Write(GamemodeTitle); buffer.Write(DurationInHours); + buffer.Write(PlayerCount); + foreach(var playerEndInfo in AllPlayersEndInfo) + { + buffer.Write(playerEndInfo.PlayerOOCName); + buffer.Write(playerEndInfo.PlayerICName); + buffer.Write(playerEndInfo.Role); + buffer.Write(playerEndInfo.Antag); + } } } From 503a2a8acfbbb348ec8ee8d664bdbeb583086dc1 Mon Sep 17 00:00:00 2001 From: Injazz Date: Fri, 10 Apr 2020 12:08:33 +0500 Subject: [PATCH 058/103] 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 059/103] 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 060/103] 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 061/103] 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 062/103] 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 063/103] 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 5d3018d76d856a4468f9e0621d457ea21edb1c7e Mon Sep 17 00:00:00 2001 From: scuffedjays Date: Sat, 11 Apr 2020 10:31:44 -0500 Subject: [PATCH 064/103] Change frm ItemList to list of labels with markup --- .../UserInterface/RoundEndSummaryWindow.cs | 51 ++++++++----------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/Content.Client/UserInterface/RoundEndSummaryWindow.cs b/Content.Client/UserInterface/RoundEndSummaryWindow.cs index b393edfddd..e68d6c8d38 100644 --- a/Content.Client/UserInterface/RoundEndSummaryWindow.cs +++ b/Content.Client/UserInterface/RoundEndSummaryWindow.cs @@ -18,59 +18,48 @@ namespace Content.Client.UserInterface { public sealed class RoundEndSummaryWindow : SS14Window { - - #pragma warning disable 649 - [Dependency] private IPlayerManager _playerManager; -#pragma warning restore 649 - - private readonly int _headerFontSize = 14; private VBoxContainer VBox { get; } - private ItemList _playerList; - protected override Vector2? CustomSize => (520, 580); public RoundEndSummaryWindow(string gm, uint duration, List info ) { Title = Loc.GetString("Round End Summary"); - - var cache = IoCManager.Resolve(); - var inputManager = IoCManager.Resolve(); - Font headerFont = new VectorFont(cache.GetResource("/Nano/NotoSans/NotoSans-Regular.ttf"), _headerFontSize); - - var scrollContainer = new ScrollContainer(); - scrollContainer.AddChild(VBox = new VBoxContainer()); - Contents.AddChild(scrollContainer); - + VBox = new VBoxContainer(); + Contents.AddChild(VBox); //Gamemode Name var gamemodeLabel = new RichTextLabel(); gamemodeLabel.SetMarkup(Loc.GetString("Round of [color=white]{0}[/color] has ended.", gm)); VBox.AddChild(gamemodeLabel); //Duration - var roundDurationInfo = new RichTextLabel(); - roundDurationInfo.SetMarkup(Loc.GetString("The round lasted for [color=yellow]{0}[/color] hours.", duration)); - VBox.AddChild(roundDurationInfo); + //var roundDurationInfo = new RichTextLabel(); + //roundDurationInfo.SetMarkup(Loc.GetString("The round lasted for [color=yellow]{0}[/color] hours.", duration)); + //VBox.AddChild(roundDurationInfo); - //Populate list of players. - _playerManager = IoCManager.Resolve(); - _playerList = new ItemList() - { - SizeFlagsStretchRatio = 8, - SizeFlagsVertical = SizeFlags.FillExpand, - SelectMode = ItemList.ItemListSelectMode.Button - }; + //Initialize what will be the list of players display. + var scrollContainer = new ScrollContainer(); + scrollContainer.SizeFlagsVertical = SizeFlags.FillExpand; + var innerScrollContainer = new VBoxContainer(); - foreach(var plyinfo in info) + //Create labels for each player info. + foreach (var plyinfo in info) { var oocName = plyinfo.PlayerOOCName; var icName = plyinfo.PlayerICName; var role = plyinfo.Role; var wasAntag = plyinfo.Antag; - _playerList.AddItem($"{oocName} was {icName} playing role of {role}."); + + var playerInfoText = new RichTextLabel(); + playerInfoText.SetMarkup(Loc.GetString($"[color=gray]{oocName}[/color] was [color=white]{icName}[/color] playing role of [color=orange]{role}[/color].")); + innerScrollContainer.AddChild(playerInfoText); } - VBox.AddChild(_playerList); + scrollContainer.AddChild(innerScrollContainer); + //Attach the entire ScrollContainer that holds all the playerinfo. + VBox.AddChild(scrollContainer); + + //Finally, display the window. OpenCentered(); MoveToFront(); From 5c6d045f3e627be280d5ff6d3de9ef1f257a9ab8 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 12 Apr 2020 00:58:06 +0200 Subject: [PATCH 065/103] 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 066/103] 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 18e10e289e4c2c59ae31f9833bf7ec11c737b4e9 Mon Sep 17 00:00:00 2001 From: scuffedjays Date: Sun, 12 Apr 2020 00:59:44 -0500 Subject: [PATCH 067/103] Added tabs to separate player info and general round info. Colored players IC name depending on antag status. --- .../UserInterface/RoundEndSummaryWindow.cs | 61 +++++++++++++------ 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/Content.Client/UserInterface/RoundEndSummaryWindow.cs b/Content.Client/UserInterface/RoundEndSummaryWindow.cs index e68d6c8d38..024182fab6 100644 --- a/Content.Client/UserInterface/RoundEndSummaryWindow.cs +++ b/Content.Client/UserInterface/RoundEndSummaryWindow.cs @@ -18,52 +18,79 @@ namespace Content.Client.UserInterface { public sealed class RoundEndSummaryWindow : SS14Window { - private VBoxContainer VBox { get; } + private VBoxContainer RoundEndSummaryTab { get; } + private VBoxContainer PlayerManifestoTab { get; } + private TabContainer RoundEndWindowTabs { get; } protected override Vector2? CustomSize => (520, 580); public RoundEndSummaryWindow(string gm, uint duration, List info ) { + Title = Loc.GetString("Round End Summary"); - VBox = new VBoxContainer(); - Contents.AddChild(VBox); + + //Round End Window is split into two tabs, one about the round stats + //and the other is a list of RoundEndPlayerInfo for each player. + //This tab would be a good place for things like: "x many people died.", + //"clown slipped the crew x times.", "x shots were fired this round.", etc. + //Also good for serious info. + RoundEndSummaryTab = new VBoxContainer() + { + Name = Loc.GetString("Round Information") + }; + + //Tab for listing unique info per player. + PlayerManifestoTab = new VBoxContainer() + { + Name = Loc.GetString("Player Manifesto") + }; + + RoundEndWindowTabs = new TabContainer(); + RoundEndWindowTabs.AddChild(RoundEndSummaryTab); + RoundEndWindowTabs.AddChild(PlayerManifestoTab); + + Contents.AddChild(RoundEndWindowTabs); + //Gamemode Name var gamemodeLabel = new RichTextLabel(); gamemodeLabel.SetMarkup(Loc.GetString("Round of [color=white]{0}[/color] has ended.", gm)); - VBox.AddChild(gamemodeLabel); - - //Duration - //var roundDurationInfo = new RichTextLabel(); - //roundDurationInfo.SetMarkup(Loc.GetString("The round lasted for [color=yellow]{0}[/color] hours.", duration)); - //VBox.AddChild(roundDurationInfo); + RoundEndSummaryTab.AddChild(gamemodeLabel); + //TODO: Implement showing the duration of the round. //Initialize what will be the list of players display. var scrollContainer = new ScrollContainer(); scrollContainer.SizeFlagsVertical = SizeFlags.FillExpand; var innerScrollContainer = new VBoxContainer(); + //Put antags on top of the list. + var manifestSortedList = info.OrderBy(p => !p.Antag); //Create labels for each player info. - foreach (var plyinfo in info) + foreach (var plyinfo in manifestSortedList) { - var oocName = plyinfo.PlayerOOCName; - var icName = plyinfo.PlayerICName; - var role = plyinfo.Role; - var wasAntag = plyinfo.Antag; - var playerInfoText = new RichTextLabel(); - playerInfoText.SetMarkup(Loc.GetString($"[color=gray]{oocName}[/color] was [color=white]{icName}[/color] playing role of [color=orange]{role}[/color].")); + var playerInfoText = new RichTextLabel() + { + SizeFlagsVertical = SizeFlags.Fill + }; + + //TODO: On Hover display a popup detailing more play info. + //For example: their antag goals and if they completed them sucessfully. + var icNameColor = plyinfo.Antag ? "red" : "white"; + playerInfoText.SetMarkup( + Loc.GetString($"[color=gray]{plyinfo.PlayerOOCName}[/color] was [color={icNameColor}]{plyinfo.PlayerICName}[/color] playing role of [color=orange]{plyinfo.Role}[/color].")); innerScrollContainer.AddChild(playerInfoText); } scrollContainer.AddChild(innerScrollContainer); //Attach the entire ScrollContainer that holds all the playerinfo. - VBox.AddChild(scrollContainer); + PlayerManifestoTab.AddChild(scrollContainer); //Finally, display the window. OpenCentered(); MoveToFront(); } + } } From d261a21b5772087c15aded0831fac78d7b7bc90b Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sun, 12 Apr 2020 14:44:14 +0200 Subject: [PATCH 068/103] 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 069/103] 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 070/103] 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 071/103] 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 072/103] 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; } From 409d873403c2eb327e4a73c0fa27dbdfc5ccf775 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Tue, 14 Apr 2020 15:41:23 +0200 Subject: [PATCH 073/103] Fix ratio issues on SlipSolution --- Content.Shared/Chemistry/Solution.cs | 7 +++++-- .../Shared/Chemistry/Solution_Tests.cs | 20 ++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index 13b15d94b4..5faefd8430 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -181,11 +181,13 @@ namespace Content.Shared.Chemistry newSolution = new Solution(); var newTotalVolume = ReagentUnit.New(0M); - var ratio = (TotalVolume - quantity).Decimal() / TotalVolume.Decimal(); + var remainingVolume = TotalVolume; for (var i = 0; i < _contents.Count; i++) { var reagent = _contents[i]; + var ratio = (remainingVolume - quantity).Decimal() / remainingVolume.Decimal(); + remainingVolume -= reagent.Quantity; var newQuantity = reagent.Quantity * ratio; var splitQuantity = reagent.Quantity - newQuantity; @@ -193,10 +195,11 @@ namespace Content.Shared.Chemistry _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); newSolution._contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity)); newTotalVolume += splitQuantity; + quantity -= splitQuantity; } - TotalVolume = TotalVolume * ratio; newSolution.TotalVolume = newTotalVolume; + TotalVolume -= newTotalVolume; return newSolution; } diff --git a/Content.Tests/Shared/Chemistry/Solution_Tests.cs b/Content.Tests/Shared/Chemistry/Solution_Tests.cs index f149d66043..d5d101353e 100644 --- a/Content.Tests/Shared/Chemistry/Solution_Tests.cs +++ b/Content.Tests/Shared/Chemistry/Solution_Tests.cs @@ -255,7 +255,25 @@ namespace Content.Tests.Shared.Chemistry Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(reduce)); Assert.That(splitSolution.TotalVolume.Float(), Is.EqualTo(reduce)); } - + + [Test] + [TestCase(2)] + [TestCase(10)] + [TestCase(100)] + [TestCase(1000)] + public void SplitRounding(int amount) + { + var solutionOne = new Solution(); + solutionOne.AddReagent("foo", ReagentUnit.New(amount)); + solutionOne.AddReagent("bar", ReagentUnit.New(amount)); + solutionOne.AddReagent("baz", ReagentUnit.New(amount)); + + var splitAmount = ReagentUnit.New(5); + var split = solutionOne.SplitSolution(splitAmount); + + Assert.That(split.TotalVolume, Is.EqualTo(splitAmount)); + } + [Test] public void SplitSolutionMoreThanTotalRemovesAll() { From 1605a500988a045cec5926d9bc18d5e961a34a96 Mon Sep 17 00:00:00 2001 From: scuffedjays Date: Tue, 14 Apr 2020 23:26:37 -0500 Subject: [PATCH 074/103] Changed duration to use TimeSpan. Using actual round realtime span to determine round length. --- Content.Client/GameTicking/ClientGameTicker.cs | 2 +- .../UserInterface/RoundEndSummaryWindow.cs | 9 +++++++-- Content.Server/GameTicking/GameTicker.cs | 5 +++-- Content.Shared/SharedGameTicker.cs | 17 ++++++++++++----- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs index 621be8e936..1d3d278a67 100644 --- a/Content.Client/GameTicking/ClientGameTicker.cs +++ b/Content.Client/GameTicking/ClientGameTicker.cs @@ -73,7 +73,7 @@ namespace Content.Client.GameTicking { //This is not ideal at all, but I don't see an immediately better fit anywhere else. - var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.DurationInHours, message.AllPlayersEndInfo); + var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundDuration, message.AllPlayersEndInfo); } } diff --git a/Content.Client/UserInterface/RoundEndSummaryWindow.cs b/Content.Client/UserInterface/RoundEndSummaryWindow.cs index 024182fab6..5e8600b188 100644 --- a/Content.Client/UserInterface/RoundEndSummaryWindow.cs +++ b/Content.Client/UserInterface/RoundEndSummaryWindow.cs @@ -13,6 +13,7 @@ using System.Linq; using System.Collections.Generic; using static Robust.Client.UserInterface.Controls.ItemList; using static Content.Shared.SharedGameTicker; +using System; namespace Content.Client.UserInterface { @@ -23,7 +24,7 @@ namespace Content.Client.UserInterface private TabContainer RoundEndWindowTabs { get; } protected override Vector2? CustomSize => (520, 580); - public RoundEndSummaryWindow(string gm, uint duration, List info ) + public RoundEndSummaryWindow(string gm, TimeSpan roundTimeSpan, List info ) { Title = Loc.GetString("Round End Summary"); @@ -55,7 +56,11 @@ namespace Content.Client.UserInterface gamemodeLabel.SetMarkup(Loc.GetString("Round of [color=white]{0}[/color] has ended.", gm)); RoundEndSummaryTab.AddChild(gamemodeLabel); - //TODO: Implement showing the duration of the round. + //Duration + var roundTimeLabel = new RichTextLabel(); + roundTimeLabel.SetMarkup(Loc.GetString("It lasted for [color=yellow]{0} hours, {1} minutes, and {2} seconds.", + roundTimeSpan.Hours,roundTimeSpan.Minutes,roundTimeSpan.Seconds)); + RoundEndSummaryTab.AddChild(roundTimeLabel); //Initialize what will be the list of players display. var scrollContainer = new ScrollContainer(); diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index a7b2b9bf61..2862b43f16 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -206,8 +206,9 @@ namespace Content.Server.GameTicking var roundEndMessage = _netManager.CreateNetMessage(); roundEndMessage.GamemodeTitle = MakeGamePreset().ModeTitle; - //TODO:Grab actual timespan of round. - roundEndMessage.DurationInHours = 1337; + //Get the timespan of the round. + var gameTime = IoCManager.Resolve(); + roundEndMessage.RoundDuration = gameTime.RealTime; //Generate a list of basic player info to display in the end round summary. var listOfPlayerInfo = new List(); diff --git a/Content.Shared/SharedGameTicker.cs b/Content.Shared/SharedGameTicker.cs index 41a1651d12..38d3e0985e 100644 --- a/Content.Shared/SharedGameTicker.cs +++ b/Content.Shared/SharedGameTicker.cs @@ -137,8 +137,8 @@ namespace Content.Shared #endregion public string GamemodeTitle; - //TODO: Change to a more detailed measurement of time. - public uint DurationInHours; + public TimeSpan RoundDuration; + public uint PlayerCount; @@ -147,7 +147,11 @@ namespace Content.Shared public override void ReadFromBuffer(NetIncomingMessage buffer) { GamemodeTitle = buffer.ReadString(); - DurationInHours = buffer.ReadUInt32(); + + var hours = buffer.ReadInt32(); + var mins = buffer.ReadInt32(); + var seconds = buffer.ReadInt32(); + RoundDuration = new TimeSpan(hours, mins, seconds); PlayerCount = buffer.ReadUInt32(); AllPlayersEndInfo = new List(); @@ -163,13 +167,16 @@ namespace Content.Shared AllPlayersEndInfo.Add(readPlayerData); } - + } public override void WriteToBuffer(NetOutgoingMessage buffer) { buffer.Write(GamemodeTitle); - buffer.Write(DurationInHours); + buffer.Write(RoundDuration.Hours); + buffer.Write(RoundDuration.Minutes); + buffer.Write(RoundDuration.Seconds); + buffer.Write(PlayerCount); foreach(var playerEndInfo in AllPlayersEndInfo) From 7ae8f8429d3d7ce87633477d9b0b9d753e4e5ec2 Mon Sep 17 00:00:00 2001 From: scuffedjays Date: Wed, 15 Apr 2020 17:46:53 -0500 Subject: [PATCH 075/103] Fixed calculation of round duration --- Content.Server/GameTicking/GameTicker.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index dccdffafd8..29662977bf 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -48,6 +48,7 @@ namespace Content.Server.GameTicking private const string PlayerPrototypeName = "HumanMob_Content"; private const string ObserverPrototypeName = "MobObserver"; private const string MapFile = "Maps/stationstation.yml"; + private static TimeSpan _roundStartTimeSpan; [ViewVariables] private readonly List _gameRules = new List(); [ViewVariables] private readonly List _manifest = new List(); @@ -194,6 +195,7 @@ namespace Content.Server.GameTicking SpawnPlayer(player, job, false); } + _roundStartTimeSpan = new TimeSpan(DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second); _sendStatusToAll(); } @@ -220,14 +222,15 @@ namespace Content.Server.GameTicking roundEndMessage.GamemodeTitle = MakeGamePreset().ModeTitle; //Get the timespan of the round. - var gameTime = IoCManager.Resolve(); - roundEndMessage.RoundDuration = gameTime.RealTime; + roundEndMessage.RoundDuration = new TimeSpan(DateTime.Now.Hour, + DateTime.Now.Minute, + DateTime.Now.Second) + .Subtract(_roundStartTimeSpan); //Generate a list of basic player info to display in the end round summary. var listOfPlayerInfo = new List(); foreach(var ply in _playerManager.GetAllPlayers().OrderBy(p => p.Name)) { - if (ply == null) continue; if(ply.AttachedEntity.TryGetComponent(out var mindComponent) && mindComponent.HasMind) { @@ -235,7 +238,7 @@ namespace Content.Server.GameTicking { PlayerOOCName = ply.Name, PlayerICName = mindComponent.Mind.CurrentEntity.Name, - Role = mindComponent.Mind.AllRoles.First().Name, + Role = mindComponent.Mind.AllRoles.First() != null ? mindComponent.Mind.AllRoles.First().Name : Loc.GetString("Unkown"), Antag = false }; listOfPlayerInfo.Add(playerEndRoundInfo); From 43b27e93bc081edd4a894855161059560ed3d796 Mon Sep 17 00:00:00 2001 From: scuffedjays Date: Thu, 16 Apr 2020 13:07:58 -0500 Subject: [PATCH 076/103] Fix the duration timing again. --- Content.Server/GameTicking/GameTicker.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 29662977bf..5d58779946 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -195,7 +195,7 @@ namespace Content.Server.GameTicking SpawnPlayer(player, job, false); } - _roundStartTimeSpan = new TimeSpan(DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second); + _roundStartTimeSpan = IoCManager.Resolve().RealTime; _sendStatusToAll(); } @@ -222,10 +222,7 @@ namespace Content.Server.GameTicking roundEndMessage.GamemodeTitle = MakeGamePreset().ModeTitle; //Get the timespan of the round. - roundEndMessage.RoundDuration = new TimeSpan(DateTime.Now.Hour, - DateTime.Now.Minute, - DateTime.Now.Second) - .Subtract(_roundStartTimeSpan); + roundEndMessage.RoundDuration = IoCManager.Resolve().RealTime.Subtract(_roundStartTimeSpan); //Generate a list of basic player info to display in the end round summary. var listOfPlayerInfo = new List(); @@ -238,7 +235,7 @@ namespace Content.Server.GameTicking { PlayerOOCName = ply.Name, PlayerICName = mindComponent.Mind.CurrentEntity.Name, - Role = mindComponent.Mind.AllRoles.First() != null ? mindComponent.Mind.AllRoles.First().Name : Loc.GetString("Unkown"), + Role = mindComponent.Mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("Unkown"), Antag = false }; listOfPlayerInfo.Add(playerEndRoundInfo); From f9ba0ddf24ab19af588b083e451cd845d055db65 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 16 Apr 2020 21:22:15 +0200 Subject: [PATCH 077/103] Update submodule. --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 3d6a3a6216..c3859bd52a 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 3d6a3a6216fa752f422a0f1720e02fec8e8e145e +Subproject commit c3859bd52a09a82938e05193fee2b64cc5eafb68 From a1c9ea8bbd1ec09397d3b15602009956e36b467a Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 16 Apr 2020 21:23:08 +0200 Subject: [PATCH 078/103] GameTicker watchdog update integration. --- Content.Server/GameTicking/GameTicker.cs | 67 +++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index b6e8c51492..7231fff4ba 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Content.Server.GameObjects; using Content.Server.GameObjects.Components.Access; using Content.Server.GameObjects.Components.Markers; @@ -16,9 +17,11 @@ using Content.Shared; using Content.Shared.Chat; using Content.Shared.Jobs; using Content.Shared.Preferences; +using Robust.Server.Interfaces; using Robust.Server.Interfaces.Maps; using Robust.Server.Interfaces.Player; using Robust.Server.Player; +using Robust.Server.ServerStatus; using Robust.Shared.Configuration; using Robust.Shared.Enums; using Robust.Shared.GameObjects; @@ -35,16 +38,18 @@ using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Timers; using Robust.Shared.Timing; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; +using Timer = Robust.Shared.Timers.Timer; namespace Content.Server.GameTicking { public partial class GameTicker : SharedGameTicker, IGameTicker { + private static readonly TimeSpan UpdateRestartDelay = TimeSpan.FromSeconds(20); + private const string PlayerPrototypeName = "HumanMob_Content"; private const string ObserverPrototypeName = "MobObserver"; private const string MapFile = "Maps/stationstation.yml"; @@ -67,6 +72,10 @@ namespace Content.Server.GameTicking [ViewVariables] private bool LobbyEnabled => _configurationManager.GetCVar("game.lobbyenabled"); + [ViewVariables] private bool _updateOnRoundEnd; + private CancellationTokenSource _updateShutdownCts; + + [ViewVariables] public GameRunLevel RunLevel { @@ -109,6 +118,16 @@ namespace Content.Server.GameTicking _initialized = true; JobControllerInit(); + + _watchdogApi.UpdateReceived += WatchdogApiOnUpdateReceived; + } + + private void WatchdogApiOnUpdateReceived() + { + _chatManager.DispatchServerAnnouncement(Loc.GetString( + "Update has been received, server will automatically restart for update at the end of this round.")); + _updateOnRoundEnd = true; + ServerEmptyUpdateRestartCheck(); } public void Update(FrameEventArgs frameEventArgs) @@ -122,6 +141,13 @@ namespace Content.Server.GameTicking public void RestartRound() { + if (_updateOnRoundEnd) + { + _baseServer.Shutdown( + Loc.GetString("Server is shutting down for update and will automatically restart.")); + return; + } + Logger.InfoS("ticker", "Restarting round!"); SendServerMessage("Restarting round..."); @@ -421,6 +447,11 @@ namespace Content.Server.GameTicking switch (args.NewStatus) { + case SessionStatus.Connecting: + // Cancel shutdown update timer in progress. + _updateShutdownCts?.Cancel(); + break; + case SessionStatus.Connected: { // Always make sure the client has player data. Mind gets assigned on spawn. @@ -475,11 +506,43 @@ namespace Content.Server.GameTicking if (_playersInLobby.ContainsKey(session)) _playersInLobby.Remove(session); _chatManager.DispatchServerAnnouncement($"Player {args.Session.SessionId} left server!"); + ServerEmptyUpdateRestartCheck(); break; } } } + /// + /// Checks whether there are still players on the server, + /// and if not starts a timer to automatically reboot the server if an update is available. + /// + private void ServerEmptyUpdateRestartCheck() + { + // Can't simple check the current connected player count since that doesn't update + // before PlayerStatusChanged gets fired. + // So in the disconnect handler we'd still see a single player otherwise. + var playersOnline = _playerManager.GetAllPlayers().Any(p => p.Status != SessionStatus.Disconnected); + if (playersOnline || !_updateOnRoundEnd) + { + // Still somebody online. + return; + } + + if (_updateShutdownCts != null && !_updateShutdownCts.IsCancellationRequested) + { + // Do nothing because I guess we already have a timer running..? + return; + } + + _updateShutdownCts = new CancellationTokenSource(); + + Timer.Spawn(UpdateRestartDelay, () => + { + _baseServer.Shutdown( + Loc.GetString("Server is shutting down for update and will automatically restart.")); + }, _updateShutdownCts.Token); + } + private void SpawnPlayer(IPlayerSession session, string jobId = null, bool lateJoin = true) { var character = (HumanoidCharacterProfile) _prefsManager @@ -623,6 +686,8 @@ The current game mode is [color=white]{0}[/color]", gameMode); [Dependency] private readonly ILocalizationManager _localization; [Dependency] private readonly IRobustRandom _robustRandom; [Dependency] private readonly IServerPreferencesManager _prefsManager; + [Dependency] private readonly IBaseServer _baseServer; + [Dependency] private readonly IWatchdogApi _watchdogApi; #pragma warning restore 649 } From 84d14ab1855b21c53b399bf2d2d3333d911cee8f Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 16 Apr 2020 21:23:26 +0200 Subject: [PATCH 079/103] Use new GetFromJson method for convenience. --- Content.Server/MoMMILink.cs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/Content.Server/MoMMILink.cs b/Content.Server/MoMMILink.cs index 2b7bcfc8bb..0ae57f928d 100644 --- a/Content.Server/MoMMILink.cs +++ b/Content.Server/MoMMILink.cs @@ -88,21 +88,15 @@ namespace Content.Server } var password = _configurationManager.GetCVar("status.mommipassword"); - OOCPostMessage message; - using (var streamReader = new StreamReader(request.Body, EncodingHelpers.UTF8)) - using (var jsonReader = new JsonTextReader(streamReader)) + OOCPostMessage message = null; + try { - var serializer = new JsonSerializer(); - try - { - message = serializer.Deserialize(jsonReader); - } - catch (JsonSerializationException) - { - response.StatusCode = (int) HttpStatusCode.BadRequest; - return true; - } + message = request.GetFromJson(); + } + catch (JsonSerializationException) + { + // message null so enters block down below. } if (message == null) From f80cc1429fc85fd801adf9f3986c02bd07fd0f78 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 17 Apr 2020 00:29:29 +0200 Subject: [PATCH 080/103] Attempt github actions --- .github/workflows/build-content.yml | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/build-content.yml diff --git a/.github/workflows/build-content.yml b/.github/workflows/build-content.yml new file mode 100644 index 0000000000..822d501023 --- /dev/null +++ b/.github/workflows/build-content.yml @@ -0,0 +1,39 @@ +# This is a basic workflow to help you get started with Actions + +name: Build Content + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + branches: [ master ] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + - name: Init submodule + run: git submodule update --init --recursive + + # Runs a single command using the runners shell + - name: Build and package + run: Tools/package_release_build.py -p windows mac linux + + - name: Generate hashes + run: Tools/generate_hashes.ps1 + + - name: Upload artifacts + uses: actions/upload-artifact@v1.0.0 + with: + # Artifact name + name: test + # Directory containing files to upload + path: release/ From b1ea06249316cc5bb8ae268a3720c96fdf3cfdf9 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 17 Apr 2020 00:39:26 +0200 Subject: [PATCH 081/103] More actions attempts --- .github/workflows/build-content.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-content.yml b/.github/workflows/build-content.yml index 822d501023..625c287761 100644 --- a/.github/workflows/build-content.yml +++ b/.github/workflows/build-content.yml @@ -34,6 +34,6 @@ jobs: uses: actions/upload-artifact@v1.0.0 with: # Artifact name - name: test + name: release # Directory containing files to upload path: release/ From 04eab336a4d721d390a2a171493db2ff2849fdf0 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 17 Apr 2020 02:36:18 +0200 Subject: [PATCH 082/103] Delete build-content.yml --- .github/workflows/build-content.yml | 39 ----------------------------- 1 file changed, 39 deletions(-) delete mode 100644 .github/workflows/build-content.yml diff --git a/.github/workflows/build-content.yml b/.github/workflows/build-content.yml deleted file mode 100644 index 625c287761..0000000000 --- a/.github/workflows/build-content.yml +++ /dev/null @@ -1,39 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: Build Content - -# Controls when the action will run. Triggers the workflow on push or pull request -# events but only for the master branch -on: - push: - branches: [ master ] - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - - name: Init submodule - run: git submodule update --init --recursive - - # Runs a single command using the runners shell - - name: Build and package - run: Tools/package_release_build.py -p windows mac linux - - - name: Generate hashes - run: Tools/generate_hashes.ps1 - - - name: Upload artifacts - uses: actions/upload-artifact@v1.0.0 - with: - # Artifact name - name: release - # Directory containing files to upload - path: release/ From 27771a31f05d57b5c8a8c645a7819ec0f8f090ca Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 17 Apr 2020 14:44:33 +0200 Subject: [PATCH 083/103] Improvements to allow better connection dialog in content. --- Content.Client/EntryPoint.cs | 15 +++- Content.Client/State/LauncherConnecting.cs | 100 ++++++++++++++++++--- RobustToolbox | 2 +- 3 files changed, 104 insertions(+), 13 deletions(-) diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index ff63ab58ad..7e5dd43715 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -232,14 +232,27 @@ namespace Content.Client { if (args.NewLevel == ClientRunLevel.Initialize) { - _stateManager.RequestStateChange(); + SwitchToDefaultState(args.OldLevel == ClientRunLevel.Connected || + args.OldLevel == ClientRunLevel.InGame); } }; + SwitchToDefaultState(); + } + + private void SwitchToDefaultState(bool disconnected = false) + { // Fire off into state dependent on launcher or not. + if (_gameController.LaunchState.FromLauncher) { _stateManager.RequestStateChange(); + var state = (LauncherConnecting) _stateManager.CurrentState; + + if (disconnected) + { + state.SetDisconnected(); + } } else { diff --git a/Content.Client/State/LauncherConnecting.cs b/Content.Client/State/LauncherConnecting.cs index df1af3c6dc..3a31b1237a 100644 --- a/Content.Client/State/LauncherConnecting.cs +++ b/Content.Client/State/LauncherConnecting.cs @@ -21,11 +21,17 @@ namespace Content.Client.State [Dependency] private readonly IStylesheetManager _stylesheetManager; [Dependency] private readonly IClientNetManager _clientNetManager; [Dependency] private readonly IGameController _gameController; + [Dependency] private readonly IBaseClient _baseClient; #pragma warning restore 649 private Control _control; private Label _connectStatus; + private Control _connectingStatus; + private Control _connectFail; + private Label _connectFailReason; + private Control _disconnected; + public override void Startup() { var panelTex = ResC.GetTexture("/Nano/button.svg.96dpi.png"); @@ -37,11 +43,12 @@ namespace Content.Client.State back.SetPatchMargin(StyleBox.Margin.All, 10); Button exitButton; - Label connectFailReason; - Control connectingStatus; + Button reconnectButton; + Button retryButton; var address = _gameController.LaunchState.Ss14Address ?? _gameController.LaunchState.ConnectAddress; + VBoxContainer disconnected; _control = new Control { Stylesheet = _stylesheetManager.SheetSpace, @@ -101,9 +108,10 @@ namespace Content.Client.State { new Control { + SizeFlagsVertical = Control.SizeFlags.FillExpand, Children = { - (connectingStatus = new VBoxContainer + (_connectingStatus = new VBoxContainer { SeparationOverride = 0, Children = @@ -121,9 +129,52 @@ namespace Content.Client.State }), } }), - (connectFailReason = new Label + (_connectFail = new VBoxContainer { - Align = Label.AlignMode.Center + Visible = false, + SeparationOverride = 0, + Children = + { + (_connectFailReason = new Label + { + Align = Label.AlignMode.Center + }), + + (retryButton = new Button + { + Text = "Retry", + SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter, + SizeFlagsVertical = + Control.SizeFlags.Expand | + Control.SizeFlags.ShrinkEnd + }) + } + }), + + (_disconnected = new VBoxContainer + { + SeparationOverride = 0, + Children = + { + new Label + { + Text = "Disconnected from server:", + Align = Label.AlignMode.Center + }, + new Label + { + Text = _baseClient.LastDisconnectReason, + Align = Label.AlignMode.Center + }, + (reconnectButton = new Button + { + Text = "Reconnect", + SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter, + SizeFlagsVertical = + Control.SizeFlags.Expand | + Control.SizeFlags.ShrinkEnd + }) + } }) } }, @@ -136,8 +187,6 @@ namespace Content.Client.State Text = address, StyleClasses = {StyleBase.StyleClassLabelSubText}, SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter, - SizeFlagsVertical = - Control.SizeFlags.ShrinkEnd | Control.SizeFlags.Expand, } } }, @@ -197,15 +246,25 @@ namespace Content.Client.State _gameController.Shutdown("Exit button pressed"); }; + void Retry(BaseButton.ButtonEventArgs args) + { + _baseClient.ConnectToServer(_gameController.LaunchState.ConnectEndpoint); + SetActivePage(Page.Connecting); + } + + reconnectButton.OnPressed += Retry; + retryButton.OnPressed += Retry; + _clientNetManager.ConnectFailed += (sender, args) => { - connectFailReason.Text = Loc.GetString("Failed to connect to server:\n{0}", args.Reason); - connectingStatus.Visible = false; - connectFailReason.Visible = true; + _connectFailReason.Text = Loc.GetString("Failed to connect to server:\n{0}", args.Reason); + SetActivePage(Page.ConnectFailed); }; _clientNetManager.ClientConnectStateChanged += ConnectStateChanged; + SetActivePage(Page.Connecting); + ConnectStateChanged(_clientNetManager.ClientConnectState); } @@ -213,7 +272,7 @@ namespace Content.Client.State { _connectStatus.Text = Loc.GetString(state switch { - ClientConnectionState.NotConnecting => "Not connecting?", + ClientConnectionState.NotConnecting => "You should not be seeing this", ClientConnectionState.ResolvingHost => "Resolving server address...", ClientConnectionState.EstablishingConnection => "Establishing initial connection...", ClientConnectionState.Handshake => "Doing handshake...", @@ -226,5 +285,24 @@ namespace Content.Client.State { _control.Dispose(); } + + public void SetDisconnected() + { + SetActivePage(Page.Disconnected); + } + + private void SetActivePage(Page page) + { + _connectingStatus.Visible = page == Page.Connecting; + _connectFail.Visible = page == Page.ConnectFailed; + _disconnected.Visible = page == Page.Disconnected; + } + + private enum Page + { + Connecting, + ConnectFailed, + Disconnected, + } } } diff --git a/RobustToolbox b/RobustToolbox index c3859bd52a..ba933f7db0 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit c3859bd52a09a82938e05193fee2b64cc5eafb68 +Subproject commit ba933f7db0d43cbe5a04144f652ee8324cf64606 From 7d6e6edcd1414141eda747c35003867e2a6116a6 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Fri, 17 Apr 2020 15:40:17 +0200 Subject: [PATCH 084/103] Disable collision for things inside EntityStorageComponents + enable putting mobs in lockers --- .../Items/Storage/EntityStorageComponent.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs index 77eab4ac52..75fa7111c2 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs @@ -150,8 +150,8 @@ namespace Content.Server.GameObjects.Components if(!entity.Transform.IsMapTransform) continue; - // only items that can be stored in an inventory, or a player, can be eaten by a locker - if(!entity.HasComponent() && !entity.HasComponent()) + // only items that can be stored in an inventory, or a mob, can be eaten by a locker + if (!entity.HasComponent() && !entity.HasComponent()) continue; if (!AddToContents(entity)) @@ -207,7 +207,8 @@ namespace Content.Server.GameObjects.Components private bool AddToContents(IEntity entity) { var collidableComponent = Owner.GetComponent(); - if(entity.TryGetComponent(out var entityCollidableComponent)) + ICollidableComponent entityCollidableComponent; + if (entity.TryGetComponent(out entityCollidableComponent)) { if(MaxSize < entityCollidableComponent.WorldAABB.Size.X || MaxSize < entityCollidableComponent.WorldAABB.Size.Y) @@ -247,6 +248,10 @@ namespace Content.Server.GameObjects.Components } Contents.Insert(entity); entity.Transform.WorldPosition = worldPos; + if (entityCollidableComponent != null) + { + entityCollidableComponent.CollisionEnabled = false; + } return true; } return false; @@ -256,7 +261,13 @@ namespace Content.Server.GameObjects.Components { foreach (var contained in Contents.ContainedEntities.ToArray()) { - Contents.Remove(contained); + if(Contents.Remove(contained)) + { + if (contained.TryGetComponent(out var entityCollidableComponent)) + { + entityCollidableComponent.CollisionEnabled = true; + } + } } } From 73d754e2d216f97dff18a2884ee6f728412b126d Mon Sep 17 00:00:00 2001 From: zumorica Date: Fri, 17 Apr 2020 18:51:51 +0200 Subject: [PATCH 085/103] Observers now get correct name on observe from lobby --- Content.Server/GameTicking/GameTicker.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index b2f0a862c3..910446c8df 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -630,12 +630,17 @@ namespace Content.Server.GameTicking private void _spawnObserver(IPlayerSession session) { + var name = _prefsManager + .GetPreferences(session.SessionId.Username) + .SelectedCharacter.Name; + _playerJoinGame(session); var data = session.ContentData(); data.WipeMind(); data.Mind = new Mind(session.SessionId); var mob = _spawnObserverMob(); + mob.Name = name; data.Mind.TransferTo(mob); } From cdf47aab4465544f8940f6944402a8fa6b93f6a0 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 17 Apr 2020 19:11:10 +0200 Subject: [PATCH 086/103] Fix ghosts not being hidden when you do-aghost. --- .../GameObjects/Components/Observer/GhostComponent.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Content.Client/GameObjects/Components/Observer/GhostComponent.cs b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs index 21403b3a76..c71e04a56b 100644 --- a/Content.Client/GameObjects/Components/Observer/GhostComponent.cs +++ b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs @@ -18,6 +18,8 @@ namespace Content.Client.GameObjects.Components.Observer [ViewVariables(VVAccess.ReadOnly)] public bool CanReturnToBody { get; private set; } = true; + private bool _isAttached; + #pragma warning disable 649 [Dependency] private readonly IGameHud _gameHud; [Dependency] private readonly IPlayerManager _playerManager; @@ -29,6 +31,12 @@ namespace Content.Client.GameObjects.Components.Observer base.OnRemove(); _gui?.Dispose(); + + // PlayerDetachedMsg might not fire due to deletion order so... + if (_isAttached) + { + SetGhostVisibility(false); + } } @@ -68,12 +76,14 @@ namespace Content.Client.GameObjects.Components.Observer _gameHud.HandsContainer.AddChild(_gui); SetGhostVisibility(true); + _isAttached = true; break; case PlayerDetachedMsg _: _gui.Parent?.RemoveChild(_gui); SetGhostVisibility(false); + _isAttached = false; break; } } From 01e8e4784b9e097d55b2b0e090c7d23b565da88e Mon Sep 17 00:00:00 2001 From: zumorica Date: Fri, 17 Apr 2020 19:16:42 +0200 Subject: [PATCH 087/103] Joining round as observer greys out return to body button --- Content.Server/GameTicking/GameTicker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 910446c8df..d81bafeffa 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -6,6 +6,7 @@ using Content.Server.GameObjects; using Content.Server.GameObjects.Components.Access; using Content.Server.GameObjects.Components.Markers; using Content.Server.GameObjects.Components.Mobs; +using Content.Server.GameObjects.Components.Observer; using Content.Server.GameTicking.GamePresets; using Content.Server.Interfaces; using Content.Server.Interfaces.Chat; @@ -641,6 +642,7 @@ namespace Content.Server.GameTicking var mob = _spawnObserverMob(); mob.Name = name; + mob.GetComponent().CanReturnToBody = false; data.Mind.TransferTo(mob); } From 02ccc6dc45fba00a8ba8ad507c09b29220f120b7 Mon Sep 17 00:00:00 2001 From: zumorica Date: Fri, 17 Apr 2020 19:19:37 +0200 Subject: [PATCH 088/103] Only delete ghosts on return to body or unghost --- Content.Server/Administration/AGhost.cs | 6 ++++-- .../GameObjects/Components/Observer/GhostComponent.cs | 2 +- Content.Server/Observer/Ghost.cs | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Content.Server/Administration/AGhost.cs b/Content.Server/Administration/AGhost.cs index 40547197e1..f4eb9fca29 100644 --- a/Content.Server/Administration/AGhost.cs +++ b/Content.Server/Administration/AGhost.cs @@ -1,10 +1,12 @@ -using Content.Server.GameObjects.Components.Observer; +using System.Timers; +using Content.Server.GameObjects.Components.Observer; using Content.Server.Players; using Robust.Server.Interfaces.Console; using Robust.Server.Interfaces.Player; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; +using Timer = Robust.Shared.Timers.Timer; namespace Content.Server.Administration { @@ -27,7 +29,7 @@ namespace Content.Server.Administration { var visiting = mind.VisitingEntity; mind.UnVisit(); - visiting.Delete(); + Timer.Spawn(100, visiting.Delete); } else { diff --git a/Content.Server/GameObjects/Components/Observer/GhostComponent.cs b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs index 6adc21ed33..a829ad5987 100644 --- a/Content.Server/GameObjects/Components/Observer/GhostComponent.cs +++ b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs @@ -50,6 +50,7 @@ namespace Content.Server.GameObjects.Components.Observer if (netChannel == null || netChannel == actor.playerSession.ConnectedClient) { actor.playerSession.ContentData().Mind.UnVisit(); + Timer.Spawn(100, Owner.Delete); } break; case PlayerAttachedMsg msg: @@ -58,7 +59,6 @@ namespace Content.Server.GameObjects.Components.Observer break; case PlayerDetachedMsg msg: msg.OldPlayer.VisibilityMask &= ~(int)VisibilityFlags.Ghost; - Timer.Spawn(100, Owner.Delete); break; default: break; diff --git a/Content.Server/Observer/Ghost.cs b/Content.Server/Observer/Ghost.cs index 712d673c5a..c87581f3d3 100644 --- a/Content.Server/Observer/Ghost.cs +++ b/Content.Server/Observer/Ghost.cs @@ -10,6 +10,7 @@ using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Map; +using Robust.Shared.Timers; namespace Content.Server.Observer { @@ -37,6 +38,7 @@ namespace Content.Server.Observer if (mind.VisitingEntity != null) { mind.UnVisit(); + Timer.Spawn(100, mind.VisitingEntity.Delete); } var position = player.AttachedEntity?.Transform.GridPosition ?? IoCManager.Resolve().GetObserverSpawnPoint(); From c2e328e5ae74c269d9bd572c505e55e40a8e0f69 Mon Sep 17 00:00:00 2001 From: zumorica Date: Fri, 17 Apr 2020 19:23:06 +0200 Subject: [PATCH 089/103] Turns out we don't need to spawn a timer to delete ghosts anymore --- Content.Server/Administration/AGhost.cs | 2 +- .../GameObjects/Components/Observer/GhostComponent.cs | 2 +- Content.Server/Observer/Ghost.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Server/Administration/AGhost.cs b/Content.Server/Administration/AGhost.cs index f4eb9fca29..3eff686aef 100644 --- a/Content.Server/Administration/AGhost.cs +++ b/Content.Server/Administration/AGhost.cs @@ -29,7 +29,7 @@ namespace Content.Server.Administration { var visiting = mind.VisitingEntity; mind.UnVisit(); - Timer.Spawn(100, visiting.Delete); + visiting.Delete(); } else { diff --git a/Content.Server/GameObjects/Components/Observer/GhostComponent.cs b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs index a829ad5987..bcb1365b56 100644 --- a/Content.Server/GameObjects/Components/Observer/GhostComponent.cs +++ b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs @@ -50,7 +50,7 @@ namespace Content.Server.GameObjects.Components.Observer if (netChannel == null || netChannel == actor.playerSession.ConnectedClient) { actor.playerSession.ContentData().Mind.UnVisit(); - Timer.Spawn(100, Owner.Delete); + Owner.Delete(); } break; case PlayerAttachedMsg msg: diff --git a/Content.Server/Observer/Ghost.cs b/Content.Server/Observer/Ghost.cs index c87581f3d3..12696ef85d 100644 --- a/Content.Server/Observer/Ghost.cs +++ b/Content.Server/Observer/Ghost.cs @@ -38,7 +38,7 @@ namespace Content.Server.Observer if (mind.VisitingEntity != null) { mind.UnVisit(); - Timer.Spawn(100, mind.VisitingEntity.Delete); + mind.VisitingEntity.Delete(); } var position = player.AttachedEntity?.Transform.GridPosition ?? IoCManager.Resolve().GetObserverSpawnPoint(); From a408cdad0a419c95291ea6bac6c0c87b69a884e2 Mon Sep 17 00:00:00 2001 From: zumorica Date: Fri, 17 Apr 2020 19:28:08 +0200 Subject: [PATCH 090/103] Fix ControlMob not deleting ghosts --- Content.Server/Administration/ControlMob.cs | 6 ++++++ Content.Server/GlobalVerbs/ControlMobVerb.cs | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/Content.Server/Administration/ControlMob.cs b/Content.Server/Administration/ControlMob.cs index 4a72b5dd89..8b08f3d99e 100644 --- a/Content.Server/Administration/ControlMob.cs +++ b/Content.Server/Administration/ControlMob.cs @@ -1,4 +1,5 @@ using Content.Server.GameObjects.Components.Mobs; +using Content.Server.GameObjects.Components.Observer; using Content.Server.Mobs; using Content.Server.Players; using Robust.Server.Interfaces.Console; @@ -55,9 +56,14 @@ namespace Content.Server.Administration return; } + var oldEntity = mind.CurrentEntity; + mindComponent.Mind?.TransferTo(null); mind.TransferTo(target); + if(oldEntity.HasComponent()) + oldEntity.Delete(); + } } } diff --git a/Content.Server/GlobalVerbs/ControlMobVerb.cs b/Content.Server/GlobalVerbs/ControlMobVerb.cs index 52150d57be..2da62dba18 100644 --- a/Content.Server/GlobalVerbs/ControlMobVerb.cs +++ b/Content.Server/GlobalVerbs/ControlMobVerb.cs @@ -1,6 +1,7 @@ using Content.Server.GameObjects; using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Nutrition; +using Content.Server.GameObjects.Components.Observer; using Content.Server.Players; using Content.Shared.GameObjects; using Robust.Server.Console; @@ -41,9 +42,13 @@ namespace Content.Server.GlobalVerbs { var userMind = user.GetComponent().playerSession.ContentData().Mind; var targetMind = target.GetComponent(); + var oldEntity = userMind.CurrentEntity; targetMind.Mind?.TransferTo(null); userMind.TransferTo(target); + + if(oldEntity.HasComponent()) + oldEntity.Delete(); } } } From 47e964a882e7fe8b99e18bcfa4741dd7b27f5ff1 Mon Sep 17 00:00:00 2001 From: zumorica Date: Fri, 17 Apr 2020 19:38:18 +0200 Subject: [PATCH 091/103] Deadchat now has color --- Content.Client/Chat/ChatManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Content.Client/Chat/ChatManager.cs b/Content.Client/Chat/ChatManager.cs index dd8f437142..8a0fcdeabd 100644 --- a/Content.Client/Chat/ChatManager.cs +++ b/Content.Client/Chat/ChatManager.cs @@ -184,6 +184,9 @@ namespace Content.Client.Chat case ChatChannel.OOC: color = Color.LightSkyBlue; break; + case ChatChannel.Dead: + color = Color.MediumPurple; + break; } _currentChatBox?.AddLine(messageText, message.Channel, color); From 13a6b3df7e910a0cd27cb65ffb8a6ca85a04f5cb Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 17 Apr 2020 21:17:05 +0200 Subject: [PATCH 092/103] Update submodule. --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index ba933f7db0..2cc52ea767 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit ba933f7db0d43cbe5a04144f652ee8324cf64606 +Subproject commit 2cc52ea767c883bb4ac28b0766f9502b85b48924 From 9d130d98d34c1792d25aad36928ddf3dd946aa10 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 17 Apr 2020 23:47:03 +0200 Subject: [PATCH 093/103] Remove some bad style decisions. --- .editorconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index b683dad667..993abbe794 100644 --- a/.editorconfig +++ b/.editorconfig @@ -40,8 +40,8 @@ csharp_space_between_method_declaration_parameter_list_parentheses=false csharp_space_between_parentheses=false csharp_space_between_square_brackets=false csharp_style_expression_bodied_accessors=true:suggestion -csharp_style_expression_bodied_constructors=true:suggestion -csharp_style_expression_bodied_methods=true:suggestion +csharp_style_expression_bodied_constructors=false:suggestion +csharp_style_expression_bodied_methods=false:suggestion csharp_style_expression_bodied_properties=true:suggestion csharp_style_var_elsewhere=true:suggestion csharp_style_var_for_built_in_types=true:suggestion From c4f147bab4230029bc8c457cd1cb34ba4551ce63 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 18 Apr 2020 00:38:19 +0200 Subject: [PATCH 094/103] Store original character name in mind. --- Content.Server/GameTicking/GameTicker.cs | 5 ++++- Content.Server/Mobs/Mind.cs | 5 ++++- Content.Server/Observer/Ghost.cs | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index d81bafeffa..764c65633a 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -583,7 +583,10 @@ namespace Content.Server.GameTicking var data = session.ContentData(); data.WipeMind(); - data.Mind = new Mind(session.SessionId); + data.Mind = new Mind(session.SessionId) + { + CharacterName = character.Name + }; if (jobId == null) { diff --git a/Content.Server/Mobs/Mind.cs b/Content.Server/Mobs/Mind.cs index 88b446928f..2b11bf8474 100644 --- a/Content.Server/Mobs/Mind.cs +++ b/Content.Server/Mobs/Mind.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Content.Server.GameObjects.Components.Mobs; using Content.Server.Players; @@ -48,6 +48,9 @@ namespace Content.Server.Mobs [ViewVariables] public IEntity CurrentEntity => VisitingEntity ?? OwnedEntity; + [ViewVariables(VVAccess.ReadWrite)] + public string CharacterName { get; set; } + /// /// The component currently owned by this mind. /// Can be null. diff --git a/Content.Server/Observer/Ghost.cs b/Content.Server/Observer/Ghost.cs index 12696ef85d..31a1d42bd3 100644 --- a/Content.Server/Observer/Ghost.cs +++ b/Content.Server/Observer/Ghost.cs @@ -63,7 +63,8 @@ namespace Content.Server.Observer var entityManager = IoCManager.Resolve(); var ghost = entityManager.SpawnEntity("MobObserver", position); - ghost.Name = name; + ghost.Name = mind.CharacterName; + var ghostComponent = ghost.GetComponent(); ghostComponent.CanReturnToBody = canReturn; From 4125c36e8d1e8651ee392dc829b513c0f1e92318 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 18 Apr 2020 00:38:43 +0200 Subject: [PATCH 095/103] Allow mind transfer to entity if said entity is also the current visit target. --- Content.Server/Mobs/Mind.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Content.Server/Mobs/Mind.cs b/Content.Server/Mobs/Mind.cs index 2b11bf8474..e931b80fbc 100644 --- a/Content.Server/Mobs/Mind.cs +++ b/Content.Server/Mobs/Mind.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Collections.Generic; using Content.Server.GameObjects.Components.Mobs; using Content.Server.Players; +using Robust.Server.Interfaces.GameObjects; using Robust.Server.Interfaces.Player; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; @@ -142,6 +143,7 @@ namespace Content.Server.Mobs public void TransferTo(IEntity entity) { MindComponent component = null; + bool alreadyAttached = false; if (entity != null) { if (!entity.TryGetComponent(out component)) @@ -153,6 +155,17 @@ namespace Content.Server.Mobs // TODO: Kick them out, maybe? throw new ArgumentException("That entity already has a mind.", nameof(entity)); } + + if (entity.TryGetComponent(out IActorComponent actor)) + { + // Happens when transferring to your currently visited entity. + if (actor.playerSession != Session) + { + throw new ArgumentException("Visit target already has a session.", nameof(entity)); + } + + alreadyAttached = true; + } } OwnedMob?.InternalEjectMind(); @@ -160,7 +173,7 @@ namespace Content.Server.Mobs OwnedMob?.InternalAssignMind(this); // Player is CURRENTLY connected. - if (Session != null && OwnedMob != null) + if (Session != null && OwnedMob != null && !alreadyAttached) { Session.AttachToEntity(entity); } From ce2d6175d2c9074c84c4a096ae110f68e0dd5cb6 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 18 Apr 2020 00:39:22 +0200 Subject: [PATCH 096/103] Handle MindComponent deletion: You now get turned into a ghost if it happens, or return to body gets disabled if you were already ghosted. --- .../Components/Mobs/MindComponent.cs | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Content.Server/GameObjects/Components/Mobs/MindComponent.cs b/Content.Server/GameObjects/Components/Mobs/MindComponent.cs index 882d827969..0505ddd363 100644 --- a/Content.Server/GameObjects/Components/Mobs/MindComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/MindComponent.cs @@ -1,4 +1,5 @@ -using Content.Server.Mobs; +using Content.Server.GameObjects.Components.Observer; +using Content.Server.Mobs; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.ViewVariables; @@ -46,13 +47,31 @@ namespace Content.Server.GameObjects.Components.Mobs Mind = value; } - public override void OnRemove() + protected override void Shutdown() { - base.OnRemove(); + base.Shutdown(); if (HasMind) { - Mind.TransferTo(null); + var visiting = Mind.VisitingEntity; + if (visiting != null) + { + if (visiting.TryGetComponent(out GhostComponent ghost)) + { + ghost.CanReturnToBody = false; + } + + Mind.TransferTo(visiting); + } + else + { + var ghost = Owner.EntityManager.SpawnEntity("MobObserver", Owner.Transform.GridPosition); + ghost.Name = Mind.CharacterName; + + var ghostComponent = ghost.GetComponent(); + ghostComponent.CanReturnToBody = false; + Mind.TransferTo(ghost); + } } } } From 4328d17994859a34309c4f2fe3d3a0b4b77ff63b Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 18 Apr 2020 00:39:36 +0200 Subject: [PATCH 097/103] Update submodule --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 2cc52ea767..12fb54904a 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 2cc52ea767c883bb4ac28b0766f9502b85b48924 +Subproject commit 12fb54904af95b7f2083fb51d649ffa3b2a817fd From b137d4eaf06724140338123146799300bd76e9b5 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 18 Apr 2020 01:10:26 +0200 Subject: [PATCH 098/103] Adds NoExamine SolutionCap. --- .../GameObjects/Components/Chemistry/SolutionComponent.cs | 7 +++++++ Content.Shared/Chemistry/SolutionCaps.cs | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 7e56d748ab..230c23c659 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -111,6 +111,8 @@ namespace Content.Server.GameObjects.Components.Chemistry /// public bool Injector => (Capabilities & SolutionCaps.Injector) != 0; + public bool NoExamine => (Capabilities & SolutionCaps.NoExamine) != 0; + /// public override void ExposeData(ObjectSerializer serializer) { @@ -273,6 +275,11 @@ namespace Content.Server.GameObjects.Components.Chemistry void IExamine.Examine(FormattedMessage message) { + if (NoExamine) + { + return; + } + message.AddText(_loc.GetString("Contains:\n")); if (ReagentList.Count == 0) { diff --git a/Content.Shared/Chemistry/SolutionCaps.cs b/Content.Shared/Chemistry/SolutionCaps.cs index 66fbc71801..6f5ecb3bda 100644 --- a/Content.Shared/Chemistry/SolutionCaps.cs +++ b/Content.Shared/Chemistry/SolutionCaps.cs @@ -24,6 +24,8 @@ namespace Content.Shared.Chemistry /// Allows us to have obscenely large containers that are harder to abuse in chem dispensers /// since they can't be placed directly in them. /// - FitsInDispenser = 16, + FitsInDispenser = 16, + + NoExamine = 32, } } From 8076ecfc2e4b17d7987a79adb4d320b6bb8798ac Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 18 Apr 2020 01:10:41 +0200 Subject: [PATCH 099/103] You can no longer see the solution contents of humans via examine. --- Resources/Prototypes/Entities/mobs/human.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/mobs/human.yml b/Resources/Prototypes/Entities/mobs/human.yml index fbe6939007..0815ecdd04 100644 --- a/Resources/Prototypes/Entities/mobs/human.yml +++ b/Resources/Prototypes/Entities/mobs/human.yml @@ -16,6 +16,7 @@ # Organs - type: Solution maxVol: 250 + caps: 32 - type: Bloodstream max_volume: 100 - type: Stomach From 028ca7a7320c38921bbf541f091b1ccfcb277936 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 18 Apr 2020 12:10:50 +0200 Subject: [PATCH 100/103] Remove hardcoded ghosting from MoverSystem. --- .../Components/Mobs/SpeciesComponent.cs | 13 ++++++++++++- .../GameObjects/EntitySystems/MoverSystem.cs | 14 ++++++++++---- .../Components/Movement/IRelayMoveInput.cs | 9 +++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 Content.Server/Interfaces/GameObjects/Components/Movement/IRelayMoveInput.cs diff --git a/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs b/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs index f3952cb92d..d3db6ac632 100644 --- a/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs @@ -3,9 +3,12 @@ using System.Collections.Generic; using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; +using Content.Server.Interfaces.GameObjects.Components.Movement; +using Content.Server.Observer; using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Mobs; using Robust.Server.GameObjects; +using Robust.Server.Interfaces.Player; using Robust.Shared.ContentPack; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -15,7 +18,7 @@ using Robust.Shared.Serialization; namespace Content.Server.GameObjects { [RegisterComponent] - public class SpeciesComponent : SharedSpeciesComponent, IActionBlocker, IOnDamageBehavior, IExAct + public class SpeciesComponent : SharedSpeciesComponent, IActionBlocker, IOnDamageBehavior, IExAct, IRelayMoveInput { /// /// Damagestates are reached by reaching a certain damage threshold, they will block actions after being reached @@ -198,6 +201,14 @@ namespace Content.Server.GameObjects Owner.GetComponent().TakeDamage(DamageType.Brute, bruteDamage, null); Owner.GetComponent().TakeDamage(DamageType.Heat, burnDamage, null); } + + void IRelayMoveInput.MoveInputPressed(IPlayerSession session) + { + if (CurrentDamageState is DeadState) + { + new Ghost().Execute(null, session, null); + } + } } /// diff --git a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs index 4b2038d855..82a38ca8ca 100644 --- a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs @@ -186,13 +186,19 @@ namespace Content.Server.GameObjects.EntitySystems private static void HandleDirChange(ICommonSession session, Direction dir, bool state) { - if (!TryGetAttachedComponent(session as IPlayerSession, out IMoverComponent moverComp)) + var playerSes = session as IPlayerSession; + if (!TryGetAttachedComponent(playerSes, out IMoverComponent moverComp)) return; - var owner = (session as IPlayerSession)?.AttachedEntity; + var owner = playerSes?.AttachedEntity; - if (owner != null && owner.TryGetComponent(out SpeciesComponent species) && species.CurrentDamageState is DeadState) - new Ghost().Execute(null, (IPlayerSession)session, null); + if (owner != null) + { + foreach (var comp in owner.GetAllComponents()) + { + comp.MoveInputPressed(playerSes); + } + } moverComp.SetVelocityDirection(dir, state); } diff --git a/Content.Server/Interfaces/GameObjects/Components/Movement/IRelayMoveInput.cs b/Content.Server/Interfaces/GameObjects/Components/Movement/IRelayMoveInput.cs new file mode 100644 index 0000000000..53df4ed7e1 --- /dev/null +++ b/Content.Server/Interfaces/GameObjects/Components/Movement/IRelayMoveInput.cs @@ -0,0 +1,9 @@ +using Robust.Server.Interfaces.Player; + +namespace Content.Server.Interfaces.GameObjects.Components.Movement +{ + public interface IRelayMoveInput + { + void MoveInputPressed(IPlayerSession session); + } +} From 1578a32fa144987175d6460af0da93ee6649f290 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 18 Apr 2020 13:46:35 +0200 Subject: [PATCH 101/103] Put base movement in control of MovementSpeedModifierComponent. PlayerInputMoverComponent was unsuitable because it gets added/removed dynamically and is specific to players (AIs don't use it). --- .../Movement/AiControllerComponent.cs | 29 ++++-------- .../MovementSpeedModifierComponent.cs | 25 ++++++++++ .../Movement/PlayerInputMoverComponent.cs | 46 ++++--------------- 3 files changed, 42 insertions(+), 58 deletions(-) diff --git a/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs b/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs index 755d00dee7..f5e947d3d3 100644 --- a/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs @@ -56,18 +56,6 @@ namespace Content.Server.GameObjects.Components.Movement serializer.DataField(ref _visionRadius, "vision", 8.0f); } - /// - /// Movement speed (m/s) that the entity walks, before modifiers - /// - [ViewVariables(VVAccess.ReadWrite)] - public float BaseWalkSpeed { get; set; } = PlayerInputMoverComponent.DefaultBaseWalkSpeed; - - /// - /// Movement speed (m/s) that the entity sprints, before modifiers - /// - [ViewVariables(VVAccess.ReadWrite)] - public float BaseSprintSpeed { get; set; } = PlayerInputMoverComponent.DefaultBaseSprintSpeed; - /// /// Movement speed (m/s) that the entity walks, after modifiers /// @@ -76,14 +64,15 @@ namespace Content.Server.GameObjects.Components.Movement { get { - float speed = BaseWalkSpeed; - if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) + if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) { - speed *= component.WalkSpeedModifier; + return component.CurrentWalkSpeed; } - return speed; + + return MovementSpeedModifierComponent.DefaultBaseWalkSpeed; } } + /// /// Movement speed (m/s) that the entity walks, after modifiers /// @@ -92,12 +81,12 @@ namespace Content.Server.GameObjects.Components.Movement { get { - float speed = BaseSprintSpeed; - if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) + if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) { - speed *= component.SprintSpeedModifier; + return component.CurrentSprintSpeed; } - return speed; + + return MovementSpeedModifierComponent.DefaultBaseSprintSpeed; } } diff --git a/Content.Server/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs b/Content.Server/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs index aaa6a38261..738884f463 100644 --- a/Content.Server/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs @@ -1,14 +1,20 @@ using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Movement { [RegisterComponent] public class MovementSpeedModifierComponent : Component { + public const float DefaultBaseWalkSpeed = 4.0f; + public const float DefaultBaseSprintSpeed = 7.0f; + public override string Name => "MovementSpeedModifier"; private float _cachedWalkSpeedModifier = 1.0f; + [ViewVariables] public float WalkSpeedModifier { get @@ -18,6 +24,7 @@ namespace Content.Server.GameObjects.Components.Movement } } private float _cachedSprintSpeedModifier; + [ViewVariables] public float SprintSpeedModifier { get @@ -27,6 +34,16 @@ namespace Content.Server.GameObjects.Components.Movement } } + [ViewVariables(VVAccess.ReadWrite)] + public float BaseWalkSpeed { get; set; } + [ViewVariables(VVAccess.ReadWrite)] + public float BaseSprintSpeed { get; set; } + + [ViewVariables] + public float CurrentWalkSpeed => WalkSpeedModifier * BaseWalkSpeed; + [ViewVariables] + public float CurrentSprintSpeed => SprintSpeedModifier * BaseSprintSpeed; + /// /// set to warn us that a component's movespeed modifier has changed /// @@ -37,6 +54,14 @@ namespace Content.Server.GameObjects.Components.Movement _movespeedModifiersNeedRefresh = true; } + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(this, p => p.BaseWalkSpeed, "baseWalkSpeed", 4); + serializer.DataField(this, p => p.BaseSprintSpeed, "baseSprintSpeed", 7); + } + /// /// Recalculate movement speed with current modifiers, or return early if no change /// diff --git a/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs b/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs index 969b071bd8..f3229c82ac 100644 --- a/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs @@ -22,9 +22,6 @@ namespace Content.Server.GameObjects.Components.Movement [ComponentReference(typeof(IMoverComponent))] public class PlayerInputMoverComponent : Component, IMoverComponent, ICollideSpecial { - public const float DefaultBaseWalkSpeed = 4.0f; - public const float DefaultBaseSprintSpeed = 7.0f; - #pragma warning disable 649 [Dependency] private readonly IConfigurationManager _configurationManager; #pragma warning restore 649 @@ -37,19 +34,6 @@ namespace Content.Server.GameObjects.Components.Movement /// public override string Name => "PlayerInputMover"; - - /// - /// Movement speed (m/s) that the entity walks, before modifiers - /// - [ViewVariables(VVAccess.ReadWrite)] - public float BaseWalkSpeed { get; set; } = DefaultBaseWalkSpeed; - - /// - /// Movement speed (m/s) that the entity sprints, before modifiers - /// - [ViewVariables(VVAccess.ReadWrite)] - public float BaseSprintSpeed { get; set; } = DefaultBaseSprintSpeed; - /// /// Movement speed (m/s) that the entity walks, after modifiers /// @@ -58,12 +42,12 @@ namespace Content.Server.GameObjects.Components.Movement { get { - float speed = BaseWalkSpeed; - if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) + if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) { - speed *= component.WalkSpeedModifier; + return component.CurrentWalkSpeed; } - return speed; + + return MovementSpeedModifierComponent.DefaultBaseWalkSpeed; } } /// @@ -74,12 +58,12 @@ namespace Content.Server.GameObjects.Components.Movement { get { - float speed = BaseSprintSpeed; - if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) + if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) { - speed *= component.SprintSpeedModifier; + return component.CurrentSprintSpeed; } - return speed; + + return MovementSpeedModifierComponent.DefaultBaseSprintSpeed; } } @@ -114,20 +98,6 @@ namespace Content.Server.GameObjects.Components.Movement base.OnAdd(); } - /// - public override void ExposeData(ObjectSerializer serializer) - { - base.ExposeData(serializer); - - //only save the base speeds - the current speeds are transient. - serializer.DataReadWriteFunction("wspd", DefaultBaseWalkSpeed, value => BaseWalkSpeed = value, () => BaseWalkSpeed); - serializer.DataReadWriteFunction("rspd", DefaultBaseSprintSpeed, value => BaseSprintSpeed = value, () => BaseSprintSpeed); - - // The velocity and moving directions is usually set from player or AI input, - // so we don't want to save/load these derived fields. - } - - /// /// Toggles one of the four cardinal directions. Each of the four directions are /// composed into a single direction vector, . Enabling From 20295723b294bd963b9eedf71fcf8f1f67132563 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 18 Apr 2020 13:46:56 +0200 Subject: [PATCH 102/103] Make ghosts twice as fast. --- Resources/Prototypes/Entities/mobs/observer.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/Prototypes/Entities/mobs/observer.yml b/Resources/Prototypes/Entities/mobs/observer.yml index 26d817f212..2a6b6d90b5 100644 --- a/Resources/Prototypes/Entities/mobs/observer.yml +++ b/Resources/Prototypes/Entities/mobs/observer.yml @@ -20,3 +20,7 @@ netsync: false drawdepth: Mobs texture: Mob/observer.png + + - type: MovementSpeedModifier + baseSprintSpeed: 14 + baseWalkSpeed: 7 From dd6c919a32f6e4c5fe2ffcc0ce9ed7973f4a4aaf Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 18 Apr 2020 13:47:06 +0200 Subject: [PATCH 103/103] Re-enable run bind. --- Resources/keybinds.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index 7076f916eb..504ec81af9 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -23,9 +23,9 @@ binds: - function: MoveDown type: State key: S -#- function: Run -# type: State -# key: Shift +- function: Run + type: State + key: Shift - function: ShowEscapeMenu type: State key: Escape