]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
ItemToggle system expansion (#22369)
authorDarkie <darksaiyanis@gmail.com>
Sun, 24 Dec 2023 06:11:05 +0000 (08:11 +0200)
committerGitHub <noreply@github.com>
Sun, 24 Dec 2023 06:11:05 +0000 (17:11 +1100)
* Fixed EnergySword and variants having incorrect sound on attacking when in their Off state.

* Removed the unused ItemToggle from the serverside and created a new shared ItemToggleComponent and System, now used for the e-blade family of items. Also added e-blade hum and swing sounds. Thanks Sloth for the initial code!

* Changing Stunbaton system to include the itemToggle system.

* Adapted changes that have come up in the meantime.

* Changed damagespecifier to be serializable and autoNetworked in melee weapon components. Fixes a bug that makes it so client-side, damage values are not updated on toggle.

* Made the ItemToggleSystem have both a shared and a server component. Ported the Stun Baton and Stun Prod to the new toggleable system. Added a failure to activate noise component.

* Ported the welders to the new item toggle system. Set it so deactivated damage and item size default to the item's regular options.

* Removed unnecessary usings.

* Small modification to the stun prod.

* Made the integration test use the new method to turn the welders on.

* Fixed a few testing issues, applied a few changes requested by Delta.

* Updated Stunbaton code for consistentcy when it comes to calling the itemToggle component.

* Removed a redundant return; as per Delta.
Made examining the stun baton for charge rely on the battery component instead.

* Removed the welder visualizer system, now using the generic one. Removed some unused usings. Removed the welder visuals and layers.
Ported lighters to the new system.
Added zippi (sic) lighters.

* Renamed variables used to make them less generic.

* Simplified the light update code.

* Fixed the unit test to use the itemToggle system for welders now.

* Made the name shorter. I can't tell if the welding damage when interacted with actually does anything though. I can't figure out how to trigger it.

* Fixed some YML issues.

* Added a client side item toggle system just to make the shared code run on local UID's too.

* Fixed some more Yaml.

* Made the Zippi lighter have its own parent item, so it doesnt' conflict with the random pattern on the regular lighter.

* Made the zippi lighter its own in-hand sprites.

* Added a summary for the activated property in itemtoggle component.

* Fixed a typo in the itemToggle Component.

* Fixed a typo.

* Added to the remarks for the ItemToggleComponent.

* Fixed up the lighter yaml to make it use a generic term instead of a toggle layer enum for the random skin.

* Fixed a bug I introduced accidentally with the humming sound.

* Removed 2 unnecessary events from the ItemToggleSystem and component.

* Fixed a bug by only making the server run the item activation code, since the client cannot predict whether or not the activation will be cancelled.

* Cleaned up some names and functions getting called.

* Renamed a couple of variables and removed the explicit datafields from the component. Removed "activated: false" from yml since they're already deactivated by default.

* Added an IsActivated function, used it in the welder and stun baton systems code.
Refactored welder code to remove the WelderToggle event, now using the ItemToggleActivatedEvent instead for eye protection check.

* Fixed a typo. Added some comments.

* Split the ItemToggle into smaller components.
Changed the items that used the toggle system to work with the smaller components.
Made the mirror shield reflect energy shots with a 95% chance.

* Fixed the namespaces for the server components and whatnot.

* Fixed a doubled deactivation sound from using activated wieldable items (like the double Esword).
Fixed wrong yml with the e-dagger.
Fixed the disarm malus code.

* Added the zippo lighter to the detective's trench coat.

* Removed the default hit sound for the double e-sword since it was unnecessary.

* Changed e-sword damage numbers to be in line with the changes made by Emisse.

* Made no damage sounds be autoNetworked, so it changes can be changed on activation/deactivation of items.
Made Welders and Eswords sound like themselves but quieter if they hit for 0 damage, instead of taps.
You can choose what sound to play when a weapon does 0 damage when activated now.
Fixed a bug with swing sounds.

* Typo.

* Fixed a bug where the welder would blind you if you used it while it was off.

* Created a single abstract method called when an item has completed its toggle.

* Update Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
* Fixed a comment.

* Made most component variables readOnly for ItemToggle. There is no need to be able to change them from within the variable viewer.

* Removed trailing white spaces.

* Made the Use a field instead of a property in the itemToggleActivation/Deactivation attempt events.

* Small fixes.

* Removed ForceToggle, just use the toggle method instead.

* Fixed a bug with item sharpness staying even after getting deactivated, if the item gained sharpness that way (esword).

* Used ProtoId in the welder component.

* Made damage NetSerializable as well.

* Added networking and data fields to a couple of components.

* Made component variables autonetworked. Added some comments.

* Moved the events that modify item components on toggle to events, handled (where possible) in the systems linked to said components.

* Made all the component variables readWrite again.

* Added the component get to the WelderStatus.

* Added a predictable bool to the item toggle component.

* Replaced the Activated/Deactivated events with ToggleDone, with an Activated argument. Used that to simplify some systems.

* Added a reflect update raise event.

* Removed the Zippo changes. To add in a later PR.

* Removed the zippo from meta.json too.

* Small fix.

* Another small fix.

* Fixed the wieldable system thing in ItemToggle.

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
47 files changed:
Content.Client/Items/Systems/ItemToggleSystem.cs [new file with mode: 0644]
Content.Client/Tools/ToolSystem.cs
Content.Client/Tools/UI/WelderStatusControl.cs
Content.Client/Tools/Visualizers/WelderVisualizerSystem.cs [deleted file]
Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs
Content.IntegrationTests/Tests/Interaction/InteractionTest.cs
Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs
Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs
Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs [new file with mode: 0644]
Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs [new file with mode: 0644]
Content.Server/Item/ItemToggle/ItemToggleSystem.cs [new file with mode: 0644]
Content.Server/Item/ItemToggleSystem.cs [deleted file]
Content.Server/Stunnable/Systems/StunbatonSystem.cs
Content.Server/Tools/Components/WelderComponent.cs
Content.Server/Tools/ToolSystem.Welder.cs
Content.Server/Tools/ToolSystem.cs
Content.Server/Weapons/Melee/EnergySword/EnergySwordComponent.cs
Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs
Content.Server/Weapons/Reflect/ReflectSystem.cs
Content.Shared/Damage/DamageSpecifier.cs
Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs [new file with mode: 0644]
Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs [new file with mode: 0644]
Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs [new file with mode: 0644]
Content.Shared/Item/ItemToggle/Components/ItemToggleMeleeWeaponComponent.cs [new file with mode: 0644]
Content.Shared/Item/ItemToggle/Components/ItemToggleSizeComponent.cs [new file with mode: 0644]
Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs [new file with mode: 0644]
Content.Shared/Item/ItemToggleComponent.cs [deleted file]
Content.Shared/Item/SharedItemSystem.cs
Content.Shared/Stunnable/SharedStunbatonSystem.cs
Content.Shared/Stunnable/StunbatonComponent.cs
Content.Shared/Tools/Components/SharedWelderComponent.cs
Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs
Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
Resources/Audio/Weapons/attributions.yml
Resources/Audio/Weapons/ebladehum.ogg [new file with mode: 0644]
Resources/Audio/Weapons/eblademiss.ogg [new file with mode: 0644]
Resources/Prototypes/Entities/Objects/Shields/shields.yml
Resources/Prototypes/Entities/Objects/Tools/lighters.yml
Resources/Prototypes/Entities/Objects/Tools/welders.yml
Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml
Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml
Resources/Prototypes/Entities/Objects/Weapons/security.yml
Resources/Textures/Objects/Tools/lighters.rsi/inhand-left-flame.png [moved from Resources/Textures/Objects/Tools/lighters.rsi/on-inhand-left.png with 100% similarity]
Resources/Textures/Objects/Tools/lighters.rsi/inhand-left.png [moved from Resources/Textures/Objects/Tools/lighters.rsi/off-inhand-left.png with 100% similarity]
Resources/Textures/Objects/Tools/lighters.rsi/inhand-right-flame.png [moved from Resources/Textures/Objects/Tools/lighters.rsi/on-inhand-right.png with 100% similarity]
Resources/Textures/Objects/Tools/lighters.rsi/inhand-right.png [moved from Resources/Textures/Objects/Tools/lighters.rsi/off-inhand-right.png with 100% similarity]
Resources/Textures/Objects/Tools/lighters.rsi/meta.json

diff --git a/Content.Client/Items/Systems/ItemToggleSystem.cs b/Content.Client/Items/Systems/ItemToggleSystem.cs
new file mode 100644 (file)
index 0000000..46d6f1b
--- /dev/null
@@ -0,0 +1,9 @@
+using Content.Shared.Item.ItemToggle;
+
+namespace Content.Shared.Item;
+
+/// <inheritdoc/>
+public sealed class ItemToggleSystem : SharedItemToggleSystem
+{
+
+}
index 966f37146e84691f5427a0cba7155716fe1a3b8b..a305fd5bb21c063a6b25b6bdd462fecb1a9b66ee 100644 (file)
@@ -1,7 +1,7 @@
 using Content.Client.Items;
 using Content.Client.Tools.Components;
 using Content.Client.Tools.UI;
-using Content.Shared.Tools;
+using Content.Shared.Item;
 using Content.Shared.Tools.Components;
 using Robust.Client.GameObjects;
 using Robust.Shared.GameStates;
@@ -50,7 +50,7 @@ namespace Content.Client.Tools
 
         private void OnWelderGetStatusMessage(EntityUid uid, WelderComponent component, ItemStatusCollectMessage args)
         {
-            args.Controls.Add(new WelderStatusControl(component));
+            args.Controls.Add(new WelderStatusControl(component, uid));
         }
 
         private void OnWelderHandleState(EntityUid uid, WelderComponent welder, ref ComponentHandleState args)
@@ -60,7 +60,6 @@ namespace Content.Client.Tools
 
             welder.FuelCapacity = state.FuelCapacity;
             welder.Fuel = state.Fuel;
-            welder.Lit = state.Lit;
             welder.UiUpdateNeeded = true;
         }
 
index c8d4df2b8f054a7802a4a4b82dbf717b11b811fb..8da7a1448be4852083e6afb5030bd22b5b9f2ab5 100644 (file)
@@ -1,6 +1,7 @@
 using Content.Client.Message;
 using Content.Client.Stylesheets;
 using Content.Client.Tools.Components;
+using Content.Shared.Item;
 using Robust.Client.UserInterface;
 using Robust.Client.UserInterface.Controls;
 using Robust.Shared.Timing;
@@ -9,13 +10,19 @@ namespace Content.Client.Tools.UI;
 
 public sealed class WelderStatusControl : Control
 {
+    [Dependency] private readonly IEntityManager _entMan = default!;
+
     private readonly WelderComponent _parent;
+    private readonly ItemToggleComponent? _toggleComponent;
     private readonly RichTextLabel _label;
 
-    public WelderStatusControl(WelderComponent parent)
+    public WelderStatusControl(WelderComponent parent, EntityUid? uid = null)
     {
         _parent = parent;
-        _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}};
+        _entMan = IoCManager.Resolve<IEntityManager>();
+        if (_entMan.TryGetComponent<ItemToggleComponent>(uid, out var itemToggle))
+            _toggleComponent = itemToggle;
+        _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
         AddChild(_label);
 
         UpdateDraw();
@@ -39,7 +46,11 @@ public sealed class WelderStatusControl : Control
 
         var fuelCap = _parent.FuelCapacity;
         var fuel = _parent.Fuel;
-        var lit = _parent.Lit;
+        var lit = false;
+        if (_toggleComponent != null)
+        {
+            lit = _toggleComponent.Activated;
+        }
 
         _label.SetMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
             ("colorName", fuel < fuelCap / 4f ? "darkorange" : "orange"),
diff --git a/Content.Client/Tools/Visualizers/WelderVisualizerSystem.cs b/Content.Client/Tools/Visualizers/WelderVisualizerSystem.cs
deleted file mode 100644 (file)
index 02df7d9..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-using Content.Client.Tools.Components;
-using Content.Shared.Tools.Components;
-using Robust.Client.GameObjects;
-
-namespace Content.Client.Tools.Visualizers;
-
-public sealed class WelderVisualizerSystem : VisualizerSystem<WelderComponent>
-{
-    protected override void OnAppearanceChange(EntityUid uid, WelderComponent component, ref AppearanceChangeEvent args)
-    {
-        if (args.Sprite == null)
-            return;
-
-        if (AppearanceSystem.TryGetData<bool>(uid, WelderVisuals.Lit, out var isLit, args.Component))
-        {
-            args.Sprite.LayerSetVisible(WelderLayers.Flame, isLit);
-        }
-    }
-}
index 6c842f00827c8b6a2e155ed02f6db50ef87afcc0..695de4461dbf49b05e936675fee6d595a0c4ae9a 100644 (file)
@@ -10,8 +10,8 @@ using Content.Server.Atmos.Components;
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Construction.Components;
 using Content.Server.Gravity;
+using Content.Server.Item;
 using Content.Server.Power.Components;
-using Content.Server.Tools.Components;
 using Content.Shared.Atmos;
 using Content.Shared.Construction.Prototypes;
 using Content.Shared.Gravity;
@@ -164,7 +164,7 @@ public abstract partial class InteractionTest
 
         // spawn and pick up the new item
         var item = await SpawnEntity(entity, SEntMan.GetCoordinates(PlayerCoords));
-        WelderComponent? welder = null;
+        ItemToggleComponent? itemToggle = null;
 
         await Server.WaitPost(() =>
         {
@@ -173,14 +173,16 @@ public abstract partial class InteractionTest
             Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHand, false, false, Hands));
 
             // turn on welders
-            if (enableWelder && SEntMan.TryGetComponent(item, out welder) && !welder.Lit)
-                Assert.That(ToolSys.TryTurnWelderOn(item, playerEnt, welder));
+            if (enableWelder && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated)
+            {
+                Assert.That(ItemToggleSys.TryActivate(item, playerEnt, itemToggle: itemToggle));
+            }
         });
 
         await RunTicks(1);
         Assert.That(Hands.ActiveHandEntity, Is.EqualTo(item));
-        if (enableWelder && welder != null)
-            Assert.That(welder.Lit);
+        if (enableWelder && itemToggle != null)
+            Assert.That(itemToggle.Activated);
 
         return item;
     }
index 58076454c1a1b9e8230658b125691d41df2b1885..f54d772881bf47b76ef82a97b6f40427dfca1581 100644 (file)
@@ -12,6 +12,7 @@ using Content.Shared.DoAfter;
 using Content.Shared.Hands.Components;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.Interaction;
