ItemToggle system expansion (#22369)

* 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>
This commit is contained in:
Darkie
2023-12-24 08:11:05 +02:00
committed by GitHub
parent 1de682e23f
commit a3fbab84e6
47 changed files with 1188 additions and 810 deletions

View File

@@ -0,0 +1,9 @@
using Content.Shared.Item.ItemToggle;
namespace Content.Shared.Item;
/// <inheritdoc/>
public sealed class ItemToggleSystem : SharedItemToggleSystem
{
}

View File

@@ -1,7 +1,7 @@
using Content.Client.Items; using Content.Client.Items;
using Content.Client.Tools.Components; using Content.Client.Tools.Components;
using Content.Client.Tools.UI; using Content.Client.Tools.UI;
using Content.Shared.Tools; using Content.Shared.Item;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
@@ -50,7 +50,7 @@ namespace Content.Client.Tools
private void OnWelderGetStatusMessage(EntityUid uid, WelderComponent component, ItemStatusCollectMessage args) 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) private void OnWelderHandleState(EntityUid uid, WelderComponent welder, ref ComponentHandleState args)
@@ -60,7 +60,6 @@ namespace Content.Client.Tools
welder.FuelCapacity = state.FuelCapacity; welder.FuelCapacity = state.FuelCapacity;
welder.Fuel = state.Fuel; welder.Fuel = state.Fuel;
welder.Lit = state.Lit;
welder.UiUpdateNeeded = true; welder.UiUpdateNeeded = true;
} }

View File

@@ -1,6 +1,7 @@
using Content.Client.Message; using Content.Client.Message;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Client.Tools.Components; using Content.Client.Tools.Components;
using Content.Shared.Item;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -9,13 +10,19 @@ namespace Content.Client.Tools.UI;
public sealed class WelderStatusControl : Control public sealed class WelderStatusControl : Control
{ {
[Dependency] private readonly IEntityManager _entMan = default!;
private readonly WelderComponent _parent; private readonly WelderComponent _parent;
private readonly ItemToggleComponent? _toggleComponent;
private readonly RichTextLabel _label; private readonly RichTextLabel _label;
public WelderStatusControl(WelderComponent parent) public WelderStatusControl(WelderComponent parent, EntityUid? uid = null)
{ {
_parent = parent; _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); AddChild(_label);
UpdateDraw(); UpdateDraw();
@@ -39,7 +46,11 @@ public sealed class WelderStatusControl : Control
var fuelCap = _parent.FuelCapacity; var fuelCap = _parent.FuelCapacity;
var fuel = _parent.Fuel; 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", _label.SetMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
("colorName", fuel < fuelCap / 4f ? "darkorange" : "orange"), ("colorName", fuel < fuelCap / 4f ? "darkorange" : "orange"),

View File

@@ -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);
}
}
}

View File

@@ -10,8 +10,8 @@ using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Construction.Components; using Content.Server.Construction.Components;
using Content.Server.Gravity; using Content.Server.Gravity;
using Content.Server.Item;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Tools.Components;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Construction.Prototypes; using Content.Shared.Construction.Prototypes;
using Content.Shared.Gravity; using Content.Shared.Gravity;
@@ -164,7 +164,7 @@ public abstract partial class InteractionTest
// spawn and pick up the new item // spawn and pick up the new item
var item = await SpawnEntity(entity, SEntMan.GetCoordinates(PlayerCoords)); var item = await SpawnEntity(entity, SEntMan.GetCoordinates(PlayerCoords));
WelderComponent? welder = null; ItemToggleComponent? itemToggle = null;
await Server.WaitPost(() => await Server.WaitPost(() =>
{ {
@@ -173,14 +173,16 @@ public abstract partial class InteractionTest
Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHand, false, false, Hands)); Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHand, false, false, Hands));
// turn on welders // turn on welders
if (enableWelder && SEntMan.TryGetComponent(item, out welder) && !welder.Lit) if (enableWelder && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated)
Assert.That(ToolSys.TryTurnWelderOn(item, playerEnt, welder)); {
Assert.That(ItemToggleSys.TryActivate(item, playerEnt, itemToggle: itemToggle));
}
}); });
await RunTicks(1); await RunTicks(1);
Assert.That(Hands.ActiveHandEntity, Is.EqualTo(item)); Assert.That(Hands.ActiveHandEntity, Is.EqualTo(item));
if (enableWelder && welder != null) if (enableWelder && itemToggle != null)
Assert.That(welder.Lit); Assert.That(itemToggle.Activated);
return item; return item;
} }

View File

@@ -12,6 +12,7 @@ using Content.Shared.DoAfter;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Server.Item;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Players; using Content.Shared.Players;
using Robust.Client.Input; using Robust.Client.Input;
@@ -23,6 +24,7 @@ using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.UnitTesting; using Robust.UnitTesting;
using Content.Shared.Item.ItemToggle;
namespace Content.IntegrationTests.Tests.Interaction; namespace Content.IntegrationTests.Tests.Interaction;
@@ -97,6 +99,7 @@ public abstract partial class InteractionTest
protected Content.Server.Construction.ConstructionSystem SConstruction = default!; protected Content.Server.Construction.ConstructionSystem SConstruction = default!;
protected SharedDoAfterSystem DoAfterSys = default!; protected SharedDoAfterSystem DoAfterSys = default!;
protected ToolSystem ToolSys = default!; protected ToolSystem ToolSys = default!;
protected SharedItemToggleSystem ItemToggleSys = default!;
protected InteractionTestSystem STestSystem = default!; protected InteractionTestSystem STestSystem = default!;
protected SharedTransformSystem Transform = default!; protected SharedTransformSystem Transform = default!;
protected ISawmill SLogger = default!; protected ISawmill SLogger = default!;
@@ -152,6 +155,7 @@ public abstract partial class InteractionTest
HandSys = SEntMan.System<SharedHandsSystem>(); HandSys = SEntMan.System<SharedHandsSystem>();
InteractSys = SEntMan.System<SharedInteractionSystem>(); InteractSys = SEntMan.System<SharedInteractionSystem>();
ToolSys = SEntMan.System<ToolSystem>(); ToolSys = SEntMan.System<ToolSystem>();
ItemToggleSys = SEntMan.System<SharedItemToggleSystem>();
DoAfterSys = SEntMan.System<SharedDoAfterSystem>(); DoAfterSys = SEntMan.System<SharedDoAfterSystem>();
Transform = SEntMan.System<SharedTransformSystem>(); Transform = SEntMan.System<SharedTransformSystem>();
SConstruction = SEntMan.System<Server.Construction.ConstructionSystem>(); SConstruction = SEntMan.System<Server.Construction.ConstructionSystem>();

View File

