diff --git a/Content.Client/Construction/UI/ConstructionMenuPresenter.cs b/Content.Client/Construction/UI/ConstructionMenuPresenter.cs index 78b93f5544..76f358bb8a 100644 --- a/Content.Client/Construction/UI/ConstructionMenuPresenter.cs +++ b/Content.Client/Construction/UI/ConstructionMenuPresenter.cs @@ -5,7 +5,8 @@ using Content.Client.HUD; using Content.Client.Resources; using Content.Shared.Construction.Prototypes; using Content.Shared.Construction.Steps; -using Content.Shared.Tool; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; using Robust.Client.Graphics; using Robust.Client.Placement; using Robust.Client.ResourceManagement; @@ -271,7 +272,7 @@ namespace Content.Client.Construction.UI stepList.AddItem(Loc.GetString( "construction-presenter-tool-step", ("step-number", stepNumber++), - ("tool", toolStep.Tool.GetToolName())), + ("tool", _prototypeManager.Index(toolStep.Tool).ToolName)), icon); break; @@ -314,7 +315,7 @@ namespace Content.Client.Construction.UI ("step-number", stepNumber), ("parallel-number", parallelNumber), ("substep-number", subStepNumber++), - ("tool", toolStep.Tool.GetToolName())), + ("tool", _prototypeManager.Index(toolStep.Tool).ToolName)), icon); break; @@ -341,7 +342,7 @@ namespace Content.Client.Construction.UI } } - private static Texture? GetTextureForStep(IResourceCache resourceCache, ConstructionGraphStep step) + private Texture? GetTextureForStep(IResourceCache resourceCache, ConstructionGraphStep step) { switch (step) { @@ -349,23 +350,7 @@ namespace Content.Client.Construction.UI return materialStep.MaterialPrototype.Icon?.Frame0(); case ToolConstructionGraphStep toolStep: - switch (toolStep.Tool) - { - case ToolQuality.Anchoring: - return resourceCache.GetTexture("/Textures/Objects/Tools/wrench.rsi/icon.png"); - case ToolQuality.Prying: - return resourceCache.GetTexture("/Textures/Objects/Tools/crowbar.rsi/icon.png"); - case ToolQuality.Screwing: - return resourceCache.GetTexture("/Textures/Objects/Tools/screwdriver.rsi/screwdriver-map.png"); - case ToolQuality.Cutting: - return resourceCache.GetTexture("/Textures/Objects/Tools/wirecutters.rsi/cutters-map.png"); - case ToolQuality.Welding: - return resourceCache.GetTexture("/Textures/Objects/Tools/welder.rsi/icon.png"); - case ToolQuality.Multitool: - return resourceCache.GetTexture("/Textures/Objects/Tools/multitool.rsi/icon.png"); - } - - break; + return _prototypeManager.Index(toolStep.Tool).Icon?.Frame0(); case ArbitraryInsertConstructionGraphStep arbitraryStep: return arbitraryStep.Icon?.Frame0(); diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index a1575636cd..5c575d7f76 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -22,11 +22,6 @@ namespace Content.Client.Entry "FootstepModifier", "HeatResistance", "EntityStorage", - "Wirecutter", - "Screwdriver", - "Multitool", - "Wrench", - "Crowbar", "MeleeWeapon", "MeleeChemicalInjector", "Dice", @@ -91,6 +86,7 @@ namespace Content.Client.Entry "TilePrying", "RandomSpriteColor", "ConditionalSpawner", + "DamageOnToolInteract", "ExaminableBattery", "PottedPlantHide", "SecureEntityStorage", @@ -119,7 +115,6 @@ namespace Content.Client.Entry "SurgeryTool", "EmitSoundOnThrow", "Flash", - "DamageOnToolInteract", "TrashSpawner", "RCD", "RCDDeconstructWhitelist", diff --git a/Content.Client/Tools/Components/MultiToolComponent.cs b/Content.Client/Tools/Components/MultipleToolComponent.cs similarity index 75% rename from Content.Client/Tools/Components/MultiToolComponent.cs rename to Content.Client/Tools/Components/MultipleToolComponent.cs index df90cd55be..e689a414e3 100644 --- a/Content.Client/Tools/Components/MultiToolComponent.cs +++ b/Content.Client/Tools/Components/MultipleToolComponent.cs @@ -1,7 +1,7 @@ using Content.Client.Items.Components; using Content.Client.Message; using Content.Client.Stylesheets; -using Content.Shared.Tool; +using Content.Shared.Tools.Components; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.GameObjects; @@ -13,26 +13,23 @@ using Robust.Shared.ViewVariables; namespace Content.Client.Tools.Components { [RegisterComponent] - [NetworkedComponent()] - public class MultiToolComponent : Component, IItemStatus + public class MultipleToolComponent : SharedMultipleToolComponent, IItemStatus { - private ToolQuality _behavior; + private string? _behavior; [DataField("statusShowBehavior")] private bool _statusShowBehavior = true; [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; [ViewVariables] public bool StatusShowBehavior => _statusShowBehavior; - [ViewVariables] public ToolQuality? Behavior => _behavior; - - public override string Name => "MultiTool"; + [ViewVariables] public string? Behavior => _behavior; public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) { base.HandleComponentState(curState, nextState); - if (curState is not MultiToolComponentState tool) return; + if (curState is not MultipleToolComponentState tool) return; - _behavior = tool.Quality; + _behavior = tool.QualityName; _uiUpdateNeeded = true; } @@ -41,10 +38,10 @@ namespace Content.Client.Tools.Components private sealed class StatusControl : Control { - private readonly MultiToolComponent _parent; + private readonly MultipleToolComponent _parent; private readonly RichTextLabel _label; - public StatusControl(MultiToolComponent parent) + public StatusControl(MultipleToolComponent parent) { _parent = parent; _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}}; @@ -64,7 +61,7 @@ namespace Content.Client.Tools.Components _parent._uiUpdateNeeded = false; - _label.SetMarkup(_parent.StatusShowBehavior ? _parent.Behavior.ToString() ?? string.Empty : string.Empty); + _label.SetMarkup(_parent.StatusShowBehavior ? _parent.Behavior ?? string.Empty : string.Empty); } } } diff --git a/Content.Client/Tools/Components/WelderComponent.cs b/Content.Client/Tools/Components/WelderComponent.cs index 91efcaf270..bd226f5ca0 100644 --- a/Content.Client/Tools/Components/WelderComponent.cs +++ b/Content.Client/Tools/Components/WelderComponent.cs @@ -2,43 +2,30 @@ using System; using Content.Client.Items.Components; using Content.Client.Message; using Content.Client.Stylesheets; -using Content.Shared.Tool; +using Content.Shared.Tools.Components; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; +using Robust.Shared.Analyzers; using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; using Robust.Shared.Localization; using Robust.Shared.Timing; using Robust.Shared.ViewVariables; namespace Content.Client.Tools.Components { - [RegisterComponent] - [NetworkedComponent()] - public class WelderComponent : SharedToolComponent, IItemStatus + [RegisterComponent, Friend(typeof(ToolSystem), typeof(StatusControl))] + public class WelderComponent : SharedWelderComponent, IItemStatus { public override string Name => "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)] + public bool UiUpdateNeeded { get; set; } - public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) - { - base.HandleComponentState(curState, nextState); + [ViewVariables] + public float FuelCapacity { get; set; } - if (curState is not WelderComponentState weld) - return; - - FuelCapacity = weld.FuelCapacity; - Fuel = weld.Fuel; - Activated = weld.Activated; - _behavior = weld.Quality; - _uiUpdateNeeded = true; - } + [ViewVariables] + public float Fuel { get; set; } public Control MakeControl() => new StatusControl(this); @@ -53,7 +40,7 @@ namespace Content.Client.Tools.Components _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}}; AddChild(_label); - parent._uiUpdateNeeded = true; + parent.UiUpdateNeeded = true; } /// @@ -61,12 +48,12 @@ namespace Content.Client.Tools.Components { base.FrameUpdate(args); - if (!_parent._uiUpdateNeeded) + if (!_parent.UiUpdateNeeded) { return; } - _parent._uiUpdateNeeded = false; + _parent.UiUpdateNeeded = false; var fuelCap = _parent.FuelCapacity; var fuel = _parent.Fuel; diff --git a/Content.Client/Tools/ToolSystem.cs b/Content.Client/Tools/ToolSystem.cs new file mode 100644 index 0000000000..eb424ecc94 --- /dev/null +++ b/Content.Client/Tools/ToolSystem.cs @@ -0,0 +1,28 @@ +using Content.Client.Tools.Components; +using Content.Shared.Tools.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; + +namespace Content.Client.Tools +{ + public class ToolSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnWelderHandleState); + } + + private void OnWelderHandleState(EntityUid uid, WelderComponent welder, ref ComponentHandleState args) + { + if (args.Current is not WelderComponentState state) + return; + + welder.FuelCapacity = state.FuelCapacity; + welder.Fuel = state.Fuel; + welder.Lit = state.Lit; + welder.UiUpdateNeeded = true; + } + } +} diff --git a/Content.Server/AME/Components/AMEPartComponent.cs b/Content.Server/AME/Components/AMEPartComponent.cs index d677f9ade6..426a5a0563 100644 --- a/Content.Server/AME/Components/AMEPartComponent.cs +++ b/Content.Server/AME/Components/AMEPartComponent.cs @@ -5,7 +5,7 @@ using Content.Server.Tools.Components; using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Sound; -using Content.Shared.Tool; +using Content.Shared.Tools.Components; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.GameObjects; @@ -13,7 +13,9 @@ using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Map; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.AME.Components { @@ -25,18 +27,23 @@ namespace Content.Server.AME.Components [Dependency] private readonly IServerEntityManager _serverEntityManager = default!; public override string Name => "AMEPart"; - [DataField("unwrapSound")] private SoundSpecifier _unwrapSound = new SoundPathSpecifier("/Audio/Effects/unwrap.ogg"); + + [DataField("unwrapSound")] + private SoundSpecifier _unwrapSound = new SoundPathSpecifier("/Audio/Effects/unwrap.ogg"); + + [DataField("qualityNeeded", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _qualityNeeded = "Pulsing"; async Task IInteractUsing.InteractUsing(InteractUsingEventArgs args) { if (!args.User.TryGetComponent(out var hands)) { Owner.PopupMessage(args.User, Loc.GetString("ame-part-component-interact-using-no-hands")); - return true; + return false; } - if (!args.Using.TryGetComponent(out var multitool) || multitool.Qualities != ToolQuality.Multitool) - return true; + if (!args.Using.TryGetComponent(out var tool) || !tool.Qualities.Contains(_qualityNeeded)) + return false; if (!_mapManager.TryGetGrid(args.ClickLocation.GetGridId(_serverEntityManager), out var mapGrid)) return false; // No AME in space. @@ -53,7 +60,7 @@ namespace Content.Server.AME.Components SoundSystem.Play(Filter.Pvs(Owner), _unwrapSound.GetSound(), Owner); - Owner.Delete(); + Owner.QueueDelete(); return true; } diff --git a/Content.Server/Configurable/ConfigurationComponent.cs b/Content.Server/Configurable/ConfigurationComponent.cs index 808d75e5ce..3d25932855 100644 --- a/Content.Server/Configurable/ConfigurationComponent.cs +++ b/Content.Server/Configurable/ConfigurationComponent.cs @@ -6,11 +6,15 @@ using Content.Server.Tools.Components; using Content.Server.UserInterface; using Content.Shared.Configurable; using Content.Shared.Interaction; -using Content.Shared.Tool; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; +using Content.Shared.Verbs; +using Robust.Server.Console; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.ViewVariables; namespace Content.Server.Configurable @@ -29,6 +33,9 @@ namespace Content.Server.Configurable [DataField("validation")] private readonly Regex _validation = new ("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled); + [DataField("qualityNeeded", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _qualityNeeded = "Pulsing"; + void ISerializationHooks.BeforeSerialization() { _keys = _config.Keys.ToList(); @@ -76,10 +83,7 @@ namespace Content.Server.Configurable if (UserInterface == null || !eventArgs.User.TryGetComponent(out ActorComponent? actor)) return false; - if (!eventArgs.Using.TryGetComponent(out var tool)) - return false; - - if (!await tool.UseTool(eventArgs.User, Owner, 0.2f, ToolQuality.Multitool)) + if (!eventArgs.Using.TryGetComponent(out var tool) || !tool.Qualities.Contains(_qualityNeeded)) return false; OpenUserInterface(actor); diff --git a/Content.Server/Construction/Components/AnchorableComponent.cs b/Content.Server/Construction/Components/AnchorableComponent.cs index 2926635144..baceb11265 100644 --- a/Content.Server/Construction/Components/AnchorableComponent.cs +++ b/Content.Server/Construction/Components/AnchorableComponent.cs @@ -2,13 +2,16 @@ using System; using System.Threading.Tasks; using Content.Server.Coordinates.Helpers; using Content.Server.Pulling; +using Content.Server.Tools; using Content.Server.Tools.Components; using Content.Shared.Interaction; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; using Content.Shared.Pulling.Components; -using Content.Shared.Tool; using Robust.Shared.GameObjects; using Robust.Shared.Physics; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.ViewVariables; namespace Content.Server.Construction.Components @@ -20,8 +23,8 @@ namespace Content.Server.Construction.Components public override string Name => "Anchorable"; [ViewVariables] - [DataField("tool")] - public ToolQuality Tool { get; private set; } = ToolQuality.Anchoring; + [DataField("tool", customTypeSerializer:typeof(PrototypeIdSerializer))] + public string Tool { get; private set; } = "Anchoring"; [ViewVariables] int IInteractUsing.Priority => 1; @@ -56,7 +59,7 @@ namespace Content.Server.Construction.Components if (attempt.Cancelled) return false; - return utilizing.TryGetComponent(out ToolComponent? tool) && await tool.UseTool(user, Owner, 0.5f + attempt.Delay, Tool); + return await EntitySystem.Get().UseTool(utilizing.Uid, user.Uid, Owner.Uid, 0f, 0.5f + attempt.Delay, Tool); } /// diff --git a/Content.Server/Construction/Components/ConstructionComponent.cs b/Content.Server/Construction/Components/ConstructionComponent.cs index 1ba1aca3dd..f9c9a7fc3c 100644 --- a/Content.Server/Construction/Components/ConstructionComponent.cs +++ b/Content.Server/Construction/Components/ConstructionComponent.cs @@ -5,12 +5,13 @@ using System.Linq; using System.Threading.Tasks; using Content.Server.DoAfter; using Content.Server.Stack; +using Content.Server.Tools; using Content.Server.Tools.Components; using Content.Shared.Construction; using Content.Shared.Construction.Prototypes; using Content.Shared.Construction.Steps; using Content.Shared.Interaction; -using Content.Shared.Tool; +using Content.Shared.Tools.Components; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -227,19 +228,7 @@ namespace Content.Server.Construction.Components switch (step) { case ToolConstructionGraphStep toolStep: - // Gotta take welder fuel into consideration. - if (toolStep.Tool == ToolQuality.Welding) - { - if (eventArgs.Using.TryGetComponent(out WelderComponent? welder) && - await welder.UseTool(eventArgs.User, Owner, step.DoAfter, toolStep.Tool, toolStep.Fuel)) - { - handled = true; - } - break; - } - - if (eventArgs.Using.TryGetComponent(out ToolComponent? tool) && - await tool.UseTool(eventArgs.User, Owner, step.DoAfter, toolStep.Tool)) + if (await EntitySystem.Get().UseTool(eventArgs.Using.Uid, eventArgs.User.Uid, Owner.Uid, toolStep.Fuel, step.DoAfter, toolStep.Tool)) { handled = true; } diff --git a/Content.Server/Construction/Components/WelderRefinableComponent.cs b/Content.Server/Construction/Components/WelderRefinableComponent.cs index 620f1f3c05..c6eb09027d 100644 --- a/Content.Server/Construction/Components/WelderRefinableComponent.cs +++ b/Content.Server/Construction/Components/WelderRefinableComponent.cs @@ -1,12 +1,15 @@ using System.Collections.Generic; using System.Threading.Tasks; using Content.Server.Stack; +using Content.Server.Tools; using Content.Server.Tools.Components; using Content.Shared.Interaction; using Content.Shared.Stacks; -using Content.Shared.Tool; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.ViewVariables; namespace Content.Server.Construction.Components @@ -18,13 +21,18 @@ namespace Content.Server.Construction.Components [RegisterComponent] public class WelderRefinableComponent : Component, IInteractUsing { - [ViewVariables] [DataField("refineResult")] private HashSet? _refineResult = new() { }; - [ViewVariables] + [DataField("refineTime")] private float _refineTime = 2f; + [DataField("refineFuel")] + private float _refineFuel = 0f; + + [DataField("qualityNeeded", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _qualityNeeded = "Welding"; + private bool _beingWelded; public override string Name => "WelderRefinable"; @@ -38,9 +46,12 @@ namespace Content.Server.Construction.Components // check if someone is already welding object if (_beingWelded) return false; + _beingWelded = true; - if (!await tool.UseTool(eventArgs.User, Owner, _refineTime, ToolQuality.Welding)) + var toolSystem = EntitySystem.Get(); + + if (!await toolSystem.UseTool(eventArgs.Using.Uid, eventArgs.User.Uid, Owner.Uid, _refineFuel, _refineTime, _qualityNeeded)) { // failed to veld - abort refine _beingWelded = false; diff --git a/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs b/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs index 0cdfe5fa18..353a42b4e0 100644 --- a/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs +++ b/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs @@ -1,55 +1,28 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Content.Server.Tools.Components; using Content.Shared.Damage; -using Content.Shared.Interaction; -using Content.Shared.Tool; +using Content.Shared.Tools; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Utility; using Robust.Shared.ViewVariables; namespace Content.Server.Damage.Components { [RegisterComponent] - public class DamageOnToolInteractComponent : Component, IInteractUsing + public class DamageOnToolInteractComponent : Component { public override string Name => "DamageOnToolInteract"; [DataField("tools")] - private List _tools = new(); + public PrototypeFlags Tools { get; } = new (); + // TODO: Remove this snowflake stuff, make damage per-tool quality perhaps? [DataField("weldingDamage")] [ViewVariables(VVAccess.ReadWrite)] - public DamageSpecifier? WeldingDamage; + public DamageSpecifier? WeldingDamage { get; } [DataField("defaultDamage")] [ViewVariables(VVAccess.ReadWrite)] - public DamageSpecifier? DefaultDamage; - - async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) - { - if (eventArgs.Using.TryGetComponent(out var tool)) - { - foreach (var toolQuality in _tools) - { - if (WeldingDamage != null && tool.HasQuality(ToolQuality.Welding) && toolQuality == ToolQuality.Welding) - { - if (eventArgs.Using.TryGetComponent(out WelderComponent? welder) && welder.WelderLit) - { - EntitySystem.Get().TryChangeDamage(eventArgs.Target.Uid, WeldingDamage); - return true; - } - break; //If the tool quality is welding and its not lit or its not actually a welder that can be lit then its pointless to continue. - } - - if (DefaultDamage != null && tool.HasQuality(toolQuality)) - { - EntitySystem.Get().TryChangeDamage(eventArgs.Target.Uid, DefaultDamage); - return true; - } - } - } - return false; - } + public DamageSpecifier? DefaultDamage { get; } } } diff --git a/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs b/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs new file mode 100644 index 0000000000..225ff9ae76 --- /dev/null +++ b/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs @@ -0,0 +1,45 @@ +using Content.Server.Damage.Components; +using Content.Server.Tools.Components; +using Content.Shared.Damage; +using Content.Shared.Interaction; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Server.Damage.Systems +{ + public class DamageOnToolInteractSystem : EntitySystem + { + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInteracted); + } + + private void OnInteracted(EntityUid uid, DamageOnToolInteractComponent component, InteractUsingEvent args) + { + if (args.Handled) + return; + + if (component.WeldingDamage is {} weldingDamage + && args.Used.TryGetComponent(out var welder) + && welder.Lit) + { + _damageableSystem.TryChangeDamage(args.Target.Uid, weldingDamage); + args.Handled = true; + return; + } + + if (component.DefaultDamage is {} damage + && args.Used.TryGetComponent(out var tool) + && tool.Qualities.ContainsAny(component.Tools)) + { + _damageableSystem.TryChangeDamage(args.Target.Uid, damage); + args.Handled = true; + return; + } + } + } +} diff --git a/Content.Server/Doors/Components/ServerDoorComponent.cs b/Content.Server/Doors/Components/ServerDoorComponent.cs index c19d863d6f..3f31c127d4 100644 --- a/Content.Server/Doors/Components/ServerDoorComponent.cs +++ b/Content.Server/Doors/Components/ServerDoorComponent.cs @@ -9,12 +9,14 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Construction.Components; using Content.Server.Hands.Components; using Content.Server.Stunnable.Components; +using Content.Server.Tools; using Content.Server.Tools.Components; using Content.Shared.Damage; using Content.Shared.Doors; using Content.Shared.Interaction; using Content.Shared.Sound; -using Content.Shared.Tool; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.GameObjects; @@ -23,7 +25,9 @@ using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Player; using Robust.Shared.Players; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.ViewVariables; using Timer = Robust.Shared.Timing.Timer; @@ -35,9 +39,15 @@ namespace Content.Server.Doors.Components public class ServerDoorComponent : SharedDoorComponent, IActivate, IInteractUsing, IMapInit { [ViewVariables] - [DataField("board")] + [DataField("board", customTypeSerializer:typeof(PrototypeIdSerializer))] private string? _boardPrototype; + [DataField("weldingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _weldingQuality = "Welding"; + + [DataField("pryingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _pryingQuality = "Prying"; + [DataField("tryOpenDoorSound")] private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg"); @@ -627,8 +637,10 @@ namespace Content.Server.Doors.Components return false; } + var toolSystem = EntitySystem.Get(); + // for prying doors - if (tool.HasQuality(ToolQuality.Prying) && !IsWeldedShut) + if (tool.Qualities.Contains(_pryingQuality) && !IsWeldedShut) { var ev = new DoorGetPryTimeModifierEvent(); Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false); @@ -636,8 +648,8 @@ namespace Content.Server.Doors.Components var canEv = new BeforeDoorPryEvent(eventArgs); Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, canEv, false); - var successfulPry = await tool.UseTool(eventArgs.User, Owner, - ev.PryTimeModifier * PryTime, ToolQuality.Prying, () => !canEv.Cancelled); + var successfulPry = await toolSystem.UseTool(eventArgs.Using.Uid, eventArgs.User.Uid, Owner.Uid, + 0f, ev.PryTimeModifier * PryTime, _pryingQuality, () => !canEv.Cancelled); if (successfulPry && !IsWeldedShut) { @@ -655,12 +667,12 @@ namespace Content.Server.Doors.Components } // for welding doors - if (CanWeldShut && tool.Owner.TryGetComponent(out WelderComponent? welder) && welder.WelderLit) + if (CanWeldShut && tool.Owner.TryGetComponent(out WelderComponent? welder) && welder.Lit) { if(!_beingWelded) { _beingWelded = true; - if(await welder.UseTool(eventArgs.User, Owner, 3f, ToolQuality.Welding, 3f, () => CanWeldShut)) + if(await toolSystem.UseTool(eventArgs.Using.Uid, eventArgs.User.Uid, Owner.Uid, 3f, 3f, _weldingQuality, () => CanWeldShut)) { // just in case if (!CanWeldShut) diff --git a/Content.Server/Power/Components/CableComponent.cs b/Content.Server/Power/Components/CableComponent.cs index 31310b79fd..3ec0909c1a 100644 --- a/Content.Server/Power/Components/CableComponent.cs +++ b/Content.Server/Power/Components/CableComponent.cs @@ -1,10 +1,13 @@ using System.Threading.Tasks; using Content.Server.Stack; +using Content.Server.Tools; using Content.Server.Tools.Components; using Content.Shared.Interaction; -using Content.Shared.Tool; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.ViewVariables; namespace Content.Server.Power.Components @@ -21,6 +24,9 @@ namespace Content.Server.Power.Components [DataField("cableDroppedOnCutPrototype")] private string? _cableDroppedOnCutPrototype = "CableHVStack1"; + [DataField("cuttingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _cuttingQuality = "Cutting"; + /// /// Checked by to determine if there is /// already a cable of a type on a tile. @@ -36,7 +42,7 @@ namespace Content.Server.Power.Components return false; if (!eventArgs.Using.TryGetComponent(out var tool)) return false; - if (!await tool.UseTool(eventArgs.User, Owner, 0.25f, ToolQuality.Cutting)) return false; + if (!await EntitySystem.Get().UseTool(eventArgs.Using.Uid, eventArgs.User.Uid, Owner.Uid, 0f, 0.25f, _cuttingQuality)) return false; Owner.Delete(); var droppedEnt = Owner.EntityManager.SpawnEntity(_cableDroppedOnCutPrototype, eventArgs.ClickLocation); diff --git a/Content.Server/Repairable/RepairableComponent.cs b/Content.Server/Repairable/RepairableComponent.cs index 1398d88799..a1e6e86189 100644 --- a/Content.Server/Repairable/RepairableComponent.cs +++ b/Content.Server/Repairable/RepairableComponent.cs @@ -1,5 +1,7 @@ +using Content.Shared.Tools; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.ViewVariables; namespace Content.Server.Repairable @@ -12,6 +14,9 @@ namespace Content.Server.Repairable [ViewVariables(VVAccess.ReadWrite)] [DataField("fuelCost")] public int FuelCost = 5; + [ViewVariables(VVAccess.ReadWrite)] [DataField("qualityNeeded", customTypeSerializer:typeof(PrototypeIdSerializer))] + public string QualityNeeded = "Welding"; + [ViewVariables(VVAccess.ReadWrite)] [DataField("doAfterDelay")] public int DoAfterDelay = 1; } diff --git a/Content.Server/Repairable/RepairableSystem.cs b/Content.Server/Repairable/RepairableSystem.cs index cd36b1b096..bb5b2d7921 100644 --- a/Content.Server/Repairable/RepairableSystem.cs +++ b/Content.Server/Repairable/RepairableSystem.cs @@ -1,16 +1,18 @@ +using Content.Server.Tools; using Content.Server.Tools.Components; using Content.Shared.Damage; using Content.Shared.Interaction; using Content.Shared.Popups; -using Content.Shared.Tool; +using Content.Shared.Tools.Components; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; namespace Content.Server.Repairable { - public class ReairableSystem : EntitySystem + public class RepairableSystem : EntitySystem { + [Dependency] private readonly ToolSystem _toolSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; public override void Initialize() @@ -20,16 +22,12 @@ namespace Content.Server.Repairable public async void Repair(EntityUid uid, RepairableComponent component, InteractUsingEvent args) { - // Only repair if you are using a lit welder - if (!args.Used.TryGetComponent(out WelderComponent? welder) || !welder.WelderLit) - return; - // Only try repair the target if it is damaged if (!component.Owner.TryGetComponent(out DamageableComponent? damageable) || damageable.TotalDamage == 0) return; - // Can the welder actually repair this, does it have enough fuel? - if (!await welder.UseTool(args.User, component.Owner, component.DoAfterDelay, ToolQuality.Welding, component.FuelCost)) + // Can the tool actually repair this, does it have enough fuel? + if (!await _toolSystem.UseTool(args.Used.Uid, args.User.Uid, uid, component.FuelCost, component.DoAfterDelay, component.QualityNeeded)) return; // Repair all damage @@ -38,7 +36,7 @@ namespace Content.Server.Repairable component.Owner.PopupMessage(args.User, Loc.GetString("comp-repairable-repair", ("target", component.Owner), - ("welder", args.Used))); + ("tool", args.Used))); args.Handled = true; } diff --git a/Content.Server/Storage/Components/EntityStorageComponent.cs b/Content.Server/Storage/Components/EntityStorageComponent.cs index d04d456a78..bb0c5114d7 100644 --- a/Content.Server/Storage/Components/EntityStorageComponent.cs +++ b/Content.Server/Storage/Components/EntityStorageComponent.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Content.Server.Tools; using Content.Server.Ghost.Components; using Content.Server.Tools.Components; using Content.Shared.ActionBlocker; @@ -14,7 +15,8 @@ using Content.Shared.Placeable; using Content.Shared.Popups; using Content.Shared.Sound; using Content.Shared.Storage; -using Content.Shared.Tool; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; using Content.Shared.Verbs; using Robust.Server.GameObjects; using Robust.Shared.Audio; @@ -26,6 +28,7 @@ using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Player; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.ViewVariables; namespace Content.Server.Storage.Components @@ -64,6 +67,9 @@ namespace Content.Server.Storage.Components [DataField("open")] private bool _open; + [DataField("weldingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _weldingQuality = "Welding"; + [DataField("CanWeldShut")] private bool _canWeldShut = true; @@ -393,7 +399,7 @@ namespace Content.Server.Storage.Components return false; } - if (!eventArgs.Using.TryGetComponent(out WelderComponent? tool) || !tool.WelderLit) + if (!eventArgs.Using.TryGetComponent(out WelderComponent? tool) || !tool.Lit) { _beingWelded = false; return false; @@ -404,7 +410,9 @@ namespace Content.Server.Storage.Components _beingWelded = true; - if (!await tool.UseTool(eventArgs.User, Owner, 1f, ToolQuality.Welding, 1f)) + var toolSystem = EntitySystem.Get(); + + if (!await toolSystem.UseTool(eventArgs.Using.Uid, eventArgs.User.Uid, Owner.Uid, 1f, 1f, _weldingQuality)) { _beingWelded = false; return false; diff --git a/Content.Server/Toilet/ToiletComponent.cs b/Content.Server/Toilet/ToiletComponent.cs index 063ee4ca0d..24334f4ea6 100644 --- a/Content.Server/Toilet/ToiletComponent.cs +++ b/Content.Server/Toilet/ToiletComponent.cs @@ -4,6 +4,7 @@ using Content.Server.Buckle.Components; using Content.Server.Chat.Managers; using Content.Server.Popups; using Content.Server.Storage.Components; +using Content.Server.Tools; using Content.Server.Tools.Components; using Content.Shared.Audio; using Content.Shared.Body.Components; @@ -13,7 +14,8 @@ using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Sound; using Content.Shared.Toilet; -using Content.Shared.Tool; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.GameObjects; @@ -22,6 +24,7 @@ using Robust.Shared.Localization; using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; @@ -37,6 +40,9 @@ namespace Content.Server.Toilet private bool _isPrying = false; + [DataField("pryingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _pryingQuality = "Prying"; + [ViewVariables] public bool LidOpen { get; private set; } [ViewVariables] public bool IsSeatUp { get; private set; } @@ -62,14 +68,14 @@ namespace Content.Server.Toilet { // are player trying place or lift of cistern lid? if (eventArgs.Using.TryGetComponent(out ToolComponent? tool) - && tool!.HasQuality(ToolQuality.Prying)) + && tool.Qualities.Contains(_pryingQuality)) { // check if someone is already prying this toilet if (_isPrying) return false; _isPrying = true; - if (!await tool.UseTool(eventArgs.User, Owner, PryLidTime, ToolQuality.Prying)) + if (!await EntitySystem.Get().UseTool(eventArgs.Using.Uid, eventArgs.User.Uid, Owner.Uid, 0f, PryLidTime, _pryingQuality)) { _isPrying = false; return false; diff --git a/Content.Server/Tools/Components/MultipleToolComponent.cs b/Content.Server/Tools/Components/MultipleToolComponent.cs new file mode 100644 index 0000000000..9e2dfb4e42 --- /dev/null +++ b/Content.Server/Tools/Components/MultipleToolComponent.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using Content.Shared.Sound; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.Localization; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; + +namespace Content.Server.Tools.Components +{ + /// + /// Not to be confused with Multitool (power) + /// + [RegisterComponent] + public class MultipleToolComponent : SharedMultipleToolComponent + { + [DataDefinition] + public class ToolEntry + { + [DataField("behavior", required:true)] + public PrototypeFlags Behavior { get; } = new(); + + [DataField("useSound")] + public SoundSpecifier? Sound { get; } = null; + + [DataField("changeSound")] + public SoundSpecifier? ChangeSound { get; } = null; + + [DataField("sprite")] + public SpriteSpecifier? Sprite { get; } = null; + } + + [DataField("entries", required:true)] + public ToolEntry[] Entries { get; } = Array.Empty(); + + [ViewVariables] + public int CurrentEntry = 0; + + [ViewVariables] + public string CurrentQualityName = string.Empty; + } +} diff --git a/Content.Server/Tools/Components/MultitoolComponent.cs b/Content.Server/Tools/Components/MultitoolComponent.cs deleted file mode 100644 index ecc6289ff4..0000000000 --- a/Content.Server/Tools/Components/MultitoolComponent.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using Content.Shared.Interaction; -using Content.Shared.Sound; -using Content.Shared.Tool; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; -using Robust.Shared.Player; -using Robust.Shared.Players; -using Robust.Shared.Serialization.Manager.Attributes; - -namespace Content.Server.Tools.Components -{ - /// - /// Not to be confused with Multitool (power) - /// - [RegisterComponent] - [NetworkedComponent()] - public class MultiToolComponent : Component, IUse - { - [DataDefinition] - public class ToolEntry - { - [DataField("behavior")] public ToolQuality Behavior { get; private set; } = ToolQuality.None; - - [DataField("state")] - public string State { get; } = string.Empty; - - [DataField("texture")] - public string Texture { get; } = string.Empty; - - [DataField("sprite")] - public string Sprite { get; } = string.Empty; - - [DataField("useSound", required: true)] - public SoundSpecifier Sound { get; } = default!; - - [DataField("changeSound", required: true)] - public SoundSpecifier ChangeSound { get; } = default!; - } - - public override string Name => "MultiTool"; - [DataField("tools")] private List _tools = new(); - private int _currentTool = 0; - - private ToolComponent? _tool; - private SpriteComponent? _sprite; - - protected override void Initialize() - { - base.Initialize(); - Owner.TryGetComponent(out _tool); - Owner.TryGetComponent(out _sprite); - SetTool(); - } - - public void Cycle() - { - _currentTool = (_currentTool + 1) % _tools.Count; - SetTool(); - var current = _tools[_currentTool]; - SoundSystem.Play(Filter.Pvs(Owner), current.ChangeSound.GetSound(), Owner); - } - - private void SetTool() - { - if (_tool == null) return; - - var current = _tools[_currentTool]; - - _tool.UseSound = current.Sound; - _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(); - } - - bool IUse.UseEntity(UseEntityEventArgs eventArgs) - { - Cycle(); - return true; - } - - public override ComponentState GetComponentState(ICommonSession player) - { - return new MultiToolComponentState(_tool?.Qualities ?? ToolQuality.None); - } - } -} diff --git a/Content.Server/Tools/Components/TilePryingComponent.cs b/Content.Server/Tools/Components/TilePryingComponent.cs index d12b4c5c55..f397a09b4f 100644 --- a/Content.Server/Tools/Components/TilePryingComponent.cs +++ b/Content.Server/Tools/Components/TilePryingComponent.cs @@ -2,11 +2,13 @@ using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; using Content.Shared.Maps; -using Content.Shared.Tool; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Tools.Components { @@ -17,9 +19,13 @@ namespace Content.Server.Tools.Components [Dependency] private readonly IMapManager _mapManager = default!; public override string Name => "TilePrying"; + [DataField("toolComponentNeeded")] private bool _toolComponentNeeded = true; + [DataField("qualityNeeded", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _qualityNeeded = "Prying"; + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { TryPryTile(eventArgs.User, eventArgs.ClickLocation); @@ -43,9 +49,10 @@ namespace Content.Server.Tools.Components var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId]; - if (!tileDef.CanCrowbar) return; + if (!tileDef.CanCrowbar) + return; - if (_toolComponentNeeded && !await tool!.UseTool(user, null, 0f, ToolQuality.Prying)) + if (_toolComponentNeeded && !await EntitySystem.Get().UseTool(Owner.Uid, user.Uid, null, 0f, 0f, _qualityNeeded)) return; coordinates.PryTile(Owner.EntityManager, _mapManager); diff --git a/Content.Server/Tools/Components/ToolComponent.cs b/Content.Server/Tools/Components/ToolComponent.cs index de29c0a930..924beeb7dc 100644 --- a/Content.Server/Tools/Components/ToolComponent.cs +++ b/Content.Server/Tools/Components/ToolComponent.cs @@ -1,40 +1,20 @@ -using System; -using System.Threading.Tasks; -using Content.Server.DoAfter; -using Content.Shared.ActionBlocker; -using Content.Shared.Audio; using Content.Shared.Sound; -using Content.Shared.Tool; -using Robust.Shared.Audio; +using Content.Shared.Tools; +using Robust.Shared.Analyzers; using Robust.Shared.GameObjects; -using Robust.Shared.Player; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Utility; using Robust.Shared.ViewVariables; namespace Content.Server.Tools.Components { - public interface IToolComponent + [RegisterComponent, Friend(typeof(ToolSystem))] + public class ToolComponent : Component { - ToolQuality Qualities { get; set; } - } + public override string Name => "Tool"; - [RegisterComponent] - [ComponentReference(typeof(IToolComponent))] - public class ToolComponent : SharedToolComponent, IToolComponent - { [DataField("qualities")] - protected ToolQuality _qualities = ToolQuality.None; - - [ViewVariables] - public override ToolQuality Qualities - { - get => _qualities; - set - { - _qualities = value; - Dirty(); - } - } + public PrototypeFlags Qualities { get; set; } = new(); /// /// For tool interactions that have a delay before action this will modify the rate, time to wait is divided by this value @@ -43,63 +23,7 @@ namespace Content.Server.Tools.Components [DataField("speed")] public float SpeedModifier { get; set; } = 1; - // Some tools don't play a sound on use. [DataField("useSound")] public SoundSpecifier? UseSound { 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 virtual async Task UseTool(IEntity user, IEntity? target, float doAfterDelay, ToolQuality toolQualityNeeded, Func? doAfterCheck = null) - { - if (!HasQuality(toolQualityNeeded) || !EntitySystem.Get().CanInteract(user)) - return false; - - if (doAfterDelay > 0f) - { - var doAfterSystem = EntitySystem.Get(); - - var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / SpeedModifier, default, target) - { - ExtraCheck = doAfterCheck, - BreakOnDamage = false, // TODO: Change this to true once breathing is fixed. - BreakOnStun = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, - NeedHand = true, - }; - - var result = await doAfterSystem.WaitDoAfter(doAfterArgs); - - if (result == DoAfterStatus.Cancelled) - return false; - } - - PlayUseSound(); - - return true; - } - - public void PlayUseSound(float volume = -5f) - { - if (UseSound == null) - return; - - SoundSystem.Play(Filter.Pvs(Owner), UseSound.GetSound(), Owner, AudioHelpers.WithVariation(0.15f).WithVolume(volume)); - } } } diff --git a/Content.Server/Tools/Components/WelderComponent.cs b/Content.Server/Tools/Components/WelderComponent.cs index cc38cb72fe..680e8f8b39 100644 --- a/Content.Server/Tools/Components/WelderComponent.cs +++ b/Content.Server/Tools/Components/WelderComponent.cs @@ -1,328 +1,52 @@ -using System; -using System.Threading.Tasks; -using Content.Server.Act; -using Content.Server.Atmos.EntitySystems; -using Content.Server.Chat.Managers; -using Content.Server.Chemistry.Components; -using Content.Server.Explosion; -using Content.Server.Items; -using Content.Server.Popups; -using Content.Shared.Audio; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Components.SolutionManager; -using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Interaction; -using Content.Shared.Popups; using Content.Shared.Sound; -using Content.Shared.Tool; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; +using Content.Shared.Tools.Components; using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; -using Robust.Shared.IoC; -using Robust.Shared.Localization; -using Robust.Shared.Player; -using Robust.Shared.Players; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Tools.Components { [RegisterComponent] - [ComponentReference(typeof(ToolComponent))] - [ComponentReference(typeof(IToolComponent))] - [NetworkedComponent()] - public class WelderComponent : ToolComponent, IUse, ISuicideAct, IAfterInteract + public class WelderComponent : SharedWelderComponent { - [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; - - public override string Name => "Welder"; - - public const string SolutionName = "welder"; + /// + /// Solution on the entity that contains the fuel. + /// + [DataField("fuelSolution")] + public string FuelSolution { get; } = "Welder"; /// - /// Default Cost of using the welder fuel for an action + /// Reagent that will be used as fuel for welding. /// - public const float DefaultFuelCost = 10; + [DataField("fuelReagent", customTypeSerializer:typeof(PrototypeIdSerializer))] + public string FuelReagent { get; } = "WeldingFuel"; /// - /// Rate at which we expunge fuel from ourselves when activated + /// Fuel consumption per second, while the welder is active. /// - public const float FuelLossRate = 0.5f; + [DataField("fuelConsumption")] + public ReagentUnit FuelConsumption { get; } = ReagentUnit.New(0.05f); - private bool _welderLit; - private WelderSystem _welderSystem = default!; - private SpriteComponent? _spriteComponent; - private PointLightComponent? _pointLightComponent; - - [DataField("weldSounds")] - private SoundSpecifier WeldSounds { get; set; } = new SoundCollectionSpecifier("Welder"); + /// + /// A fuel amount to be consumed when the welder goes from being unlit to being lit. + /// + [DataField("welderOnConsume")] + public ReagentUnit FuelLitCost { get; } = ReagentUnit.New(0.5f); + /// + /// Sound played when the welder is turned off. + /// [DataField("welderOffSounds")] - private SoundSpecifier WelderOffSounds { get; set; } = new SoundCollectionSpecifier("WelderOff"); + public SoundSpecifier WelderOffSounds { get; } = new SoundCollectionSpecifier("WelderOff"); + /// + /// Sound played when the tool is turned on. + /// [DataField("welderOnSounds")] - private SoundSpecifier WelderOnSounds { get; set; } = new SoundCollectionSpecifier("WelderOn"); + public SoundSpecifier WelderOnSounds { get; } = new SoundCollectionSpecifier("WelderOn"); [DataField("welderRefill")] - private SoundSpecifier WelderRefill { get; set; } = new SoundPathSpecifier("/Audio/Effects/refill.ogg"); - - [ViewVariables] public float Fuel => WelderSolution?.GetReagentQuantity("WeldingFuel").Float() ?? 0f; - - [ViewVariables] public float FuelCapacity => WelderSolution?.MaxVolume.Float() ?? 0f; - - private Solution? WelderSolution - { - get - { - Owner.EntityManager.EntitySysManager.GetEntitySystem() - .TryGetSolution(Owner, SolutionName, out var solution); - return solution; - } - } - - /// - /// Status of welder, whether it is ignited - /// - [ViewVariables] - public bool WelderLit - { - get => _welderLit; - private set - { - _welderLit = value; - Dirty(); - } - } - - protected override void Initialize() - { - base.Initialize(); - - AddQuality(ToolQuality.Welding); - - _welderSystem = _entitySystemManager.GetEntitySystem(); - - Owner.EnsureComponent(); - Owner.TryGetComponent(out _spriteComponent); - Owner.TryGetComponent(out _pointLightComponent); - EntitySystem.Get().EnsureSolution(Owner, "welder"); - } - - public override ComponentState GetComponentState(ICommonSession player) - { - return new WelderComponentState(FuelCapacity, Fuel, WelderLit); - } - - public override async Task UseTool(IEntity user, IEntity? target, float doAfterDelay, - ToolQuality toolQualityNeeded, Func? doAfterCheck = null) - { - bool ExtraCheck() - { - var extraCheck = doAfterCheck?.Invoke() ?? true; - - if (!CanWeld(DefaultFuelCost)) - { - target?.PopupMessage(user, "Can't weld!"); - - return false; - } - - return extraCheck; - } - - var canUse = await base.UseTool(user, target, doAfterDelay, toolQualityNeeded, ExtraCheck); - - return toolQualityNeeded.HasFlag(ToolQuality.Welding) ? canUse && TryWeld(DefaultFuelCost, user) : canUse; - } - - public async Task UseTool(IEntity user, IEntity target, float doAfterDelay, ToolQuality toolQualityNeeded, - float fuelConsumed, Func? doAfterCheck = null) - { - bool ExtraCheck() - { - var extraCheck = doAfterCheck?.Invoke() ?? true; - - return extraCheck && CanWeld(fuelConsumed); - } - - return await base.UseTool(user, target, doAfterDelay, toolQualityNeeded, ExtraCheck) && - TryWeld(fuelConsumed, user); - } - - private bool TryWeld(float value, IEntity? user = null, bool silent = false) - { - if (!WelderLit) - { - if (!silent && user != null) - Owner.PopupMessage(user, Loc.GetString("welder-component-welder-not-lit-message")); - - return false; - } - - if (!CanWeld(value)) - { - if (!silent && user != null) - Owner.PopupMessage(user, Loc.GetString("welder-component-cannot-weld-message")); - - return false; - } - - if (WelderSolution == null) - return false; - - var succeeded = EntitySystem.Get() - .TryRemoveReagent(Owner.Uid, WelderSolution, "WeldingFuel", ReagentUnit.New(value)); - - if (succeeded && !silent) - { - PlaySound(WeldSounds); - } - - return succeeded; - } - - 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) - { - var item = Owner.GetComponent(); - - if (WelderLit) - { - WelderLit = false; - // Layer 1 is the flame. - item.EquippedPrefix = "off"; - _spriteComponent?.LayerSetVisible(1, false); - - if (_pointLightComponent != null) _pointLightComponent.Enabled = false; - - PlaySound(WelderOffSounds, -5); - _welderSystem.Unsubscribe(this); - return true; - } - - if (!CanLitWelder() && user != null) - { - Owner.PopupMessage(user, Loc.GetString("welder-component-no-fuel-message")); - return false; - } - - WelderLit = true; - item.EquippedPrefix = "on"; - _spriteComponent?.LayerSetVisible(1, true); - - if (_pointLightComponent != null) _pointLightComponent.Enabled = true; - - PlaySound(WelderOnSounds, -5); - _welderSystem.Subscribe(this); - - EntitySystem.Get().HotspotExpose(Owner.Transform.Coordinates, 700, 50, true); - - return true; - } - - bool IUse.UseEntity(UseEntityEventArgs eventArgs) - { - return ToggleWelderStatus(eventArgs.User); - } - - protected override void Shutdown() - { - base.Shutdown(); - _welderSystem.Unsubscribe(this); - } - - public void OnUpdate(float frameTime) - { - if (!HasQuality(ToolQuality.Welding) || !WelderLit || Owner.Deleted) - return; - - EntitySystem.Get().TryRemoveReagent(Owner.Uid, WelderSolution, "WeldingFuel", - ReagentUnit.New(FuelLossRate * frameTime)); - - EntitySystem.Get().HotspotExpose(Owner.Transform.Coordinates, 700, 50, true); - - if (Fuel == 0) - ToggleWelderStatus(); - } - - SuicideKind ISuicideAct.Suicide(IEntity victim, IChatManager chat) - { - string othersMessage; - string selfMessage; - - if (TryWeld(5, victim, silent: true)) - { - PlaySound(WeldSounds); - - othersMessage = - Loc.GetString("welder-component-suicide-lit-others-message", - ("victim", victim)); - victim.PopupMessageOtherClients(othersMessage); - - selfMessage = Loc.GetString("welder-component-suicide-lit-message"); - victim.PopupMessage(selfMessage); - - return SuicideKind.Heat; - } - - othersMessage = Loc.GetString("welder-component-suicide-unlit-others-message", ("victim", victim)); - victim.PopupMessageOtherClients(othersMessage); - - selfMessage = Loc.GetString("welder-component-suicide-unlit-message"); - victim.PopupMessage(selfMessage); - - return SuicideKind.Blunt; - } - - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) - { - if (eventArgs.Target == null || !eventArgs.CanReach) - { - return false; - } - - if (eventArgs.Target.TryGetComponent(out ReagentTankComponent? tank) - && tank.TankType == ReagentTankType.Fuel - && EntitySystem.Get() - .TryGetDrainableSolution(eventArgs.Target.Uid, out var targetSolution) - && WelderSolution != null) - { - var trans = ReagentUnit.Min(WelderSolution.AvailableVolume, targetSolution.DrainAvailable); - if (trans > 0) - { - var drained = EntitySystem.Get().Drain(eventArgs.Target.Uid, targetSolution, trans); - EntitySystem.Get().TryAddSolution(Owner.Uid, WelderSolution, drained); - SoundSystem.Play(Filter.Pvs(Owner), WelderRefill.GetSound(), Owner); - eventArgs.Target.PopupMessage(eventArgs.User, - Loc.GetString("welder-component-after-interact-refueled-message")); - } - else - { - eventArgs.Target.PopupMessage(eventArgs.User, - Loc.GetString("welder-component-no-fuel-in-tank", ("owner", eventArgs.Target))); - } - } - - return true; - } - - private void PlaySound(SoundSpecifier sound, float volume = -5f) - { - SoundSystem.Play(Filter.Pvs(Owner), sound.GetSound(), Owner, AudioHelpers.WithVariation(0.15f).WithVolume(volume)); - } + public SoundSpecifier WelderRefill { get; } = new SoundPathSpecifier("/Audio/Effects/refill.ogg"); } } diff --git a/Content.Server/Tools/ToolSystem.MultipleTool.cs b/Content.Server/Tools/ToolSystem.MultipleTool.cs new file mode 100644 index 0000000000..6ea8d80af8 --- /dev/null +++ b/Content.Server/Tools/ToolSystem.MultipleTool.cs @@ -0,0 +1,108 @@ +using System; +using System.Linq; +using Content.Server.Tools.Components; +using Content.Shared.Interaction; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.Tools +{ + public partial class ToolSystem + { + private void InitializeMultipleTools() + { + SubscribeLocalEvent(OnMultipleToolStartup); + SubscribeLocalEvent(OnMultipleToolUsedInHand); + SubscribeLocalEvent(OnMultipleToolActivated); + SubscribeLocalEvent(OnMultipleToolGetState); + } + + private void OnMultipleToolStartup(EntityUid uid, MultipleToolComponent multiple, ComponentStartup args) + { + // Only set the multiple tool if we have a tool component. + if(EntityManager.TryGetComponent(uid, out ToolComponent? tool)) + SetMultipleTool(uid, multiple, tool); + } + + private void OnMultipleToolUsedInHand(EntityUid uid, MultipleToolComponent multiple, UseInHandEvent args) + { + if (args.Handled) + return; + + args.Handled = CycleMultipleTool(uid, multiple); + } + + private void OnMultipleToolActivated(EntityUid uid, MultipleToolComponent multiple, ActivateInWorldEvent args) + { + if (args.Handled) + return; + + args.Handled = CycleMultipleTool(uid, multiple); + } + + private void OnMultipleToolGetState(EntityUid uid, MultipleToolComponent multiple, ref ComponentGetState args) + { + args.State = new MultipleToolComponentState(multiple.CurrentQualityName); + } + + public bool CycleMultipleTool(EntityUid uid, MultipleToolComponent? multiple = null) + { + if (!Resolve(uid, ref multiple)) + return false; + + if (multiple.Entries.Length == 0) + return false; + + multiple.CurrentEntry = (multiple.CurrentEntry + 1) % multiple.Entries.Length; + SetMultipleTool(uid, multiple); + + var current = multiple.Entries[multiple.CurrentEntry]; + + if(current.ChangeSound is {} changeSound) + SoundSystem.Play(Filter.Pvs(uid), changeSound.GetSound(), uid); + + return true; + } + + public void SetMultipleTool(EntityUid uid, MultipleToolComponent? multiple = null, ToolComponent? tool = null, SpriteComponent? sprite = null) + { + if (!Resolve(uid, ref multiple, ref tool)) + return; + + // Sprite is optional. + Resolve(uid, ref sprite); + + if (multiple.Entries.Length == 0) + { + multiple.CurrentQualityName = Loc.GetString("multiple-tool-component-no-behavior"); + multiple.Dirty(); + return; + } + + var current = multiple.Entries[multiple.CurrentEntry]; + + tool.UseSound = current.Sound; + tool.Qualities = current.Behavior; + + if (_prototypeManager.TryIndex(current.Behavior.First(), out ToolQualityPrototype? quality)) + { + multiple.CurrentQualityName = Loc.GetString(quality.Name); + } + + multiple.Dirty(); + + if (current.Sprite == null || sprite == null) + return; + + sprite.LayerSetSprite(0, current.Sprite); + } + } +} diff --git a/Content.Server/Tools/ToolSystem.Welder.cs b/Content.Server/Tools/ToolSystem.Welder.cs new file mode 100644 index 0000000000..08bbb84756 --- /dev/null +++ b/Content.Server/Tools/ToolSystem.Welder.cs @@ -0,0 +1,332 @@ +using System.Collections.Generic; +using System.Linq; +using Content.Server.Chemistry.Components; +using Content.Server.Items; +using Content.Server.Tools.Components; +using Content.Shared.Audio; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Examine; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Content.Shared.Temperature; +using Content.Shared.Tools.Components; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.Localization; +using Robust.Shared.Player; + +namespace Content.Server.Tools +{ + public partial class ToolSystem + { + private readonly HashSet _activeWelders = new(); + + private const float WelderUpdateTimer = 1f; + private float _welderTimer = 0f; + + public void InitializeWelders() + { + SubscribeLocalEvent(OnWelderStartup); + SubscribeLocalEvent(OnWelderIsHotEvent); + SubscribeLocalEvent(OnWelderExamine); + SubscribeLocalEvent(OnWelderSolutionChange); + SubscribeLocalEvent(OnWelderUseInHand); + SubscribeLocalEvent(OnWelderActivate); + SubscribeLocalEvent(OnWelderAfterInteract); + SubscribeLocalEvent(OnWelderToolUseAttempt); + SubscribeLocalEvent(OnWelderToolUseFinishAttempt); + SubscribeLocalEvent(OnWelderShutdown); + SubscribeLocalEvent(OnWelderGetState); + } + + public (ReagentUnit fuel, ReagentUnit capacity) GetWelderFuelAndCapacity(EntityUid uid, WelderComponent? welder = null, SolutionContainerManagerComponent? solutionContainer = null) + { + if (!Resolve(uid, ref welder, ref solutionContainer) + || !_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var fuelSolution, solutionContainer)) + return (ReagentUnit.Zero, ReagentUnit.Zero); + + return (_solutionContainerSystem.GetReagentQuantity(uid, welder.FuelReagent), fuelSolution.MaxVolume); + } + + public bool TryToggleWelder(EntityUid uid, EntityUid? user, + WelderComponent? welder = null, + SolutionContainerManagerComponent? solutionContainer = null, + ItemComponent? item = null, + PointLightComponent? light = null, + SpriteComponent? sprite = null) + { + // Right now, we only need the welder. + // So let's not unnecessarily resolve components + if (!Resolve(uid, ref welder)) + return false; + + return !welder.Lit + ? TryTurnWelderOn(uid, user, welder, solutionContainer, item, light, sprite) + : TryTurnWelderOff(uid, user, welder, item, light, sprite); + } + + public bool TryTurnWelderOn(EntityUid uid, EntityUid? user, + WelderComponent? welder = null, + SolutionContainerManagerComponent? solutionContainer = null, + ItemComponent? item = null, + PointLightComponent? light = null, + SpriteComponent? sprite = null) + { + if (!Resolve(uid, ref welder, ref solutionContainer)) + return false; + + // Optional components. + Resolve(uid, ref item, ref light, ref sprite); + + if (!_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var solution, solutionContainer)) + return false; + + var fuel = solution.GetReagentQuantity(welder.FuelReagent); + + // Not enough fuel to lit welder. + if (fuel == ReagentUnit.Zero || fuel < welder.FuelLitCost) + { + if(user != null) + _popupSystem.PopupEntity(Loc.GetString("welder-component-no-fuel-message"), uid, Filter.Entities(user.Value)); + return false; + } + + if (user != null && !_actionBlockerSystem.CanInteract(user.Value)) + return false; + + solution.RemoveReagent(welder.FuelReagent, welder.FuelLitCost); + + welder.Lit = true; + + if(item != null) + item.EquippedPrefix = "on"; + + sprite?.LayerSetVisible(1, true); + + if (light != null) + light.Enabled = true; + + SoundSystem.Play(Filter.Pvs(uid), welder.WelderOnSounds.GetSound(), uid, AudioHelpers.WithVariation(0.125f).WithVolume(-5f)); + + // TODO: Use ITransformComponent directly. + _atmosphereSystem.HotspotExpose(welder.Owner.Transform.Coordinates, 700, 50, true); + + welder.Dirty(); + + _activeWelders.Add(uid); + return true; + } + + public bool TryTurnWelderOff(EntityUid uid, EntityUid? user, + WelderComponent? welder = null, + ItemComponent? item = null, + PointLightComponent? light = null, + SpriteComponent? sprite = null) + { + if (!Resolve(uid, ref welder)) + return false; + + // Optional components. + Resolve(uid, ref item, ref light, ref sprite); + + if (user != null && !_actionBlockerSystem.CanInteract(user.Value)) + return false; + + welder.Lit = false; + + // TODO: Make all this use visualizers. + if (item != null) + item.EquippedPrefix = "off"; + + // Layer 1 is the flame. + sprite?.LayerSetVisible(1, false); + + if (light != null) + light.Enabled = false; + + SoundSystem.Play(Filter.Pvs(uid), welder.WelderOffSounds.GetSound(), uid, AudioHelpers.WithVariation(0.125f).WithVolume(-5f)); + + welder.Dirty(); + + _activeWelders.Remove(uid); + return true; + } + + private void OnWelderStartup(EntityUid uid, WelderComponent component, ComponentStartup args) + { + component.Dirty(); + } + + private void OnWelderIsHotEvent(EntityUid uid, WelderComponent welder, IsHotEvent args) + { + args.IsHot = welder.Lit; + } + + private void OnWelderExamine(EntityUid uid, WelderComponent welder, ExaminedEvent args) + { + if (welder.Lit) + { + args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message")); + } + else + { + args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-not-lit-message")); + } + + if (args.IsInDetailsRange) + { + var (fuel, capacity) = GetWelderFuelAndCapacity(uid, welder); + + args.PushMarkup(Loc.GetString("welder-component-on-examine-detailed-message", + ("colorName", fuel < capacity / ReagentUnit.New(4f) ? "darkorange" : "orange"), + ("fuelLeft", fuel), + ("fuelCapacity", capacity))); + } + } + + private void OnWelderSolutionChange(EntityUid uid, WelderComponent welder, SolutionChangedEvent args) + { + welder.Dirty(); + } + + private void OnWelderActivate(EntityUid uid, WelderComponent welder, ActivateInWorldEvent args) + { + args.Handled = TryToggleWelder(uid, args.User.Uid, welder); + } + + private void OnWelderAfterInteract(EntityUid uid, WelderComponent welder, AfterInteractEvent args) + { + if (args.Handled) + return; + + if (args.Target == null || !args.CanReach) + return; + + // TODO: Clean up this inherited oldcode. + + if (args.Target.TryGetComponent(out ReagentTankComponent? tank) + && tank.TankType == ReagentTankType.Fuel + && _solutionContainerSystem.TryGetDrainableSolution(args.Target.Uid, out var targetSolution) + && _solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var welderSolution)) + { + var trans = ReagentUnit.Min(welderSolution.AvailableVolume, targetSolution.DrainAvailable); + if (trans > 0) + { + var drained = _solutionContainerSystem.Drain(args.Target.Uid, targetSolution, trans); + _solutionContainerSystem.TryAddSolution(uid, welderSolution, drained); + SoundSystem.Play(Filter.Pvs(uid), welder.WelderRefill.GetSound(), uid); + args.Target.PopupMessage(args.User, Loc.GetString("welder-component-after-interact-refueled-message")); + } + else + { + args.Target.PopupMessage(args.User, Loc.GetString("welder-component-no-fuel-in-tank", ("owner", args.Target))); + } + } + + args.Handled = true; + } + + private void OnWelderUseInHand(EntityUid uid, WelderComponent welder, UseInHandEvent args) + { + args.Handled = TryToggleWelder(uid, args.User.Uid, welder); + } + + private void OnWelderToolUseAttempt(EntityUid uid, WelderComponent welder, ToolUseAttemptEvent args) + { + if (args.Cancelled) + return; + + if (!welder.Lit) + { + _popupSystem.PopupEntity(Loc.GetString("welder-component-welder-not-lit-message"), uid, Filter.Entities(args.User)); + args.Cancel(); + return; + } + + var (fuel, _) = GetWelderFuelAndCapacity(uid, welder); + + if (ReagentUnit.New(args.Fuel) > fuel) + { + _popupSystem.PopupEntity(Loc.GetString("welder-component-cannot-weld-message"), uid, Filter.Entities(args.User)); + args.Cancel(); + return; + } + } + + private void OnWelderToolUseFinishAttempt(EntityUid uid, WelderComponent welder, ToolUseFinishAttemptEvent args) + { + if (args.Cancelled) + return; + + if (!welder.Lit) + { + _popupSystem.PopupEntity(Loc.GetString("welder-component-welder-not-lit-message"), uid, Filter.Entities(args.User)); + args.Cancel(); + return; + } + + var (fuel, _) = GetWelderFuelAndCapacity(uid, welder); + + var neededFuel = ReagentUnit.New(args.Fuel); + + if (neededFuel > fuel) + { + args.Cancel(); + } + + if (!_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var solution)) + { + args.Cancel(); + return; + } + + solution.RemoveReagent(welder.FuelReagent, neededFuel); + welder.Dirty(); + } + + private void OnWelderShutdown(EntityUid uid, WelderComponent welder, ComponentShutdown args) + { + _activeWelders.Remove(uid); + } + + private void OnWelderGetState(EntityUid uid, WelderComponent welder, ref ComponentGetState args) + { + var (fuel, capacity) = GetWelderFuelAndCapacity(uid, welder); + args.State = new WelderComponentState(capacity.Float(), fuel.Float(), welder.Lit); + } + + private void UpdateWelders(float frameTime) + { + _welderTimer += frameTime; + + if (_welderTimer < WelderUpdateTimer) + return; + + foreach (var tool in _activeWelders.ToArray()) + { + if (!EntityManager.TryGetComponent(tool, out WelderComponent? welder) + || !EntityManager.TryGetComponent(tool, out SolutionContainerManagerComponent? solutionContainer)) + continue; + + if (!_solutionContainerSystem.TryGetSolution(tool, welder.FuelSolution, out var solution, solutionContainer)) + continue; + + // TODO: Use ITransformComponent directly. + _atmosphereSystem.HotspotExpose(welder.Owner.Transform.Coordinates, 700, 50, true); + + solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer); + + if (solution.GetReagentQuantity(welder.FuelReagent) <= ReagentUnit.Zero) + TryTurnWelderOff(tool, null, welder); + + welder.Dirty(); + } + + _welderTimer -= WelderUpdateTimer; + } + } +} diff --git a/Content.Server/Tools/ToolSystem.cs b/Content.Server/Tools/ToolSystem.cs new file mode 100644 index 0000000000..961f8063a9 --- /dev/null +++ b/Content.Server/Tools/ToolSystem.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Content.Server.Atmos.EntitySystems; +using Content.Server.DoAfter; +using Content.Server.Popups; +using Content.Server.Tools.Components; +using Content.Shared.ActionBlocker; +using Content.Shared.Audio; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Tools; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.Tools +{ + public partial class ToolSystem : EntitySystem + { + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; + [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + + + public override void Initialize() + { + base.Initialize(); + + InitializeWelders(); + InitializeMultipleTools(); + } + + public async Task UseTool(EntityUid tool, EntityUid user, EntityUid? target, float fuel, + float doAfterDelay, IEnumerable toolQualitiesNeeded, Func? doAfterCheck = null, + ToolComponent? toolComponent = null) + { + if (!Resolve(tool, ref toolComponent)) + return false; + + if (!toolComponent.Qualities.ContainsAll(toolQualitiesNeeded) || !_actionBlockerSystem.CanInteract(user)) + return false; + + var beforeAttempt = new ToolUseAttemptEvent(fuel, user); + RaiseLocalEvent(tool, beforeAttempt, false); + + if (beforeAttempt.Cancelled) + return false; + + if (doAfterDelay > 0f) + { + var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / toolComponent.SpeedModifier, default, target) + { + ExtraCheck = doAfterCheck, + BreakOnDamage = true, + BreakOnStun = true, + BreakOnTargetMove = true, + BreakOnUserMove = true, + NeedHand = true, + }; + + var result = await _doAfterSystem.WaitDoAfter(doAfterArgs); + + if (result == DoAfterStatus.Cancelled) + return false; + } + + var afterAttempt = new ToolUseFinishAttemptEvent(fuel, user); + RaiseLocalEvent(tool, afterAttempt, false); + + if (afterAttempt.Cancelled) + return false; + + if (toolComponent.UseSound != null) + PlayToolSound(tool, toolComponent); + + return true; + } + + public Task UseTool(EntityUid tool, EntityUid user, EntityUid? target, float fuel, + float doAfterDelay, string toolQualityNeeded, Func? doAfterCheck = null, + ToolComponent? toolComponent = null) + { + return UseTool(tool, user, target, fuel, doAfterDelay, new [] {toolQualityNeeded}, doAfterCheck, toolComponent); + } + + public void PlayToolSound(EntityUid uid, ToolComponent? tool = null) + { + if (!Resolve(uid, ref tool)) + return; + + if (tool.UseSound is not {} sound) + return; + + // Pass tool.Owner to Filter.Pvs to avoid a TryGetEntity call. + SoundSystem.Play(Filter.Pvs(tool.Owner), sound.GetSound(), uid, + AudioHelpers.WithVariation(0.175f).WithVolume(-5f)); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + UpdateWelders(frameTime); + } + } + + /// + /// Attempt event called *before* any do afters to see if the tool usage should succeed or not. + /// You can change the fuel consumption by changing the Fuel property. + /// + public class ToolUseAttemptEvent : CancellableEntityEventArgs + { + public float Fuel { get; set; } + public EntityUid User { get; } + + public ToolUseAttemptEvent(float fuel, EntityUid user) + { + Fuel = fuel; + User = user; + } + } + + /// + /// Attempt event called *after* any do afters to see if the tool usage should succeed or not. + /// You can use this event to consume any fuel needed. + /// + public class ToolUseFinishAttemptEvent : CancellableEntityEventArgs + { + public float Fuel { get; } + public EntityUid User { get; } + + public ToolUseFinishAttemptEvent(float fuel, EntityUid user) + { + Fuel = fuel; + } + } +} diff --git a/Content.Server/Tools/WelderSystem.cs b/Content.Server/Tools/WelderSystem.cs deleted file mode 100644 index 2a8451dfb6..0000000000 --- a/Content.Server/Tools/WelderSystem.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Content.Server.Tools.Components; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Examine; -using Content.Shared.Temperature; -using Robust.Shared.GameObjects; -using Robust.Shared.Localization; - -namespace Content.Server.Tools -{ - /// - /// Despite the name, it's only really used for the welder logic in tools. Go figure. - /// - public class WelderSystem : EntitySystem - { - private readonly HashSet _activeWelders = new(); - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSolutionChange); - SubscribeLocalEvent(OnIsHotEvent); - SubscribeLocalEvent(OnExamine); - } - - private void OnIsHotEvent(EntityUid uid, WelderComponent component, IsHotEvent args) - { - args.IsHot = component.WelderLit; - } - - private void OnExamine(EntityUid uid, WelderComponent component, ExaminedEvent args) - { - if (component.WelderLit) - { - args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message")); - } - else - { - args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-not-lit-message")); - } - - if (args.IsInDetailsRange) - { - args.PushMarkup(Loc.GetString("welder-component-on-examine-detailed-message", - ("colorName", component.Fuel < component.FuelCapacity / 4f ? "darkorange" : "orange"), - ("fuelLeft", Math.Round(component.Fuel)), - ("fuelCapacity", component.FuelCapacity))); - } - } - - private void OnSolutionChange(EntityUid uid, WelderComponent component, SolutionChangedEvent args) - { - component.Dirty(); - } - - public bool Subscribe(WelderComponent welder) - { - return _activeWelders.Add(welder); - } - - public bool Unsubscribe(WelderComponent welder) - { - return _activeWelders.Remove(welder); - } - - public override void Update(float frameTime) - { - foreach (var tool in _activeWelders.ToArray()) - { - tool.OnUpdate(frameTime); - } - } - } -} diff --git a/Content.Server/WireHacking/WiresComponent.cs b/Content.Server/WireHacking/WiresComponent.cs index 8b65de151f..9ba5aee098 100644 --- a/Content.Server/WireHacking/WiresComponent.cs +++ b/Content.Server/WireHacking/WiresComponent.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Content.Server.Hands.Components; +using Content.Server.Tools; using Content.Server.Tools.Components; using Content.Server.UserInterface; using Content.Server.VendingMachines; @@ -11,7 +12,8 @@ using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; using Content.Shared.Popups; using Content.Shared.Sound; -using Content.Shared.Tool; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; using Content.Shared.Wires; using JetBrains.Annotations; using Robust.Server.GameObjects; @@ -23,6 +25,7 @@ using Robust.Shared.Localization; using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; @@ -37,6 +40,15 @@ namespace Content.Server.WireHacking private bool _isPanelOpen; + [DataField("screwingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _screwingQuality = "Pulsing"; + + [DataField("cuttingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _cuttingQuality = "Cutting"; + + [DataField("pulsingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] + private string _pulsingQuality = "Pulsing"; + /// /// Opening the maintenance panel (typically with a screwdriver) changes this. /// @@ -418,33 +430,36 @@ namespace Content.Server.WireHacking var activeHandEntity = handsComponent.GetActiveHand?.Owner; ToolComponent? tool = null; activeHandEntity?.TryGetComponent(out tool); + var toolSystem = EntitySystem.Get(); switch (msg.Action) { case WiresAction.Cut: - if (tool == null || !tool.HasQuality(ToolQuality.Cutting)) + if (tool == null || !tool.Qualities.Contains(_cuttingQuality)) { player.PopupMessageCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters")); return; } - tool.PlayUseSound(); + // activeHandEntity cannot be null because tool is not null. + toolSystem.PlayToolSound(activeHandEntity!.Uid, tool); wire.IsCut = true; UpdateUserInterface(); break; case WiresAction.Mend: - if (tool == null || !tool.HasQuality(ToolQuality.Cutting)) + if (tool == null || !tool.Qualities.Contains(_cuttingQuality)) { player.PopupMessageCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters")); return; } - tool.PlayUseSound(); + // activeHandEntity cannot be null because tool is not null. + toolSystem.PlayToolSound(activeHandEntity!.Uid, tool); wire.IsCut = false; UpdateUserInterface(); break; case WiresAction.Pulse: - if (tool == null || !tool.HasQuality(ToolQuality.Multitool)) + if (tool == null || !tool.Qualities.Contains(_pulsingQuality)) { player.PopupMessageCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters")); return; @@ -490,10 +505,12 @@ namespace Content.Server.WireHacking return false; } + var toolSystem = EntitySystem.Get(); + // opens the wires ui if using a tool with cutting or multitool quality on it if (IsPanelOpen && - (tool.HasQuality(ToolQuality.Cutting) || - tool.HasQuality(ToolQuality.Multitool))) + (tool.Qualities.Contains(_cuttingQuality) || + tool.Qualities.Contains(_pulsingQuality))) { if (eventArgs.User.TryGetComponent(out ActorComponent? actor)) { @@ -503,7 +520,8 @@ namespace Content.Server.WireHacking } // screws the panel open if the tool can do so - else if (await tool.UseTool(eventArgs.User, Owner, 0.5f, ToolQuality.Screwing)) + else if (await toolSystem.UseTool(tool.Owner.Uid, eventArgs.User.Uid, Owner.Uid, + 0f, 0.5f, _screwingQuality, toolComponent:tool)) { IsPanelOpen = !IsPanelOpen; if (IsPanelOpen) diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs index d55858f3cd..61d4e821f7 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs @@ -208,7 +208,7 @@ namespace Content.Shared.Chemistry.EntitySystems } public bool TryGetSolution(IEntity? target, string name, - [NotNullWhen(true)] out Solution? solution) + [NotNullWhen(true)] out Solution? solution, SolutionContainerManagerComponent? solutionsMgr = null) { if (target == null || target.Deleted) { @@ -216,13 +216,12 @@ namespace Content.Shared.Chemistry.EntitySystems return false; } - return TryGetSolution(target.Uid, name, out solution); + return TryGetSolution(target.Uid, name, out solution, solutionsMgr); } - public bool TryGetSolution(EntityUid uid, string name, - [NotNullWhen(true)] out Solution? solution) + public bool TryGetSolution(EntityUid uid, string name, [NotNullWhen(true)] out Solution? solution, SolutionContainerManagerComponent? solutionsMgr = null) { - if (!EntityManager.TryGetComponent(uid, out SolutionContainerManagerComponent? solutionsMgr)) + if (!Resolve(uid, ref solutionsMgr)) { solution = null; return false; diff --git a/Content.Shared/Construction/Steps/ToolConstructionGraphStep.cs b/Content.Shared/Construction/Steps/ToolConstructionGraphStep.cs index 2a0d7ba78e..e4396d9a3a 100644 --- a/Content.Shared/Construction/Steps/ToolConstructionGraphStep.cs +++ b/Content.Shared/Construction/Steps/ToolConstructionGraphStep.cs @@ -1,7 +1,12 @@ +using System.Linq; using Content.Shared.Examine; -using Content.Shared.Tool; +using Content.Shared.Tools; +using Content.Shared.Tools.Components; +using Robust.Shared.IoC; using Robust.Shared.Localization; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Utility; namespace Content.Shared.Construction.Steps @@ -9,7 +14,8 @@ namespace Content.Shared.Construction.Steps [DataDefinition] public class ToolConstructionGraphStep : ConstructionGraphStep { - [DataField("tool")] public ToolQuality Tool { get; } = ToolQuality.None; + [DataField("tool", required:true, customTypeSerializer:typeof(PrototypeIdSerializer))] + public string Tool { get; } = string.Empty; [DataField("fuel")] public float Fuel { get; } = 10; @@ -19,11 +25,15 @@ namespace Content.Shared.Construction.Steps { if (!string.IsNullOrEmpty(ExamineOverride)) { - examinedEvent.Message.AddMarkup(Loc.GetString(ExamineOverride)); + examinedEvent.PushMarkup(Loc.GetString(ExamineOverride)); return; } - examinedEvent.Message.AddMarkup(Loc.GetString("construction-use-tool-entity", ("toolName", Tool.GetToolName()))); + if (string.IsNullOrEmpty(Tool) || !IoCManager.Resolve().TryIndex(Tool, out ToolQualityPrototype? quality)) + return; + + examinedEvent.PushMarkup(Loc.GetString("construction-use-tool-entity", ("toolName", Loc.GetString(quality.ToolName)))); + } } } diff --git a/Content.Shared/Tool/SharedToolComponent.cs b/Content.Shared/Tool/SharedToolComponent.cs deleted file mode 100644 index 028516e952..0000000000 --- a/Content.Shared/Tool/SharedToolComponent.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization; - -namespace Content.Shared.Tool -{ - [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 static class ToolQualityHelpers - { - public static string GetToolName(this ToolQuality quality) - { - return quality switch - { - ToolQuality.Anchoring => "Wrench", - ToolQuality.Prying => "Crowbar", - ToolQuality.Screwing => "Screwdriver", - ToolQuality.Cutting => "Wirecutters", - ToolQuality.Welding => "Welding tool", - ToolQuality.Multitool => "Multitool", - _ => throw new ArgumentOutOfRangeException() - }; - } - } - - 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) - { - 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) - { - FuelCapacity = fuelCapacity; - Fuel = fuel; - Activated = activated; - Quality = ToolQuality.Welding; - } - } -} diff --git a/Content.Shared/Tools/Components/SharedMultipleToolComponent.cs b/Content.Shared/Tools/Components/SharedMultipleToolComponent.cs new file mode 100644 index 0000000000..9eab25365a --- /dev/null +++ b/Content.Shared/Tools/Components/SharedMultipleToolComponent.cs @@ -0,0 +1,24 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Tools.Components +{ + [NetworkedComponent] + public class SharedMultipleToolComponent : Component + { + public override string Name => "MultipleTool"; + } + + [NetSerializable, Serializable] + public class MultipleToolComponentState : ComponentState + { + public string QualityName { get; } + + public MultipleToolComponentState(string qualityName) + { + QualityName = qualityName; + } + } +} diff --git a/Content.Shared/Tools/Components/SharedWelderComponent.cs b/Content.Shared/Tools/Components/SharedWelderComponent.cs new file mode 100644 index 0000000000..dd10bfa77f --- /dev/null +++ b/Content.Shared/Tools/Components/SharedWelderComponent.cs @@ -0,0 +1,30 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Tools.Components +{ + [NetworkedComponent] + public abstract class SharedWelderComponent : Component + { + public override string Name => "Welder"; + + public bool Lit { get; set; } + } + + [NetSerializable, Serializable] + public class WelderComponentState : ComponentState + { + public float FuelCapacity { get; } + public float Fuel { get; } + public bool Lit { get; } + + public WelderComponentState(float fuelCapacity, float fuel, bool lit) + { + FuelCapacity = fuelCapacity; + Fuel = fuel; + Lit = lit; + } + } +} diff --git a/Content.Shared/Tools/ToolQualityPrototype.cs b/Content.Shared/Tools/ToolQualityPrototype.cs new file mode 100644 index 0000000000..d54e36ce35 --- /dev/null +++ b/Content.Shared/Tools/ToolQualityPrototype.cs @@ -0,0 +1,41 @@ +using Content.Shared.Sound; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Utility; + +namespace Content.Shared.Tools +{ + [Prototype("tool")] + public class ToolQualityPrototype : IPrototype + { + [DataField("id", required: true)] + public string ID { get; } = default!; + + /// + /// Human-readable name for this tool quality e.g. "Anchoring" + /// + /// This is a localization string ID. + [DataField("name")] + public string Name { get; } = string.Empty; + + /// + /// Human-readable name for a tool of this type e.g. "Wrench" + /// + /// This is a localization string ID. + [DataField("toolName")] + public string ToolName { get; } = string.Empty; + + /// + /// An icon that will be used to represent this tool type. + /// + [DataField("icon")] + public SpriteSpecifier? Icon { get; } = null; + + /// + /// The default entity prototype for this tool type. + /// + [DataField("spawn", required:true, customTypeSerializer:typeof(PrototypeIdSerializer))] + public string Spawn { get; } = string.Empty; + } +} diff --git a/Resources/Locale/en-US/repairable/repairable-component.ftl b/Resources/Locale/en-US/repairable/repairable-component.ftl index 0341364f48..039e4a1016 100644 --- a/Resources/Locale/en-US/repairable/repairable-component.ftl +++ b/Resources/Locale/en-US/repairable/repairable-component.ftl @@ -4,7 +4,7 @@ comp-repairable-repair = You repair {PROPER($target) -> [true] {""} *[false] the{" "} -}{$target} with {PROPER($welder) -> +}{$target} with {PROPER($tool) -> [true] {""} *[false] the{" "} -}{$welder} +}{$tool} diff --git a/Resources/Locale/en-US/tools/components/multiple-tool-component.ftl b/Resources/Locale/en-US/tools/components/multiple-tool-component.ftl new file mode 100644 index 0000000000..da92fc6f5f --- /dev/null +++ b/Resources/Locale/en-US/tools/components/multiple-tool-component.ftl @@ -0,0 +1 @@ +multiple-tool-component-no-behavior = None diff --git a/Resources/Locale/en-US/tools/tool-qualities.ftl b/Resources/Locale/en-US/tools/tool-qualities.ftl new file mode 100644 index 0000000000..0f7e8cdc41 --- /dev/null +++ b/Resources/Locale/en-US/tools/tool-qualities.ftl @@ -0,0 +1,17 @@ +tool-quality-anchoring-name = Anchoring +tool-quality-anchoring-tool-name = Wrench + +tool-quality-prying-name = Prying +tool-quality-prying-tool-name = Wrench + +tool-quality-screwing-name = Screwing +tool-quality-screwing-tool-name = Wrench + +tool-quality-cutting-name = Cutting +tool-quality-cutting-tool-name = Wirecutter + +tool-quality-welding-name = Welding +tool-quality-welding-tool-name = Welder + +tool-quality-pulsing-name = Pulsing +tool-quality-pulsing-tool-name = Multitool diff --git a/Resources/Prototypes/Entities/Debugging/spanisharmyknife.yml b/Resources/Prototypes/Entities/Debugging/spanisharmyknife.yml index f06620fe12..4f17188b20 100644 --- a/Resources/Prototypes/Entities/Debugging/spanisharmyknife.yml +++ b/Resources/Prototypes/Entities/Debugging/spanisharmyknife.yml @@ -24,30 +24,18 @@ - type: Tool qualities: - Prying + - type: MultipleTool statusShowBehavior: true - - type: MultiTool - tools: + entries: - behavior: Prying - state: icon - useSound: - path: /Audio/Items/jaws_pry.ogg - changeSound: - path: /Audio/Items/change_jaws.ogg + useSound: /Audio/Items/jaws_pry.ogg + changeSound: /Audio/Items/change_jaws.ogg - behavior: Cutting - state: icon - useSound: - path: /Audio/Items/jaws_cut.ogg - changeSound: - path: /Audio/Items/change_jaws.ogg + useSound: /Audio/Items/jaws_cut.ogg + changeSound: /Audio/Items/change_jaws.ogg - behavior: Screwing - state: icon - useSound: - path: /Audio/Items/drill_use.ogg - changeSound: - path: /Audio/Items/change_drill.ogg + useSound: /Audio/Items/drill_use.ogg + changeSound: /Audio/Items/change_drill.ogg - behavior: Anchoring - state: icon - useSound: - path: /Audio/Items/drill_use.ogg - changeSound: - path: /Audio/Items/change_drill.ogg + useSound: /Audio/Items/drill_use.ogg + changeSound: /Audio/Items/change_drill.ogg diff --git a/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml b/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml index 6e566fed11..f993606af0 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml @@ -106,7 +106,7 @@ sprite: Objects/Tools/Cowtools/mooltitool.rsi - type: Tool qualities: - - Multitool + - Pulsing - type: SignalLinker - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml b/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml index a9f3d16df9..1f9f9a9854 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml @@ -20,21 +20,21 @@ - type: Tool qualities: - Prying + - type: MultipleTool statusShowBehavior: true - - type: MultiTool - tools: + entries: - behavior: Prying - state: jaws_pry - useSound: - path: /Audio/Items/jaws_pry.ogg - changeSound: - path: /Audio/Items/change_jaws.ogg + sprite: + sprite: Objects/Tools/jaws_of_life.rsi + state: jaws_pry + useSound: /Audio/Items/jaws_pry.ogg + changeSound: /Audio/Items/change_jaws.ogg - behavior: Cutting - state: jaws_cutter - useSound: - path: /Audio/Items/jaws_cut.ogg - changeSound: - path: /Audio/Items/change_jaws.ogg + sprite: + sprite: Objects/Tools/jaws_of_life.rsi + state: jaws_cutter + useSound: /Audio/Items/jaws_cut.ogg + changeSound: /Audio/Items/change_jaws.ogg - type: entity name: syndicate jaws of life @@ -43,10 +43,19 @@ description: Useful for entering the station or its departments. components: - type: Sprite + sprite: Objects/Tools/jaws_of_life.rsi state: syn_jaws_pry - - type: MultiTool - tools: + - type: MultipleTool + entries: - behavior: Prying - state: syn_jaws_pry + sprite: + sprite: Objects/Tools/jaws_of_life.rsi + state: syn_jaws_pry + useSound: /Audio/Items/jaws_pry.ogg + changeSound: /Audio/Items/change_jaws.ogg - behavior: Cutting - state: syn_jaws_cutter + sprite: + sprite: Objects/Tools/jaws_of_life.rsi + state: syn_jaws_cutter + useSound: /Audio/Items/jaws_cut.ogg + changeSound: /Audio/Items/change_jaws.ogg diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index f9ba16a04d..4638e9b2dd 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -177,7 +177,7 @@ - Belt - type: Tool qualities: - - Multitool + - Pulsing - type: SignalLinker - type: entity @@ -198,17 +198,21 @@ - type: Tool qualities: - Screwing + - type: MultipleTool statusShowBehavior: true - - type: MultiTool - tools: + entries: - behavior: Screwing - state: drill_screw + sprite: + sprite: Objects/Tools/drill.rsi + state: drill_screw useSound: path: /Audio/Items/drill_use.ogg changeSound: path: /Audio/Items/change_drill.ogg - behavior: Anchoring - state: drill_bolt + sprite: + sprite: Objects/Tools/drill.rsi + state: drill_bolt useSound: path: /Audio/Items/drill_use.ogg changeSound: diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index 9d9230b038..0967639ce6 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -25,14 +25,18 @@ Blunt: 10 - type: ItemStatus - type: RefillableSolution - solution: welder + solution: Welder - type: SolutionContainerManager solutions: - welder: + Welder: reagents: - ReagentId: WeldingFuel Quantity: 100 maxVol: 100 + - type: Tool + useSound: + collection: Welder + qualities: Welding - type: Welder - type: PointLight enabled: false @@ -53,7 +57,7 @@ solution: welder - type: SolutionContainerManager solutions: - welder: + Welder: reagents: - ReagentId: WeldingFuel Quantity: 1000 @@ -74,10 +78,10 @@ - type: Item sprite: Objects/Tools/welder_mini.rsi - type: RefillableSolution - solution: welder + solution: Welder - type: SolutionContainerManager solutions: - welder: + Welder: reagents: - ReagentId: WeldingFuel Quantity: 25 diff --git a/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml b/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml index 7a53dec57f..68dd023345 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml @@ -14,7 +14,7 @@ tankType: Fuel - type: DamageOnToolInteract tools: - - 16 # welding + - Welding weldingDamage: types: Heat: 10 diff --git a/Resources/Prototypes/tool_qualities.yml b/Resources/Prototypes/tool_qualities.yml new file mode 100644 index 0000000000..123c07fbbb --- /dev/null +++ b/Resources/Prototypes/tool_qualities.yml @@ -0,0 +1,41 @@ +- type: tool + id: Anchoring + name: tool-quality-anchoring-name + toolName: tool-quality-anchoring-tool-name + spawn: Wrench + icon: Objects/Tools/wrench.rsi/icon.png + +- type: tool + id: Prying + name: tool-quality-prying-name + toolName: tool-quality-prying-tool-name + spawn: Crowbar + icon: Objects/Tools/crowbar.rsi/icon.png + +- type: tool + id: Screwing + name: tool-quality-screwing-name + toolName: tool-quality-screwing-tool-name + spawn: Screwdriver + icon: Objects/Tools/screwdriver.rsi/screwdriver-map.png + +- type: tool + id: Cutting + name: tool-quality-cutting-name + toolName: tool-quality-cutting-tool-name + spawn: Wirecutter + icon: Objects/Tools/wirecutters.rsi/cutters-map.png + +- type: tool + id: Welding + name: tool-quality-welding-name + toolName: tool-quality-welding-tool-name + spawn: Welder + icon: Objects/Tools/welder.rsi/icon.png + +- type: tool + id: Pulsing + name: tool-quality-pulsing-name + toolName: tool-quality-pulsing-tool-name + spawn: Multitool + icon: Objects/Tools/multitool.rsi/icon.png