+using Content.Server.Item;
 using Content.Shared.Mind;
 using Content.Shared.Players;
 using Robust.Client.Input;
@@ -23,6 +24,7 @@ using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Timing;
 using Robust.UnitTesting;
+using Content.Shared.Item.ItemToggle;
 
 namespace Content.IntegrationTests.Tests.Interaction;
 
@@ -97,6 +99,7 @@ public abstract partial class InteractionTest
     protected Content.Server.Construction.ConstructionSystem SConstruction = default!;
     protected SharedDoAfterSystem DoAfterSys = default!;
     protected ToolSystem ToolSys = default!;
+    protected SharedItemToggleSystem ItemToggleSys = default!;
     protected InteractionTestSystem STestSystem = default!;
     protected SharedTransformSystem Transform = default!;
     protected ISawmill SLogger = default!;
@@ -152,6 +155,7 @@ public abstract partial class InteractionTest
         HandSys = SEntMan.System<SharedHandsSystem>();
         InteractSys = SEntMan.System<SharedInteractionSystem>();
         ToolSys = SEntMan.System<ToolSystem>();
+        ItemToggleSys = SEntMan.System<SharedItemToggleSystem>();
         DoAfterSys = SEntMan.System<SharedDoAfterSystem>();
         Transform = SEntMan.System<SharedTransformSystem>();
         SConstruction = SEntMan.System<Server.Construction.ConstructionSystem>();
index 2abd6fdf86a5c23321300974de67b456156098ea..c12c71a850f7b1dc4b9f41810049c8be95a938c1 100644 (file)
@@ -1,6 +1,7 @@
 using Content.Server.Administration.Logs;
 using Content.Server.Damage.Components;
 using Content.Server.Tools.Components;
+using Content.Shared.Item;
 using Content.Shared.Damage;
 using Content.Shared.Database;
 using Content.Shared.Interaction;
@@ -11,7 +12,7 @@ namespace Content.Server.Damage.Systems
     public sealed class DamageOnToolInteractSystem : EntitySystem
     {
         [Dependency] private readonly DamageableSystem _damageableSystem = default!;
-        [Dependency] private readonly IAdminLogManager _adminLogger= default!;
+        [Dependency] private readonly IAdminLogManager _adminLogger = default!;
 
         public override void Initialize()
         {
@@ -25,16 +26,19 @@ namespace Content.Server.Damage.Systems
             if (args.Handled)
                 return;
 
+            if (!TryComp<ItemToggleComponent>(uid, out var itemToggle))
+                return;
+
             if (component.WeldingDamage is {} weldingDamage
-                && EntityManager.TryGetComponent(args.Used, out WelderComponent? welder)
-                && welder.Lit
-                && !welder.TankSafe)
+            && EntityManager.TryGetComponent(args.Used, out WelderComponent? welder)
+            && itemToggle.Activated
+            && !welder.TankSafe)
             {
                 var dmg = _damageableSystem.TryChangeDamage(args.Target, weldingDamage, origin: args.User);
 
                 if (dmg != null)
                     _adminLogger.Add(LogType.Damaged,
-                        $"{ToPrettyString(args.User):user} used {ToPrettyString(args.Used):used} as a welder to deal {dmg.Total:damage} damage to {ToPrettyString(args.Target):target}");
+                        $"{ToPrettyString(args.User):user} used {ToPrettyString(args.Used):used} as a welder to deal {dmg.GetTotal():damage} damage to {ToPrettyString(args.Target):target}");
 
                 args.Handled = true;
             }
@@ -46,7 +50,7 @@ namespace Content.Server.Damage.Systems
 
                 if (dmg != null)
                     _adminLogger.Add(LogType.Damaged,
-                        $"{ToPrettyString(args.User):user} used {ToPrettyString(args.Used):used} as a tool to deal {dmg.Total:damage} damage to {ToPrettyString(args.Target):target}");
+                        $"{ToPrettyString(args.User):user} used {ToPrettyString(args.Used):used} as a tool to deal {dmg.GetTotal():damage} damage to {ToPrettyString(args.Target):target}");
 
                 args.Handled = true;
             }
index d31f9eeed64cb638f7eb582cae74b886dd2e2e61..1b6a5bb9308eccb28222d7a03e2739e7e847ce75 100644 (file)
@@ -1,10 +1,10 @@
-using Content.Shared.Eye.Blinding;
 using Content.Shared.StatusEffect;
 using Content.Shared.Inventory;
-using Content.Server.Tools;
+using Content.Shared.Item;
 using Content.Shared.Eye.Blinding.Components;
 using Content.Shared.Eye.Blinding.Systems;
 using Content.Shared.Tools.Components;
