diff --git a/Content.Client/GameObjects/Components/Atmos/ExtinguisherVisualizer.cs b/Content.Client/GameObjects/Components/Atmos/ExtinguisherVisualizer.cs new file mode 100644 index 0000000000..6a33b51e00 --- /dev/null +++ b/Content.Client/GameObjects/Components/Atmos/ExtinguisherVisualizer.cs @@ -0,0 +1,67 @@ +using System; +using JetBrains.Annotations; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Client.GameObjects.Components.Animations; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.Animations; +using Robust.Shared.Maths; +using Content.Shared.GameObjects.Components; + +namespace Content.Client.GameObjects.Components.Atmos +{ + [UsedImplicitly] + public class ExtinguisherVisualizer : AppearanceVisualizer + { + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + if (component.Deleted) + { + return; + } + + if (component.TryGetData(ExtinguisherVisuals.Rotation, out var degrees)) + { + SetRotation(component, Angle.FromDegrees(degrees)); + } + } + + private void SetRotation(AppearanceComponent component, Angle rotation) + { + var sprite = component.Owner.GetComponent(); + + if (!sprite.Owner.TryGetComponent(out AnimationPlayerComponent animation)) + { + sprite.Rotation = rotation; + return; + } + + if (animation.HasRunningAnimation("rotate")) + { + animation.Stop("rotate"); + } + + animation.Play(new Animation + { + Length = TimeSpan.FromSeconds(0.125), + AnimationTracks = + { + new AnimationTrackComponentProperty + { + ComponentType = typeof(ISpriteComponent), + Property = nameof(ISpriteComponent.Rotation), + InterpolationMode = AnimationInterpolationMode.Linear, + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(sprite.Rotation, 0), + new AnimationTrackProperty.KeyFrame(rotation, 0.125f) + } + } + } + }, "rotate"); + } + } +} diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs index fd11f67b25..d4588e5c84 100644 --- a/Content.Client/IgnoredComponents.cs +++ b/Content.Client/IgnoredComponents.cs @@ -156,6 +156,8 @@ "Vapor", "DamageOnHighSpeedImpact", "Barotrauma", + "GasSprayer", + "GasVapor", "MobStateManager", "Metabolism", }; diff --git a/Content.Server/Atmos/GasSprayerComponent.cs b/Content.Server/Atmos/GasSprayerComponent.cs new file mode 100644 index 0000000000..4b78d413a9 --- /dev/null +++ b/Content.Server/Atmos/GasSprayerComponent.cs @@ -0,0 +1,74 @@ +using Content.Server.GameObjects.Components.Chemistry; +using Content.Server.Interfaces; +using Content.Shared.Chemistry; +using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Pointing; +using Content.Shared.Interfaces.GameObjects.Components; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; + + +namespace Content.Server.Atmos +{ + [RegisterComponent] + public class GasSprayerComponent : Component, IAfterInteract + { +#pragma warning disable 649 + [Dependency] private readonly IServerNotifyManager _notifyManager = default!; + [Dependency] private readonly IServerEntityManager _serverEntityManager = default!; +#pragma warning restore 649 + + //TODO: create a function that can create a gas based on a solution mix + public override string Name => "GasSprayer"; + + private string _spraySound; + private string _sprayType; + private string _fuelType; + private string _fuelName; + private int _fuelCost; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(ref _spraySound, "spraySound", string.Empty); + serializer.DataField(ref _sprayType, "sprayType", string.Empty); + serializer.DataField(ref _fuelType, "fuelType", string.Empty); + serializer.DataField(ref _fuelName, "fuelName", "fuel"); + serializer.DataField(ref _fuelCost, "fuelCost", 50); + } + + public void AfterInteract(AfterInteractEventArgs eventArgs) + { + if (!Owner.TryGetComponent(out SolutionComponent tank)) + return; + + if (tank.Solution.GetReagentQuantity(_fuelType) == 0) + { + _notifyManager.PopupMessage(Owner, eventArgs.User, + Loc.GetString("{0:theName} is out of {1}!", Owner, _fuelName)); + } + else + { + tank.TryRemoveReagent(_fuelType, ReagentUnit.New(_fuelCost)); + + var playerPos = eventArgs.User.Transform.GridPosition; + var direction = (eventArgs.ClickLocation.Position - playerPos.Position).Normalized; + playerPos.Offset(direction/2); + + var spray = _serverEntityManager.SpawnEntity(_sprayType, playerPos); + spray.GetComponent() + .SetData(ExtinguisherVisuals.Rotation, direction.ToAngle().Degrees); + spray.GetComponent().StartMove(direction, 5); + + EntitySystem.Get().PlayFromEntity(_spraySound, Owner); + } + } + } +} diff --git a/Content.Server/Atmos/GasVaporComponent.cs b/Content.Server/Atmos/GasVaporComponent.cs new file mode 100644 index 0000000000..b28d2396a6 --- /dev/null +++ b/Content.Server/Atmos/GasVaporComponent.cs @@ -0,0 +1,120 @@ +using Content.Shared.Physics; +using Content.Server.Atmos.Reactions; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using Content.Server.GameObjects.Components.Atmos; +using Content.Server.Interfaces; +using Content.Shared.Atmos; + +namespace Content.Server.Atmos +{ + [RegisterComponent] + class GasVaporComponent : Component, ICollideBehavior, IGasMixtureHolder + { + [Dependency] private readonly IMapManager _mapManager = default!; + public override string Name => "GasVapor"; + + [ViewVariables] public GasMixture Air { get; set; } + + private bool _running; + private Vector2 _direction; + private float _velocity; + private float _disspateTimer = 0; + private float _dissipationInterval; + private Gas _gas; + private float _gasVolume; + private float _gasTemperature; + private float _gasAmount; + + public override void Initialize() + { + base.Initialize(); + Air = new GasMixture(_gasVolume){Temperature = _gasTemperature}; + Air.SetMoles(_gas,_gasAmount); + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(ref _dissipationInterval, "dissipationInterval", 1); + serializer.DataField(ref _gas, "gas", Gas.WaterVapor); + serializer.DataField(ref _gasVolume, "gasVolume", 200); + serializer.DataField(ref _gasTemperature, "gasTemperature", Atmospherics.T20C); + serializer.DataField(ref _gasAmount, "gasAmount", 20); + } + + public void StartMove(Vector2 dir, float velocity) + { + _running = true; + _direction = dir; + _velocity = velocity; + + if (Owner.TryGetComponent(out ICollidableComponent collidable)) + { + var controller = collidable.EnsureController(); + controller.Move(_direction, _velocity); + } + } + + public void Update(float frameTime) + { + if (!_running) + return; + + if (Owner.TryGetComponent(out ICollidableComponent collidable)) + { + var worldBounds = collidable.WorldAABB; + var mapGrid = _mapManager.GetGrid(Owner.Transform.GridID); + + var tiles = mapGrid.GetTilesIntersecting(worldBounds); + + foreach (var tile in tiles) + { + var pos = tile.GridIndices.ToGridCoordinates(_mapManager, tile.GridIndex); + var atmos = AtmosHelpers.GetTileAtmosphere(pos); + + if (atmos.Air == null) + { + return; + } + + if (atmos.Air.React(this) != ReactionResult.NoReaction) + { + Owner.Delete(); + } + } + } + + _disspateTimer += frameTime; + if (_disspateTimer > _dissipationInterval) + { + Air.SetMoles(_gas, Air.TotalMoles/2 ); + } + + if (Air.TotalMoles < 1) + { + Owner.Delete(); + } + } + + void ICollideBehavior.CollideWith(IEntity collidedWith) + { + // Check for collision with a impassable object (e.g. wall) and stop + if (collidedWith.TryGetComponent(out ICollidableComponent collidable) && + (collidable.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && + collidable.Hard && + Owner.TryGetComponent(out ICollidableComponent coll)) + { + var controller = coll.EnsureController(); + controller.Stop(); + Owner.Delete(); + } + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/GasVaporSystem.cs b/Content.Server/GameObjects/EntitySystems/GasVaporSystem.cs new file mode 100644 index 0000000000..a12600f634 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/GasVaporSystem.cs @@ -0,0 +1,20 @@ +using Content.Server.Atmos; +using Robust.Shared.GameObjects.Systems; + +namespace Content.Server.GameObjects.EntitySystems +{ + public class GasVaporSystem : EntitySystem + { + /// + public override void Update(float frameTime) + { + foreach (var GasVapor in ComponentManager.EntityQuery()) + { + if (GasVapor.Initialized) + { + GasVapor.Update(frameTime); + } + } + } + } +} diff --git a/Content.Shared/GameObjects/Components/SharedGasSprayerComponent.cs b/Content.Shared/GameObjects/Components/SharedGasSprayerComponent.cs new file mode 100644 index 0000000000..a15f92882c --- /dev/null +++ b/Content.Shared/GameObjects/Components/SharedGasSprayerComponent.cs @@ -0,0 +1,17 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components +{ + public class SharedGasSprayerComponent : Component + { + public sealed override string Name => "GasSprayer"; + } + + [Serializable, NetSerializable] + public enum ExtinguisherVisuals + { + Rotation + } +} diff --git a/Content.Shared/Physics/GasVaporController.cs b/Content.Shared/Physics/GasVaporController.cs new file mode 100644 index 0000000000..d58c15176d --- /dev/null +++ b/Content.Shared/Physics/GasVaporController.cs @@ -0,0 +1,16 @@ +using Robust.Shared.Maths; +using Robust.Shared.Physics; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Content.Shared.Physics +{ + public class GasVaporController : VirtualController + { + public void Move(Vector2 velocityDirection, float speed) + { + LinearVelocity = velocityDirection * speed; + } + } +} diff --git a/Resources/Prototypes/Entities/Objects/Misc/extinguisher_spray.yml b/Resources/Prototypes/Entities/Objects/Misc/extinguisher_spray.yml new file mode 100644 index 0000000000..da84fe7e02 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Misc/extinguisher_spray.yml @@ -0,0 +1,28 @@ +- type: entity + name: Extinguisher Spray + id: ExtinguisherSpray + description: Extinguisher Spray + components: + - type: Sprite + sprite: Effects/extinguisherSpray.rsi + layers: + - state: extinguish + - type: Icon + sprite: Effects/extinguisherSpray.rsi + state: extinguish + - type: GasVapor + dissipationInterval: 1 + gas: WaterVapor + gasVolume: 200 + gasTemperature: 293.15 + gasAmount: 20 + - type: Physics + - type: Collidable + shapes: + - !type:PhysShapeAabb + bounds: "-0.25,-0.25,0.25,0.25" + mask: + - Impassable + - type: Appearance + visuals: + - type: ExtinguisherVisualizer diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index a6c264b1d7..c341ef56d0 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -4,9 +4,26 @@ id: FireExtinguisher description: Extinguishes fires. components: - - type: Sprite - texture: Objects/Misc/fire_extinguisher.png - - type: Icon - texture: Objects/Misc/fire_extinguisher.png - - type: Item - size: 10 + - type: Sprite + sprite: Objects/Misc/fire_extinguisher.rsi + layers: + - state: fire_extinguisher_open + - type: Icon + sprite: Objects/Misc/fire_extinguisher.rsi + state: fire_extinguisher_open + - type: Item + sprite: Objects/Misc/fire_extinguisher.rsi + size: 10 + - type: Solution + maxVol: 1000 + caps: 9 + contents: + reagents: + - ReagentId: chem.H2O + Quantity: 1000 + - type: GasSprayer + spraySound: /Audio/Effects/spray.ogg + sprayType: ExtinguisherSpray + fuelType: chem.H2O + fuelName: water + fuelCost: 50 diff --git a/Resources/Textures/Effects/explosion.rsi/meta.json b/Resources/Textures/Effects/explosion.rsi/meta.json index d552172063..c70a3e60f8 100644 --- a/Resources/Textures/Effects/explosion.rsi/meta.json +++ b/Resources/Textures/Effects/explosion.rsi/meta.json @@ -1 +1,25 @@ -{"version": 1, "size": {"x": 96, "y": 96}, "states": [{"name": "explosionfast", "directions": 1, "delays": [[0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06]]}]} \ No newline at end of file +{ + "version": 1, + "size": { + "x": 96, + "y": 96 + }, + "states": [ + { + "name": "explosionfast", + "directions": 1, + "delays": [ + [ + 0.06, + 0.06, + 0.06, + 0.06, + 0.06, + 0.06, + 0.06, + 0.06 + ] + ] + } + ] +} diff --git a/Resources/Textures/Effects/extinguisherSpray.rsi/extinguish.png b/Resources/Textures/Effects/extinguisherSpray.rsi/extinguish.png new file mode 100644 index 0000000000..0eb6d7ebd5 Binary files /dev/null and b/Resources/Textures/Effects/extinguisherSpray.rsi/extinguish.png differ diff --git a/Resources/Textures/Effects/extinguisherSpray.rsi/meta.json b/Resources/Textures/Effects/extinguisherSpray.rsi/meta.json new file mode 100644 index 0000000000..9e3c4a97b6 --- /dev/null +++ b/Resources/Textures/Effects/extinguisherSpray.rsi/meta.json @@ -0,0 +1,63 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "extinguish", + "directions": 8, + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + } + ] +} diff --git a/Resources/Textures/Objects/Misc/fire_extinguisher.png b/Resources/Textures/Objects/Misc/fire_extinguisher.png deleted file mode 100644 index 87a128c8fe..0000000000 Binary files a/Resources/Textures/Objects/Misc/fire_extinguisher.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/fire_extinguisher_closed.png b/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/fire_extinguisher_closed.png new file mode 100644 index 0000000000..7c99615ffe Binary files /dev/null and b/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/fire_extinguisher_closed.png differ diff --git a/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/fire_extinguisher_open.png b/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/fire_extinguisher_open.png new file mode 100644 index 0000000000..7909d14f26 Binary files /dev/null and b/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/fire_extinguisher_open.png differ diff --git a/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/inhand-left.png b/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/inhand-left.png new file mode 100644 index 0000000000..fb697da356 Binary files /dev/null and b/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/inhand-right.png b/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/inhand-right.png new file mode 100644 index 0000000000..fb697da356 Binary files /dev/null and b/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/meta.json b/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/meta.json new file mode 100644 index 0000000000..d4447ddf98 --- /dev/null +++ b/Resources/Textures/Objects/Misc/fire_extinguisher.rsi/meta.json @@ -0,0 +1,65 @@ +{ + "version": 1, + "license": "CC BY-SA 3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation at commit 9bebd81ae0b0a7f952b59886a765c681205de31f", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "fire_extinguisher_open", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "fire_extinguisher_closed", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "inhand-right", + "directions": 4, + "delays": [ + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ] + ] + }, + { + "name": "inhand-left", + "directions": 4, + "delays": [ + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ] + ] + } + ] +} diff --git a/Resources/Textures/Objects/Tools/lantern.rsi/meta.json b/Resources/Textures/Objects/Tools/lantern.rsi/meta.json index 2205be15d5..745406c95d 100644 --- a/Resources/Textures/Objects/Tools/lantern.rsi/meta.json +++ b/Resources/Textures/Objects/Tools/lantern.rsi/meta.json @@ -1,6 +1,6 @@ { "version": 1, - "license": "CC BY-SA 3.0", + "license": "CC BY-SA 3.0", "copyright": "Taken from https://github.com/tgstation/tgstation at commit 9bebd81ae0b0a7f952b59886a765c681205de31f", "size": { "x": 32,