@@ -1,6 +1,7 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Damage.Components; using Content.Server.Damage.Components;
using Content.Server.Tools.Components; using Content.Server.Tools.Components;
using Content.Shared.Item;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Interaction; using Content.Shared.Interaction;
@@ -11,7 +12,7 @@ namespace Content.Server.Damage.Systems
public sealed class DamageOnToolInteractSystem : EntitySystem public sealed class DamageOnToolInteractSystem : EntitySystem
{ {
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger= default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -25,16 +26,19 @@ namespace Content.Server.Damage.Systems
if (args.Handled) if (args.Handled)
return; return;
if (!TryComp<ItemToggleComponent>(uid, out var itemToggle))
return;
if (component.WeldingDamage is {} weldingDamage if (component.WeldingDamage is {} weldingDamage
&& EntityManager.TryGetComponent(args.Used, out WelderComponent? welder) && EntityManager.TryGetComponent(args.Used, out WelderComponent? welder)
&& welder.Lit && itemToggle.Activated
&& !welder.TankSafe) && !welder.TankSafe)
{ {
var dmg = _damageableSystem.TryChangeDamage(args.Target, weldingDamage, origin: args.User); var dmg = _damageableSystem.TryChangeDamage(args.Target, weldingDamage, origin: args.User);
if (dmg != null) if (dmg != null)
_adminLogger.Add(LogType.Damaged, _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; args.Handled = true;
} }
@@ -46,7 +50,7 @@ namespace Content.Server.Damage.Systems
if (dmg != null) if (dmg != null)
_adminLogger.Add(LogType.Damaged, _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; args.Handled = true;
} }

View File

@@ -1,10 +1,10 @@
using Content.Shared.Eye.Blinding;
using Content.Shared.StatusEffect; using Content.Shared.StatusEffect;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Server.Tools; using Content.Shared.Item;
using Content.Shared.Eye.Blinding.Components; using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Eye.Blinding.Systems; using Content.Shared.Eye.Blinding.Systems;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Content.Shared.Item.ItemToggle;
namespace Content.Server.Eye.Blinding.EyeProtection 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 StatusEffectsSystem _statusEffectsSystem = default!;
[Dependency] private readonly BlindableSystem _blindingSystem = default!; [Dependency] private readonly BlindableSystem _blindingSystem = default!;
[Dependency] private readonly SharedItemToggleSystem _itemToggle = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<RequiresEyeProtectionComponent, ToolUseAttemptEvent>(OnUseAttempt); SubscribeLocalEvent<RequiresEyeProtectionComponent, ToolUseAttemptEvent>(OnUseAttempt);
SubscribeLocalEvent<RequiresEyeProtectionComponent, WelderToggledEvent>(OnWelderToggled); SubscribeLocalEvent<RequiresEyeProtectionComponent, ItemToggleDoneEvent>(OnWelderToggled);
SubscribeLocalEvent<EyeProtectionComponent, GetEyeProtectionEvent>(OnGetProtection); SubscribeLocalEvent<EyeProtectionComponent, GetEyeProtectionEvent>(OnGetProtection);
SubscribeLocalEvent<EyeProtectionComponent, InventoryRelayedEvent<GetEyeProtectionEvent>>(OnGetRelayedProtection); SubscribeLocalEvent<EyeProtectionComponent, InventoryRelayedEvent<GetEyeProtectionEvent>>(OnGetRelayedProtection);
@@ -44,7 +46,7 @@ namespace Content.Server.Eye.Blinding.EyeProtection
var ev = new GetEyeProtectionEvent(); var ev = new GetEyeProtectionEvent();
RaiseLocalEvent(args.User, ev); RaiseLocalEvent(args.User, ev);
var time = (float) (component.StatusEffectTime- ev.Protection).TotalSeconds; var time = (float) (component.StatusEffectTime - ev.Protection).TotalSeconds;
if (time <= 0) if (time <= 0)
return; return;
@@ -55,9 +57,9 @@ namespace Content.Server.Eye.Blinding.EyeProtection
_statusEffectsSystem.TryAddStatusEffect(args.User, TemporaryBlindnessSystem.BlindingStatusEffect, _statusEffectsSystem.TryAddStatusEffect(args.User, TemporaryBlindnessSystem.BlindingStatusEffect,
statusTimeSpan, false, 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);
} }
} }
} }

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -2,45 +2,39 @@ using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Server.Power.Events; using Content.Server.Power.Events;
using Content.Server.Stunnable.Components; using Content.Server.Stunnable.Components;
using Content.Shared.Audio;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Damage.Events; using Content.Shared.Damage.Events;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Interaction.Events;
using Content.Shared.Item; using Content.Shared.Item;
using Content.Shared.Item.ItemToggle;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Stunnable; 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 namespace Content.Server.Stunnable.Systems
{ {
public sealed class StunbatonSystem : SharedStunbatonSystem public sealed class StunbatonSystem : SharedStunbatonSystem
{ {
[Dependency] private readonly SharedItemSystem _item = default!; [Dependency] private readonly SharedItemSystem _item = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly RiggableSystem _riggableSystem = default!; [Dependency] private readonly RiggableSystem _riggableSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly BatterySystem _battery = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedItemToggleSystem _itemToggle = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<StunbatonComponent, UseInHandEvent>(OnUseInHand); SubscribeLocalEvent<BatteryComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<StunbatonComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<StunbatonComponent, SolutionChangedEvent>(OnSolutionChange); SubscribeLocalEvent<StunbatonComponent, SolutionChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt); SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt);
SubscribeLocalEvent<StunbatonComponent, ItemToggleActivateAttemptEvent>(TryTurnOn);
SubscribeLocalEvent<StunbatonComponent, ItemToggleDoneEvent>(ToggleDone);
} }
private void OnStaminaHitAttempt(EntityUid uid, StunbatonComponent component, ref StaminaDamageOnHitAttemptEvent args) private void OnStaminaHitAttempt(EntityUid uid, StunbatonComponent component, ref StaminaDamageOnHitAttemptEvent args)
{ {
if (!component.Activated || if (!_itemToggle.IsActivated(uid) ||
!TryComp<BatteryComponent>(uid, out var battery) || !_battery.TryUseCharge(uid, component.EnergyPerUse, battery)) !TryComp<BatteryComponent>(uid, out var battery) || !_battery.TryUseCharge(uid, component.EnergyPerUse, battery))
{ {
args.Cancelled = true; args.Cancelled = true;
return; return;
@@ -48,93 +42,56 @@ namespace Content.Server.Stunnable.Systems
if (battery.CurrentCharge < component.EnergyPerUse) if (battery.CurrentCharge < component.EnergyPerUse)
{ {
_audio.PlayPvs(component.SparksSound, uid, AudioHelpers.WithVariation(0.25f)); _itemToggle.Toggle(uid, predicted: false);
TurnOff(uid, component);
} }
} }
private void OnUseInHand(EntityUid uid, StunbatonComponent comp, UseInHandEvent args) private void OnExamined(EntityUid uid, BatteryComponent battery, ExaminedEvent args)
{ {
if (comp.Activated) var onMsg = _itemToggle.IsActivated(uid)
{ ? Loc.GetString("comp-stunbaton-examined-on")
TurnOff(uid, comp); : Loc.GetString("comp-stunbaton-examined-off");
} args.PushMarkup(onMsg);
else
{ var chargeMessage = Loc.GetString("stunbaton-component-on-examine-charge",
TurnOn(uid, comp, args.User); ("charge", (int) (battery.CurrentCharge / battery.MaxCharge * 100)));
} args.PushMarkup(chargeMessage);
} }
private void OnExamined(EntityUid uid, StunbatonComponent comp, ExaminedEvent args) private void ToggleDone(EntityUid uid, StunbatonComponent comp, ref ItemToggleDoneEvent args)
{ {
var msg = comp.Activated if (!TryComp<ItemComponent>(uid, out var item))
? 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))));
}
}
private void TurnOff(EntityUid uid, StunbatonComponent comp)
{
if (!comp.Activated)
return; return;
_item.SetHeldPrefix(uid, args.Activated ? "on" : "off", item);
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);
} }
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) if (!TryComp<BatteryComponent>(uid, out var battery) || battery.CurrentCharge < comp.EnergyPerUse)
{ {
args.Cancelled = true;
_audio.PlayPvs(comp.TurnOnFailSound, uid, AudioHelpers.WithVariation(0.25f)); if (args.User != null)
_popup.PopupEntity(Loc.GetString("stunbaton-component-low-charge"), user, user); {
_popup.PopupEntity(Loc.GetString("stunbaton-component-low-charge"), (EntityUid) args.User, (EntityUid) args.User);
}
return; return;
} }
if (TryComp<RiggableComponent>(uid, out var rig) && rig.IsRigged) 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 // https://github.com/space-wizards/space-station-14/pull/17288#discussion_r1241213341
private void OnSolutionChange(EntityUid uid, StunbatonComponent component, SolutionChangedEvent args) private void OnSolutionChange(EntityUid uid, StunbatonComponent component, SolutionChangedEvent args)
{ {
// Explode if baton is activated and rigged. // 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; return;
if (component.Activated && riggable.IsRigged) if (_itemToggle.IsActivated(uid) && riggable.IsRigged)
_riggableSystem.Explode(uid, battery); _riggableSystem.Explode(uid, battery);
} }