+using Content.Shared.Item.ItemToggle;
 
 namespace Content.Server.Eye.Blinding.EyeProtection
 {
@@ -12,11 +12,13 @@ namespace Content.Server.Eye.Blinding.EyeProtection
     {
         [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
         [Dependency] private readonly BlindableSystem _blindingSystem = default!;
+        [Dependency] private readonly SharedItemToggleSystem _itemToggle = default!;
+        
         public override void Initialize()
         {
             base.Initialize();
             SubscribeLocalEvent<RequiresEyeProtectionComponent, ToolUseAttemptEvent>(OnUseAttempt);
-            SubscribeLocalEvent<RequiresEyeProtectionComponent, WelderToggledEvent>(OnWelderToggled);
+            SubscribeLocalEvent<RequiresEyeProtectionComponent, ItemToggleDoneEvent>(OnWelderToggled);
 
             SubscribeLocalEvent<EyeProtectionComponent, GetEyeProtectionEvent>(OnGetProtection);
             SubscribeLocalEvent<EyeProtectionComponent, InventoryRelayedEvent<GetEyeProtectionEvent>>(OnGetRelayedProtection);
@@ -44,7 +46,7 @@ namespace Content.Server.Eye.Blinding.EyeProtection
             var ev = new GetEyeProtectionEvent();
             RaiseLocalEvent(args.User, ev);
 
-            var time = (float) (component.StatusEffectTime- ev.Protection).TotalSeconds;
+            var time = (float) (component.StatusEffectTime - ev.Protection).TotalSeconds;
             if (time <= 0)
                 return;
 
@@ -55,9 +57,9 @@ namespace Content.Server.Eye.Blinding.EyeProtection
             _statusEffectsSystem.TryAddStatusEffect(args.User, TemporaryBlindnessSystem.BlindingStatusEffect,
                 statusTimeSpan, false, TemporaryBlindnessSystem.BlindingStatusEffect);
         }
-        private void OnWelderToggled(EntityUid uid, RequiresEyeProtectionComponent component, WelderToggledEvent args)
+        private void OnWelderToggled(EntityUid uid, RequiresEyeProtectionComponent component, ItemToggleDoneEvent args)
         {
-            component.Toggled = args.WelderOn;
+            component.Toggled = _itemToggle.IsActivated(uid);
         }
     }
 }
diff --git a/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs b/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs
new file mode 100644 (file)
index 0000000..923a10b
--- /dev/null
@@ -0,0 +1,20 @@
+namespace Content.Server.Item;
+
+/// <summary>
+/// Handles whether this item applies a disarm malus when active. 
+/// </summary>
+[RegisterComponent]
+public sealed partial class ItemToggleDisarmMalusComponent : Component
+{
+    /// <summary>
+    ///     Item has this modifier to the chance to disarm when activated.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadOnly), DataField]
+    public float? ActivatedDisarmMalus = null;
+
+    /// <summary>
+    ///     Item has this modifier to the chance to disarm when deactivated. If none is mentioned, it uses the item's default disarm modifier.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadOnly), DataField]
+    public float? DeactivatedDisarmMalus = null;
+}
diff --git a/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs b/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs
new file mode 100644 (file)
index 0000000..ea2efae
--- /dev/null
@@ -0,0 +1,14 @@
+namespace Content.Server.Item;
+
+/// <summary>
+/// Handles whether this item is sharp when toggled on. 
+/// </summary>
+[RegisterComponent]
+public sealed partial class ItemToggleSharpComponent : Component
+{
+    /// <summary>
+    ///     Item can be used to butcher when activated.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadOnly), DataField]
+    public bool ActivatedSharp = true;
+}
diff --git a/Content.Server/Item/ItemToggle/ItemToggleSystem.cs b/Content.Server/Item/ItemToggle/ItemToggleSystem.cs
new file mode 100644 (file)
index 0000000..8a6903d
--- /dev/null
@@ -0,0 +1,57 @@
+using Content.Shared.Item;
+using Content.Server.CombatMode.Disarm;
+using Content.Server.Kitchen.Components;
+using Content.Shared.Item.ItemToggle;
+
+namespace Content.Server.Item;
+
+public sealed class ItemToggleSystem : SharedItemToggleSystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ItemToggleComponent, ItemToggleDoneEvent>(Toggle);
+    }
+
+    private void Toggle(EntityUid uid, ItemToggleComponent comp, ref ItemToggleDoneEvent args)
+    {
+        if (args.Activated == true)
+        {
+            if (TryComp<ItemToggleSharpComponent>(uid, out var itemSharpness))
+            {
+                if (itemSharpness.ActivatedSharp)
+                    EnsureComp<SharpComponent>(uid);
+            }
+
+            if (!TryComp<ItemToggleDisarmMalusComponent>(uid, out var itemToggleDisarmMalus) ||
+                !TryComp<DisarmMalusComponent>(uid, out var malus))
+                return;
+
+            //Default the deactivated DisarmMalus to the item's value before activation happens.
+            itemToggleDisarmMalus.DeactivatedDisarmMalus ??= malus.Malus;
+
+            if (itemToggleDisarmMalus.ActivatedDisarmMalus != null)
+            {
+                malus.Malus = (float) itemToggleDisarmMalus.ActivatedDisarmMalus;
+            }
+        }
+        else
+        {
+            if (TryComp<ItemToggleSharpComponent>(uid, out var itemSharpness))
+            {
+                if (itemSharpness.ActivatedSharp)
+                    RemCompDeferred<SharpComponent>(uid);
+            }
+
+            if (!TryComp<ItemToggleDisarmMalusComponent>(uid, out var itemToggleDisarmMalus) ||
+                !TryComp<DisarmMalusComponent>(uid, out var malus))
+                return;
+
+            if (itemToggleDisarmMalus.DeactivatedDisarmMalus != null)
+            {
+                malus.Malus = (float) itemToggleDisarmMalus.DeactivatedDisarmMalus;
+            }
+        }
+    }
+}
diff --git a/Content.Server/Item/ItemToggleSystem.cs b/Content.Server/Item/ItemToggleSystem.cs
deleted file mode 100644 (file)
index fb72148..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-using Content.Server.CombatMode.Disarm;
-using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
-using Content.Shared.Item;
-using Content.Shared.Toggleable;
-using Content.Shared.Tools.Components;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Player;
-
-namespace Content.Server.Weapons.Melee.ItemToggle;
-
-public sealed class ItemToggleSystem : EntitySystem
-{
-    [Dependency] private readonly SharedItemSystem _item = default!;
-    [Dependency] private readonly SharedAudioSystem _audio = default!;
-    [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
-
-    public override void Initialize()
-    {
-        base.Initialize();
-
-        SubscribeLocalEvent<ItemToggleComponent, UseInHandEvent>(OnUseInHand);
-        SubscribeLocalEvent<ItemToggleComponent, InteractUsingEvent>(OnInteractUsing);
-        SubscribeLocalEvent<ItemToggleComponent, ItemToggleDeactivatedEvent>(TurnOff);
-        SubscribeLocalEvent<ItemToggleComponent, ItemToggleActivatedEvent>(TurnOn);
-    }
-
-    private void OnUseInHand(EntityUid uid, ItemToggleComponent comp, UseInHandEvent args)
-    {
-        if (args.Handled)
-            return;
-
-        args.Handled = true;
-
-        if (comp.Activated)
-        {
-            var ev = new ItemToggleDeactivatedEvent();
-            RaiseLocalEvent(uid, ref ev);
-        }
-        else
-        {
-            var ev = new ItemToggleActivatedEvent();
-            RaiseLocalEvent(uid, ref ev);
-        }
-
-        UpdateAppearance(uid, comp);
-    }
-
-    private void TurnOff(EntityUid uid, ItemToggleComponent comp, ref ItemToggleDeactivatedEvent args)
-    {
-        if (TryComp(uid, out ItemComponent? item))
-            _item.SetSize(uid, comp.OffSize, item);
-
-        if (TryComp<DisarmMalusComponent>(uid, out var malus))
-            malus.Malus -= comp.ActivatedDisarmMalus;
-
-        _audio.PlayEntity(comp.DeActivateSound, Filter.Pvs(uid, entityManager: EntityManager), uid, true, comp.DeActivateSound.Params);
-
-        comp.Activated = false;
-    }
-
-    private void TurnOn(EntityUid uid, ItemToggleComponent comp, ref ItemToggleActivatedEvent args)
-    {
-        if (TryComp(uid, out ItemComponent? item))
-            _item.SetSize(uid, comp.OnSize, item);
-
-        if (TryComp<DisarmMalusComponent>(uid, out var malus))
-            malus.Malus += comp.ActivatedDisarmMalus;
-
-        _audio.PlayEntity(comp.ActivateSound, Filter.Pvs(uid, entityManager: EntityManager), uid, true, comp.ActivateSound.Params);
-
-        comp.Activated = true;
-    }
-
-    private void UpdateAppearance(EntityUid uid, ItemToggleComponent component)
-    {
-        if (!TryComp(uid, out AppearanceComponent? appearanceComponent))
-            return;
-
-        _appearance.SetData(uid, ToggleableLightVisuals.Enabled, component.Activated, appearanceComponent);
-    }
-
-    private void OnInteractUsing(EntityUid uid, ItemToggleComponent comp, InteractUsingEvent args)
-    {
-        if (args.Handled)
-            return;
-
-        if (!TryComp(args.Used, out ToolComponent? tool) || !tool.Qualities.ContainsAny("Pulsing"))
-            return;
-
-        args.Handled = true;
-    }
-}
index da2391a86baadef7585e4ea1a829e69763aee8e3..2ed30d8942882463acc6585419db982759b9f15b 100644 (file)
@@ -2,45 +2,39 @@ using Content.Server.Power.Components;
 using Content.Server.Power.EntitySystems;
 using Content.Server.Power.Events;
 using Content.Server.Stunnable.Components;
-using Content.Shared.Audio;
 using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Damage.Events;
 using Content.Shared.Examine;
-using Content.Shared.Interaction.Events;
 using Content.Shared.Item;
+using Content.Shared.Item.ItemToggle;
 using Content.Shared.Popups;
 using Content.Shared.Stunnable;
-using Content.Shared.Toggleable;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Player;
 
 namespace Content.Server.Stunnable.Systems
 {
     public sealed class StunbatonSystem : SharedStunbatonSystem
     {
         [Dependency] private readonly SharedItemSystem _item = default!;
-        [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
         [Dependency] private readonly RiggableSystem _riggableSystem = default!;
         [Dependency] private readonly SharedPopupSystem _popup = default!;
         [Dependency] private readonly BatterySystem _battery = default!;
-        [Dependency] private readonly SharedAudioSystem _audio = default!;
+        [Dependency] private readonly SharedItemToggleSystem _itemToggle = default!;
 
         public override void Initialize()
         {
             base.Initialize();
 
-            SubscribeLocalEvent<StunbatonComponent, UseInHandEvent>(OnUseInHand);
-            SubscribeLocalEvent<StunbatonComponent, ExaminedEvent>(OnExamined);
+            SubscribeLocalEvent<BatteryComponent, ExaminedEvent>(OnExamined);
             SubscribeLocalEvent<StunbatonComponent, SolutionChangedEvent>(OnSolutionChange);
             SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt);
+            SubscribeLocalEvent<StunbatonComponent, ItemToggleActivateAttemptEvent>(TryTurnOn);
+            SubscribeLocalEvent<StunbatonComponent, ItemToggleDoneEvent>(ToggleDone);
         }
 
         private void OnStaminaHitAttempt(EntityUid uid, StunbatonComponent component, ref StaminaDamageOnHitAttemptEvent args)
         {
-            if (!component.Activated ||
-                !TryComp<BatteryComponent>(uid, out var battery) || !_battery.TryUseCharge(uid, component.EnergyPerUse, battery))
+            if (!_itemToggle.IsActivated(uid) ||
+            !TryComp<BatteryComponent>(uid, out var battery) || !_battery.TryUseCharge(uid, component.EnergyPerUse, battery))
             {
                 args.Cancelled = true;
                 return;
@@ -48,93 +42,56 @@ namespace Content.Server.Stunnable.Systems
 
             if (battery.CurrentCharge < component.EnergyPerUse)
             {
-                _audio.PlayPvs(component.SparksSound, uid, AudioHelpers.WithVariation(0.25f));
-                TurnOff(uid, component);
+                _itemToggle.Toggle(uid, predicted: false);
             }
         }
 
-        private void OnUseInHand(EntityUid uid, StunbatonComponent comp, UseInHandEvent args)
+        private void OnExamined(EntityUid uid, BatteryComponent battery, ExaminedEvent args)
         {
-            if (comp.Activated)
-            {
-                TurnOff(uid, comp);
-            }
-            else
-            {
-                TurnOn(uid, comp, args.User);
-            }
-        }
-
-        private void OnExamined(EntityUid uid, StunbatonComponent comp, ExaminedEvent args)
-        {
-            var msg = comp.Activated
-                ? Loc.GetString("comp-stunbaton-examined-on")
-                : Loc.GetString("comp-stunbaton-examined-off");
-            args.PushMarkup(msg);
-            if (TryComp<BatteryComponent>(uid, out var battery))
-            {
-                args.PushMarkup(Loc.GetString("stunbaton-component-on-examine-charge",
-                    ("charge", (int)((battery.CurrentCharge/battery.MaxCharge) * 100))));
-            }
+            var onMsg = _itemToggle.IsActivated(uid)
+            ? Loc.GetString("comp-stunbaton-examined-on")
+            : Loc.GetString("comp-stunbaton-examined-off");
+            args.PushMarkup(onMsg);
+
+            var chargeMessage = Loc.GetString("stunbaton-component-on-examine-charge",
+                ("charge", (int) (battery.CurrentCharge / battery.MaxCharge * 100)));
+            args.PushMarkup(chargeMessage);
         }
 
-        private void TurnOff(EntityUid uid, StunbatonComponent comp)
+        private void ToggleDone(EntityUid uid, StunbatonComponent comp, ref ItemToggleDoneEvent args)
         {
-            if (!comp.Activated)
+            if (!TryComp<ItemComponent>(uid, out var item))
                 return;
-
-            if (TryComp<AppearanceComponent>(uid, out var appearance) &&
-                TryComp<ItemComponent>(uid, out var item))
-            {
-                _item.SetHeldPrefix(uid, "off", item);
-                _appearance.SetData(uid, ToggleVisuals.Toggled, false, appearance);
-            }
-
-            _audio.PlayPvs(comp.SparksSound, uid, AudioHelpers.WithVariation(0.25f));
-
-            comp.Activated = false;
-            Dirty(uid, comp);
+            _item.SetHeldPrefix(uid, args.Activated ? "on" : "off", item);
         }
 
-        private void TurnOn(EntityUid uid, StunbatonComponent comp, EntityUid user)
+        private void TryTurnOn(EntityUid uid, StunbatonComponent comp, ref ItemToggleActivateAttemptEvent args)
         {
-            if (comp.Activated)
-                return;
-
             if (!TryComp<BatteryComponent>(uid, out var battery) || battery.CurrentCharge < comp.EnergyPerUse)
             {
-
-                _audio.PlayPvs(comp.TurnOnFailSound, uid, AudioHelpers.WithVariation(0.25f));
-                _popup.PopupEntity(Loc.GetString("stunbaton-component-low-charge"), user, user);
+                args.Cancelled = true;
+                if (args.User != null)
+                {
+                    _popup.PopupEntity(Loc.GetString("stunbaton-component-low-charge"), (EntityUid) args.User, (EntityUid) args.User);
+                }
                 return;
             }
 
             if (TryComp<RiggableComponent>(uid, out var rig) && rig.IsRigged)
             {
-                _riggableSystem.Explode(uid, battery, user);
+                _riggableSystem.Explode(uid, battery, args.User);
             }
-
-
-            if (EntityManager.TryGetComponent<AppearanceComponent>(uid, out var appearance) &&
-                EntityManager.TryGetComponent<ItemComponent>(uid, out var item))
-            {
-                _item.SetHeldPrefix(uid, "on", item);
-                _appearance.SetData(uid, ToggleVisuals.Toggled, true, appearance);
-            }
-
-            _audio.PlayPvs(comp.SparksSound, uid, AudioHelpers.WithVariation(0.25f));
-            comp.Activated = true;
-            Dirty(uid, comp);
         }
 
         // https://github.com/space-wizards/space-station-14/pull/17288#discussion_r1241213341
         private void OnSolutionChange(EntityUid uid, StunbatonComponent component, SolutionChangedEvent args)
         {
             // Explode if baton is activated and rigged.
-            if (!TryComp<RiggableComponent>(uid, out var riggable) || !TryComp<BatteryComponent>(uid, out var battery))
+            if (!TryComp<RiggableComponent>(uid, out var riggable) ||
+                !TryComp<BatteryComponent>(uid, out var battery))
                 return;
 
-            if (component.Activated && riggable.IsRigged)
+            if (_itemToggle.IsActivated(uid) && riggable.IsRigged)
                 _riggableSystem.Explode(uid, battery);
         }
 
index 48fbd3640a3576ea574c77d5e8b409ea8fd03778..a620fa2ef4717c1a2da94bba18ee28cc495cf9f9 100644 (file)
@@ -1,9 +1,8 @@
 using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Damage;
 using Content.Shared.FixedPoint;
 using Content.Shared.Tools.Components;
 using Robust.Shared.Audio;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.Prototypes;
 
 namespace Content.Server.Tools.Components
 {
@@ -13,56 +12,37 @@ namespace Content.Server.Tools.Components
         /// <summary>
         ///     Solution on the entity that contains the fuel.
         /// </summary>
-        [DataField("fuelSolution"), ViewVariables(VVAccess.ReadWrite)]
+        [DataField, ViewVariables(VVAccess.ReadWrite)]
         public string FuelSolution { get; private set; } = "Welder";
 
         /// <summary>
         ///     Reagent that will be used as fuel for welding.
         /// </summary>
-        [DataField("fuelReagent", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>)), ViewVariables(VVAccess.ReadWrite)]
-        public string FuelReagent { get; private set; } = "WeldingFuel";
+        [DataField, ViewVariables(VVAccess.ReadWrite)]
+        public ProtoId<ReagentPrototype> FuelReagent { get; private set; } = "WeldingFuel";
 
         /// <summary>
-        ///     Fuel consumption per second, while the welder is active.
+        ///     Fuel consumption per second while the welder is active.
         /// </summary>
-        [DataField("fuelConsumption"), ViewVariables(VVAccess.ReadWrite)]
+        [DataField, ViewVariables(VVAccess.ReadWrite)]
         public FixedPoint2 FuelConsumption { get; private set; } = FixedPoint2.New(2.0f);
 
         /// <summary>
         ///     A fuel amount to be consumed when the welder goes from being unlit to being lit.
         /// </summary>
-        [DataField("fuelLitCost"), ViewVariables(VVAccess.ReadWrite)]
+        [DataField, ViewVariables(VVAccess.ReadWrite)]
         public FixedPoint2 FuelLitCost { get; private set; } = FixedPoint2.New(0.5f);
 
         /// <summary>
-        ///     Sound played when the welder is turned off.
+        ///     Sound played when refilling the welder.
         /// </summary>
-        [DataField("welderOffSounds")]
-        public SoundSpecifier WelderOffSounds { get; private set; } = new SoundCollectionSpecifier("WelderOff");
-
-        /// <summary>
-        ///     Sound played when the tool is turned on.
-        /// </summary>
-        [DataField("welderOnSounds")]
-        public SoundSpecifier WelderOnSounds { get; private set; } = new SoundCollectionSpecifier("WelderOn");
-
-        [DataField("welderRefill")]
+        [DataField]
         public SoundSpecifier WelderRefill { get; private set; } = new SoundPathSpecifier("/Audio/Effects/refill.ogg");
 
-        /// <summary>
-        ///     When the welder is lit, this damage is added to the base melee weapon damage.
-        /// </summary>
-        /// <remarks>
-        ///     If this is a standard welder, this damage bonus should probably subtract the entity's standard melee weapon damage
-        ///     and replace it all with heat damage.
-        /// </remarks>
-        [DataField("litMeleeDamageBonus")]
-        public DamageSpecifier LitMeleeDamageBonus = new();
-
         /// <summary>
         ///     Whether the item is safe to refill while lit without exploding the tank.
         /// </summary>
-        [DataField("tankSafe")]
+        [DataField]
         public bool TankSafe = false; //I have no idea what I'm doing
 
     }
index e0dc341a167a83587c6c48e1984b0a1e5485fd4f..7827cbdfc549f8c57aa091ececcac4dc6c31e7d2 100644 (file)
@@ -2,24 +2,21 @@ using System.Linq;
 using Content.Server.Chemistry.Components;
 using Content.Server.Tools.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Database;
 using Content.Shared.DoAfter;
 using Content.Shared.Examine;
 using Content.Shared.FixedPoint;
 using Content.Shared.Interaction;
 using Content.Shared.Item;
-using Content.Shared.Temperature;
-using Content.Shared.Toggleable;
+using Content.Shared.Item.ItemToggle;
 using Content.Shared.Tools.Components;
-using Content.Shared.Weapons.Melee.Events;
-using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
 
 namespace Content.Server.Tools
 {
     public sealed partial class ToolSystem
     {
+        [Dependency] private readonly SharedItemToggleSystem _itemToggle = default!;
         private readonly HashSet<EntityUid> _activeWelders = new();
 
         private const float WelderUpdateTimer = 1f;
@@ -27,106 +24,51 @@ namespace Content.Server.Tools
 
         public void InitializeWelders()
         {
-            SubscribeLocalEvent<WelderComponent, ComponentStartup>(OnWelderStartup);
-            SubscribeLocalEvent<WelderComponent, IsHotEvent>(OnWelderIsHotEvent);
             SubscribeLocalEvent<WelderComponent, ExaminedEvent>(OnWelderExamine);
-            SubscribeLocalEvent<WelderComponent, SolutionChangedEvent>(OnWelderSolutionChange);
-            SubscribeLocalEvent<WelderComponent, ActivateInWorldEvent>(OnWelderActivate);
             SubscribeLocalEvent<WelderComponent, AfterInteractEvent>(OnWelderAfterInteract);
             SubscribeLocalEvent<WelderComponent, DoAfterAttemptEvent<ToolDoAfterEvent>>(OnWelderToolUseAttempt);
             SubscribeLocalEvent<WelderComponent, ComponentShutdown>(OnWelderShutdown);
             SubscribeLocalEvent<WelderComponent, ComponentGetState>(OnWelderGetState);
-            SubscribeLocalEvent<WelderComponent, GetMeleeDamageEvent>(OnGetMeleeDamage);
-        }
-
-        private void OnGetMeleeDamage(EntityUid uid, WelderComponent component, ref GetMeleeDamageEvent args)
-        {
-            if (component.Lit)
-                args.Damage += component.LitMeleeDamageBonus;
+            SubscribeLocalEvent<WelderComponent, ItemToggleActivateAttemptEvent>(TryTurnOn);
+            SubscribeLocalEvent<WelderComponent, ItemToggleDeactivateAttemptEvent>(TurnOff);
         }
 
         public (FixedPoint2 fuel, FixedPoint2 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))
+                || !_solutionContainer.TryGetSolution(uid, welder.FuelSolution, out var fuelSolution, solutionContainer))
                 return (FixedPoint2.Zero, FixedPoint2.Zero);
 
-            return (_solutionContainerSystem.GetTotalPrototypeQuantity(uid, welder.FuelReagent), fuelSolution.MaxVolume);
-        }
-
-        public bool TryToggleWelder(EntityUid uid, EntityUid? user,
-            WelderComponent? welder = null,
-            SolutionContainerManagerComponent? solutionContainer = null,
-            ItemComponent? item = null,
-            SharedPointLightComponent? light = null,
-            AppearanceComponent? appearance = 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, appearance)
-                : TryTurnWelderOff(uid, user, welder, item, light, appearance);
+            return (_solutionContainer.GetTotalPrototypeQuantity(uid, welder.FuelReagent), fuelSolution.MaxVolume);
         }
 
-        public bool TryTurnWelderOn(EntityUid uid, EntityUid? user,
-            WelderComponent? welder = null,
-            SolutionContainerManagerComponent? solutionContainer = null,
-            ItemComponent? item = null,
-            SharedPointLightComponent? light = null,
-            AppearanceComponent? appearance = null,
-            TransformComponent? transform = null)
+        public void TryTurnOn(EntityUid uid, WelderComponent welder, ref ItemToggleActivateAttemptEvent args)
         {
-            if (!Resolve(uid, ref welder, ref solutionContainer, ref transform))
-                return false;
-
-            // Optional components.
-            Resolve(uid, ref item, ref appearance, false);
-
-            _light.ResolveLight(uid, ref light);
-
-            if (!_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var solution, solutionContainer))
-                return false;
-
+            if (!_solutionContainer.TryGetSolution(uid, welder.FuelSolution, out var solution) ||
+                !TryComp<TransformComponent>(uid, out var transform))
+            {
+                args.Cancelled = true;
+                return;
+            }
             var fuel = solution.GetTotalPrototypeQuantity(welder.FuelReagent);
 
             // Not enough fuel to lit welder.
             if (fuel == FixedPoint2.Zero || fuel < welder.FuelLitCost)
             {
-                if(user != null)
-                    _popupSystem.PopupEntity(Loc.GetString("welder-component-no-fuel-message"), uid, user.Value);
-                return false;
+                if (args.User != null)
+                {
+                    _popup.PopupEntity(Loc.GetString("welder-component-no-fuel-message"), uid, (EntityUid) args.User);
+                }
+                args.Cancelled = true;
+                return;
             }
 
             solution.RemoveReagent(welder.FuelReagent, welder.FuelLitCost);
 
-            welder.Lit = true;
-
             // Logging
-            if (user != null)
-                _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user.Value):user} toggled {ToPrettyString(uid):welder} on");
-            else
-                _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(uid):welder} toggled on");
-
-            var ev = new WelderToggledEvent(true);
-            RaiseLocalEvent(uid, ev);
-
-            var hotEvent = new IsHotEvent() {IsHot = true};
-            RaiseLocalEvent(uid, hotEvent);
-
-            _appearanceSystem.SetData(uid, WelderVisuals.Lit, true);
-            _appearanceSystem.SetData(uid, ToggleableLightVisuals.Enabled, true);
-
-            if (light != null)
-            {
-                _light.SetEnabled(uid, true, light);
-            }
+            _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(uid):welder} on");
 
