diff --git a/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs b/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs index 923a10b22a..30fa84ed90 100644 --- a/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs +++ b/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs @@ -1,19 +1,21 @@ namespace Content.Server.Item; /// -/// Handles whether this item applies a disarm malus when active. +/// Handles whether this item applies a disarm malus when active. /// [RegisterComponent] public sealed partial class ItemToggleDisarmMalusComponent : Component { /// /// Item has this modifier to the chance to disarm when activated. + /// If null, the value will be inferred from the current malus just before the malus is first deactivated. /// [ViewVariables(VVAccess.ReadOnly), DataField] public float? ActivatedDisarmMalus = null; /// /// Item has this modifier to the chance to disarm when deactivated. If none is mentioned, it uses the item's default disarm modifier. + /// If null, the value will be inferred from the current malus just before the malus is first activated. /// [ViewVariables(VVAccess.ReadOnly), DataField] public float? DeactivatedDisarmMalus = null; diff --git a/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs b/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs index ea2efae147..227491b16c 100644 --- a/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs +++ b/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs @@ -1,14 +1,9 @@ namespace Content.Server.Item; /// -/// Handles whether this item is sharp when toggled on. +/// Handles whether this item is sharp when toggled on. /// [RegisterComponent] public sealed partial class ItemToggleSharpComponent : Component { - /// - /// Item can be used to butcher when activated. - /// - [ViewVariables(VVAccess.ReadOnly), DataField] - public bool ActivatedSharp = true; } diff --git a/Content.Server/Item/ItemToggle/ItemToggleSystem.cs b/Content.Server/Item/ItemToggle/ItemToggleSystem.cs index 4507ace9d2..f98415eb08 100644 --- a/Content.Server/Item/ItemToggle/ItemToggleSystem.cs +++ b/Content.Server/Item/ItemToggle/ItemToggleSystem.cs @@ -1,9 +1,7 @@ -using Content.Shared.Item; using Content.Server.CombatMode.Disarm; using Content.Server.Kitchen.Components; using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle.Components; -using ItemToggleComponent = Content.Shared.Item.ItemToggle.Components.ItemToggleComponent; namespace Content.Server.Item; @@ -13,47 +11,34 @@ public sealed class ItemToggleSystem : SharedItemToggleSystem { base.Initialize(); - SubscribeLocalEvent(Toggle); + SubscribeLocalEvent(ToggleSharp); + SubscribeLocalEvent(ToggleMalus); } - private void Toggle(EntityUid uid, ItemToggleComponent comp, ref ItemToggledEvent args) + private void ToggleSharp(Entity ent, ref ItemToggledEvent args) { - if (args.Activated == true) - { - if (TryComp(uid, out var itemSharpness)) - { - if (itemSharpness.ActivatedSharp) - EnsureComp(uid); - } - - if (!TryComp(uid, out var itemToggleDisarmMalus) || - !TryComp(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; - } - } + // TODO generalize this into a "ToggleComponentComponent", though probably with a better name + if (args.Activated) + EnsureComp(ent); else + RemCompDeferred(ent); + } + + private void ToggleMalus(Entity ent, ref ItemToggledEvent args) + { + if (!TryComp(ent, out var malus)) + return; + + if (args.Activated) { - if (TryComp(uid, out var itemSharpness)) - { - if (itemSharpness.ActivatedSharp) - RemCompDeferred(uid); - } - - if (!TryComp(uid, out var itemToggleDisarmMalus) || - !TryComp(uid, out var malus)) - return; - - if (itemToggleDisarmMalus.DeactivatedDisarmMalus != null) - { - malus.Malus = (float) itemToggleDisarmMalus.DeactivatedDisarmMalus; - } + ent.Comp.DeactivatedDisarmMalus ??= malus.Malus; + if (ent.Comp.ActivatedDisarmMalus is {} activatedMalus) + malus.Malus = activatedMalus; + return; } + + ent.Comp.ActivatedDisarmMalus ??= malus.Malus; + if (ent.Comp.DeactivatedDisarmMalus is {} deactivatedMalus) + malus.Malus = deactivatedMalus; } } diff --git a/Content.Server/Stunnable/Systems/StunbatonSystem.cs b/Content.Server/Stunnable/Systems/StunbatonSystem.cs index 5f019490b5..c1782efaba 100644 --- a/Content.Server/Stunnable/Systems/StunbatonSystem.cs +++ b/Content.Server/Stunnable/Systems/StunbatonSystem.cs @@ -58,10 +58,7 @@ namespace Content.Server.Stunnable.Systems private void ToggleDone(Entity entity, ref ItemToggledEvent args) { - if (!TryComp(entity, out var item)) - return; - - _item.SetHeldPrefix(entity.Owner, args.Activated ? "on" : "off", component: item); + _item.SetHeldPrefix(entity.Owner, args.Activated ? "on" : "off"); } private void TryTurnOn(Entity entity, ref ItemToggleActivateAttemptEvent args) diff --git a/Content.Server/Tools/ToolSystem.Welder.cs b/Content.Server/Tools/ToolSystem.Welder.cs index 1eabd6c6d2..727526b398 100644 --- a/Content.Server/Tools/ToolSystem.Welder.cs +++ b/Content.Server/Tools/ToolSystem.Welder.cs @@ -7,7 +7,6 @@ using Content.Shared.DoAfter; using Content.Shared.Examine; using Content.Shared.FixedPoint; using Content.Shared.Interaction; -using Content.Shared.Item; using Content.Shared.Item.ItemToggle; using Content.Shared.Tools.Components; using Robust.Shared.GameStates; @@ -32,8 +31,8 @@ namespace Content.Server.Tools SubscribeLocalEvent>(OnWelderToolUseAttempt); SubscribeLocalEvent(OnWelderShutdown); SubscribeLocalEvent(OnWelderGetState); - SubscribeLocalEvent(TryTurnOn); - SubscribeLocalEvent(TurnOff); + SubscribeLocalEvent(OnToggle); + SubscribeLocalEvent(OnActivateAttempt); } public (FixedPoint2 fuel, FixedPoint2 capacity) GetWelderFuelAndCapacity(EntityUid uid, WelderComponent? welder = null, SolutionContainerManagerComponent? solutionContainer = null) @@ -45,55 +44,54 @@ namespace Content.Server.Tools return (fuelSolution.GetTotalPrototypeQuantity(welder.FuelReagent), fuelSolution.MaxVolume); } - public void TryTurnOn(Entity entity, ref ItemToggleActivateAttemptEvent args) + private void OnToggle(Entity entity, ref ItemToggledEvent args) { - if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution, out var solution) || - !TryComp(entity, out var transform)) + if (args.Activated) + TurnOn(entity, args.User); + else + TurnOff(entity, args.User); + } + + private void OnActivateAttempt(Entity entity, ref ItemToggleActivateAttemptEvent args) + { + if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution, out var solution)) { args.Cancelled = true; + args.Popup = Loc.GetString("welder-component-no-fuel-message"); return; } var fuel = solution.GetTotalPrototypeQuantity(entity.Comp.FuelReagent); - - // Not enough fuel to lit welder. if (fuel == FixedPoint2.Zero || fuel < entity.Comp.FuelLitCost) { - if (args.User != null) - { - _popup.PopupEntity(Loc.GetString("welder-component-no-fuel-message"), entity, (EntityUid) args.User); - } + args.Popup = Loc.GetString("welder-component-no-fuel-message"); args.Cancelled = true; - return; } + } + + public void TurnOn(Entity entity, EntityUid? user) + { + if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution)) + return; _solutionContainer.RemoveReagent(entity.Comp.FuelSolution.Value, entity.Comp.FuelReagent, entity.Comp.FuelLitCost); + AdminLogger.Add(LogType.InteractActivate, LogImpact.Low, + $"{ToPrettyString(user):user} toggled {ToPrettyString(entity.Owner):welder} on"); - // Logging - AdminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(entity.Owner):welder} on"); - - _ignitionSource.SetIgnited(entity.Owner); - - if (transform.GridUid is { } gridUid) + var xform = Transform(entity); + if (xform.GridUid is { } gridUid) { - var position = _transformSystem.GetGridOrMapTilePosition(entity.Owner, transform); + var position = _transformSystem.GetGridOrMapTilePosition(entity.Owner, xform); _atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, entity.Owner, true); } - Dirty(entity); - _activeWelders.Add(entity); } - public void TurnOff(Entity entity, ref ItemToggleDeactivateAttemptEvent args) + public void TurnOff(Entity entity, EntityUid? user) { - // Logging - AdminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(entity.Owner):welder} off"); - - _ignitionSource.SetIgnited(entity.Owner, false); - - Dirty(entity); - + AdminLogger.Add(LogType.InteractActivate, LogImpact.Low, + $"{ToPrettyString(user):user} toggled {ToPrettyString(entity.Owner):welder} off"); _activeWelders.Remove(entity); } @@ -186,7 +184,9 @@ namespace Content.Server.Tools if (_welderTimer < WelderUpdateTimer) return; - // TODO Use an "active welder" component instead, EntityQuery over that. + // TODO Serialization. _activeWelders is not serialized. + // Need to use some "active" component, and EntityQuery over that. + // Probably best to generalize it to a "ToggleableFuelDrain" component. foreach (var tool in _activeWelders.ToArray()) { if (!TryComp(tool, out WelderComponent? welder) diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs index 3ea80ec735..620ddfd194 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs @@ -16,7 +16,7 @@ public sealed partial class ItemToggleComponent : Component /// /// The item's toggle state. /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + [DataField, AutoNetworkedField] public bool Activated = false; /// @@ -45,6 +45,12 @@ public sealed partial class ItemToggleComponent : Component /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public SoundSpecifier? SoundFailToActivate; + + /// + /// Whether or not to toggle the entity's lights on or off. + /// + [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + public bool ToggleLight = true; } /// @@ -55,6 +61,11 @@ public record struct ItemToggleActivateAttemptEvent(EntityUid? User) { public bool Cancelled = false; public readonly EntityUid? User = User; + + /// + /// Pop-up that gets shown to users explaining why the attempt was cancelled. + /// + public string? Popup { get; set; } } /// @@ -77,21 +88,3 @@ public readonly record struct ItemToggledEvent(bool Predicted, bool Activated, E public readonly bool Activated = Activated; public readonly EntityUid? User = User; } - -/// -/// Raised directed on an entity when an itemtoggle is activated. -/// -[ByRefEvent] -public readonly record struct ItemToggleActivatedEvent(EntityUid? User) -{ - public readonly EntityUid? User = User; -} - -/// -/// Raised directed on an entity when an itemtoggle is deactivated. -/// -[ByRefEvent] -public readonly record struct ItemToggleDeactivatedEvent(EntityUid? User) -{ - public readonly EntityUid? User = User; -} diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs index c3eb1d1e58..4bcd6e25c0 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs @@ -5,12 +5,7 @@ namespace Content.Shared.Item.ItemToggle.Components; /// /// Handles whether the item is hot when toggled on. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[RegisterComponent, NetworkedComponent] public sealed partial class ItemToggleHotComponent : Component { - /// - /// Item becomes hot when active. - /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] - public bool IsHotWhenActivated = true; } diff --git a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs index e2e5fc2e3c..523f67bac3 100644 --- a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Interaction.Events; using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Popups; using Content.Shared.Temperature; using Content.Shared.Toggleable; using Content.Shared.Wieldable; @@ -20,11 +21,13 @@ public abstract class SharedItemToggleSystem : EntitySystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedPointLightSystem _light = default!; [Dependency] private readonly INetManager _netManager = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(TurnOffonUnwielded); SubscribeLocalEvent(TurnOnonWielded); SubscribeLocalEvent(OnUseInHand); @@ -34,6 +37,11 @@ public abstract class SharedItemToggleSystem : EntitySystem SubscribeLocalEvent(UpdateActiveSound); } + private void OnStartup(Entity ent, ref ComponentStartup args) + { + UpdateVisuals(ent); + } + private void OnUseInHand(EntityUid uid, ItemToggleComponent itemToggle, UseInHandEvent args) { if (args.Handled) @@ -73,6 +81,9 @@ public abstract class SharedItemToggleSystem : EntitySystem if (itemToggle.Activated) return true; + if (!itemToggle.Predictable && _netManager.IsClient) + return true; + var attempt = new ItemToggleActivateAttemptEvent(user); RaiseLocalEvent(uid, ref attempt); @@ -83,12 +94,16 @@ public abstract class SharedItemToggleSystem : EntitySystem else _audio.PlayPvs(itemToggle.SoundFailToActivate, uid); + if (attempt.Popup != null && user != null) + { + if (predicted) + _popup.PopupClient(attempt.Popup, uid, user.Value); + else + _popup.PopupEntity(attempt.Popup, uid, user.Value); + } + 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, predicted, user); @@ -103,6 +118,9 @@ public abstract class SharedItemToggleSystem : EntitySystem if (!Resolve(uid, ref itemToggle)) return false; + if (!itemToggle.Predictable && _netManager.IsClient) + return true; + if (!itemToggle.Activated) return true; @@ -114,10 +132,6 @@ public abstract class SharedItemToggleSystem : EntitySystem 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, predicted, user); return true; } @@ -135,7 +149,6 @@ public abstract class SharedItemToggleSystem : EntitySystem } var soundToPlay = itemToggle.SoundActivate; - if (predicted) _audio.PlayPredicted(soundToPlay, uid, user); else @@ -146,10 +159,8 @@ public abstract class SharedItemToggleSystem : EntitySystem var toggleUsed = new ItemToggledEvent(predicted, Activated: true, user); RaiseLocalEvent(uid, ref toggleUsed); - var activev = new ItemToggleActivatedEvent(user); - RaiseLocalEvent(uid, ref activev); - itemToggle.Activated = true; + UpdateVisuals((uid, itemToggle)); Dirty(uid, itemToggle); } @@ -158,35 +169,38 @@ public abstract class SharedItemToggleSystem : EntitySystem /// private void Deactivate(EntityUid uid, ItemToggleComponent itemToggle, bool predicted, EntityUid? user = null) { - // TODO: Fix this hardcoding - TryComp(uid, out AppearanceComponent? appearance); - _appearance.SetData(uid, ToggleableLightVisuals.Enabled, false, appearance); - _appearance.SetData(uid, ToggleVisuals.Toggled, false, appearance); - - if (_light.TryGetLight(uid, out var light)) - { - _light.SetEnabled(uid, false, light); - } - var soundToPlay = itemToggle.SoundDeactivate; - if (predicted) _audio.PlayPredicted(soundToPlay, uid, user); else _audio.PlayPvs(soundToPlay, uid); - // END FIX HARDCODING var toggleUsed = new ItemToggledEvent(predicted, Activated: false, user); RaiseLocalEvent(uid, ref toggleUsed); - var activev = new ItemToggleDeactivatedEvent(user); - RaiseLocalEvent(uid, ref activev); - itemToggle.Activated = false; + UpdateVisuals((uid, itemToggle)); Dirty(uid, itemToggle); } + private void UpdateVisuals(Entity ent) + { + if (TryComp(ent, out AppearanceComponent? appearance)) + { + _appearance.SetData(ent, ToggleVisuals.Toggled, ent.Comp.Activated, appearance); + + if (ent.Comp.ToggleLight) + _appearance.SetData(ent, ToggleableLightVisuals.Enabled, ent.Comp.Activated, appearance); + } + + if (!ent.Comp.ToggleLight) + return; + + if (_light.TryGetLight(ent, out var light)) + _light.SetEnabled(ent, ent.Comp.Activated, light); + } + /// /// 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. /// @@ -218,8 +232,7 @@ public abstract class SharedItemToggleSystem : EntitySystem /// private void OnIsHotEvent(EntityUid uid, ItemToggleHotComponent itemToggleHot, IsHotEvent args) { - if (itemToggleHot.IsHotWhenActivated) - args.IsHot = IsActivated(uid); + args.IsHot |= IsActivated(uid); } ///