View File

@@ -1,9 +1,8 @@
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Prototypes;
namespace Content.Server.Tools.Components namespace Content.Server.Tools.Components
{ {
@@ -13,56 +12,37 @@ namespace Content.Server.Tools.Components
/// <summary> /// <summary>
/// Solution on the entity that contains the fuel. /// Solution on the entity that contains the fuel.
/// </summary> /// </summary>
[DataField("fuelSolution"), ViewVariables(VVAccess.ReadWrite)] [DataField, ViewVariables(VVAccess.ReadWrite)]
public string FuelSolution { get; private set; } = "Welder"; public string FuelSolution { get; private set; } = "Welder";
/// <summary> /// <summary>
/// Reagent that will be used as fuel for welding. /// Reagent that will be used as fuel for welding.
/// </summary> /// </summary>
[DataField("fuelReagent", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>)), ViewVariables(VVAccess.ReadWrite)] [DataField, ViewVariables(VVAccess.ReadWrite)]
public string FuelReagent { get; private set; } = "WeldingFuel"; public ProtoId<ReagentPrototype> FuelReagent { get; private set; } = "WeldingFuel";
/// <summary> /// <summary>
/// Fuel consumption per second, while the welder is active. /// Fuel consumption per second while the welder is active.
/// </summary> /// </summary>
[DataField("fuelConsumption"), ViewVariables(VVAccess.ReadWrite)] [DataField, ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 FuelConsumption { get; private set; } = FixedPoint2.New(2.0f); public FixedPoint2 FuelConsumption { get; private set; } = FixedPoint2.New(2.0f);
/// <summary> /// <summary>
/// A fuel amount to be consumed when the welder goes from being unlit to being lit. /// A fuel amount to be consumed when the welder goes from being unlit to being lit.
/// </summary> /// </summary>
[DataField("fuelLitCost"), ViewVariables(VVAccess.ReadWrite)] [DataField, ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 FuelLitCost { get; private set; } = FixedPoint2.New(0.5f); public FixedPoint2 FuelLitCost { get; private set; } = FixedPoint2.New(0.5f);
/// <summary> /// <summary>
/// Sound played when the welder is turned off. /// Sound played when refilling the welder.
/// </summary> /// </summary>
[DataField("welderOffSounds")] [DataField]
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")]
public SoundSpecifier WelderRefill { get; private set; } = new SoundPathSpecifier("/Audio/Effects/refill.ogg"); 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> /// <summary>
/// Whether the item is safe to refill while lit without exploding the tank. /// Whether the item is safe to refill while lit without exploding the tank.
/// </summary> /// </summary>
[DataField("tankSafe")] [DataField]
public bool TankSafe = false; //I have no idea what I'm doing public bool TankSafe = false; //I have no idea what I'm doing
} }

View File

@@ -2,24 +2,21 @@ using System.Linq;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Tools.Components; using Content.Server.Tools.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Item; using Content.Shared.Item;
using Content.Shared.Temperature; using Content.Shared.Item.ItemToggle;
using Content.Shared.Toggleable;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Server.Tools namespace Content.Server.Tools
{ {
public sealed partial class ToolSystem public sealed partial class ToolSystem
{ {
[Dependency] private readonly SharedItemToggleSystem _itemToggle = default!;
private readonly HashSet<EntityUid> _activeWelders = new(); private readonly HashSet<EntityUid> _activeWelders = new();
private const float WelderUpdateTimer = 1f; private const float WelderUpdateTimer = 1f;
@@ -27,106 +24,51 @@ namespace Content.Server.Tools
public void InitializeWelders() public void InitializeWelders()
{ {
SubscribeLocalEvent<WelderComponent, ComponentStartup>(OnWelderStartup);
SubscribeLocalEvent<WelderComponent, IsHotEvent>(OnWelderIsHotEvent);
SubscribeLocalEvent<WelderComponent, ExaminedEvent>(OnWelderExamine); SubscribeLocalEvent<WelderComponent, ExaminedEvent>(OnWelderExamine);
SubscribeLocalEvent<WelderComponent, SolutionChangedEvent>(OnWelderSolutionChange);
SubscribeLocalEvent<WelderComponent, ActivateInWorldEvent>(OnWelderActivate);
SubscribeLocalEvent<WelderComponent, AfterInteractEvent>(OnWelderAfterInteract); SubscribeLocalEvent<WelderComponent, AfterInteractEvent>(OnWelderAfterInteract);
SubscribeLocalEvent<WelderComponent, DoAfterAttemptEvent<ToolDoAfterEvent>>(OnWelderToolUseAttempt); SubscribeLocalEvent<WelderComponent, DoAfterAttemptEvent<ToolDoAfterEvent>>(OnWelderToolUseAttempt);
SubscribeLocalEvent<WelderComponent, ComponentShutdown>(OnWelderShutdown); SubscribeLocalEvent<WelderComponent, ComponentShutdown>(OnWelderShutdown);
SubscribeLocalEvent<WelderComponent, ComponentGetState>(OnWelderGetState); SubscribeLocalEvent<WelderComponent, ComponentGetState>(OnWelderGetState);
SubscribeLocalEvent<WelderComponent, GetMeleeDamageEvent>(OnGetMeleeDamage); SubscribeLocalEvent<WelderComponent, ItemToggleActivateAttemptEvent>(TryTurnOn);
} SubscribeLocalEvent<WelderComponent, ItemToggleDeactivateAttemptEvent>(TurnOff);
private void OnGetMeleeDamage(EntityUid uid, WelderComponent component, ref GetMeleeDamageEvent args)
{
if (component.Lit)
args.Damage += component.LitMeleeDamageBonus;
} }
public (FixedPoint2 fuel, FixedPoint2 capacity) GetWelderFuelAndCapacity(EntityUid uid, WelderComponent? welder = null, SolutionContainerManagerComponent? solutionContainer = null) public (FixedPoint2 fuel, FixedPoint2 capacity) GetWelderFuelAndCapacity(EntityUid uid, WelderComponent? welder = null, SolutionContainerManagerComponent? solutionContainer = null)
{ {
if (!Resolve(uid, ref welder, ref solutionContainer) 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 (FixedPoint2.Zero, FixedPoint2.Zero);
return (_solutionContainerSystem.GetTotalPrototypeQuantity(uid, welder.FuelReagent), fuelSolution.MaxVolume); return (_solutionContainer.GetTotalPrototypeQuantity(uid, welder.FuelReagent), fuelSolution.MaxVolume);
} }
public bool TryToggleWelder(EntityUid uid, EntityUid? user, public void TryTurnOn(EntityUid uid, WelderComponent welder, ref ItemToggleActivateAttemptEvent args)
WelderComponent? welder = null,
SolutionContainerManagerComponent? solutionContainer = null,
ItemComponent? item = null,
SharedPointLightComponent? light = null,
AppearanceComponent? appearance = null)
{ {
// Right now, we only need the welder. if (!_solutionContainer.TryGetSolution(uid, welder.FuelSolution, out var solution) ||
// So let's not unnecessarily resolve components !TryComp<TransformComponent>(uid, out var transform))
if (!Resolve(uid, ref welder)) {
return false; args.Cancelled = true;
return;
return !welder.Lit }
? TryTurnWelderOn(uid, user, welder, solutionContainer, item, light, appearance)
: TryTurnWelderOff(uid, user, welder, item, light, appearance);
}
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)
{
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;
var fuel = solution.GetTotalPrototypeQuantity(welder.FuelReagent); var fuel = solution.GetTotalPrototypeQuantity(welder.FuelReagent);
// Not enough fuel to lit welder. // Not enough fuel to lit welder.
if (fuel == FixedPoint2.Zero || fuel < welder.FuelLitCost) if (fuel == FixedPoint2.Zero || fuel < welder.FuelLitCost)
{ {
if(user != null) if (args.User != null)
_popupSystem.PopupEntity(Loc.GetString("welder-component-no-fuel-message"), uid, user.Value); {
return false; _popup.PopupEntity(Loc.GetString("welder-component-no-fuel-message"), uid, (EntityUid) args.User);
}
args.Cancelled = true;
return;
} }
solution.RemoveReagent(welder.FuelReagent, welder.FuelLitCost); solution.RemoveReagent(welder.FuelReagent, welder.FuelLitCost);
welder.Lit = true;
// Logging // Logging
if (user != null) _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(uid):welder} on");
_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); if (transform.GridUid is { } gridUid)
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);
}
_audioSystem.PlayPvs(welder.WelderOnSounds, uid, AudioParams.Default.WithVariation(0.125f).WithVolume(-5f));
if (transform.GridUid is {} gridUid)
{ {
var position = _transformSystem.GetGridOrMapTilePosition(uid, transform); var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
_atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, uid, true); _atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, uid, true);
@@ -135,68 +77,21 @@ namespace Content.Server.Tools
Dirty(uid, welder); Dirty(uid, welder);
_activeWelders.Add(uid); _activeWelders.Add(uid);
return true;
} }
public bool TryTurnWelderOff(EntityUid uid, EntityUid? user, public void TurnOff(EntityUid uid, WelderComponent welder, ref ItemToggleDeactivateAttemptEvent args)
WelderComponent? welder = null,
ItemComponent? item = null,
SharedPointLightComponent? light = null,
AppearanceComponent? appearance = null)
{ {
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 // Logging
if (user != null) _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(uid):welder} off");
_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));
Dirty(uid, welder); Dirty(uid, welder);
_activeWelders.Remove(uid); _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) 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")); 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) private void OnWelderAfterInteract(EntityUid uid, WelderComponent welder, AfterInteractEvent args)
{ {
if (args.Handled) if (args.Handled)
return; return;
if (args.Target is not {Valid: true} target || !args.CanReach) if (args.Target is not { Valid: true } target || !args.CanReach)
return; return;
// TODO: Clean up this inherited oldcode. if (TryComp(target, out ReagentTankComponent? tank)
if (EntityManager.TryGetComponent(target, out ReagentTankComponent? tank)
&& tank.TankType == ReagentTankType.Fuel && tank.TankType == ReagentTankType.Fuel
&& _solutionContainerSystem.TryGetDrainableSolution(target, out var targetSolution) && _solutionContainer.TryGetDrainableSolution(target, out var targetSolution)
&& _solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var welderSolution)) && _solutionContainer.TryGetSolution(uid, welder.FuelSolution, out var welderSolution))
{ {
var trans = FixedPoint2.Min(welderSolution.AvailableVolume, targetSolution.Volume); var trans = FixedPoint2.Min(welderSolution.AvailableVolume, targetSolution.Volume);
if (trans > 0) if (trans > 0)
{ {
var drained = _solutionContainerSystem.Drain(target, targetSolution, trans); var drained = _solutionContainer.Drain(target, targetSolution, trans);
_solutionContainerSystem.TryAddSolution(uid, welderSolution, drained); _solutionContainer.TryAddSolution(uid, welderSolution, drained);
_audioSystem.PlayPvs(welder.WelderRefill, uid); _audio.PlayPvs(welder.WelderRefill, uid);
_popupSystem.PopupEntity(Loc.GetString("welder-component-after-interact-refueled-message"), uid, args.User); _popup.PopupEntity(Loc.GetString("welder-component-after-interact-refueled-message"), uid, args.User);
} }
else if (welderSolution.AvailableVolume <= 0) 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 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; 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(); args.Cancel();
return;
} }
} }
@@ -287,7 +165,7 @@ namespace Content.Server.Tools
private void OnWelderGetState(EntityUid uid, WelderComponent welder, ref ComponentGetState args) private void OnWelderGetState(EntityUid uid, WelderComponent welder, ref ComponentGetState args)
{ {
var (fuel, capacity) = GetWelderFuelAndCapacity(uid, welder); 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) private void UpdateWelders(float frameTime)
@@ -297,37 +175,26 @@ namespace Content.Server.Tools
if (_welderTimer < WelderUpdateTimer) if (_welderTimer < WelderUpdateTimer)
return; return;
// TODO Use an "active welder" component instead, EntityQuery over that. // TODO Use an "active welder" component instead, EntityQuery over that.
foreach (var tool in _activeWelders.ToArray()) foreach (var tool in _activeWelders.ToArray())
{ {
if (!EntityManager.TryGetComponent(tool, out WelderComponent? welder) if (!TryComp(tool, out WelderComponent? welder)
|| !EntityManager.TryGetComponent(tool, out SolutionContainerManagerComponent? solutionContainer) || !TryComp(tool, out SolutionContainerManagerComponent? solutionContainer))
|| !EntityManager.TryGetComponent(tool, out TransformComponent? transform))
continue; continue;
if (!_solutionContainerSystem.TryGetSolution(tool, welder.FuelSolution, out var solution, solutionContainer)) if (!_solutionContainer.TryGetSolution(tool, welder.FuelSolution, out var solution, solutionContainer))
continue; continue;
solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer); solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer);
if (solution.GetTotalPrototypeQuantity(welder.FuelReagent) <= FixedPoint2.Zero) if (solution.GetTotalPrototypeQuantity(welder.FuelReagent) <= FixedPoint2.Zero)
TryTurnWelderOff(tool, null, welder); {
_itemToggle.Toggle(tool, predicted: false);
}
Dirty(tool, welder); Dirty(tool, welder);
} }
_welderTimer -= WelderUpdateTimer; _welderTimer -= WelderUpdateTimer;
} }
} }
public sealed class WelderToggledEvent : EntityEventArgs
{
public bool WelderOn;
public WelderToggledEvent(bool welderOn)
{
WelderOn = welderOn;
}
}
} }