-            _audioSystem.PlayPvs(welder.WelderOnSounds, uid, AudioParams.Default.WithVariation(0.125f).WithVolume(-5f));
-
-            if (transform.GridUid is {} gridUid)
+            if (transform.GridUid is { } gridUid)
             {
                 var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
                 _atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, uid, true);
@@ -135,68 +77,21 @@ namespace Content.Server.Tools
             Dirty(uid, welder);
 
             _activeWelders.Add(uid);
-            return true;
         }
 
-        public bool TryTurnWelderOff(EntityUid uid, EntityUid? user,
-            WelderComponent? welder = null,
-            ItemComponent? item = null,
-            SharedPointLightComponent? light = null,
-            AppearanceComponent? appearance = null)
+        public void TurnOff(EntityUid uid, WelderComponent welder, ref ItemToggleDeactivateAttemptEvent args)
         {
-            if (!Resolve(uid, ref welder))
-                return false;
-
-            // Optional components.
-            Resolve(uid, ref item, ref appearance, false);
-
-            _light.ResolveLight(uid, ref light);
-
-            welder.Lit = false;
-
             // Logging
-            if (user != null)
-                _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user.Value):user} toggled {ToPrettyString(uid):welder} off");
-            else
-                _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(uid):welder} toggled off");
-
-            var ev = new WelderToggledEvent(false);
-            RaiseLocalEvent(uid, ev);
-
-            var hotEvent = new IsHotEvent() {IsHot = false};
-            RaiseLocalEvent(uid, hotEvent);
-
-            // Layer 1 is the flame.
-            _appearanceSystem.SetData(uid, WelderVisuals.Lit, false);
-            _appearanceSystem.SetData(uid, ToggleableLightVisuals.Enabled, false);
-
-            if (light != null)
-            {
-                _light.SetEnabled(uid, false, light);
-            }
-
-            _audioSystem.PlayPvs(welder.WelderOffSounds, uid, AudioParams.Default.WithVariation(0.125f).WithVolume(-5f));
+            _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(uid):welder} off");
 
             Dirty(uid, welder);
 
             _activeWelders.Remove(uid);
-            return true;
-        }
-
-        private void OnWelderStartup(EntityUid uid, WelderComponent welder, ComponentStartup args)
-        {
-            // TODO: Delete this shit what
-            Dirty(uid, welder);
-        }
-
-        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)
+            if (_itemToggle.IsActivated(uid))
             {
                 args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message"));
             }
@@ -217,50 +112,34 @@ namespace Content.Server.Tools
             }
         }
 
-        private void OnWelderSolutionChange(EntityUid uid, WelderComponent welder, SolutionChangedEvent args)
-        {
-            // TODO what
-            // ????
-            Dirty(uid, welder);
-        }
-
-        private void OnWelderActivate(EntityUid uid, WelderComponent welder, ActivateInWorldEvent args)
-        {
-            args.Handled = TryToggleWelder(uid, args.User, welder);
-            if (args.Handled)
-                args.WasLogged = true;
-        }
-
         private void OnWelderAfterInteract(EntityUid uid, WelderComponent welder, AfterInteractEvent args)
         {
             if (args.Handled)
                 return;
 
-            if (args.Target is not {Valid: true} target || !args.CanReach)
+            if (args.Target is not { Valid: true } target || !args.CanReach)
                 return;
 
-            // TODO: Clean up this inherited oldcode.
-
-            if (EntityManager.TryGetComponent(target, out ReagentTankComponent? tank)
+            if (TryComp(target, out ReagentTankComponent? tank)
                 && tank.TankType == ReagentTankType.Fuel
-                && _solutionContainerSystem.TryGetDrainableSolution(target, out var targetSolution)
-                && _solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var welderSolution))
+                && _solutionContainer.TryGetDrainableSolution(target, out var targetSolution)
+                && _solutionContainer.TryGetSolution(uid, welder.FuelSolution, out var welderSolution))
             {
                 var trans = FixedPoint2.Min(welderSolution.AvailableVolume, targetSolution.Volume);
                 if (trans > 0)
                 {
-                    var drained = _solutionContainerSystem.Drain(target, targetSolution,  trans);
-                    _solutionContainerSystem.TryAddSolution(uid, welderSolution, drained);
-                    _audioSystem.PlayPvs(welder.WelderRefill, uid);
-                    _popupSystem.PopupEntity(Loc.GetString("welder-component-after-interact-refueled-message"), uid, args.User);
+                    var drained = _solutionContainer.Drain(target, targetSolution, trans);
+                    _solutionContainer.TryAddSolution(uid, welderSolution, drained);
+                    _audio.PlayPvs(welder.WelderRefill, uid);
+                    _popup.PopupEntity(Loc.GetString("welder-component-after-interact-refueled-message"), uid, args.User);
                 }
                 else if (welderSolution.AvailableVolume <= 0)
                 {
-                    _popupSystem.PopupEntity(Loc.GetString("welder-component-already-full"), uid, args.User);
+                    _popup.PopupEntity(Loc.GetString("welder-component-already-full"), uid, args.User);
                 }
                 else
                 {
-                    _popupSystem.PopupEntity(Loc.GetString("welder-component-no-fuel-in-tank", ("owner", args.Target)), uid, args.User);
+                    _popup.PopupEntity(Loc.GetString("welder-component-no-fuel-in-tank", ("owner", args.Target)), uid, args.User);
                 }
             }
 
@@ -271,11 +150,10 @@ namespace Content.Server.Tools
         {
             var user = args.DoAfter.Args.User;
 
-            if (!welder.Lit)
+            if (!_itemToggle.IsActivated(uid))
             {
-                _popupSystem.PopupEntity(Loc.GetString("welder-component-welder-not-lit-message"), uid, user);
+                _popup.PopupEntity(Loc.GetString("welder-component-welder-not-lit-message"), uid, user);
                 args.Cancel();
-                return;
             }
         }
 
@@ -287,7 +165,7 @@ namespace Content.Server.Tools
         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);
+            args.State = new WelderComponentState(capacity.Float(), fuel.Float());
         }
 
         private void UpdateWelders(float frameTime)
@@ -297,37 +175,26 @@ namespace Content.Server.Tools
             if (_welderTimer < WelderUpdateTimer)
                 return;
 
-
             // TODO Use an "active welder" component instead, EntityQuery over that.
             foreach (var tool in _activeWelders.ToArray())
             {
-                if (!EntityManager.TryGetComponent(tool, out WelderComponent? welder)
-                    || !EntityManager.TryGetComponent(tool, out SolutionContainerManagerComponent? solutionContainer)
-                    || !EntityManager.TryGetComponent(tool, out TransformComponent? transform))
+                if (!TryComp(tool, out WelderComponent? welder)
+                    || !TryComp(tool, out SolutionContainerManagerComponent? solutionContainer))
                     continue;
 
-                if (!_solutionContainerSystem.TryGetSolution(tool, welder.FuelSolution, out var solution, solutionContainer))
+                if (!_solutionContainer.TryGetSolution(tool, welder.FuelSolution, out var solution, solutionContainer))
                     continue;
 
                 solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer);
 
                 if (solution.GetTotalPrototypeQuantity(welder.FuelReagent) <= FixedPoint2.Zero)
-                    TryTurnWelderOff(tool, null, welder);
+                {
+                    _itemToggle.Toggle(tool, predicted: false);
+                }
 
                 Dirty(tool, welder);
             }
-
             _welderTimer -= WelderUpdateTimer;
         }
     }
-
-    public sealed class WelderToggledEvent : EntityEventArgs
-    {
-        public bool WelderOn;
-
-        public WelderToggledEvent(bool welderOn)
-        {
-            WelderOn = welderOn;
-        }
-    }
 }
