diff --git a/Content.Client/Construction/ConstructionMenu.cs b/Content.Client/Construction/ConstructionMenu.cs index 81b14fe21d..d8c6a87b91 100644 --- a/Content.Client/Construction/ConstructionMenu.cs +++ b/Content.Client/Construction/ConstructionMenu.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Content.Client.GameObjects.Components.Construction; using Content.Shared.Construction; +using Content.Shared.GameObjects.Components.Interactable; using Robust.Client.Graphics; using Robust.Client.Interfaces.Placement; using Robust.Client.Interfaces.ResourceManagement; @@ -180,27 +181,27 @@ namespace Content.Client.Construction break; case ConstructionStepTool tool: - switch (tool.Tool) + switch (tool.ToolQuality) { - case ConstructionStepTool.ToolType.Wrench: + case ToolQuality.Anchoring: icon = ResourceCache.GetResource("/Textures/Objects/Tools/wrench.png"); text = "Wrench"; break; - case ConstructionStepTool.ToolType.Crowbar: + case ToolQuality.Prying: icon = ResourceCache.GetResource("/Textures/Objects/Tools/crowbar.png"); text = "Crowbar"; break; - case ConstructionStepTool.ToolType.Screwdriver: + case ToolQuality.Screwing: icon = ResourceCache.GetResource( "/Textures/Objects/Tools/screwdriver.png"); text = "Screwdriver"; break; - case ConstructionStepTool.ToolType.Welder: + case ToolQuality.Welding: icon = ResourceCache.GetResource("/Textures/Objects/tools.rsi") .RSI["welder"].Frame0; text = $"Welding tool ({tool.Amount} fuel)"; break; - case ConstructionStepTool.ToolType.Wirecutters: + case ToolQuality.Cutting: icon = ResourceCache.GetResource( "/Textures/Objects/Tools/wirecutter.png"); text = "Wirecutters"; diff --git a/Content.Client/GameObjects/Components/Interactable/MultiToolComponent.cs b/Content.Client/GameObjects/Components/Interactable/MultiToolComponent.cs new file mode 100644 index 0000000000..5eb67e8113 --- /dev/null +++ b/Content.Client/GameObjects/Components/Interactable/MultiToolComponent.cs @@ -0,0 +1,79 @@ +using System; +using Content.Client.UserInterface.Stylesheets; +using Content.Client.Utility; +using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Interactable; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.GameObjects; +using Robust.Shared.Localization; +using Robust.Shared.Serialization; +using Robust.Shared.Timing; +using Robust.Shared.ViewVariables; + +namespace Content.Client.GameObjects.Components.Interactable +{ + [RegisterComponent] + public class MultiToolComponent : Component, IItemStatus + { + private ToolQuality _behavior; + private bool _statusShowBehavior; + + [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; + [ViewVariables] public bool StatusShowBehavior => _statusShowBehavior; + [ViewVariables] public ToolQuality Behavior => _behavior; + + public override string Name => "MultiTool"; + public override uint? NetID => ContentNetIDs.MULTITOOLS; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(ref _statusShowBehavior, "statusShowBehavior", true); + } + + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + if (!(curState is MultiToolComponentState tool)) return; + + _behavior = tool.Quality; + _uiUpdateNeeded = true; + + } + + public Control MakeControl() => new StatusControl(this); + + private sealed class StatusControl : Control + { + private readonly MultiToolComponent _parent; + private readonly RichTextLabel _label; + + public StatusControl(MultiToolComponent parent) + { + _parent = parent; + _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}}; + AddChild(_label); + + parent._uiUpdateNeeded = true; + } + + protected override void Update(FrameEventArgs args) + { + base.Update(args); + + if (!_parent._uiUpdateNeeded) + { + return; + } + + _parent._uiUpdateNeeded = false; + + if(!_parent.StatusShowBehavior) + _label.SetMarkup(string.Empty); + else + _label.SetMarkup(_parent.Behavior.ToString()); + + } + } + } +} diff --git a/Content.Client/GameObjects/Components/WelderComponent.cs b/Content.Client/GameObjects/Components/Interactable/WelderComponent.cs similarity index 75% rename from Content.Client/GameObjects/Components/WelderComponent.cs rename to Content.Client/GameObjects/Components/Interactable/WelderComponent.cs index e5a64d5ccb..5852469bc6 100644 --- a/Content.Client/GameObjects/Components/WelderComponent.cs +++ b/Content.Client/GameObjects/Components/Interactable/WelderComponent.cs @@ -1,39 +1,44 @@ -using System; -using Content.Client.UserInterface; +using System; using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Content.Shared.GameObjects; -using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Interactable; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.GameObjects; using Robust.Shared.Localization; +using Robust.Shared.Serialization; using Robust.Shared.Timing; using Robust.Shared.ViewVariables; -namespace Content.Client.GameObjects.Components +namespace Content.Client.GameObjects.Components.Interactable { [RegisterComponent] - public class WelderComponent : Component, IItemStatus + public class WelderComponent : SharedToolComponent, IItemStatus { public override string Name => "Welder"; public override uint? NetID => ContentNetIDs.WELDER; + private ToolQuality _behavior; + [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; [ViewVariables] public float FuelCapacity { get; private set; } [ViewVariables] public float Fuel { get; private set; } [ViewVariables] public bool Activated { get; private set; } + [ViewVariables] public override ToolQuality Qualities => _behavior; - [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; + public override void ExposeData(ObjectSerializer serializer) + { + } public override void HandleComponentState(ComponentState curState, ComponentState nextState) { - if (!(curState is WelderComponentState cast)) + if (!(curState is WelderComponentState weld)) return; - FuelCapacity = cast.FuelCapacity; - Fuel = cast.Fuel; - Activated = cast.Activated; - + FuelCapacity = weld.FuelCapacity; + Fuel = weld.Fuel; + Activated = weld.Activated; + _behavior = weld.Quality; _uiUpdateNeeded = true; } diff --git a/Content.Server/GameObjects/Components/WrenchableComponent.cs b/Content.Server/GameObjects/Components/AnchorableComponent.cs similarity index 60% rename from Content.Server/GameObjects/Components/WrenchableComponent.cs rename to Content.Server/GameObjects/Components/AnchorableComponent.cs index 3e4e5a41a3..b44ff8f767 100644 --- a/Content.Server/GameObjects/Components/WrenchableComponent.cs +++ b/Content.Server/GameObjects/Components/AnchorableComponent.cs @@ -1,6 +1,6 @@ -using Content.Server.GameObjects.Components.Interactable.Tools; +using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.EntitySystems; -using Content.Server.Utility; +using Content.Shared.GameObjects.Components.Interactable; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; @@ -12,31 +12,26 @@ using Robust.Shared.Utility; namespace Content.Server.GameObjects.Components { [RegisterComponent] - public class WrenchableComponent : Component, IInteractUsing + public class AnchorableComponent : Component, IInteractUsing { - public override string Name => "Wrenchable"; - private AudioSystem _audioSystem; + public override string Name => "Anchorable"; public override void Initialize() { base.Initialize(); - _audioSystem = EntitySystem.Get(); + Owner.EnsureComponent(); } public bool InteractUsing(InteractUsingEventArgs eventArgs) { - if (!eventArgs.Using.HasComponent()) - { + if (!Owner.TryGetComponent(out PhysicsComponent physics) + || !eventArgs.AttackWith.TryGetComponent(out ToolComponent tool)) return false; - } - if (!Owner.TryGetComponent(out PhysicsComponent physics)) - { + if (!tool.UseTool(eventArgs.User, Owner, ToolQuality.Anchoring)) return false; - } physics.Anchored = !physics.Anchored; - _audioSystem.Play("/Audio/items/ratchet.ogg", Owner); return true; } diff --git a/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs b/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs index 291247dbbf..ecaec70e5a 100644 --- a/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs +++ b/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; -using Content.Server.GameObjects.Components.Interactable.Tools; +using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Server.Utility; using Content.Shared.Construction; using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Interactable; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.GameObjects; @@ -64,7 +65,7 @@ namespace Content.Server.GameObjects.Components.Construction var stage = Prototype.Stages[Stage]; - if (TryProcessStep(stage.Forward, eventArgs.Using)) + if (TryProcessStep(stage.Forward, eventArgs.Using, eventArgs.User)) { Stage++; if (Stage == Prototype.Stages.Count - 1) @@ -84,7 +85,7 @@ namespace Content.Server.GameObjects.Components.Construction } } - else if (TryProcessStep(stage.Backward, eventArgs.Using)) + else if (TryProcessStep(stage.Backward, eventArgs.Using, eventArgs.User)) { Stage--; if (Stage == 0) @@ -120,7 +121,7 @@ namespace Content.Server.GameObjects.Components.Construction } - bool TryProcessStep(ConstructionStep step, IEntity slapped) + bool TryProcessStep(ConstructionStep step, IEntity slapped, IEntity user) { if (step == null) { @@ -143,52 +144,16 @@ namespace Content.Server.GameObjects.Components.Construction sound.Play("/Audio/items/deconstruct.ogg", Transform.GridPosition); return true; case ConstructionStepTool toolStep: - switch (toolStep.Tool) - { - case ToolType.Crowbar: - if (slapped.HasComponent()) - { - sound.Play("/Audio/items/crowbar.ogg", Transform.GridPosition); - return true; - } - return false; - case ToolType.Welder: - if (slapped.TryGetComponent(out WelderComponent welder) && welder.TryUse(toolStep.Amount)) - { - if (_random.NextDouble() > 0.5) - sound.Play("/Audio/items/welder.ogg", Transform.GridPosition); - else - sound.Play("/Audio/items/welder2.ogg", Transform.GridPosition); - return true; - } - return false; - case ToolType.Wrench: - if (slapped.HasComponent()) - { - sound.Play("/Audio/items/ratchet.ogg", Transform.GridPosition); - return true; - } - return false; - case ToolType.Screwdriver: - if (slapped.HasComponent()) - { - if (_random.NextDouble() > 0.5) - sound.Play("/Audio/items/screwdriver.ogg", Transform.GridPosition); - else - sound.Play("/Audio/items/screwdriver2.ogg", Transform.GridPosition); - return true; - } - return false; - case ToolType.Wirecutters: - if (slapped.HasComponent()) - { - sound.Play("/Audio/items/wirecutter.ogg", Transform.GridPosition); - return true; - } - return false; - default: - throw new NotImplementedException(); - } + if (!slapped.TryGetComponent(out var tool)) + return false; + + // Handle welder manually since tool steps specify fuel amount needed, for some reason. + if (toolStep.ToolQuality.HasFlag(ToolQuality.Welding)) + return slapped.TryGetComponent(out var welder) + && welder.UseTool(user, Owner, toolStep.ToolQuality, toolStep.Amount); + + return tool.UseTool(user, Owner, toolStep.ToolQuality); + default: throw new NotImplementedException(); } diff --git a/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs b/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs index 12c763728a..41c9d62eec 100644 --- a/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs +++ b/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs @@ -1,12 +1,13 @@ using System; using System.Threading; -using Content.Server.GameObjects.Components.Interactable.Tools; +using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Power; using Content.Server.GameObjects.Components.VendingMachines; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Server.Utility; using Content.Shared.GameObjects.Components.Doors; +using Content.Shared.GameObjects.Components.Interactable; using Robust.Server.GameObjects; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; @@ -201,27 +202,25 @@ namespace Content.Server.GameObjects.Components.Doors public bool InteractUsing(InteractUsingEventArgs eventArgs) { - if (eventArgs.Using.HasComponent()) - { - if (IsPowered()) - { - var notify = IoCManager.Resolve(); - notify.PopupMessage(Owner, eventArgs.User, "The powered motors block your efforts!"); - return true; - } + if (!eventArgs.AttackWith.TryGetComponent(out var tool)) + return false; - if (State == DoorState.Closed) - { - Open(); - } - else if(State == DoorState.Open) - { - Close(); - } + if (!tool.UseTool(eventArgs.User, Owner, ToolQuality.Prying)) return false; + + if (IsPowered()) + { + var notify = IoCManager.Resolve(); + notify.PopupMessage(Owner, eventArgs.User, "The powered motors block your efforts!"); return true; } - return false; + if (State == DoorState.Closed) + Open(); + else if(State == DoorState.Open) + Close(); + + return true; + } } } diff --git a/Content.Server/GameObjects/Components/Gravity/GravityGeneratorComponent.cs b/Content.Server/GameObjects/Components/Gravity/GravityGeneratorComponent.cs index d2d43e6d9d..189ced218f 100644 --- a/Content.Server/GameObjects/Components/Gravity/GravityGeneratorComponent.cs +++ b/Content.Server/GameObjects/Components/Gravity/GravityGeneratorComponent.cs @@ -1,10 +1,11 @@ using Content.Server.GameObjects.Components.Damage; -using Content.Server.GameObjects.Components.Interactable.Tools; +using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Power; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Server.Utility; using Content.Shared.GameObjects.Components.Gravity; +using Content.Shared.GameObjects.Components.Interactable; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.UserInterface; @@ -103,27 +104,24 @@ namespace Content.Server.GameObjects.Components.Gravity public bool InteractUsing(InteractUsingEventArgs eventArgs) { - if (!eventArgs.Using.TryGetComponent(out var welder)) return false; - - if (welder.TryUse(5.0f)) - { - // Repair generator - var damagable = Owner.GetComponent(); - var breakable = Owner.GetComponent(); - damagable.HealAllDamage(); - breakable.broken = false; - _intact = true; - - var notifyManager = IoCManager.Resolve(); - - EntitySystem.Get().Play("/Audio/items/welder2.ogg", Owner); - notifyManager.PopupMessage(Owner, eventArgs.User, Loc.GetString("You repair the gravity generator with the welder")); - - return true; - } else - { + if (!eventArgs.Using.TryGetComponent(out WelderComponent tool)) return false; - } + + if (!tool.UseTool(eventArgs.User, Owner, ToolQuality.Welding, 5f)) + return false; + + // Repair generator + var damageable = Owner.GetComponent(); + var breakable = Owner.GetComponent(); + damageable.HealAllDamage(); + breakable.broken = false; + _intact = true; + + var notifyManager = IoCManager.Resolve(); + + notifyManager.PopupMessage(Owner, eventArgs.User, Loc.GetString("You repair the gravity generator with the welder")); + + return true; } public void OnBreak(BreakageEventArgs eventArgs) diff --git a/Content.Server/GameObjects/Components/Interactable/MultitoolComponent.cs b/Content.Server/GameObjects/Components/Interactable/MultitoolComponent.cs new file mode 100644 index 0000000000..0ce1054c56 --- /dev/null +++ b/Content.Server/GameObjects/Components/Interactable/MultitoolComponent.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Interactable; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Serialization; +using Robust.Shared.IoC; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Interactable +{ + /// + /// Not to be confused with Multitool (power) + /// + [RegisterComponent] + public class MultiToolComponent : Component, IUse + { + public class ToolEntry : IExposeData + { + private string _state; + private string _sound; + private string _soundCollection; + private string _texture; + private string _sprite; + private string _changeSound; + + public ToolQuality Behavior { get; private set; } + public string State => _state; + public string Texture => _texture; + public string Sprite => _sprite; + public string Sound => _sound; + public string SoundCollection => _soundCollection; + public string ChangeSound => _changeSound; + + public void ExposeData(ObjectSerializer serializer) + { + serializer.DataField(this, x => Behavior, "behavior", ToolQuality.None); + serializer.DataField(ref _state, "state", string.Empty); + serializer.DataField(ref _sprite, "sprite", string.Empty); + serializer.DataField(ref _texture, "texture", string.Empty); + serializer.DataField(ref _sound, "useSound", string.Empty); + serializer.DataField(ref _soundCollection, "useSoundCollection", string.Empty); + serializer.DataField(ref _changeSound, "changeSound", string.Empty); + } + } + +#pragma warning disable 649 + [Dependency] private IEntitySystemManager _entitySystemManager; +#pragma warning restore 649 + + public override string Name => "MultiTool"; + public override uint? NetID => ContentNetIDs.MULTITOOLS; + private List _tools; + private int _currentTool = 0; + + private AudioSystem _audioSystem; + private ToolComponent _tool; + private SpriteComponent _sprite; + + public override void Initialize() + { + base.Initialize(); + Owner.TryGetComponent(out _tool); + Owner.TryGetComponent(out _sprite); + + _audioSystem = _entitySystemManager.GetEntitySystem(); + + SetTool(); + } + + public void Cycle() + { + _currentTool = (_currentTool + 1) % _tools.Count; + SetTool(); + var current = _tools[_currentTool]; + if(!string.IsNullOrEmpty(current.ChangeSound)) + _audioSystem.Play(current.ChangeSound, Owner); + } + + private void SetTool() + { + if (_tool == null) return; + + var current = _tools[_currentTool]; + + _tool.UseSound = current.Sound; + _tool.UseSoundCollection = current.SoundCollection; + _tool.Qualities = current.Behavior; + + if (_sprite == null) return; + + if (string.IsNullOrEmpty(current.Texture)) + if (!string.IsNullOrEmpty(current.Sprite)) + _sprite.LayerSetState(0, current.State, current.Sprite); + else + _sprite.LayerSetState(0, current.State); + else + _sprite.LayerSetTexture(0, current.Texture); + + Dirty(); + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(ref _tools, "tools", new List()); + } + + public bool UseEntity(UseEntityEventArgs eventArgs) + { + Cycle(); + return true; + } + + public override ComponentState GetComponentState() + { + return new MultiToolComponentState(_tool.Qualities); + } + } +} diff --git a/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs b/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs new file mode 100644 index 0000000000..cc8a8d674a --- /dev/null +++ b/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs @@ -0,0 +1,68 @@ +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Components.Interactable; +using Content.Shared.Maps; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Interactable +{ + [RegisterComponent] + public class TilePryingComponent : Component, IAfterAttack + { +#pragma warning disable 649 + [Dependency] private IEntitySystemManager _entitySystemManager; + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager; + [Dependency] private readonly IMapManager _mapManager; + [Dependency] private readonly IPrototypeManager _prototypeManager; + [Dependency] private readonly IRobustRandom _robustRandom; +#pragma warning restore 649 + + public override string Name => "TilePrying"; + private bool _toolComponentNeeded = true; + + public void AfterAttack(AfterAttackEventArgs eventArgs) + { + TryPryTile(eventArgs.User, eventArgs.ClickLocation); + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(ref _toolComponentNeeded, "toolComponentNeeded", true); + } + + public void TryPryTile(IEntity user, GridCoordinates clickLocation) + { + if (!Owner.TryGetComponent(out var tool) && _toolComponentNeeded) + return; + + var mapGrid = _mapManager.GetGrid(clickLocation.GridID); + var tile = mapGrid.GetTileRef(clickLocation); + + var coordinates = mapGrid.GridTileToLocal(tile.GridIndices); + + if (!_entitySystemManager.GetEntitySystem().InRangeUnobstructed(user.Transform.MapPosition, coordinates.ToMapPos(_mapManager), ignoredEnt:user)) + return; + + var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId]; + + if (!tileDef.CanCrowbar) return; + + if (_toolComponentNeeded && !tool.UseTool(user, null, ToolQuality.Prying)) + return; + + var underplating = _tileDefinitionManager["underplating"]; + mapGrid.SetTile(clickLocation, new Tile(underplating.TileId)); + + //Actually spawn the relevant tile item at the right position and give it some offset to the corner. + var tileItem = Owner.EntityManager.SpawnEntity(tileDef.ItemDropPrototypeName, coordinates); + tileItem.Transform.WorldPosition += (0.2f, 0.2f); + } + } +} diff --git a/Content.Server/GameObjects/Components/Interactable/ToolComponent.cs b/Content.Server/GameObjects/Components/Interactable/ToolComponent.cs new file mode 100644 index 0000000000..9ef6d8d96f --- /dev/null +++ b/Content.Server/GameObjects/Components/Interactable/ToolComponent.cs @@ -0,0 +1,142 @@ +// Only unused on .NET Core due to KeyValuePair.Deconstruct +// ReSharper disable once RedundantUsingDirective + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Metadata.Ecma335; +using Content.Server.GameObjects.Components.Chemistry; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.Audio; +using Content.Shared.Chemistry; +using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Interactable; +using Content.Shared.Maps; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Log; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Interactable +{ + [RegisterComponent] + public class ToolComponent : SharedToolComponent + { +#pragma warning disable 649 + [Dependency] private IEntitySystemManager _entitySystemManager; + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager; + [Dependency] private readonly IMapManager _mapManager; + [Dependency] private readonly IPrototypeManager _prototypeManager; + [Dependency] private readonly IRobustRandom _robustRandom; +#pragma warning restore 649 + + private AudioSystem _audioSystem; + private InteractionSystem _interactionSystem; + private SpriteComponent _spriteComponent; + + protected ToolQuality _qualities = ToolQuality.Anchoring; + + [ViewVariables] + public override ToolQuality Qualities + { + get => _qualities; + set + { + _qualities = value; + Dirty(); + } + } + + /// + /// For tool interactions that have a delay before action this will modify the rate, time to wait is divided by this value + /// + [ViewVariables(VVAccess.ReadWrite)] + public float SpeedModifier { get; set; } = 1; + + public string UseSound { get; set; } + + public string UseSoundCollection { get; set; } + + public void AddQuality(ToolQuality quality) + { + _qualities |= quality; + Dirty(); + } + + public void RemoveQuality(ToolQuality quality) + { + _qualities &= ~quality; + Dirty(); + } + + public bool HasQuality(ToolQuality quality) + { + return _qualities.HasFlag(quality); + } + + public override void Initialize() + { + base.Initialize(); + + _audioSystem = _entitySystemManager.GetEntitySystem(); + _interactionSystem = _entitySystemManager.GetEntitySystem(); + Owner.TryGetComponent(out _spriteComponent); + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + if (serializer.Reading) + { + var qualities = serializer.ReadDataField("qualities", new List()); + foreach (var quality in qualities) + { + AddQuality(quality); + } + } + serializer.DataField(this, mod => SpeedModifier, "speed", 1); + serializer.DataField(this, use => UseSound, "useSound", string.Empty); + serializer.DataField(this, collection => UseSoundCollection, "useSoundCollection", string.Empty); + } + + public virtual bool UseTool(IEntity user, IEntity target, ToolQuality toolQualityNeeded) + { + if (!HasQuality(toolQualityNeeded) || !ActionBlockerSystem.CanInteract(user)) + return false; + + PlayUseSound(); + + return true; + } + + protected void PlaySoundCollection(string name, float volume=-5f) + { + var soundCollection = _prototypeManager.Index(name); + var file = _robustRandom.Pick(soundCollection.PickFiles); + _entitySystemManager.GetEntitySystem() + .Play(file, Owner, AudioHelpers.WithVariation(0.15f).WithVolume(volume)); + } + + public void PlayUseSound(float volume=-5f) + { + if(string.IsNullOrEmpty(UseSoundCollection)) + _audioSystem.Play(UseSound, Owner, AudioHelpers.WithVariation(0.15f).WithVolume(volume)); + else + PlaySoundCollection(UseSoundCollection, volume); + } + } +} diff --git a/Content.Server/GameObjects/Components/Interactable/Tools/BaseTool.cs b/Content.Server/GameObjects/Components/Interactable/Tools/BaseTool.cs deleted file mode 100644 index b95e91a537..0000000000 --- a/Content.Server/GameObjects/Components/Interactable/Tools/BaseTool.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Only unused on .NET Core due to KeyValuePair.Deconstruct -// ReSharper disable once RedundantUsingDirective -using Robust.Shared.Utility; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization; -using Robust.Shared.ViewVariables; - -namespace Content.Server.GameObjects.Components.Interactable.Tools -{ - public abstract class ToolComponent : Component - { - /// - /// For tool interactions that have a delay before action this will modify the rate, time to wait is divided by this value - /// - [ViewVariables(VVAccess.ReadWrite)] - public float SpeedModifier - { - get => _speedModifier; - set => _speedModifier = value; - } - private float _speedModifier = 1; - - public override void ExposeData(ObjectSerializer serializer) - { - base.ExposeData(serializer); - - serializer.DataField(ref _speedModifier, "Speed", 1); - } - - /// - /// Status modifier which determines whether or not we can act as a tool at this time - /// - /// - public virtual bool CanUse() - { - return true; - } - } -} diff --git a/Content.Server/GameObjects/Components/Interactable/Tools/CrowbarComponent.cs b/Content.Server/GameObjects/Components/Interactable/Tools/CrowbarComponent.cs deleted file mode 100644 index df44ecc92e..0000000000 --- a/Content.Server/GameObjects/Components/Interactable/Tools/CrowbarComponent.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Content.Server.GameObjects.EntitySystems; -using Content.Server.Utility; -using Content.Shared.Maps; -using Robust.Server.GameObjects.EntitySystems; -using Robust.Shared.GameObjects; -using Robust.Shared.Interfaces.GameObjects; -using Robust.Shared.Interfaces.Map; -using Robust.Shared.IoC; -using Robust.Shared.Map; - -namespace Content.Server.GameObjects.Components.Interactable.Tools -{ - [RegisterComponent] - public class CrowbarComponent : ToolComponent, IAfterInteract - { -#pragma warning disable 649 - [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager; - [Dependency] private readonly IEntitySystemManager _entitySystemManager; - [Dependency] private readonly IMapManager _mapManager; -#pragma warning restore 649 - - /// - /// Tool that can be used to crowbar things apart, such as deconstructing - /// - public override string Name => "Crowbar"; - - public void AfterInteract(AfterInteractEventArgs eventArgs) - { - if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return; - - var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GridID); - var tile = mapGrid.GetTileRef(eventArgs.ClickLocation); - var coordinates = mapGrid.GridTileToLocal(tile.GridIndices); - var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId]; - if (tileDef.CanCrowbar) - { - var underplating = _tileDefinitionManager["underplating"]; - mapGrid.SetTile(eventArgs.ClickLocation, new Tile(underplating.TileId)); - _entitySystemManager.GetEntitySystem().Play("/Audio/items/crowbar.ogg", Owner); - //Actually spawn the relevant tile item at the right position and give it some offset to the corner. - var tileItem = Owner.EntityManager.SpawnEntity(tileDef.ItemDropPrototypeName, coordinates); - tileItem.Transform.WorldPosition += (0.2f, 0.2f); - } - } - } -} diff --git a/Content.Server/GameObjects/Components/Interactable/Tools/MultitoolComponent.cs b/Content.Server/GameObjects/Components/Interactable/Tools/MultitoolComponent.cs deleted file mode 100644 index 7493374f57..0000000000 --- a/Content.Server/GameObjects/Components/Interactable/Tools/MultitoolComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Robust.Shared.GameObjects; - -namespace Content.Server.GameObjects.Components.Interactable.Tools -{ - /// - /// Tool used for interfacing/hacking into configurable computers - /// - [RegisterComponent] - public class MultitoolComponent : ToolComponent - { - public override string Name => "Multitool"; - } -} diff --git a/Content.Server/GameObjects/Components/Interactable/Tools/ScrewdriverComponent.cs b/Content.Server/GameObjects/Components/Interactable/Tools/ScrewdriverComponent.cs deleted file mode 100644 index 8f69c1b2c7..0000000000 --- a/Content.Server/GameObjects/Components/Interactable/Tools/ScrewdriverComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Robust.Shared.GameObjects; - -namespace Content.Server.GameObjects.Components.Interactable.Tools -{ - [RegisterComponent] - public class ScrewdriverComponent : ToolComponent - { - /// - /// Tool that interacts with technical components that need to be screwed in - /// - public override string Name => "Screwdriver"; - } -} diff --git a/Content.Server/GameObjects/Components/Interactable/Tools/WelderComponent.cs b/Content.Server/GameObjects/Components/Interactable/Tools/WelderComponent.cs deleted file mode 100644 index e2fa2791fc..0000000000 --- a/Content.Server/GameObjects/Components/Interactable/Tools/WelderComponent.cs +++ /dev/null @@ -1,217 +0,0 @@ -using System; -using Content.Server.GameObjects.EntitySystems; -using Content.Shared.Audio; -using Content.Shared.GameObjects; -using Content.Shared.GameObjects.Components; -using Robust.Server.GameObjects; -using Robust.Server.GameObjects.EntitySystems; -using Robust.Shared.Audio; -using Robust.Shared.GameObjects; -using Robust.Shared.Interfaces.GameObjects; -using Robust.Shared.Interfaces.Random; -using Robust.Shared.IoC; -using Robust.Shared.Localization; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Serialization; -using Robust.Shared.Utility; -using Robust.Shared.ViewVariables; - -namespace Content.Server.GameObjects.Components.Interactable.Tools -{ - /// - /// Tool used to weld metal together, light things on fire, or melt into constituent parts - /// - [RegisterComponent] - class WelderComponent : ToolComponent, IUse, IExamine - { - SpriteComponent spriteComponent; - -#pragma warning disable 649 - [Dependency] private readonly IPrototypeManager _prototypeManager; - [Dependency] private readonly IRobustRandom _robustRandom; - [Dependency] private readonly IEntitySystemManager _entitySystemManager; -#pragma warning restore 649 - - public override string Name => "Welder"; - public override uint? NetID => ContentNetIDs.WELDER; - - /// - /// Maximum fuel capacity the welder can hold - /// - [ViewVariables(VVAccess.ReadWrite)] - public float FuelCapacity - { - get => _fuelCapacity; - set - { - _fuelCapacity = value; - Dirty(); - } - } - - private float _fuelCapacity = 50; - - /// - /// Fuel the welder has to do tasks - /// - [ViewVariables(VVAccess.ReadWrite)] - public float Fuel - { - get => _fuel; - set - { - _fuel = value; - Dirty(); - } - } - - private float _fuel = 0; - private bool _activated = false; - - /// - /// Default Cost of using the welder fuel for an action - /// - public const float DefaultFuelCost = 5; - - /// - /// Rate at which we expunge fuel from ourselves when activated - /// - public const float FuelLossRate = 0.2f; - - /// - /// Status of welder, whether it is ignited - /// - [ViewVariables] - public bool Activated - { - get => _activated; - private set - { - _activated = value; - Dirty(); - } - } - - //private string OnSprite { get; set; } - //private string OffSprite { get; set; } - - public override void Initialize() - { - base.Initialize(); - - spriteComponent = Owner.GetComponent(); - } - - public override void ExposeData(ObjectSerializer serializer) - { - base.ExposeData(serializer); - - serializer.DataField(ref _fuelCapacity, "Capacity", 50); - serializer.DataField(ref _fuel, "Fuel", FuelCapacity); - serializer.DataField(ref _activated, "Activated", false); - } - - public void OnUpdate(float frameTime) - { - if (!Activated) - { - return; - } - - Fuel = Math.Max(Fuel - (FuelLossRate * frameTime), 0); - - if (Fuel == 0) - { - ToggleStatus(); - } - } - - public bool TryUse(float value) - { - if (!Activated || !CanUse(value)) - { - return false; - } - - Fuel -= value; - return true; - } - - public bool CanUse(float value) - { - return Fuel > value; - } - - public override bool CanUse() - { - return CanUse(DefaultFuelCost); - } - - public bool CanActivate() - { - return Fuel > 0; - } - - public bool UseEntity(UseEntityEventArgs eventArgs) - { - return ToggleStatus(); - } - - /// - /// Deactivates welding tool if active, activates welding tool if possible - /// - /// - public bool ToggleStatus() - { - if (Activated) - { - Activated = false; - // Layer 1 is the flame. - spriteComponent.LayerSetVisible(1, false); - PlaySoundCollection("welder_off", -5); - return true; - } - else if (CanActivate()) - { - Activated = true; - spriteComponent.LayerSetVisible(1, true); - PlaySoundCollection("welder_on", -5); - return true; - } - else - { - return false; - } - } - - void IExamine.Examine(FormattedMessage message) - { - var loc = IoCManager.Resolve(); - if (Activated) - { - message.AddMarkup(loc.GetString("[color=orange]Lit[/color]\n")); - } - else - { - message.AddText(loc.GetString("Not lit\n")); - } - - message.AddMarkup(loc.GetString("Fuel: [color={0}]{1}/{2}[/color].", - Fuel < FuelCapacity / 4f ? "darkorange" : "orange", Math.Round(Fuel), FuelCapacity)); - } - - private void PlaySoundCollection(string name, float volume) - { - var soundCollection = _prototypeManager.Index(name); - var file = _robustRandom.Pick(soundCollection.PickFiles); - _entitySystemManager.GetEntitySystem() - .Play(file, AudioParams.Default.WithVolume(volume)); - } - - public override ComponentState GetComponentState() - { - return new WelderComponentState(FuelCapacity, Fuel, Activated); - } - } -} diff --git a/Content.Server/GameObjects/Components/Interactable/Tools/WirecutterComponent.cs b/Content.Server/GameObjects/Components/Interactable/Tools/WirecutterComponent.cs deleted file mode 100644 index bc7568c381..0000000000 --- a/Content.Server/GameObjects/Components/Interactable/Tools/WirecutterComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Robust.Shared.GameObjects; - -namespace Content.Server.GameObjects.Components.Interactable.Tools -{ - /// - /// Tool that can be used for some cutting interactions such as wires or hacking - /// - [RegisterComponent] - public class WirecutterComponent : ToolComponent - { - public override string Name => "Wirecutter"; - } -} diff --git a/Content.Server/GameObjects/Components/Interactable/Tools/WrenchComponent.cs b/Content.Server/GameObjects/Components/Interactable/Tools/WrenchComponent.cs deleted file mode 100644 index 8bb24680d9..0000000000 --- a/Content.Server/GameObjects/Components/Interactable/Tools/WrenchComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Robust.Shared.GameObjects; - -namespace Content.Server.GameObjects.Components.Interactable.Tools -{ - /// - /// Wrenches bolts, and interacts with things that have been bolted - /// - [RegisterComponent] - public class WrenchComponent : ToolComponent - { - public override string Name => "Wrench"; - } -} diff --git a/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs b/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs new file mode 100644 index 0000000000..91ba2d6f28 --- /dev/null +++ b/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs @@ -0,0 +1,185 @@ +using System; +using Content.Server.GameObjects.Components.Chemistry; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Interfaces; +using Content.Shared.Chemistry; +using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Interactable; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Interactable +{ + [RegisterComponent] + [ComponentReference(typeof(ToolComponent))] + public class WelderComponent : ToolComponent, IExamine, IUse + { +#pragma warning disable 649 + [Dependency] private IEntitySystemManager _entitySystemManager; + [Dependency] private IServerNotifyManager _notifyManager; +#pragma warning restore 649 + + public override string Name => "Welder"; + public override uint? NetID => ContentNetIDs.WELDER; + + /// + /// Default Cost of using the welder fuel for an action + /// + public const float DefaultFuelCost = 10; + + /// + /// Rate at which we expunge fuel from ourselves when activated + /// + public const float FuelLossRate = 0.5f; + + private bool _welderLit = false; + private WelderSystem _welderSystem; + private SpriteComponent _spriteComponent; + private SolutionComponent _solutionComponent; + + [ViewVariables] + public float Fuel => _solutionComponent?.Solution.GetReagentQuantity("chem.WeldingFuel").Float() ?? 0f; + + [ViewVariables] + public float FuelCapacity => _solutionComponent?.MaxVolume.Float() ?? 0f; + + /// + /// Status of welder, whether it is ignited + /// + [ViewVariables] + public bool WelderLit + { + get => _welderLit; + private set + { + _welderLit = value; + Dirty(); + } + } + + public override void Initialize() + { + base.Initialize(); + + AddQuality(ToolQuality.Welding); + + _welderSystem = _entitySystemManager.GetEntitySystem(); + + Owner.TryGetComponent(out _solutionComponent); + Owner.TryGetComponent(out _spriteComponent); + } + + public override ComponentState GetComponentState() + { + return new WelderComponentState(FuelCapacity, Fuel, WelderLit); + } + + public override bool UseTool(IEntity user, IEntity target, ToolQuality toolQualityNeeded) + { + var canUse = base.UseTool(user, target, toolQualityNeeded); + + return toolQualityNeeded.HasFlag(ToolQuality.Welding) ? canUse && TryWeld(DefaultFuelCost, user) : canUse; + } + + public bool UseTool(IEntity user, IEntity target, ToolQuality toolQualityNeeded, float fuelConsumed) + { + return base.UseTool(user, target, toolQualityNeeded) && TryWeld(fuelConsumed, user); + } + + private bool TryWeld(float value, IEntity user = null) + { + if (!WelderLit) + { + _notifyManager.PopupMessage(Owner, user, Loc.GetString("The welder is turned off!")); + return false; + } + + if (!CanWeld(value)) + { + _notifyManager.PopupMessage(Owner, user, Loc.GetString("The welder does not have enough fuel for that!")); + } + + if (_solutionComponent == null) + return false; + + return _solutionComponent.TryRemoveReagent("chem.WeldingFuel", ReagentUnit.New(value)); + } + + private bool CanWeld(float value) + { + return Fuel > value || Qualities != ToolQuality.Welding; + } + + private bool CanLitWelder() + { + return Fuel > 0 || Qualities != ToolQuality.Welding; + } + + /// + /// Deactivates welding tool if active, activates welding tool if possible + /// + private bool ToggleWelderStatus(IEntity user = null) + { + if (WelderLit) + { + WelderLit = false; + // Layer 1 is the flame. + _spriteComponent.LayerSetVisible(1, false); + PlaySoundCollection("WelderOff", -5); + _welderSystem.Unsubscribe(this); + return true; + } + + if (!CanLitWelder()) + { + _notifyManager.PopupMessage(Owner, user, Loc.GetString("The welder has no fuel left!")); + return false; + } + + WelderLit = true; + _spriteComponent.LayerSetVisible(1, true); + PlaySoundCollection("WelderOn", -5); + _welderSystem.Subscribe(this); + return true; + } + + public bool UseEntity(UseEntityEventArgs eventArgs) + { + return ToggleWelderStatus(eventArgs.User); + } + + public void Examine(FormattedMessage message) + { + if (WelderLit) + { + message.AddMarkup(Loc.GetString("[color=orange]Lit[/color]\n")); + } + else + { + message.AddText(Loc.GetString("Not lit\n")); + } + + message.AddMarkup(Loc.GetString("Fuel: [color={0}]{1}/{2}[/color].", + Fuel < FuelCapacity / 4f ? "darkorange" : "orange", Math.Round(Fuel), FuelCapacity)); + } + + public void OnUpdate(float frameTime) + { + if (!HasQuality(ToolQuality.Welding) || !WelderLit) + return; + + _solutionComponent.TryRemoveReagent("chem.WeldingFuel", ReagentUnit.New(FuelLossRate * frameTime)); + + if (Fuel == 0) + ToggleWelderStatus(); + + Dirty(); + } + } +} diff --git a/Content.Server/GameObjects/Components/Power/PowerTransferComponent.cs b/Content.Server/GameObjects/Components/Power/PowerTransferComponent.cs index c7243b201c..e9ca5774bd 100644 --- a/Content.Server/GameObjects/Components/Power/PowerTransferComponent.cs +++ b/Content.Server/GameObjects/Components/Power/PowerTransferComponent.cs @@ -1,7 +1,8 @@ using System.Linq; -using Content.Server.GameObjects.Components.Interactable.Tools; +using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Components.Interactable; using Content.Server.Utility; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; @@ -141,17 +142,16 @@ namespace Content.Server.GameObjects.Components.Power public bool InteractUsing(InteractUsingEventArgs eventArgs) { - if (eventArgs.Using.TryGetComponent(out WirecutterComponent wirecutter)) - { - Owner.Delete(); - var droppedEnt = Owner.EntityManager.SpawnEntity("CableStack", eventArgs.ClickLocation); + if (!eventArgs.Using.TryGetComponent(out ToolComponent tool)) return false; + if (!tool.UseTool(eventArgs.User, Owner, ToolQuality.Cutting)) return false; - if (droppedEnt.TryGetComponent(out var stackComp)) - stackComp.Count = 1; + Owner.Delete(); + var droppedEnt = Owner.EntityManager.SpawnEntity("CableStack", eventArgs.ClickLocation); - return true; - } - return false; + if (droppedEnt.TryGetComponent(out var stackComp)) + stackComp.Count = 1; + + return true; } } } diff --git a/Content.Server/GameObjects/Components/WiresComponent.cs b/Content.Server/GameObjects/Components/WiresComponent.cs index d48140c655..6cc34385f4 100644 --- a/Content.Server/GameObjects/Components/WiresComponent.cs +++ b/Content.Server/GameObjects/Components/WiresComponent.cs @@ -1,13 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; -using Content.Server.GameObjects.Components.Interactable.Tools; +using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.VendingMachines; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Server.Interfaces.GameObjects; using Content.Server.Utility; using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Interactable; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.UserInterface; @@ -245,30 +246,31 @@ namespace Content.Server.GameObjects.Components } var activeHandEntity = handsComponent.GetActiveHand?.Owner; + activeHandEntity.TryGetComponent(out var tool); switch (msg.Action) { case WiresAction.Cut: - if (activeHandEntity?.HasComponent() != true) + if (tool == null || !tool.HasQuality(ToolQuality.Cutting)) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You need to hold a wirecutter in your hand!")); return; } - _audioSystem.Play("/Audio/items/wirecutter.ogg", Owner); + tool.PlayUseSound(); wire.IsCut = true; UpdateUserInterface(); break; case WiresAction.Mend: - if (activeHandEntity?.HasComponent() != true) + if (tool == null || !tool.HasQuality(ToolQuality.Cutting)) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You need to hold a wirecutter in your hand!")); return; } - _audioSystem.Play("/Audio/items/wirecutter.ogg", Owner); + tool.PlayUseSound(); wire.IsCut = false; UpdateUserInterface(); break; case WiresAction.Pulse: - if (activeHandEntity?.HasComponent() != true) + if (tool == null || !tool.HasQuality(ToolQuality.Multitool)) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You need to hold a multitool in your hand!")); return; @@ -298,14 +300,14 @@ namespace Content.Server.GameObjects.Components bool IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { - if (!eventArgs.Using.HasComponent()) - { + if (!eventArgs.Using.TryGetComponent(out var tool)) + return false; + if (!tool.UseTool(eventArgs.User, Owner, ToolQuality.Screwing)) return false; - } IsPanelOpen = !IsPanelOpen; EntitySystem.Get() - .Play(IsPanelOpen ? "/Audio/machines/screwdriveropen.ogg" : "/Audio/machines/screwdriverclose.ogg"); + .Play(IsPanelOpen ? "/Audio/machines/screwdriveropen.ogg" : "/Audio/machines/screwdriverclose.ogg", Owner); return true; } diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs index 60a5a27d40..14332405bb 100644 --- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs @@ -1,8 +1,10 @@ using System; using System.Linq; +using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Timing; using Content.Server.Interfaces.GameObjects; +using Content.Shared.GameObjects.Components.Interactable; using Content.Server.Utility; using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.Input; diff --git a/Content.Server/GameObjects/EntitySystems/WelderSystem.cs b/Content.Server/GameObjects/EntitySystems/WelderSystem.cs index fa1c54c7fa..411e3546fc 100644 --- a/Content.Server/GameObjects/EntitySystems/WelderSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/WelderSystem.cs @@ -1,22 +1,32 @@ -using Content.Server.GameObjects.Components.Interactable.Tools; -using Robust.Shared.GameObjects; +using System.Collections.Generic; +using System.Linq; +using Content.Server.GameObjects.Components.Interactable; using Robust.Shared.GameObjects.Systems; namespace Content.Server.GameObjects.EntitySystems { - class WelderSystem : EntitySystem + /// + /// Despite the name, it's only really used for the welder logic in tools. Go figure. + /// + public class WelderSystem : EntitySystem { - public override void Initialize() + private readonly HashSet _activeWelders = new HashSet(); + + public bool Subscribe(WelderComponent welder) { - EntityQuery = new TypeEntityQuery(typeof(WelderComponent)); + return _activeWelders.Add(welder); + } + + public bool Unsubscribe(WelderComponent welder) + { + return _activeWelders.Remove(welder); } public override void Update(float frameTime) { - foreach (var entity in RelevantEntities) + foreach (var tool in _activeWelders.ToArray()) { - var comp = entity.GetComponent(); - comp.OnUpdate(frameTime); + tool.OnUpdate(frameTime); } } } diff --git a/Content.Shared/Audio/SoundCollectionPrototype.cs b/Content.Shared/Audio/SoundCollectionPrototype.cs index 9a0c79e77c..e4918bf518 100644 --- a/Content.Shared/Audio/SoundCollectionPrototype.cs +++ b/Content.Shared/Audio/SoundCollectionPrototype.cs @@ -5,7 +5,7 @@ using YamlDotNet.RepresentationModel; namespace Content.Shared.Audio { - [Prototype("sound_collection")] + [Prototype("soundCollection")] public sealed class SoundCollectionPrototype : IPrototype, IIndexedPrototype { public string ID { get; private set; } diff --git a/Content.Shared/Construction/ConstructionPrototype.cs b/Content.Shared/Construction/ConstructionPrototype.cs index d9e04eacbf..6b7a83bb25 100644 --- a/Content.Shared/Construction/ConstructionPrototype.cs +++ b/Content.Shared/Construction/ConstructionPrototype.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Content.Shared.GameObjects.Components.Interactable; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -138,7 +139,7 @@ namespace Content.Shared.Construction if (step.TryGetNode("tool", out node)) { return new ConstructionStepTool( - node.AsEnum(), + node.AsEnum(), amount ); } @@ -190,20 +191,11 @@ namespace Content.Shared.Construction public class ConstructionStepTool : ConstructionStep { - public readonly ToolType Tool; + public readonly ToolQuality ToolQuality; - public ConstructionStepTool(ToolType tool, int amount) : base(amount) + public ConstructionStepTool(ToolQuality toolQuality, int amount) : base(amount) { - Tool = tool; - } - - public enum ToolType - { - Wrench, - Welder, - Screwdriver, - Crowbar, - Wirecutters, + ToolQuality = toolQuality; } } diff --git a/Content.Shared/GameObjects/Components/Interactable/SharedToolComponent.cs b/Content.Shared/GameObjects/Components/Interactable/SharedToolComponent.cs new file mode 100644 index 0000000000..a99f4e029f --- /dev/null +++ b/Content.Shared/GameObjects/Components/Interactable/SharedToolComponent.cs @@ -0,0 +1,53 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Interactable +{ + [Flags] + public enum ToolQuality : byte + { + None = 0, + Anchoring = 1, + Prying = 1 << 1, + Screwing = 1 << 2, + Cutting = 1 << 3, + Welding = 1 << 4, + Multitool = 1 << 5, + } + + public class SharedToolComponent : Component + { + public override string Name => "Tool"; + + public virtual ToolQuality Qualities { get; set; } + } + + [NetSerializable, Serializable] + public class MultiToolComponentState : ComponentState + { + public ToolQuality Quality { get; } + + public MultiToolComponentState(ToolQuality quality) : base(ContentNetIDs.MULTITOOLS) + { + Quality = quality; + } + } + + [NetSerializable, Serializable] + public class WelderComponentState : ComponentState + { + public float FuelCapacity { get; } + public float Fuel { get; } + public bool Activated { get; } + public ToolQuality Quality { get; } + + public WelderComponentState(float fuelCapacity, float fuel, bool activated) : base(ContentNetIDs.WELDER) + { + FuelCapacity = fuelCapacity; + Fuel = fuel; + Activated = activated; + Quality = ToolQuality.Welding; + } + } +} diff --git a/Content.Shared/GameObjects/Components/WelderComponentState.cs b/Content.Shared/GameObjects/Components/WelderComponentState.cs deleted file mode 100644 index 61aaa5ed1b..0000000000 --- a/Content.Shared/GameObjects/Components/WelderComponentState.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization; - -namespace Content.Shared.GameObjects.Components -{ - [NetSerializable, Serializable] - public class WelderComponentState : ComponentState - { - public float FuelCapacity { get; } - public float Fuel { get; } - public bool Activated { get; } - - public WelderComponentState(float fuelCapacity, float fuel, bool activated) : base(ContentNetIDs.WELDER) - { - FuelCapacity = fuelCapacity; - Fuel = fuel; - Activated = activated; - } - } -} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index 48c9344b9a..d8a53eb727 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -45,6 +45,6 @@ public const uint MICROWAVE = 1040; public const uint GRAVITY_GENERATOR = 1041; public const uint SURGERY = 1042; - + public const uint MULTITOOLS = 1043; } } diff --git a/Resources/Audio/items/change_drill.ogg b/Resources/Audio/items/change_drill.ogg new file mode 100644 index 0000000000..f8928fc00f Binary files /dev/null and b/Resources/Audio/items/change_drill.ogg differ diff --git a/Resources/Audio/items/change_jaws.ogg b/Resources/Audio/items/change_jaws.ogg new file mode 100644 index 0000000000..13960ccc3e Binary files /dev/null and b/Resources/Audio/items/change_jaws.ogg differ diff --git a/Resources/Audio/items/drill_hit.ogg b/Resources/Audio/items/drill_hit.ogg new file mode 100644 index 0000000000..0f8fa631aa Binary files /dev/null and b/Resources/Audio/items/drill_hit.ogg differ diff --git a/Resources/Audio/items/drill_use.ogg b/Resources/Audio/items/drill_use.ogg new file mode 100644 index 0000000000..82f37cd35b Binary files /dev/null and b/Resources/Audio/items/drill_use.ogg differ diff --git a/Resources/Audio/items/jaws_cut.ogg b/Resources/Audio/items/jaws_cut.ogg new file mode 100644 index 0000000000..a0bfd85502 Binary files /dev/null and b/Resources/Audio/items/jaws_cut.ogg differ diff --git a/Resources/Audio/items/jaws_pry.ogg b/Resources/Audio/items/jaws_pry.ogg new file mode 100644 index 0000000000..05178bd466 Binary files /dev/null and b/Resources/Audio/items/jaws_pry.ogg differ diff --git a/Resources/Prototypes/Entities/Buildings/computers.yml b/Resources/Prototypes/Entities/Buildings/computers.yml index ac4b3c0924..ae69da83b8 100644 --- a/Resources/Prototypes/Entities/Buildings/computers.yml +++ b/Resources/Prototypes/Entities/Buildings/computers.yml @@ -20,7 +20,7 @@ - type: Computer - type: PowerDevice priority: High - - type: Wrenchable + - type: Anchorable - type: Sprite sprite: Buildings/computer.rsi diff --git a/Resources/Prototypes/Entities/Buildings/vending_machines.yml b/Resources/Prototypes/Entities/Buildings/vending_machines.yml index 19fb4399ac..c0a9c2af49 100644 --- a/Resources/Prototypes/Entities/Buildings/vending_machines.yml +++ b/Resources/Prototypes/Entities/Buildings/vending_machines.yml @@ -39,7 +39,7 @@ - type: PowerDevice priority: Low - type: Wires - - type: Wrenchable + - type: Anchorable - type: entity parent: VendingMachine diff --git a/Resources/Prototypes/Entities/Items/powercells.yml b/Resources/Prototypes/Entities/Items/powercells.yml index 39438be0f2..f6823d605b 100644 --- a/Resources/Prototypes/Entities/Items/powercells.yml +++ b/Resources/Prototypes/Entities/Items/powercells.yml @@ -116,7 +116,7 @@ - type: Appearance visuals: - type: PowerChargerVisualizer2D - - type: Wrenchable + - type: Anchorable - type: Physics mass: 5 - type: Clickable @@ -148,7 +148,7 @@ - type: Appearance visuals: - type: PowerChargerVisualizer2D - - type: Wrenchable + - type: Anchorable - type: Physics mass: 5 - type: Clickable diff --git a/Resources/Prototypes/Entities/Items/tools.yml b/Resources/Prototypes/Entities/Items/tools.yml index c013821ffe..8c595bfa36 100644 --- a/Resources/Prototypes/Entities/Items/tools.yml +++ b/Resources/Prototypes/Entities/Items/tools.yml @@ -4,7 +4,6 @@ id: Wirecutter description: This kills the wire. components: - - type: Wirecutter - type: Sprite sprite: Objects/Tools/wirecutters.rsi layers: @@ -15,6 +14,10 @@ state: cutters-map - type: ItemCooldown - type: MeleeWeapon + - type: Tool + qualities: + - Cutting + useSound: /Audio/items/wirecutter.ogg - type: RandomToolColor state: cutters colors: @@ -32,12 +35,14 @@ id: Screwdriver description: Industrial grade torque in a small screwdriving package components: - - type: Screwdriver - type: Sprite sprite: Objects/Tools/screwdriver.rsi layers: - - state: screwdriver-map - - state: screwdriver-screwybits + - state: screwdriver-map + - state: screwdriver-screwybits + - type: Icon + sprite: Objects/Tools/screwdriver.rsi + state: screwdriver - type: Icon sprite: Objects/Tools/screwdriver.rsi @@ -45,9 +50,12 @@ - type: Item sprite: Objects/Tools/screwdriver.rsi - - type: ItemCooldown - type: MeleeWeapon + - type: Tool + qualities: + - Screwing + useSoundCollection: Screwdriver - type: RandomToolColor state: screwdriver colors: @@ -59,14 +67,12 @@ cyan: "#18a2d5" yellow: "#ffa500" - - type: entity name: Welding Tool parent: BaseItem id: Welder description: Melts anything as long as it's fueled, don't forget your eye protection! components: - - type: Welder - type: Sprite sprite: Objects/Tools/welder.rsi layers: @@ -74,13 +80,21 @@ - state: welder_flame shader: unshaded visible: false - - type: Icon sprite: Objects/Tools/welder.rsi state: welder - type: ItemCooldown - type: MeleeWeapon - type: ItemStatus + - type: Solution + maxVol: 100 + caps: 9 + contents: + reagents: + - ReagentId: chem.WeldingFuel + Quantity: 100 + - type: Welder + useSoundCollection: Welder - type: entity name: Wrench @@ -88,24 +102,16 @@ id: Wrench description: A common tool for assembly and disassembly, righty tighty lefty loosey components: - - type: Wrench - type: Sprite texture: Objects/Tools/wrench.png - type: Icon texture: Objects/Tools/wrench.png - type: ItemCooldown - type: MeleeWeapon - -- type: sound_collection - id: welder_on - files: - - /Audio/items/lighter1.ogg - - /Audio/items/lighter2.ogg - -- type: sound_collection - id: welder_off - files: - - /Audio/effects/zzzt.ogg + - type: Tool + qualities: + - Anchoring + useSound: /Audio/items/ratchet.ogg - type: entity name: Crowbar @@ -113,13 +119,17 @@ id: Crowbar description: A multipurpose tool to pry open doors and fight interdimensional invaders components: - - type: Crowbar - type: Sprite texture: Objects/Tools/crowbar.png - type: Icon texture: Objects/Tools/crowbar.png - type: ItemCooldown - type: MeleeWeapon + - type: Tool + qualities: + - Prying + useSound: /Audio/items/crowbar.ogg + - type: TilePrying - type: entity name: Multitool @@ -127,14 +137,73 @@ id: Multitool description: An advanced tool to copy, store, and send electrical pulses and signals through wires and machines components: - - type: Multitool - type: Sprite sprite: Objects/Tools/multitool.rsi state: multitool - - type: Icon sprite: Objects/Tools/multitool.rsi state: multitool - - type: Item sprite: Objects/Tools/multitool.rsi + - type: Tool + qualities: + - Multitool + +- type: entity + name: Jaws of life + parent: BaseItem + id: JawsOfLife + description: A set of jaws of life, compressed through the magic of science. + components: + - type: Sprite + sprite: Objects/Tools/jaws_of_life.rsi + state: jaws_pry + - type: Icon + sprite: Objects/Tools/jaws_of_life.rsi + state: jaws_pry + - type: Item + sprite: Objects/Tools/jaws_of_life.rsi + - type: TilePrying + - type: Tool + qualities: + - Prying + statusShowBehavior: true + - type: MultiTool + tools: + - behavior: Prying + state: jaws_pry + useSound: /Audio/items/jaws_pry.ogg + changeSound: /Audio/items/change_jaws.ogg + - behavior: Cutting + state: jaws_cutter + useSound: /Audio/items/jaws_cut.ogg + changeSound: /Audio/items/change_jaws.ogg + +- type: entity + name: Power Drill + parent: BaseItem + id: PowerDrill + description: A simple powered hand drill. + components: + - type: Sprite + sprite: Objects/Tools/drill.rsi + state: drill_screw + - type: Icon + sprite: Objects/Tools/drill.rsi + state: drill_screw + - type: Item + sprite: Objects/Tools/drill.rsi + - type: Tool + qualities: + - Screwing + statusShowBehavior: true + - type: MultiTool + tools: + - behavior: Screwing + state: drill_screw + useSound: /Audio/items/drill_use.ogg + changeSound: /Audio/items/change_drill.ogg + - behavior: Anchoring + state: drill_bolt + useSound: /Audio/items/drill_use.ogg + changeSound: /Audio/items/change_drill.ogg diff --git a/Resources/Prototypes/Reagents/chemicals.yml b/Resources/Prototypes/Reagents/chemicals.yml index 2886f5accd..35129f9d7b 100644 --- a/Resources/Prototypes/Reagents/chemicals.yml +++ b/Resources/Prototypes/Reagents/chemicals.yml @@ -105,3 +105,9 @@ name: Unstable Mutagen desc: Causes mutations when injected into living people or plants. High doses may be lethal, especially in humans. color: "#77b58e" + +- type: reagent + id: chem.WeldingFuel + name: Welding Fuel + desc: Used by welders to weld. + color: "#a76b1c" diff --git a/Resources/Prototypes/SoundCollections/dice.yml b/Resources/Prototypes/SoundCollections/dice.yml index 7e2527c08a..d4e195b413 100644 --- a/Resources/Prototypes/SoundCollections/dice.yml +++ b/Resources/Prototypes/SoundCollections/dice.yml @@ -1,4 +1,4 @@ -- type: sound_collection +- type: soundCollection id: dice files: - /Audio/items/dice/dice1.ogg @@ -7,4 +7,4 @@ - /Audio/items/dice/dice4.ogg - /Audio/items/dice/dice5.ogg - /Audio/items/dice/dice6.ogg - - /Audio/items/dice/dice7.ogg \ No newline at end of file + - /Audio/items/dice/dice7.ogg diff --git a/Resources/Prototypes/SoundCollections/footsteps.yml b/Resources/Prototypes/SoundCollections/footsteps.yml index d84481345c..2bd9e15045 100644 --- a/Resources/Prototypes/SoundCollections/footsteps.yml +++ b/Resources/Prototypes/SoundCollections/footsteps.yml @@ -1,4 +1,4 @@ -- type: sound_collection +- type: soundCollection id: footstep_carpet files: - /Audio/effects/footsteps/carpet1.ogg @@ -7,7 +7,7 @@ - /Audio/effects/footsteps/carpet4.ogg - /Audio/effects/footsteps/carpet5.ogg -- type: sound_collection +- type: soundCollection id: footstep_wood files: - /Audio/effects/footsteps/wood1.ogg @@ -16,7 +16,7 @@ - /Audio/effects/footsteps/wood4.ogg - /Audio/effects/footsteps/wood5.ogg -- type: sound_collection +- type: soundCollection id: footstep_catwalk files: - /Audio/effects/footsteps/catwalk1.ogg @@ -25,7 +25,7 @@ - /Audio/effects/footsteps/catwalk4.ogg - /Audio/effects/footsteps/catwalk5.ogg -- type: sound_collection +- type: soundCollection id: footstep_floor files: - /Audio/effects/footsteps/floor1.ogg @@ -34,7 +34,7 @@ - /Audio/effects/footsteps/floor4.ogg - /Audio/effects/footsteps/floor5.ogg -- type: sound_collection +- type: soundCollection id: footstep_hull files: - /Audio/effects/footsteps/hull1.ogg @@ -43,7 +43,7 @@ - /Audio/effects/footsteps/hull4.ogg - /Audio/effects/footsteps/hull5.ogg -- type: sound_collection +- type: soundCollection id: footstep_plating files: - /Audio/effects/footsteps/plating1.ogg @@ -52,7 +52,7 @@ - /Audio/effects/footsteps/plating4.ogg - /Audio/effects/footsteps/plating5.ogg -- type: sound_collection +- type: soundCollection id: footstep_tile files: - /Audio/effects/footsteps/tile1.ogg @@ -61,19 +61,19 @@ - /Audio/effects/footsteps/tile4.ogg - /Audio/effects/footsteps/tile5.ogg -- type: sound_collection +- type: soundCollection id: footstep_clown files: - /Audio/effects/footsteps/clownstep1.ogg - /Audio/effects/footsteps/clownstep2.ogg -- type: sound_collection +- type: soundCollection id: footstep_heavy files: - /Audio/effects/footsteps/suitstep1.ogg - /Audio/effects/footsteps/suitstep2.ogg -- type: sound_collection +- type: soundCollection id: footstep_asteroid files: - /Audio/effects/footsteps/asteroid1.ogg diff --git a/Resources/Prototypes/SoundCollections/glassbreak.yml b/Resources/Prototypes/SoundCollections/glassbreak.yml index ee3d9278bb..30bf2928e3 100644 --- a/Resources/Prototypes/SoundCollections/glassbreak.yml +++ b/Resources/Prototypes/SoundCollections/glassbreak.yml @@ -1,4 +1,4 @@ -- type: sound_collection +- type: soundCollection id: glassbreak files: - /Audio/effects/glassbreak1.ogg diff --git a/Resources/Prototypes/SoundCollections/keyboard.yml b/Resources/Prototypes/SoundCollections/keyboard.yml index 30a0c7712d..ec26a28eb9 100644 --- a/Resources/Prototypes/SoundCollections/keyboard.yml +++ b/Resources/Prototypes/SoundCollections/keyboard.yml @@ -1,8 +1,7 @@ -- type: sound_collection +- type: soundCollection id: keyboard files: - /Audio/machines/keyboard/keyboard1.ogg - /Audio/machines/keyboard/keyboard2.ogg - /Audio/machines/keyboard/keyboard3.ogg - /Audio/machines/keyboard/keyboard4.ogg - \ No newline at end of file diff --git a/Resources/Prototypes/SoundCollections/tools.yml b/Resources/Prototypes/SoundCollections/tools.yml new file mode 100644 index 0000000000..fbb9647b3d --- /dev/null +++ b/Resources/Prototypes/SoundCollections/tools.yml @@ -0,0 +1,22 @@ +- type: soundCollection + id: WelderOn + files: + - /Audio/items/lighter1.ogg + - /Audio/items/lighter2.ogg + +- type: soundCollection + id: WelderOff + files: + - /Audio/effects/zzzt.ogg + +- type: soundCollection + id: Welder + files: + - /Audio/items/welder.ogg + - /Audio/items/welder2.ogg + +- type: soundCollection + id: Screwdriver + files: + - /Audio/items/screwdriver.ogg + - /Audio/items/screwdriver2.ogg diff --git a/Resources/Textures/Objects/Tools/drill.rsi/drill_bolt.png b/Resources/Textures/Objects/Tools/drill.rsi/drill_bolt.png new file mode 100644 index 0000000000..7ae77acf3a Binary files /dev/null and b/Resources/Textures/Objects/Tools/drill.rsi/drill_bolt.png differ diff --git a/Resources/Textures/Objects/Tools/drill.rsi/drill_screw.png b/Resources/Textures/Objects/Tools/drill.rsi/drill_screw.png new file mode 100644 index 0000000000..f4cb59c283 Binary files /dev/null and b/Resources/Textures/Objects/Tools/drill.rsi/drill_screw.png differ diff --git a/Resources/Textures/Objects/Tools/drill.rsi/meta.json b/Resources/Textures/Objects/Tools/drill.rsi/meta.json new file mode 100644 index 0000000000..0b31ed1416 --- /dev/null +++ b/Resources/Textures/Objects/Tools/drill.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC BY-SA 3.0", "copyright": "Taken from https://github.com/tgstation/tgstation at commit ea59fb4b810decbb5996b36d8876614b57c3d189", "states": [{"name": "drill_bolt", "directions": 1, "delays": [[1.0]]}, {"name": "drill_screw", "directions": 1, "delays": [[1.0]]}]} diff --git a/Resources/Textures/Objects/Tools/jaws_of_life.rsi/jaws_cutter.png b/Resources/Textures/Objects/Tools/jaws_of_life.rsi/jaws_cutter.png new file mode 100644 index 0000000000..42695acd7c Binary files /dev/null and b/Resources/Textures/Objects/Tools/jaws_of_life.rsi/jaws_cutter.png differ diff --git a/Resources/Textures/Objects/Tools/jaws_of_life.rsi/jaws_pry.png b/Resources/Textures/Objects/Tools/jaws_of_life.rsi/jaws_pry.png new file mode 100644 index 0000000000..8877f81e18 Binary files /dev/null and b/Resources/Textures/Objects/Tools/jaws_of_life.rsi/jaws_pry.png differ diff --git a/Resources/Textures/Objects/Tools/jaws_of_life.rsi/meta.json b/Resources/Textures/Objects/Tools/jaws_of_life.rsi/meta.json new file mode 100644 index 0000000000..7adb58c6ef --- /dev/null +++ b/Resources/Textures/Objects/Tools/jaws_of_life.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC BY-SA 3.0", "copyright": "Taken from https://github.com/tgstation/tgstation at commit ea59fb4b810decbb5996b36d8876614b57c3d189", "states": [{"name": "jaws_cutter", "directions": 1, "delays": [[1.0]]}, {"name": "jaws_pry", "directions": 1, "delays": [[1.0]]}]} diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 494dc697c4..2fc823bb49 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -56,5 +56,8 @@ True True True + True + True + True True True