View File

@@ -19,10 +19,10 @@ namespace Content.Server.Tools
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!; [Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPointLightSystem _light = 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!; [Dependency] private readonly TransformSystem _transformSystem = default!;
public override void Initialize() public override void Initialize()

View File

@@ -1,44 +1,14 @@
using Content.Shared.Damage;
using Robust.Shared.Audio;
namespace Content.Server.Weapons.Melee.EnergySword; namespace Content.Server.Weapons.Melee.EnergySword;
[RegisterComponent] [RegisterComponent]
internal sealed partial class EnergySwordComponent : Component internal sealed partial class EnergySwordComponent : Component
{ {
public Color BladeColor = Color.DodgerBlue; [ViewVariables(VVAccess.ReadWrite), DataField("activatedColor"), AutoNetworkedField]
public Color ActivatedColor = Color.DodgerBlue;
public bool Hacked = false;
public bool Activated = false;
[DataField("isSharp")]
public bool IsSharp = true;
/// <summary> /// <summary>
/// Does this become hidden when deactivated /// A color option list for the random color picker.
/// </summary> /// </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")] [DataField("colorOptions")]
public List<Color> ColorOptions = new() public List<Color> ColorOptions = new()
{ {
@@ -49,15 +19,10 @@ internal sealed partial class EnergySwordComponent : Component
Color.MediumOrchid Color.MediumOrchid
}; };
[DataField("litDamageBonus")] public bool Hacked = false;
public DamageSpecifier LitDamageBonus = new(); /// <summary>
/// RGB cycle rate for hacked e-swords.
[DataField("litDisarmMalus")] /// </summary>
public float LitDisarmMalus = 0.6f; [DataField("cycleRate")]
public float CycleRate = 1f;
} }
[ByRefEvent]
public readonly record struct EnergySwordActivatedEvent();
[ByRefEvent]
public readonly record struct EnergySwordDeactivatedEvent();

View File

@@ -1,164 +1,38 @@
using Content.Server.CombatMode.Disarm;
using Content.Server.Kitchen.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.Light; using Content.Shared.Light;
using Content.Shared.Light.Components; using Content.Shared.Light.Components;
using Content.Shared.Temperature;
using Content.Shared.Toggleable; using Content.Shared.Toggleable;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Content.Shared.Weapons.Melee; using Content.Shared.Item;
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 Robust.Shared.Random; using Robust.Shared.Random;
namespace Content.Server.Weapons.Melee.EnergySword; namespace Content.Server.Weapons.Melee.EnergySword;
public sealed class EnergySwordSystem : EntitySystem public sealed class EnergySwordSystem : EntitySystem
{ {
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedRgbLightControllerSystem _rgbSystem = 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 SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<EnergySwordComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<EnergySwordComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<EnergySwordComponent, GetMeleeDamageEvent>(OnGetMeleeDamage);
SubscribeLocalEvent<EnergySwordComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<EnergySwordComponent, InteractUsingEvent>(OnInteractUsing); 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) private void OnMapInit(EntityUid uid, EnergySwordComponent comp, MapInitEvent args)
{ {
if (comp.ColorOptions.Count != 0) if (comp.ColorOptions.Count != 0)
comp.BladeColor = _random.Pick(comp.ColorOptions); comp.ActivatedColor = _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);
}
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)) if (!TryComp(uid, out AppearanceComponent? appearanceComponent))
return; return;
_appearance.SetData(uid, ToggleableLightVisuals.Color, comp.ActivatedColor, appearanceComponent);
_appearance.SetData(uid, ToggleableLightVisuals.Enabled, component.Activated, appearanceComponent);
_appearance.SetData(uid, ToggleableLightVisuals.Color, component.BladeColor, appearanceComponent);
} }
// Used to make the make the blade multicolored when using a multitool on it.
private void OnInteractUsing(EntityUid uid, EnergySwordComponent comp, InteractUsingEvent args) private void OnInteractUsing(EntityUid uid, EnergySwordComponent comp, InteractUsingEvent args)
{ {
if (args.Handled) if (args.Handled)
@@ -178,9 +52,4 @@ public sealed class EnergySwordSystem : EntitySystem
else else
RemComp<RgbLightControllerComponent>(uid); RemComp<RgbLightControllerComponent>(uid);
} }
private void OnIsHotEvent(EntityUid uid, EnergySwordComponent energySword, IsHotEvent args)
{
args.IsHot = energySword.Activated;
}
} }