index de6a7fefc1265763ebdb3cfc6a56196f509f7cf6..7366bfce40341da044dc537d5bbb9bc91ff6ef40 100644 (file)
@@ -19,10 +19,10 @@ namespace Content.Server.Tools
         [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
         [Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
         [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
-        [Dependency] private readonly PopupSystem _popupSystem = default!;
-        [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+        [Dependency] private readonly PopupSystem _popup = default!;
+        [Dependency] private readonly SharedAudioSystem _audio = default!;
         [Dependency] private readonly SharedPointLightSystem _light = default!;
-        [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
+        [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
         [Dependency] private readonly TransformSystem _transformSystem = default!;
 
         public override void Initialize()
index f699ef7b348f913e6e6fdb128ad8b0a1bcde186d..458c88af3ecdf4d9cbc38150b311d5f6116bae43 100644 (file)
@@ -1,44 +1,14 @@
-using Content.Shared.Damage;
-using Robust.Shared.Audio;
-
 namespace Content.Server.Weapons.Melee.EnergySword;
 
 [RegisterComponent]
 internal sealed partial class EnergySwordComponent : Component
 {
-    public Color BladeColor = Color.DodgerBlue;
-
-    public bool Hacked = false;
-
-    public bool Activated = false;
-
-    [DataField("isSharp")]
-    public bool IsSharp = true;
+    [ViewVariables(VVAccess.ReadWrite), DataField("activatedColor"), AutoNetworkedField]
+    public Color ActivatedColor = Color.DodgerBlue;
 
     /// <summary>
-    ///     Does this become hidden when deactivated
+    ///     A color option list for the random color picker.
     /// </summary>
-    [DataField("secret")]
-    public bool Secret { get; set; } = false;
-
-    /// <summary>
-    ///     RGB cycle rate for hacked e-swords.
-    /// </summary>
-    [DataField("cycleRate")]
-    public float CycleRate = 1f;
-
-    [DataField("activateSound")]
-    public SoundSpecifier ActivateSound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/ebladeon.ogg");
-
-    [DataField("deActivateSound")]
-    public SoundSpecifier DeActivateSound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/ebladeoff.ogg");
-
-    [DataField("onHitOn")]
-    public SoundSpecifier OnHitOn { get; set; } = new SoundPathSpecifier("/Audio/Weapons/eblade1.ogg");
-
-    [DataField("onHitOff")]
-    public SoundSpecifier OnHitOff { get; set; } = new SoundPathSpecifier("/Audio/Weapons/genhit1.ogg");
-
     [DataField("colorOptions")]
     public List<Color> ColorOptions = new()
     {
@@ -49,15 +19,10 @@ internal sealed partial class EnergySwordComponent : Component
         Color.MediumOrchid
     };
 
-    [DataField("litDamageBonus")]
-    public DamageSpecifier LitDamageBonus = new();
-
-    [DataField("litDisarmMalus")]
-    public float LitDisarmMalus = 0.6f;
+    public bool Hacked = false;
+    /// <summary>
+    ///     RGB cycle rate for hacked e-swords.
+    /// </summary>
+    [DataField("cycleRate")]
+    public float CycleRate = 1f;
 }
-
-[ByRefEvent]
-public readonly record struct EnergySwordActivatedEvent();
-
-[ByRefEvent]
-public readonly record struct EnergySwordDeactivatedEvent();
index a08ff17ec8c0317eb6b56007613745d8f223bba1..e8897781f5e7d4452d25afe9ebd8acfb17a5d197 100644 (file)
-using Content.Server.CombatMode.Disarm;
-using Content.Server.Kitchen.Components;
 using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
-using Content.Shared.Item;
 using Content.Shared.Light;
 using Content.Shared.Light.Components;
-using Content.Shared.Temperature;
 using Content.Shared.Toggleable;
 using Content.Shared.Tools.Components;
-using Content.Shared.Weapons.Melee;
-using Content.Shared.Weapons.Melee.Events;
-using Content.Shared.Wieldable;
-using Content.Shared.Wieldable.Components;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Player;
+using Content.Shared.Item;
 using Robust.Shared.Random;
 
 namespace Content.Server.Weapons.Melee.EnergySword;
 
 public sealed class EnergySwordSystem : EntitySystem
 {
-    [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly SharedRgbLightControllerSystem _rgbSystem = default!;
-    [Dependency] private readonly SharedItemSystem _item = default!;
-    [Dependency] private readonly SharedAudioSystem _audio = default!;
     [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+    [Dependency] private readonly IRobustRandom _random = default!;
 
     public override void Initialize()
     {
         base.Initialize();
 
         SubscribeLocalEvent<EnergySwordComponent, MapInitEvent>(OnMapInit);
-        SubscribeLocalEvent<EnergySwordComponent, GetMeleeDamageEvent>(OnGetMeleeDamage);
-        SubscribeLocalEvent<EnergySwordComponent, UseInHandEvent>(OnUseInHand);
         SubscribeLocalEvent<EnergySwordComponent, InteractUsingEvent>(OnInteractUsing);
-        SubscribeLocalEvent<EnergySwordComponent, IsHotEvent>(OnIsHotEvent);
-        SubscribeLocalEvent<EnergySwordComponent, EnergySwordDeactivatedEvent>(TurnOff);
-        SubscribeLocalEvent<EnergySwordComponent, EnergySwordActivatedEvent>(TurnOn);
-
-        SubscribeLocalEvent<EnergySwordComponent, ItemUnwieldedEvent>(TurnOffonUnwielded);
-        SubscribeLocalEvent<EnergySwordComponent, ItemWieldedEvent>(TurnOnonWielded);
     }
-
+    // Used to pick a random color for the blade on map init.
     private void OnMapInit(EntityUid uid, EnergySwordComponent comp, MapInitEvent args)
     {
         if (comp.ColorOptions.Count != 0)
-            comp.BladeColor = _random.Pick(comp.ColorOptions);
-    }
-
-    private void OnGetMeleeDamage(EntityUid uid, EnergySwordComponent comp, ref GetMeleeDamageEvent args)
-    {
-        if (!comp.Activated)
-            return;
-
-        // Adjusts base damage when the energy blade is active, by values set in yaml
-        args.Damage += comp.LitDamageBonus;
-    }
-
-    private void OnUseInHand(EntityUid uid, EnergySwordComponent comp, UseInHandEvent args)
-    {
-        if (args.Handled)
-            return;
-
-        args.Handled = true;
-
-        if (TryComp<WieldableComponent>(uid, out var wieldableComp))
-            return;
-
-        if (comp.Activated)
-        {
-            var ev = new EnergySwordDeactivatedEvent();
-            RaiseLocalEvent(uid, ref ev);
-        }
-        else
-        {
-            var ev = new EnergySwordActivatedEvent();
-            RaiseLocalEvent(uid, ref ev);
-        }
-
-        UpdateAppearance(uid, comp);
-    }
-
-    private void TurnOffonUnwielded(EntityUid uid, EnergySwordComponent comp, ItemUnwieldedEvent args)
-    {
-        var ev = new EnergySwordDeactivatedEvent();
-        RaiseLocalEvent(uid, ref ev);
-        UpdateAppearance(uid, comp);
-    }
-
-    private void TurnOnonWielded(EntityUid uid, EnergySwordComponent comp, ref ItemWieldedEvent args)
-    {
-        var ev = new EnergySwordActivatedEvent();
-        RaiseLocalEvent(uid, ref ev);
-        UpdateAppearance(uid, comp);
-    }
+            comp.ActivatedColor = _random.Pick(comp.ColorOptions);
 
-    private void TurnOff(EntityUid uid, EnergySwordComponent comp, ref EnergySwordDeactivatedEvent args)
-    {
-        if (TryComp(uid, out ItemComponent? item))
-        {
-            _item.SetSize(uid, "Small", item);
-        }
-
-        if (TryComp<DisarmMalusComponent>(uid, out var malus))
-        {
-            malus.Malus -= comp.LitDisarmMalus;
-        }
-
-        if (TryComp<MeleeWeaponComponent>(uid, out var weaponComp))
-        {
-            weaponComp.HitSound = comp.OnHitOff;
-            if (comp.Secret)
-                weaponComp.Hidden = true;
-        }
-
-        if (comp.IsSharp)
-            RemComp<SharpComponent>(uid);
-
-        _audio.PlayEntity(comp.DeActivateSound, Filter.Pvs(uid, entityManager: EntityManager), uid, true, comp.DeActivateSound.Params);
-
-        comp.Activated = false;
-    }
-
-    private void TurnOn(EntityUid uid, EnergySwordComponent comp, ref EnergySwordActivatedEvent args)
-    {
-        if (TryComp(uid, out ItemComponent? item))
-        {
-            _item.SetSize(uid, "Huge", item);
-        }
-
-        if (comp.IsSharp)
-            EnsureComp<SharpComponent>(uid);
-
-        if (TryComp<MeleeWeaponComponent>(uid, out var weaponComp))
-        {
-            weaponComp.HitSound = comp.OnHitOn;
-            if (comp.Secret)
-                weaponComp.Hidden = false;
-        }
-
-        if (TryComp<DisarmMalusComponent>(uid, out var malus))
-        {
-            malus.Malus += comp.LitDisarmMalus;
-        }
-
-        _audio.PlayEntity(comp.ActivateSound, Filter.Pvs(uid, entityManager: EntityManager), uid, true, comp.ActivateSound.Params);
-
-        comp.Activated = true;
-    }
-
-    private void UpdateAppearance(EntityUid uid, EnergySwordComponent component)
-    {
         if (!TryComp(uid, out AppearanceComponent? appearanceComponent))
             return;
-
-        _appearance.SetData(uid, ToggleableLightVisuals.Enabled, component.Activated, appearanceComponent);
-        _appearance.SetData(uid, ToggleableLightVisuals.Color, component.BladeColor, appearanceComponent);
+        _appearance.SetData(uid, ToggleableLightVisuals.Color, comp.ActivatedColor, appearanceComponent);
     }
 
+    // Used to make the make the blade multicolored when using a multitool on it.
     private void OnInteractUsing(EntityUid uid, EnergySwordComponent comp, InteractUsingEvent args)
     {
         if (args.Handled)
@@ -178,9 +52,4 @@ public sealed class EnergySwordSystem : EntitySystem
         else
             RemComp<RgbLightControllerComponent>(uid);
     }
-
-    private void OnIsHotEvent(EntityUid uid, EnergySwordComponent energySword, IsHotEvent args)
-    {
-        args.IsHot = energySword.Activated;
-    }
 }
index 48dffd87e360f14ad46da4de949cb61de12336bf..5c1939478ed889c5a86c12c6656d95070de8ce4d 100644 (file)
@@ -1,5 +1,4 @@
-using Content.Server.Weapons.Melee.EnergySword;
-using Content.Server.Weapons.Melee.ItemToggle;
+using Content.Shared.Item;
 using Content.Shared.Weapons.Reflect;
 
 namespace Content.Server.Weapons.Reflect;
@@ -10,33 +9,12 @@ public sealed class ReflectSystem : SharedReflectSystem
     {
         base.Initialize();
 
-        SubscribeLocalEvent<ReflectComponent, EnergySwordActivatedEvent>(EnableReflect);
-        SubscribeLocalEvent<ReflectComponent, EnergySwordDeactivatedEvent>(DisableReflect);
-        SubscribeLocalEvent<ReflectComponent, ItemToggleActivatedEvent>(ShieldEnableReflect);
-        SubscribeLocalEvent<ReflectComponent, ItemToggleDeactivatedEvent>(ShieldDisableReflect);
+        SubscribeLocalEvent<ReflectComponent, ItemToggleReflectUpdateEvent>(ToggleReflect);
     }
 
-    private void EnableReflect(EntityUid uid, ReflectComponent comp, ref EnergySwordActivatedEvent args)
+    private void ToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggleReflectUpdateEvent args)
     {
-        comp.Enabled = true;
-        Dirty(comp);
-    }
-
-    private void DisableReflect(EntityUid uid, ReflectComponent comp, ref EnergySwordDeactivatedEvent args)
-    {
-        comp.Enabled = false;
-        Dirty(comp);
-    }
-
-    private void ShieldEnableReflect(EntityUid uid, ReflectComponent comp, ref ItemToggleActivatedEvent args)
-    {
-        comp.Enabled = true;
-        Dirty(comp);
-    }
-
-    private void ShieldDisableReflect(EntityUid uid, ReflectComponent comp, ref ItemToggleDeactivatedEvent args)
-    {
-        comp.Enabled = false;
-        Dirty(comp);
+        comp.Enabled = args.Activated;
+        Dirty(uid, comp);
     }
 }
index b7181e297f6998ded9f565d244d86e7b8178795a..702b6fe3e9d5dbc10744beff5be150f5db31728e 100644 (file)
@@ -5,6 +5,7 @@ using JetBrains.Annotations;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
 using Robust.Shared.Utility;
+using Robust.Shared.Serialization;
 
 namespace Content.Shared.Damage
 {
@@ -15,7 +16,7 @@ namespace Content.Shared.Damage
     ///     The actual damage information is stored in <see cref="DamageDict"/>. This class provides
     ///     functions to apply resistance sets and supports basic math operations to modify this dictionary.
     /// </remarks>
-    [DataDefinition]
+    [DataDefinition, Serializable, NetSerializable]
     public sealed partial class DamageSpecifier : IEquatable<DamageSpecifier>
     {
         // These exist solely so the wiki works. Please do not touch them or use them.
diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs
new file mode 100644 (file)
index 0000000..eb99297
--- /dev/null
@@ -0,0 +1,34 @@
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Item;
+
+/// <summary>
+/// Handles the active sound being played continuously with some items that are activated (ie e-sword hum).
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ItemToggleActiveSoundComponent : Component
+{
+    /// <summary>
+    ///     The continuous noise this item makes when it's activated (like an e-sword's hum). This loops.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public SoundSpecifier? ActiveSound;
+
+    /// <summary>
+    ///     Used when the item emits sound while active.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public EntityUid? PlayingStream;
+}
+
+/// <summary>
+/// Raised in order to effect changes upon the ActiveSound of the entity.
+/// </summary>
+[ByRefEvent]
+public record struct ItemToggleActiveSoundUpdateEvent(bool Activated, bool Predicted, EntityUid? User)
+{
+    public bool Activated = Activated;
+    public bool Predicted = Predicted;
+    public EntityUid? User = User;
+}
diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs
new file mode 100644 (file)
index 0000000..6d27d9e
--- /dev/null
@@ -0,0 +1,126 @@
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Item;
+
+/// <summary>
+/// Handles generic item toggles, like a welder turning on and off, or an e-sword.
+/// </summary>
+/// <remarks>
+/// If you need extended functionality (e.g. requiring power) then add a new component and use events:
+/// ItemToggleActivateAttemptEvent, ItemToggleDeactivateAttemptEvent or ItemToggleForceToggleEvent.
+/// </remarks>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class ItemToggleComponent : Component
+{
+    /// <summary>
+    ///     The item's toggle state.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public bool Activated = false;
+
+    /// <summary>
+    ///     Whether the item's toggle can be predicted by the client.
+    /// </summary>
+    /// /// <remarks>
+    /// If server-side systems affect the item's toggle, like charge/fuel systems, then the item is not predictable.
+    /// </remarks>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public bool Predictable = true;
+
+    /// <summary>
+    ///     The noise this item makes when it is toggled on.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public SoundSpecifier? SoundActivate;
+
+    /// <summary>
+    ///     The noise this item makes when it is toggled off.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public SoundSpecifier? SoundDeactivate;
+
+    /// <summary>
+    ///     The noise this item makes when it is toggled on.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public SoundSpecifier? SoundFailToActivate;
+}
+
+/// <summary>
+/// Raised directed on an entity when its ItemToggle is attempted to be activated.
+/// </summary>
+[ByRefEvent]
+public record struct ItemToggleActivateAttemptEvent(EntityUid? User)
+{
+    public bool Cancelled = false;
+    public EntityUid? User = User;
+}
+
+/// <summary>
+/// Raised directed on an entity when its ItemToggle is attempted to be deactivated.
+/// </summary>
+[ByRefEvent]
+public record struct ItemToggleDeactivateAttemptEvent(EntityUid? User)
+{
+    public bool Cancelled = false;
+    public EntityUid? User = User;
+}
+
+/// <summary>
+/// Raised directed on an entity any sort of toggle is complete.
+/// </summary>
+[ByRefEvent]
+public record struct ItemToggleDoneEvent(bool Activated, EntityUid? User)
+{
+    public bool Activated = Activated;
+    public EntityUid? User = User;
+}
+
+/// <summary>
+/// Raised in order to play a toggle sound effect.
+/// </summary>
+[ByRefEvent]
+public record struct ItemTogglePlayToggleSoundEvent(bool Activated, bool Predicted, EntityUid? User)
+{
+    public bool Activated = Activated;
+    public bool Predicted = Predicted;
+    public EntityUid? User = User;
+}
+
+/// <summary>
+/// Raised in order to play a failure to toggle sound effect.
+/// </summary>
+[ByRefEvent]
+public record struct ItemTogglePlayFailSoundEvent(bool Predicted, EntityUid? User)
+{
+    public bool Predicted = Predicted;
+    public EntityUid? User = User;
+}
+
+/// <summary>
+/// Raised in order to effect changes upon the Light component of the entity.
+/// </summary>
+[ByRefEvent]
+public record struct ItemToggleLightUpdateEvent(bool Activated)
+{
+    public bool Activated = Activated;
+}
+
+/// <summary>
+/// Raised in order to effect changes upon the Appearance component of the entity.
+/// </summary>
+[ByRefEvent]
+public record struct ItemToggleAppearanceUpdateEvent(bool Activated)
+{
+    public bool Activated = Activated;
+}
+
+/// <summary>
+/// Raised in order to effect changes upon the Reflect component of the entity.
+/// </summary>
+[ByRefEvent]
+public record struct ItemToggleReflectUpdateEvent(bool Activated)
+{
+    public bool Activated = Activated;
+}
diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs
new file mode 100644 (file)
index 0000000..b35adfa
--- /dev/null
@@ -0,0 +1,16 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Item;
+
+/// <summary>
+/// Handles whether the item is hot when toggled on. 
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ItemToggleHotComponent : Component
+{
+    /// <summary>
+    ///     Item becomes hot when active.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public bool IsHotWhenActivated = true;
+}
diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleMeleeWeaponComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleMeleeWeaponComponent.cs
new file mode 100644 (file)
index 0000000..5e57491
--- /dev/null
@@ -0,0 +1,79 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Audio;
+using Content.Shared.Damage;
+
+namespace Content.Shared.Item;
+
+/// <summary>
+/// Handles the changes to the melee weapon component when the item is toggled. 
+/// </summary>
+/// <remarks>
+/// You can change the damage, sound on hit, on swing, as well as hidden status while activated.
+/// </remarks>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ItemToggleMeleeWeaponComponent : Component
+{
+    /// <summary>
+    ///     The noise this item makes when hitting something with it on.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public SoundSpecifier? ActivatedSoundOnHit;
+
+    /// <summary>
+    ///     The noise this item makes when hitting something with it off.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public SoundSpecifier? DeactivatedSoundOnHit;
+
+    /// <summary>
+    ///     The noise this item makes when hitting something with it on and it does no damage.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public SoundSpecifier? ActivatedSoundOnHitNoDamage;
+
+    /// <summary>
+    ///     The noise this item makes when hitting something with it off and it does no damage.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public SoundSpecifier? DeactivatedSoundOnHitNoDamage;
+
+    /// <summary>
+    ///     The noise this item makes when swinging at nothing while activated.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public SoundSpecifier? ActivatedSoundOnSwing;
+
+    /// <summary>
+    ///     The noise this item makes when swinging at nothing while not activated.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public SoundSpecifier? DeactivatedSoundOnSwing;
+
+    /// <summary>
+    ///     Damage done by this item when activated.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public DamageSpecifier? ActivatedDamage = null;
+
+    /// <summary>
+    ///     Damage done by this item when deactivated.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public DamageSpecifier? DeactivatedDamage = null;
+
+    /// <summary>
+    ///     Does this become hidden when deactivated
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public bool DeactivatedSecret = false;
+}
+
+/// <summary>
+/// Raised in order to effect changes upon the MeleeWeaponComponent of the entity.
+/// </summary>
+[ByRefEvent]
+public record struct ItemToggleMeleeWeaponUpdateEvent(bool Activated)
+{
+    public bool Activated = Activated;
+}
+
diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleSizeComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleSizeComponent.cs
new file mode 100644 (file)
index 0000000..f280934
--- /dev/null
@@ -0,0 +1,35 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Item;
+
+/// <summary>
+/// Handles the changes to the item size when toggled. 
+/// </summary>
+/// <remarks>
+/// You can change the size when activated or not. By default the sizes are copied from the item.
+/// </remarks>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ItemToggleSizeComponent : Component
+{
+    /// <summary>
+    ///     Item's size when activated
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public ProtoId<ItemSizePrototype>? ActivatedSize = null;
+
+    /// <summary>
+    ///     Item's size when deactivated. If none is mentioned, it uses the item's default size instead.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public ProtoId<ItemSizePrototype>? DeactivatedSize = null;
+}
+
+/// <summary>
+/// Raised in order to effect changes upon the MeleeWeaponComponent of the entity.
+/// </summary>
+[ByRefEvent]
+public record struct ItemToggleSizeUpdateEvent(bool Activated)
+{
+    public bool Activated = Activated;
+}
diff --git a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs
new file mode 100644 (file)
index 0000000..e44d135
--- /dev/null
@@ -0,0 +1,293 @@
+using Content.Shared.Interaction.Events;
+using Content.Shared.Toggleable;
+using Content.Shared.Temperature;
+using Content.Shared.Wieldable;
+using Content.Shared.Wieldable.Components;
+using Robust.Shared.Audio;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Network;
+
+namespace Content.Shared.Item.ItemToggle;
+/// <summary>
+/// Handles generic item toggles, like a welder turning on and off, or an e-sword.
+/// </summary>
+/// <remarks>
+/// If you need extended functionality (e.g. requiring power) then add a new component and use events.
+/// </remarks>
+public abstract class SharedItemToggleSystem : EntitySystem
+{
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+    [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+    [Dependency] private readonly SharedPointLightSystem _light = default!;
+    [Dependency] private readonly INetManager _netManager = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ItemToggleComponent, ItemUnwieldedEvent>(TurnOffonUnwielded);
+        SubscribeLocalEvent<ItemToggleComponent, ItemWieldedEvent>(TurnOnonWielded);
+        SubscribeLocalEvent<ItemToggleComponent, UseInHandEvent>(OnUseInHand);
+        SubscribeLocalEvent<ItemToggleHotComponent, IsHotEvent>(OnIsHotEvent);
+        SubscribeLocalEvent<ItemToggleActiveSoundComponent, ItemToggleActiveSoundUpdateEvent>(UpdateActiveSound);
+        SubscribeLocalEvent<AppearanceComponent, ItemToggleAppearanceUpdateEvent>(UpdateAppearance);
+        SubscribeLocalEvent<ItemToggleComponent, ItemToggleLightUpdateEvent>(UpdateLight);
+        SubscribeLocalEvent<ItemToggleComponent, ItemTogglePlayToggleSoundEvent>(PlayToggleSound);
+        SubscribeLocalEvent<ItemToggleComponent, ItemTogglePlayFailSoundEvent>(PlayFailToggleSound);
+    }
+
+    private void OnUseInHand(EntityUid uid, ItemToggleComponent itemToggle, UseInHandEvent args)
+    {
+        if (args.Handled)
+            return;
+
+        args.Handled = true;
+
+        Toggle(uid, args.User, predicted: itemToggle.Predictable, itemToggle: itemToggle);
+    }
+
+    /// <summary>
+    /// Used when an item is attempted to be toggled. 
+    /// </summary>
+    public void Toggle(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null)
+    {
+        if (!Resolve(uid, ref itemToggle))
+            return;
+
+        if (itemToggle.Activated)
+        {
+            TryDeactivate(uid, user, itemToggle: itemToggle, predicted: predicted);
+        }
+        else
+        {
+            TryActivate(uid, user, itemToggle: itemToggle, predicted: predicted);
+        }
+    }
+
+    /// <summary>
+    /// Used when an item is attempting to be activated. It returns false if the attempt fails any reason, interrupting the activation.
+    /// </summary>
+    public bool TryActivate(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null)
+    {
+        if (!Resolve(uid, ref itemToggle))
+            return false;
+
+        if (itemToggle.Activated)
+            return true;
+
+        var attempt = new ItemToggleActivateAttemptEvent(user);
+        RaiseLocalEvent(uid, ref attempt);
+
+        if (attempt.Cancelled)
+        {
+            //Raises the event to play the failure to activate noise.
+            var evPlayFailToggleSound = new ItemTogglePlayFailSoundEvent(Predicted: predicted, user);
+            RaiseLocalEvent(uid, ref evPlayFailToggleSound);
+
+            return false;
+        }
+        // If the item's toggle is unpredictable because of something like requiring fuel or charge, then clients exit here.
+        // Otherwise you get stuff like an item activating client-side and then turning back off when it synchronizes with the server.
+        if (predicted == false && _netManager.IsClient)
+            return true;
+
+        Activate(uid, itemToggle);
+
+        var evPlayToggleSound = new ItemTogglePlayToggleSoundEvent(Activated: true, Predicted: predicted, user);
+        RaiseLocalEvent(uid, ref evPlayToggleSound);
+
+        var evActiveSound = new ItemToggleActiveSoundUpdateEvent(Activated: true, Predicted: predicted, user);
+        RaiseLocalEvent(uid, ref evActiveSound);
+
+        var toggleUsed = new ItemToggleDoneEvent(Activated: true, user);
+        RaiseLocalEvent(uid, ref toggleUsed);
+
+        return true;
+    }
+
+    /// <summary>
+    /// Used when an item is attempting to be deactivated. It returns false if the attempt fails any reason, interrupting the deactivation.
+    /// </summary>
+    public bool TryDeactivate(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null)
+    {
+        if (!Resolve(uid, ref itemToggle))
+            return false;
+
+        if (!itemToggle.Activated)
+            return true;
+
+        var attempt = new ItemToggleDeactivateAttemptEvent(user);
+        RaiseLocalEvent(uid, ref attempt);
+
+        if (attempt.Cancelled)
+        {
+            return false;
+        }
+
+        // If the item's toggle is unpredictable because of something like requiring fuel or charge, then clients exit here.
+        if (predicted == false && _netManager.IsClient)
+            return true;
+
+        Deactivate(uid, itemToggle);
+
+        var evPlayToggleSound = new ItemTogglePlayToggleSoundEvent(Activated: false, Predicted: predicted, user);
+        RaiseLocalEvent(uid, ref evPlayToggleSound);
+
+        var evActiveSound = new ItemToggleActiveSoundUpdateEvent(Activated: false, Predicted: predicted, user);
+        RaiseLocalEvent(uid, ref evActiveSound);
+
+        var toggleUsed = new ItemToggleDoneEvent(Activated: false, user);
+        RaiseLocalEvent(uid, ref toggleUsed);
+
+        return true;
+    }
+
+    /// <summary>
+    /// Used to make the actual changes to the item's components on activation.
+    /// </summary>
+    private void Activate(EntityUid uid, ItemToggleComponent itemToggle)
+    {
+        UpdateComponents(uid, itemToggle.Activated = true);
+
+        Dirty(uid, itemToggle);
+    }
+
+    /// <summary>
+    /// Used to make the actual changes to the item's components on deactivation.
+    /// </summary>
+    private void Deactivate(EntityUid uid, ItemToggleComponent itemToggle)
+    {
+        UpdateComponents(uid, itemToggle.Activated = false);
+
+        Dirty(uid, itemToggle);
+    }
+
+    /// <summary>
+    /// Used to raise events to update components on toggle.
+    /// </summary>
+    private void UpdateComponents(EntityUid uid, bool activated)
+    {
+        var evSize = new ItemToggleSizeUpdateEvent(activated);
+        RaiseLocalEvent(uid, ref evSize);
+
+        var evMelee = new ItemToggleMeleeWeaponUpdateEvent(activated);
+        RaiseLocalEvent(uid, ref evMelee);
+
+        var evAppearance = new ItemToggleAppearanceUpdateEvent(activated);
+        RaiseLocalEvent(uid, ref evAppearance);
+
+        var evLight = new ItemToggleLightUpdateEvent(activated);
+        RaiseLocalEvent(uid, ref evLight);
+
+        var evReflect = new ItemToggleReflectUpdateEvent(activated);
+        RaiseLocalEvent(uid, ref evReflect);
+    }
+
+    /// <summary>
+    /// Used for items that require to be wielded in both hands to activate. For instance the dual energy sword will turn off if not wielded.
+    /// </summary>
+    private void TurnOffonUnwielded(EntityUid uid, ItemToggleComponent itemToggle, ItemUnwieldedEvent args)
+    {
+        if (itemToggle.Activated)
+            TryDeactivate(uid, args.User, itemToggle: itemToggle);
+    }
+
+    /// <summary>
+    /// Wieldable items will automatically turn on when wielded.
+    /// </summary>
+    private void TurnOnonWielded(EntityUid uid, ItemToggleComponent itemToggle, ref ItemWieldedEvent args)
+    {
+        if (!itemToggle.Activated)
+            TryActivate(uid, itemToggle: itemToggle);
+    }
+
+    public bool IsActivated(EntityUid uid, ItemToggleComponent? comp = null)
+    {
+        if (!Resolve(uid, ref comp, false))
+            return true; // assume always activated if no component
+
+        return comp.Activated;
+    }
+
+    /// <summary>
+    /// Used to make the item hot when activated.
+    /// </summary>
+    private void OnIsHotEvent(EntityUid uid, ItemToggleHotComponent itemToggleHot, IsHotEvent args)
+    {
+        if (itemToggleHot.IsHotWhenActivated)
+            args.IsHot = IsActivated(uid);
+    }
+
+    /// <summary>
+    /// Used to update item appearance.
+    /// </summary>
+    private void UpdateAppearance(EntityUid uid, AppearanceComponent appearance, ref ItemToggleAppearanceUpdateEvent args)
+    {
+        _appearance.SetData(uid, ToggleableLightVisuals.Enabled, args.Activated, appearance);
+        _appearance.SetData(uid, ToggleVisuals.Toggled, args.Activated, appearance);
+    }
+
+    /// <summary>
+    /// Used to update light settings.
+    /// </summary>
+    private void UpdateLight(EntityUid uid, ItemToggleComponent comp, ref ItemToggleLightUpdateEvent args)
+    {
+        if (!_light.TryGetLight(uid, out var light))
+            return;
+
+        _light.SetEnabled(uid, args.Activated, light);
+    }
+
+    /// <summary>
+    /// Used to update the looping active sound linked to the entity.
+    /// </summary>
+    private void UpdateActiveSound(EntityUid uid, ItemToggleActiveSoundComponent activeSound, ref ItemToggleActiveSoundUpdateEvent args)
+    {
+        if (args.Activated)
+        {
+            if (activeSound.ActiveSound != null && activeSound.PlayingStream == null)
+            {
+                if (args.Predicted)
+                    activeSound.PlayingStream = _audio.PlayPredicted(activeSound.ActiveSound, uid, args.User, AudioParams.Default.WithLoop(true)).Value.Entity;
+                else
+                    activeSound.PlayingStream = _audio.PlayPvs(activeSound.ActiveSound, uid, AudioParams.Default.WithLoop(true)).Value.Entity;
+            }
+        }
+        else
+        {
+            activeSound.PlayingStream = _audio.Stop(activeSound.PlayingStream);
+        }
+    }
+
+    /// <summary>
+    /// Used to play a toggle sound.
+    /// </summary>
+    private void PlayToggleSound(EntityUid uid, ItemToggleComponent itemToggle, ref ItemTogglePlayToggleSoundEvent args)
+    {
+        SoundSpecifier? soundToPlay;
+        if (args.Activated)
+            soundToPlay = itemToggle.SoundActivate;
+        else
+            soundToPlay = itemToggle.SoundDeactivate;
+
+        if (soundToPlay == null)
+            return;
+
+        if (args.Predicted)
+            _audio.PlayPredicted(soundToPlay, uid, args.User);
+        else
+            _audio.PlayPvs(soundToPlay, uid);
+    }
+
+    /// <summary>
+    /// Used to play a failure to toggle sound.
+    /// </summary>
+    private void PlayFailToggleSound(EntityUid uid, ItemToggleComponent itemToggle, ref ItemTogglePlayFailSoundEvent args)
+    {
+        if (args.Predicted)
+            _audio.PlayPredicted(itemToggle.SoundFailToActivate, uid, args.User);
+        else
+            _audio.PlayPvs(itemToggle.SoundFailToActivate, uid);
+    }
+}
diff --git a/Content.Shared/Item/ItemToggleComponent.cs b/Content.Shared/Item/ItemToggleComponent.cs
deleted file mode 100644 (file)
index eb25f25..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-using Content.Shared.Item;
-using Robust.Shared.Audio;
-using Robust.Shared.GameStates;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Weapons.Melee.ItemToggle;
-
-[RegisterComponent, NetworkedComponent]
-public sealed partial class ItemToggleComponent : Component
-{
-    public bool Activated = false;
-
-    [DataField("activateSound")]
-    public SoundSpecifier ActivateSound { get; set; } = default!;
-
-    [DataField("deActivateSound")]
-    public SoundSpecifier DeActivateSound { get; set; } = default!;
-
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("activatedDisarmMalus")]
-    public float ActivatedDisarmMalus = 0.6f;
-
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("offSize")]
-    public ProtoId<ItemSizePrototype> OffSize = "Small";
-
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("onSize")]
-    public ProtoId<ItemSizePrototype> OnSize = "Huge";
-}
-
-[ByRefEvent]
-public readonly record struct ItemToggleActivatedEvent();
-
-[ByRefEvent]
-public readonly record struct ItemToggleDeactivatedEvent();
index c718b21e0ffeef36f61c43f8a0f83e77c5b149e7..fe925c554745303950c85ec95a8266e9b87df075 100644 (file)
@@ -27,6 +27,8 @@ public abstract class SharedItemSystem : EntitySystem
         SubscribeLocalEvent<ItemComponent, ComponentHandleState>(OnHandleState);
 
         SubscribeLocalEvent<ItemComponent, ExaminedEvent>(OnExamine);
+
+        SubscribeLocalEvent<ItemToggleSizeComponent, ItemToggleSizeUpdateEvent>(OnItemToggle);
     }
 
     #region Public API
@@ -205,4 +207,32 @@ public abstract class SharedItemSystem : EntitySystem
 
         return adjustedShapes;
     }
+
+    /// <summary>
+    /// Used to update the Item component on item toggle (specifically size).
+    /// </summary>
+    private void OnItemToggle(EntityUid uid, ItemToggleSizeComponent itemToggleSize, ItemToggleSizeUpdateEvent args)
+    {
+        if (!TryComp(uid, out ItemComponent? item))
+            return;
+
+        if (args.Activated)
+        {
+            if (itemToggleSize.ActivatedSize != null)
+            {
+                // Set the deactivated size to the default item's size before it gets changed.
+                itemToggleSize.DeactivatedSize ??= item.Size;
+                SetSize(uid, (ProtoId<ItemSizePrototype>) itemToggleSize.ActivatedSize, item);
+            }
+        }
+        else
+        {
+            if (itemToggleSize.DeactivatedSize != null)
+            {
+                SetSize(uid, (ProtoId<ItemSizePrototype>) itemToggleSize.DeactivatedSize, item);
+            }
+        }
+
+        Dirty(uid, item);
+    }
 }
index c964a9427de6ef38d6fd638f04451e32436f3d57..28b68553b313c72a2643589b7e1c30cb90ae6a1a 100644 (file)
@@ -1,7 +1,3 @@
-using Content.Server.Stunnable.Components;
-using Content.Shared.Damage;
-using Content.Shared.Weapons.Melee.Events;
-
 namespace Content.Shared.Stunnable;
 
 public abstract class SharedStunbatonSystem : EntitySystem
@@ -9,16 +5,5 @@ public abstract class SharedStunbatonSystem : EntitySystem
     public override void Initialize()
     {
         base.Initialize();
-
-        SubscribeLocalEvent<StunbatonComponent, GetMeleeDamageEvent>(OnGetMeleeDamage);
-    }
-
-    private void OnGetMeleeDamage(EntityUid uid, StunbatonComponent component, ref GetMeleeDamageEvent args)
-    {
-        if (!component.Activated)
-            return;
-
-        // Don't apply damage if it's activated; just do stamina damage.
-        args.Damage = new DamageSpecifier();
     }
 }
index 1cbba641be86982a20ebc1b2f26e438be40d11d2..1df009f780b45f3ccc6860f777b3b08c2c4557aa 100644 (file)
@@ -9,17 +9,10 @@ namespace Content.Server.Stunnable.Components;
 [Access(typeof(SharedStunbatonSystem))]
 public sealed partial class StunbatonComponent : Component
 {
-    [DataField("activated"), ViewVariables(VVAccess.ReadWrite)]
-    [AutoNetworkedField]
-    public bool Activated = false;
-
     [DataField("energyPerUse"), ViewVariables(VVAccess.ReadWrite)]
     [AutoNetworkedField]
     public float EnergyPerUse = 350;
 
     [DataField("sparksSound")]
     public SoundSpecifier SparksSound = new SoundCollectionSpecifier("sparks");
-
-    [DataField("turnOnFailSound")]
-    public SoundSpecifier TurnOnFailSound = new SoundPathSpecifier("/Audio/Machines/button.ogg");
 }
index 735aef88c19fcc21159f986fad3d0151e2caad70..78c1cde201bf898948c45277baa0e1bf5d3a53e4 100644 (file)
@@ -4,36 +4,18 @@ using Robust.Shared.Serialization;
 namespace Content.Shared.Tools.Components
 {
     [NetworkedComponent]
-    public abstract partial class SharedWelderComponent : Component
-    {
-        public bool Lit { get; set; }
-    }
+    public abstract partial class SharedWelderComponent : Component { }
 
     [NetSerializable, Serializable]
     public sealed class WelderComponentState : ComponentState
     {
         public float FuelCapacity { get; }
         public float Fuel { get; }
-        public bool Lit { get; }
 
-        public WelderComponentState(float fuelCapacity, float fuel, bool lit)
+        public WelderComponentState(float fuelCapacity, float fuel)
         {
             FuelCapacity = fuelCapacity;
             Fuel = fuel;
-            Lit = lit;
         }
     }
-
-    [Serializable, NetSerializable]
-    public enum WelderVisuals : byte
-    {
-        Lit
-    }
-
-    [Serializable, NetSerializable]
-    public enum WelderLayers : byte
-    {
-        Base,
-        Flame
-    }
 }
index 54db0b8c67feea3000212cfdd870771a115a5f2a..d76d7340ff12ea4d531bff82af8fbb738c833b02 100644 (file)
@@ -63,8 +63,8 @@ public sealed partial class MeleeWeaponComponent : Component
     /// <summary>
     /// Base damage for this weapon. Can be modified via heavy damage or other means.
     /// </summary>
-    [DataField(required:true)]
-    [ViewVariables(VVAccess.ReadWrite)]
+    [DataField(required: true)]
+    [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
     public DamageSpecifier Damage = default!;
 
     [DataField]
@@ -113,7 +113,7 @@ public sealed partial class MeleeWeaponComponent : Component
     /// This gets played whenever a melee attack is done. This is predicted by the client.
     /// </summary>
     [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("soundSwing")]
+    [DataField("soundSwing"), AutoNetworkedField]
     public SoundSpecifier SwingSound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/punchmiss.ogg")
     {
         Params = AudioParams.Default.WithVolume(-3f).WithVariation(0.025f),
@@ -124,14 +124,14 @@ public sealed partial class MeleeWeaponComponent : Component
     // If overwatch and apex do this then we probably should too.
 
     [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("soundHit")]
+    [DataField("soundHit"), AutoNetworkedField]
     public SoundSpecifier? HitSound;
 
     /// <summary>
     /// Plays if no damage is done to the target entity.
     /// </summary>
     [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("soundNoDamage")]
+    [DataField("soundNoDamage"), AutoNetworkedField]
     public SoundSpecifier NoDamageSound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/tap.ogg");
 }
 
index 5eac283ef19e5a97229b0f2bfa568b326ab9a11b..3505149564f18ebd0c1a98bb68fe87ba7e2b3269 100644 (file)
@@ -12,6 +12,7 @@ using Content.Shared.Hands;
 using Content.Shared.Hands.Components;
 using Content.Shared.Interaction;
 using Content.Shared.Inventory;
+using Content.Shared.Item;
 using Content.Shared.Physics;
 using Content.Shared.Popups;
 using Content.Shared.Weapons.Melee.Components;
@@ -72,6 +73,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         SubscribeLocalEvent<BonusMeleeDamageComponent, GetHeavyDamageModifierEvent>(OnGetBonusHeavyDamageModifier);
         SubscribeLocalEvent<BonusMeleeAttackRateComponent, GetMeleeAttackRateEvent>(OnGetBonusMeleeAttackRate);
 
+        SubscribeLocalEvent<ItemToggleMeleeWeaponComponent, ItemToggleMeleeWeaponUpdateEvent>(OnItemToggle);
+
         SubscribeAllEvent<HeavyAttackEvent>(OnHeavyAttack);
         SubscribeAllEvent<LightAttackEvent>(OnLightAttack);
         SubscribeAllEvent<DisarmAttackEvent>(OnDisarmAttack);
@@ -849,4 +852,60 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
     }
 
     public abstract void DoLunge(EntityUid user, EntityUid weapon, Angle angle, Vector2 localPos, string? animation, bool predicted = true);
+
+    /// <summary>
+    /// Used to update the MeleeWeapon component on item toggle.
+    /// </summary>
+    private void OnItemToggle(EntityUid uid, ItemToggleMeleeWeaponComponent itemToggleMelee, ItemToggleMeleeWeaponUpdateEvent args)
+    {
+        if (!TryComp(uid, out MeleeWeaponComponent? meleeWeapon))
+            return;
+
+        if (args.Activated)
+        {
+            if (itemToggleMelee.ActivatedDamage != null)
+            {
+                //Setting deactivated damage to the weapon's regular value before changing it.
+                itemToggleMelee.DeactivatedDamage ??= meleeWeapon.Damage;
+                meleeWeapon.Damage = itemToggleMelee.ActivatedDamage;
+            }
+
+            meleeWeapon.HitSound = itemToggleMelee.ActivatedSoundOnHit;
+
+            if (itemToggleMelee.ActivatedSoundOnHitNoDamage != null)
+            {
+                //Setting the deactivated sound on no damage hit to the weapon's regular value before changing it.
+                itemToggleMelee.DeactivatedSoundOnHitNoDamage ??= meleeWeapon.NoDamageSound;
+                meleeWeapon.NoDamageSound = itemToggleMelee.ActivatedSoundOnHitNoDamage;
+            }
+
+            if (itemToggleMelee.ActivatedSoundOnSwing != null)
+            {
+                //Setting the deactivated sound on no damage hit to the weapon's regular value before changing it.
+                itemToggleMelee.DeactivatedSoundOnSwing ??= meleeWeapon.SwingSound;
+                meleeWeapon.SwingSound = itemToggleMelee.ActivatedSoundOnSwing;
+            }
+
+            if (itemToggleMelee.DeactivatedSecret)
+                meleeWeapon.Hidden = false;
+        }
+        else
+        {
+            if (itemToggleMelee.DeactivatedDamage != null)
+                meleeWeapon.Damage = itemToggleMelee.DeactivatedDamage;
+
+            meleeWeapon.HitSound = itemToggleMelee.DeactivatedSoundOnHit;
+
+            if (itemToggleMelee.DeactivatedSoundOnHitNoDamage != null)
+                meleeWeapon.NoDamageSound = itemToggleMelee.DeactivatedSoundOnHitNoDamage;
+
+            if (itemToggleMelee.DeactivatedSoundOnSwing != null)
+                meleeWeapon.SwingSound = itemToggleMelee.DeactivatedSoundOnSwing;
+
+            if (itemToggleMelee.DeactivatedSecret)
+                meleeWeapon.Hidden = true;
+        }
+
+        Dirty(uid, meleeWeapon);
+    }
 }
index 02b1c13d530a1777e1afd9139b1ed9ff321a992e..b29f498fee66b763f0dc24c303d8160f5914ee57 100644 (file)
   copyright: "User Nimfar11 on GitHub."
   source: "https://github.com/Nimfar11"
 
+- files: ["ebladehum.ogg", "eblademiss.ogg"]
+  license: "CC0-1.0"
+  copyright: "User gyzhor on freesound.org. Modified by Darkenson on github."
+  source: "https://freesound.org/people/gyzhor/sounds/47123/"
+
 - files: ["dodgeball.ogg"]
   license: "CC-BY-SA-3.0"
   copyright: "Taken from tgstation"
diff --git a/Resources/Audio/Weapons/ebladehum.ogg b/Resources/Audio/Weapons/ebladehum.ogg
new file mode 100644 (file)
index 0000000..aa885b1
Binary files /dev/null and b/Resources/Audio/Weapons/ebladehum.ogg differ
diff --git a/Resources/Audio/Weapons/eblademiss.ogg b/Resources/Audio/Weapons/eblademiss.ogg
new file mode 100644 (file)
index 0000000..5ebdbae
Binary files /dev/null and b/Resources/Audio/Weapons/eblademiss.ogg differ
index 4596085f341babae40655e2df5957cd6249bd421..c7fda9779a073c4d48406601f6ccf3fe197301cd 100644 (file)
@@ -1,4 +1,4 @@
-- type: entity
+- type: entity
   name: base shield
   parent: BaseItem
   id: BaseShield
       state: mirror-icon
     - type: Item
       heldPrefix: mirror