View File

@@ -1,5 +1,4 @@
using Content.Server.Weapons.Melee.EnergySword; using Content.Shared.Item;
using Content.Server.Weapons.Melee.ItemToggle;
using Content.Shared.Weapons.Reflect; using Content.Shared.Weapons.Reflect;
namespace Content.Server.Weapons.Reflect; namespace Content.Server.Weapons.Reflect;
@@ -10,33 +9,12 @@ public sealed class ReflectSystem : SharedReflectSystem
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ReflectComponent, EnergySwordActivatedEvent>(EnableReflect); SubscribeLocalEvent<ReflectComponent, ItemToggleReflectUpdateEvent>(ToggleReflect);
SubscribeLocalEvent<ReflectComponent, EnergySwordDeactivatedEvent>(DisableReflect);
SubscribeLocalEvent<ReflectComponent, ItemToggleActivatedEvent>(ShieldEnableReflect);
SubscribeLocalEvent<ReflectComponent, ItemToggleDeactivatedEvent>(ShieldDisableReflect);
} }
private void EnableReflect(EntityUid uid, ReflectComponent comp, ref EnergySwordActivatedEvent args) private void ToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggleReflectUpdateEvent args)
{ {
comp.Enabled = true; comp.Enabled = args.Activated;
Dirty(comp); Dirty(uid, 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);
} }
} }