-    - type: Blocking #Mirror shield should reflect heat/laser eventually, but be relatively weak to everything else.
+    - type: Reflect
+      reflectProb: 0.95
+      reflects:
+        - Energy
+    - type: Blocking #Mirror shield reflects heat/laser, but is relatively weak to everything else.
       passiveBlockModifier:
         coefficients:
           Blunt: 1.2
   description: Exotic energy shield, when folded, can even fit in your pocket.
   components:
     - type: ItemToggle
-      activatedDisarmMalus: 0.6
-      activateSound:
+      soundActivate:
         path: /Audio/Weapons/ebladeon.ogg
-      deActivateSound:
+      soundDeactivate:
         path: /Audio/Weapons/ebladeoff.ogg
-      offSize: Small
+    - type: ItemToggleActiveSound
+      activeSound:
+        path: /Audio/Weapons/ebladehum.ogg
+    - type: ItemToggleSize
+      activatedSize: Huge
+    - type: ItemToggleDisarmMalus
+      activatedDisarmMalus: 0.6
     - type: Sprite
       sprite: Objects/Weapons/Melee/e_shield.rsi
       layers:
   description: An advanced riot shield made of lightweight materials that collapses for easy storage.
   components:
     - type: ItemToggle
-      activatedDisarmMalus: 0.6
-      activateSound:
+      soundActivate:
         path: /Audio/Weapons/telescopicon.ogg
         params:
           volume: -5