View File

@@ -5,6 +5,7 @@ using JetBrains.Annotations;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Robust.Shared.Serialization;
namespace Content.Shared.Damage 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 /// 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. /// functions to apply resistance sets and supports basic math operations to modify this dictionary.
/// </remarks> /// </remarks>
[DataDefinition] [DataDefinition, Serializable, NetSerializable]
public sealed partial class DamageSpecifier : IEquatable<DamageSpecifier> public sealed partial class DamageSpecifier : IEquatable<DamageSpecifier>
{ {
// These exist solely so the wiki works. Please do not touch them or use them. // These exist solely so the wiki works. Please do not touch them or use them.

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -27,6 +27,8 @@ public abstract class SharedItemSystem : EntitySystem
SubscribeLocalEvent<ItemComponent, ComponentHandleState>(OnHandleState); SubscribeLocalEvent<ItemComponent, ComponentHandleState>(OnHandleState);
SubscribeLocalEvent<ItemComponent, ExaminedEvent>(OnExamine); SubscribeLocalEvent<ItemComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<ItemToggleSizeComponent, ItemToggleSizeUpdateEvent>(OnItemToggle);
} }
#region Public API #region Public API
@@ -205,4 +207,32 @@ public abstract class SharedItemSystem : EntitySystem
return adjustedShapes; 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);
}
} }

View File

@@ -1,7 +1,3 @@
using Content.Server.Stunnable.Components;
using Content.Shared.Damage;
using Content.Shared.Weapons.Melee.Events;
namespace Content.Shared.Stunnable; namespace Content.Shared.Stunnable;
public abstract class SharedStunbatonSystem : EntitySystem public abstract class SharedStunbatonSystem : EntitySystem
@@ -9,16 +5,5 @@ public abstract class SharedStunbatonSystem : EntitySystem
public override void Initialize() public override void Initialize()
{ {
base.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();
} }
} }

View File

@@ -9,17 +9,10 @@ namespace Content.Server.Stunnable.Components;
[Access(typeof(SharedStunbatonSystem))] [Access(typeof(SharedStunbatonSystem))]
public sealed partial class StunbatonComponent : Component public sealed partial class StunbatonComponent : Component
{ {
[DataField("activated"), ViewVariables(VVAccess.ReadWrite)]
[AutoNetworkedField]
public bool Activated = false;
[DataField("energyPerUse"), ViewVariables(VVAccess.ReadWrite)] [DataField("energyPerUse"), ViewVariables(VVAccess.ReadWrite)]
[AutoNetworkedField] [AutoNetworkedField]
public float EnergyPerUse = 350; public float EnergyPerUse = 350;
[DataField("sparksSound")] [DataField("sparksSound")]
public SoundSpecifier SparksSound = new SoundCollectionSpecifier("sparks"); public SoundSpecifier SparksSound = new SoundCollectionSpecifier("sparks");
[DataField("turnOnFailSound")]
public SoundSpecifier TurnOnFailSound = new SoundPathSpecifier("/Audio/Machines/button.ogg");
} }

View File

@@ -4,36 +4,18 @@ using Robust.Shared.Serialization;
namespace Content.Shared.Tools.Components namespace Content.Shared.Tools.Components
{ {
[NetworkedComponent] [NetworkedComponent]
public abstract partial class SharedWelderComponent : Component public abstract partial class SharedWelderComponent : Component { }
{
public bool Lit { get; set; }
}
[NetSerializable, Serializable] [NetSerializable, Serializable]
public sealed class WelderComponentState : ComponentState public sealed class WelderComponentState : ComponentState
{ {
public float FuelCapacity { get; } public float FuelCapacity { get; }
public float Fuel { 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; FuelCapacity = fuelCapacity;
Fuel = fuel; Fuel = fuel;
Lit = lit;
} }
} }
[Serializable, NetSerializable]
public enum WelderVisuals : byte
{
Lit
}
[Serializable, NetSerializable]
public enum WelderLayers : byte
{
Base,
Flame
}
} }

View File

@@ -63,8 +63,8 @@ public sealed partial class MeleeWeaponComponent : Component
/// <summary> /// <summary>
/// Base damage for this weapon. Can be modified via heavy damage or other means. /// Base damage for this weapon. Can be modified via heavy damage or other means.
/// </summary> /// </summary>
[DataField(required:true)] [DataField(required: true)]
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
public DamageSpecifier Damage = default!; public DamageSpecifier Damage = default!;
[DataField] [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. /// This gets played whenever a melee attack is done. This is predicted by the client.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("soundSwing")] [DataField("soundSwing"), AutoNetworkedField]
public SoundSpecifier SwingSound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/punchmiss.ogg") public SoundSpecifier SwingSound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/punchmiss.ogg")
{ {
Params = AudioParams.Default.WithVolume(-3f).WithVariation(0.025f), 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. // If overwatch and apex do this then we probably should too.
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("soundHit")] [DataField("soundHit"), AutoNetworkedField]
public SoundSpecifier? HitSound; public SoundSpecifier? HitSound;
/// <summary> /// <summary>
/// Plays if no damage is done to the target entity. /// Plays if no damage is done to the target entity.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("soundNoDamage")] [DataField("soundNoDamage"), AutoNetworkedField]
public SoundSpecifier NoDamageSound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/tap.ogg"); public SoundSpecifier NoDamageSound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/tap.ogg");
} }

View File

@@ -12,6 +12,7 @@ using Content.Shared.Hands;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Item;
using Content.Shared.Physics; using Content.Shared.Physics;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Weapons.Melee.Components; using Content.Shared.Weapons.Melee.Components;
@@ -72,6 +73,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
SubscribeLocalEvent<BonusMeleeDamageComponent, GetHeavyDamageModifierEvent>(OnGetBonusHeavyDamageModifier); SubscribeLocalEvent<BonusMeleeDamageComponent, GetHeavyDamageModifierEvent>(OnGetBonusHeavyDamageModifier);
SubscribeLocalEvent<BonusMeleeAttackRateComponent, GetMeleeAttackRateEvent>(OnGetBonusMeleeAttackRate); SubscribeLocalEvent<BonusMeleeAttackRateComponent, GetMeleeAttackRateEvent>(OnGetBonusMeleeAttackRate);
SubscribeLocalEvent<ItemToggleMeleeWeaponComponent, ItemToggleMeleeWeaponUpdateEvent>(OnItemToggle);
SubscribeAllEvent<HeavyAttackEvent>(OnHeavyAttack); SubscribeAllEvent<HeavyAttackEvent>(OnHeavyAttack);
SubscribeAllEvent<LightAttackEvent>(OnLightAttack); SubscribeAllEvent<LightAttackEvent>(OnLightAttack);
SubscribeAllEvent<DisarmAttackEvent>(OnDisarmAttack); 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); 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);
}
} }

View File

@@ -33,6 +33,11 @@
copyright: "User Nimfar11 on GitHub." copyright: "User Nimfar11 on GitHub."
source: "https://github.com/Nimfar11" 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"] - files: ["dodgeball.ogg"]
license: "CC-BY-SA-3.0" license: "CC-BY-SA-3.0"
copyright: "Taken from tgstation" copyright: "Taken from tgstation"

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,4 @@
- type: entity - type: entity
name: base shield name: base shield
parent: BaseItem parent: BaseItem
id: BaseShield id: BaseShield
@@ -316,7 +316,11 @@
state: mirror-icon state: mirror-icon
- type: Item - type: Item
heldPrefix: mirror 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: passiveBlockModifier:
coefficients: coefficients:
Blunt: 1.2 Blunt: 1.2
@@ -356,12 +360,17 @@
description: Exotic energy shield, when folded, can even fit in your pocket. description: Exotic energy shield, when folded, can even fit in your pocket.
components: components:
- type: ItemToggle - type: ItemToggle
activatedDisarmMalus: 0.6 soundActivate:
activateSound:
path: /Audio/Weapons/ebladeon.ogg path: /Audio/Weapons/ebladeon.ogg
deActivateSound: soundDeactivate:
path: /Audio/Weapons/ebladeoff.ogg 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 - type: Sprite
sprite: Objects/Weapons/Melee/e_shield.rsi sprite: Objects/Weapons/Melee/e_shield.rsi
layers: layers:
@@ -461,16 +470,18 @@
description: An advanced riot shield made of lightweight materials that collapses for easy storage. description: An advanced riot shield made of lightweight materials that collapses for easy storage.
components: components:
- type: ItemToggle - type: ItemToggle
activatedDisarmMalus: 0.6 soundActivate:
activateSound:
path: /Audio/Weapons/telescopicon.ogg path: /Audio/Weapons/telescopicon.ogg
params: params:
volume: -5 volume: -5
deActivateSound: soundDeactivate:
path: /Audio/Weapons/telescopicoff.ogg path: /Audio/Weapons/telescopicoff.ogg
params: params:
volume: -5 volume: -5
offSize: Small - type: ItemToggleDisarmMalus
activatedDisarmMalus: 0.6
- type: ItemToggleSize
activatedSize: Huge
- type: Sprite - type: Sprite
sprite: Objects/Weapons/Melee/teleriot_shield.rsi sprite: Objects/Weapons/Melee/teleriot_shield.rsi
layers: layers:

View File

@@ -4,45 +4,73 @@
id: Lighter id: Lighter
description: "A simple plastic cigarette lighter." description: "A simple plastic cigarette lighter."
components: components:
# Sloth: What is this comment ????????? - type: ItemToggle
- 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) predictable: false
available: soundActivate:
- enum.WelderLayers.Base: collection: lighterOnSounds
basic_icon_base-1: "" soundDeactivate:
- enum.WelderLayers.Base: collection: lighterOffSounds
basic_icon_base-2: "" - type: ItemToggleMeleeWeapon
- enum.WelderLayers.Base: activatedDamage:
basic_icon_base-3: "" types:
- enum.WelderLayers.Base: Heat: 1
basic_icon_base-4: "" - type: ItemToggleSize
- enum.WelderLayers.Base: activatedSize: Small
basic_icon_base-5: "" - type: ItemToggleHot
- enum.WelderLayers.Base:
basic_icon_base-6: ""
- enum.WelderLayers.Base:
basic_icon_base-7: ""
- enum.WelderLayers.Base:
basic_icon_base-8: ""
- enum.WelderLayers.Base:
basic_icon_base-9: ""
- enum.WelderLayers.Base:
basic_icon_base-10: ""
- enum.WelderLayers.Base:
basic_icon_base-11: ""
- type: Sprite - type: Sprite
sprite: Objects/Tools/lighters.rsi sprite: Objects/Tools/lighters.rsi
layers: layers:
- state: icon_map - state: icon_map
map: ["enum.WelderLayers.Base"] - state: cheap_icon_base
- state: lighter_flame map: [ "skin" ]
map: ["enum.WelderLayers.Flame"]
shader: unshaded
visible: false
- state: basic_icon_top - state: basic_icon_top
- state: lighter_flame
visible: false
shader: unshaded
map: [ "flame" ]
- type: Appearance
- type: RandomSprite
available:
- skin:
basic_icon_base-1: ""
- skin:
basic_icon_base-2: ""
- skin:
basic_icon_base-3: ""
- skin:
basic_icon_base-4: ""
- skin:
basic_icon_base-5: ""
- skin:
basic_icon_base-6: ""
- skin:
basic_icon_base-7: ""
- skin:
basic_icon_base-8: ""
- skin:
basic_icon_base-9: ""
- skin:
basic_icon_base-10: ""
- skin:
basic_icon_base-11: ""
- 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 - type: Item
size: Tiny size: Tiny
sprite: Objects/Tools/lighters.rsi sprite: Objects/Tools/lighters.rsi
heldPrefix: off
- type: ItemCooldown - type: ItemCooldown
- type: RefillableSolution - type: RefillableSolution
solution: Welder solution: Welder
@@ -56,24 +84,16 @@
- type: MeleeWeapon - type: MeleeWeapon
damage: damage:
types: types:
Blunt: 0 #this feels hacky, but is needed for burn damage while active (i think) Blunt: 0
- type: Welder - type: Welder
fuelConsumption: 0.01 fuelConsumption: 0.01
fuelLitCost: 0.1 fuelLitCost: 0.1
litMeleeDamageBonus:
types:
Heat: 1
tankSafe: true tankSafe: true
welderOnSounds:
collection: lighterOnSounds
welderOffSounds:
collection: lighterOffSounds
- type: PointLight - type: PointLight
enabled: false enabled: false
netsync: false
radius: 1.1 #smallest possible radius: 1.1 #smallest possible
color: orange color: orange
- type: Appearance
- type: entity - type: entity
name: cheap lighter name: cheap lighter
@@ -81,20 +101,21 @@
id: CheapLighter id: CheapLighter
description: "A dangerously inexpensive plastic lighter, don't burn your thumb!" description: "A dangerously inexpensive plastic lighter, don't burn your thumb!"
components: components:
- type: RandomSprite
available:
- enum.WelderLayers.Base:
cheap_icon_base: Rainbow
- type: Sprite - type: Sprite
sprite: Objects/Tools/lighters.rsi sprite: Objects/Tools/lighters.rsi
layers: layers:
- state: icon_map - state: icon_map
map: ["enum.WelderLayers.Base"] - state: cheap_icon_base
- state: lighter_flame map: [ "skin" ]
map: ["enum.WelderLayers.Flame"]
shader: unshaded
visible: false
- state: cheap_icon_top - state: cheap_icon_top
- state: lighter_flame
visible: false
shader: unshaded
map: [ "flame" ]
- type: RandomSprite
available:
- skin:
cheap_icon_base: Rainbow
- type: SolutionContainerManager - type: SolutionContainerManager
solutions: solutions:
Welder: Welder:
@@ -102,5 +123,3 @@
- ReagentId: WeldingFuel - ReagentId: WeldingFuel
Quantity: 4 Quantity: 4
maxVol: 4 #uses less fuel than a welder, so this isnt as bad as it looks maxVol: 4 #uses less fuel than a welder, so this isnt as bad as it looks
#TODO: zippos