-      deActivateSound:
+      soundDeactivate:
         path: /Audio/Weapons/telescopicoff.ogg
         params:
           volume: -5
-      offSize: Small
+    - type: ItemToggleDisarmMalus
+      activatedDisarmMalus: 0.6
+    - type: ItemToggleSize
+      activatedSize: Huge
     - type: Sprite
       sprite: Objects/Weapons/Melee/teleriot_shield.rsi
       layers:
index a3b6b3fd6e8182ebe93aa13e969a18a9e60dd998..7d3ede8eafd8fc59831c48b6ec8f33eaba31e168 100644 (file)
@@ -4,45 +4,73 @@
   id: Lighter
   description: "A simple plastic cigarette lighter."
   components:
-  # Sloth: What is this comment ?????????
-  - type: RandomSprite #this has to be before sprite component for the flame to toggle right because weldercomponent behaves weird (and i dont trust myself to fix it right)
+  - type: ItemToggle
+    predictable: false
+    soundActivate:
+      collection: lighterOnSounds
+    soundDeactivate:
+      collection: lighterOffSounds
+  - type: ItemToggleMeleeWeapon
+    activatedDamage:
+        types:
+            Heat: 1
+  - type: ItemToggleSize
+    activatedSize: Small
+  - type: ItemToggleHot
+  - type: Sprite
+    sprite: Objects/Tools/lighters.rsi
+    layers:
+    - state: icon_map
+    - state: cheap_icon_base
+      map: [ "skin" ]
+    - state: basic_icon_top
+    - state: lighter_flame
+      visible: false
+      shader: unshaded
+      map: [ "flame" ]
+  - type: Appearance
+  - type: RandomSprite
     available:
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-1: ""
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-2: ""
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-3: ""
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-4: ""
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-5: ""
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-6: ""
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-7: ""
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-8: ""
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-9: ""
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-10: ""
-      - enum.WelderLayers.Base:
+      - skin:
           basic_icon_base-11: ""
-  - type: Sprite
-    sprite: Objects/Tools/lighters.rsi
-    layers:
-    - state: icon_map
-      map: ["enum.WelderLayers.Base"]
-    - state: lighter_flame
-      map: ["enum.WelderLayers.Flame"]
-      shader: unshaded
-      visible: false
-    - state: basic_icon_top
+  - type: GenericVisualizer
+    visuals:
+      enum.ToggleVisuals.Toggled:
+        flame:
+          True: { visible: true }
+          False: { visible: false }
+  - type: ToggleableLightVisuals
+    spriteLayer: lighter_flame
+    inhandVisuals:
+      left:
+      - state: inhand-left-flame
+        shader: unshaded
+      right:
+      - state: inhand-right-flame
+        shader: unshaded
   - type: Item
     size: Tiny
     sprite: Objects/Tools/lighters.rsi
-    heldPrefix: off
   - type: ItemCooldown
   - type: RefillableSolution
     solution: Welder
   - type: MeleeWeapon
     damage:
       types:
-        Blunt: 0 #this feels hacky, but is needed for burn damage while active (i think)
+        Blunt: 0
   - type: Welder
     fuelConsumption: 0.01
     fuelLitCost: 0.1
-    litMeleeDamageBonus:
-      types:
-        Heat: 1
     tankSafe: true
-    welderOnSounds:
-      collection: lighterOnSounds
-    welderOffSounds:
-      collection: lighterOffSounds
   - type: PointLight
     enabled: false
+    netsync: false
     radius: 1.1 #smallest possible
     color: orange
-  - type: Appearance
-
 
 - type: entity
   name: cheap lighter
   id: CheapLighter
   description: "A dangerously inexpensive plastic lighter, don't burn your thumb!"
   components:
-  - type: RandomSprite
-    available:
-      - enum.WelderLayers.Base:
-          cheap_icon_base: Rainbow
   - type: Sprite
     sprite: Objects/Tools/lighters.rsi
     layers:
     - state: icon_map
-      map: ["enum.WelderLayers.Base"]
+    - state: cheap_icon_base
+      map: [ "skin" ]
+    - state: cheap_icon_top
     - state: lighter_flame
-      map: ["enum.WelderLayers.Flame"]
-      shader: unshaded
       visible: false
-    - state: cheap_icon_top
+      shader: unshaded
+      map: [ "flame" ]
+  - type: RandomSprite
+    available:
+      - skin:
+          cheap_icon_base: Rainbow
   - type: SolutionContainerManager
     solutions:
       Welder:
         - ReagentId: WeldingFuel
           Quantity: 4
         maxVol: 4 #uses less fuel than a welder, so this isnt as bad as it looks
-
-    #TODO: zippos
index caa0d4c20888e0a2dd8135132abe5b87456aff55..ccfbca824620bf5cf0ed336df91bf718b64ef07c 100644 (file)
     sprite: Objects/Tools/welder.rsi
     layers:
     - state: icon
-      map: ["enum.WelderLayers.Base"]
     - state: welder_flame
-      map: ["enum.WelderLayers.Flame"]
-      shader: unshaded
       visible: false
+      shader: unshaded
+      map: ["enum.ToggleVisuals.Layer"]
+  - type: GenericVisualizer
+    visuals:
+      enum.ToggleVisuals.Toggled:
+        enum.ToggleVisuals.Layer:
+          True: { visible: true }
+          False: { visible: false }
   - type: Item
     size: Small
     sprite: Objects/Tools/welder.rsi
+  - type: ItemToggle
+    predictable: false
+    soundActivate:
+      collection: WelderOn
+      params:
+        variation: 0.125
+        volume: -5
+    soundDeactivate:
+      collection: WelderOff
+      params:
+        variation: 0.125
+        volume: -5
+  - type: ItemToggleMeleeWeapon
+    activatedSoundOnHit:
+      path: /Audio/Weapons/eblade1.ogg
+      params:
+        variation: 0.250
+        volume: -10
+    activatedSoundOnHitNoDamage:
+      path: /Audio/Weapons/eblade1.ogg
+      params:
+        variation: 0.250
+        volume: -12
+    activatedDamage:
+        types:
+            Heat: 8
+  - type: ItemToggleSize
+    activatedSize: Large
+  - type: ItemToggleHot
+  - type: ItemToggleDisarmMalus
+    activatedDisarmMalus: 0.6
   - type: ToggleableLightVisuals
     spriteLayer: flame
     inhandVisuals:
       collection: Welder
     qualities: Welding
   - type: Welder
-    litMeleeDamageBonus:
-      types:
-        Heat: 8
-        Blunt: -5
   - type: PointLight
     enabled: false
     radius: 1.5
index b2b7bfd20ca7542b377eaa366eef6291045d8a4d..a070a5519993b469f3316fca9f95e2c2f7a105d0 100644 (file)
@@ -5,13 +5,39 @@
   description: A very loud & dangerous sword with a beam made of pure, concentrated plasma. Cuts through unarmored targets like butter.
   components:
   - type: EnergySword
-    litDamageBonus:
+  - type: ItemToggle
+    soundActivate:
+      path: /Audio/Weapons/ebladeon.ogg
+    soundDeactivate:
+      path: /Audio/Weapons/ebladeoff.ogg
+  - type: ItemToggleActiveSound
+    activeSound:
+      path: /Audio/Weapons/ebladehum.ogg
+  - type: ItemToggleSharp
+  - type: ItemToggleHot
+  - type: ItemToggleDisarmMalus
+    activatedDisarmMalus: 0.6
+  - type: ItemToggleSize
+    activatedSize: Huge
+  - type: ItemToggleMeleeWeapon
+    activatedSoundOnHit:
+      path: /Audio/Weapons/eblade1.ogg
+      params:
+        variation: 0.250
+    activatedSoundOnHitNoDamage:
+      path: /Audio/Weapons/eblade1.ogg
+      params:
+        variation: 0.250
+        volume: -10
+    activatedSoundOnSwing:
+      path: /Audio/Weapons/eblademiss.ogg
+      params:
+        variation: 0.125
+    activatedDamage:
         types:
             Slash: 15
             Heat: 15
             Structural: 20
-            Blunt: -4.5
-    litDisarmMalus: 0.6
   - type: Sprite
     sprite: Objects/Weapons/Melee/e_sword.rsi
     layers:
@@ -24,8 +50,6 @@
   - type: MeleeWeapon
     wideAnimationRotation: -135
     attackRate: 1
-    soundHit:
-      path: /Audio/Weapons/eblade1.ogg
     damage:
       types:
         Blunt: 4.5
   description: 'A dark ink pen.'
   components:
   - type: EnergySword
-    secret: true
-    litDamageBonus:
-        types:
-            Slash: 10
-            Heat: 10
-            Blunt: -1
-    litDisarmMalus: 0.4
-    activateSound: !type:SoundPathSpecifier
+  - type: ItemToggle
+    soundActivate:
       path: /Audio/Weapons/ebladeon.ogg
       params:
         volume: -6
-    deActivateSound: !type:SoundPathSpecifier
+    soundDeactivate:
       path: /Audio/Weapons/ebladeoff.ogg
       params:
         volume: -6
+  - type: ItemToggleMeleeWeapon
+    activatedSoundOnSwing:
+      path: /Audio/Weapons/eblademiss.ogg
+      params:
+        volume: -6
+        variation: 0.250
+    activatedDamage:
+        types:
+            Slash: 10
+            Heat: 10
+    deactivatedSecret: true
+  - type: ItemToggleActiveSound
+    activeSound:
+      path: /Audio/Weapons/ebladehum.ogg
+      params:
+        volume: -6
+  - type: ItemToggleDisarmMalus
+    activatedDisarmMalus: 0.4
   - type: Sprite
     sprite: Objects/Weapons/Melee/e_dagger.rsi
     layers:
   description: An exotic energy weapon.
   components:
   - type: EnergySword
-    secret: true
-    litDamageBonus:
+  - type: ItemToggleMeleeWeapon
+    activatedDamage:
         types:
             Slash: 10
             Heat: 12
-            Blunt: -1
-    litDisarmMalus: 0.6
+    deactivatedSecret: true
+  - type: ItemToggleDisarmMalus
+    activatedDisarmMalus: 0.6
   - type: Sprite
     sprite: Objects/Weapons/Melee/e_cutlass.rsi
     layers:
   id: EnergySwordDouble
   description: Syndicate Command Interns thought that having one blade on the energy sword was not enough. This can be stored in pockets.
   components:
-  - type: Wieldable
   - type: EnergySword
-    litDamageBonus:
+  - type: ItemToggle
+    soundActivate:
+      path: /Audio/Weapons/ebladeon.ogg
+      params:
+        volume: 3
+    soundDeactivate:
+      path: /Audio/Weapons/ebladeoff.ogg
+      params:
+        volume: 3
+  - type: ItemToggleMeleeWeapon
+    activatedSoundOnSwing:
+      path: /Audio/Weapons/eblademiss.ogg
+      params:
+        volume: 3
+        variation: 0.250
+    activatedDamage:
         types:
             Slash: 12
             Heat: 12
             Structural: 15
-            Blunt: -4.5
-    litDisarmMalus: 0.7
+  - type: ItemToggleActiveSound
+    activeSound:
+      path: /Audio/Weapons/ebladehum.ogg
+      params:
+        volume: 3
+  - type: ItemToggleDisarmMalus
+    activatedDisarmMalus: 0.7
+  - type: Wieldable
   - type: MeleeWeapon
     wideAnimationRotation: -135
     attackRate: 1.5
     angle: 100
-    soundHit:
-      path: /Audio/Weapons/eblade1.ogg
     damage:
       types:
         Blunt: 4.5
     size: Small
     sprite: Objects/Weapons/Melee/e_sword_double-inhands.rsi
   - type: Reflect
-    enabled: true
     reflectProb: .75
     spread: 75
   - type: UseDelay
index b43ea5059c4198657277bd6d14e71fbc01ff7c66..e6ecbe059ff9ad108318ece659d3eff251a0f0f9 100644 (file)
@@ -9,6 +9,23 @@
     layers:
     - state: stunprod_off
       map: [ "enum.ToggleVisuals.Layer" ]
+  - type: ItemToggle
+    soundActivate:
+      collection: sparks
+      params:
+        variation: 0.250
+    soundDeactivate:
+      collection: sparks
+      params:
+        variation: 0.250
+    soundFailToActivate:
+      path: /Audio/Machines/button.ogg
+      params:
+        variation: 0.250
+  - type: ItemToggleMeleeWeapon
+    activatedDamage:
+      types:
+        Blunt: 0
   - type: Stunbaton
     energyPerUse: 70
   - type: MeleeWeapon
@@ -37,7 +54,7 @@
   - type: Appearance
   - type: GenericVisualizer
     visuals:
-     enum.ToggleVisuals.Toggled:
+      enum.ToggleVisuals.Toggled:
         enum.ToggleVisuals.Layer:
           True: {state: stunprod_on}
           False: {state: stunprod_off}
index bf4a155ee47e151f33ccb67c646d6d968a2efb22..d55a396164a31cdee253d9c665dccfb3aa60217f 100644 (file)
       map: [ "enum.ToggleVisuals.Layer" ]
   - type: Stunbaton
     energyPerUse: 50
+  - type: ItemToggle
+    predictable: false
+    soundActivate:
+      collection: sparks
+      params:
+        variation: 0.250
+    soundDeactivate:
+      collection: sparks
+      params:
+        variation: 0.250
+    soundFailToActivate:
+      path: /Audio/Machines/button.ogg
+      params:
+        variation: 0.250
+  - type: ItemToggleMeleeWeapon
+    activatedDamage:
+      types:
+        Blunt: 0
   - type: MeleeWeapon
     wideAnimationRotation: -135
     damage:
index e1c6d6e74922136d642043b3db019175631660da..7617a25de9a1f11e512258b3049979ae6457d280 100644 (file)
       "name": "zippo_top"
     },
     {
-      "name": "off-inhand-left",
+      "name": "inhand-left",
       "directions": 4
     },
     {
-      "name": "off-inhand-right",
+      "name": "inhand-right",
       "directions": 4
     },
     {
-      "name": "on-inhand-left",
+      "name": "inhand-left-flame",
       "directions": 4
     },
     {
-      "name": "on-inhand-right",
+      "name": "inhand-right-flame",
       "directions": 4
     }