View File

@@ -11,14 +11,50 @@
sprite: Objects/Tools/welder.rsi sprite: Objects/Tools/welder.rsi
layers: layers:
- state: icon - state: icon
map: ["enum.WelderLayers.Base"]
- state: welder_flame - state: welder_flame
map: ["enum.WelderLayers.Flame"]
shader: unshaded
visible: false 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 - type: Item
size: Small size: Small
sprite: Objects/Tools/welder.rsi 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 - type: ToggleableLightVisuals
spriteLayer: flame spriteLayer: flame
inhandVisuals: inhandVisuals:
@@ -48,10 +84,6 @@
collection: Welder collection: Welder
qualities: Welding qualities: Welding
- type: Welder - type: Welder
litMeleeDamageBonus:
types:
Heat: 8
Blunt: -5
- type: PointLight - type: PointLight
enabled: false enabled: false
radius: 1.5 radius: 1.5

View 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. description: A very loud & dangerous sword with a beam made of pure, concentrated plasma. Cuts through unarmored targets like butter.
components: components:
- type: EnergySword - 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: types:
Slash: 15 Slash: 15
Heat: 15 Heat: 15
Structural: 20 Structural: 20
Blunt: -4.5
litDisarmMalus: 0.6
- type: Sprite - type: Sprite
sprite: Objects/Weapons/Melee/e_sword.rsi sprite: Objects/Weapons/Melee/e_sword.rsi
layers: layers:
@@ -24,8 +50,6 @@
- type: MeleeWeapon - type: MeleeWeapon
wideAnimationRotation: -135 wideAnimationRotation: -135
attackRate: 1 attackRate: 1
soundHit:
path: /Audio/Weapons/eblade1.ogg
damage: damage:
types: types:
Blunt: 4.5 Blunt: 4.5
@@ -63,21 +87,33 @@
description: 'A dark ink pen.' description: 'A dark ink pen.'
components: components:
- type: EnergySword - type: EnergySword
secret: true - type: ItemToggle
litDamageBonus: soundActivate:
types:
Slash: 10
Heat: 10
Blunt: -1
litDisarmMalus: 0.4
activateSound: !type:SoundPathSpecifier
path: /Audio/Weapons/ebladeon.ogg path: /Audio/Weapons/ebladeon.ogg
params: params:
volume: -6 volume: -6
deActivateSound: !type:SoundPathSpecifier soundDeactivate:
path: /Audio/Weapons/ebladeoff.ogg path: /Audio/Weapons/ebladeoff.ogg
params: params:
volume: -6 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 - type: Sprite
sprite: Objects/Weapons/Melee/e_dagger.rsi sprite: Objects/Weapons/Melee/e_dagger.rsi
layers: layers:
@@ -146,13 +182,14 @@
description: An exotic energy weapon. description: An exotic energy weapon.
components: components:
- type: EnergySword - type: EnergySword
secret: true - type: ItemToggleMeleeWeapon
litDamageBonus: activatedDamage:
types: types:
Slash: 10 Slash: 10
Heat: 12 Heat: 12
Blunt: -1 deactivatedSecret: true
litDisarmMalus: 0.6 - type: ItemToggleDisarmMalus
activatedDisarmMalus: 0.6
- type: Sprite - type: Sprite
sprite: Objects/Weapons/Melee/e_cutlass.rsi sprite: Objects/Weapons/Melee/e_cutlass.rsi
layers: layers:
@@ -181,21 +218,39 @@
id: EnergySwordDouble id: EnergySwordDouble
description: Syndicate Command Interns thought that having one blade on the energy sword was not enough. This can be stored in pockets. description: Syndicate Command Interns thought that having one blade on the energy sword was not enough. This can be stored in pockets.
components: components:
- type: Wieldable
- type: EnergySword - 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: types:
Slash: 12 Slash: 12
Heat: 12 Heat: 12
Structural: 15 Structural: 15
Blunt: -4.5 - type: ItemToggleActiveSound
litDisarmMalus: 0.7 activeSound:
path: /Audio/Weapons/ebladehum.ogg
params:
volume: 3
- type: ItemToggleDisarmMalus
activatedDisarmMalus: 0.7
- type: Wieldable
- type: MeleeWeapon - type: MeleeWeapon
wideAnimationRotation: -135 wideAnimationRotation: -135
attackRate: 1.5 attackRate: 1.5
angle: 100 angle: 100
soundHit:
path: /Audio/Weapons/eblade1.ogg
damage: damage:
types: types:
Blunt: 4.5 Blunt: 4.5
@@ -212,7 +267,6 @@
size: Small size: Small
sprite: Objects/Weapons/Melee/e_sword_double-inhands.rsi sprite: Objects/Weapons/Melee/e_sword_double-inhands.rsi
- type: Reflect - type: Reflect
enabled: true
reflectProb: .75 reflectProb: .75
spread: 75 spread: 75
- type: UseDelay - type: UseDelay

View File

@@ -9,6 +9,23 @@
layers: layers:
- state: stunprod_off - state: stunprod_off
map: [ "enum.ToggleVisuals.Layer" ] 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 - type: Stunbaton
energyPerUse: 70 energyPerUse: 70
- type: MeleeWeapon - type: MeleeWeapon
@@ -37,7 +54,7 @@
- type: Appearance - type: Appearance
- type: GenericVisualizer - type: GenericVisualizer
visuals: visuals:
enum.ToggleVisuals.Toggled: enum.ToggleVisuals.Toggled:
enum.ToggleVisuals.Layer: enum.ToggleVisuals.Layer:
True: {state: stunprod_on} True: {state: stunprod_on}
False: {state: stunprod_off} False: {state: stunprod_off}

View File

@@ -11,6 +11,24 @@
map: [ "enum.ToggleVisuals.Layer" ] map: [ "enum.ToggleVisuals.Layer" ]
- type: Stunbaton - type: Stunbaton
energyPerUse: 50 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 - type: MeleeWeapon
wideAnimationRotation: -135 wideAnimationRotation: -135
damage: damage:

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -71,19 +71,19 @@
"name": "zippo_top" "name": "zippo_top"
}, },
{ {
"name": "off-inhand-left", "name": "inhand-left",
"directions": 4 "directions": 4
}, },
{ {
"name": "off-inhand-right", "name": "inhand-right",
"directions": 4 "directions": 4
}, },
{ {
"name": "on-inhand-left", "name": "inhand-left-flame",
"directions": 4 "directions": 4
}, },
{ {
"name": "on-inhand-right", "name": "inhand-right-flame",
"directions": 4 "directions": 4
} }