From f5ea561bea76a95e3c5657849197c11ed7e9fb1d Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Thu, 18 Jul 2024 02:41:15 +0200 Subject: [PATCH 001/366] minor SharedInteractionSystem cleanup (#30139) cleanup SharedInteractionSystem --- Content.Shared/Interaction/SharedInteractionSystem.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index cc548fd4b16..b54d2a41fe1 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -42,8 +42,6 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; -#pragma warning disable 618 - namespace Content.Shared.Interaction { /// @@ -556,11 +554,11 @@ public void InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? targe protected bool ValidateInteractAndFace(EntityUid user, EntityCoordinates coordinates) { // Verify user is on the same map as the entity they clicked on - if (coordinates.GetMapId(EntityManager) != Transform(user).MapID) + if (_transform.GetMapId(coordinates) != Transform(user).MapID) return false; if (!HasComp(user)) - _rotateToFaceSystem.TryFaceCoordinates(user, coordinates.ToMapPos(EntityManager, _transform)); + _rotateToFaceSystem.TryFaceCoordinates(user, _transform.ToMapCoordinates(coordinates).Position); return true; } @@ -893,7 +891,7 @@ public bool InRangeUnobstructed( Ignored? predicate = null, bool popup = false) { - return InRangeUnobstructed(origin, other.ToMap(EntityManager, _transform), range, collisionMask, predicate, popup); + return InRangeUnobstructed(origin, _transform.ToMapCoordinates(other), range, collisionMask, predicate, popup); } /// @@ -1009,7 +1007,7 @@ public void InteractDoAfter( bool checkDeletion = false ) { - if (target is {Valid: false}) + if (target is { Valid: false }) target = null; if (checkDeletion && (IsDeleted(user) || IsDeleted(used) || IsDeleted(target))) From 803671acdabf3f3617a1563bf30faae0a2138190 Mon Sep 17 00:00:00 2001 From: themias <89101928+themias@users.noreply.github.com> Date: Tue, 30 Jul 2024 04:35:30 -0400 Subject: [PATCH 002/366] Fix fingerprint transfer on weapon attack (#30257) * Fix fingerprint transfer on weapon attack * Switch to just not raising the event * one more --- Content.Shared/Interaction/SharedInteractionSystem.cs | 6 +++++- Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index b54d2a41fe1..ab4e6c5b8cf 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -988,7 +988,7 @@ public void InteractUsing( RaiseLocalEvent(target, interactUsingEvent, true); DoContactInteraction(user, used, interactUsingEvent); DoContactInteraction(user, target, interactUsingEvent); - + // Contact interactions are currently only used for forensics, so we don't raise used -> target if (interactUsingEvent.Handled) return; @@ -1018,6 +1018,8 @@ public void InteractDoAfter( DoContactInteraction(user, used, afterInteractEvent); if (canReach) DoContactInteraction(user, target, afterInteractEvent); + // Contact interactions are currently only used for forensics, so we don't raise used -> target + } if (afterInteractEvent.Handled) return; @@ -1031,6 +1033,8 @@ public void InteractDoAfter( DoContactInteraction(user, used, afterInteractUsingEvent); if (canReach) DoContactInteraction(user, target, afterInteractUsingEvent); + // Contact interactions are currently only used for forensics, so we don't raise used -> target + } } #region ActivateItemInWorld diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index aa15ecfb286..eefd6bb0535 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -468,7 +468,7 @@ protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, Entity var weapon = GetEntity(ev.Weapon); - Interaction.DoContactInteraction(weapon, target); + // We skip weapon -> target interaction, as forensics system applies DNA on hit Interaction.DoContactInteraction(user, weapon); // If the user is using a long-range weapon, this probably shouldn't be happening? But I'll interpret melee as a @@ -608,7 +608,7 @@ private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeU // For stuff that cares about it being attacked. foreach (var target in targets) { - Interaction.DoContactInteraction(weapon, target); + // We skip weapon -> target interaction, as forensics system applies DNA on hit // If the user is using a long-range weapon, this probably shouldn't be happening? But I'll interpret melee as a // somewhat messy scuffle. See also, light attacks. From 56aee075b3363dca08bcb5369b9d76f920fcde93 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Fri, 2 Aug 2024 07:44:59 +0200 Subject: [PATCH 003/366] Fix borgs being unable to state laws or open other UIs without modules (#30299) borg law 2 state laws all day --- Content.Shared/Interaction/SharedInteractionSystem.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index ab4e6c5b8cf..32d69dc8e73 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -189,10 +189,7 @@ private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev return; } - if (!uiComp.RequireHands) - return; - - if (!_handsQuery.TryComp(ev.Actor, out var hands) || hands.Hands.Count == 0) + if (uiComp.RequireHands && !_handsQuery.HasComp(ev.Actor)) ev.Cancel(); } From 8e4df8777323d3809c891a91b74fcb84273d36d8 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Sat, 25 May 2024 20:14:48 +0000 Subject: [PATCH 004/366] antag objective issuing refactor (#28216) --- Content.Server/Antag/AntagObjectivesSystem.cs | 35 +++++++++++++ .../Antag/AntagRandomObjectivesSystem.cs | 52 +++++++++++++++++++ .../Antag/AntagSelectionSystem.API.cs | 2 +- Content.Server/Antag/AntagSelectionSystem.cs | 12 +++++ .../Components/AntagObjectivesComponent.cs | 18 +++++++ .../AntagRandomObjectivesComponent.cs | 52 +++++++++++++++++++ .../Components/AntagSelectionComponent.cs | 7 +++ .../Rules/Components/ThiefRuleComponent.cs | 21 +------- .../Rules/Components/TraitorRuleComponent.cs | 6 --- .../Rules/GenericAntagRuleSystem.cs | 5 +- .../GameTicking/Rules/ThiefRuleSystem.cs | 39 -------------- .../GameTicking/Rules/TraitorRuleSystem.cs | 30 ++--------- Content.Server/Objectives/ObjectivesSystem.cs | 51 ++++++------------ .../Locale/en-US/objectives/round-end.ftl | 1 - Resources/Prototypes/GameRules/events.yml | 5 +- Resources/Prototypes/GameRules/midround.yml | 12 +++++ Resources/Prototypes/GameRules/roundstart.yml | 18 ++++++- .../Prototypes/Objectives/objectiveGroups.yml | 5 -- 18 files changed, 231 insertions(+), 140 deletions(-) create mode 100644 Content.Server/Antag/AntagObjectivesSystem.cs create mode 100644 Content.Server/Antag/AntagRandomObjectivesSystem.cs create mode 100644 Content.Server/Antag/Components/AntagObjectivesComponent.cs create mode 100644 Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs diff --git a/Content.Server/Antag/AntagObjectivesSystem.cs b/Content.Server/Antag/AntagObjectivesSystem.cs new file mode 100644 index 00000000000..5aa31f66f67 --- /dev/null +++ b/Content.Server/Antag/AntagObjectivesSystem.cs @@ -0,0 +1,35 @@ +using Content.Server.Antag.Components; +using Content.Server.Objectives; +using Content.Shared.Mind; +using Content.Shared.Objectives.Systems; + +namespace Content.Server.Antag; + +/// +/// Adds fixed objectives to an antag made with AntagObjectivesComponent. +/// +public sealed class AntagObjectivesSystem : EntitySystem +{ + [Dependency] private readonly SharedMindSystem _mind = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAntagSelected); + } + + private void OnAntagSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) + { + if (!_mind.TryGetMind(args.Session, out var mindId, out var mind)) + { + Log.Error($"Antag {ToPrettyString(args.EntityUid):player} was selected by {ToPrettyString(ent):rule} but had no mind attached!"); + return; + } + + foreach (var id in ent.Comp.Objectives) + { + _mind.TryAddObjective(mindId, mind, id); + } + } +} diff --git a/Content.Server/Antag/AntagRandomObjectivesSystem.cs b/Content.Server/Antag/AntagRandomObjectivesSystem.cs new file mode 100644 index 00000000000..c935b8c0648 --- /dev/null +++ b/Content.Server/Antag/AntagRandomObjectivesSystem.cs @@ -0,0 +1,52 @@ +using Content.Server.Antag.Components; +using Content.Server.Objectives; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; +using Content.Shared.Objectives.Systems; +using Robust.Shared.Random; + +namespace Content.Server.Antag; + +/// +/// Adds fixed objectives to an antag made with AntagRandomObjectivesComponent. +/// +public sealed class AntagRandomObjectivesSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly ObjectivesSystem _objectives = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAntagSelected); + } + + private void OnAntagSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) + { + if (!_mind.TryGetMind(args.Session, out var mindId, out var mind)) + { + Log.Error($"Antag {ToPrettyString(args.EntityUid):player} was selected by {ToPrettyString(ent):rule} but had no mind attached!"); + return; + } + + var difficulty = 0f; + foreach (var set in ent.Comp.Sets) + { + if (!_random.Prob(set.Prob)) + continue; + + for (var pick = 0; pick < set.MaxPicks && ent.Comp.MaxDifficulty > difficulty; pick++) + { + if (_objectives.GetRandomObjective(mindId, mind, set.Groups) is not {} objective) + continue; + + _mind.AddObjective(mindId, mind, objective); + var adding = Comp(objective).Difficulty; + difficulty += adding; + Log.Debug($"Added objective {ToPrettyString(objective):objective} to {ToPrettyString(args.EntityUid):player} with {adding} difficulty"); + } + } + } +} diff --git a/Content.Server/Antag/AntagSelectionSystem.API.cs b/Content.Server/Antag/AntagSelectionSystem.API.cs index 77f543cdcf1..8893d9a476d 100644 --- a/Content.Server/Antag/AntagSelectionSystem.API.cs +++ b/Content.Server/Antag/AntagSelectionSystem.API.cs @@ -147,7 +147,7 @@ public List> GetAntagMinds(Entity - /// Helper specifically for + /// Helper to get just the mind entities and not names. /// public List GetAntagMindEntityUids(Entity ent) { diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index 8efdc2738b9..b23afe23103 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles.Components; using Content.Server.Mind; +using Content.Server.Objectives; using Content.Server.Preferences.Managers; using Content.Server.Roles; using Content.Server.Roles.Jobs; @@ -51,6 +52,8 @@ public override void Initialize() SubscribeLocalEvent(OnTakeGhostRole); + SubscribeLocalEvent(OnObjectivesTextGetInfo); + SubscribeLocalEvent(OnPlayerSpawning); SubscribeLocalEvent(OnJobsAssigned); SubscribeLocalEvent(OnSpawnComplete); @@ -416,6 +419,15 @@ public bool IsEntityValid(EntityUid? entity, AntagSelectionDefinition def) return true; } + + private void OnObjectivesTextGetInfo(Entity ent, ref ObjectivesTextGetInfoEvent args) + { + if (ent.Comp.AgentName is not {} name) + return; + + args.Minds = ent.Comp.SelectedMinds; + args.AgentName = Loc.GetString(name); + } } /// diff --git a/Content.Server/Antag/Components/AntagObjectivesComponent.cs b/Content.Server/Antag/Components/AntagObjectivesComponent.cs new file mode 100644 index 00000000000..357c138f46b --- /dev/null +++ b/Content.Server/Antag/Components/AntagObjectivesComponent.cs @@ -0,0 +1,18 @@ +using Content.Server.Antag; +using Content.Shared.Objectives.Components; +using Robust.Shared.Prototypes; + +namespace Content.Server.Antag.Components; + +/// +/// Gives antags selected by this rule a fixed list of objectives. +/// +[RegisterComponent, Access(typeof(AntagObjectivesSystem))] +public sealed partial class AntagObjectivesComponent : Component +{ + /// + /// List of static objectives to give. + /// + [DataField(required: true)] + public List> Objectives = new(); +} diff --git a/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs b/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs new file mode 100644 index 00000000000..9a551acc499 --- /dev/null +++ b/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs @@ -0,0 +1,52 @@ +using Content.Server.Antag; +using Content.Shared.Random; +using Robust.Shared.Prototypes; + +namespace Content.Server.Antag.Components; + +/// +/// Gives antags selected by this rule a random list of objectives. +/// +[RegisterComponent, Access(typeof(AntagRandomObjectivesSystem))] +public sealed partial class AntagRandomObjectivesComponent : Component +{ + /// + /// Each set of objectives to add. + /// + [DataField(required: true)] + public List Sets = new(); + + /// + /// If the total difficulty of the currently given objectives exceeds, no more will be given. + /// + [DataField(required: true)] + public float MaxDifficulty; +} + +/// +/// A set of objectives to try picking. +/// Difficulty is checked over all sets, but each set has its own probability and pick count. +/// +[DataRecord] +public record struct AntagObjectiveSet() +{ + /// + /// The grouping used by the objective system to pick random objectives. + /// First a group is picked from these, then an objective from that group. + /// + [DataField(required: true)] + public ProtoId Groups = string.Empty; + + /// + /// Probability of this set being used. + /// + [DataField] + public float Prob = 1f; + + /// + /// Number of times to try picking objectives from this set. + /// Even if there is enough difficulty remaining, no more will be given after this. + /// + [DataField] + public int MaxPicks = 20; +} diff --git a/Content.Server/Antag/Components/AntagSelectionComponent.cs b/Content.Server/Antag/Components/AntagSelectionComponent.cs index 5b6699dab76..cae56c1a7bb 100644 --- a/Content.Server/Antag/Components/AntagSelectionComponent.cs +++ b/Content.Server/Antag/Components/AntagSelectionComponent.cs @@ -42,6 +42,13 @@ public sealed partial class AntagSelectionComponent : Component /// Is not serialized. /// public HashSet SelectedSessions = new(); + + /// + /// Locale id for the name of the antag. + /// If this is set then the antag is listed in the round-end summary. + /// + [DataField] + public LocId? AgentName; } [DataDefinition] diff --git a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs index 01a078625ae..6ad1e177755 100644 --- a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs @@ -8,23 +8,4 @@ namespace Content.Server.GameTicking.Rules.Components; /// Stores data for . /// [RegisterComponent, Access(typeof(ThiefRuleSystem))] -public sealed partial class ThiefRuleComponent : Component -{ - [DataField] - public ProtoId BigObjectiveGroup = "ThiefBigObjectiveGroups"; - - [DataField] - public ProtoId SmallObjectiveGroup = "ThiefObjectiveGroups"; - - [DataField] - public ProtoId EscapeObjectiveGroup = "ThiefEscapeObjectiveGroups"; - - [DataField] - public float BigObjectiveChance = 0.7f; - - [DataField] - public float MaxObjectiveDifficulty = 2.5f; - - [DataField] - public int MaxStealObjectives = 10; -} +public sealed partial class ThiefRuleComponent : Component; diff --git a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs index 47a4adeaf34..cec635b4caa 100644 --- a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs @@ -22,9 +22,6 @@ public sealed partial class TraitorRuleComponent : Component [DataField] public ProtoId SyndicateFaction = "Syndicate"; - [DataField] - public ProtoId ObjectiveGroup = "TraitorObjectiveGroups"; - [DataField] public ProtoId CodewordAdjectives = "adjectives"; @@ -72,7 +69,4 @@ public enum SelectionState /// [DataField] public int StartingBalance = 20; - - [DataField] - public int MaxDifficulty = 5; } diff --git a/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs b/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs index 81bdda706bd..0367aa1460c 100644 --- a/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs @@ -1,6 +1,8 @@ using Content.Server.GameTicking.Rules.Components; using Content.Server.Objectives; +using Content.Shared.Mind; using System.Diagnostics.CodeAnalysis; +using System.Linq; namespace Content.Server.GameTicking.Rules; @@ -47,7 +49,8 @@ public bool StartRule(string rule, EntityUid mindId, [NotNullWhen(true)] out Ent private void OnObjectivesTextGetInfo(EntityUid uid, GenericAntagRuleComponent comp, ref ObjectivesTextGetInfoEvent args) { - args.Minds = comp.Minds; + // just temporary until this is deleted + args.Minds = comp.Minds.Select(mindId => (mindId, Comp(mindId).CharacterName ?? "?")).ToList(); args.AgentName = Loc.GetString(comp.AgentName); } } diff --git a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs index 083085fa0d8..faec4a9e9ca 100644 --- a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs @@ -24,7 +24,6 @@ public override void Initialize() SubscribeLocalEvent(AfterAntagSelected); SubscribeLocalEvent(OnGetBriefing); - SubscribeLocalEvent(OnObjectivesTextGetInfo); } private void AfterAntagSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) @@ -33,41 +32,9 @@ private void AfterAntagSelected(Entity ent, ref AfterAntagEn return; //Generate objectives - GenerateObjectives(mindId, mind, ent); _antag.SendBriefing(args.EntityUid, MakeBriefing(args.EntityUid), null, null); } - private void GenerateObjectives(EntityUid mindId, MindComponent mind, ThiefRuleComponent thiefRule) - { - // Give thieves their objectives - var difficulty = 0f; - - if (_random.Prob(thiefRule.BigObjectiveChance)) // 70% chance to 1 big objective (structure or animal) - { - var objective = _objectives.GetRandomObjective(mindId, mind, thiefRule.BigObjectiveGroup); - if (objective != null) - { - _mindSystem.AddObjective(mindId, mind, objective.Value); - difficulty += Comp(objective.Value).Difficulty; - } - } - - for (var i = 0; i < thiefRule.MaxStealObjectives && thiefRule.MaxObjectiveDifficulty > difficulty; i++) // Many small objectives - { - var objective = _objectives.GetRandomObjective(mindId, mind, thiefRule.SmallObjectiveGroup); - if (objective == null) - continue; - - _mindSystem.AddObjective(mindId, mind, objective.Value); - difficulty += Comp(objective.Value).Difficulty; - } - - //Escape target - var escapeObjective = _objectives.GetRandomObjective(mindId, mind, thiefRule.EscapeObjectiveGroup); - if (escapeObjective != null) - _mindSystem.AddObjective(mindId, mind, escapeObjective.Value); - } - //Add mind briefing private void OnGetBriefing(Entity thief, ref GetBriefingEvent args) { @@ -87,10 +54,4 @@ private string MakeBriefing(EntityUid thief) briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n"; return briefing; } - - private void OnObjectivesTextGetInfo(Entity ent, ref ObjectivesTextGetInfoEvent args) - { - args.Minds = _antag.GetAntagMindEntityUids(ent.Owner); - args.AgentName = Loc.GetString("thief-round-end-agent-name"); - } } diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 3d0a02d6aa9..abf46b7b967 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -33,15 +33,12 @@ public sealed class TraitorRuleSystem : GameRuleSystem [Dependency] private readonly SharedJobSystem _jobs = default!; [Dependency] private readonly ObjectivesSystem _objectives = default!; - public const int MaxPicks = 20; - public override void Initialize() { base.Initialize(); SubscribeLocalEvent(AfterEntitySelected); - SubscribeLocalEvent(OnObjectivesTextGetInfo); SubscribeLocalEvent(OnObjectivesTextPrepend); } @@ -69,7 +66,7 @@ private void MakeCodewords(TraitorRuleComponent component) } } - public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true) + public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true) { //Grab the mind if it wasnt provided if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind)) @@ -122,37 +119,16 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool RaiseLocalEvent(traitor, new MoodEffectEvent("TraitorFocused")); - // Give traitors their objectives - if (giveObjectives) - { - var difficulty = 0f; - for (var pick = 0; pick < MaxPicks && component.MaxDifficulty > difficulty; pick++) - { - var objective = _objectives.GetRandomObjective(mindId, mind, component.ObjectiveGroup); - if (objective == null) - continue; - - _mindSystem.AddObjective(mindId, mind, objective.Value); - var adding = Comp(objective.Value).Difficulty; - difficulty += adding; - Log.Debug($"Added objective {ToPrettyString(objective):objective} with {adding} difficulty"); - } - } - return true; } - private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args) - { - args.Minds = _antag.GetAntagMindEntityUids(uid); - args.AgentName = Loc.GetString("traitor-round-end-agent-name"); - } - + // TODO: AntagCodewordsComponent private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args) { args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords))); } + // TODO: figure out how to handle this? add priority to briefing event? private string GenerateBriefing(string[] codewords, Note[]? uplinkCode, string? objectiveIssuer = null) { var sb = new StringBuilder(); diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index bf013bc0402..18077b413ad 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -36,14 +36,14 @@ public override void Initialize() private void OnRoundEndText(RoundEndTextAppendEvent ev) { // go through each gamerule getting data for the roundend summary. - var summaries = new Dictionary>>(); + var summaries = new Dictionary>>(); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var gameRule)) { if (!_gameTicker.IsGameRuleAdded(uid, gameRule)) continue; - var info = new ObjectivesTextGetInfoEvent(new List(), string.Empty); + var info = new ObjectivesTextGetInfoEvent(new List<(EntityUid, string)>(), string.Empty); RaiseLocalEvent(uid, ref info); if (info.Minds.Count == 0) continue; @@ -51,7 +51,7 @@ private void OnRoundEndText(RoundEndTextAppendEvent ev) // first group the gamerules by their agents, for example 2 different dragons var agent = info.AgentName; if (!summaries.ContainsKey(agent)) - summaries[agent] = new Dictionary>(); + summaries[agent] = new Dictionary>(); var prepend = new ObjectivesTextPrependEvent(""); RaiseLocalEvent(uid, ref prepend); @@ -79,7 +79,7 @@ private void OnRoundEndText(RoundEndTextAppendEvent ev) foreach (var (_, minds) in summary) { total += minds.Count; - totalInCustody += minds.Where(m => IsInCustody(m)).Count(); + totalInCustody += minds.Where(pair => IsInCustody(pair.Item1)).Count(); } var result = new StringBuilder(); @@ -104,19 +104,16 @@ private void OnRoundEndText(RoundEndTextAppendEvent ev) } } - private void AddSummary(StringBuilder result, string agent, List minds) + private void AddSummary(StringBuilder result, string agent, List<(EntityUid, string)> minds) { var agentSummaries = new List<(string summary, float successRate, int completedObjectives)>(); - foreach (var mindId in minds) + foreach (var (mindId, name) in minds) { - if (!TryComp(mindId, out MindComponent? mind)) - continue; - - var title = GetTitle(mindId, mind); - if (title == null) + if (!TryComp(mindId, out var mind)) continue; + var title = GetTitle((mindId, mind), name); var custody = IsInCustody(mindId, mind) ? Loc.GetString("objectives-in-custody") : string.Empty; var objectives = mind.Objectives; @@ -238,34 +235,18 @@ private bool IsInCustody(EntityUid mindId, MindComponent? mind = null) /// /// Get the title for a player's mind used in round end. + /// Pass in the original entity name which is shown alongside username. /// - public string? GetTitle(EntityUid mindId, MindComponent? mind = null) + public string GetTitle(Entity mind, string name) { - if (!Resolve(mindId, ref mind)) - return null; - - var name = mind.CharacterName; - var username = (string?) null; - - if (mind.OriginalOwnerUserId != null && - _player.TryGetPlayerData(mind.OriginalOwnerUserId.Value, out var sessionData)) + if (Resolve(mind, ref mind.Comp) && + mind.Comp.OriginalOwnerUserId != null && + _player.TryGetPlayerData(mind.Comp.OriginalOwnerUserId.Value, out var sessionData)) { - username = sessionData.UserName; + var username = sessionData.UserName; + return Loc.GetString("objectives-player-user-named", ("user", username), ("name", name)); } - - if (username != null) - { - if (name != null) - return Loc.GetString("objectives-player-user-named", ("user", username), ("name", name)); - - return Loc.GetString("objectives-player-user", ("user", username)); - } - - // nothing to identify the player by, just give up - if (name == null) - return null; - return Loc.GetString("objectives-player-named", ("name", name)); } } @@ -279,7 +260,7 @@ private bool IsInCustody(EntityUid mindId, MindComponent? mind = null) /// The objectives system already checks if the game rule is added so you don't need to check that in this event's handler. /// [ByRefEvent] -public record struct ObjectivesTextGetInfoEvent(List Minds, string AgentName); +public record struct ObjectivesTextGetInfoEvent(List<(EntityUid, string)> Minds, string AgentName); /// /// Raised on the game rule before text for each agent's objectives is added, letting you prepend something. diff --git a/Resources/Locale/en-US/objectives/round-end.ftl b/Resources/Locale/en-US/objectives/round-end.ftl index b4314b2caff..3da81fc9640 100644 --- a/Resources/Locale/en-US/objectives/round-end.ftl +++ b/Resources/Locale/en-US/objectives/round-end.ftl @@ -6,7 +6,6 @@ objectives-round-end-result = {$count -> objectives-round-end-result-in-custody = {$custody} out of {$count} {MAKEPLURAL($agent)} were in custody. objectives-player-user-named = [color=White]{$name}[/color] ([color=gray]{$user}[/color]) -objectives-player-user = [color=gray]{$user}[/color] objectives-player-named = [color=White]{$name}[/color] objectives-no-objectives = {$custody}{$title} was a {$agent}. diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 801dcc4b859..d4a58f961aa 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -402,9 +402,9 @@ prototype: Nukeops - type: entity + noSpawn: true + parent: BaseTraitorRule id: SleeperAgentsRule - parent: BaseGameRule - categories: [ HideSpawnMenu ] components: - type: StationEvent earliestStart: 25 @@ -413,7 +413,6 @@ reoccurrenceDelay: 30 startAnnouncement: false - type: AlertLevelInterceptionRule - - type: TraitorRule - type: AntagSelection definitions: - prefRoles: [ Traitor ] diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index fca0073b4e5..ba2cd530480 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -35,7 +35,19 @@ id: Thief components: - type: ThiefRule + - type: AntagObjectives + objectives: + - EscapeThiefShuttleObjective + - type: AntagRandomObjectives + sets: + - groups: ThiefBigObjectiveGroups + prob: 0.7 + maxPicks: 1 + - groups: ThiefObjectiveGroups + maxPicks: 10 + maxDifficulty: 2.5 - type: AntagSelection + agentName: thief-round-end-agent-name definitions: - prefRoles: [ Thief ] maxRange: diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 4ae53c9b37a..a433f10d062 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -134,16 +134,30 @@ prototype: Nukeops - type: entity - id: Traitor + abstract: true parent: BaseGameRule + id: BaseTraitorRule + components: + - type: TraitorRule + # TODO: codewords in yml + # TODO: uplink in yml + - type: AntagRandomObjectives + sets: + - groups: TraitorObjectiveGroups + maxDifficulty: 5 + - type: AntagSelection + agentName: traitor-round-end-agent-name + +- type: entity categories: [ HideSpawnMenu ] + parent: BaseTraitorRule + id: Traitor components: - type: GameRule minPlayers: 5 delay: min: 240 max: 420 - - type: TraitorRule - type: AntagSelection definitions: - prefRoles: [ Traitor ] diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index fb4ce6f4a16..d7e08841307 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -61,11 +61,6 @@ ThiefObjectiveGroupStructure: 0 #Temporarily disabled until obvious ways to steal structures are added ThiefObjectiveGroupAnimal: 2 -- type: weightedRandom - id: ThiefEscapeObjectiveGroups - weights: - ThiefObjectiveGroupEscape: 1 - - type: weightedRandom id: ThiefObjectiveGroupCollection weights: From ef2ea95edefbd814a485a43e255122b837fa2045 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 01:48:14 -0400 Subject: [PATCH 005/366] keep up --- Resources/Prototypes/GameRules/events.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index d4a58f961aa..d2780011c96 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -402,7 +402,7 @@ prototype: Nukeops - type: entity - noSpawn: true + categories: [ HideSpawnMenu ] parent: BaseTraitorRule id: SleeperAgentsRule components: From 1fd02db8912480b2988aa7fd45929599e2119ebc Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Sat, 25 May 2024 20:15:56 +0000 Subject: [PATCH 006/366] move nukie profile loading into its own rule (#28208) * move profile loading out of nukeops rule * make BaseNukeopsRule and use AntagLoadProfileRule * untroll --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Rules/AntagLoadProfileRuleSystem.cs | 39 +++++++++++++++++++ .../AntagLoadProfileRuleCOmponent.cs | 7 ++++ .../GameTicking/Rules/NukeopsRuleSystem.cs | 29 -------------- Resources/Prototypes/GameRules/events.yml | 4 +- Resources/Prototypes/GameRules/roundstart.yml | 16 ++++++-- 5 files changed, 60 insertions(+), 35 deletions(-) create mode 100644 Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs create mode 100644 Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs diff --git a/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs b/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs new file mode 100644 index 00000000000..fd3fb6cd655 --- /dev/null +++ b/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs @@ -0,0 +1,39 @@ +using Content.Server.Antag; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Humanoid; +using Content.Server.Preferences.Managers; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Preferences; +using Robust.Shared.Prototypes; + +namespace Content.Server.GameTicking.Rules; + +public sealed class AntagLoadProfileRuleSystem : GameRuleSystem +{ + [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IServerPreferencesManager _prefs = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSelectEntity); + } + + private void OnSelectEntity(Entity ent, ref AntagSelectEntityEvent args) + { + if (args.Handled) + return; + + var profile = args.Session != null + ? _prefs.GetPreferences(args.Session.UserId).SelectedCharacter as HumanoidCharacterProfile + : HumanoidCharacterProfile.RandomWithSpecies(); + if (profile?.Species is not {} speciesId || !_proto.TryIndex(speciesId, out var species)) + species = _proto.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); + + args.Entity = Spawn(species.Prototype); + _humanoid.LoadProfile(args.Entity.Value, profile); + } +} diff --git a/Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs b/Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs new file mode 100644 index 00000000000..5e58fd14fc0 --- /dev/null +++ b/Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server.GameTicking.Rules.Components; + +/// +/// Makes this rules antags spawn a humanoid, either from the player's profile or a random one. +/// +[RegisterComponent] +public sealed partial class AntagLoadProfileRuleComponent : Component; diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 7cc51db5766..c3e8e90f843 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -1,13 +1,9 @@ using Content.Server.Antag; using Content.Server.Communications; using Content.Server.GameTicking.Rules.Components; -using Content.Server.Humanoid; -using Content.Server.NPC.Components; -using Content.Server.NPC.Systems; using Content.Server.Nuke; using Content.Server.NukeOps; using Content.Server.Popups; -using Content.Server.Preferences.Managers; using Content.Server.Roles; using Content.Server.RoundEnd; using Content.Server.Shuttles.Events; @@ -15,19 +11,14 @@ using Content.Server.Station.Components; using Content.Server.Store.Components; using Content.Server.Store.Systems; -using Content.Shared.GameTicking.Components; -using Content.Shared.Humanoid; -using Content.Shared.Humanoid.Prototypes; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Nuke; using Content.Shared.NukeOps; -using Content.Shared.Preferences; using Content.Shared.Store; using Content.Shared.Tag; using Content.Shared.Zombies; using Robust.Shared.Map; -using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; using System.Linq; @@ -36,10 +27,7 @@ namespace Content.Server.GameTicking.Rules; public sealed class NukeopsRuleSystem : GameRuleSystem { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IServerPreferencesManager _prefs = default!; [Dependency] private readonly EmergencyShuttleSystem _emergency = default!; - [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; @@ -71,7 +59,6 @@ public override void Initialize() SubscribeLocalEvent(OnWarDeclared); SubscribeLocalEvent(OnShuttleCallAttempt); - SubscribeLocalEvent(OnAntagSelectEntity); SubscribeLocalEvent(OnAfterAntagEntSelected); } @@ -471,22 +458,6 @@ private void CheckRoundShouldEnd(Entity ent) nukeops.RoundEndBehavior = RoundEndBehavior.Nothing; } - // this should really go anywhere else but im tired. - private void OnAntagSelectEntity(Entity ent, ref AntagSelectEntityEvent args) - { - if (args.Handled) - return; - - var profile = args.Session != null - ? _prefs.GetPreferences(args.Session.UserId).SelectedCharacter as HumanoidCharacterProfile - : HumanoidCharacterProfile.RandomWithSpecies(); - if (!_prototypeManager.TryIndex(profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies, out SpeciesPrototype? species)) - species = _prototypeManager.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); - - args.Entity = Spawn(species.Prototype); - _humanoid.LoadProfile(args.Entity.Value, profile!); - } - private void OnAfterAntagEntSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) { if (ent.Comp.TargetStation is not { } station) diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index d2780011c96..a0649eb2d6b 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -368,9 +368,9 @@ prototype: InitialInfected - type: entity - id: LoneOpsSpawn - parent: BaseGameRule categories: [ HideSpawnMenu ] + parent: BaseNukeopsRule + id: LoneOpsSpawn components: - type: StationEvent earliestStart: 35 diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index a433f10d062..80354861ade 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -64,17 +64,25 @@ roundEndDelay: 10 - type: entity - id: Nukeops + abstract: true parent: BaseGameRule - categories: [ HideSpawnMenu ] + id: BaseNukeopsRule components: - - type: GameRule - minPlayers: 35 - type: RandomMetadata #this generates the random operation name cuz it's cool. nameSegments: - operationPrefix - operationSuffix - type: NukeopsRule + - type: AntagSelection + - type: AntagLoadProfileRule + +- type: entity + categories: [ HideSpawnMenu ] + parent: BaseNukeopsRule + id: Nukeops + components: + - type: GameRule + minPlayers: 20 - type: LoadMapRule gameMap: NukieOutpost - type: AntagSelection From 4939a31b158ca33295512acf314f6d0512e9ef5f Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Thu, 11 Jul 2024 05:55:56 +0000 Subject: [PATCH 007/366] item toggling giga rework + full ninja refactor (#28039) * item toggle refactoring and some new systems * add ToggleClothing component/system * unhardcode magboots gravity logic * make magboots and speedboots use ItemToggle and stuff * remove now useless clothing components * update client/server magboots systems * add note to use ItemToggledEvent in ToggleActionEvent doc * refactor PowerCellDraw to use ItemToggle for ui open/close control * add TryUseCharges, refactor charges system * update magboot trigger code * make borg use ItemToggle, network SelectedModule instead of now removed Activated * add AccessToggle for borg * the giga ninja refactor * update ninja yml * update ItemToggle usage for some stuff * fix activatableui requires power * random fixing * yaml fixing * nuke ItemToggleDisarmMalus * make defib use ItemToggle * make things that use power not turn on if missing use charge * pro * fix sound prediction * bruh * proximity detector use ItemToggle * oop * big idiot syndrome * fix ninja spawn rule and make it generic * fix ninja spawn rule yml * move loading profiles into AntagLoadProfileRule * more ninja refactor * ninja yml fixes * the dreaded copy paste ops * remove useless NinjaRuleComponent and ue AntagSelection for greeting * fix invisibility * move IsCompleted to SharedObjectivesSystem * ability fixes * oop fix powercell instantly draining itself * sentient speedboots gaming * make reflect use ItemToggle * fix other test * loadprofilerule moved into its own pr * remove conflict with dragon refactor * remove all GenericAntag code from ninja * ) * probably * remove old enabled * great language bravo vince * GREAT LANGUAGE * who made this language * because it stinks * reparent blood-red magboots to magboots probbbly works * most of the review stuff * hasGrav doesnt mean what i thought it did * make health analyzer use itemtoggle, not fail test * fix mag/speed boots being wacky * UNTROLL * add ItemToggle to the random health analyzers * a * remove unused obsolete borg func * untrolling * :trollface: * fix test * fix * g * untroll --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Items/Systems/ItemToggleSystem.cs | 9 - .../Ninja/Systems/ItemCreatorSystem.cs | 5 + .../Ninja/Systems/NinjaGlovesSystem.cs | 7 +- .../Ninja/Systems/NinjaSuitSystem.cs | 21 +- Content.Client/Ninja/Systems/NinjaSystem.cs | 9 +- .../Ninja/Systems/SpiderChargeSystem.cs | 5 + .../Interaction/InteractionTest.Helpers.cs | 2 +- .../Tests/Interaction/InteractionTest.cs | 4 +- .../Components/AutoRechargeComponent.cs | 1 + .../Charges/Systems/ChargesSystem.cs | 12 +- .../Rules/Components/NinjaRuleComponent.cs | 27 --- .../ItemToggleDisarmMalusComponent.cs | 22 --- .../Components/ItemToggleSharpComponent.cs | 9 - .../Item/ItemToggle/ItemToggleSystem.cs | 44 ----- .../Components/HealthAnalyzerComponent.cs | 3 + Content.Server/Medical/DefibrillatorSystem.cs | 72 +------ .../Medical/HealthAnalyzerSystem.cs | 21 +- .../Ninja/Events/BatteryChangedEvent.cs | 2 +- .../Ninja/Systems/BatteryDrainerSystem.cs | 21 +- .../Ninja/Systems/ItemCreatorSystem.cs | 57 ++++++ .../Ninja/Systems/NinjaGlovesSystem.cs | 102 +++------- .../Ninja/Systems/NinjaSuitSystem.cs | 79 ++------ .../Ninja/Systems/SpaceNinjaSystem.cs | 80 +------- .../Ninja/Systems/SpiderChargeSystem.cs | 3 +- .../Ninja/Systems/StunProviderSystem.cs | 22 ++- .../Objectives/Systems/CodeConditionSystem.cs | 14 -- .../PowerCell/PowerCellSystem.Draw.cs | 38 ++-- Content.Server/PowerCell/PowerCellSystem.cs | 2 +- .../Silicons/Borgs/BorgSystem.Modules.cs | 4 +- Content.Server/Silicons/Borgs/BorgSystem.cs | 79 +++----- .../Components/NinjaSpawnRuleComponent.cs | 16 -- .../Components/SpaceSpawnRuleComponent.cs | 25 +++ .../{NinjaSpawnRule.cs => SpaceSpawnRule.cs} | 38 ++-- .../Stunnable/Systems/StunbatonSystem.cs | 2 +- .../Weapons/Misc/TetherGunSystem.cs | 6 +- .../Systems/ArtifactMagnetTriggerSystem.cs | 9 +- .../Components/AccessToggleComponent.cs | 11 ++ .../Access/Systems/AccessToggleSystem.cs | 21 ++ .../Beeper/Components/BeeperComponent.cs | 11 +- Content.Shared/Beeper/Systems/BeeperSystem.cs | 37 ++-- .../Beeper/Systems/ProximityBeeperSystem.cs | 84 +------- .../Charges/Systems/SharedChargesSystem.cs | 33 +++- .../ClothingSpeedModifierComponent.cs | 19 +- .../Clothing/ClothingSpeedModifierSystem.cs | 107 ++--------- .../Components/StealthClothingComponent.cs | 43 ----- .../Components/ToggleClothingComponent.cs | 40 ++++ .../ToggleClothingSpeedComponent.cs | 35 ---- .../EntitySystems/StealthClothingSystem.cs | 144 -------------- .../EntitySystems/ToggleClothingSystem.cs | 58 ++++++ Content.Shared/Clothing/MagbootsComponent.cs | 20 +- Content.Shared/Clothing/MagbootsSystem.cs | 90 +++++++++ .../Clothing/SharedMagbootsSystem.cs | 101 ---------- .../Item/ItemToggle/ComponentTogglerSystem.cs | 26 +++ .../Components/ComponentTogglerComponent.cs | 32 ++++ .../ItemToggleActiveSoundComponent.cs | 4 +- .../Components/ItemToggleComponent.cs | 9 +- .../Components/ToggleVerbComponent.cs | 18 ++ ...temToggleSystem.cs => ItemToggleSystem.cs} | 180 +++++++++--------- .../Item/ItemToggle/ToggleVerbSystem.cs | 34 ++++ .../Medical/DefibrillatorComponent.cs | 16 +- .../Components/BatteryDrainerComponent.cs | 12 +- .../Components/BombingTargetComponent.cs | 4 +- .../Ninja/Components/DashAbilityComponent.cs | 14 +- .../Ninja/Components/EmagProviderComponent.cs | 13 +- .../Ninja/Components/EnergyKatanaComponent.cs | 4 +- .../Ninja/Components/ItemCreatorComponent.cs | 52 +++++ .../Ninja/Components/NinjaGlovesComponent.cs | 44 +++-- .../Ninja/Components/NinjaSuitComponent.cs | 87 ++------- .../Ninja/Components/SpaceNinjaComponent.cs | 19 +- .../Ninja/Components/SpiderChargeComponent.cs | 9 +- .../Ninja/Components/StunProviderComponent.cs | 38 ++-- .../Ninja/Systems/DashAbilitySystem.cs | 80 +++----- .../Ninja/Systems/EmagProviderSystem.cs | 27 +-- .../Ninja/Systems/EnergyKatanaSystem.cs | 25 +-- .../Ninja/Systems/ItemCreatorSystem.cs | 56 ++++++ .../Systems/SharedBatteryDrainerSystem.cs | 25 ++- .../Ninja/Systems/SharedNinjaGlovesSystem.cs | 108 ++++++----- .../Ninja/Systems/SharedNinjaSuitSystem.cs | 154 ++++++++------- .../Ninja/Systems/SharedSpaceNinjaSystem.cs | 59 +++--- .../Ninja/Systems/SharedSpiderChargeSystem.cs | 6 + .../Ninja/Systems/SharedStunProviderSystem.cs | 18 +- .../Systems/SharedObjectivesSystem.cs | 35 +++- .../Pinpointer/SharedProximityBeeper.cs | 9 - .../PowerCell/PowerCellDrawComponent.cs | 18 +- .../PowerCell/SharedPowerCellSystem.cs | 27 ++- .../Components/ProximityDetectorComponent.cs | 8 +- .../Systems/ProximityDetectionSystem.cs | 86 ++++----- .../Borgs/Components/BorgChassisComponent.cs | 8 +- .../Silicons/Borgs/SharedBorgSystem.cs | 4 +- .../Toggleable/ToggleActionEvent.cs | 7 +- .../Tools/Systems/SharedToolSystem.cs | 2 +- .../ActivatableUISystem.Power.cs | 24 ++- .../Weapons/Reflect/ReflectComponent.cs | 7 +- .../Weapons/Reflect/ReflectSystem.cs | 32 ++-- Resources/Prototypes/Actions/ninja.yml | 10 +- .../Entities/Clothing/Hands/gloves.yml | 27 ++- .../Entities/Clothing/Head/misc.yml | 30 +++ .../Entities/Clothing/OuterClothing/suits.yml | 26 ++- .../Entities/Clothing/Shoes/magboots.yml | 155 ++++----------- .../Entities/Clothing/Shoes/misc.yml | 31 ++- .../Entities/Clothing/base_clothing.yml | 9 + .../Entities/Markers/Spawners/ghost_roles.yml | 7 +- .../Mobs/Cyborgs/base_borg_chassis.yml | 5 + .../Prototypes/Entities/Mobs/Player/human.yml | 25 --- .../Objects/Devices/base_handheld.yml | 11 ++ .../Entities/Objects/Devices/pda.yml | 3 + .../Entities/Objects/Devices/station_map.yml | 11 +- .../Entities/Objects/Shields/shields.yml | 13 +- .../Objects/Specific/Medical/defib.yml | 5 + .../Medical/handheld_crew_monitor.yml | 9 +- .../Specific/Medical/healthanalyzer.yml | 2 + .../Objects/Specific/Research/anomaly.yml | 9 +- .../Objects/Tools/handheld_mass_scanner.yml | 3 +- .../Entities/Objects/Tools/welders.yml | 6 +- .../Weapons/Guns/Launchers/launchers.yml | 4 + .../Objects/Weapons/Melee/e_sword.yml | 43 ++--- .../Entities/Objects/Weapons/Melee/sword.yml | 14 +- .../Structures/Machines/Medical/cryo_pod.yml | 1 + Resources/Prototypes/GameRules/events.yml | 38 +++- Resources/Prototypes/GameRules/midround.yml | 31 --- 120 files changed, 1653 insertions(+), 2041 deletions(-) delete mode 100644 Content.Client/Items/Systems/ItemToggleSystem.cs create mode 100644 Content.Client/Ninja/Systems/ItemCreatorSystem.cs create mode 100644 Content.Client/Ninja/Systems/SpiderChargeSystem.cs delete mode 100644 Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs delete mode 100644 Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs delete mode 100644 Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs delete mode 100644 Content.Server/Item/ItemToggle/ItemToggleSystem.cs create mode 100644 Content.Server/Ninja/Systems/ItemCreatorSystem.cs delete mode 100644 Content.Server/StationEvents/Components/NinjaSpawnRuleComponent.cs create mode 100644 Content.Server/StationEvents/Components/SpaceSpawnRuleComponent.cs rename Content.Server/StationEvents/Events/{NinjaSpawnRule.cs => SpaceSpawnRule.cs} (53%) create mode 100644 Content.Shared/Access/Components/AccessToggleComponent.cs create mode 100644 Content.Shared/Access/Systems/AccessToggleSystem.cs delete mode 100644 Content.Shared/Clothing/Components/StealthClothingComponent.cs create mode 100644 Content.Shared/Clothing/Components/ToggleClothingComponent.cs delete mode 100644 Content.Shared/Clothing/Components/ToggleClothingSpeedComponent.cs delete mode 100644 Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs create mode 100644 Content.Shared/Clothing/EntitySystems/ToggleClothingSystem.cs create mode 100644 Content.Shared/Clothing/MagbootsSystem.cs delete mode 100644 Content.Shared/Clothing/SharedMagbootsSystem.cs create mode 100644 Content.Shared/Item/ItemToggle/ComponentTogglerSystem.cs create mode 100644 Content.Shared/Item/ItemToggle/Components/ComponentTogglerComponent.cs create mode 100644 Content.Shared/Item/ItemToggle/Components/ToggleVerbComponent.cs rename Content.Shared/Item/ItemToggle/{SharedItemToggleSystem.cs => ItemToggleSystem.cs} (67%) create mode 100644 Content.Shared/Item/ItemToggle/ToggleVerbSystem.cs create mode 100644 Content.Shared/Ninja/Components/ItemCreatorComponent.cs create mode 100644 Content.Shared/Ninja/Systems/ItemCreatorSystem.cs create mode 100644 Content.Shared/Ninja/Systems/SharedSpiderChargeSystem.cs delete mode 100644 Content.Shared/Pinpointer/SharedProximityBeeper.cs create mode 100644 Resources/Prototypes/Entities/Objects/Devices/base_handheld.yml diff --git a/Content.Client/Items/Systems/ItemToggleSystem.cs b/Content.Client/Items/Systems/ItemToggleSystem.cs deleted file mode 100644 index 46d6f1b464d..00000000000 --- a/Content.Client/Items/Systems/ItemToggleSystem.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Shared.Item.ItemToggle; - -namespace Content.Shared.Item; - -/// -public sealed class ItemToggleSystem : SharedItemToggleSystem -{ - -} diff --git a/Content.Client/Ninja/Systems/ItemCreatorSystem.cs b/Content.Client/Ninja/Systems/ItemCreatorSystem.cs new file mode 100644 index 00000000000..9ab62cc12db --- /dev/null +++ b/Content.Client/Ninja/Systems/ItemCreatorSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Ninja.Systems; + +namespace Content.Client.Ninja.Systems; + +public sealed class ItemCreatorSystem : SharedItemCreatorSystem; diff --git a/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs b/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs index 7758c3d7e2b..5b07b1588fd 100644 --- a/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs +++ b/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs @@ -2,9 +2,4 @@ namespace Content.Client.Ninja.Systems; -/// -/// Does nothing special, only exists to provide a client implementation. -/// -public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem -{ -} +public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem; diff --git a/Content.Client/Ninja/Systems/NinjaSuitSystem.cs b/Content.Client/Ninja/Systems/NinjaSuitSystem.cs index fde1801b37d..852ea8af46e 100644 --- a/Content.Client/Ninja/Systems/NinjaSuitSystem.cs +++ b/Content.Client/Ninja/Systems/NinjaSuitSystem.cs @@ -1,24 +1,5 @@ -using Content.Shared.Clothing.EntitySystems; -using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Systems; namespace Content.Client.Ninja.Systems; -/// -/// Disables cloak prediction since client has no knowledge of battery power. -/// Cloak will still be enabled after server tells it. -/// -public sealed class NinjaSuitSystem : SharedNinjaSuitSystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAttemptStealth); - } - - private void OnAttemptStealth(EntityUid uid, NinjaSuitComponent comp, AttemptStealthEvent args) - { - args.Cancel(); - } -} +public sealed class NinjaSuitSystem : SharedNinjaSuitSystem; diff --git a/Content.Client/Ninja/Systems/NinjaSystem.cs b/Content.Client/Ninja/Systems/NinjaSystem.cs index aa2fa2047f1..958dc6a5d9a 100644 --- a/Content.Client/Ninja/Systems/NinjaSystem.cs +++ b/Content.Client/Ninja/Systems/NinjaSystem.cs @@ -2,11 +2,4 @@ namespace Content.Client.Ninja.Systems; -/// -/// Currently does nothing special clientside. -/// All functionality is in shared and server. -/// Only exists to prevent crashing. -/// -public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem -{ -} +public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem; diff --git a/Content.Client/Ninja/Systems/SpiderChargeSystem.cs b/Content.Client/Ninja/Systems/SpiderChargeSystem.cs new file mode 100644 index 00000000000..b107fd3867d --- /dev/null +++ b/Content.Client/Ninja/Systems/SpiderChargeSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Ninja.Systems; + +namespace Content.Client.Ninja.Systems; + +public sealed class SpiderChargeSystem : SharedSpiderChargeSystem; diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs index a19b62cd70a..0f2c314ed01 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs @@ -171,7 +171,7 @@ await Server.WaitPost(() => // turn on welders if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated) { - Assert.That(ItemToggleSys.TryActivate(item, playerEnt, itemToggle: itemToggle)); + Assert.That(ItemToggleSys.TryActivate((item, itemToggle), user: playerEnt)); } }); diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index 457d3e31920..b3d684e01a0 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -104,7 +104,7 @@ public abstract partial class InteractionTest protected Content.Server.Construction.ConstructionSystem SConstruction = default!; protected SharedDoAfterSystem DoAfterSys = default!; protected ToolSystem ToolSys = default!; - protected SharedItemToggleSystem ItemToggleSys = default!; + protected ItemToggleSystem ItemToggleSys = default!; protected InteractionTestSystem STestSystem = default!; protected SharedTransformSystem Transform = default!; protected SharedMapSystem MapSystem = default!; @@ -165,7 +165,7 @@ public virtual async Task Setup() HandSys = SEntMan.System(); InteractSys = SEntMan.System(); ToolSys = SEntMan.System(); - ItemToggleSys = SEntMan.System(); + ItemToggleSys = SEntMan.System(); DoAfterSys = SEntMan.System(); Transform = SEntMan.System(); MapSystem = SEntMan.System(); diff --git a/Content.Server/Charges/Components/AutoRechargeComponent.cs b/Content.Server/Charges/Components/AutoRechargeComponent.cs index 9dcf555ea93..165b181dcbc 100644 --- a/Content.Server/Charges/Components/AutoRechargeComponent.cs +++ b/Content.Server/Charges/Components/AutoRechargeComponent.cs @@ -7,6 +7,7 @@ namespace Content.Server.Charges.Components; /// Something with limited charges that can be recharged automatically. /// Requires LimitedChargesComponent to function. /// +// TODO: no reason this cant be predicted and server system deleted [RegisterComponent, AutoGenerateComponentPause] [Access(typeof(ChargesSystem))] public sealed partial class AutoRechargeComponent : Component diff --git a/Content.Server/Charges/Systems/ChargesSystem.cs b/Content.Server/Charges/Systems/ChargesSystem.cs index 03e192e680e..974928ee4bb 100644 --- a/Content.Server/Charges/Systems/ChargesSystem.cs +++ b/Content.Server/Charges/Systems/ChargesSystem.cs @@ -37,15 +37,17 @@ protected override void OnExamine(EntityUid uid, LimitedChargesComponent comp, E args.PushMarkup(Loc.GetString("limited-charges-recharging", ("seconds", timeRemaining))); } - public override void UseCharge(EntityUid uid, LimitedChargesComponent? comp = null) + public override void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp = null) { - if (!Resolve(uid, ref comp, false)) + if (!Query.Resolve(uid, ref comp, false)) return; var startRecharge = comp.Charges == comp.MaxCharges; - base.UseCharge(uid, comp); - // start the recharge time after first use at full charge - if (startRecharge && TryComp(uid, out var recharge)) + base.AddCharges(uid, change, comp); + + // if a charge was just used from full, start the recharge timer + // TODO: probably make this an event instead of having le server system that just does this + if (change < 0 && startRecharge && TryComp(uid, out var recharge)) recharge.NextChargeTime = _timing.CurTime + recharge.RechargeDuration; } } diff --git a/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs deleted file mode 100644 index fa352eb320b..00000000000 --- a/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Content.Server.Ninja.Systems; -using Content.Shared.Communications; -using Content.Shared.Random; -using Robust.Shared.Audio; -using Robust.Shared.Prototypes; - -namespace Content.Server.GameTicking.Rules.Components; - -/// -/// Stores some configuration used by the ninja system. -/// Objectives and roundend summary are handled by . -/// -[RegisterComponent, Access(typeof(SpaceNinjaSystem))] -public sealed partial class NinjaRuleComponent : Component -{ - /// - /// List of threats that can be called in. Copied onto when gloves are enabled. - /// - [DataField(required: true)] - public ProtoId Threats = string.Empty; - - /// - /// Sound played when making the player a ninja via antag control or ghost role - /// - [DataField] - public SoundSpecifier? GreetingSound = new SoundPathSpecifier("/Audio/Misc/ninja_greeting.ogg"); -} diff --git a/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs b/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs deleted file mode 100644 index 30fa84ed90b..00000000000 --- a/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Content.Server.Item; - -/// -/// 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 deleted file mode 100644 index 227491b16c2..00000000000 --- a/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Content.Server.Item; - -/// -/// Handles whether this item is sharp when toggled on. -/// -[RegisterComponent] -public sealed partial class ItemToggleSharpComponent : Component -{ -} diff --git a/Content.Server/Item/ItemToggle/ItemToggleSystem.cs b/Content.Server/Item/ItemToggle/ItemToggleSystem.cs deleted file mode 100644 index f98415eb08f..00000000000 --- a/Content.Server/Item/ItemToggle/ItemToggleSystem.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Content.Server.CombatMode.Disarm; -using Content.Server.Kitchen.Components; -using Content.Shared.Item.ItemToggle; -using Content.Shared.Item.ItemToggle.Components; - -namespace Content.Server.Item; - -public sealed class ItemToggleSystem : SharedItemToggleSystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(ToggleSharp); - SubscribeLocalEvent(ToggleMalus); - } - - private void ToggleSharp(Entity ent, ref ItemToggledEvent args) - { - // 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) - { - 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/Medical/Components/HealthAnalyzerComponent.cs b/Content.Server/Medical/Components/HealthAnalyzerComponent.cs index f0b56cbd195..9d25db680ec 100644 --- a/Content.Server/Medical/Components/HealthAnalyzerComponent.cs +++ b/Content.Server/Medical/Components/HealthAnalyzerComponent.cs @@ -6,6 +6,9 @@ namespace Content.Server.Medical.Components; /// /// After scanning, retrieves the target Uid to use with its related UI. /// +/// +/// Requires ItemToggleComponent. +/// [RegisterComponent, AutoGenerateComponentPause] [Access(typeof(HealthAnalyzerSystem), typeof(CryoPodSystem))] public sealed partial class HealthAnalyzerComponent : Component diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index 7b0b4110201..d0c650c4e84 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Interaction; using Content.Shared.Interaction.Components; using Content.Shared.Interaction.Events; +using Content.Shared.Item.ItemToggle; using Content.Shared.Medical; using Content.Shared.Mind; using Content.Shared.Mobs; @@ -37,6 +38,7 @@ public sealed class DefibrillatorSystem : EntitySystem [Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly ElectrocutionSystem _electrocution = default!; [Dependency] private readonly EuiManager _euiManager = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly RottingSystem _rotting = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; @@ -50,30 +52,10 @@ public sealed class DefibrillatorSystem : EntitySystem /// public override void Initialize() { - SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnPowerCellSlotEmpty); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnDoAfter); } - private void OnUseInHand(EntityUid uid, DefibrillatorComponent component, UseInHandEvent args) - { - if (args.Handled || !TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay))) - return; - - if (!TryToggle(uid, component, args.User)) - return; - - args.Handled = true; - _useDelay.TryResetDelay((uid, useDelay)); - } - - private void OnPowerCellSlotEmpty(EntityUid uid, DefibrillatorComponent component, ref PowerCellSlotEmptyEvent args) - { - if (!TerminatingOrDeleted(uid)) - TryDisable(uid, component); - } - private void OnAfterInteract(EntityUid uid, DefibrillatorComponent component, AfterInteractEvent args) { if (args.Handled || args.Target is not { } target) @@ -96,54 +78,12 @@ private void OnDoAfter(EntityUid uid, DefibrillatorComponent component, Defibril Zap(uid, target, args.User, component); } - public bool TryToggle(EntityUid uid, DefibrillatorComponent? component = null, EntityUid? user = null) - { - if (!Resolve(uid, ref component)) - return false; - - return component.Enabled - ? TryDisable(uid, component) - : TryEnable(uid, component, user); - } - - public bool TryEnable(EntityUid uid, DefibrillatorComponent? component = null, EntityUid? user = null) - { - if (!Resolve(uid, ref component)) - return false; - - if (component.Enabled) - return false; - - if (!_powerCell.HasActivatableCharge(uid)) - return false; - - component.Enabled = true; - _appearance.SetData(uid, ToggleVisuals.Toggled, true); - _audio.PlayPvs(component.PowerOnSound, uid); - return true; - } - - public bool TryDisable(EntityUid uid, DefibrillatorComponent? component = null) - { - if (!Resolve(uid, ref component)) - return false; - - if (!component.Enabled) - return false; - - component.Enabled = false; - _appearance.SetData(uid, ToggleVisuals.Toggled, false); - - _audio.PlayPvs(component.PowerOffSound, uid); - return true; - } - public bool CanZap(EntityUid uid, EntityUid target, EntityUid? user = null, DefibrillatorComponent? component = null) { if (!Resolve(uid, ref component)) return false; - if (!component.Enabled) + if (!_toggle.IsActivated(uid)) { if (user != null) _popup.PopupEntity(Loc.GetString("defibrillator-not-on"), uid, user.Value); @@ -250,7 +190,11 @@ public void Zap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorCo // if we don't have enough power left for another shot, turn it off if (!_powerCell.HasActivatableCharge(uid)) - TryDisable(uid, component); + _toggle.TryDeactivate(uid); + + // TODO clean up this clown show above + var ev = new TargetDefibrillatedEvent(user, (uid, component)); + RaiseLocalEvent(target, ref ev); } public override void Update(float frameTime) diff --git a/Content.Server/Medical/HealthAnalyzerSystem.cs b/Content.Server/Medical/HealthAnalyzerSystem.cs index 11d2758cdd8..f284a214e65 100644 --- a/Content.Server/Medical/HealthAnalyzerSystem.cs +++ b/Content.Server/Medical/HealthAnalyzerSystem.cs @@ -14,6 +14,8 @@ using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.MedicalScanner; using Content.Shared.Mobs.Components; using Content.Shared.PowerCell; @@ -32,6 +34,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem [Dependency] private readonly PowerCellSystem _cell = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedBodySystem _bodySystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; @@ -42,7 +45,7 @@ public override void Initialize() SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnDoAfter); SubscribeLocalEvent(OnInsertedIntoContainer); - SubscribeLocalEvent(OnPowerCellSlotEmpty); + SubscribeLocalEvent(OnToggled); SubscribeLocalEvent(OnDropped); // Start-Shitmed Subs.BuiEvents(HealthAnalyzerUiKey.Key, subs => @@ -132,16 +135,16 @@ private void OnDoAfter(Entity uid, ref HealthAnalyzerDo private void OnInsertedIntoContainer(Entity uid, ref EntGotInsertedIntoContainerMessage args) { if (uid.Comp.ScannedEntity is { } patient) - StopAnalyzingEntity(uid, patient); + _toggle.TryDeactivate(uid.Owner); } /// - /// Disable continuous updates once battery is dead + /// Disable continuous updates once turned off /// - private void OnPowerCellSlotEmpty(Entity uid, ref PowerCellSlotEmptyEvent args) + private void OnToggled(Entity ent, ref ItemToggledEvent args) { - if (uid.Comp.ScannedEntity is { } patient) - StopAnalyzingEntity(uid, patient); + if (!args.Activated && ent.Comp.ScannedEntity is { } patient) + StopAnalyzingEntity(ent, patient); } /// @@ -150,7 +153,7 @@ private void OnPowerCellSlotEmpty(Entity uid, ref Power private void OnDropped(Entity uid, ref DroppedEvent args) { if (uid.Comp.ScannedEntity is { } patient) - StopAnalyzingEntity(uid, patient); + _toggle.TryDeactivate(uid.Owner); } private void OpenUserInterface(EntityUid user, EntityUid analyzer) @@ -171,7 +174,7 @@ private void BeginAnalyzingEntity(Entity healthAnalyzer //Link the health analyzer to the scanned entity healthAnalyzer.Comp.ScannedEntity = target; healthAnalyzer.Comp.CurrentBodyPart = part; - _cell.SetPowerCellDrawEnabled(healthAnalyzer, true); + _toggle.TryActivate(healthAnalyzer.Owner); UpdateScannedUser(healthAnalyzer, target, true, part); } @@ -187,7 +190,7 @@ private void StopAnalyzingEntity(Entity healthAnalyzer, healthAnalyzer.Comp.ScannedEntity = null; healthAnalyzer.Comp.CurrentBodyPart = null; - _cell.SetPowerCellDrawEnabled(target, false); + _toggle.TryDeactivate(healthAnalyzer.Owner); UpdateScannedUser(healthAnalyzer, target, false); } diff --git a/Content.Server/Ninja/Events/BatteryChangedEvent.cs b/Content.Server/Ninja/Events/BatteryChangedEvent.cs index 45bfedfee76..1848e881868 100644 --- a/Content.Server/Ninja/Events/BatteryChangedEvent.cs +++ b/Content.Server/Ninja/Events/BatteryChangedEvent.cs @@ -1,7 +1,7 @@ namespace Content.Server.Ninja.Events; /// -/// Raised on the ninja when the suit has its powercell changed. +/// Raised on the ninja and suit when the suit has its powercell changed. /// [ByRefEvent] public record struct NinjaBatteryChangedEvent(EntityUid Battery, EntityUid BatteryHolder); diff --git a/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs b/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs index 552ee0397fb..c4b5c6dc2bf 100644 --- a/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs +++ b/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs @@ -33,16 +33,17 @@ public override void Initialize() /// Start do after for draining a power source. /// Can't predict PNBC existing so only done on server. /// - private void OnBeforeInteractHand(EntityUid uid, BatteryDrainerComponent comp, BeforeInteractHandEvent args) + private void OnBeforeInteractHand(Entity ent, ref BeforeInteractHandEvent args) { + var (uid, comp) = ent; var target = args.Target; - if (args.Handled || comp.BatteryUid == null || !HasComp(target)) + if (args.Handled || comp.BatteryUid is not {} battery || !HasComp(target)) return; // handles even if battery is full so you can actually see the poup args.Handled = true; - if (_battery.IsFull(comp.BatteryUid.Value)) + if (_battery.IsFull(battery)) { _popup.PopupEntity(Loc.GetString("battery-drainer-full"), uid, uid, PopupType.Medium); return; @@ -60,23 +61,24 @@ private void OnBeforeInteractHand(EntityUid uid, BatteryDrainerComponent comp, B _doAfter.TryStartDoAfter(doAfterArgs); } - private void OnBatteryChanged(EntityUid uid, BatteryDrainerComponent comp, ref NinjaBatteryChangedEvent args) + private void OnBatteryChanged(Entity ent, ref NinjaBatteryChangedEvent args) { - SetBattery(uid, args.Battery, comp); + SetBattery((ent, ent.Comp), args.Battery); } /// - protected override void OnDoAfterAttempt(EntityUid uid, BatteryDrainerComponent comp, DoAfterAttemptEvent args) + protected override void OnDoAfterAttempt(Entity ent, ref DoAfterAttemptEvent args) { - base.OnDoAfterAttempt(uid, comp, args); + base.OnDoAfterAttempt(ent, ref args); - if (comp.BatteryUid == null || _battery.IsFull(comp.BatteryUid.Value)) + if (ent.Comp.BatteryUid is not {} battery || _battery.IsFull(battery)) args.Cancel(); } /// - protected override bool TryDrainPower(EntityUid uid, BatteryDrainerComponent comp, EntityUid target) + protected override bool TryDrainPower(Entity ent, EntityUid target) { + var (uid, comp) = ent; if (comp.BatteryUid == null || !TryComp(comp.BatteryUid.Value, out var battery)) return false; @@ -99,6 +101,7 @@ protected override bool TryDrainPower(EntityUid uid, BatteryDrainerComponent com var output = input * comp.DrainEfficiency; _battery.SetCharge(comp.BatteryUid.Value, battery.CurrentCharge + output, battery); + // TODO: create effect message or something Spawn("EffectSparks", Transform(target).Coordinates); _audio.PlayPvs(comp.SparkSound, target); _popup.PopupEntity(Loc.GetString("battery-drainer-success", ("battery", target)), uid, uid); diff --git a/Content.Server/Ninja/Systems/ItemCreatorSystem.cs b/Content.Server/Ninja/Systems/ItemCreatorSystem.cs new file mode 100644 index 00000000000..d7a7be995db --- /dev/null +++ b/Content.Server/Ninja/Systems/ItemCreatorSystem.cs @@ -0,0 +1,57 @@ +using Content.Server.Ninja.Events; +using Content.Server.Power.EntitySystems; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Ninja.Components; +using Content.Shared.Ninja.Systems; +using Content.Shared.Popups; + +namespace Content.Server.Ninja.Systems; + +public sealed class ItemCreatorSystem : SharedItemCreatorSystem +{ + [Dependency] private readonly BatterySystem _battery = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnCreateItem); + SubscribeLocalEvent(OnBatteryChanged); + } + + private void OnCreateItem(Entity ent, ref CreateItemEvent args) + { + var (uid, comp) = ent; + if (comp.Battery is not {} battery) + return; + + args.Handled = true; + + var user = args.Performer; + if (!_battery.TryUseCharge(battery, comp.Charge)) + { + _popup.PopupEntity(Loc.GetString(comp.NoPowerPopup), user, user); + return; + } + + var ev = new CreateItemAttemptEvent(user); + RaiseLocalEvent(uid, ref ev); + if (ev.Cancelled) + return; + + // try to put throwing star in hand, otherwise it goes on the ground + var star = Spawn(comp.SpawnedPrototype, Transform(user).Coordinates); + _hands.TryPickupAnyHand(user, star); + } + + private void OnBatteryChanged(Entity ent, ref NinjaBatteryChangedEvent args) + { + if (ent.Comp.Battery == args.Battery) + return; + + ent.Comp.Battery = args.Battery; + Dirty(ent, ent.Comp); + } +} diff --git a/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs b/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs index ac76ae6b771..3aaf7c5d58e 100644 --- a/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs +++ b/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs @@ -1,13 +1,8 @@ -using Content.Server.Communications; -using Content.Server.Mind; using Content.Server.Ninja.Events; -using Content.Server.Objectives.Systems; -using Content.Shared.Communications; -using Content.Shared.CriminalRecords.Components; +using Content.Shared.Mind; +using Content.Shared.Objectives.Systems; using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Systems; -using Content.Shared.Research.Components; -using Content.Shared.Toggleable; namespace Content.Server.Ninja.Systems; @@ -16,89 +11,44 @@ namespace Content.Server.Ninja.Systems; /// public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem { - [Dependency] private readonly EmagProviderSystem _emagProvider = default!; - [Dependency] private readonly CodeConditionSystem _codeCondition = default!; - [Dependency] private readonly CommsHackerSystem _commsHacker = default!; - [Dependency] private readonly SharedStunProviderSystem _stunProvider = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedObjectivesSystem _objectives = default!; [Dependency] private readonly SpaceNinjaSystem _ninja = default!; - public override void Initialize() + protected override void EnableGloves(Entity ent, Entity user) { - base.Initialize(); + base.EnableGloves(ent, user); - SubscribeLocalEvent(OnToggleAction); - } - - /// - /// Toggle gloves, if the user is a ninja wearing a ninja suit. - /// - private void OnToggleAction(EntityUid uid, NinjaGlovesComponent comp, ToggleActionEvent args) - { - if (args.Handled) + // can't use abilities if suit is not equipped, this is checked elsewhere but just making sure to satisfy nullability + if (user.Comp.Suit is not {} suit) return; - args.Handled = true; - - var user = args.Performer; - // need to wear suit to enable gloves - if (!TryComp(user, out var ninja) - || ninja.Suit == null - || !HasComp(ninja.Suit.Value)) - { - Popup.PopupEntity(Loc.GetString("ninja-gloves-not-wearing-suit"), user, user); + if (!_mind.TryGetMind(user, out var mindId, out var mind)) return; - } - - // show its state to the user - var enabling = comp.User == null; - Appearance.SetData(uid, ToggleVisuals.Toggled, enabling); - var message = Loc.GetString(enabling ? "ninja-gloves-on" : "ninja-gloves-off"); - Popup.PopupEntity(message, user, user); - if (enabling) + foreach (var ability in ent.Comp.Abilities) { - EnableGloves(uid, comp, user, ninja); + // non-objective abilities are added in shared already + if (ability.Objective is not {} objId) + continue; + + // prevent doing an objective multiple times by toggling gloves after doing them + // if it's not tied to an objective always add them anyway + if (!_mind.TryFindObjective((mindId, mind), objId, out var obj)) + { + Log.Error($"Ninja glove ability of {ent} referenced missing objective {ability.Objective} of {_mind.MindOwnerLoggingString(mind)}"); + continue; + } + + if (!_objectives.IsCompleted(obj.Value, (mindId, mind))) + EntityManager.AddComponents(user, ability.Components); } - else - { - DisableGloves(uid, comp); - } - } - private void EnableGloves(EntityUid uid, NinjaGlovesComponent comp, EntityUid user, SpaceNinjaComponent ninja) - { - // can't use abilities if suit is not equipped, this is checked elsewhere but just making sure to satisfy nullability - if (ninja.Suit == null) - return; - - comp.User = user; - Dirty(uid, comp); - _ninja.AssignGloves(user, uid, ninja); - - var drainer = EnsureComp(user); - var stun = EnsureComp(user); - _stunProvider.SetNoPowerPopup(user, "ninja-no-power", stun); + // let abilities that use battery power work if (_ninja.GetNinjaBattery(user, out var battery, out var _)) { - var ev = new NinjaBatteryChangedEvent(battery.Value, ninja.Suit.Value); + var ev = new NinjaBatteryChangedEvent(battery.Value, suit); RaiseLocalEvent(user, ref ev); } - - var emag = EnsureComp(user); - _emagProvider.SetWhitelist(user, comp.DoorjackWhitelist, emag); - - EnsureComp(user); - // prevent calling in multiple threats by toggling gloves after - if (!_codeCondition.IsCompleted(user, ninja.TerrorObjective)) - { - var hacker = EnsureComp(user); - var rule = _ninja.NinjaRule(user); - if (rule != null) - _commsHacker.SetThreats(user, rule.Threats, hacker); - } - if (!_codeCondition.IsCompleted(user, ninja.MassArrestObjective)) - { - EnsureComp(user); - } } } diff --git a/Content.Server/Ninja/Systems/NinjaSuitSystem.cs b/Content.Server/Ninja/Systems/NinjaSuitSystem.cs index 04095b549c6..63054eaad50 100644 --- a/Content.Server/Ninja/Systems/NinjaSuitSystem.cs +++ b/Content.Server/Ninja/Systems/NinjaSuitSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Ninja.Events; using Content.Server.Power.Components; using Content.Server.PowerCell; -using Content.Shared.Clothing.EntitySystems; using Content.Shared.Hands.EntitySystems; using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Systems; @@ -29,15 +28,13 @@ public override void Initialize() SubscribeLocalEvent(OnSuitInsertAttempt); SubscribeLocalEvent(OnEmpAttempt); - SubscribeLocalEvent(OnAttemptStealth); - SubscribeLocalEvent(OnCreateThrowingStar); SubscribeLocalEvent(OnRecallKatana); SubscribeLocalEvent(OnEmp); } - protected override void NinjaEquippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user, SpaceNinjaComponent ninja) + protected override void NinjaEquipped(Entity ent, Entity user) { - base.NinjaEquippedSuit(uid, comp, user, ninja); + base.NinjaEquipped(ent, user); _ninja.SetSuitPowerAlert(user); } @@ -57,16 +54,15 @@ private void OnSuitInsertAttempt(EntityUid uid, NinjaSuitComponent comp, Contain // can only upgrade power cell, not swap to recharge instantly otherwise ninja could just swap batteries with flashlights in maints for easy power if (!TryComp(args.EntityUid, out var inserting) || inserting.MaxCharge <= battery.MaxCharge) - { args.Cancel(); - } // tell ninja abilities that use battery to update it so they don't use charge from the old one var user = Transform(uid).ParentUid; - if (!HasComp(user)) + if (!_ninja.IsNinja(user)) return; var ev = new NinjaBatteryChangedEvent(args.EntityUid, uid); + RaiseLocalEvent(uid, ref ev); RaiseLocalEvent(user, ref ev); } @@ -77,64 +73,22 @@ private void OnEmpAttempt(EntityUid uid, NinjaSuitComponent comp, EmpAttemptEven args.Cancel(); } - protected override void UserUnequippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user) + protected override void UserUnequippedSuit(Entity ent, Entity user) { - base.UserUnequippedSuit(uid, comp, user); + base.UserUnequippedSuit(ent, user); // remove power indicator _ninja.SetSuitPowerAlert(user); } - private void OnAttemptStealth(EntityUid uid, NinjaSuitComponent comp, AttemptStealthEvent args) + private void OnRecallKatana(Entity ent, ref RecallKatanaEvent args) { - var user = args.User; - // need 1 second of charge to turn on stealth - var chargeNeeded = SuitWattage(uid, comp); - // being attacked while cloaked gives no power message since it overloads the power supply or something - if (!_ninja.GetNinjaBattery(user, out _, out var battery) || battery.CurrentCharge < chargeNeeded) - { - Popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user); - args.Cancel(); - return; - } - - if (comp.DisableCooldown > GameTiming.CurTime) - { - Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium); - args.Cancel(); - return; - } - - StealthClothing.SetEnabled(uid, user, true); - } - - private void OnCreateThrowingStar(EntityUid uid, NinjaSuitComponent comp, CreateThrowingStarEvent args) - { - args.Handled = true; + var (uid, comp) = ent; var user = args.Performer; - if (!_ninja.TryUseCharge(user, comp.ThrowingStarCharge)) - { - Popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user); + if (!_ninja.NinjaQuery.TryComp(user, out var ninja) || ninja.Katana == null) return; - } - - if (comp.DisableCooldown > GameTiming.CurTime) - { - Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium); - return; - } - - // try to put throwing star in hand, otherwise it goes on the ground - var star = Spawn(comp.ThrowingStarPrototype, Transform(user).Coordinates); - _hands.TryPickupAnyHand(user, star); - } - private void OnRecallKatana(EntityUid uid, NinjaSuitComponent comp, RecallKatanaEvent args) - { args.Handled = true; - var user = args.Performer; - if (!TryComp(user, out var ninja) || ninja.Katana == null) - return; var katana = ninja.Katana.Value; var coords = _transform.GetWorldPosition(katana); @@ -146,11 +100,8 @@ private void OnRecallKatana(EntityUid uid, NinjaSuitComponent comp, RecallKatana return; } - if (comp.DisableCooldown > GameTiming.CurTime) - { - Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium); + if (CheckDisabled(ent, user)) return; - } // TODO: teleporting into belt slot var message = _hands.TryPickupAnyHand(user, katana) @@ -159,9 +110,11 @@ private void OnRecallKatana(EntityUid uid, NinjaSuitComponent comp, RecallKatana Popup.PopupEntity(Loc.GetString(message), user, user); } - private void OnEmp(EntityUid uid, NinjaSuitComponent comp, NinjaEmpEvent args) + private void OnEmp(Entity ent, ref NinjaEmpEvent args) { + var (uid, comp) = ent; args.Handled = true; + var user = args.Performer; if (!_ninja.TryUseCharge(user, comp.EmpCharge)) { @@ -169,13 +122,9 @@ private void OnEmp(EntityUid uid, NinjaSuitComponent comp, NinjaEmpEvent args) return; } - if (comp.DisableCooldown > GameTiming.CurTime) - { - Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium); + if (CheckDisabled(ent, user)) return; - } - // I don't think this affects the suit battery, but if it ever does in the future add a blacklist for it var coords = _transform.GetMapCoordinates(user); _emp.EmpPulse(coords, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration); } diff --git a/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs b/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs index 0c1e88653fa..28ab6332276 100644 --- a/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs +++ b/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Chat.Managers; using Content.Server.CriminalRecords.Systems; using Content.Server.GameTicking.Rules.Components; -using Content.Server.GenericAntag; using Content.Server.Objectives.Components; using Content.Server.Objectives.Systems; using Content.Server.Power.Components; @@ -11,7 +10,6 @@ using Content.Server.Research.Systems; using Content.Server.Roles; using Content.Shared.Alert; -using Content.Shared.Clothing.EntitySystems; using Content.Shared.Doors.Components; using Content.Shared.IdentityManagement; using Content.Shared.Mind; @@ -26,11 +24,6 @@ namespace Content.Server.Ninja.Systems; -// TODO: when syndiborgs are a thing have a borg converter with 6 second doafter -// engi -> saboteur -// medi -> idk reskin it -// other -> assault - /// /// Main ninja system that handles ninja setup, provides helper methods for the rest of the code to use. /// @@ -44,13 +37,11 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem [Dependency] private readonly RoleSystem _role = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly StealthClothingSystem _stealthClothing = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnNinjaCreated); SubscribeLocalEvent(OnDoorjack); SubscribeLocalEvent(OnResearchStolen); SubscribeLocalEvent(OnThreatCalledIn); @@ -62,7 +53,7 @@ public override void Update(float frameTime) var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var ninja)) { - UpdateNinja(uid, ninja, frameTime); + SetSuitPowerAlert((uid, ninja)); } } @@ -80,31 +71,13 @@ private int Download(EntityUid uid, List ids) return newCount - oldCount; } - /// - /// Returns a ninja's gamerule config data. - /// If the gamerule was not started then it will be started automatically. - /// - public NinjaRuleComponent? NinjaRule(EntityUid uid, GenericAntagComponent? comp = null) - { - if (!Resolve(uid, ref comp)) - return null; - - // mind not added yet so no rule - if (comp.RuleEntity == null) - return null; - - return CompOrNull(comp.RuleEntity); - } - // TODO: can probably copy paste borg code here /// /// Update the alert for the ninja's suit power indicator. /// - public void SetSuitPowerAlert(EntityUid uid, SpaceNinjaComponent? comp = null) + public void SetSuitPowerAlert(Entity ent) { - if (!Resolve(uid, ref comp, false)) - return; - + var (uid, comp) = ent; if (comp.Deleted || comp.Suit == null) { _alerts.ClearAlert(uid, comp.SuitPowerAlert); @@ -145,53 +118,6 @@ public override bool TryUseCharge(EntityUid user, float charge) return GetNinjaBattery(user, out var uid, out var battery) && _battery.TryUseCharge(uid.Value, charge, battery); } - /// - /// Set up everything for ninja to work and send the greeting message/sound. - /// Objectives are added by . - /// - private void OnNinjaCreated(EntityUid uid, SpaceNinjaComponent comp, ref GenericAntagCreatedEvent args) - { - var mindId = args.MindId; - var mind = args.Mind; - - if (mind.Session == null) - return; - - var config = NinjaRule(uid); - if (config == null) - return; - - var role = new NinjaRoleComponent - { - PrototypeId = "SpaceNinja" - }; - _role.MindAddRole(mindId, role, mind); - _role.MindPlaySound(mindId, config.GreetingSound, mind); - - var session = mind.Session; - _audio.PlayGlobal(config.GreetingSound, Filter.Empty().AddPlayer(session), false, AudioParams.Default); - _chatMan.DispatchServerMessage(session, Loc.GetString("ninja-role-greeting")); - } - - // TODO: PowerCellDraw, modify when cloak enabled - /// - /// Handle constant power drains from passive usage and cloak. - /// - private void UpdateNinja(EntityUid uid, SpaceNinjaComponent ninja, float frameTime) - { - if (ninja.Suit == null) - return; - - float wattage = Suit.SuitWattage(ninja.Suit.Value); - - SetSuitPowerAlert(uid, ninja); - if (!TryUseCharge(uid, wattage * frameTime)) - { - // ran out of power, uncloak ninja - _stealthClothing.SetEnabled(ninja.Suit.Value, uid, false); - } - } - /// /// Increment greentext when emagging a door. /// diff --git a/Content.Server/Ninja/Systems/SpiderChargeSystem.cs b/Content.Server/Ninja/Systems/SpiderChargeSystem.cs index 64c958d6f1a..c262651f27a 100644 --- a/Content.Server/Ninja/Systems/SpiderChargeSystem.cs +++ b/Content.Server/Ninja/Systems/SpiderChargeSystem.cs @@ -7,6 +7,7 @@ using Content.Server.Sticky.Events; using Content.Shared.Interaction; using Content.Shared.Ninja.Components; +using Content.Shared.Ninja.Systems; using Robust.Shared.GameObjects; namespace Content.Server.Ninja.Systems; @@ -14,7 +15,7 @@ namespace Content.Server.Ninja.Systems; /// /// Prevents planting a spider charge outside of its location and handles greentext. /// -public sealed class SpiderChargeSystem : EntitySystem +public sealed class SpiderChargeSystem : SharedSpiderChargeSystem { [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly PopupSystem _popup = default!; diff --git a/Content.Server/Ninja/Systems/StunProviderSystem.cs b/Content.Server/Ninja/Systems/StunProviderSystem.cs index 970ca78e2cc..822486cff52 100644 --- a/Content.Server/Ninja/Systems/StunProviderSystem.cs +++ b/Content.Server/Ninja/Systems/StunProviderSystem.cs @@ -6,9 +6,11 @@ using Content.Shared.Ninja.Systems; using Content.Shared.Popups; using Content.Shared.Stunnable; -using Robust.Shared.Prototypes; +using Content.Shared.Timing; +using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Timing; +using Robust.Shared.Prototypes; namespace Content.Server.Ninja.Systems; @@ -19,11 +21,12 @@ public sealed class StunProviderSystem : SharedStunProviderSystem { [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly DamageableSystem _damageable = default!; - [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly UseDelaySystem _useDelay = default!; public override void Initialize() { @@ -36,16 +39,18 @@ public override void Initialize() /// /// Stun clicked mobs on the whitelist, if there is enough power. /// - private void OnBeforeInteractHand(EntityUid uid, StunProviderComponent comp, BeforeInteractHandEvent args) + private void OnBeforeInteractHand(Entity ent, ref BeforeInteractHandEvent args) { // TODO: generic check + var (uid, comp) = ent; if (args.Handled || comp.BatteryUid == null || !_gloves.AbilityCheck(uid, args, out var target)) return; - if (target == uid || !comp.Whitelist.IsValid(target, EntityManager)) + if (target == uid || _whitelist.IsWhitelistFail(comp.Whitelist, target)) return; - if (_timing.CurTime < comp.NextStun) + var useDelay = EnsureComp(uid); + if (_useDelay.IsDelayed((uid, useDelay), id: comp.DelayId)) return; // take charge from battery @@ -61,13 +66,14 @@ private void OnBeforeInteractHand(EntityUid uid, StunProviderComponent comp, Bef _stun.TryParalyze(target, comp.StunTime, refresh: false); // short cooldown to prevent instant stunlocking - comp.NextStun = _timing.CurTime + comp.Cooldown; + _useDelay.SetLength((uid, useDelay), comp.Cooldown, id: comp.DelayId); + _useDelay.TryResetDelay((uid, useDelay), id: comp.DelayId); args.Handled = true; } - private void OnBatteryChanged(EntityUid uid, StunProviderComponent comp, ref NinjaBatteryChangedEvent args) + private void OnBatteryChanged(Entity ent, ref NinjaBatteryChangedEvent args) { - SetBattery(uid, args.Battery, comp); + SetBattery((ent, ent.Comp), args.Battery); } } diff --git a/Content.Server/Objectives/Systems/CodeConditionSystem.cs b/Content.Server/Objectives/Systems/CodeConditionSystem.cs index 7ba312f4bb9..fbc58dafe82 100644 --- a/Content.Server/Objectives/Systems/CodeConditionSystem.cs +++ b/Content.Server/Objectives/Systems/CodeConditionSystem.cs @@ -35,20 +35,6 @@ public bool IsCompleted(Entity ent) return ent.Comp.Completed; } - /// - /// Returns true if a mob's objective with a certain prototype is completed. - /// - public bool IsCompleted(Entity mob, string prototype) - { - if (_mind.GetMind(mob, mob.Comp) is not {} mindId) - return false; - - if (!_mind.TryFindObjective(mindId, prototype, out var obj)) - return false; - - return IsCompleted(obj.Value); - } - /// /// Sets an objective's completed field. /// diff --git a/Content.Server/PowerCell/PowerCellSystem.Draw.cs b/Content.Server/PowerCell/PowerCellSystem.Draw.cs index 4155a4f6bec..9ebd677f473 100644 --- a/Content.Server/PowerCell/PowerCellSystem.Draw.cs +++ b/Content.Server/PowerCell/PowerCellSystem.Draw.cs @@ -1,4 +1,5 @@ using Content.Server.Power.Components; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.PowerCell; using Content.Shared.PowerCell.Components; @@ -10,22 +11,20 @@ public sealed partial class PowerCellSystem * Handles PowerCellDraw */ - private static readonly TimeSpan Delay = TimeSpan.FromSeconds(1); - public override void Update(float frameTime) { base.Update(frameTime); - var query = EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp, out var slot)) + while (query.MoveNext(out var uid, out var comp, out var slot, out var toggle)) { - if (!comp.Drawing) + if (!comp.Enabled || !toggle.Activated) continue; if (Timing.CurTime < comp.NextUpdateTime) continue; - comp.NextUpdateTime += Delay; + comp.NextUpdateTime += comp.Delay; if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, slot)) continue; @@ -33,7 +32,8 @@ public override void Update(float frameTime) if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate, battery)) continue; - comp.Drawing = false; + Toggle.TryDeactivate((uid, toggle)); + var ev = new PowerCellSlotEmptyEvent(); RaiseLocalEvent(uid, ref ev); } @@ -42,26 +42,9 @@ public override void Update(float frameTime) private void OnDrawChargeChanged(EntityUid uid, PowerCellDrawComponent component, ref ChargeChangedEvent args) { // Update the bools for client prediction. - bool canDraw; - bool canUse; - - if (component.UseRate > 0f) - { - canUse = args.Charge > component.UseRate; - } - else - { - canUse = true; - } + var canUse = component.UseRate <= 0f || args.Charge > component.UseRate; - if (component.DrawRate > 0f) - { - canDraw = args.Charge > 0f; - } - else - { - canDraw = true; - } + var canDraw = component.DrawRate <= 0f || args.Charge > 0f; if (canUse != component.CanUse || canDraw != component.CanDraw) { @@ -76,6 +59,9 @@ private void OnDrawCellChanged(EntityUid uid, PowerCellDrawComponent component, var canDraw = !args.Ejected && HasCharge(uid, float.MinValue); var canUse = !args.Ejected && HasActivatableCharge(uid, component); + if (!canDraw) + Toggle.TryDeactivate(uid); + if (canUse != component.CanUse || canDraw != component.CanDraw) { component.CanDraw = canDraw; diff --git a/Content.Server/PowerCell/PowerCellSystem.cs b/Content.Server/PowerCell/PowerCellSystem.cs index f45a01b2e1b..0d2d9012bc7 100644 --- a/Content.Server/PowerCell/PowerCellSystem.cs +++ b/Content.Server/PowerCell/PowerCellSystem.cs @@ -39,8 +39,8 @@ public override void Initialize() SubscribeLocalEvent(OnDrawChargeChanged); SubscribeLocalEvent(OnDrawCellChanged); - // funny SubscribeLocalEvent(OnCellSlotExamined); + // funny SubscribeLocalEvent(OnSlotMicrowaved); } diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs index 623b6ba9787..0feecad4d08 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs @@ -30,7 +30,7 @@ private void OnModuleGotInserted(EntityUid uid, BorgModuleComponent component, E if (!TryComp(chassis, out var chassisComp) || args.Container != chassisComp.ModuleContainer || - !chassisComp.Activated) + !Toggle.IsActivated(chassis)) return; if (!_powerCell.HasDrawCharge(uid)) @@ -144,6 +144,7 @@ public void SelectModule(EntityUid chassis, var ev = new BorgModuleSelectedEvent(chassis); RaiseLocalEvent(moduleUid, ref ev); chassisComp.SelectedModule = moduleUid; + Dirty(chassis, chassisComp); } /// @@ -163,6 +164,7 @@ public void UnselectModule(EntityUid chassis, BorgChassisComponent? chassisComp var ev = new BorgModuleUnselectedEvent(chassis); RaiseLocalEvent(chassisComp.SelectedModule.Value, ref ev); chassisComp.SelectedModule = null; + Dirty(chassis, chassisComp); } private void OnItemModuleSelected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleSelectedEvent args) diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index b6e5adbe2d4..b8546f30e7d 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.Database; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Mobs; @@ -75,6 +76,7 @@ public override void Initialize() SubscribeLocalEvent(OnPowerCellSlotEmpty); SubscribeLocalEvent(OnUIOpenAttempt); SubscribeLocalEvent(OnGetDeadIC); + SubscribeLocalEvent(OnToggled); SubscribeLocalEvent(OnBrainMindAdded); SubscribeLocalEvent(OnBrainPointAttempt); @@ -175,11 +177,11 @@ private void OnMobStateChanged(EntityUid uid, BorgChassisComponent component, Mo if (args.NewMobState == MobState.Alive) { if (_mind.TryGetMind(uid, out _, out _)) - _powerCell.SetPowerCellDrawEnabled(uid, true); + _powerCell.SetDrawEnabled(uid, true); } else { - _powerCell.SetPowerCellDrawEnabled(uid, false); + _powerCell.SetDrawEnabled(uid, false); } } @@ -187,24 +189,10 @@ private void OnPowerCellChanged(EntityUid uid, BorgChassisComponent component, P { UpdateBatteryAlert((uid, component)); - if (!TryComp(uid, out var draw)) - return; - - // if we eject the battery or run out of charge, then disable - if (args.Ejected || !_powerCell.HasDrawCharge(uid)) - { - DisableBorgAbilities(uid, component); - return; - } - // if we aren't drawing and suddenly get enough power to draw again, reeanble. - if (_powerCell.HasDrawCharge(uid, draw)) + if (_powerCell.HasDrawCharge(uid)) { - // only reenable the powerdraw if a player has the role. - if (!draw.Drawing && _mind.TryGetMind(uid, out _, out _) && _mobState.IsAlive(uid)) - _powerCell.SetPowerCellDrawEnabled(uid, true); - - EnableBorgAbilities(uid, component); + Toggle.TryActivate(uid); } UpdateUI(uid, component); @@ -212,7 +200,7 @@ private void OnPowerCellChanged(EntityUid uid, BorgChassisComponent component, P private void OnPowerCellSlotEmpty(EntityUid uid, BorgChassisComponent component, ref PowerCellSlotEmptyEvent args) { - DisableBorgAbilities(uid, component); + Toggle.TryDeactivate(uid); UpdateUI(uid, component); } @@ -228,6 +216,23 @@ private void OnGetDeadIC(EntityUid uid, BorgChassisComponent component, ref GetC args.Dead = true; } + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + var (uid, comp) = ent; + if (args.Activated) + InstallAllModules(uid, comp); + else + DisableAllModules(uid, comp); + + // only enable the powerdraw if there is a player in the chassis + var drawing = _mind.TryGetMind(uid, out _, out _) && _mobState.IsAlive(ent); + _powerCell.SetDrawEnabled(uid, drawing); + + UpdateUI(uid, comp); + + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); + } + private void OnBrainMindAdded(EntityUid uid, BorgBrainComponent component, MindAddedMessage args) { if (!Container.TryGetOuterContainer(uid, Transform(uid), out var container)) @@ -280,44 +285,14 @@ private void UpdateBatteryAlert(Entity ent, PowerCellSlotC _alerts.ShowAlert(ent, ent.Comp.BatteryAlert, chargePercent); } - /// - /// Activates the borg, enabling all of its modules. - /// - public void EnableBorgAbilities(EntityUid uid, BorgChassisComponent component, PowerCellDrawComponent? powerCell = null) - { - if (component.Activated) - return; - - component.Activated = true; - InstallAllModules(uid, component); - Dirty(uid, component); - _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); - } - - /// - /// Deactivates the borg, disabling all of its modules and decreasing its speed. - /// - public void DisableBorgAbilities(EntityUid uid, BorgChassisComponent component) - { - if (!component.Activated) - return; - - component.Activated = false; - DisableAllModules(uid, component); - Dirty(uid, component); - _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); - } - /// /// Activates a borg when a player occupies it /// public void BorgActivate(EntityUid uid, BorgChassisComponent component) { Popup.PopupEntity(Loc.GetString("borg-mind-added", ("name", Identity.Name(uid, EntityManager))), uid); - _powerCell.SetPowerCellDrawEnabled(uid, true); - _access.SetAccessEnabled(uid, true); + Toggle.TryActivate(uid); _appearance.SetData(uid, BorgVisuals.HasPlayer, true); - Dirty(uid, component); } /// @@ -326,10 +301,8 @@ public void BorgActivate(EntityUid uid, BorgChassisComponent component) public void BorgDeactivate(EntityUid uid, BorgChassisComponent component) { Popup.PopupEntity(Loc.GetString("borg-mind-removed", ("name", Identity.Name(uid, EntityManager))), uid); - _powerCell.SetPowerCellDrawEnabled(uid, false); - _access.SetAccessEnabled(uid, false); + Toggle.TryDeactivate(uid); _appearance.SetData(uid, BorgVisuals.HasPlayer, false); - Dirty(uid, component); } /// diff --git a/Content.Server/StationEvents/Components/NinjaSpawnRuleComponent.cs b/Content.Server/StationEvents/Components/NinjaSpawnRuleComponent.cs deleted file mode 100644 index d758247eca2..00000000000 --- a/Content.Server/StationEvents/Components/NinjaSpawnRuleComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Content.Server.StationEvents.Events; - -namespace Content.Server.StationEvents.Components; - -/// -/// Configuration component for the Space Ninja antag. -/// -[RegisterComponent, Access(typeof(NinjaSpawnRule))] -public sealed partial class NinjaSpawnRuleComponent : Component -{ - /// - /// Distance that the ninja spawns from the station's half AABB radius - /// - [DataField("spawnDistance")] - public float SpawnDistance = 20f; -} diff --git a/Content.Server/StationEvents/Components/SpaceSpawnRuleComponent.cs b/Content.Server/StationEvents/Components/SpaceSpawnRuleComponent.cs new file mode 100644 index 00000000000..a0168077fd6 --- /dev/null +++ b/Content.Server/StationEvents/Components/SpaceSpawnRuleComponent.cs @@ -0,0 +1,25 @@ +using Content.Server.StationEvents.Events; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; + +namespace Content.Server.StationEvents.Components; + +/// +/// Component for spawning antags in space around a station. +/// Requires AntagSelectionComponent. +/// +[RegisterComponent, Access(typeof(SpaceSpawnRule))] +public sealed partial class SpaceSpawnRuleComponent : Component +{ + /// + /// Distance that the entity spawns from the station's half AABB radius + /// + [DataField] + public float SpawnDistance = 20f; + + /// + /// Location that was picked. + /// + [DataField] + public MapCoordinates? Coords; +} diff --git a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs b/Content.Server/StationEvents/Events/SpaceSpawnRule.cs similarity index 53% rename from Content.Server/StationEvents/Events/NinjaSpawnRule.cs rename to Content.Server/StationEvents/Events/SpaceSpawnRule.cs index 9cbc193ce61..6fccaaa5cfc 100644 --- a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs +++ b/Content.Server/StationEvents/Events/SpaceSpawnRule.cs @@ -1,5 +1,5 @@ +using Content.Server.Antag; using Content.Server.GameTicking.Rules.Components; -using Content.Server.Ninja.Systems; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; using Content.Shared.GameTicking.Components; @@ -9,18 +9,28 @@ namespace Content.Server.StationEvents.Events; /// -/// Event for spawning a Space Ninja mid-game. +/// Station event component for spawning this rules antags in space around a station. /// -public sealed class NinjaSpawnRule : StationEventSystem +public sealed class SpaceSpawnRule : StationEventSystem { [Dependency] private readonly SharedTransformSystem _transform = default!; - protected override void Started(EntityUid uid, NinjaSpawnRuleComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args) + public override void Initialize() { - base.Started(uid, comp, gameRule, args); + base.Initialize(); + + SubscribeLocalEvent(OnSelectLocation); + } + + protected override void Added(EntityUid uid, SpaceSpawnRuleComponent comp, GameRuleComponent gameRule, GameRuleAddedEvent args) + { + base.Added(uid, comp, gameRule, args); if (!TryGetRandomStation(out var station)) + { + ForceEndSelf(uid, gameRule); return; + } var stationData = Comp(station.Value); @@ -28,22 +38,28 @@ protected override void Started(EntityUid uid, NinjaSpawnRuleComponent comp, Gam var gridUid = StationSystem.GetLargestGrid(stationData); if (gridUid == null || !TryComp(gridUid, out var grid)) { - Sawmill.Warning("Chosen station has no grids, cannot spawn space ninja!"); + Sawmill.Warning("Chosen station has no grids, cannot pick location for {ToPrettyString(uid):rule}"); + ForceEndSelf(uid, gameRule); return; } - // figure out its AABB size and use that as a guide to how far ninja should be + // figure out its AABB size and use that as a guide to how far the spawner should be var size = grid.LocalAABB.Size.Length() / 2; var distance = size + comp.SpawnDistance; var angle = RobustRandom.NextAngle(); // position relative to station center var location = angle.ToVec() * distance; - // create the spawner, the ninja will appear when a ghost has picked the role + // create the spawner! var xform = Transform(gridUid.Value); var position = _transform.GetWorldPosition(xform) + location; - var coords = new MapCoordinates(position, xform.MapID); - Sawmill.Info($"Creating ninja spawnpoint at {coords}"); - Spawn("SpawnPointGhostSpaceNinja", coords); + comp.Coords = new MapCoordinates(position, xform.MapID); + Sawmill.Info($"Picked location {comp.Coords} for {ToPrettyString(uid):rule}"); + } + + private void OnSelectLocation(Entity ent, ref AntagSelectLocationEvent args) + { + if (ent.Comp.Coords is {} coords) + args.Coordinates.Add(coords); } } diff --git a/Content.Server/Stunnable/Systems/StunbatonSystem.cs b/Content.Server/Stunnable/Systems/StunbatonSystem.cs index c1782efabaf..97dd2c7e735 100644 --- a/Content.Server/Stunnable/Systems/StunbatonSystem.cs +++ b/Content.Server/Stunnable/Systems/StunbatonSystem.cs @@ -19,7 +19,7 @@ public sealed class StunbatonSystem : SharedStunbatonSystem [Dependency] private readonly RiggableSystem _riggableSystem = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly BatterySystem _battery = default!; - [Dependency] private readonly SharedItemToggleSystem _itemToggle = default!; + [Dependency] private readonly ItemToggleSystem _itemToggle = default!; public override void Initialize() { diff --git a/Content.Server/Weapons/Misc/TetherGunSystem.cs b/Content.Server/Weapons/Misc/TetherGunSystem.cs index f6aafe376d6..2bf53d46f4b 100644 --- a/Content.Server/Weapons/Misc/TetherGunSystem.cs +++ b/Content.Server/Weapons/Misc/TetherGunSystem.cs @@ -1,4 +1,5 @@ using Content.Server.PowerCell; +using Content.Shared.Item.ItemToggle; using Content.Shared.PowerCell; using Content.Shared.Weapons.Misc; using Robust.Shared.Physics.Components; @@ -8,6 +9,7 @@ namespace Content.Server.Weapons.Misc; public sealed class TetherGunSystem : SharedTetherGunSystem { [Dependency] private readonly PowerCellSystem _cell = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; public override void Initialize() { @@ -36,12 +38,12 @@ protected override void StartTether(EntityUid gunUid, BaseForceGunComponent comp PhysicsComponent? targetPhysics = null, TransformComponent? targetXform = null) { base.StartTether(gunUid, component, target, user, targetPhysics, targetXform); - _cell.SetPowerCellDrawEnabled(gunUid, true); + _toggle.TryActivate(gunUid); } protected override void StopTether(EntityUid gunUid, BaseForceGunComponent component, bool land = true, bool transfer = false) { base.StopTether(gunUid, component, land, transfer); - _cell.SetPowerCellDrawEnabled(gunUid, false); + _toggle.TryDeactivate(gunUid); } } diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMagnetTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMagnetTriggerSystem.cs index c6f56a27508..a585a9ef452 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMagnetTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMagnetTriggerSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Salvage; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Shared.Clothing; +using Content.Shared.Item.ItemToggle.Components; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -29,11 +30,11 @@ public override void Update(float frameTime) _toActivate.Clear(); - //assume that there's more instruments than artifacts - var query = EntityQueryEnumerator(); - while (query.MoveNext(out _, out var magboot, out var magXform)) + //assume that there's more magboots than artifacts + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out var magboot, out var magXform, out var toggle)) { - if (!magboot.On) + if (!toggle.Activated) continue; var artiQuery = EntityQueryEnumerator(); diff --git a/Content.Shared/Access/Components/AccessToggleComponent.cs b/Content.Shared/Access/Components/AccessToggleComponent.cs new file mode 100644 index 00000000000..60a606ac7ea --- /dev/null +++ b/Content.Shared/Access/Components/AccessToggleComponent.cs @@ -0,0 +1,11 @@ +using Content.Shared.Access.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Access.Components; + +/// +/// Toggles an access provider with ItemToggle. +/// Requires . +/// +[RegisterComponent, NetworkedComponent, Access(typeof(AccessToggleSystem))] +public sealed partial class AccessToggleComponent : Component; diff --git a/Content.Shared/Access/Systems/AccessToggleSystem.cs b/Content.Shared/Access/Systems/AccessToggleSystem.cs new file mode 100644 index 00000000000..564aca06812 --- /dev/null +++ b/Content.Shared/Access/Systems/AccessToggleSystem.cs @@ -0,0 +1,21 @@ +using Content.Shared.Access.Components; +using Content.Shared.Item.ItemToggle.Components; + +namespace Content.Shared.Access.Systems; + +public sealed class AccessToggleSystem : EntitySystem +{ + [Dependency] private readonly SharedAccessSystem _access = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggled); + } + + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + _access.SetAccessEnabled(ent, args.Activated); + } +} diff --git a/Content.Shared/Beeper/Components/BeeperComponent.cs b/Content.Shared/Beeper/Components/BeeperComponent.cs index 54d242709c4..f6efbb10f3e 100644 --- a/Content.Shared/Beeper/Components/BeeperComponent.cs +++ b/Content.Shared/Beeper/Components/BeeperComponent.cs @@ -10,15 +10,12 @@ namespace Content.Shared.Beeper.Components; /// This is used for an item that beeps based on /// proximity to a specified component. /// +/// +/// Requires ItemToggleComponent to control it. +/// [RegisterComponent, NetworkedComponent, Access(typeof(BeeperSystem)), AutoGenerateComponentState] public sealed partial class BeeperComponent : Component { - /// - /// Whether or not it's on. - /// - [DataField, AutoNetworkedField] - public bool Enabled = true; - /// /// How much to scale the interval by (< 0 = min, > 1 = max) /// @@ -56,7 +53,7 @@ public sealed partial class BeeperComponent : Component /// Is the beep muted /// [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public bool IsMuted = false; + public bool IsMuted; /// /// The sound played when the locator beeps. diff --git a/Content.Shared/Beeper/Systems/BeeperSystem.cs b/Content.Shared/Beeper/Systems/BeeperSystem.cs index c51eef4da9d..a52e19f7552 100644 --- a/Content.Shared/Beeper/Systems/BeeperSystem.cs +++ b/Content.Shared/Beeper/Systems/BeeperSystem.cs @@ -1,5 +1,7 @@ using Content.Shared.Beeper.Components; using Content.Shared.FixedPoint; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; using Robust.Shared.Timing; @@ -11,34 +13,20 @@ namespace Content.Shared.Beeper.Systems; public sealed class BeeperSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly INetManager _net = default!; - - public override void Initialize() - { - } + [Dependency] private readonly ItemToggleSystem _toggle = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; public override void Update(float frameTime) { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var beeper)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var beeper, out var toggle)) { - if (!beeper.Enabled) - continue; - RunUpdate_Internal(uid, beeper); + if (toggle.Activated) + RunUpdate_Internal(uid, beeper); } } - public void SetEnable(EntityUid owner, bool isEnabled, BeeperComponent? beeper = null) - { - if (!Resolve(owner, ref beeper) || beeper.Enabled == isEnabled) - return; - beeper.Enabled = isEnabled; - - RunUpdate_Internal(owner, beeper); - Dirty(owner, beeper); - } - public void SetIntervalScaling(EntityUid owner, BeeperComponent beeper, FixedPoint2 newScaling) { newScaling = FixedPoint2.Clamp(newScaling, 0, 1); @@ -70,6 +58,7 @@ public void SetMute(EntityUid owner, bool isMuted, BeeperComponent? comp = null) if (!Resolve(owner, ref comp)) return; comp.IsMuted = isMuted; + Dirty(owner, comp); } private void UpdateBeepInterval(EntityUid owner, BeeperComponent beeper) @@ -91,19 +80,17 @@ public void ForceUpdate(EntityUid owner, BeeperComponent? beeper = null) private void RunUpdate_Internal(EntityUid owner, BeeperComponent beeper) { - if (!beeper.Enabled) - { + if (!_toggle.IsActivated(owner)) return; - } + UpdateBeepInterval(owner, beeper); if (beeper.NextBeep >= _timing.CurTime) return; + var beepEvent = new BeepPlayedEvent(beeper.IsMuted); RaiseLocalEvent(owner, ref beepEvent); if (!beeper.IsMuted && _net.IsServer) - { _audio.PlayPvs(beeper.BeepSound, owner); - } beeper.LastBeepTime = _timing.CurTime; } } diff --git a/Content.Shared/Beeper/Systems/ProximityBeeperSystem.cs b/Content.Shared/Beeper/Systems/ProximityBeeperSystem.cs index bd857d4c29b..ed3c6366c13 100644 --- a/Content.Shared/Beeper/Systems/ProximityBeeperSystem.cs +++ b/Content.Shared/Beeper/Systems/ProximityBeeperSystem.cs @@ -1,7 +1,6 @@ using Content.Shared.Beeper.Components; -using Content.Shared.Interaction.Events; +using Content.Shared.Item.ItemToggle; using Content.Shared.Pinpointer; -using Content.Shared.PowerCell; using Content.Shared.ProximityDetection; using Content.Shared.ProximityDetection.Components; using Content.Shared.ProximityDetection.Systems; @@ -9,20 +8,17 @@ namespace Content.Shared.Beeper.Systems; /// -/// This handles logic for implementing proximity beeper as a handheld tool /> +/// This handles controlling a beeper from proximity detector events. /// public sealed class ProximityBeeperSystem : EntitySystem { [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedPowerCellSystem _powerCell = default!; [Dependency] private readonly ProximityDetectionSystem _proximity = default!; [Dependency] private readonly BeeperSystem _beeper = default!; /// public override void Initialize() { - SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnPowerCellSlotEmpty); SubscribeLocalEvent(OnNewProximityTarget); SubscribeLocalEvent(OnProximityTargetUpdate); } @@ -33,82 +29,16 @@ private void OnProximityTargetUpdate(EntityUid owner, ProximityBeeperComponent p return; if (args.Target == null) { - _beeper.SetEnable(owner, false, beeper); + _beeper.SetMute(owner, true, beeper); return; } - _beeper.SetIntervalScaling(owner,args.Distance/args.Detector.Range, beeper); - _beeper.SetEnable(owner, true, beeper); - } - - private void OnNewProximityTarget(EntityUid owner, ProximityBeeperComponent proxBeeper, ref NewProximityTargetEvent args) - { - _beeper.SetEnable(owner, args.Target != null); - } - private void OnUseInHand(EntityUid uid, ProximityBeeperComponent proxBeeper, UseInHandEvent args) - { - if (args.Handled) - return; - args.Handled = TryToggle(uid, proxBeeper, user: args.User); + _beeper.SetIntervalScaling(owner, args.Distance / args.Detector.Range, beeper); + _beeper.SetMute(owner, false, beeper); } - private void OnPowerCellSlotEmpty(EntityUid uid, ProximityBeeperComponent beeper, ref PowerCellSlotEmptyEvent args) - { - if (_proximity.GetEnable(uid)) - TryDisable(uid); - } - public bool TryEnable(EntityUid owner, BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null, - PowerCellDrawComponent? draw = null,EntityUid? user = null) - { - if (!Resolve(owner, ref beeper, ref detector)) - return false; - if (Resolve(owner, ref draw, false) && !_powerCell.HasActivatableCharge(owner, battery: draw, user: user)) - return false; - Enable(owner, beeper, detector, draw); - return true; - } - private void Enable(EntityUid owner, BeeperComponent beeper, - ProximityDetectorComponent detector, PowerCellDrawComponent? draw) - { - _proximity.SetEnable(owner, true, detector); - _appearance.SetData(owner, ProximityBeeperVisuals.Enabled, true); - _powerCell.SetPowerCellDrawEnabled(owner, true, draw); - } - - - /// - /// Disables the proximity beeper - /// - public bool TryDisable(EntityUid owner,BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null, PowerCellDrawComponent? draw = null) - { - if (!Resolve(owner, ref beeper, ref detector)) - return false; - - if (!detector.Enabled) - return false; - Disable(owner, beeper, detector, draw); - return true; - } - private void Disable(EntityUid owner, BeeperComponent beeper, - ProximityDetectorComponent detector, PowerCellDrawComponent? draw) - { - _proximity.SetEnable(owner, false, detector); - _appearance.SetData(owner, ProximityBeeperVisuals.Enabled, false); - _beeper.SetEnable(owner, false, beeper); - _powerCell.SetPowerCellDrawEnabled(owner, false, draw); - } - - /// - /// toggles the proximity beeper - /// - public bool TryToggle(EntityUid owner, ProximityBeeperComponent? proxBeeper = null, BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null, - PowerCellDrawComponent? draw = null, EntityUid? user = null) + private void OnNewProximityTarget(EntityUid owner, ProximityBeeperComponent proxBeeper, ref NewProximityTargetEvent args) { - if (!Resolve(owner, ref proxBeeper, ref beeper, ref detector)) - return false; - - return detector.Enabled - ? TryDisable(owner, beeper, detector, draw) - : TryEnable(owner, beeper, detector, draw,user); + _beeper.SetMute(owner, args.Target != null); } } diff --git a/Content.Shared/Charges/Systems/SharedChargesSystem.cs b/Content.Shared/Charges/Systems/SharedChargesSystem.cs index 5de1383cde0..7f95ef184e4 100644 --- a/Content.Shared/Charges/Systems/SharedChargesSystem.cs +++ b/Content.Shared/Charges/Systems/SharedChargesSystem.cs @@ -5,10 +5,14 @@ namespace Content.Shared.Charges.Systems; public abstract class SharedChargesSystem : EntitySystem { + protected EntityQuery Query; + public override void Initialize() { base.Initialize(); + Query = GetEntityQuery(); + SubscribeLocalEvent(OnExamine); } @@ -30,9 +34,9 @@ protected virtual void OnExamine(EntityUid uid, LimitedChargesComponent comp, Ex /// /// Tries to add a number of charges. If it over or underflows it will be clamped, wasting the extra charges. /// - public void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp = null) + public virtual void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp = null) { - if (!Resolve(uid, ref comp, false)) + if (!Query.Resolve(uid, ref comp, false)) return; var old = comp.Charges; @@ -47,7 +51,7 @@ public void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp public bool IsEmpty(EntityUid uid, LimitedChargesComponent? comp = null) { // can't be empty if there are no limited charges - if (!Resolve(uid, ref comp, false)) + if (!Query.Resolve(uid, ref comp, false)) return false; return comp.Charges <= 0; @@ -56,10 +60,24 @@ public bool IsEmpty(EntityUid uid, LimitedChargesComponent? comp = null) /// /// Uses a single charge. Must check IsEmpty beforehand to prevent using with 0 charge. /// - public virtual void UseCharge(EntityUid uid, LimitedChargesComponent? comp = null) + public void UseCharge(EntityUid uid, LimitedChargesComponent? comp = null) + { + AddCharges(uid, -1, comp); + } + + /// + /// Checks IsEmpty and uses a charge if it isn't empty. + /// + public bool TryUseCharge(Entity ent) { - if (Resolve(uid, ref comp, false)) - AddCharges(uid, -1, comp); + if (!Query.Resolve(ent, ref ent.Comp, false)) + return true; + + if (IsEmpty(ent, ent.Comp)) + return false; + + UseCharge(ent, ent.Comp); + return true; } /// @@ -80,7 +98,6 @@ public bool HasInsufficientCharges(EntityUid uid, int requiredCharges, LimitedCh /// public virtual void UseCharges(EntityUid uid, int chargesUsed, LimitedChargesComponent? comp = null) { - if (Resolve(uid, ref comp, false)) - AddCharges(uid, -chargesUsed, comp); + AddCharges(uid, -chargesUsed, comp); } } diff --git a/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs b/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs index c3c4baf19d0..866ce38a572 100644 --- a/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs +++ b/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs @@ -3,20 +3,18 @@ namespace Content.Shared.Clothing; +/// +/// Modifies speed when worn and activated. +/// Supports ItemToggleComponent. +/// [RegisterComponent, NetworkedComponent, Access(typeof(ClothingSpeedModifierSystem))] public sealed partial class ClothingSpeedModifierComponent : Component { - [DataField("walkModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)] + [DataField] public float WalkModifier = 1.0f; - [DataField("sprintModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)] + [DataField] public float SprintModifier = 1.0f; - - /// - /// Is this clothing item currently 'actively' slowing you down? - /// e.g. magboots can be turned on and off. - /// - [DataField("enabled")] public bool Enabled = true; } [Serializable, NetSerializable] @@ -25,12 +23,9 @@ public sealed class ClothingSpeedModifierComponentState : ComponentState public float WalkModifier; public float SprintModifier; - public bool Enabled; - - public ClothingSpeedModifierComponentState(float walkModifier, float sprintModifier, bool enabled) + public ClothingSpeedModifierComponentState(float walkModifier, float sprintModifier) { WalkModifier = walkModifier; SprintModifier = sprintModifier; - Enabled = enabled; } } diff --git a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs index 66ff5c624a4..c89ab827e67 100644 --- a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs +++ b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs @@ -1,11 +1,10 @@ -using Content.Shared.Actions; using Content.Shared.Clothing.Components; using Content.Shared.Examine; -using Content.Shared.IdentityManagement; using Content.Shared.Inventory; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Movement.Systems; using Content.Shared.PowerCell; -using Content.Shared.Toggleable; using Content.Shared.Verbs; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -15,12 +14,12 @@ namespace Content.Shared.Clothing; public sealed class ClothingSpeedModifierSystem : EntitySystem { - [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly ClothingSpeedModifierSystem _clothingSpeedModifier = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedPowerCellSystem _powerCell = default!; public override void Initialize() @@ -31,39 +30,12 @@ public override void Initialize() SubscribeLocalEvent(OnHandleState); SubscribeLocalEvent>(OnRefreshMoveSpeed); SubscribeLocalEvent>(OnClothingVerbExamine); - - SubscribeLocalEvent>(AddToggleVerb); - SubscribeLocalEvent(OnGetActions); - SubscribeLocalEvent(OnToggleSpeed); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnPowerCellSlotEmpty); + SubscribeLocalEvent(OnToggled); } - // Public API - - public void SetClothingSpeedModifierEnabled(EntityUid uid, bool enabled, ClothingSpeedModifierComponent? component = null) - { - if (!Resolve(uid, ref component, false)) - return; - - if (component.Enabled != enabled) - { - component.Enabled = enabled; - Dirty(uid, component); - - // inventory system will automatically hook into the event raised by this and update accordingly - if (_container.TryGetContainingContainer(uid, out var container)) - { - _movementSpeed.RefreshMovementSpeedModifiers(container.Owner); - } - } - } - - // Event handlers - private void OnGetState(EntityUid uid, ClothingSpeedModifierComponent component, ref ComponentGetState args) { - args.State = new ClothingSpeedModifierComponentState(component.WalkModifier, component.SprintModifier, component.Enabled); + args.State = new ClothingSpeedModifierComponentState(component.WalkModifier, component.SprintModifier); } private void OnHandleState(EntityUid uid, ClothingSpeedModifierComponent component, ref ComponentHandleState args) @@ -71,13 +43,11 @@ private void OnHandleState(EntityUid uid, ClothingSpeedModifierComponent compone if (args.Current is not ClothingSpeedModifierComponentState state) return; - var diff = component.Enabled != state.Enabled || - !MathHelper.CloseTo(component.SprintModifier, state.SprintModifier) || + var diff = !MathHelper.CloseTo(component.SprintModifier, state.SprintModifier) || !MathHelper.CloseTo(component.WalkModifier, state.WalkModifier); component.WalkModifier = state.WalkModifier; component.SprintModifier = state.SprintModifier; - component.Enabled = state.Enabled; // Avoid raising the event for the container if nothing changed. // We'll still set the values in case they're slightly different but within tolerance. @@ -89,10 +59,8 @@ private void OnHandleState(EntityUid uid, ClothingSpeedModifierComponent compone private void OnRefreshMoveSpeed(EntityUid uid, ClothingSpeedModifierComponent component, InventoryRelayedEvent args) { - if (!component.Enabled) - return; - - args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); + if (_toggle.IsActivated(uid)) + args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); } private void OnClothingVerbExamine(EntityUid uid, ClothingSpeedModifierComponent component, GetVerbsEvent args) @@ -142,60 +110,15 @@ private void OnClothingVerbExamine(EntityUid uid, ClothingSpeedModifierComponent _examine.AddDetailedExamineVerb(args, component, msg, Loc.GetString("clothing-speed-examinable-verb-text"), "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png", Loc.GetString("clothing-speed-examinable-verb-message")); } - private void OnMapInit(Entity uid, ref MapInitEvent args) - { - _actions.AddAction(uid, ref uid.Comp.ToggleActionEntity, uid.Comp.ToggleAction); - } - - private void OnToggleSpeed(Entity uid, ref ToggleClothingSpeedEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - SetSpeedToggleEnabled(uid, !uid.Comp.Enabled, args.Performer); - } - - private void SetSpeedToggleEnabled(Entity uid, bool value, EntityUid? user) + private void OnToggled(Entity ent, ref ItemToggledEvent args) { - if (uid.Comp.Enabled == value) - return; - - TryComp(uid, out var draw); - if (value && !_powerCell.HasDrawCharge(uid, draw, user: user)) - return; + // make sentient boots slow or fast too + _movementSpeed.RefreshMovementSpeedModifiers(ent); - uid.Comp.Enabled = value; - - _appearance.SetData(uid, ToggleVisuals.Toggled, uid.Comp.Enabled); - _actions.SetToggled(uid.Comp.ToggleActionEntity, uid.Comp.Enabled); - _clothingSpeedModifier.SetClothingSpeedModifierEnabled(uid.Owner, uid.Comp.Enabled); - _powerCell.SetPowerCellDrawEnabled(uid, uid.Comp.Enabled, draw); - Dirty(uid, uid.Comp); - } - - private void AddToggleVerb(Entity uid, ref GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - var user = args.User; - ActivationVerb verb = new() + if (_container.TryGetContainingContainer(ent.Owner, out var container)) { - Text = Loc.GetString("toggle-clothing-verb-text", - ("entity", Identity.Entity(uid, EntityManager))), - Act = () => SetSpeedToggleEnabled(uid, !uid.Comp.Enabled, user) - }; - args.Verbs.Add(verb); - } - - private void OnGetActions(Entity uid, ref GetItemActionsEvent args) - { - args.AddAction(ref uid.Comp.ToggleActionEntity, uid.Comp.ToggleAction); - } - - private void OnPowerCellSlotEmpty(Entity uid, ref PowerCellSlotEmptyEvent args) - { - SetSpeedToggleEnabled(uid, false, null); + // inventory system will automatically hook into the event raised by this and update accordingly + _movementSpeed.RefreshMovementSpeedModifiers(container.Owner); + } } } diff --git a/Content.Shared/Clothing/Components/StealthClothingComponent.cs b/Content.Shared/Clothing/Components/StealthClothingComponent.cs deleted file mode 100644 index fedf48b36ed..00000000000 --- a/Content.Shared/Clothing/Components/StealthClothingComponent.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Clothing.EntitySystems; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Clothing.Components; - -/// -/// Adds StealthComponent to the user when enabled, either by an action or the system's SetEnabled method. -/// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(StealthClothingSystem))] -public sealed partial class StealthClothingComponent : Component -{ - /// - /// Whether stealth effect is enabled. - /// - [DataField("enabled"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public bool Enabled; - - /// - /// Number added to MinVisibility when stealthed, to make the user not fully invisible. - /// - [DataField("visibility"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public float Visibility; - - [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ToggleAction = "ActionTogglePhaseCloak"; - - /// - /// The action for enabling and disabling stealth. - /// - [DataField, AutoNetworkedField] public EntityUid? ToggleActionEntity; -} - -/// -/// When stealth is enabled, disables it. -/// When it is disabled, raises before enabling. -/// Put any checks in a handler for that event to cancel it. -/// -public sealed partial class ToggleStealthEvent : InstantActionEvent -{ -} diff --git a/Content.Shared/Clothing/Components/ToggleClothingComponent.cs b/Content.Shared/Clothing/Components/ToggleClothingComponent.cs new file mode 100644 index 00000000000..c77aa03475f --- /dev/null +++ b/Content.Shared/Clothing/Components/ToggleClothingComponent.cs @@ -0,0 +1,40 @@ +using Content.Shared.Actions; +using Content.Shared.Clothing.EntitySystems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Clothing.Components; + +/// +/// Clothing that can be enabled and disabled with an action. +/// Requires . +/// +/// +/// Not to be confused with for hardsuit helmets and such. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(ToggleClothingSystem))] +public sealed partial class ToggleClothingComponent : Component +{ + /// + /// The action to add when equipped, even if not worn. + /// This must raise to then get handled. + /// + [DataField(required: true)] + public EntProtoId Action = string.Empty; + + [DataField, AutoNetworkedField] + public EntityUid? ActionEntity; + + /// + /// If true, automatically disable the clothing after unequipping it. + /// + [DataField] + public bool DisableOnUnequip; +} + +/// +/// Raised on the clothing when being equipped to see if it should add the action. +/// +[ByRefEvent] +public record struct ToggleClothingCheckEvent(EntityUid User, bool Cancelled = false); diff --git a/Content.Shared/Clothing/Components/ToggleClothingSpeedComponent.cs b/Content.Shared/Clothing/Components/ToggleClothingSpeedComponent.cs deleted file mode 100644 index 90b2d7322e0..00000000000 --- a/Content.Shared/Clothing/Components/ToggleClothingSpeedComponent.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Content.Shared.Actions; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Clothing.Components; - -/// -/// This is used for a clothing item that gives a speed modification that is toggleable. -/// -[RegisterComponent, NetworkedComponent, Access(typeof(ClothingSpeedModifierSystem)), AutoGenerateComponentState] -public sealed partial class ToggleClothingSpeedComponent : Component -{ - /// - /// The action for toggling the clothing. - /// - [DataField] - public EntProtoId ToggleAction = "ActionToggleSpeedBoots"; - - /// - /// The action entity - /// - [DataField, AutoNetworkedField] - public EntityUid? ToggleActionEntity; - - /// - /// The state of the toggle. - /// - [DataField, AutoNetworkedField] - public bool Enabled; -} - -public sealed partial class ToggleClothingSpeedEvent : InstantActionEvent -{ - -} diff --git a/Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs deleted file mode 100644 index e96d9f866aa..00000000000 --- a/Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs +++ /dev/null @@ -1,144 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Clothing.Components; -using Content.Shared.Inventory.Events; -using Content.Shared.Stealth; -using Content.Shared.Stealth.Components; - -namespace Content.Shared.Clothing.EntitySystems; - -/// -/// Handles the toggle action and disables stealth when clothing is unequipped. -/// -public sealed class StealthClothingSystem : EntitySystem -{ - [Dependency] private readonly SharedStealthSystem _stealth = default!; - [Dependency] private readonly ActionContainerSystem _actionContainer = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnGetItemActions); - SubscribeLocalEvent(OnToggleStealth); - SubscribeLocalEvent(OnHandleState); - SubscribeLocalEvent(OnUnequipped); - SubscribeLocalEvent(OnMapInit); - } - - private void OnMapInit(EntityUid uid, StealthClothingComponent component, MapInitEvent args) - { - _actionContainer.EnsureAction(uid, ref component.ToggleActionEntity, component.ToggleAction); - Dirty(uid, component); - } - - /// - /// Sets the clothing's stealth effect for the user. - /// - /// True if it was changed, false otherwise - public bool SetEnabled(EntityUid uid, EntityUid user, bool enabled, StealthClothingComponent? comp = null) - { - if (!Resolve(uid, ref comp) || comp.Enabled == enabled) - return false; - - // TODO remove this when clothing unequip on delete is less sus - // prevent debug assert when ending round and its disabled - if (MetaData(user).EntityLifeStage >= EntityLifeStage.Terminating) - return false; - - comp.Enabled = enabled; - Dirty(uid, comp); - - var stealth = EnsureComp(user); - // slightly visible, but doesn't change when moving so it's ok - var visibility = enabled ? stealth.MinVisibility + comp.Visibility : stealth.MaxVisibility; - _stealth.SetVisibility(user, visibility, stealth); - _stealth.SetEnabled(user, enabled, stealth); - return true; - } - - /// - /// Raise then add the toggle action if it was not cancelled. - /// - private void OnGetItemActions(EntityUid uid, StealthClothingComponent comp, GetItemActionsEvent args) - { - var ev = new AddStealthActionEvent(args.User); - RaiseLocalEvent(uid, ev); - if (ev.Cancelled) - return; - - args.AddAction(ref comp.ToggleActionEntity, comp.ToggleAction); - } - - /// - /// Raises if enabling. - /// - private void OnToggleStealth(EntityUid uid, StealthClothingComponent comp, ToggleStealthEvent args) - { - args.Handled = true; - var user = args.Performer; - if (comp.Enabled) - { - SetEnabled(uid, user, false, comp); - return; - } - - var ev = new AttemptStealthEvent(user); - RaiseLocalEvent(uid, ev); - if (ev.Cancelled) - return; - - SetEnabled(uid, user, true, comp); - } - - /// - /// Calls when server sends new state. - /// - private void OnHandleState(EntityUid uid, StealthClothingComponent comp, ref AfterAutoHandleStateEvent args) - { - // SetEnabled checks if it is the same, so change it to before state was received from the server - var enabled = comp.Enabled; - comp.Enabled = !enabled; - var user = Transform(uid).ParentUid; - SetEnabled(uid, user, enabled, comp); - } - - /// - /// Force unstealths the user, doesnt remove StealthComponent since other things might use it - /// - private void OnUnequipped(EntityUid uid, StealthClothingComponent comp, GotUnequippedEvent args) - { - SetEnabled(uid, args.Equipee, false, comp); - } -} - -/// -/// Raised on the stealth clothing when attempting to add an action. -/// -public sealed class AddStealthActionEvent : CancellableEntityEventArgs -{ - /// - /// User that equipped the stealth clothing. - /// - public EntityUid User; - - public AddStealthActionEvent(EntityUid user) - { - User = user; - } -} - -/// -/// Raised on the stealth clothing when the user is attemping to enable it. -/// -public sealed class AttemptStealthEvent : CancellableEntityEventArgs -{ - /// - /// User that is attempting to enable the stealth clothing. - /// - public EntityUid User; - - public AttemptStealthEvent(EntityUid user) - { - User = user; - } -} diff --git a/Content.Shared/Clothing/EntitySystems/ToggleClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/ToggleClothingSystem.cs new file mode 100644 index 00000000000..9889376c9d4 --- /dev/null +++ b/Content.Shared/Clothing/EntitySystems/ToggleClothingSystem.cs @@ -0,0 +1,58 @@ +using Content.Shared.Actions; +using Content.Shared.Clothing; +using Content.Shared.Clothing.Components; +using Content.Shared.Inventory; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Toggleable; + +namespace Content.Shared.Clothing.EntitySystems; + +/// +/// Handles adding and using a toggle action for . +/// +public sealed class ToggleClothingSystem : EntitySystem +{ + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnGetActions); + SubscribeLocalEvent(OnToggleAction); + SubscribeLocalEvent(OnUnequipped); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + var (uid, comp) = ent; + // test funny + if (string.IsNullOrEmpty(comp.Action)) + return; + + _actions.AddAction(uid, ref comp.ActionEntity, comp.Action); + _actions.SetToggled(comp.ActionEntity, _toggle.IsActivated(ent.Owner)); + Dirty(uid, comp); + } + + private void OnGetActions(Entity ent, ref GetItemActionsEvent args) + { + var ev = new ToggleClothingCheckEvent(args.User); + RaiseLocalEvent(ent, ref ev); + if (!ev.Cancelled) + args.AddAction(ent.Comp.ActionEntity); + } + + private void OnToggleAction(Entity ent, ref ToggleActionEvent args) + { + args.Handled = _toggle.Toggle(ent.Owner, args.Performer); + } + + private void OnUnequipped(Entity ent, ref ClothingGotUnequippedEvent args) + { + if (ent.Comp.DisableOnUnequip) + _toggle.TryDeactivate(ent.Owner, args.Wearer); + } +} diff --git a/Content.Shared/Clothing/MagbootsComponent.cs b/Content.Shared/Clothing/MagbootsComponent.cs index 0d074ff38b6..4bef74fd335 100644 --- a/Content.Shared/Clothing/MagbootsComponent.cs +++ b/Content.Shared/Clothing/MagbootsComponent.cs @@ -1,23 +1,25 @@ using Content.Shared.Alert; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Clothing; -[RegisterComponent, NetworkedComponent(), AutoGenerateComponentState] +[RegisterComponent, NetworkedComponent] [Access(typeof(SharedMagbootsSystem))] public sealed partial class MagbootsComponent : Component { [DataField] - public EntProtoId ToggleAction = "ActionToggleMagboots"; - - [DataField, AutoNetworkedField] - public EntityUid? ToggleActionEntity; + public ProtoId MagbootsAlert = "Magboots"; - [DataField("on"), AutoNetworkedField] - public bool On; + /// + /// If true, the user must be standing on a grid or planet map to experience the weightlessness-canceling effect + /// + [DataField] + public bool RequiresGrid = true; + /// + /// Slot the clothing has to be worn in to work. + /// [DataField] - public ProtoId MagbootsAlert = "Magboots"; + public string Slot = "shoes"; } diff --git a/Content.Shared/Clothing/MagbootsSystem.cs b/Content.Shared/Clothing/MagbootsSystem.cs new file mode 100644 index 00000000000..88d987aae18 --- /dev/null +++ b/Content.Shared/Clothing/MagbootsSystem.cs @@ -0,0 +1,90 @@ +using Content.Shared.Actions; +using Content.Shared.Alert; +using Content.Shared.Atmos.Components; +using Content.Shared.Clothing.EntitySystems; +using Content.Shared.Gravity; +using Content.Shared.Inventory; +using Content.Shared.Item; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; +using Robust.Shared.Containers; + +namespace Content.Shared.Clothing; + +public sealed class SharedMagbootsSystem : EntitySystem +{ + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly ClothingSystem _clothing = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly SharedItemSystem _item = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggled); + SubscribeLocalEvent(OnGotEquipped); + SubscribeLocalEvent(OnGotUnequipped); + SubscribeLocalEvent(OnIsWeightless); + SubscribeLocalEvent>(OnIsWeightless); + } + + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + var (uid, comp) = ent; + // only stick to the floor if being worn in the correct slot + if (_container.TryGetContainingContainer(uid, out var container) && + _inventory.TryGetSlotEntity(container.Owner, comp.Slot, out var worn) + && uid == worn) + { + UpdateMagbootEffects(container.Owner, ent, args.Activated); + } + + var prefix = args.Activated ? "on" : null; + _item.SetHeldPrefix(ent, prefix); + _clothing.SetEquippedPrefix(ent, prefix); + } + + private void OnGotUnequipped(Entity ent, ref ClothingGotUnequippedEvent args) + { + UpdateMagbootEffects(args.Wearer, ent, false); + } + + private void OnGotEquipped(Entity ent, ref ClothingGotEquippedEvent args) + { + UpdateMagbootEffects(args.Wearer, ent, _toggle.IsActivated(ent.Owner)); + } + + public void UpdateMagbootEffects(EntityUid user, Entity ent, bool state) + { + // TODO: public api for this and add access + if (TryComp(user, out var moved)) + moved.Enabled = !state; + + if (state) + _alerts.ShowAlert(user, ent.Comp.MagbootsAlert); + else + _alerts.ClearAlert(user, ent.Comp.MagbootsAlert); + } + + private void OnIsWeightless(Entity ent, ref IsWeightlessEvent args) + { + if (args.Handled || !_toggle.IsActivated(ent.Owner)) + return; + + // do not cancel weightlessness if the person is in off-grid. + if (ent.Comp.RequiresGrid && !_gravity.EntityOnGravitySupportingGridOrMap(ent.Owner)) + return; + + args.IsWeightless = false; + args.Handled = true; + } + + private void OnIsWeightless(Entity ent, ref InventoryRelayedEvent args) + { + OnIsWeightless(ent, ref args.Args); + } +} diff --git a/Content.Shared/Clothing/SharedMagbootsSystem.cs b/Content.Shared/Clothing/SharedMagbootsSystem.cs deleted file mode 100644 index 27fb0c1a502..00000000000 --- a/Content.Shared/Clothing/SharedMagbootsSystem.cs +++ /dev/null @@ -1,101 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Clothing.EntitySystems; -using Content.Shared.Inventory; -using Content.Shared.Item; -using Content.Shared.Slippery; -using Content.Shared.Toggleable; -using Content.Shared.Verbs; -using Robust.Shared.Containers; - -namespace Content.Shared.Clothing; - -public abstract class SharedMagbootsSystem : EntitySystem -{ - [Dependency] private readonly ClothingSpeedModifierSystem _clothingSpeedModifier = default!; - [Dependency] private readonly ClothingSystem _clothing = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly SharedActionsSystem _sharedActions = default!; - [Dependency] private readonly SharedActionsSystem _actionContainer = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedContainerSystem _sharedContainer = default!; - [Dependency] private readonly SharedItemSystem _item = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent>(AddToggleVerb); - SubscribeLocalEvent>(OnSlipAttempt); - SubscribeLocalEvent(OnGetActions); - SubscribeLocalEvent(OnToggleMagboots); - SubscribeLocalEvent(OnMapInit); - } - - private void OnMapInit(EntityUid uid, MagbootsComponent component, MapInitEvent args) - { - _actionContainer.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction); - Dirty(uid, component); - } - - private void OnToggleMagboots(EntityUid uid, MagbootsComponent component, ToggleMagbootsEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - - ToggleMagboots(uid, component); - } - - private void ToggleMagboots(EntityUid uid, MagbootsComponent magboots) - { - magboots.On = !magboots.On; - - if (_sharedContainer.TryGetContainingContainer(uid, out var container) && - _inventory.TryGetSlotEntity(container.Owner, "shoes", out var entityUid) && entityUid == uid) - UpdateMagbootEffects(container.Owner, uid, true, magboots); - - if (TryComp(uid, out var item)) - { - _item.SetHeldPrefix(uid, magboots.On ? "on" : null, component: item); - _clothing.SetEquippedPrefix(uid, magboots.On ? "on" : null); - } - - _appearance.SetData(uid, ToggleVisuals.Toggled, magboots.On); - OnChanged(uid, magboots); - Dirty(uid, magboots); - } - - protected virtual void UpdateMagbootEffects(EntityUid parent, EntityUid uid, bool state, MagbootsComponent? component) { } - - protected void OnChanged(EntityUid uid, MagbootsComponent component) - { - _sharedActions.SetToggled(component.ToggleActionEntity, component.On); - _clothingSpeedModifier.SetClothingSpeedModifierEnabled(uid, component.On); - } - - private void AddToggleVerb(EntityUid uid, MagbootsComponent component, GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - ActivationVerb verb = new(); - verb.Text = Loc.GetString("toggle-magboots-verb-get-data-text"); - verb.Act = () => ToggleMagboots(uid, component); - // TODO VERB ICON add toggle icon? maybe a computer on/off symbol? - args.Verbs.Add(verb); - } - - private void OnSlipAttempt(EntityUid uid, MagbootsComponent component, InventoryRelayedEvent args) - { - if (component.On) - args.Args.Cancel(); - } - - private void OnGetActions(EntityUid uid, MagbootsComponent component, GetItemActionsEvent args) - { - args.AddAction(ref component.ToggleActionEntity, component.ToggleAction); - } -} - -public sealed partial class ToggleMagbootsEvent : InstantActionEvent {} diff --git a/Content.Shared/Item/ItemToggle/ComponentTogglerSystem.cs b/Content.Shared/Item/ItemToggle/ComponentTogglerSystem.cs new file mode 100644 index 00000000000..760cefe27d4 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/ComponentTogglerSystem.cs @@ -0,0 +1,26 @@ +using Content.Shared.Item.ItemToggle.Components; + +namespace Content.Shared.Item.ItemToggle; + +/// +/// Handles component manipulation. +/// +public sealed class ComponentTogglerSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggled); + } + + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + var target = ent.Comp.Parent ? Transform(ent).ParentUid : ent.Owner; + + if (args.Activated) + EntityManager.AddComponents(target, ent.Comp.Components); + else + EntityManager.RemoveComponents(target, ent.Comp.RemoveComponents ?? ent.Comp.Components); + } +} diff --git a/Content.Shared/Item/ItemToggle/Components/ComponentTogglerComponent.cs b/Content.Shared/Item/ItemToggle/Components/ComponentTogglerComponent.cs new file mode 100644 index 00000000000..20ef0a02315 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/Components/ComponentTogglerComponent.cs @@ -0,0 +1,32 @@ +using Content.Shared.Item.ItemToggle; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Item.ItemToggle.Components; + +/// +/// Adds or removes components when toggled. +/// Requires . +/// +[RegisterComponent, NetworkedComponent, Access(typeof(ComponentTogglerSystem))] +public sealed partial class ComponentTogglerComponent : Component +{ + /// + /// The components to add when activated. + /// + [DataField(required: true)] + public ComponentRegistry Components = new(); + + /// + /// The components to remove when deactivated. + /// If this is null is reused. + /// + [DataField] + public ComponentRegistry? RemoveComponents; + + /// + /// If true, adds components on the entity's parent instead of the entity itself. + /// + [DataField] + public bool Parent; +} diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs index 6d534713578..cdac49ae6d6 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs @@ -12,12 +12,12 @@ public sealed partial class ItemToggleActiveSoundComponent : Component /// /// The continuous noise this item makes when it's activated (like an e-sword's hum). /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + [DataField(required: true), AutoNetworkedField] public SoundSpecifier? ActiveSound; /// /// Used when the item emits sound while active. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField] public EntityUid? PlayingStream; } diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs index 620ddfd1942..46249fdd0de 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs @@ -8,7 +8,7 @@ namespace Content.Shared.Item.ItemToggle.Components; /// /// /// If you need extended functionality (e.g. requiring power) then add a new component and use events: -/// ItemToggleActivateAttemptEvent, ItemToggleDeactivateAttemptEvent or ItemToggleForceToggleEvent. +/// ItemToggleActivateAttemptEvent, ItemToggleDeactivateAttemptEvent, ItemToggledEvent. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ItemToggleComponent : Component @@ -19,6 +19,13 @@ public sealed partial class ItemToggleComponent : Component [DataField, AutoNetworkedField] public bool Activated = false; + /// + /// If this is set to false then the item can't be toggled by pressing Z. + /// Use another system to do it then. + /// + [DataField] + public bool OnUse = true; + /// /// Whether the item's toggle can be predicted by the client. /// diff --git a/Content.Shared/Item/ItemToggle/Components/ToggleVerbComponent.cs b/Content.Shared/Item/ItemToggle/Components/ToggleVerbComponent.cs new file mode 100644 index 00000000000..b673c55e0f1 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/Components/ToggleVerbComponent.cs @@ -0,0 +1,18 @@ +using Content.Shared.Item.ItemToggle; +using Robust.Shared.GameStates; + +namespace Content.Shared.Item.ItemToggle.Components; + +/// +/// Adds a verb for toggling something, requires . +/// +[RegisterComponent, NetworkedComponent, Access(typeof(ToggleVerbSystem))] +public sealed partial class ToggleVerbComponent : Component +{ + /// + /// Text the verb will have. + /// Gets passed "entity" as the entity's identity string. + /// + [DataField(required: true)] + public LocId Text = string.Empty; +} diff --git a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs similarity index 67% rename from Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs rename to Content.Shared/Item/ItemToggle/ItemToggleSystem.cs index d07fd5a735e..c4e4150659b 100644 --- a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs @@ -18,12 +18,12 @@ namespace Content.Shared.Item.ItemToggle; /// /// If you need extended functionality (e.g. requiring power) then add a new component and use events. /// -public abstract class SharedItemToggleSystem : EntitySystem +public sealed class ItemToggleSystem : EntitySystem { - [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedPointLightSystem _light = default!; - [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() @@ -31,8 +31,9 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(TurnOffonUnwielded); - SubscribeLocalEvent(TurnOnonWielded); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(TurnOffOnUnwielded); + SubscribeLocalEvent(TurnOnOnWielded); SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnIsHotEvent); @@ -47,57 +48,76 @@ private void OnStartup(Entity ent, ref ComponentStartup arg UpdateVisuals(ent); } - private void OnUseInHand(EntityUid uid, ItemToggleComponent itemToggle, UseInHandEvent args) + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + if (!ent.Comp.Activated) + return; + + var ev = new ItemToggledEvent(Predicted: ent.Comp.Predictable, Activated: ent.Comp.Activated, User: null); + RaiseLocalEvent(ent, ref ev); + } + + private void OnUseInHand(Entity ent, ref UseInHandEvent args) { - if (args.Handled) + if (args.Handled || !ent.Comp.OnUse) return; args.Handled = true; - Toggle(uid, args.User, predicted: itemToggle.Predictable, itemToggle: itemToggle); + Toggle((ent, ent.Comp), args.User, predicted: ent.Comp.Predictable); } /// /// Used when an item is attempted to be toggled. + /// Sets its state to the opposite of what it is. /// - public void Toggle(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null) + /// Same as + public bool Toggle(Entity ent, EntityUid? user = null, bool predicted = true) { - if (!Resolve(uid, ref itemToggle)) - return; + if (!Resolve(ent, ref ent.Comp)) + return false; - if (itemToggle.Activated) - { - TryDeactivate(uid, user, itemToggle: itemToggle, predicted: predicted); - } + return TrySetActive(ent, !ent.Comp.Activated, user, predicted); + } + + /// + /// Tries to set the activated bool from a value. + /// + /// false if the attempt fails for any reason + public bool TrySetActive(Entity ent, bool active, EntityUid? user = null, bool predicted = true) + { + if (active) + return TryActivate(ent, user, predicted: predicted); else - { - TryActivate(uid, user, itemToggle: itemToggle, predicted: predicted); - } + return TryDeactivate(ent, user, predicted: predicted); } /// /// Used when an item is attempting to be activated. It returns false if the attempt fails any reason, interrupting the activation. /// - public bool TryActivate(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null) + public bool TryActivate(Entity ent, EntityUid? user = null, bool predicted = true) { - if (!Resolve(uid, ref itemToggle)) + if (!Resolve(ent, ref ent.Comp)) return false; - if (itemToggle.Activated) + var uid = ent.Owner; + var comp = ent.Comp; + if (comp.Activated) return true; - if (!itemToggle.Predictable && _netManager.IsClient) + if (!comp.Predictable && _netManager.IsClient) return true; var attempt = new ItemToggleActivateAttemptEvent(user); RaiseLocalEvent(uid, ref attempt); + if (!comp.Predictable) predicted = false; if (attempt.Cancelled) { if (predicted) - _audio.PlayPredicted(itemToggle.SoundFailToActivate, uid, user); + _audio.PlayPredicted(comp.SoundFailToActivate, uid, user); else - _audio.PlayPvs(itemToggle.SoundFailToActivate, uid); + _audio.PlayPvs(comp.SoundFailToActivate, uid); if (attempt.Popup != null && user != null) { @@ -110,7 +130,7 @@ public bool TryActivate(EntityUid uid, EntityUid? user = null, bool predicted = return false; } - Activate(uid, itemToggle, predicted, user); + Activate((uid, comp), predicted, user); return true; } @@ -118,75 +138,65 @@ public bool TryActivate(EntityUid uid, EntityUid? user = null, bool predicted = /// /// Used when an item is attempting to be deactivated. It returns false if the attempt fails any reason, interrupting the deactivation. /// - public bool TryDeactivate(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null) + public bool TryDeactivate(Entity ent, EntityUid? user = null, bool predicted = true) { - if (!Resolve(uid, ref itemToggle)) + if (!Resolve(ent, ref ent.Comp)) return false; - if (!itemToggle.Predictable && _netManager.IsClient) + var uid = ent.Owner; + var comp = ent.Comp; + if (!comp.Activated) return true; - if (!itemToggle.Activated) + if (!comp.Predictable && _netManager.IsClient) return true; var attempt = new ItemToggleDeactivateAttemptEvent(user); RaiseLocalEvent(uid, ref attempt); if (attempt.Cancelled) - { return false; - } - Deactivate(uid, itemToggle, predicted, user); + if (!comp.Predictable) predicted = false; + Deactivate((uid, comp), predicted, user); return true; } - private void Activate(EntityUid uid, ItemToggleComponent itemToggle, bool predicted, EntityUid? user = null) + private void Activate(Entity ent, bool predicted, EntityUid? user = null) { - // TODO: Fix this hardcoding - TryComp(uid, out AppearanceComponent? appearance); - _appearance.SetData(uid, ToggleableLightVisuals.Enabled, true, appearance); - _appearance.SetData(uid, ToggleVisuals.Toggled, true, appearance); - - if (_light.TryGetLight(uid, out var light)) - { - _light.SetEnabled(uid, true, light); - } - - var soundToPlay = itemToggle.SoundActivate; + var (uid, comp) = ent; + var soundToPlay = comp.SoundActivate; if (predicted) _audio.PlayPredicted(soundToPlay, uid, user); else _audio.PlayPvs(soundToPlay, uid); - // END FIX HARDCODING + comp.Activated = true; + UpdateVisuals((uid, comp)); + Dirty(uid, comp); var toggleUsed = new ItemToggledEvent(predicted, Activated: true, user); RaiseLocalEvent(uid, ref toggleUsed); - - itemToggle.Activated = true; - UpdateVisuals((uid, itemToggle)); - Dirty(uid, itemToggle); } /// /// Used to make the actual changes to the item's components on deactivation. /// - private void Deactivate(EntityUid uid, ItemToggleComponent itemToggle, bool predicted, EntityUid? user = null) + private void Deactivate(Entity ent, bool predicted, EntityUid? user = null) { - var soundToPlay = itemToggle.SoundDeactivate; + var (uid, comp) = ent; + var soundToPlay = comp.SoundDeactivate; if (predicted) _audio.PlayPredicted(soundToPlay, uid, user); else _audio.PlayPvs(soundToPlay, uid); - // END FIX HARDCODING + + comp.Activated = false; + UpdateVisuals((uid, comp)); + Dirty(uid, comp); var toggleUsed = new ItemToggledEvent(predicted, Activated: false, user); RaiseLocalEvent(uid, ref toggleUsed); - - itemToggle.Activated = false; - UpdateVisuals((uid, itemToggle)); - Dirty(uid, itemToggle); } private void UpdateVisuals(Entity ent) @@ -209,68 +219,56 @@ private void UpdateVisuals(Entity ent) /// /// 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. /// - private void TurnOffonUnwielded(EntityUid uid, ItemToggleComponent itemToggle, ItemUnwieldedEvent args) + private void TurnOffOnUnwielded(Entity ent, ref ItemUnwieldedEvent args) { - if (itemToggle.Activated) - TryDeactivate(uid, args.User, itemToggle: itemToggle); + TryDeactivate((ent, ent.Comp), args.User); } /// /// Wieldable items will automatically turn on when wielded. /// - private void TurnOnonWielded(EntityUid uid, ItemToggleComponent itemToggle, ref ItemWieldedEvent args) + private void TurnOnOnWielded(Entity ent, ref ItemWieldedEvent args) { - if (!itemToggle.Activated) - TryActivate(uid, itemToggle: itemToggle); + // FIXME: for some reason both client and server play sound + TryActivate((ent, ent.Comp)); } - public bool IsActivated(EntityUid uid, ItemToggleComponent? comp = null) + public bool IsActivated(Entity ent) { - if (!Resolve(uid, ref comp, false)) + if (!Resolve(ent, ref ent.Comp, false)) return true; // assume always activated if no component - return comp.Activated; + return ent.Comp.Activated; } /// /// Used to make the item hot when activated. /// - private void OnIsHotEvent(EntityUid uid, ItemToggleHotComponent itemToggleHot, IsHotEvent args) + private void OnIsHotEvent(Entity ent, ref IsHotEvent args) { - args.IsHot |= IsActivated(uid); + args.IsHot |= IsActivated(ent.Owner); } /// /// Used to update the looping active sound linked to the entity. /// - private void UpdateActiveSound(EntityUid uid, ItemToggleActiveSoundComponent activeSound, ref ItemToggledEvent args) + private void UpdateActiveSound(Entity ent, ref ItemToggledEvent args) { - if (args.Activated) + var (uid, comp) = ent; + if (!args.Activated) { - if (activeSound.ActiveSound != null && activeSound.PlayingStream == null) - { - if (args.Predicted) - { - var playingStream = _audio.PlayPredicted(activeSound.ActiveSound, uid, args.User, AudioParams.Default.WithLoop(true)); - - if (playingStream == null) - return; - - activeSound.PlayingStream = playingStream!.Value.Entity; - } else - { - var playingStream = _audio.PlayPvs(activeSound.ActiveSound, uid, AudioParams.Default.WithLoop(true)); - - if (playingStream == null) - return; - - activeSound.PlayingStream = playingStream!.Value.Entity; - } - } + comp.PlayingStream = _audio.Stop(comp.PlayingStream); + return; } - else + + if (comp.ActiveSound != null && comp.PlayingStream == null) { - activeSound.PlayingStream = _audio.Stop(activeSound.PlayingStream); + var loop = AudioParams.Default.WithLoop(true); + var stream = args.Predicted + ? _audio.PlayPredicted(comp.ActiveSound, uid, args.User, loop) + : _audio.PlayPvs(comp.ActiveSound, uid, loop); + if (stream?.Entity is {} entity) + comp.PlayingStream = entity; } } diff --git a/Content.Shared/Item/ItemToggle/ToggleVerbSystem.cs b/Content.Shared/Item/ItemToggle/ToggleVerbSystem.cs new file mode 100644 index 00000000000..858cd9bc111 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/ToggleVerbSystem.cs @@ -0,0 +1,34 @@ +using Content.Shared.IdentityManagement; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Verbs; + +namespace Content.Shared.Item.ItemToggle; + +/// +/// Adds a verb for toggling something with . +/// +public sealed class ToggleVerbSystem : EntitySystem +{ + [Dependency] private readonly ItemToggleSystem _toggle = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnGetVerbs); + } + + private void OnGetVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + var name = Identity.Entity(ent, EntityManager); + var user = args.User; + args.Verbs.Add(new ActivationVerb() + { + Text = Loc.GetString(ent.Comp.Text, ("entity", name)), + Act = () => _toggle.Toggle(ent.Owner, user) + }); + } +} diff --git a/Content.Shared/Medical/DefibrillatorComponent.cs b/Content.Shared/Medical/DefibrillatorComponent.cs index 2da52852854..1d6f690bc3b 100644 --- a/Content.Shared/Medical/DefibrillatorComponent.cs +++ b/Content.Shared/Medical/DefibrillatorComponent.cs @@ -10,16 +10,11 @@ namespace Content.Shared.Medical; /// /// This is used for defibrillators; a machine that shocks a dead /// person back into the world of the living. +/// Uses ItemToggleComponent /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentPause] public sealed partial class DefibrillatorComponent : Component { - /// - /// Whether or not it's turned on and able to be used. - /// - [DataField("enabled"), ViewVariables(VVAccess.ReadWrite)] - public bool Enabled; - /// /// The time at which the zap cooldown will be completed /// @@ -66,15 +61,6 @@ public sealed partial class DefibrillatorComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField("zapSound")] public SoundSpecifier? ZapSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_zap.ogg"); - /// - /// The sound when the defib is powered on. - /// - [ViewVariables(VVAccess.ReadWrite), DataField("powerOnSound")] - public SoundSpecifier? PowerOnSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_safety_on.ogg"); - - [ViewVariables(VVAccess.ReadWrite), DataField("powerOffSound")] - public SoundSpecifier? PowerOffSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_safety_off.ogg"); - [ViewVariables(VVAccess.ReadWrite), DataField("chargeSound")] public SoundSpecifier? ChargeSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_charge.ogg"); diff --git a/Content.Shared/Ninja/Components/BatteryDrainerComponent.cs b/Content.Shared/Ninja/Components/BatteryDrainerComponent.cs index 55bcdd0f0a5..9c39c4724ce 100644 --- a/Content.Shared/Ninja/Components/BatteryDrainerComponent.cs +++ b/Content.Shared/Ninja/Components/BatteryDrainerComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Ninja.Systems; using Robust.Shared.Audio; +using Robust.Shared.GameStates; namespace Content.Shared.Ninja.Components; @@ -7,32 +8,33 @@ namespace Content.Shared.Ninja.Components; /// Component for draining power from APCs/substations/SMESes, when ProviderUid is set to a battery cell. /// Does not rely on relay, simply being on the user and having BatteryUid set is enough. /// -[RegisterComponent, Access(typeof(SharedBatteryDrainerSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedBatteryDrainerSystem))] public sealed partial class BatteryDrainerComponent : Component { /// /// The powercell entity to drain power into. /// Determines whether draining is possible. /// - [DataField("batteryUid"), ViewVariables(VVAccess.ReadWrite)] + [DataField, AutoNetworkedField] public EntityUid? BatteryUid; /// /// Conversion rate between joules in a device and joules added to battery. /// Should be very low since powercells store nothing compared to even an APC. /// - [DataField("drainEfficiency"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float DrainEfficiency = 0.001f; /// /// Time that the do after takes to drain charge from a battery, in seconds /// - [DataField("drainTime"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float DrainTime = 1f; /// /// Sound played after the doafter ends. /// - [DataField("sparkSound")] + [DataField] public SoundSpecifier SparkSound = new SoundCollectionSpecifier("sparks"); } diff --git a/Content.Shared/Ninja/Components/BombingTargetComponent.cs b/Content.Shared/Ninja/Components/BombingTargetComponent.cs index bf0eaec84be..c429eb6880e 100644 --- a/Content.Shared/Ninja/Components/BombingTargetComponent.cs +++ b/Content.Shared/Ninja/Components/BombingTargetComponent.cs @@ -4,6 +4,4 @@ namespace Content.Shared.Ninja.Components; /// Makes this warp point a valid bombing target for ninja's spider charge. /// [RegisterComponent] -public sealed partial class BombingTargetComponent : Component -{ -} +public sealed partial class BombingTargetComponent : Component; diff --git a/Content.Shared/Ninja/Components/DashAbilityComponent.cs b/Content.Shared/Ninja/Components/DashAbilityComponent.cs index ba4060c7035..464f48f187e 100644 --- a/Content.Shared/Ninja/Components/DashAbilityComponent.cs +++ b/Content.Shared/Ninja/Components/DashAbilityComponent.cs @@ -8,6 +8,7 @@ namespace Content.Shared.Ninja.Components; /// /// Adds an action to dash, teleport to clicked position, when this item is held. +/// Cancel to prevent using it. /// [RegisterComponent, NetworkedComponent, Access(typeof(DashAbilitySystem)), AutoGenerateComponentState] public sealed partial class DashAbilityComponent : Component @@ -16,19 +17,10 @@ public sealed partial class DashAbilityComponent : Component /// The action id for dashing. /// [DataField] - public EntProtoId DashAction = "ActionEnergyKatanaDash"; + public EntProtoId DashAction = "ActionEnergyKatanaDash"; [DataField, AutoNetworkedField] public EntityUid? DashActionEntity; - - /// - /// Sound played when using dash action. - /// - [DataField("blinkSound"), ViewVariables(VVAccess.ReadWrite)] - public SoundSpecifier BlinkSound = new SoundPathSpecifier("/Audio/Magic/blink.ogg") - { - Params = AudioParams.Default.WithVolume(5f) - }; } -public sealed partial class DashEvent : WorldTargetActionEvent { } +public sealed partial class DashEvent : WorldTargetActionEvent; diff --git a/Content.Shared/Ninja/Components/EmagProviderComponent.cs b/Content.Shared/Ninja/Components/EmagProviderComponent.cs index db7678f61d7..ae3e85cbe42 100644 --- a/Content.Shared/Ninja/Components/EmagProviderComponent.cs +++ b/Content.Shared/Ninja/Components/EmagProviderComponent.cs @@ -2,7 +2,7 @@ using Content.Shared.Tag; using Content.Shared.Whitelist; using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Prototypes; namespace Content.Shared.Ninja.Components; @@ -10,19 +10,18 @@ namespace Content.Shared.Ninja.Components; /// Component for emagging things on click. /// No charges but checks against a whitelist. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -[Access(typeof(EmagProviderSystem))] +[RegisterComponent, NetworkedComponent, Access(typeof(EmagProviderSystem))] public sealed partial class EmagProviderComponent : Component { /// /// The tag that marks an entity as immune to emagging. /// - [DataField("emagImmuneTag", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string EmagImmuneTag = "EmagImmune"; + [DataField] + public ProtoId EmagImmuneTag = "EmagImmune"; /// /// Whitelist that entities must be on to work. /// - [DataField("whitelist"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public EntityWhitelist? Whitelist = null; + [DataField] + public EntityWhitelist? Whitelist; } diff --git a/Content.Shared/Ninja/Components/EnergyKatanaComponent.cs b/Content.Shared/Ninja/Components/EnergyKatanaComponent.cs index 33b8fc78933..84c58bb6480 100644 --- a/Content.Shared/Ninja/Components/EnergyKatanaComponent.cs +++ b/Content.Shared/Ninja/Components/EnergyKatanaComponent.cs @@ -7,6 +7,4 @@ namespace Content.Shared.Ninja.Components; /// Requires a ninja with a suit for abilities to work. /// [RegisterComponent, NetworkedComponent] -public sealed partial class EnergyKatanaComponent : Component -{ -} +public sealed partial class EnergyKatanaComponent : Component; diff --git a/Content.Shared/Ninja/Components/ItemCreatorComponent.cs b/Content.Shared/Ninja/Components/ItemCreatorComponent.cs new file mode 100644 index 00000000000..d9f66d21a31 --- /dev/null +++ b/Content.Shared/Ninja/Components/ItemCreatorComponent.cs @@ -0,0 +1,52 @@ +using Content.Shared.Actions; +using Content.Shared.Ninja.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Ninja.Components; + +/// +/// Uses battery charge to spawn an item and place it in the user's hands. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedItemCreatorSystem))] +public sealed partial class ItemCreatorComponent : Component +{ + /// + /// The battery entity to use charge from + /// + [DataField, AutoNetworkedField] + public EntityUid? Battery; + + /// + /// The action id for creating an item. + /// + [DataField(required: true)] + public EntProtoId Action = string.Empty; + + [DataField, AutoNetworkedField] + public EntityUid? ActionEntity; + + /// + /// Battery charge used to create an item. + /// + [DataField(required: true)] + public float Charge = 14.4f; + + /// + /// Item to create with the action + /// + [DataField(required: true)] + public EntProtoId SpawnedPrototype = string.Empty; + + /// + /// Popup shown to the user when there isn't enough power to create an item. + /// + [DataField(required: true)] + public LocId NoPowerPopup = string.Empty; +} + +/// +/// Action event to use an . +/// +public sealed partial class CreateItemEvent : InstantActionEvent; diff --git a/Content.Shared/Ninja/Components/NinjaGlovesComponent.cs b/Content.Shared/Ninja/Components/NinjaGlovesComponent.cs index 7b57926330b..3b9e2a5e356 100644 --- a/Content.Shared/Ninja/Components/NinjaGlovesComponent.cs +++ b/Content.Shared/Ninja/Components/NinjaGlovesComponent.cs @@ -1,20 +1,17 @@ -using Content.Shared.DoAfter; using Content.Shared.Ninja.Systems; -using Content.Shared.Toggleable; -using Content.Shared.Whitelist; +using Content.Shared.Objectives.Components; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Utility; namespace Content.Shared.Ninja.Components; /// /// Component for toggling glove powers. -/// Powers being enabled is controlled by User not being null. /// +/// +/// Requires ItemToggleComponent. +/// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [Access(typeof(SharedNinjaGlovesSystem))] public sealed partial class NinjaGlovesComponent : Component @@ -22,24 +19,33 @@ public sealed partial class NinjaGlovesComponent : Component /// /// Entity of the ninja using these gloves, usually means enabled /// - [DataField("user"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? User; /// - /// The action id for toggling ninja gloves abilities + /// Abilities to give to the user when enabled. /// - [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ToggleAction = "ActionToggleNinjaGloves"; + [DataField(required: true)] + public List Abilities = new(); +} - [DataField, AutoNetworkedField] - public EntityUid? ToggleActionEntity; +/// +/// An ability that adds components to the user when the gloves are enabled. +/// +[DataRecord] +public record struct NinjaGloveAbility() +{ + /// + /// If not null, checks if an objective with this prototype has been completed. + /// If it has, the ability components are skipped to prevent doing the objective twice. + /// The objective must have CodeConditionComponent to be checked. + /// + [DataField] + public EntProtoId? Objective; /// - /// The whitelist used for the emag provider to emag airlocks only (not regular doors). + /// Components to add and remove. /// - [DataField("doorjackWhitelist")] - public EntityWhitelist DoorjackWhitelist = new() - { - Components = new[] {"Airlock"} - }; + [DataField(required: true)] + public ComponentRegistry Components = new(); } diff --git a/Content.Shared/Ninja/Components/NinjaSuitComponent.cs b/Content.Shared/Ninja/Components/NinjaSuitComponent.cs index 7e7b1ffcd30..8b477b2aa5f 100644 --- a/Content.Shared/Ninja/Components/NinjaSuitComponent.cs +++ b/Content.Shared/Ninja/Components/NinjaSuitComponent.cs @@ -3,9 +3,6 @@ using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Utility; namespace Content.Shared.Ninja.Components; @@ -14,68 +11,27 @@ namespace Content.Shared.Ninja.Components; /// Component for ninja suit abilities and power consumption. /// As an implementation detail, dashing with katana is a suit action which isn't ideal. /// -[RegisterComponent, NetworkedComponent, Access(typeof(SharedNinjaSuitSystem)), AutoGenerateComponentState] -[AutoGenerateComponentPause] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedNinjaSuitSystem))] public sealed partial class NinjaSuitComponent : Component { - /// - /// Battery charge used passively, in watts. Will last 1000 seconds on a small-capacity power cell. - /// - [DataField("passiveWattage")] - public float PassiveWattage = 0.36f; - - /// - /// Battery charge used while cloaked, stacks with passive. Will last 200 seconds while cloaked on a small-capacity power cell. - /// - [DataField("cloakWattage")] - public float CloakWattage = 1.44f; - /// /// Sound played when a ninja is hit while cloaked. /// - [DataField("revealSound")] + [DataField] public SoundSpecifier RevealSound = new SoundPathSpecifier("/Audio/Effects/chime.ogg"); /// - /// How long to disable all abilities when revealed. - /// Normally, ninjas are revealed when attacking or getting damaged. - /// - [DataField("disableTime")] - public TimeSpan DisableTime = TimeSpan.FromSeconds(5); - - /// - /// Time at which we will be able to use our abilities again + /// ID of the use delay to disable all ninja abilities. /// - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] - [AutoPausedField] - public TimeSpan DisableCooldown; - - /// - /// The action id for creating throwing stars. - /// - [DataField("createThrowingStarAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string CreateThrowingStarAction = "ActionCreateThrowingStar"; - - [DataField, AutoNetworkedField] - public EntityUid? CreateThrowingStarActionEntity; - - /// - /// Battery charge used to create a throwing star. Can do it 25 times on a small-capacity power cell. - /// - [DataField("throwingStarCharge")] - public float ThrowingStarCharge = 14.4f; - - /// - /// Throwing star item to create with the action - /// - [DataField("throwingStarPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ThrowingStarPrototype = "ThrowingStarNinja"; + [DataField] + public string DisableDelayId = "suit_powers"; /// /// The action id for recalling a bound energy katana /// - [DataField("recallKatanaAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string RecallKatanaAction = "ActionRecallKatana"; + [DataField] + public EntProtoId RecallKatanaAction = "ActionRecallKatana"; [DataField, AutoNetworkedField] public EntityUid? RecallKatanaActionEntity; @@ -84,14 +40,14 @@ public sealed partial class NinjaSuitComponent : Component /// Battery charge used per tile the katana teleported. /// Uses 1% of a default battery per tile. /// - [DataField("recallCharge")] + [DataField] public float RecallCharge = 3.6f; /// /// The action id for creating an EMP burst /// - [DataField("empAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string EmpAction = "ActionNinjaEmp"; + [DataField] + public EntProtoId EmpAction = "ActionNinjaEmp"; [DataField, AutoNetworkedField] public EntityUid? EmpActionEntity; @@ -99,36 +55,29 @@ public sealed partial class NinjaSuitComponent : Component /// /// Battery charge used to create an EMP burst. Can do it 2 times on a small-capacity power cell. /// - [DataField("empCharge")] + [DataField] public float EmpCharge = 180f; + // TODO: EmpOnTrigger bruh /// /// Range of the EMP in tiles. /// - [DataField("empRange")] + [DataField] public float EmpRange = 6f; /// /// Power consumed from batteries by the EMP /// - [DataField("empConsumption")] + [DataField] public float EmpConsumption = 100000f; /// /// How long the EMP effects last for, in seconds /// - [DataField("empDuration")] + [DataField] public float EmpDuration = 60f; } -public sealed partial class CreateThrowingStarEvent : InstantActionEvent -{ -} - -public sealed partial class RecallKatanaEvent : InstantActionEvent -{ -} +public sealed partial class RecallKatanaEvent : InstantActionEvent; -public sealed partial class NinjaEmpEvent : InstantActionEvent -{ -} +public sealed partial class NinjaEmpEvent : InstantActionEvent; diff --git a/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs b/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs index 91c816df5c9..a19537be1c8 100644 --- a/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs +++ b/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs @@ -7,34 +7,28 @@ namespace Content.Shared.Ninja.Components; /// /// Component placed on a mob to make it a space ninja, able to use suit and glove powers. -/// Contains ids of all ninja equipment and the game rule. +/// Contains ids of all ninja equipment. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [Access(typeof(SharedSpaceNinjaSystem))] public sealed partial class SpaceNinjaComponent : Component { - /// - /// The ninja game rule that spawned this ninja. - /// - [DataField("rule")] - public EntityUid? Rule; - /// /// Currently worn suit /// - [DataField("suit"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? Suit; /// - /// Currently worn gloves + /// Currently worn gloves, if enabled. /// - [DataField("gloves"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? Gloves; /// /// Bound katana, set once picked up and never removed /// - [DataField("katana"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? Katana; /// @@ -55,6 +49,9 @@ public sealed partial class SpaceNinjaComponent : Component [DataField] public EntProtoId SpiderChargeObjective = "SpiderChargeObjective"; + /// + /// Alert to show for suit power. + /// [DataField] public ProtoId SuitPowerAlert = "SuitPower"; } diff --git a/Content.Shared/Ninja/Components/SpiderChargeComponent.cs b/Content.Shared/Ninja/Components/SpiderChargeComponent.cs index dacf47bb235..3ba4494cca4 100644 --- a/Content.Shared/Ninja/Components/SpiderChargeComponent.cs +++ b/Content.Shared/Ninja/Components/SpiderChargeComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Ninja.Systems; using Robust.Shared.GameStates; namespace Content.Shared.Ninja.Components; @@ -6,14 +7,14 @@ namespace Content.Shared.Ninja.Components; /// Component for the Space Ninja's unique Spider Charge. /// Only this component detonating can trigger the ninja's objective. /// -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedSpiderChargeSystem))] public sealed partial class SpiderChargeComponent : Component { /// Range for planting within the target area - [DataField("range")] + [DataField] public float Range = 10f; /// The ninja that planted this charge - [DataField("planter")] - public EntityUid? Planter = null; + [DataField] + public EntityUid? Planter; } diff --git a/Content.Shared/Ninja/Components/StunProviderComponent.cs b/Content.Shared/Ninja/Components/StunProviderComponent.cs index 37a27074a49..2da094291d7 100644 --- a/Content.Shared/Ninja/Components/StunProviderComponent.cs +++ b/Content.Shared/Ninja/Components/StunProviderComponent.cs @@ -3,7 +3,6 @@ using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Ninja.Components; @@ -11,32 +10,33 @@ namespace Content.Shared.Ninja.Components; /// Component for stunning mobs on click outside of harm mode. /// Knocks them down for a bit and deals shock damage. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedStunProviderSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedStunProviderSystem))] public sealed partial class StunProviderComponent : Component { /// /// The powercell entity to take power from. /// Determines whether stunning is possible. /// - [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? BatteryUid; /// /// Sound played when stunning someone. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public SoundSpecifier Sound = new SoundCollectionSpecifier("sparks"); /// /// Joules required in the battery to stun someone. Defaults to 10 uses on a small battery. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float StunCharge = 36f; /// /// Damage dealt when stunning someone /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public DamageSpecifier StunDamage = new() { DamageDict = new() @@ -48,34 +48,30 @@ public sealed partial class StunProviderComponent : Component /// /// Time that someone is stunned for, stacks if done multiple times. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan StunTime = TimeSpan.FromSeconds(5); /// /// How long stunning is disabled after stunning something. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan Cooldown = TimeSpan.FromSeconds(2); /// - /// Locale string to popup when there is no power + /// ID of the cooldown use delay. /// - [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] - public string NoPowerPopup = string.Empty; + [DataField] + public string DelayId = "stun_cooldown"; /// - /// Whitelist for what counts as a mob. + /// Locale string to popup when there is no power /// - [DataField] - public EntityWhitelist Whitelist = new() - { - Components = new[] {"Stamina"} - }; + [DataField(required: true)] + public LocId NoPowerPopup = string.Empty; /// - /// When someone can next be stunned. - /// Essentially a UseDelay unique to this component. + /// Whitelist for what counts as a mob. /// - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] - public TimeSpan NextStun = TimeSpan.Zero; + [DataField(required: true)] + public EntityWhitelist Whitelist = new(); } diff --git a/Content.Shared/Ninja/Systems/DashAbilitySystem.cs b/Content.Shared/Ninja/Systems/DashAbilitySystem.cs index 4853968b61f..1385219e473 100644 --- a/Content.Shared/Ninja/Systems/DashAbilitySystem.cs +++ b/Content.Shared/Ninja/Systems/DashAbilitySystem.cs @@ -16,6 +16,7 @@ namespace Content.Shared.Ninja.Systems; /// public sealed class DashAbilitySystem : EntitySystem { + [Dependency] private readonly ActionContainerSystem _actionContainer = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedChargesSystem _charges = default!; @@ -23,48 +24,40 @@ public sealed class DashAbilitySystem : EntitySystem [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly ActionContainerSystem _actionContainer = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnGetItemActions); + SubscribeLocalEvent(OnGetActions); SubscribeLocalEvent(OnDash); SubscribeLocalEvent(OnMapInit); } - private void OnMapInit(EntityUid uid, DashAbilityComponent component, MapInitEvent args) + private void OnMapInit(Entity ent, ref MapInitEvent args) { - _actionContainer.EnsureAction(uid, ref component.DashActionEntity, component.DashAction); - Dirty(uid, component); + var (uid, comp) = ent; + _actionContainer.EnsureAction(uid, ref comp.DashActionEntity, comp.DashAction); + Dirty(uid, comp); } - private void OnGetItemActions(EntityUid uid, DashAbilityComponent comp, GetItemActionsEvent args) + private void OnGetActions(Entity ent, ref GetItemActionsEvent args) { - var ev = new AddDashActionEvent(args.User); - RaiseLocalEvent(uid, ev); - - if (ev.Cancelled) - return; - - args.AddAction(ref comp.DashActionEntity, comp.DashAction); + if (CheckDash(ent, args.User)) + args.AddAction(ent.Comp.DashActionEntity); } /// /// Handle charges and teleport to a visible location. /// - private void OnDash(EntityUid uid, DashAbilityComponent comp, DashEvent args) + private void OnDash(Entity ent, ref DashEvent args) { if (!_timing.IsFirstTimePredicted) return; + var (uid, comp) = ent; var user = args.Performer; - args.Handled = true; - - var ev = new DashAttemptEvent(user); - RaiseLocalEvent(uid, ev); - if (ev.Cancelled) + if (!CheckDash(uid, user)) return; if (!_hands.IsHolding(user, uid, out var _)) @@ -73,15 +66,8 @@ private void OnDash(EntityUid uid, DashAbilityComponent comp, DashEvent args) return; } - TryComp(uid, out var charges); - if (_charges.IsEmpty(uid, charges)) - { - _popup.PopupClient(Loc.GetString("dash-ability-no-charges", ("item", uid)), user, user); - return; - } var origin = _transform.GetMapCoordinates(user); var target = args.Target.ToMap(EntityManager, _transform); - // prevent collision with the user duh if (!_examine.InRangeUnOccluded(origin, target, SharedInteractionSystem.MaxRaycastRange, null)) { // can only dash if the destination is visible on screen @@ -89,36 +75,28 @@ private void OnDash(EntityUid uid, DashAbilityComponent comp, DashEvent args) return; } - _transform.SetCoordinates(user, args.Target); - _transform.AttachToGridOrMap(user); - _audio.PlayPredicted(comp.BlinkSound, user, user); - if (charges != null) - _charges.UseCharge(uid, charges); - } -} + if (!_charges.TryUseCharge(uid)) + { + _popup.PopupClient(Loc.GetString("dash-ability-no-charges", ("item", uid)), user, user); + return; + } -/// -/// Raised on the item before adding the dash action -/// -public sealed class AddDashActionEvent : CancellableEntityEventArgs -{ - public EntityUid User; + var xform = Transform(user); + _transform.SetCoordinates(user, xform, args.Target); + _transform.AttachToGridOrMap(user, xform); + args.Handled = true; + } - public AddDashActionEvent(EntityUid user) + public bool CheckDash(EntityUid uid, EntityUid user) { - User = user; + var ev = new CheckDashEvent(user); + RaiseLocalEvent(uid, ref ev); + return !ev.Cancelled; } } /// -/// Raised on the item before dashing is done. +/// Raised on the item before adding the dash action and when using the action. /// -public sealed class DashAttemptEvent : CancellableEntityEventArgs -{ - public EntityUid User; - - public DashAttemptEvent(EntityUid user) - { - User = user; - } -} +[ByRefEvent] +public record struct CheckDashEvent(EntityUid User, bool Cancelled = false); diff --git a/Content.Shared/Ninja/Systems/EmagProviderSystem.cs b/Content.Shared/Ninja/Systems/EmagProviderSystem.cs index df9cf8ac825..ae0bacaf5f6 100644 --- a/Content.Shared/Ninja/Systems/EmagProviderSystem.cs +++ b/Content.Shared/Ninja/Systems/EmagProviderSystem.cs @@ -1,6 +1,6 @@ using Content.Shared.Administration.Logs; -using Content.Shared.Emag.Systems; using Content.Shared.Database; +using Content.Shared.Emag.Systems; using Content.Shared.Interaction; using Content.Shared.Ninja.Components; using Content.Shared.Tag; @@ -14,9 +14,10 @@ namespace Content.Shared.Ninja.Systems; public sealed class EmagProviderSystem : EntitySystem { [Dependency] private readonly EmagSystem _emag = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; - [Dependency] private readonly TagSystem _tags = default!; + [Dependency] private readonly TagSystem _tag = default!; public override void Initialize() { @@ -28,18 +29,20 @@ public override void Initialize() /// /// Emag clicked entities that are on the whitelist. /// - private void OnBeforeInteractHand(EntityUid uid, EmagProviderComponent comp, BeforeInteractHandEvent args) + private void OnBeforeInteractHand(Entity ent, ref BeforeInteractHandEvent args) { // TODO: change this into a generic check event thing - if (args.Handled || !_gloves.AbilityCheck(uid, args, out var target)) + if (args.Handled || !_gloves.AbilityCheck(ent, args, out var target)) return; + var (uid, comp) = ent; + // only allowed to emag entities on the whitelist - if (comp.Whitelist != null && !comp.Whitelist.IsValid(target, EntityManager)) + if (_whitelist.IsWhitelistFail(comp.Whitelist, target)) return; // only allowed to emag non-immune entities - if (_tags.HasTag(target, comp.EmagImmuneTag)) + if (_tag.HasTag(target, comp.EmagImmuneTag)) return; var handled = _emag.DoEmagEffect(uid, target); @@ -51,18 +54,6 @@ private void OnBeforeInteractHand(EntityUid uid, EmagProviderComponent comp, Bef RaiseLocalEvent(uid, ref ev); args.Handled = true; } - - /// - /// Set the whitelist for emagging something outside of yaml. - /// - public void SetWhitelist(EntityUid uid, EntityWhitelist? whitelist, EmagProviderComponent? comp = null) - { - if (!Resolve(uid, ref comp)) - return; - - comp.Whitelist = whitelist; - Dirty(uid, comp); - } } /// diff --git a/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs b/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs index d427ffa39b4..281b97a648a 100644 --- a/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs +++ b/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs @@ -15,33 +15,20 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnEquipped); - SubscribeLocalEvent(OnAddDashAction); - SubscribeLocalEvent(OnDashAttempt); + SubscribeLocalEvent(OnCheckDash); } /// /// When equipped by a ninja, try to bind it. /// - private void OnEquipped(EntityUid uid, EnergyKatanaComponent comp, GotEquippedEvent args) + private void OnEquipped(Entity ent, ref GotEquippedEvent args) { - // check if user isnt a ninja or already has a katana bound - var user = args.Equipee; - if (!TryComp(user, out var ninja) || ninja.Katana != null) - return; - - // bind it since its unbound - _ninja.BindKatana(user, uid, ninja); - } - - private void OnAddDashAction(EntityUid uid, EnergyKatanaComponent comp, AddDashActionEvent args) - { - if (!HasComp(args.User)) - args.Cancel(); + _ninja.BindKatana(args.Equipee, ent); } - private void OnDashAttempt(EntityUid uid, EnergyKatanaComponent comp, DashAttemptEvent args) + private void OnCheckDash(Entity ent, ref CheckDashEvent args) { - if (!TryComp(args.User, out var ninja) || ninja.Katana != uid) - args.Cancel(); + if (!_ninja.IsNinja(args.User)) + args.Cancelled = true; } } diff --git a/Content.Shared/Ninja/Systems/ItemCreatorSystem.cs b/Content.Shared/Ninja/Systems/ItemCreatorSystem.cs new file mode 100644 index 00000000000..56112e9a697 --- /dev/null +++ b/Content.Shared/Ninja/Systems/ItemCreatorSystem.cs @@ -0,0 +1,56 @@ +using Content.Shared.Actions; +using Content.Shared.Ninja.Components; + +namespace Content.Shared.Ninja.Systems; + +/// +/// Handles predicting that the action exists, creating items is done serverside. +/// +public abstract class SharedItemCreatorSystem : EntitySystem +{ + [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnGetActions); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + var (uid, comp) = ent; + // test funny dont mind me + if (string.IsNullOrEmpty(comp.Action)) + return; + + _actionContainer.EnsureAction(uid, ref comp.ActionEntity, comp.Action); + Dirty(uid, comp); + } + + private void OnGetActions(Entity ent, ref GetItemActionsEvent args) + { + if (CheckItemCreator(ent, args.User)) + args.AddAction(ent.Comp.ActionEntity); + } + + public bool CheckItemCreator(EntityUid uid, EntityUid user) + { + var ev = new CheckItemCreatorEvent(user); + RaiseLocalEvent(uid, ref ev); + return !ev.Cancelled; + } +} + +/// +/// Raised on the item creator before adding the action. +/// +[ByRefEvent] +public record struct CheckItemCreatorEvent(EntityUid User, bool Cancelled = false); + +/// +/// Raised on the item creator before creating an item. +/// +[ByRefEvent] +public record struct CreateItemAttemptEvent(EntityUid User, bool Cancelled = false); diff --git a/Content.Shared/Ninja/Systems/SharedBatteryDrainerSystem.cs b/Content.Shared/Ninja/Systems/SharedBatteryDrainerSystem.cs index ac11063eb71..0abcca7d1bd 100644 --- a/Content.Shared/Ninja/Systems/SharedBatteryDrainerSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedBatteryDrainerSystem.cs @@ -18,34 +18,32 @@ public override void Initialize() } /// - /// Cancel any drain doafters if the battery is removed or gets filled. + /// Cancel any drain doafters if the battery is removed or, on the server, gets filled. /// - protected virtual void OnDoAfterAttempt(EntityUid uid, BatteryDrainerComponent comp, DoAfterAttemptEvent args) + protected virtual void OnDoAfterAttempt(Entity ent, ref DoAfterAttemptEvent args) { - if (comp.BatteryUid == null) - { + if (ent.Comp.BatteryUid == null) args.Cancel(); - } } /// /// Drain power from a power source (on server) and repeat if it succeeded. /// Client will predict always succeeding since power is serverside. /// - private void OnDoAfter(EntityUid uid, BatteryDrainerComponent comp, DrainDoAfterEvent args) + private void OnDoAfter(Entity ent, ref DrainDoAfterEvent args) { - if (args.Cancelled || args.Handled || args.Target == null) + if (args.Cancelled || args.Handled || args.Target is not {} target) return; // repeat if there is still power to drain - args.Repeat = TryDrainPower(uid, comp, args.Target.Value); + args.Repeat = TryDrainPower(ent, target); } /// /// Attempt to drain as much power as possible into the powercell. /// Client always predicts this as succeeding since power is serverside and it can only fail once, when the powercell is filled or the target is emptied. /// - protected virtual bool TryDrainPower(EntityUid uid, BatteryDrainerComponent comp, EntityUid target) + protected virtual bool TryDrainPower(Entity ent, EntityUid target) { return true; } @@ -53,12 +51,13 @@ protected virtual bool TryDrainPower(EntityUid uid, BatteryDrainerComponent comp /// /// Sets the battery field on the drainer. /// - public void SetBattery(EntityUid uid, EntityUid? battery, BatteryDrainerComponent? comp = null) + public void SetBattery(Entity ent, EntityUid? battery) { - if (!Resolve(uid, ref comp)) + if (!Resolve(ent, ref ent.Comp) || ent.Comp.BatteryUid == battery) return; - comp.BatteryUid = battery; + ent.Comp.BatteryUid = battery; + Dirty(ent, ent.Comp); } } @@ -66,4 +65,4 @@ public void SetBattery(EntityUid uid, EntityUid? battery, BatteryDrainerComponen /// DoAfter event for . /// [Serializable, NetSerializable] -public sealed partial class DrainDoAfterEvent : SimpleDoAfterEvent { } +public sealed partial class DrainDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs b/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs index f61d0c6a908..8b892190b7b 100644 --- a/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs @@ -1,15 +1,13 @@ -using Content.Shared.Actions; +using Content.Shared.Clothing.Components; using Content.Shared.CombatMode; -using Content.Shared.Communications; -using Content.Shared.CriminalRecords.Components; using Content.Shared.Examine; using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Inventory.Events; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Ninja.Components; using Content.Shared.Popups; -using Content.Shared.Research.Components; -using Content.Shared.Toggleable; using Robust.Shared.Timing; namespace Content.Shared.Ninja.Systems; @@ -20,85 +18,105 @@ namespace Content.Shared.Ninja.Systems; public abstract class SharedNinjaGlovesSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] private readonly SharedCombatModeSystem _combatMode = default!; - [Dependency] protected readonly SharedInteractionSystem Interaction = default!; - [Dependency] protected readonly SharedPopupSystem Popup = default!; - [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnGetItemActions); + SubscribeLocalEvent(OnToggleCheck); + SubscribeLocalEvent(OnActivateAttempt); + SubscribeLocalEvent(OnToggled); SubscribeLocalEvent(OnExamined); - SubscribeLocalEvent(OnUnequipped); - SubscribeLocalEvent(OnMapInit); - } - - private void OnMapInit(EntityUid uid, NinjaGlovesComponent component, MapInitEvent args) - { - _actionContainer.EnsureAction(uid, ref component.ToggleActionEntity, component.ToggleAction); - Dirty(uid, component); } /// /// Disable glove abilities and show the popup if they were enabled previously. /// - public void DisableGloves(EntityUid uid, NinjaGlovesComponent? comp = null) + private void DisableGloves(Entity ent) { + var (uid, comp) = ent; + // already disabled? - if (!Resolve(uid, ref comp) || comp.User == null) + if (comp.User is not {} user) return; - var user = comp.User.Value; comp.User = null; Dirty(uid, comp); - Appearance.SetData(uid, ToggleVisuals.Toggled, false); - Popup.PopupClient(Loc.GetString("ninja-gloves-off"), user, user); - - RemComp(user); - RemComp(user); - RemComp(user); - RemComp(user); - RemComp(user); - RemComp(user); + foreach (var ability in comp.Abilities) + { + EntityManager.RemoveComponents(user, ability.Components); + } } /// - /// Adds the toggle action when equipped. + /// Adds the toggle action when equipped by a ninja only. /// - private void OnGetItemActions(EntityUid uid, NinjaGlovesComponent comp, GetItemActionsEvent args) + private void OnToggleCheck(Entity ent, ref ToggleClothingCheckEvent args) { - if (HasComp(args.User)) - args.AddAction(ref comp.ToggleActionEntity, comp.ToggleAction); + if (!_ninja.IsNinja(args.User)) + args.Cancelled = true; } /// /// Show if the gloves are enabled when examining. /// - private void OnExamined(EntityUid uid, NinjaGlovesComponent comp, ExaminedEvent args) + private void OnExamined(Entity ent, ref ExaminedEvent args) { if (!args.IsInDetailsRange) return; - args.PushText(Loc.GetString(comp.User != null ? "ninja-gloves-examine-on" : "ninja-gloves-examine-off")); + var on = _toggle.IsActivated(ent.Owner) ? "on" : "off"; + args.PushText(Loc.GetString($"ninja-gloves-examine-{on}")); } - /// - /// Disable gloves when unequipped and clean up ninja's gloves reference - /// - private void OnUnequipped(EntityUid uid, NinjaGlovesComponent comp, GotUnequippedEvent args) + private void OnActivateAttempt(Entity ent, ref ItemToggleActivateAttemptEvent args) { - if (comp.User != null) + if (args.User is not {} user + || !_ninja.NinjaQuery.TryComp(user, out var ninja) + // need to wear suit to enable gloves + || !HasComp(ninja.Suit)) { - var user = comp.User.Value; - Popup.PopupClient(Loc.GetString("ninja-gloves-off"), user, user); - DisableGloves(uid, comp); + args.Cancelled = true; + args.Popup = Loc.GetString("ninja-gloves-not-wearing-suit"); + return; } } + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + if ((args.User ?? ent.Comp.User) is not {} user) + return; + + var message = Loc.GetString(args.Activated ? "ninja-gloves-on" : "ninja-gloves-off"); + _popup.PopupClient(message, user, user); + + if (args.Activated && _ninja.NinjaQuery.TryComp(user, out var ninja)) + EnableGloves(ent, (user, ninja)); + else + DisableGloves(ent); + } + + protected virtual void EnableGloves(Entity ent, Entity user) + { + var (uid, comp) = ent; + comp.User = user; + Dirty(uid, comp); + _ninja.AssignGloves(user, uid); + + // yeah this is just ComponentToggler but with objective checking + foreach (var ability in comp.Abilities) + { + // can't predict the objective related abilities + if (ability.Objective == null) + EntityManager.AddComponents(user, ability.Components); + } + } // TODO: generic event thing /// @@ -112,6 +130,6 @@ public bool AbilityCheck(EntityUid uid, BeforeInteractHandEvent args, out Entity && !_combatMode.IsInCombatMode(uid) && TryComp(uid, out var hands) && hands.ActiveHandEntity == null - && Interaction.InRangeUnobstructed(uid, target); + && _interaction.InRangeUnobstructed(uid, target); } } diff --git a/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs b/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs index fed41eaed8a..3800d15b267 100644 --- a/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs @@ -1,11 +1,14 @@ using Content.Shared.Actions; +using Content.Shared.Clothing; using Content.Shared.Clothing.Components; using Content.Shared.Clothing.EntitySystems; using Content.Shared.Inventory.Events; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Ninja.Components; using Content.Shared.Popups; +using Content.Shared.Timing; using Robust.Shared.Audio.Systems; -using Robust.Shared.Timing; namespace Content.Shared.Ninja.Systems; @@ -14,137 +17,158 @@ namespace Content.Shared.Ninja.Systems; /// public abstract class SharedNinjaSuitSystem : EntitySystem { - [Dependency] protected readonly IGameTiming GameTiming = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; - [Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; - [Dependency] protected readonly StealthClothingSystem StealthClothing = default!; + [Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!; + [Dependency] private readonly UseDelaySystem _useDelay = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); - - SubscribeLocalEvent(OnEquipped); + SubscribeLocalEvent(OnEquipped); SubscribeLocalEvent(OnGetItemActions); - SubscribeLocalEvent(OnAddStealthAction); + SubscribeLocalEvent(OnCloakCheck); + SubscribeLocalEvent(OnStarCheck); + SubscribeLocalEvent(OnCreateStarAttempt); + SubscribeLocalEvent(OnActivateAttempt); SubscribeLocalEvent(OnUnequipped); } - private void OnMapInit(EntityUid uid, NinjaSuitComponent component, MapInitEvent args) + private void OnEquipped(Entity ent, ref ClothingGotEquippedEvent args) { - _actionContainer.EnsureAction(uid, ref component.RecallKatanaActionEntity, component.RecallKatanaAction); - _actionContainer.EnsureAction(uid, ref component.CreateThrowingStarActionEntity, component.CreateThrowingStarAction); - _actionContainer.EnsureAction(uid, ref component.EmpActionEntity, component.EmpAction); - Dirty(uid, component); + var user = args.Wearer; + if (_ninja.NinjaQuery.TryComp(user, out var ninja)) + NinjaEquipped(ent, (user, ninja)); } - /// - /// Call the shared and serverside code for when a ninja equips the suit. - /// - private void OnEquipped(EntityUid uid, NinjaSuitComponent comp, GotEquippedEvent args) + protected virtual void NinjaEquipped(Entity ent, Entity user) { - var user = args.Equipee; - if (!TryComp(user, out var ninja)) - return; + // mark the user as wearing this suit, used when being attacked among other things + _ninja.AssignSuit(user, ent); + } - NinjaEquippedSuit(uid, comp, user, ninja); + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + var (uid, comp) = ent; + _actionContainer.EnsureAction(uid, ref comp.RecallKatanaActionEntity, comp.RecallKatanaAction); + _actionContainer.EnsureAction(uid, ref comp.EmpActionEntity, comp.EmpAction); + Dirty(uid, comp); } /// /// Add all the actions when a suit is equipped by a ninja. /// - private void OnGetItemActions(EntityUid uid, NinjaSuitComponent comp, GetItemActionsEvent args) + private void OnGetItemActions(Entity ent, ref GetItemActionsEvent args) { - if (!HasComp(args.User)) + if (!_ninja.IsNinja(args.User)) return; + var comp = ent.Comp; args.AddAction(ref comp.RecallKatanaActionEntity, comp.RecallKatanaAction); - args.AddAction(ref comp.CreateThrowingStarActionEntity, comp.CreateThrowingStarAction); args.AddAction(ref comp.EmpActionEntity, comp.EmpAction); } /// - /// Only add stealth clothing's toggle action when equipped by a ninja. + /// Only add toggle cloak action when equipped by a ninja. /// - private void OnAddStealthAction(EntityUid uid, NinjaSuitComponent comp, AddStealthActionEvent args) + private void OnCloakCheck(Entity ent, ref ToggleClothingCheckEvent args) { - if (!HasComp(args.User)) - args.Cancel(); + if (!_ninja.IsNinja(args.User)) + args.Cancelled = true; } - /// - /// Call the shared and serverside code for when anyone unequips a suit. - /// - private void OnUnequipped(EntityUid uid, NinjaSuitComponent comp, GotUnequippedEvent args) + private void OnStarCheck(Entity ent, ref CheckItemCreatorEvent args) + { + if (!_ninja.IsNinja(args.User)) + args.Cancelled = true; + } + + private void OnCreateStarAttempt(Entity ent, ref CreateItemAttemptEvent args) { - UserUnequippedSuit(uid, comp, args.Equipee); + if (CheckDisabled(ent, args.User)) + args.Cancelled = true; } /// - /// Called when a suit is equipped by a space ninja. - /// In the future it might be changed to an explicit activation toggle/verb like gloves are. + /// Call the shared and serverside code for when anyone unequips a suit. /// - protected virtual void NinjaEquippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user, SpaceNinjaComponent ninja) + private void OnUnequipped(Entity ent, ref GotUnequippedEvent args) { - // mark the user as wearing this suit, used when being attacked among other things - _ninja.AssignSuit(user, uid, ninja); - - // initialize phase cloak, but keep it off - StealthClothing.SetEnabled(uid, user, false); + var user = args.Equipee; + if (_ninja.NinjaQuery.TryComp(user, out var ninja)) + UserUnequippedSuit(ent, (user, ninja)); } /// /// Force uncloaks the user and disables suit abilities. /// - public void RevealNinja(EntityUid uid, EntityUid user, bool disable = true, NinjaSuitComponent? comp = null, StealthClothingComponent? stealthClothing = null) + public void RevealNinja(Entity ent, EntityUid user, bool disable = true) { - if (!Resolve(uid, ref comp, ref stealthClothing)) + if (!Resolve(ent, ref ent.Comp)) return; - if (!StealthClothing.SetEnabled(uid, user, false, stealthClothing)) - return; - - if (!disable) + var uid = ent.Owner; + var comp = ent.Comp; + if (_toggle.TryDeactivate(uid, user) || !disable) return; // previously cloaked, disable abilities for a short time _audio.PlayPredicted(comp.RevealSound, uid, user); Popup.PopupClient(Loc.GetString("ninja-revealed"), user, user, PopupType.MediumCaution); - comp.DisableCooldown = GameTiming.CurTime + comp.DisableTime; + _useDelay.TryResetDelay(uid, id: comp.DisableDelayId); + } + + private void OnActivateAttempt(Entity ent, ref ItemToggleActivateAttemptEvent args) + { + if (!_ninja.IsNinja(args.User)) + { + args.Cancelled = true; + return; + } + + if (IsDisabled((ent, ent.Comp, null))) + { + args.Cancelled = true; + args.Popup = Loc.GetString("ninja-suit-cooldown"); + } } - // TODO: modify PowerCellDrain /// - /// Returns the power used by a suit + /// Returns true if the suit is currently disabled /// - public float SuitWattage(EntityUid uid, NinjaSuitComponent? suit = null) + public bool IsDisabled(Entity ent) + { + if (!Resolve(ent, ref ent.Comp1, ref ent.Comp2)) + return false; + + return _useDelay.IsDelayed((ent, ent.Comp2), ent.Comp1.DisableDelayId); + } + + protected bool CheckDisabled(Entity ent, EntityUid user) { - if (!Resolve(uid, ref suit)) - return 0f; + if (IsDisabled((ent, ent.Comp, null))) + { + Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium); + return true; + } - float wattage = suit.PassiveWattage; - if (TryComp(uid, out var stealthClothing) && stealthClothing.Enabled) - wattage += suit.CloakWattage; - return wattage; + return false; } /// /// Called when a suit is unequipped, not necessarily by a space ninja. /// In the future it might be changed to also have explicit deactivation via toggle. /// - protected virtual void UserUnequippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user) + protected virtual void UserUnequippedSuit(Entity ent, Entity user) { - if (!TryComp(user, out var ninja)) - return; - // mark the user as not wearing a suit - _ninja.AssignSuit(user, null, ninja); + _ninja.AssignSuit(user, null); // disable glove abilities - if (ninja.Gloves != null && TryComp(ninja.Gloves.Value, out var gloves)) - _gloves.DisableGloves(ninja.Gloves.Value, gloves); + if (user.Comp.Gloves is {} uid) + _toggle.TryDeactivate(uid, user: user); } } diff --git a/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs b/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs index 522f29fe420..d738f2dd8a2 100644 --- a/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Popups; +using System.Diagnostics.CodeAnalysis; namespace Content.Shared.Ninja.Systems; @@ -14,49 +15,59 @@ public abstract class SharedSpaceNinjaSystem : EntitySystem [Dependency] protected readonly SharedNinjaSuitSystem Suit = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; + public EntityQuery NinjaQuery; + public override void Initialize() { base.Initialize(); + NinjaQuery = GetEntityQuery(); + SubscribeLocalEvent(OnNinjaAttacked); SubscribeLocalEvent(OnNinjaAttack); SubscribeLocalEvent(OnShotAttempted); } + public bool IsNinja([NotNullWhen(true)] EntityUid? uid) + { + return NinjaQuery.HasComp(uid); + } + /// /// Set the ninja's worn suit entity /// - public void AssignSuit(EntityUid uid, EntityUid? suit, SpaceNinjaComponent? comp = null) + public void AssignSuit(Entity ent, EntityUid? suit) { - if (!Resolve(uid, ref comp) || comp.Suit == suit) + if (ent.Comp.Suit == suit) return; - comp.Suit = suit; - Dirty(uid, comp); + ent.Comp.Suit = suit; + Dirty(ent, ent.Comp); } /// /// Set the ninja's worn gloves entity /// - public void AssignGloves(EntityUid uid, EntityUid? gloves, SpaceNinjaComponent? comp = null) + public void AssignGloves(Entity ent, EntityUid? gloves) { - if (!Resolve(uid, ref comp) || comp.Gloves == gloves) + if (ent.Comp.Gloves == gloves) return; - comp.Gloves = gloves; - Dirty(uid, comp); + ent.Comp.Gloves = gloves; + Dirty(ent, ent.Comp); } /// /// Bind a katana entity to a ninja, letting it be recalled and dash. + /// Does nothing if the player is not a ninja or already has a katana bound. /// - public void BindKatana(EntityUid uid, EntityUid? katana, SpaceNinjaComponent? comp = null) + public void BindKatana(Entity ent, EntityUid katana) { - if (!Resolve(uid, ref comp) || comp.Katana == katana) + if (!NinjaQuery.Resolve(ent, ref ent.Comp) || ent.Comp.Katana != null) return; - comp.Katana = katana; - Dirty(uid, comp); + ent.Comp.Katana = katana; + Dirty(ent, ent.Comp); } /// @@ -71,32 +82,32 @@ public virtual bool TryUseCharge(EntityUid user, float charge) /// /// Handle revealing ninja if cloaked when attacked. /// - private void OnNinjaAttacked(EntityUid uid, SpaceNinjaComponent comp, AttackedEvent args) + private void OnNinjaAttacked(Entity ent, ref AttackedEvent args) { - if (comp.Suit != null && TryComp(comp.Suit, out var stealthClothing) && stealthClothing.Enabled) - { - Suit.RevealNinja(comp.Suit.Value, uid, true, null, stealthClothing); - } + TryRevealNinja(ent, disable: true); } /// /// Handle revealing ninja if cloaked when attacking. /// Only reveals, there is no cooldown. /// - private void OnNinjaAttack(EntityUid uid, SpaceNinjaComponent comp, ref MeleeAttackEvent args) + private void OnNinjaAttack(Entity ent, ref MeleeAttackEvent args) + { + TryRevealNinja(ent, disable: false); + } + + private void TryRevealNinja(Entity ent, bool disable) { - if (comp.Suit != null && TryComp(comp.Suit, out var stealthClothing) && stealthClothing.Enabled) - { - Suit.RevealNinja(comp.Suit.Value, uid, false, null, stealthClothing); - } + if (ent.Comp.Suit is {} uid && TryComp(ent.Comp.Suit, out var suit)) + Suit.RevealNinja((uid, suit), ent, disable: disable); } /// /// Require ninja to fight with HONOR, no guns! /// - private void OnShotAttempted(EntityUid uid, SpaceNinjaComponent comp, ref ShotAttemptedEvent args) + private void OnShotAttempted(Entity ent, ref ShotAttemptedEvent args) { - Popup.PopupClient(Loc.GetString("gun-disabled"), uid, uid); + Popup.PopupClient(Loc.GetString("gun-disabled"), ent, ent); args.Cancel(); } } diff --git a/Content.Shared/Ninja/Systems/SharedSpiderChargeSystem.cs b/Content.Shared/Ninja/Systems/SharedSpiderChargeSystem.cs new file mode 100644 index 00000000000..f4b158aced4 --- /dev/null +++ b/Content.Shared/Ninja/Systems/SharedSpiderChargeSystem.cs @@ -0,0 +1,6 @@ +namespace Content.Shared.Ninja.Systems; + +/// +/// Sticking triggering and exploding are all in server so this is just for access. +/// +public abstract class SharedSpiderChargeSystem : EntitySystem; diff --git a/Content.Shared/Ninja/Systems/SharedStunProviderSystem.cs b/Content.Shared/Ninja/Systems/SharedStunProviderSystem.cs index 61b6e4313ed..061c019c9b6 100644 --- a/Content.Shared/Ninja/Systems/SharedStunProviderSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedStunProviderSystem.cs @@ -11,22 +11,12 @@ public abstract class SharedStunProviderSystem : EntitySystem /// /// Set the battery field on the stun provider. /// - public void SetBattery(EntityUid uid, EntityUid? battery, StunProviderComponent? comp = null) + public void SetBattery(Entity ent, EntityUid? battery) { - if (!Resolve(uid, ref comp)) + if (!Resolve(ent, ref ent.Comp) || ent.Comp.BatteryUid == battery) return; - comp.BatteryUid = battery; - } - - /// - /// Set the no power popup field on the stun provider. - /// - public void SetNoPowerPopup(EntityUid uid, string popup, StunProviderComponent? comp = null) - { - if (!Resolve(uid, ref comp)) - return; - - comp.NoPowerPopup = popup; + ent.Comp.BatteryUid = battery; + Dirty(ent, ent.Comp); } } diff --git a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs index 07032a00ce9..8d2c4dcfebe 100644 --- a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs +++ b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs @@ -92,7 +92,7 @@ public bool CanBeAssigned(EntityUid uid, EntityUid mindId, MindComponent mind, O } /// - /// Get the title, description, icon and progress of an objective using . + /// Get the title, description, icon and progress of an objective using . /// If any of them are null it is logged and null is returned. /// /// ID of the condition entity @@ -103,20 +103,43 @@ public bool CanBeAssigned(EntityUid uid, EntityUid mindId, MindComponent mind, O if (!Resolve(mindId, ref mind)) return null; - var ev = new ObjectiveGetProgressEvent(mindId, mind); - RaiseLocalEvent(uid, ref ev); + if (GetProgress(uid, (mindId, mind)) is not {} progress) + return null; var comp = Comp(uid); var meta = MetaData(uid); var title = meta.EntityName; var description = meta.EntityDescription; - if (comp.Icon == null || ev.Progress == null) + if (comp.Icon == null) { - Log.Error($"An objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind)} is missing icon or progress ({ev.Progress})"); + Log.Error($"An objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind)} is missing an icon!"); return null; } - return new ObjectiveInfo(title, description, comp.Icon, ev.Progress.Value); + return new ObjectiveInfo(title, description, comp.Icon, progress); + } + + /// + /// Gets the progress of an objective using . + /// Returning null is a programmer error. + /// + public float? GetProgress(EntityUid uid, Entity mind) + { + var ev = new ObjectiveGetProgressEvent(mind, mind.Comp); + RaiseLocalEvent(uid, ref ev); + if (ev.Progress != null) + return ev.Progress; + + Log.Error($"Objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind.Comp)} didn't set a progress value!"); + return null; + } + + /// + /// Returns true if an objective is completed. + /// + public bool IsCompleted(EntityUid uid, Entity mind) + { + return (GetProgress(uid, mind) ?? 0f) >= 0.999f; } /// diff --git a/Content.Shared/Pinpointer/SharedProximityBeeper.cs b/Content.Shared/Pinpointer/SharedProximityBeeper.cs deleted file mode 100644 index 51631126833..00000000000 --- a/Content.Shared/Pinpointer/SharedProximityBeeper.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Pinpointer; - -[Serializable, NetSerializable] -public enum ProximityBeeperVisuals : byte -{ - Enabled -} diff --git a/Content.Shared/PowerCell/PowerCellDrawComponent.cs b/Content.Shared/PowerCell/PowerCellDrawComponent.cs index 708a86a8eaf..94de7c77878 100644 --- a/Content.Shared/PowerCell/PowerCellDrawComponent.cs +++ b/Content.Shared/PowerCell/PowerCellDrawComponent.cs @@ -6,6 +6,10 @@ namespace Content.Shared.PowerCell; /// /// Indicates that the entity's ActivatableUI requires power or else it closes. /// +/// +/// With ActivatableUI it will activate and deactivate when the ui is opened and closed, drawing power inbetween. +/// Requires to work. +/// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] public sealed partial class PowerCellDrawComponent : Component { @@ -26,10 +30,12 @@ public sealed partial class PowerCellDrawComponent : Component #endregion /// - /// Is this power cell currently drawing power every tick. + /// Whether drawing is enabled, regardless of ItemToggle. + /// Having no cell will still disable it. + /// Only use this if you really don't want it to use power for some time. /// - [ViewVariables(VVAccess.ReadWrite), DataField("enabled")] - public bool Drawing; + [DataField, AutoNetworkedField] + public bool Enabled = true; /// /// How much the entity draws while the UI is open. @@ -51,4 +57,10 @@ public sealed partial class PowerCellDrawComponent : Component [DataField("nextUpdate", customTypeSerializer: typeof(TimeOffsetSerializer))] [AutoPausedField] public TimeSpan NextUpdateTime; + + /// + /// How long to wait between power drawing. + /// + [DataField] + public TimeSpan Delay = TimeSpan.FromSeconds(1); } diff --git a/Content.Shared/PowerCell/SharedPowerCellSystem.cs b/Content.Shared/PowerCell/SharedPowerCellSystem.cs index 508bfc85f08..2b2a836633c 100644 --- a/Content.Shared/PowerCell/SharedPowerCellSystem.cs +++ b/Content.Shared/PowerCell/SharedPowerCellSystem.cs @@ -1,4 +1,6 @@ using Content.Shared.Containers.ItemSlots; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.PowerCell.Components; using Content.Shared.Rejuvenate; using Robust.Shared.Containers; @@ -11,14 +13,19 @@ public abstract class SharedPowerCellSystem : EntitySystem [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] protected readonly ItemToggleSystem Toggle = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnRejuvenate); SubscribeLocalEvent(OnCellInserted); SubscribeLocalEvent(OnCellRemoved); SubscribeLocalEvent(OnCellInsertAttempt); + + SubscribeLocalEvent(OnActivateAttempt); + SubscribeLocalEvent(OnToggled); } private void OnRejuvenate(EntityUid uid, PowerCellSlotComponent component, RejuvenateEvent args) @@ -63,13 +70,25 @@ protected virtual void OnCellRemoved(EntityUid uid, PowerCellSlotComponent compo RaiseLocalEvent(uid, new PowerCellChangedEvent(true), false); } - public void SetPowerCellDrawEnabled(EntityUid uid, bool enabled, PowerCellDrawComponent? component = null) + private void OnActivateAttempt(Entity ent, ref ItemToggleActivateAttemptEvent args) + { + if (!HasDrawCharge(ent, ent.Comp, user: args.User) + || !HasActivatableCharge(ent, ent.Comp, user: args.User)) + args.Cancelled = true; + } + + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + ent.Comp.NextUpdateTime = Timing.CurTime; + } + + public void SetDrawEnabled(Entity ent, bool enabled) { - if (!Resolve(uid, ref component, false) || enabled == component.Drawing) + if (!Resolve(ent, ref ent.Comp, false) || ent.Comp.Enabled == enabled) return; - component.Drawing = enabled; - component.NextUpdateTime = Timing.CurTime; + ent.Comp.Enabled = enabled; + Dirty(ent, ent.Comp); } /// diff --git a/Content.Shared/ProximityDetection/Components/ProximityDetectorComponent.cs b/Content.Shared/ProximityDetection/Components/ProximityDetectorComponent.cs index 09cb7f06d5d..7e2bb4dfe62 100644 --- a/Content.Shared/ProximityDetection/Components/ProximityDetectorComponent.cs +++ b/Content.Shared/ProximityDetection/Components/ProximityDetectorComponent.cs @@ -10,12 +10,6 @@ namespace Content.Shared.ProximityDetection.Components; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState ,Access(typeof(ProximityDetectionSystem))] public sealed partial class ProximityDetectorComponent : Component { - /// - /// Whether or not it's on. - /// - [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] - public bool Enabled = true; - /// /// The criteria used to filter entities /// Note: RequireAll is only supported for tags, all components are required to count as a match! @@ -35,13 +29,13 @@ public sealed partial class ProximityDetectorComponent : Component [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public FixedPoint2 Distance = -1; - /// /// The farthest distance to search for targets /// [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public FixedPoint2 Range = 10f; + // TODO: use timespans not this public float AccumulatedFrameTime; [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] diff --git a/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs b/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs index db25e8bc511..df302f94771 100644 --- a/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs +++ b/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs @@ -1,4 +1,6 @@ -using Content.Shared.ProximityDetection.Components; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.ProximityDetection.Components; using Content.Shared.Tag; using Robust.Shared.Network; @@ -9,6 +11,7 @@ namespace Content.Shared.ProximityDetection.Systems; public sealed class ProximityDetectionSystem : EntitySystem { [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly INetManager _net = default!; @@ -17,10 +20,10 @@ public sealed class ProximityDetectionSystem : EntitySystem public override void Initialize() { - SubscribeLocalEvent(OnPaused); - SubscribeLocalEvent(OnUnpaused); - SubscribeLocalEvent(OnCompInit); + base.Initialize(); + SubscribeLocalEvent(OnCompInit); + SubscribeLocalEvent(OnToggled); } private void OnCompInit(EntityUid uid, ProximityDetectorComponent component, ComponentInit args) @@ -30,57 +33,39 @@ private void OnCompInit(EntityUid uid, ProximityDetectorComponent component, Com Log.Debug("DetectorComponent only supports requireAll = false for tags. All components are required for a match!"); } - private void OnPaused(EntityUid owner, ProximityDetectorComponent component, EntityPausedEvent args) - { - SetEnable_Internal(owner,component,false); - } - - private void OnUnpaused(EntityUid owner, ProximityDetectorComponent detector, ref EntityUnpausedEvent args) - { - SetEnable_Internal(owner, detector,true); - } - public void SetEnable(EntityUid owner, bool enabled, ProximityDetectorComponent? detector = null) - { - if (!Resolve(owner, ref detector) || detector.Enabled == enabled) - return; - SetEnable_Internal(owner ,detector, enabled); - } - public override void Update(float frameTime) { if (_net.IsClient) return; + var query = EntityQueryEnumerator(); while (query.MoveNext(out var owner, out var detector)) { - if (!detector.Enabled) + if (!_toggle.IsActivated(owner)) continue; + detector.AccumulatedFrameTime += frameTime; if (detector.AccumulatedFrameTime < detector.UpdateRate) continue; + detector.AccumulatedFrameTime -= detector.UpdateRate; RunUpdate_Internal(owner, detector); } } - public bool GetEnable(EntityUid owner, ProximityDetectorComponent? detector = null) + private void OnToggled(Entity ent, ref ItemToggledEvent args) { - return Resolve(owner, ref detector, false) && detector.Enabled; - } - - private void SetEnable_Internal(EntityUid owner,ProximityDetectorComponent detector, bool enabled) - { - detector.Enabled = enabled; - var noDetectEvent = new ProximityTargetUpdatedEvent(detector, detector.TargetEnt, detector.Distance); - RaiseLocalEvent(owner, ref noDetectEvent); - if (!enabled) + if (args.Activated) { - detector.AccumulatedFrameTime = 0; - RunUpdate_Internal(owner, detector); - Dirty(owner, detector); + RunUpdate_Internal(ent, ent.Comp); return; } - RunUpdate_Internal(owner, detector); + + var noDetectEvent = new ProximityTargetUpdatedEvent(ent.Comp, Target: null, ent.Comp.Distance); + RaiseLocalEvent(ent, ref noDetectEvent); + + ent.Comp.AccumulatedFrameTime = 0; + Dirty(ent, ent.Comp); } public void ForceUpdate(EntityUid owner, ProximityDetectorComponent? detector = null) @@ -90,11 +75,31 @@ public void ForceUpdate(EntityUid owner, ProximityDetectorComponent? detector = RunUpdate_Internal(owner, detector); } + private void ClearTarget(Entity ent) + { + var (uid, comp) = ent; + if (comp.TargetEnt == null) + return; + + comp.Distance = -1; + comp.TargetEnt = null; + var noDetectEvent = new ProximityTargetUpdatedEvent(comp, null, -1); + RaiseLocalEvent(uid, ref noDetectEvent); + var newTargetEvent = new NewProximityTargetEvent(comp, null); + RaiseLocalEvent(uid, ref newTargetEvent); + Dirty(uid, comp); + } private void RunUpdate_Internal(EntityUid owner,ProximityDetectorComponent detector) { if (!_net.IsServer) //only run detection checks on the server! return; + + if (Deleted(detector.TargetEnt)) + { + ClearTarget((owner, detector)); + } + var xformQuery = GetEntityQuery(); var xform = xformQuery.GetComponent(owner); List<(EntityUid TargetEnt, float Distance)> detections = new(); @@ -173,15 +178,7 @@ private void UpdateTargetFromClosest(EntityUid owner, ProximityDetectorComponent { if (detections.Count == 0) { - if (detector.TargetEnt == null) - return; - detector.Distance = -1; - detector.TargetEnt = null; - var noDetectEvent = new ProximityTargetUpdatedEvent(detector, null, -1); - RaiseLocalEvent(owner, ref noDetectEvent); - var newTargetEvent = new NewProximityTargetEvent(detector, null); - RaiseLocalEvent(owner, ref newTargetEvent); - Dirty(owner, detector); + ClearTarget((owner, detector)); return; } var closestDistance = detections[0].Distance; @@ -198,6 +195,7 @@ private void UpdateTargetFromClosest(EntityUid owner, ProximityDetectorComponent var newData = newTarget || detector.Distance != closestDistance; detector.TargetEnt = closestEnt; detector.Distance = closestDistance; + Dirty(owner, detector); if (newTarget) { var newTargetEvent = new NewProximityTargetEvent(detector, closestEnt); diff --git a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs index e1776873da9..de0fe0bce38 100644 --- a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs +++ b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs @@ -15,12 +15,6 @@ namespace Content.Shared.Silicons.Borgs.Components; [RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem)), AutoGenerateComponentState] public sealed partial class BorgChassisComponent : Component { - /// - /// Whether or not the borg is activated, meaning it has access to modules and a heightened movement speed - /// - [DataField("activated"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public bool Activated; - #region Brain /// /// A whitelist for which entities count as valid brains @@ -68,7 +62,7 @@ public sealed partial class BorgChassisComponent : Component /// /// The currently selected module /// - [DataField("selectedModule")] + [DataField("selectedModule"), AutoNetworkedField] public EntityUid? SelectedModule; #region Visuals diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs index 0431d95a42f..896d97bf52c 100644 --- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Access.Components; using Content.Shared.Containers.ItemSlots; +using Content.Shared.Item.ItemToggle; using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; using Content.Shared.Popups; @@ -17,6 +18,7 @@ public abstract partial class SharedBorgSystem : EntitySystem { [Dependency] protected readonly SharedContainerSystem Container = default!; [Dependency] protected readonly ItemSlotsSystem ItemSlots = default!; + [Dependency] protected readonly ItemToggleSystem Toggle = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; /// @@ -87,7 +89,7 @@ protected virtual void OnRemoved(EntityUid uid, BorgChassisComponent component, private void OnRefreshMovementSpeedModifiers(EntityUid uid, BorgChassisComponent component, RefreshMovementSpeedModifiersEvent args) { - if (component.Activated) + if (Toggle.IsActivated(uid)) return; if (!TryComp(uid, out var movement)) diff --git a/Content.Shared/Toggleable/ToggleActionEvent.cs b/Content.Shared/Toggleable/ToggleActionEvent.cs index 1283b6699bf..f28e62e7dd1 100644 --- a/Content.Shared/Toggleable/ToggleActionEvent.cs +++ b/Content.Shared/Toggleable/ToggleActionEvent.cs @@ -4,9 +4,12 @@ namespace Content.Shared.Toggleable; /// -/// Generic action-event for toggle-able components. +/// Generic action-event for toggle-able components. /// -public sealed partial class ToggleActionEvent : InstantActionEvent { } +/// +/// If you are using ItemToggleComponent subscribe to ItemToggledEvent instead. +/// +public sealed partial class ToggleActionEvent : InstantActionEvent; /// /// Generic enum keys for toggle-visualizer appearance data & sprite layers. diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.cs b/Content.Shared/Tools/Systems/SharedToolSystem.cs index 0d7972a8de6..aa69d3db78a 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.cs @@ -24,7 +24,7 @@ public abstract partial class SharedToolSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] protected readonly SharedInteractionSystem InteractionSystem = default!; - [Dependency] protected readonly SharedItemToggleSystem ItemToggle = default!; + [Dependency] protected readonly ItemToggleSystem ItemToggle = default!; [Dependency] private readonly SharedMapSystem _maps = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] protected readonly SharedSolutionContainerSystem SolutionContainerSystem = default!; diff --git a/Content.Shared/UserInterface/ActivatableUISystem.Power.cs b/Content.Shared/UserInterface/ActivatableUISystem.Power.cs index b8a815c7a81..e494253c832 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.Power.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.Power.cs @@ -1,3 +1,5 @@ +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.PowerCell; using Robust.Shared.Containers; @@ -5,6 +7,7 @@ namespace Content.Shared.UserInterface; public sealed partial class ActivatableUISystem { + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedPowerCellSystem _cell = default!; private void InitializePower() @@ -12,27 +15,22 @@ private void InitializePower() SubscribeLocalEvent(OnBatteryOpenAttempt); SubscribeLocalEvent(OnBatteryOpened); SubscribeLocalEvent(OnBatteryClosed); - - SubscribeLocalEvent(OnPowerCellRemoved); + SubscribeLocalEvent(OnToggled); } - private void OnPowerCellRemoved(EntityUid uid, PowerCellDrawComponent component, EntRemovedFromContainerMessage args) + private void OnToggled(Entity ent, ref ItemToggledEvent args) { - _cell.SetPowerCellDrawEnabled(uid, false); - - if (!HasComp(uid) || - !TryComp(uid, out ActivatableUIComponent? activatable)) - { + // only close ui when losing power + if (!TryComp(ent, out var activatable) || args.Activated) return; - } if (activatable.Key == null) { - Log.Error($"Encountered null key in activatable ui on entity {ToPrettyString(uid)}"); + Log.Error($"Encountered null key in activatable ui on entity {ToPrettyString(ent)}"); return; } - _uiSystem.CloseUi(uid, activatable.Key); + _uiSystem.CloseUi(ent.Owner, activatable.Key); } private void OnBatteryOpened(EntityUid uid, ActivatableUIRequiresPowerCellComponent component, BoundUIOpenedEvent args) @@ -42,7 +40,7 @@ private void OnBatteryOpened(EntityUid uid, ActivatableUIRequiresPowerCellCompon if (!args.UiKey.Equals(activatable.Key)) return; - _cell.SetPowerCellDrawEnabled(uid, true); + _toggle.TryActivate(uid); } private void OnBatteryClosed(EntityUid uid, ActivatableUIRequiresPowerCellComponent component, BoundUIClosedEvent args) @@ -54,7 +52,7 @@ private void OnBatteryClosed(EntityUid uid, ActivatableUIRequiresPowerCellCompon // Stop drawing power if this was the last person with the UI open. if (!_uiSystem.IsUiOpen(uid, activatable.Key)) - _cell.SetPowerCellDrawEnabled(uid, false); + _toggle.TryDeactivate(uid); } /// diff --git a/Content.Shared/Weapons/Reflect/ReflectComponent.cs b/Content.Shared/Weapons/Reflect/ReflectComponent.cs index 5d8432ac776..5edd445afcb 100644 --- a/Content.Shared/Weapons/Reflect/ReflectComponent.cs +++ b/Content.Shared/Weapons/Reflect/ReflectComponent.cs @@ -5,16 +5,11 @@ namespace Content.Shared.Weapons.Reflect; /// /// Entities with this component have a chance to reflect projectiles and hitscan shots +/// Uses ItemToggleComponent to control reflection. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ReflectComponent : Component { - /// - /// Can only reflect when enabled - /// - [DataField("enabled"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public bool Enabled = true; - /// /// What we reflect. /// diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 76c98a427f5..7f20974a144 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Hands; using Content.Shared.Inventory; using Content.Shared.Inventory.Events; +using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Popups; using Content.Shared.Projectiles; @@ -31,10 +32,11 @@ namespace Content.Shared.Weapons.Reflect; /// public sealed class ReflectSystem : EntitySystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -102,10 +104,9 @@ private void OnReflectCollide(EntityUid uid, ReflectComponent component, ref Pro private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid projectile, ProjectileComponent? projectileComp = null, ReflectComponent? reflect = null) { - // Do we have the components needed to try a reflect at all? if ( !Resolve(reflector, ref reflect, false) || - !reflect.Enabled || + !_toggle.IsActivated(reflector) || !TryComp(projectile, out var reflective) || (reflect.Reflects & reflective.Reflective) == 0x0 || !TryComp(projectile, out var physics) || @@ -210,16 +211,10 @@ private bool TryReflectHitscan( { newDirection = null; if (!TryComp(reflector, out var reflect) || - !reflect.Enabled || - TryComp(reflector, out var staminaComponent) && staminaComponent.Critical || - _standing.IsDown(reflector)) - return false; - - // Non cultists can't use cult items to reflect anything. - if (HasComp(reflector) && !HasComp(user)) - return false; - - if (!_random.Prob(CalcReflectChance(reflector, reflect))) + !_toggle.IsActivated(reflector) || + !_random.Prob(reflect.ReflectProb)) + { + newDirection = null; return false; if (_netManager.IsServer) @@ -273,13 +268,8 @@ private void OnReflectHandUnequipped(EntityUid uid, ReflectComponent component, private void OnToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggledEvent args) { - comp.Enabled = args.Activated; - Dirty(uid, comp); - - if (comp.Enabled) - EnableAlert(uid); - else - DisableAlert(uid); + if (args.User is {} user) + RefreshReflectUser(user); } /// @@ -289,7 +279,7 @@ private void RefreshReflectUser(EntityUid user) { foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(user, SlotFlags.WITHOUT_POCKET)) { - if (!HasComp(ent)) + if (!HasComp(ent) || !_toggle.IsActivated(ent)) continue; EnsureComp(user); diff --git a/Resources/Prototypes/Actions/ninja.yml b/Resources/Prototypes/Actions/ninja.yml index 51bfc33c494..3ad67844d66 100644 --- a/Resources/Prototypes/Actions/ninja.yml +++ b/Resources/Prototypes/Actions/ninja.yml @@ -2,7 +2,7 @@ - type: entity id: ActionToggleNinjaGloves name: Toggle ninja gloves - description: Toggles all glove actions on left click. Includes your doorjack, draining power, stunning enemies, downloading research and calling in a threat. + description: Toggles all glove actions on left click. Includes your doorjack, draining power, stunning enemies and hacking certain computers. categories: [ HideSpawnMenu ] components: - type: InstantAction @@ -23,7 +23,7 @@ state: icon itemIconStyle: NoItem priority: -10 - event: !type:CreateThrowingStarEvent {} + event: !type:CreateItemEvent {} - type: entity id: ActionRecallKatana @@ -64,7 +64,7 @@ # have to plan (un)cloaking ahead of time useDelay: 5 priority: -9 - event: !type:ToggleStealthEvent + event: !type:ToggleActionEvent # katana - type: entity @@ -78,6 +78,10 @@ sprite: Objects/Magic/magicactions.rsi state: blink itemIconStyle: NoItem + sound: + path: /Audio/Magic/blink.ogg + params: + volume: 5 priority: -12 event: !type:DashEvent checkCanAccess: false diff --git a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml index 2abe1f0a80f..748e3482a8e 100644 --- a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml +++ b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml @@ -206,7 +206,7 @@ - type: FingerprintMask - type: entity - parent: ClothingHandsBase + parent: [ClothingHandsBase, BaseToggleClothing] id: ClothingHandsGlovesSpaceNinja name: space ninja gloves description: These black nano-enhanced gloves insulate from electricity and provide fire resistance. @@ -233,7 +233,32 @@ - type: FingerprintMask - type: Thieving stripTimeReduction: 1 + stealthy: true + - type: ToggleClothing + action: ActionToggleNinjaGloves - type: NinjaGloves + abilities: + - components: + - type: BatteryDrainer + - type: StunProvider + noPowerPopup: ninja-no-power + whitelist: + components: + - Stamina + - type: EmagProvider + whitelist: + components: + - Airlock + - objective: StealResearchObjective + components: + - type: ResearchStealer + - objective: TerrorObjective + components: + - type: CommsHacker + threats: NinjaThreats + - objective: MassArrestObjective + components: + - type: CriminalRecordsHacker - type: entity parent: ClothingHandsGlovesColorBlack diff --git a/Resources/Prototypes/Entities/Clothing/Head/misc.yml b/Resources/Prototypes/Entities/Clothing/Head/misc.yml index c7ba6e0b32a..10eaf1cb44e 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/misc.yml @@ -172,6 +172,36 @@ - type: AddAccentClothing accent: OwOAccent +- type: entity + parent: [ClothingHeadHatCatEars, BaseToggleClothing] + id: ClothingHeadHatCatEarsValid + suffix: Valid, DO NOT MAP + components: + - type: ToggleClothing + action: ActionBecomeValid + disableOnUnequip: true + - type: ComponentToggler + parent: true + components: + - type: KillSign + - type: Tag + tags: [] # ignore "WhitelistChameleon" tag + - type: Sprite + sprite: Clothing/Head/Hats/catears.rsi + - type: Clothing + sprite: Clothing/Head/Hats/catears.rsi + - type: AddAccentClothing + accent: OwOAccent + +- type: entity + noSpawn: true + id: ActionBecomeValid + name: Become Valid + description: "*notices your killsign* owo whats this" + components: + - type: InstantAction + event: !type:ToggleActionEvent + - type: entity parent: ClothingHeadBase id: ClothingHeadHatDogEars diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml index 6215fb3b45a..7ba33bd0da6 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml @@ -154,7 +154,7 @@ - HidesHarpyWings - type: entity - parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing] + parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing, BaseToggleClothing] id: ClothingOuterSuitSpaceNinja name: space ninja suit description: This black technologically advanced, cybernetically-enhanced suit provides many abilities like invisibility or teleportation. @@ -163,9 +163,7 @@ sprite: Clothing/OuterClothing/Suits/spaceninja.rsi - type: Clothing sprite: Clothing/OuterClothing/Suits/spaceninja.rsi - - type: StealthClothing - visibility: 1.1 - toggleAction: ActionTogglePhaseCloak + # hardsuit stuff - type: PressureProtection highPressureMultiplier: 0.6 lowPressureMultiplier: 1000 @@ -178,7 +176,27 @@ Slash: 0.8 Piercing: 0.8 Heat: 0.8 + # phase cloak + - type: ToggleClothing + action: ActionTogglePhaseCloak + - type: ComponentToggler + parent: true + components: + - type: Stealth + minVisibility: 0.1 + lastVisibility: 0.1 + - type: PowerCellDraw + drawRate: 1.8 # 200 seconds on the default cell + # throwing star ability + - type: ItemCreator + action: ActionCreateThrowingStar + charge: 14.4 + spawnedPrototype: ThrowingStarNinja + noPowerPopup: ninja-no-power + # core ninja suit stuff - type: NinjaSuit + - type: UseDelay + delay: 5 # disable time - type: PowerCellSlot cellSlotId: cell_slot # throwing in a recharger would bypass glove charging mechanic diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index 13fbc087164..bb2b163f080 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -1,45 +1,39 @@ - type: entity - parent: ClothingShoesBase + parent: [ClothingShoesBase, BaseToggleClothing] id: ClothingShoesBootsMag name: magboots description: Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. components: - - type: Sprite - sprite: Clothing/Shoes/Boots/magboots.rsi - layers: - - state: icon - map: [ "enum.ToggleVisuals.Layer" ] - - type: Clothing - sprite: Clothing/Shoes/Boots/magboots.rsi - - type: Magboots - - type: ClothingSpeedModifier - walkModifier: 0.85 - sprintModifier: 0.8 - enabled: false - - type: Appearance - - type: GenericVisualizer - visuals: - enum.ToggleVisuals.Toggled: - enum.ToggleVisuals.Layer: - True: {state: icon-on} - False: {state: icon} - - type: StaticPrice - price: 200 - - type: Tag - tags: - - WhitelistChameleon - - type: ReverseEngineering - difficulty: 2 - recipes: - - ClothingShoesBootsMag - - type: DamageOtherOnHit - damage: - types: - Blunt: 9 - staminaCost: 11.5 - soundHit: - path: /Audio/Weapons/smash.ogg - + - type: Sprite + sprite: Clothing/Shoes/Boots/magboots.rsi + layers: + - state: icon + map: [ "enum.ToggleVisuals.Layer" ] + - type: Clothing + sprite: Clothing/Shoes/Boots/magboots.rsi + - type: ToggleClothing + action: ActionToggleMagboots + - type: ToggleVerb + text: toggle-magboots-verb-get-data-text + - type: ComponentToggler + components: + - type: NoSlip + - type: Magboots + - type: ClothingSpeedModifier + walkModifier: 0.85 + sprintModifier: 0.8 + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: + True: {state: icon-on} + False: {state: icon} + - type: StaticPrice + price: 200 + - type: Tag + tags: + - WhitelistChameleon - type: entity parent: ClothingShoesBootsMag @@ -52,13 +46,9 @@ state: icon - type: Clothing sprite: Clothing/Shoes/Boots/magboots-advanced.rsi - - type: Magboots - toggleAction: ActionToggleMagbootsAdvanced - type: ClothingSpeedModifier walkModifier: 1 sprintModifier: 1 - enabled: false - - type: NoSlip - type: Tag tags: - WhitelistChameleon @@ -81,8 +71,6 @@ sprite: Clothing/Shoes/Boots/magboots-science.rsi - type: Clothing sprite: Clothing/Shoes/Boots/magboots-science.rsi - - type: Magboots - toggleAction: ActionToggleMagbootsSci - type: entity parent: ClothingShoesBootsMag @@ -93,7 +81,6 @@ - type: ClothingSpeedModifier walkModifier: 1.10 #PVS isn't too much of an issue when you are blind... sprintModifier: 1.10 - enabled: false - type: StaticPrice price: 3000 @@ -108,12 +95,9 @@ state: icon - type: Clothing sprite: Clothing/Shoes/Boots/magboots-syndicate.rsi - - type: Magboots - toggleAction: ActionToggleMagbootsSyndie - type: ClothingSpeedModifier walkModifier: 0.95 sprintModifier: 0.9 - enabled: false - type: GasTank outputPressure: 42.6 air: @@ -121,79 +105,18 @@ volume: 0.75 temperature: 293.15 moles: - - 0.153853429 # oxygen - - 0.153853429 # nitrogen - - type: ActivatableUI - key: enum.SharedGasTankUiKey.Key - - type: UserInterface - interfaces: - enum.SharedGasTankUiKey.Key: - type: GasTankBoundUserInterface - - type: Explosive - explosionType: Default - maxIntensity: 20 - - type: Jetpack - moleUsage: 0.00085 - - type: CanMoveInAir - - type: InputMover - toParent: true - - type: MovementSpeedModifier - weightlessAcceleration: 1 - weightlessFriction: 0.3 - weightlessModifier: 1.2 - - type: Tag - tags: - - WhitelistChameleon - - type: DamageOtherOnHit - damage: - types: - Blunt: 20 - staminaCost: 25 - soundHit: - collection: MetalThud + - 0.153853429 # oxygen + - 0.153853429 # nitrogen + - type: Item + sprite: null + size: Normal - type: entity - id: ActionBaseToggleMagboots + id: ActionToggleMagboots name: Toggle Magboots description: Toggles the magboots on and off. categories: [ HideSpawnMenu ] components: - type: InstantAction - itemIconStyle: NoItem - event: !type:ToggleMagbootsEvent - -- type: entity - id: ActionToggleMagboots - parent: ActionBaseToggleMagboots - categories: [ HideSpawnMenu ] - components: - - type: InstantAction - icon: { sprite: Clothing/Shoes/Boots/magboots.rsi, state: icon } - iconOn: { sprite : Clothing/Shoes/Boots/magboots.rsi, state: icon-on } - -- type: entity - id: ActionToggleMagbootsAdvanced - parent: ActionBaseToggleMagboots - categories: [ HideSpawnMenu ] - components: - - type: InstantAction - icon: { sprite: Clothing/Shoes/Boots/magboots-advanced.rsi, state: icon } - iconOn: Clothing/Shoes/Boots/magboots-advanced.rsi/icon-on.png - -- type: entity - id: ActionToggleMagbootsSci - parent: ActionBaseToggleMagboots - categories: [ HideSpawnMenu ] - components: - - type: InstantAction - icon: { sprite: Clothing/Shoes/Boots/magboots-science.rsi, state: icon } - iconOn: Clothing/Shoes/Boots/magboots-science.rsi/icon-on.png - -- type: entity - id: ActionToggleMagbootsSyndie - parent: ActionBaseToggleMagboots - categories: [ HideSpawnMenu ] - components: - - type: InstantAction - icon: { sprite: Clothing/Shoes/Boots/magboots-syndicate.rsi, state: icon } - iconOn: Clothing/Shoes/Boots/magboots-syndicate.rsi/icon-on.png + itemIconStyle: BigItem + event: !type:ToggleActionEvent diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml index 888bcd769cf..5d81a416199 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml @@ -88,7 +88,7 @@ - type: NoSlip - type: entity - parent: [ClothingShoesBase, PowerCellSlotSmallItem] + parent: [ClothingShoesBase, PowerCellSlotSmallItem, BaseToggleClothing] id: ClothingShoesBootsSpeed name: speed boots description: High-tech boots woven with quantum fibers, able to convert electricity into pure speed! @@ -100,12 +100,11 @@ map: [ "enum.ToggleVisuals.Layer" ] - type: Clothing sprite: Clothing/Shoes/Boots/speedboots.rsi - - type: ToggleClothingSpeed - toggleAction: ActionToggleSpeedBoots + - type: ToggleClothing + action: ActionToggleSpeedBoots - type: ClothingSpeedModifier walkModifier: 1.5 sprintModifier: 1.5 - enabled: false - type: Appearance - type: GenericVisualizer visuals: @@ -131,7 +130,23 @@ categories: [ HideSpawnMenu ] components: - type: InstantAction - itemIconStyle: NoItem - event: !type:ToggleClothingSpeedEvent - icon: { sprite: Clothing/Shoes/Boots/speedboots.rsi, state: icon } - iconOn: { sprite: Clothing/Shoes/Boots/speedboots.rsi, state: icon-on } + itemIconStyle: BigItem + event: !type:ToggleActionEvent + +- type: entity + parent: ClothingShoesBase + id: ClothingShoesBootsMoon + name: moon boots + description: Special anti-gravity boots developed with a speciality blend of lunar rock gel. Shipped from the Netherlands. + components: + - type: Sprite + sprite: Clothing/Shoes/Boots/moonboots.rsi + layers: + - state: icon + - type: Clothing + sprite: Clothing/Shoes/Boots/moonboots.rsi + - type: AntiGravityClothing + - type: StaticPrice + price: 75 + - type: Tag + tags: [ ] diff --git a/Resources/Prototypes/Entities/Clothing/base_clothing.yml b/Resources/Prototypes/Entities/Clothing/base_clothing.yml index bc592616be9..3d0732d1cab 100644 --- a/Resources/Prototypes/Entities/Clothing/base_clothing.yml +++ b/Resources/Prototypes/Entities/Clothing/base_clothing.yml @@ -51,3 +51,12 @@ - type: GroupExamine - type: Armor modifiers: {} + +# for clothing that can be toggled, like magboots +- type: entity + abstract: true + id: BaseToggleClothing + components: + - type: ItemToggle + onUse: false # can't really wear it like that + - type: ToggleClothing diff --git a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml index 3911a686266..f0f37822f4c 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml @@ -130,10 +130,9 @@ state: alive - type: entity + noSpawn: true + parent: BaseAntagSpawner id: SpawnPointGhostSpaceNinja - name: ghost role spawn point - suffix: space ninja - parent: MarkerBase components: - type: GhostRole name: ghost-role-information-space-ninja-name @@ -141,8 +140,6 @@ rules: ghost-role-information-space-ninja-rules raffle: settings: default - - type: GhostRoleMobSpawner - prototype: MobHumanSpaceNinja - type: Sprite sprite: Markers/jobs.rsi layers: diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 1d0a8d92280..2989c40ff77 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -114,6 +114,11 @@ - type: PowerCellSlot cellSlotId: cell_slot fitsInCharger: true + - type: ItemToggle + onUse: false # no item-borg toggling sorry + - type: AccessToggle + # TODO: refactor movement to just be based on toggle like speedboots but for the boots themselves + # TODO: or just have sentient speedboots be fast idk - type: PowerCellDraw drawRate: 0.6 - type: ItemSlots diff --git a/Resources/Prototypes/Entities/Mobs/Player/human.yml b/Resources/Prototypes/Entities/Mobs/Player/human.yml index 4284632d288..a7fb7de2f87 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/human.yml @@ -69,28 +69,3 @@ - Syndicate - type: Psionic powerRollMultiplier: 7 - -# Space Ninja -- type: entity - categories: [ HideSpawnMenu ] - name: Space Ninja - parent: MobHuman - id: MobHumanSpaceNinja - components: - - type: RandomHumanoidAppearance - randomizeName: false - - type: Loadout - prototypes: [SpaceNinjaGear] - - type: NpcFactionMember - factions: - - Syndicate - - type: SpaceNinja - - type: GenericAntag - rule: Ninja - - type: AutoImplant - implants: - - DeathAcidifierImplant - - type: RandomMetadata - nameSegments: - - names_ninja_title - - names_ninja diff --git a/Resources/Prototypes/Entities/Objects/Devices/base_handheld.yml b/Resources/Prototypes/Entities/Objects/Devices/base_handheld.yml new file mode 100644 index 00000000000..259323fede3 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/base_handheld.yml @@ -0,0 +1,11 @@ +- type: entity + abstract: true + parent: [BaseItem, PowerCellSlotSmallItem] + id: BaseHandheldComputer + components: + - type: ActivatableUIRequiresPowerCell + - type: ItemToggle + onUse: false # above component does the toggling + - type: PowerCellDraw + drawRate: 0 + useRate: 20 diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 2dbcfc60ab3..00df4e02cbe 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -118,6 +118,9 @@ id: BaseMedicalPDA abstract: true components: + - type: ItemToggle + toggleLight: false + onUse: false - type: HealthAnalyzer scanDelay: 1 scanningEndSound: diff --git a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml index 0d2f890a1d3..54fc4a70c5a 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml @@ -1,9 +1,9 @@ - type: entity + parent: BaseItem id: BaseHandheldStationMap name: station map description: Displays a readout of the current station. abstract: true - parent: BaseItem components: - type: StationMap - type: Sprite @@ -34,14 +34,9 @@ - type: entity id: HandheldStationMap parent: - - BaseHandheldStationMap - - PowerCellSlotSmallItem + - BaseHandheldStationMap + - BaseHandheldComputer suffix: Handheld, Powered - components: - - type: PowerCellDraw - drawRate: 0 - useRate: 20 - - type: ActivatableUIRequiresPowerCell - type: entity id: HandheldStationMapEmpty diff --git a/Resources/Prototypes/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Entities/Objects/Shields/shields.yml index f0d9a7c0b1b..ac23bd3995d 100644 --- a/Resources/Prototypes/Entities/Objects/Shields/shields.yml +++ b/Resources/Prototypes/Entities/Objects/Shields/shields.yml @@ -325,8 +325,10 @@ path: /Audio/Weapons/ebladehum.ogg - type: ItemToggleSize activatedSize: Huge - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.6 + - type: ComponentToggler + components: + - type: DisarmMalus + malus: 0.6 - type: Sprite sprite: Objects/Weapons/Melee/e_shield.rsi layers: @@ -358,7 +360,6 @@ energy: 2 color: blue - type: Reflect - enabled: false reflectProb: 0.95 innate: true reflects: @@ -436,8 +437,10 @@ path: /Audio/Weapons/telescopicoff.ogg params: volume: -5 - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.6 + - type: ComponentToggler + components: + - type: DisarmMalus + malus: 0.6 - type: ItemToggleSize activatedSize: Huge - type: Sprite diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml index 1a905685934..0a7041ea548 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml @@ -31,6 +31,11 @@ size: Large - type: Speech speechVerb: Robotic + - type: ItemToggle + soundActivate: + path: /Audio/Items/Defib/defib_safety_on.ogg + soundDeactivate: + path: /Audio/Items/Defib/defib_safety_off.ogg - type: Defibrillator zapHeal: types: diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml index a9d6bbb20f5..93208f80c00 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml @@ -1,19 +1,14 @@ - type: entity name: handheld crew monitor suffix: DO NOT MAP - parent: - - BaseItem - - PowerCellSlotSmallItem + parent: BaseHandheldComputer + # CMO-only bud, don't add more. id: HandheldCrewMonitor description: A hand-held crew monitor displaying the status of suit sensors. components: - type: Sprite sprite: Objects/Specific/Medical/handheldcrewmonitor.rsi state: scanner - - type: PowerCellDraw - drawRate: 0 - useRate: 20 - - type: ActivatableUIRequiresPowerCell - type: ActivatableUI key: enum.CrewMonitoringUIKey.Key - type: UserInterface diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml index 47e633276b8..df9c27149d3 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml @@ -21,6 +21,8 @@ interfaces: enum.HealthAnalyzerUiKey.Key: type: HealthAnalyzerBoundUserInterface + - type: ItemToggle + onUse: false - type: HealthAnalyzer scanningEndSound: path: "/Audio/Items/Medical/healthscanner.ogg" diff --git a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml index 3c3b2154226..a90f7099adc 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml @@ -40,23 +40,22 @@ - state: screen shader: unshaded visible: false - map: ["enum.PowerDeviceVisualLayers.Powered"] + map: ["enum.ToggleVisuals.Layer"] - type: Appearance - type: GenericVisualizer visuals: - enum.ProximityBeeperVisuals.Enabled: - enum.PowerDeviceVisualLayers.Powered: + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: True: { visible: true } False: { visible: false } + - type: ItemToggle - type: ProximityBeeper - type: ProximityDetector - enabled: false range: 20 criteria: components: - Anomaly - type: Beeper - enabled: false minBeepInterval: 0.15 maxBeepInterval: 1.00 beepSound: diff --git a/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml b/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml index 38b1f7cda9f..6491f699f00 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml @@ -1,6 +1,6 @@ - type: entity name: handheld mass scanner - parent: [ BaseItem, PowerCellSlotSmallItem] + parent: BaseHandheldComputer id: HandHeldMassScanner description: A hand-held mass scanner. components: @@ -27,7 +27,6 @@ - type: PowerCellDraw drawRate: 3 useRate: 100 - - type: ActivatableUIRequiresPowerCell - type: ActivatableUI key: enum.RadarConsoleUiKey.Key inHandsOnly: true diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index eae5cdbf46f..3b4394bdc3c 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -64,8 +64,10 @@ - type: ItemToggleSize activatedSize: Large - type: ItemToggleHot - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.6 + - type: ComponentToggler + components: + - type: DisarmMalus + malus: 0.6 - type: ToggleableLightVisuals spriteLayer: flame inhandVisuals: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index dc49dce0f3a..e95db1c6e9b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -180,6 +180,8 @@ - type: TetherGun frequency: 5 dampingRatio: 4 + - type: ItemToggle + onUse: false - type: PowerCellDraw - type: Sprite sprite: Objects/Weapons/Guns/Launchers/tether_gun.rsi @@ -225,6 +227,8 @@ path: /Audio/Weapons/soup.ogg params: volume: 2 + - type: ItemToggle + onUse: false - type: PowerCellDraw - type: Sprite sprite: Objects/Weapons/Guns/Launchers/force_gun.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index 59c6d63c6e1..e4dd7b5b418 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -13,10 +13,12 @@ - type: ItemToggleActiveSound activeSound: path: /Audio/Weapons/ebladehum.ogg - - type: ItemToggleSharp + - type: ComponentToggler + components: + - type: Sharp + - type: DisarmMalus + malus: 0.6 - type: ItemToggleHot - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.6 - type: ItemToggleSize activatedSize: Huge - type: ItemToggleMeleeWeapon @@ -86,15 +88,7 @@ right: - state: inhand-right-blade shader: unshaded - - type: DisarmMalus - malus: 0 - type: Reflect - enabled: false - reflectProb: 0.5 - minReflectProb: 0.25 - - type: Tag - tags: - - NoPaint - type: IgnitionSource temperature: 700 - type: Scalpel @@ -129,7 +123,6 @@ suffix: E-Dagger description: 'A dark ink pen.' components: - - type: EnergySword - type: ItemToggle soundActivate: path: /Audio/Weapons/ebladeon.ogg @@ -155,14 +148,11 @@ path: /Audio/Weapons/ebladehum.ogg params: volume: -6 - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.4 - - type: ItemToggleEmbeddableProjectile - activatedOffset: 0.0,0.0 - activatedRemovalTime: 3 - - type: ItemToggleThrowingAngle - activatedAngle: 135 - deleteOnDeactivate: false + - type: ComponentToggler + components: + - type: Sharp + - type: DisarmMalus + malus: 0.4 - type: Sprite sprite: Objects/Weapons/Melee/e_dagger.rsi layers: @@ -246,15 +236,12 @@ id: EnergyCutlass description: An exotic energy weapon, brutal blade crackling with crudely harnessed plasma. #DeltaV - nicer description. components: - - type: EnergySword - type: ItemToggleMeleeWeapon activatedDamage: types: Slash: 10 Heat: 12 deactivatedSecret: true - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.6 - type: Sprite sprite: DeltaV/Objects/Weapons/Melee/e_cutlass.rsi # DeltaV layers: @@ -290,8 +277,8 @@ id: EnergySwordDouble description: Syndicate Command's intern thought that having only one blade on energy swords was not cool enough. This can be stored in pockets. components: - - type: EnergySword - type: ItemToggle + onUse: false # wielding events control it instead soundActivate: path: /Audio/Weapons/ebladeon.ogg params: @@ -316,9 +303,13 @@ path: /Audio/Weapons/ebladehum.ogg params: volume: 3 - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.7 + - type: ComponentToggler + components: + - type: Sharp + - type: DisarmMalus + malus: 0.7 - type: Wieldable + wieldSound: null # esword light sound instead - type: MeleeWeapon wideAnimationRotation: -135 attackRate: .6666 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 2da3d3bb92e..d36f0704751 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -30,13 +30,7 @@ - type: ThrowingAngle angle: 225 - type: Reflect - enabled: true - # Design intent: a robust captain or tot can sacrifice movement to make the most of this weapon, but they have to - # really restrict themselves to walking speed or less. - reflectProb: 0.5 - velocityBeforeNotMaxProb: 1.0 - velocityBeforeMinProb: 3.0 - minReflectProb: 0.1 + reflectProb: .1 spread: 90 - type: Item size: Normal @@ -261,11 +255,7 @@ path: /Audio/Effects/explosion_small1.ogg - type: DamageOtherOnHit - type: Reflect - enabled: true - reflectProb: 0.5 # In robust hands, deflects as well as an e-sword - velocityBeforeNotMaxProb: 1.0 - velocityBeforeMinProb: 3.0 - minReflectProb: 0.1 + reflectProb: .25 spread: 90 - type: Item size: Ginormous diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml index 4cb76ea4b92..0485b5a5178 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml @@ -85,6 +85,7 @@ whitelist: components: - FitsInDispenser + - type: ItemToggle - type: HealthAnalyzer scanDelay: 0 - type: UserInterface diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index a0649eb2d6b..8a9c4880884 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -104,7 +104,43 @@ earliestStart: 30 reoccurrenceDelay: 20 minimumPlayers: 30 - - type: NinjaSpawnRule + - type: SpaceSpawnRule + - type: AntagLoadProfileRule + - type: AntagObjectives + objectives: + - StealResearchObjective + - DoorjackObjective + - SpiderChargeObjective + - TerrorObjective + - MassArrestObjective + - NinjaSurviveObjective + - type: AntagSelection + agentName: ninja-round-end-agent-name + definitions: + - spawnerPrototype: SpawnPointGhostSpaceNinja + min: 1 + max: 1 + pickPlayer: false + startingGear: SpaceNinjaGear + briefing: + text: ninja-role-greeting + color: Green + sound: /Audio/Misc/ninja_greeting.ogg + components: + - type: SpaceNinja + - type: NpcFactionMember + factions: + - Syndicate + - type: AutoImplant + implants: + - DeathAcidifierImplant + - type: RandomMetadata + nameSegments: + - names_ninja_title + - names_ninja + mindComponents: + - type: NinjaRole + prototype: SpaceNinja - type: entity parent: BaseGameRule diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index ba2cd530480..50864930b0f 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -1,34 +1,3 @@ -# doesnt spawn a ninja or anything, just stores configuration for it -# see NinjaSpawn event for spawning -- type: entity - id: Ninja - parent: BaseGameRule - categories: [ HideSpawnMenu ] - components: - - type: GenericAntagRule - agentName: ninja-round-end-agent-name - objectives: - - StealResearchObjective - - DoorjackObjective - - SpiderChargeObjective - - TerrorObjective - - MassArrestObjective - - NinjaSurviveObjective - - type: NinjaRule - threats: NinjaThreats - -# stores configuration for dragon -- type: entity - categories: [ HideSpawnMenu ] - parent: BaseGameRule - id: Dragon - components: - - type: GenericAntagRule - agentName: dragon-round-end-agent-name - objectives: - - CarpRiftsObjective - - DragonSurviveObjective - - type: entity categories: [ HideSpawnMenu ] parent: BaseGameRule From 0102f7d21b079348115c0ef581e44faaaa105784 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sun, 4 Aug 2024 07:38:53 +0200 Subject: [PATCH 008/366] replace all uses of TryGetContainingContainer with non-obsolete overload (#30583) * replace all uses of TryGetContainerContainer with non-obsolete overload * rerun --- Content.Client/Commands/HideMechanismsCommand.cs | 2 +- Content.Client/Instruments/UI/InstrumentMenu.xaml.cs | 2 +- Content.Client/Items/Systems/ItemSystem.cs | 2 +- Content.Client/Movement/Systems/JetpackSystem.cs | 4 ++-- Content.Server/Atmos/Rotting/RottingSystem.cs | 6 +++--- Content.Server/Cargo/Systems/CargoSystem.Bounty.cs | 2 +- .../Nutrition/EntitySystems/SmokingSystem.cs | 2 +- Content.Server/PDA/PdaSystem.cs | 2 +- Content.Server/Polymorph/Systems/PolymorphSystem.cs | 2 +- Content.Server/PowerCell/PowerCellSystem.cs | 4 ++-- Content.Server/Resist/EscapeInventorySystem.cs | 2 +- .../Singularity/EntitySystems/EventHorizonSystem.cs | 6 +++--- .../Body/Systems/SharedBodySystem.Organs.cs | 2 +- .../Body/Systems/SharedBodySystem.Parts.cs | 6 +++--- .../Clothing/ClothingSpeedModifierSystem.cs | 4 ++-- .../Clothing/EntitySystems/ClothingSystem.cs | 2 +- Content.Shared/Clothing/MagbootsSystem.cs | 2 +- .../Damage/Systems/DamageOnHoldingSystem.cs | 2 +- .../Interaction/SharedInteractionSystem.cs | 4 ++-- Content.Shared/Item/SharedItemSystem.cs | 4 ++-- .../Materials/SharedMaterialReclaimerSystem.cs | 2 +- .../Movement/Systems/SharedJetpackSystem.cs | 12 ++++++------ .../Storage/EntitySystems/SharedStorageSystem.cs | 2 +- .../Ranged/Systems/SharedGunSystem.Clothing.cs | 2 +- 24 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Content.Client/Commands/HideMechanismsCommand.cs b/Content.Client/Commands/HideMechanismsCommand.cs index 5f9afc78b98..97d792628a6 100644 --- a/Content.Client/Commands/HideMechanismsCommand.cs +++ b/Content.Client/Commands/HideMechanismsCommand.cs @@ -30,7 +30,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) sprite.ContainerOccluded = false; var tempParent = uid; - while (containerSys.TryGetContainingContainer(tempParent, out var container)) + while (containerSys.TryGetContainingContainer((tempParent, null, null), out var container)) { if (!container.ShowContents) { diff --git a/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs b/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs index da443e3fb5b..b54a91ee975 100644 --- a/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs +++ b/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs @@ -180,7 +180,7 @@ private bool PlayCheck() var container = _owner.Entities.System(); // If we're a handheld instrument, we might be in a container. Get it just in case. - container.TryGetContainingContainer(instrumentEnt, out var conMan); + container.TryGetContainingContainer((Entity, null, null), out var conMan); // If the instrument is handheld and we're not holding it, we return. if ((instrument.Handheld && (conMan == null || conMan.Owner != localEntity))) diff --git a/Content.Client/Items/Systems/ItemSystem.cs b/Content.Client/Items/Systems/ItemSystem.cs index 5e60d06d0ce..2b5a41c6ce7 100644 --- a/Content.Client/Items/Systems/ItemSystem.cs +++ b/Content.Client/Items/Systems/ItemSystem.cs @@ -43,7 +43,7 @@ private void OnEquipped(EntityUid uid, SpriteComponent component, GotEquippedEve public override void VisualsChanged(EntityUid uid) { // if the item is in a container, it might be equipped to hands or inventory slots --> update visuals. - if (Container.TryGetContainingContainer(uid, out var container)) + if (Container.TryGetContainingContainer((uid, null, null), out var container)) RaiseLocalEvent(container.Owner, new VisualsChangedEvent(GetNetEntity(uid), container.ID)); } diff --git a/Content.Client/Movement/Systems/JetpackSystem.cs b/Content.Client/Movement/Systems/JetpackSystem.cs index b7f5e48821f..6f9ef830408 100644 --- a/Content.Client/Movement/Systems/JetpackSystem.cs +++ b/Content.Client/Movement/Systems/JetpackSystem.cs @@ -63,15 +63,15 @@ public override void Update(float frameTime) private void CreateParticles(EntityUid uid) { + var uidXform = Transform(uid); // Don't show particles unless the user is moving. - if (Container.TryGetContainingContainer(uid, out var container) && + if (Container.TryGetContainingContainer((uid, uidXform, null), out var container) && TryComp(container.Owner, out var body) && body.LinearVelocity.LengthSquared() < 1f) { return; } - var uidXform = Transform(uid); var coordinates = uidXform.Coordinates; var gridUid = coordinates.GetGridUid(EntityManager); diff --git a/Content.Server/Atmos/Rotting/RottingSystem.cs b/Content.Server/Atmos/Rotting/RottingSystem.cs index 40bdf657439..bfff8a91d0d 100644 --- a/Content.Server/Atmos/Rotting/RottingSystem.cs +++ b/Content.Server/Atmos/Rotting/RottingSystem.cs @@ -34,7 +34,7 @@ private void OnGibbed(EntityUid uid, RottingComponent component, BeingGibbedEven if (!TryComp(uid, out var perishable)) return; - var molsToDump = perishable.MolsPerSecondPerUnitMass * physics.FixturesMass * (float) component.TotalRotTime.TotalSeconds; + var molsToDump = perishable.MolsPerSecondPerUnitMass * physics.FixturesMass * (float)component.TotalRotTime.TotalSeconds; var tileMix = _atmosphere.GetTileMixture(uid, excite: true); tileMix?.AdjustMoles(Gas.Ammonia, molsToDump); } @@ -77,7 +77,7 @@ public void ReduceAccumulator(EntityUid uid, TimeSpan time) /// private float GetRotRate(EntityUid uid) { - if (_container.TryGetContainingContainer(uid, out var container) && + if (_container.TryGetContainingContainer((uid, null, null), out var container) && TryComp(container.Owner, out var rotContainer)) { return rotContainer.DecayModifier; @@ -147,7 +147,7 @@ public override void Update(float frameTime) continue; // We need a way to get the mass of the mob alone without armor etc in the future // or just remove the mass mechanics altogether because they aren't good. - var molRate = perishable.MolsPerSecondPerUnitMass * (float) rotting.RotUpdateRate.TotalSeconds; + var molRate = perishable.MolsPerSecondPerUnitMass * (float)rotting.RotUpdateRate.TotalSeconds; var tileMix = _atmosphere.GetTileMixture(uid, excite: true); tileMix?.AdjustMoles(Gas.Ammonia, molRate * physics.FixturesMass); } diff --git a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs index e132e4f12a3..9922cc5a37e 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs @@ -135,7 +135,7 @@ private void OnGetBountyPrice(EntityUid uid, CargoBountyLabelComponent component return; // make sure this label was actually applied to a crate. - if (!_container.TryGetContainingContainer(uid, out var container) || container.ID != LabelSystem.ContainerName) + if (!_container.TryGetContainingContainer((uid, null, null), out var container) || container.ID != LabelSystem.ContainerName) return; if (component.AssociatedStationId is not { } station || !TryComp(station, out var database)) diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs index a33c944c994..7f8253efedf 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs @@ -142,7 +142,7 @@ public override void Update(float frameTime) // This is awful. I hate this so much. // TODO: Please, someone refactor containers and free me from this bullshit. - if (!_container.TryGetContainingContainer(uid, out var containerManager) || + if (!_container.TryGetContainingContainer((uid, null, null), out var containerManager) || !(_inventorySystem.TryGetSlotEntity(containerManager.Owner, "mask", out var inMaskSlotUid) && inMaskSlotUid == uid) || !TryComp(containerManager.Owner, out BloodstreamComponent? bloodstream)) { diff --git a/Content.Server/PDA/PdaSystem.cs b/Content.Server/PDA/PdaSystem.cs index d4934ee24e5..f75fd9920f3 100644 --- a/Content.Server/PDA/PdaSystem.cs +++ b/Content.Server/PDA/PdaSystem.cs @@ -121,7 +121,7 @@ private void OnNotification(Entity ent, ref CartridgeLoaderNotific { _ringer.RingerPlayRingtone(ent.Owner); - if (!_containerSystem.TryGetContainingContainer(ent, out var container) + if (!_containerSystem.TryGetContainingContainer((ent, null, null), out var container) || !TryComp(container.Owner, out var actor)) return; diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs index 5c970b1a748..913299eea91 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs @@ -226,7 +226,7 @@ private void OnDestruction(Entity ent, ref Destructi var childXform = Transform(child); _transform.SetLocalRotation(child, targetTransformComp.LocalRotation, childXform); - if (_container.TryGetContainingContainer(uid, out var cont)) + if (_container.TryGetContainingContainer((uid, targetTransformComp, null), out var cont)) _container.Insert(child, cont); //Transfers all damage from the original to the new one diff --git a/Content.Server/PowerCell/PowerCellSystem.cs b/Content.Server/PowerCell/PowerCellSystem.cs index 0d2d9012bc7..f7b4cf02491 100644 --- a/Content.Server/PowerCell/PowerCellSystem.cs +++ b/Content.Server/PowerCell/PowerCellSystem.cs @@ -64,11 +64,11 @@ private void OnChargeChanged(EntityUid uid, PowerCellComponent component, ref Ch } var frac = args.Charge / args.MaxCharge; - var level = (byte) ContentHelpers.RoundToNearestLevels(frac, 1, PowerCellComponent.PowerCellVisualsLevels); + var level = (byte)ContentHelpers.RoundToNearestLevels(frac, 1, PowerCellComponent.PowerCellVisualsLevels); _sharedAppearanceSystem.SetData(uid, PowerCellVisuals.ChargeLevel, level); // If this power cell is inside a cell-slot, inform that entity that the power has changed (for updating visuals n such). - if (_containerSystem.TryGetContainingContainer(uid, out var container) + if (_containerSystem.TryGetContainingContainer((uid, null, null), out var container) && TryComp(container.Owner, out PowerCellSlotComponent? slot) && _itemSlotsSystem.TryGetSlot(container.Owner, slot.CellSlotId, out var itemSlot)) { diff --git a/Content.Server/Resist/EscapeInventorySystem.cs b/Content.Server/Resist/EscapeInventorySystem.cs index 2e060b1b42d..a6eb703ab0f 100644 --- a/Content.Server/Resist/EscapeInventorySystem.cs +++ b/Content.Server/Resist/EscapeInventorySystem.cs @@ -56,7 +56,7 @@ private void OnRelayMovement(EntityUid uid, CanEscapeInventoryComponent componen if (!args.HasDirectionalMovement) return; - if (!_containerSystem.TryGetContainingContainer(uid, out var container) || !_actionBlockerSystem.CanInteract(uid, container.Owner)) + if (!_containerSystem.TryGetContainingContainer((uid, null, null), out var container) || !_actionBlockerSystem.CanInteract(uid, container.Owner)) return; // Make sure there's nothing stopped the removal (like being glued) diff --git a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs index c74a3c49d63..2aec9a45ad1 100644 --- a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs @@ -97,7 +97,7 @@ public void Update(EntityUid uid, EventHorizonComponent? eventHorizon = null, Tr return; // Handle singularities some admin smited into a locker. - if (_containerSystem.TryGetContainingContainer(uid, out var container, transform: xform) + if (_containerSystem.TryGetContainingContainer((uid, xform, null), out var container) && !AttemptConsumeEntity(uid, container.Owner, eventHorizon)) { // Locker is indestructible. Consume everything else in the locker instead of magically teleporting out. @@ -214,7 +214,7 @@ public void ConsumeEntitiesInContainer(EntityUid hungry, BaseContainer container if (_containerSystem.Insert(entity, target_container)) break; - _containerSystem.TryGetContainingContainer(target_container.Owner, out target_container); + _containerSystem.TryGetContainingContainer((target_container.Owner, null, null), out target_container); } // If we couldn't or there was no container to insert into just dump them to the map/grid. @@ -470,7 +470,7 @@ private void OnContainerConsumed(EntityUid uid, ContainerManagerComponent comp, { var drop_container = args.Container; if (drop_container is null) - _containerSystem.TryGetContainingContainer(uid, out drop_container); + _containerSystem.TryGetContainingContainer((uid, null, null), out drop_container); foreach (var container in comp.GetAllContainers()) { diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs b/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs index 9c9fdc57276..a67d4b1f7fa 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs @@ -148,7 +148,7 @@ public bool InsertOrgan( /// public bool RemoveOrgan(EntityUid organId, OrganComponent? organ = null) { - if (!Containers.TryGetContainingContainer(organId, out var container)) + if (!Containers.TryGetContainingContainer((organId, null, null), out var container)) return false; var parent = container.Owner; diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index dc088542279..36a650863e4 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -379,7 +379,7 @@ private void DisablePart(Entity partEnt) /// public EntityUid? GetParentPartOrNull(EntityUid uid) { - if (!Containers.TryGetContainingContainer(uid, out var container)) + if (!Containers.TryGetContainingContainer((uid, null, null), out var container)) return null; var parent = container.Owner; @@ -395,7 +395,7 @@ private void DisablePart(Entity partEnt) /// public (EntityUid Parent, string Slot)? GetParentPartAndSlotOrNull(EntityUid uid) { - if (!Containers.TryGetContainingContainer(uid, out var container)) + if (!Containers.TryGetContainingContainer((uid, null, null), out var container)) return null; var slotId = GetPartSlotContainerIdFromContainer(container.ID); @@ -425,7 +425,7 @@ public bool TryGetParentBodyPart( parentUid = null; parentComponent = null; - if (Containers.TryGetContainingContainer(partUid, out var container) && + if (Containers.TryGetContainingContainer((partUid, null, null), out var container) && TryComp(container.Owner, out parentComponent)) { parentUid = container.Owner; diff --git a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs index c89ab827e67..670bf99ef90 100644 --- a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs +++ b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs @@ -51,7 +51,7 @@ private void OnHandleState(EntityUid uid, ClothingSpeedModifierComponent compone // Avoid raising the event for the container if nothing changed. // We'll still set the values in case they're slightly different but within tolerance. - if (diff && _container.TryGetContainingContainer(uid, out var container)) + if (diff && _container.TryGetContainingContainer((uid, null, null), out var container)) { _movementSpeed.RefreshMovementSpeedModifiers(container.Owner); } @@ -115,7 +115,7 @@ private void OnToggled(Entity ent, ref ItemToggl // make sentient boots slow or fast too _movementSpeed.RefreshMovementSpeedModifiers(ent); - if (_container.TryGetContainingContainer(ent.Owner, out var container)) + if (_container.TryGetContainingContainer((ent.Owner, null, null), out var container)) { // inventory system will automatically hook into the event raised by this and update accordingly _movementSpeed.RefreshMovementSpeedModifiers(container.Owner); diff --git a/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs index 9e3f917e96f..d03cd095bb7 100644 --- a/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs @@ -165,7 +165,7 @@ private void OnHandleState(EntityUid uid, ClothingComponent component, ref Compo if (args.Current is ClothingComponentState state) { SetEquippedPrefix(uid, state.EquippedPrefix, component); - if (component.InSlot != null && _containerSys.TryGetContainingContainer(uid, out var container)) + if (component.InSlot != null && _containerSys.TryGetContainingContainer((uid, null, null), out var container)) { CheckEquipmentForLayerHide(uid, container.Owner); } diff --git a/Content.Shared/Clothing/MagbootsSystem.cs b/Content.Shared/Clothing/MagbootsSystem.cs index 88d987aae18..d41f27fefb0 100644 --- a/Content.Shared/Clothing/MagbootsSystem.cs +++ b/Content.Shared/Clothing/MagbootsSystem.cs @@ -36,7 +36,7 @@ private void OnToggled(Entity ent, ref ItemToggledEvent args) { var (uid, comp) = ent; // only stick to the floor if being worn in the correct slot - if (_container.TryGetContainingContainer(uid, out var container) && + if (_container.TryGetContainingContainer((uid, null, null), out var container) && _inventory.TryGetSlotEntity(container.Owner, comp.Slot, out var worn) && uid == worn) { diff --git a/Content.Shared/Damage/Systems/DamageOnHoldingSystem.cs b/Content.Shared/Damage/Systems/DamageOnHoldingSystem.cs index c13ec0a1b9f..78e86f5ab42 100644 --- a/Content.Shared/Damage/Systems/DamageOnHoldingSystem.cs +++ b/Content.Shared/Damage/Systems/DamageOnHoldingSystem.cs @@ -37,7 +37,7 @@ public override void Update(float frameTime) { if (!component.Enabled || component.NextDamage > _timing.CurTime) continue; - if (_container.TryGetContainingContainer(uid, out var container)) + if (_container.TryGetContainingContainer((uid, null, null), out var container)) { _damageableSystem.TryChangeDamage(container.Owner, component.Damage, origin: uid); } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 32d69dc8e73..53a66e5ada5 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -1226,7 +1226,7 @@ public bool IsAccessible(Entity user, Entity public bool CanAccessViaStorage(EntityUid user, EntityUid target) { - if (!_containerSystem.TryGetContainingContainer(target, out var container)) + if (!_containerSystem.TryGetContainingContainer((target, null, null), out var container)) return false; return CanAccessViaStorage(user, target, container); @@ -1252,7 +1252,7 @@ public bool CanAccessEquipment(EntityUid user, EntityUid target) if (Deleted(target)) return false; - if (!_containerSystem.TryGetContainingContainer(target, out var container)) + if (!_containerSystem.TryGetContainingContainer((target, null, null), out var container)) return false; var wearer = container.Owner; diff --git a/Content.Shared/Item/SharedItemSystem.cs b/Content.Shared/Item/SharedItemSystem.cs index 5eaa25f4847..a058cb86588 100644 --- a/Content.Shared/Item/SharedItemSystem.cs +++ b/Content.Shared/Item/SharedItemSystem.cs @@ -110,8 +110,8 @@ private void AddPickupVerb(EntityUid uid, ItemComponent component, GetVerbsEvent // if the item already in a container (that is not the same as the user's), then change the text. // this occurs when the item is in their inventory or in an open backpack - Container.TryGetContainingContainer(args.User, out var userContainer); - if (Container.TryGetContainingContainer(args.Target, out var container) && container != userContainer) + Container.TryGetContainingContainer((args.User, null, null), out var userContainer); + if (Container.TryGetContainingContainer((args.Target, null, null), out var container) && container != userContainer) verb.Text = Loc.GetString("pick-up-verb-get-data-text-inventory"); else verb.Text = Loc.GetString("pick-up-verb-get-data-text"); diff --git a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs index cd4ae2e7676..d57eea7e3ed 100644 --- a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs +++ b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs @@ -100,7 +100,7 @@ public bool TryStartProcessItem(EntityUid uid, EntityUid item, MaterialReclaimer if (component.Blacklist is {} blacklist && blacklist.IsValid(item)) return false; - if (Container.TryGetContainingContainer(item, out _) && !Container.TryRemoveFromContainer(item)) + if (Container.TryGetContainingContainer((item, null, null), out _) && !Container.TryRemoveFromContainer(item)) return false; if (user != null) diff --git a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs index 8d358f8db39..8eb943d7153 100644 --- a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs +++ b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs @@ -15,12 +15,12 @@ namespace Content.Shared.Movement.Systems; public abstract class SharedJetpackSystem : EntitySystem { - [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; - [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; + [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedContainerSystem Container = default!; - [Dependency] private readonly SharedMoverController _mover = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedMoverController _mover = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; [Dependency] private readonly IConfigurationManager _config = default!; @@ -164,7 +164,7 @@ public void SetEnabled(EntityUid uid, JetpackComponent component, bool enabled, if (user == null) { - Container.TryGetContainingContainer(uid, out var container); + Container.TryGetContainingContainer((uid, null, null), out var container); user = container?.Owner; } diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index fad9d724388..d5449e88f8a 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -1408,7 +1408,7 @@ private void OnLockToggled(EntityUid uid, StorageComponent component, ref LockTo private void OnStackCountChanged(EntityUid uid, MetaDataComponent component, StackCountChangedEvent args) { - if (_containerSystem.TryGetContainingContainer(uid, out var container, component) && + if (_containerSystem.TryGetContainingContainer((uid, null, component), out var container) && container.ID == StorageComponent.ContainerId) { UpdateAppearance(container.Owner); diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Clothing.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Clothing.cs index 77ee419ac3b..78386e89519 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Clothing.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Clothing.cs @@ -33,7 +33,7 @@ private bool TryGetClothingSlotEntity(EntityUid uid, ClothingSlotAmmoProviderCom { slotEntity = null; - if (!Containers.TryGetContainingContainer(uid, out var container)) + if (!Containers.TryGetContainingContainer((uid, null, null), out var container)) return false; var user = container.Owner; From 1e4cb00770c0b40e3857c847be8763e8a7826ddc Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Sat, 10 Aug 2024 19:29:44 -0700 Subject: [PATCH 009/366] Add a component that inserts the held item when no interaction happens on the stored item (#29823) * Insert when held item has no interaction with stored item * Decouple inserting on failure * Add component that stores the used entity when no interaction happened * Add prediction --- .../Interaction/SharedInteractionSystem.cs | 67 +++++++++++++------ .../StoreAfterFailedInteractComponent.cs | 8 +++ .../EntitySystems/SharedStorageSystem.cs | 40 ++++++----- .../StoreAfterFailedInteractSystem.cs | 21 ++++++ .../Events/StorageInsertFailedEvent.cs | 6 ++ 5 files changed, 105 insertions(+), 37 deletions(-) create mode 100644 Content.Shared/Storage/Components/StoreAfterFailedInteractComponent.cs create mode 100644 Content.Shared/Storage/EntitySystems/StoreAfterFailedInteractSystem.cs create mode 100644 Content.Shared/Storage/Events/StorageInsertFailedEvent.cs diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 53a66e5ada5..d325de92c83 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -956,11 +956,21 @@ public bool RangedInteractDoBefore( } /// - /// Uses a item/object on an entity + /// Uses an item/object on an entity /// Finds components with the InteractUsing interface and calls their function /// NOTE: Does not have an InRangeUnobstructed check /// - public void InteractUsing( + /// User doing the interaction. + /// Item being used on the . + /// Entity getting interacted with by the using the + /// entity. + /// The location that the clicked. + /// Whether to check that the can interact with the + /// . + /// Whether to check that the can use the + /// entity. + /// True if the interaction was handled. Otherwise, false. + public bool InteractUsing( EntityUid user, EntityUid used, EntityUid target, @@ -972,13 +982,21 @@ public void InteractUsing( return; if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) - return; + return false; + + if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) + return false; if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, used)) - return; + return false; + + _adminLogger.Add( + LogType.InteractUsing, + LogImpact.Low, + $"{ToPrettyString(user):user} interacted with {ToPrettyString(target):target} using {ToPrettyString(used):used}"); if (RangedInteractDoBefore(user, used, target, clickLocation, true)) - return; + return true; // all interactions should only happen when in range / unobstructed, so no range check is needed var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation); @@ -987,42 +1005,44 @@ public void InteractUsing( DoContactInteraction(user, target, interactUsingEvent); // Contact interactions are currently only used for forensics, so we don't raise used -> target if (interactUsingEvent.Handled) - return; + return true; - InteractDoAfter(user, used, target, clickLocation, canReach: true); + if (InteractDoAfter(user, used, target, clickLocation, canReach: true)) + return true; + return false; } /// /// Used when clicking on an entity resulted in no other interaction. Used for low-priority interactions. /// - public void InteractDoAfter( - EntityUid user, - EntityUid used, - EntityUid? target, - EntityCoordinates clickLocation, - bool canReach, - bool checkDeletion = false - ) + /// + /// + /// + /// + /// Whether the is in range of the . + /// + /// True if the interaction was handled. Otherwise, false. + public bool InteractDoAfter(EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool canReach) { if (target is { Valid: false }) target = null; if (checkDeletion && (IsDeleted(user) || IsDeleted(used) || IsDeleted(target))) - return; + return false; var afterInteractEvent = new AfterInteractEvent(user, used, target, clickLocation, canReach); RaiseLocalEvent(used, afterInteractEvent); DoContactInteraction(user, used, afterInteractEvent); + if (canReach) DoContactInteraction(user, target, afterInteractEvent); - // Contact interactions are currently only used for forensics, so we don't raise used -> target - } + // Contact interactions are currently only used for forensics, so we don't raise used -> target if (afterInteractEvent.Handled) - return; + return true; if (target == null) - return; + return false; var afterInteractUsingEvent = new AfterInteractUsingEvent(user, used, target, clickLocation, canReach); RaiseLocalEvent(target.Value, afterInteractUsingEvent); @@ -1030,8 +1050,11 @@ public void InteractDoAfter( DoContactInteraction(user, used, afterInteractUsingEvent); if (canReach) DoContactInteraction(user, target, afterInteractUsingEvent); - // Contact interactions are currently only used for forensics, so we don't raise used -> target - } + // Contact interactions are currently only used for forensics, so we don't raise used -> target + + if (afterInteractUsingEvent.Handled) + return true; + return false; } #region ActivateItemInWorld diff --git a/Content.Shared/Storage/Components/StoreAfterFailedInteractComponent.cs b/Content.Shared/Storage/Components/StoreAfterFailedInteractComponent.cs new file mode 100644 index 00000000000..578da52dfc1 --- /dev/null +++ b/Content.Shared/Storage/Components/StoreAfterFailedInteractComponent.cs @@ -0,0 +1,8 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Storage.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class StoreAfterFailedInteractComponent : Component +{ +} diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index d5449e88f8a..9fd8bdf5e7b 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -21,6 +21,7 @@ using Content.Shared.Stacks; using Content.Shared.Storage.Components; using Content.Shared.Timing; +using Content.Shared.Storage.Events; using Content.Shared.Verbs; using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; @@ -350,7 +351,7 @@ private void OnInteractUsing(EntityUid uid, StorageComponent storageComp, Intera if (HasComp(uid)) return; - PlayerInsertHeldEntity(uid, args.User, storageComp); + PlayerInsertHeldEntity((uid, storageComp), args.User); // Always handle it, even if insertion fails. // We don't want to trigger any AfterInteract logic here. // Example issue would be placing wires if item doesn't fit in backpack. @@ -597,7 +598,15 @@ private void OnInteractWithItem(StorageInteractWithItemEvent msg, EntitySessionE } // Else, interact using the held item - _interactionSystem.InteractUsing(player, hands.ActiveHandEntity.Value, entity, Transform(entity).Coordinates, checkCanInteract: false); + if (_interactionSystem.InteractUsing(player, + player.Comp.ActiveHandEntity.Value, + item, + Transform(item).Coordinates, + checkCanInteract: false)) + return; + + var failedEv = new StorageInsertFailedEvent((storage, storage.Comp), (player, player.Comp)); + RaiseLocalEvent(storage, ref failedEv); } private void OnSetItemLocation(StorageSetItemLocationEvent msg, EntitySessionEventArgs args) @@ -1039,35 +1048,36 @@ public bool Insert( /// /// Inserts an entity into storage from the player's active hand /// - /// - /// The player to insert an entity from - /// - /// true if inserted, false otherwise - public bool PlayerInsertHeldEntity(EntityUid uid, EntityUid player, StorageComponent? storageComp = null) + /// The storage entity and component to insert into. + /// The player and hands component to insert the held entity from. + /// True if inserted, otherwise false. + public bool PlayerInsertHeldEntity(Entity ent, Entity player) { - if (!Resolve(uid, ref storageComp) || !TryComp(player, out HandsComponent? hands) || hands.ActiveHandEntity == null) + if (!Resolve(ent.Owner, ref ent.Comp) + || !Resolve(player.Owner, ref player.Comp) + || player.Comp.ActiveHandEntity == null) return false; - var toInsert = hands.ActiveHandEntity; + var toInsert = player.Comp.ActiveHandEntity; - if (!CanInsert(uid, toInsert.Value, out var reason, storageComp)) + if (!CanInsert(ent, toInsert.Value, out var reason, ent.Comp)) { - _popupSystem.PopupClient(Loc.GetString(reason ?? "comp-storage-cant-insert"), uid, player); + _popupSystem.PopupClient(Loc.GetString(reason ?? "comp-storage-cant-insert"), ent, player); return false; } - if (!_sharedHandsSystem.CanDrop(player, toInsert.Value, hands)) + if (!_sharedHandsSystem.CanDrop(player, toInsert.Value, player.Comp)) { - _popupSystem.PopupClient(Loc.GetString("comp-storage-cant-drop", ("entity", toInsert.Value)), uid, player); + _popupSystem.PopupClient(Loc.GetString("comp-storage-cant-drop", ("entity", toInsert.Value)), ent, player); return false; } - return PlayerInsertEntityInWorld((uid, storageComp), player, toInsert.Value); + return PlayerInsertEntityInWorld((ent, ent.Comp), player, toInsert.Value); } /// /// Inserts an Entity () in the world into storage, informing if it fails. - /// is *NOT* held, see . + /// is *NOT* held, see . /// /// /// The player to insert an entity with diff --git a/Content.Shared/Storage/EntitySystems/StoreAfterFailedInteractSystem.cs b/Content.Shared/Storage/EntitySystems/StoreAfterFailedInteractSystem.cs new file mode 100644 index 00000000000..77f72806f4b --- /dev/null +++ b/Content.Shared/Storage/EntitySystems/StoreAfterFailedInteractSystem.cs @@ -0,0 +1,21 @@ +using Content.Shared.Storage.Components; +using Content.Shared.Storage.Events; + +namespace Content.Shared.Storage.EntitySystems; + +public sealed class StoreAfterFailedInteractSystem : EntitySystem +{ + [Dependency] private readonly SharedStorageSystem _storage = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStorageInsertFailed); + } + + private void OnStorageInsertFailed(Entity ent, ref StorageInsertFailedEvent args) + { + _storage.PlayerInsertHeldEntity(args.Storage, args.Player); + } +} diff --git a/Content.Shared/Storage/Events/StorageInsertFailedEvent.cs b/Content.Shared/Storage/Events/StorageInsertFailedEvent.cs new file mode 100644 index 00000000000..50f759e43fd --- /dev/null +++ b/Content.Shared/Storage/Events/StorageInsertFailedEvent.cs @@ -0,0 +1,6 @@ +using Content.Shared.Hands.Components; + +namespace Content.Shared.Storage.Events; + +[ByRefEvent] +public record struct StorageInsertFailedEvent(Entity Storage, Entity Player); From 89c061be103501c25c3ce1d58c9db683d24854fd Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Wed, 29 May 2024 23:46:22 -0700 Subject: [PATCH 010/366] meow --- .../Components/ApcPowerReceiverComponent.cs | 8 +++ .../EntitySystems/PowerReceiverSystem.cs | 23 ++++++++ .../Components/ApcPowerReceiverComponent.cs | 18 +++---- .../Power/EntitySystems/PowerNetSystem.cs | 21 +++++--- .../EntitySystems/PowerReceiverSystem.cs | 52 ++++++------------- .../ApcPowerReceiverComponentState.cs | 9 ++++ .../SharedApcPowerReceiverComponent.cs | 10 ++++ .../SharedPowerReceiverSystem.cs | 6 +++ 8 files changed, 92 insertions(+), 55 deletions(-) create mode 100644 Content.Client/Power/Components/ApcPowerReceiverComponent.cs create mode 100644 Content.Client/Power/EntitySystems/PowerReceiverSystem.cs create mode 100644 Content.Shared/Power/Components/ApcPowerReceiverComponentState.cs create mode 100644 Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs create mode 100644 Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs diff --git a/Content.Client/Power/Components/ApcPowerReceiverComponent.cs b/Content.Client/Power/Components/ApcPowerReceiverComponent.cs new file mode 100644 index 00000000000..fbebcb7cf83 --- /dev/null +++ b/Content.Client/Power/Components/ApcPowerReceiverComponent.cs @@ -0,0 +1,8 @@ +using Content.Shared.Power.Components; + +namespace Content.Client.Power.Components; + +[RegisterComponent] +public sealed partial class ApcPowerReceiverComponent : SharedApcPowerReceiverComponent +{ +} diff --git a/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs new file mode 100644 index 00000000000..4d56592c232 --- /dev/null +++ b/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs @@ -0,0 +1,23 @@ +using Content.Client.Power.Components; +using Content.Shared.Power.Components; +using Content.Shared.Power.EntitySystems; +using Robust.Shared.GameStates; + +namespace Content.Client.Power.EntitySystems; + +public sealed class PowerReceiverSystem : SharedPowerReceiverSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnHandleState); + } + + private void OnHandleState(EntityUid uid, ApcPowerReceiverComponent component, ref ComponentHandleState args) + { + if (args.Current is not ApcPowerReceiverComponentState state) + return; + + component.Powered = state.Powered; + } +} diff --git a/Content.Server/Power/Components/ApcPowerReceiverComponent.cs b/Content.Server/Power/Components/ApcPowerReceiverComponent.cs index b8340d0be4b..9a68e2aabb8 100644 --- a/Content.Server/Power/Components/ApcPowerReceiverComponent.cs +++ b/Content.Server/Power/Components/ApcPowerReceiverComponent.cs @@ -1,5 +1,6 @@ using Content.Server.Power.NodeGroups; using Content.Server.Power.Pow3r; +using Content.Shared.Power.Components; namespace Content.Server.Power.Components { @@ -8,11 +9,8 @@ namespace Content.Server.Power.Components /// so that it can receive power from a . /// [RegisterComponent] - public sealed partial class ApcPowerReceiverComponent : Component + public sealed partial class ApcPowerReceiverComponent : SharedApcPowerReceiverComponent { - [ViewVariables] - public bool Powered => (MathHelper.CloseToPercent(NetworkLoad.ReceivingPower, Load) || !NeedsPower) && !PowerDisabled; - /// /// Amount of charge this needs from an APC per second to function. /// @@ -33,7 +31,7 @@ public bool NeedsPower { _needsPower = value; // Reset this so next tick will do a power update. - PoweredLastUpdate = null; + Recalculate = true; } } @@ -50,7 +48,8 @@ public bool PowerDisabled { set => NetworkLoad.Enabled = !value; } - public bool? PoweredLastUpdate; + // TODO Is this needed? It forces a PowerChangedEvent when NeedsPower is toggled even if it changes to the same state. + public bool Recalculate; [ViewVariables] public PowerState.Load NetworkLoad { get; } = new PowerState.Load @@ -66,10 +65,5 @@ public bool PowerDisabled { /// Does nothing on the client. /// [ByRefEvent] - public readonly record struct PowerChangedEvent(bool Powered, float ReceivingPower) - { - public readonly bool Powered = Powered; - public readonly float ReceivingPower = ReceivingPower; - } - + public readonly record struct PowerChangedEvent(bool Powered, float ReceivingPower); } diff --git a/Content.Server/Power/EntitySystems/PowerNetSystem.cs b/Content.Server/Power/EntitySystems/PowerNetSystem.cs index 2d80c810e2e..6c35ba20083 100644 --- a/Content.Server/Power/EntitySystems/PowerNetSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerNetSystem.cs @@ -22,6 +22,7 @@ public sealed class PowerNetSystem : EntitySystem [Dependency] private readonly PowerNetConnectorSystem _powerNetConnector = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IParallelManager _parMan = default!; + [Dependency] private readonly PowerReceiverSystem _powerReceiver = default!; private readonly PowerState _powerState = new(); private readonly HashSet _powerNetReconnectQueue = new(); @@ -313,19 +314,27 @@ private void UpdateApcPowerReceiver() var enumerator = AllEntityQuery(); while (enumerator.MoveNext(out var uid, out var apcReceiver)) { - var powered = apcReceiver.Powered; - if (powered == apcReceiver.PoweredLastUpdate) + var powered = !apcReceiver.PowerDisabled + && (!apcReceiver.NeedsPower + || MathHelper.CloseToPercent(apcReceiver.NetworkLoad.ReceivingPower, + apcReceiver.Load)); + + // If new value is the same as the old, then exit + if (!apcReceiver.Recalculate && apcReceiver.Powered == powered) continue; - if (metaQuery.GetComponent(uid).EntityPaused) + var metadata = metaQuery.Comp(uid); + if (metadata.EntityPaused) continue; - apcReceiver.PoweredLastUpdate = powered; - var ev = new PowerChangedEvent(apcReceiver.Powered, apcReceiver.NetworkLoad.ReceivingPower); + apcReceiver.Recalculate = false; + apcReceiver.Powered = powered; + Dirty(uid, apcReceiver, metadata); + var ev = new PowerChangedEvent(powered, apcReceiver.NetworkLoad.ReceivingPower); RaiseLocalEvent(uid, ref ev); - if (appearanceQuery.TryGetComponent(uid, out var appearance)) + if (appearanceQuery.TryComp(uid, out var appearance)) _appearance.SetData(uid, PowerDeviceVisuals.Powered, powered, appearance); } } diff --git a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs index 9ba30813dd5..0a5d475f27f 100644 --- a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs @@ -7,10 +7,13 @@ using Content.Shared.Examine; using Content.Shared.Hands.Components; using Content.Shared.Power; +using Content.Shared.Power.Components; +using Content.Shared.Power.EntitySystems; using Content.Shared.Verbs; using Robust.Server.Audio; using Robust.Server.GameObjects; using Robust.Shared.Audio; +using Robust.Shared.GameStates; using Robust.Shared.Utility; using Content.Shared.Emp; using Content.Shared.Interaction; @@ -18,7 +21,7 @@ namespace Content.Server.Power.EntitySystems { - public sealed class PowerReceiverSystem : EntitySystem + public sealed class PowerReceiverSystem : SharedPowerReceiverSystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminManager _adminManager = default!; @@ -43,8 +46,7 @@ public override void Initialize() SubscribeLocalEvent>(OnGetVerbs); SubscribeLocalEvent>(AddSwitchPowerVerb); - SubscribeLocalEvent(OnEmpPulse); - SubscribeLocalEvent(OnEmpEnd); + SubscribeLocalEvent(OnGetState); _recQuery = GetEntityQuery(); _provQuery = GetEntityQuery(); @@ -151,14 +153,18 @@ private void AddSwitchPowerVerb(EntityUid uid, PowerSwitchComponent component, G args.Verbs.Add(verb); } + private void OnGetState(EntityUid uid, ApcPowerReceiverComponent component, ref ComponentGetState args) + { + args.State = new ApcPowerReceiverComponentState + { + Powered = component.Powered + }; + } + private void ProviderChanged(Entity receiver) { var comp = receiver.Comp; comp.NetworkLoad.LinkedNetwork = default; - var ev = new PowerChangedEvent(comp.Powered, comp.NetworkLoad.ReceivingPower); - - RaiseLocalEvent(receiver, ref ev); - _appearance.SetData(receiver, PowerDeviceVisuals.Powered, comp.Powered); } /// @@ -166,12 +172,10 @@ private void ProviderChanged(Entity receiver) /// Otherwise, it returns 'true' because if something doesn't take power /// it's effectively always powered. /// + /// True when entity has no ApcPowerReceiverComponent or is Powered. False when not. public bool IsPowered(EntityUid uid, ApcPowerReceiverComponent? receiver = null) { - if (!_recQuery.Resolve(uid, ref receiver, false)) - return true; - - return receiver.Powered; + return !_recQuery.Resolve(uid, ref receiver, false) || receiver.Powered; } /// @@ -204,35 +208,9 @@ public bool TogglePower(EntityUid uid, bool playSwitchSound = true, ApcPowerRece return !receiver.PowerDisabled; // i.e. PowerEnabled } - public bool TryTogglePower(EntityUid uid, bool playSwitchSound = true, ApcPowerReceiverComponent? receiver = null, EntityUid? user = null) - { - if (HasComp(uid)) - return false; - - return TogglePower(uid, playSwitchSound, receiver, user); - } - public void SetLoad(ApcPowerReceiverComponent comp, float load) { comp.Load = load; } - - private void OnEmpPulse(EntityUid uid, ApcPowerReceiverComponent component, ref EmpPulseEvent args) - { - if (!component.PowerDisabled) - { - args.Affected = true; - args.Disabled = true; - TogglePower(uid, false); - } - } - - private void OnEmpEnd(EntityUid uid, ApcPowerReceiverComponent component, ref EmpDisabledRemoved args) - { - if (component.PowerDisabled) - { - TogglePower(uid, false); - } - } } } diff --git a/Content.Shared/Power/Components/ApcPowerReceiverComponentState.cs b/Content.Shared/Power/Components/ApcPowerReceiverComponentState.cs new file mode 100644 index 00000000000..9b18d6ad938 --- /dev/null +++ b/Content.Shared/Power/Components/ApcPowerReceiverComponentState.cs @@ -0,0 +1,9 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Power.Components; + +[Serializable, NetSerializable] +public sealed class ApcPowerReceiverComponentState : ComponentState +{ + public bool Powered; +} diff --git a/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs b/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs new file mode 100644 index 00000000000..d73993357a3 --- /dev/null +++ b/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Power.Components; + +[NetworkedComponent] +public abstract partial class SharedApcPowerReceiverComponent : Component +{ + [ViewVariables] + public bool Powered; +} diff --git a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs new file mode 100644 index 00000000000..be2a9dc3abc --- /dev/null +++ b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs @@ -0,0 +1,6 @@ +namespace Content.Shared.Power.EntitySystems; + +public abstract class SharedPowerReceiverSystem : EntitySystem +{ + +} From ee6f398e50457cb596765eccf55fcedc83f17fad Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Sun, 12 May 2024 15:07:54 +0000 Subject: [PATCH 011/366] Record deletion (#27883) * Allow for Station Records interface for aghosts to delete records * Fix record consoles not working when there are more than 2 crew members. HOW DID NOONE NOTICE THIS SOONER??? * Stop being unconventional --- .../StationRecords/GeneralRecord.xaml | 13 +++++ .../StationRecords/GeneralRecord.xaml.cs | 33 ++++++++++++ ...lStationRecordConsoleBoundUserInterface.cs | 1 + .../GeneralStationRecordConsoleWindow.xaml | 2 +- .../GeneralStationRecordConsoleWindow.xaml.cs | 51 +++---------------- .../GeneralStationRecordConsoleComponent.cs | 6 +++ .../GeneralStationRecordConsoleSystem.cs | 20 ++++++-- .../StationRecords/GeneralRecordsUi.cs | 23 +++++++-- .../general-station-records.ftl | 1 + .../Entities/Mobs/Player/admin_ghost.yml | 1 + 10 files changed, 100 insertions(+), 51 deletions(-) create mode 100644 Content.Client/StationRecords/GeneralRecord.xaml create mode 100644 Content.Client/StationRecords/GeneralRecord.xaml.cs diff --git a/Content.Client/StationRecords/GeneralRecord.xaml b/Content.Client/StationRecords/GeneralRecord.xaml new file mode 100644 index 00000000000..add688c3f38 --- /dev/null +++ b/Content.Client/StationRecords/GeneralRecord.xaml @@ -0,0 +1,13 @@ + + + [DataField] public StationRecordsFilter? Filter; + + /// + /// Whether this Records Console is able to delete entries. + /// + [DataField] + public bool CanDeleteEntries; } diff --git a/Content.Server/StationRecords/Systems/GeneralStationRecordConsoleSystem.cs b/Content.Server/StationRecords/Systems/GeneralStationRecordConsoleSystem.cs index a5202285d99..87246ab6757 100644 --- a/Content.Server/StationRecords/Systems/GeneralStationRecordConsoleSystem.cs +++ b/Content.Server/StationRecords/Systems/GeneralStationRecordConsoleSystem.cs @@ -23,9 +23,22 @@ public override void Initialize() subs.Event(UpdateUserInterface); subs.Event(OnKeySelected); subs.Event(OnFiltersChanged); + subs.Event(OnRecordDelete); }); } + private void OnRecordDelete(Entity ent, ref DeleteStationRecord args) + { + if (!ent.Comp.CanDeleteEntries) + return; + + var owning = _station.GetOwningStation(ent.Owner); + + if (owning != null) + _stationRecords.RemoveRecord(new StationRecordKey(args.Id, owning.Value)); + UpdateUserInterface(ent); // Apparently an event does not get raised for this. + } + private void UpdateUserInterface(Entity ent, ref T args) { UpdateUserInterface(ent); @@ -68,8 +81,9 @@ private void UpdateUserInterface(Entity en case 0: _ui.SetUiState(uid, GeneralStationRecordConsoleKey.Key, new GeneralStationRecordConsoleState()); return; - case 1: - console.ActiveKey = listing.Keys.First(); + default: + if (console.ActiveKey == null) + console.ActiveKey = listing.Keys.First(); break; } @@ -79,7 +93,7 @@ private void UpdateUserInterface(Entity en var key = new StationRecordKey(id, owningStation.Value); _stationRecords.TryGetRecord(key, out var record, stationRecords); - GeneralStationRecordConsoleState newState = new(id, record, listing, console.Filter); + GeneralStationRecordConsoleState newState = new(id, record, listing, console.Filter, ent.Comp.CanDeleteEntries); _ui.SetUiState(uid, GeneralStationRecordConsoleKey.Key, newState); } } diff --git a/Content.Shared/StationRecords/GeneralRecordsUi.cs b/Content.Shared/StationRecords/GeneralRecordsUi.cs index 860454efde5..2105c53df25 100644 --- a/Content.Shared/StationRecords/GeneralRecordsUi.cs +++ b/Content.Shared/StationRecords/GeneralRecordsUi.cs @@ -37,17 +37,22 @@ public sealed class GeneralStationRecordConsoleState : BoundUserInterfaceState public readonly GeneralStationRecord? Record; public readonly Dictionary? RecordListing; public readonly StationRecordsFilter? Filter; + public readonly bool CanDeleteEntries; - public GeneralStationRecordConsoleState(uint? key, GeneralStationRecord? record, - Dictionary? recordListing, StationRecordsFilter? newFilter) + public GeneralStationRecordConsoleState(uint? key, + GeneralStationRecord? record, + Dictionary? recordListing, + StationRecordsFilter? newFilter, + bool canDeleteEntries) { SelectedKey = key; Record = record; RecordListing = recordListing; Filter = newFilter; + CanDeleteEntries = canDeleteEntries; } - public GeneralStationRecordConsoleState() : this(null, null, null, null) + public GeneralStationRecordConsoleState() : this(null, null, null, null, false) { } @@ -69,3 +74,15 @@ public SelectStationRecord(uint? selectedKey) SelectedKey = selectedKey; } } + + +[Serializable, NetSerializable] +public sealed class DeleteStationRecord : BoundUserInterfaceMessage +{ + public DeleteStationRecord(uint id) + { + Id = id; + } + + public readonly uint Id; +} diff --git a/Resources/Locale/en-US/station-records/general-station-records.ftl b/Resources/Locale/en-US/station-records/general-station-records.ftl index e36bfac7cb3..4516a547f4d 100644 --- a/Resources/Locale/en-US/station-records/general-station-records.ftl +++ b/Resources/Locale/en-US/station-records/general-station-records.ftl @@ -16,3 +16,4 @@ general-station-record-prints-filter = Fingerprints general-station-record-dna-filter = DNA general-station-record-console-search-records = Search general-station-record-console-reset-filters = Reset +general-station-record-console-delete = Delete diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index c9aac35732b..1366dfb76ab 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -80,6 +80,7 @@ - type: CargoOrderConsole - type: CrewMonitoringConsole - type: GeneralStationRecordConsole + canDeleteEntries: true - type: DeviceNetwork deviceNetId: Wireless receiveFrequencyId: CrewMonitor From 47710b6e8e246da7732d7227f4c5313b8f0da3b9 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Tue, 18 Jun 2024 06:59:37 -0400 Subject: [PATCH 012/366] Implement vital chef's hat functionality (#25950) * Implement crucial chef's hat functionality * Unified stopping code and added events. * Added documentation to events * Rerun tests * Made review changes, and fixed potential desync bug. * Update whitelist --- .../Systems/PilotedByClothingSystem.cs | 19 ++ .../Components/PilotedByClothingComponent.cs | 12 ++ .../Components/PilotedClothingComponent.cs | 38 ++++ .../EntitySystems/PilotedClothingSystem.cs | 169 ++++++++++++++++++ .../Entities/Clothing/Head/hats.yml | 4 + .../Prototypes/Entities/Mobs/NPCs/animals.yml | 2 + Resources/Prototypes/tags.yml | 4 + 7 files changed, 248 insertions(+) create mode 100644 Content.Client/Clothing/Systems/PilotedByClothingSystem.cs create mode 100644 Content.Shared/Clothing/Components/PilotedByClothingComponent.cs create mode 100644 Content.Shared/Clothing/Components/PilotedClothingComponent.cs create mode 100644 Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs diff --git a/Content.Client/Clothing/Systems/PilotedByClothingSystem.cs b/Content.Client/Clothing/Systems/PilotedByClothingSystem.cs new file mode 100644 index 00000000000..c04cf0a60ba --- /dev/null +++ b/Content.Client/Clothing/Systems/PilotedByClothingSystem.cs @@ -0,0 +1,19 @@ +using Content.Shared.Clothing.Components; +using Robust.Client.Physics; + +namespace Content.Client.Clothing.Systems; + +public sealed partial class PilotedByClothingSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUpdatePredicted); + } + + private void OnUpdatePredicted(Entity entity, ref UpdateIsPredictedEvent args) + { + args.BlockPrediction = true; + } +} diff --git a/Content.Shared/Clothing/Components/PilotedByClothingComponent.cs b/Content.Shared/Clothing/Components/PilotedByClothingComponent.cs new file mode 100644 index 00000000000..cd4d0d62030 --- /dev/null +++ b/Content.Shared/Clothing/Components/PilotedByClothingComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Clothing.Components; + +/// +/// Disables client-side physics prediction for this entity. +/// Without this, movement with is very rubberbandy. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PilotedByClothingComponent : Component +{ +} diff --git a/Content.Shared/Clothing/Components/PilotedClothingComponent.cs b/Content.Shared/Clothing/Components/PilotedClothingComponent.cs new file mode 100644 index 00000000000..a349e4e485e --- /dev/null +++ b/Content.Shared/Clothing/Components/PilotedClothingComponent.cs @@ -0,0 +1,38 @@ +using Content.Shared.Whitelist; +using Robust.Shared.GameStates; + +namespace Content.Shared.Clothing.Components; + +/// +/// Allows an entity stored in this clothing item to pass inputs to the entity wearing it. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PilotedClothingComponent : Component +{ + /// + /// Whitelist for entities that are allowed to act as pilots when inside this entity. + /// + [DataField] + public EntityWhitelist? PilotWhitelist; + + /// + /// Should movement input be relayed from the pilot to the target? + /// + [DataField] + public bool RelayMovement = true; + + + /// + /// Reference to the entity contained in the clothing and acting as pilot. + /// + [DataField, AutoNetworkedField] + public EntityUid? Pilot; + + /// + /// Reference to the entity wearing this clothing who will be controlled by the pilot. + /// + [DataField, AutoNetworkedField] + public EntityUid? Wearer; + + public bool IsActive => Pilot != null && Wearer != null; +} diff --git a/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs new file mode 100644 index 00000000000..49df7aee943 --- /dev/null +++ b/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs @@ -0,0 +1,169 @@ +using Content.Shared.Clothing.Components; +using Content.Shared.Inventory.Events; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.Storage; +using Content.Shared.Whitelist; +using Robust.Shared.Containers; +using Robust.Shared.Timing; + +namespace Content.Shared.Clothing.EntitySystems; + +public sealed partial class PilotedClothingSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedMoverController _moverController = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnEntInserted); + SubscribeLocalEvent(OnEntRemoved); + SubscribeLocalEvent(OnEquipped); + SubscribeLocalEvent(OnUnequipped); + } + + private void OnEntInserted(Entity entity, ref EntInsertedIntoContainerMessage args) + { + // Make sure the entity was actually inserted into storage and not a different container. + if (!TryComp(entity, out StorageComponent? storage) || args.Container != storage.Container) + return; + + // Check potential pilot against whitelist, if one exists. + if (_whitelist.IsWhitelistFail(entity.Comp.PilotWhitelist, args.Entity)) + return; + + entity.Comp.Pilot = args.Entity; + Dirty(entity); + + // Attempt to setup control link, if Pilot and Wearer are both present. + StartPiloting(entity); + } + + private void OnEntRemoved(Entity entity, ref EntRemovedFromContainerMessage args) + { + // Make sure the removed entity is actually the pilot. + if (args.Entity != entity.Comp.Pilot) + return; + + StopPiloting(entity); + entity.Comp.Pilot = null; + Dirty(entity); + } + + private void OnEquipped(Entity entity, ref GotEquippedEvent args) + { + if (!TryComp(entity, out ClothingComponent? clothing)) + return; + + // Make sure the clothing item was equipped to the right slot, and not just held in a hand. + var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE; + if (!isCorrectSlot) + return; + + entity.Comp.Wearer = args.Equipee; + Dirty(entity); + + // Attempt to setup control link, if Pilot and Wearer are both present. + StartPiloting(entity); + } + + private void OnUnequipped(Entity entity, ref GotUnequippedEvent args) + { + StopPiloting(entity); + + entity.Comp.Wearer = null; + Dirty(entity); + } + + /// + /// Attempts to establish movement/interaction relay connection(s) from Pilot to Wearer. + /// If either is missing, fails and returns false. + /// + private bool StartPiloting(Entity entity) + { + // Make sure we have both a Pilot and a Wearer + if (entity.Comp.Pilot == null || entity.Comp.Wearer == null) + return false; + + if (!_timing.IsFirstTimePredicted) + return false; + + var pilotEnt = entity.Comp.Pilot.Value; + var wearerEnt = entity.Comp.Wearer.Value; + + // Add component to block prediction of wearer + EnsureComp(wearerEnt); + + if (entity.Comp.RelayMovement) + { + // Establish movement input relay. + _moverController.SetRelay(pilotEnt, wearerEnt); + } + + var pilotEv = new StartedPilotingClothingEvent(entity, wearerEnt); + RaiseLocalEvent(pilotEnt, ref pilotEv); + + var wearerEv = new StartingBeingPilotedByClothing(entity, pilotEnt); + RaiseLocalEvent(wearerEnt, ref wearerEv); + + return true; + } + + /// + /// Removes components from the Pilot and Wearer to stop the control relay. + /// Returns false if a connection does not already exist. + /// + private bool StopPiloting(Entity entity) + { + if (entity.Comp.Pilot == null || entity.Comp.Wearer == null) + return false; + + // Clean up components on the Pilot + var pilotEnt = entity.Comp.Pilot.Value; + RemCompDeferred(pilotEnt); + + // Clean up components on the Wearer + var wearerEnt = entity.Comp.Wearer.Value; + RemCompDeferred(wearerEnt); + RemCompDeferred(wearerEnt); + + // Raise an event on the Pilot + var pilotEv = new StoppedPilotingClothingEvent(entity, wearerEnt); + RaiseLocalEvent(pilotEnt, ref pilotEv); + + // Raise an event on the Wearer + var wearerEv = new StoppedBeingPilotedByClothing(entity, pilotEnt); + RaiseLocalEvent(wearerEnt, ref wearerEv); + + return true; + } +} + +/// +/// Raised on the Pilot when they gain control of the Wearer. +/// +[ByRefEvent] +public record struct StartedPilotingClothingEvent(EntityUid Clothing, EntityUid Wearer); + +/// +/// Raised on the Pilot when they lose control of the Wearer, +/// due to the Pilot exiting the clothing or the clothing being unequipped by the Wearer. +/// +[ByRefEvent] +public record struct StoppedPilotingClothingEvent(EntityUid Clothing, EntityUid Wearer); + +/// +/// Raised on the Wearer when the Pilot gains control of them. +/// +[ByRefEvent] +public record struct StartingBeingPilotedByClothing(EntityUid Clothing, EntityUid Pilot); + +/// +/// Raised on the Wearer when the Pilot loses control of them +/// due to the Pilot exiting the clothing or the clothing being unequipped by the Wearer. +/// +[ByRefEvent] +public record struct StoppedBeingPilotedByClothing(EntityUid Clothing, EntityUid Pilot); diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index 5b5d281362d..6bba9320e8b 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -252,6 +252,10 @@ - type: ContainerContainer containers: storagebase: !type:Container + - type: PilotedClothing + pilotWhitelist: + tags: + - ChefPilot - type: Tag tags: - ClothMade diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 6e369389a90..5aa41301d77 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1726,6 +1726,7 @@ tags: - Trash - VimPilot + - ChefPilot - Mouse - Meat - type: Respirator @@ -3374,6 +3375,7 @@ - type: Tag tags: - VimPilot + - ChefPilot - Trash - Hamster - Meat diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index e7f20f8f47b..415f9369dc7 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -371,6 +371,10 @@ - type: Tag id: Chicken +# Allowed to control someone wearing a Chef's hat if inside their hat. +- type: Tag + id: ChefPilot + - type: Tag id: ChemDispensable # container that can go into the chem dispenser From c5f663bf5567865be921d501af25b74d3393b572 Mon Sep 17 00:00:00 2001 From: Mervill Date: Mon, 29 Jul 2024 00:28:17 -0700 Subject: [PATCH 013/366] Make the powered examine text fully client predicted (#30441) * Make the powered examine text fully client predicted * switch to using the Entity API for the examine event --- .../Power/EntitySystems/PowerReceiverSystem.cs | 9 ++++++++- .../Power/EntitySystems/PowerReceiverSystem.cs | 16 +++++----------- .../EntitySystems/SharedPowerReceiverSystem.cs | 13 +++++++++++-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs index 4d56592c232..61e20f751ca 100644 --- a/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs +++ b/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs @@ -1,6 +1,7 @@ -using Content.Client.Power.Components; +using Content.Client.Power.Components; using Content.Shared.Power.Components; using Content.Shared.Power.EntitySystems; +using Content.Shared.Examine; using Robust.Shared.GameStates; namespace Content.Client.Power.EntitySystems; @@ -10,9 +11,15 @@ public sealed class PowerReceiverSystem : SharedPowerReceiverSystem public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnHandleState); } + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + args.PushMarkup(GetExamineText(ent.Comp.Powered)); + } + private void OnHandleState(EntityUid uid, ApcPowerReceiverComponent component, ref ComponentHandleState args) { if (args.Current is not ApcPowerReceiverComponentState state) diff --git a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs index 0a5d475f27f..a314fe0ca11 100644 --- a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs @@ -52,6 +52,11 @@ public override void Initialize() _provQuery = GetEntityQuery(); } + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + args.PushMarkup(GetExamineText(ent.Comp.Powered)); + } + private void OnGetVerbs(EntityUid uid, ApcPowerReceiverComponent component, GetVerbsEvent args) { if (!_adminManager.HasAdminFlag(args.User, AdminFlags.Admin)) @@ -67,17 +72,6 @@ private void OnGetVerbs(EntityUid uid, ApcPowerReceiverComponent component, GetV }); } - /// - ///Adds some markup to the examine text of whatever object is using this component to tell you if it's powered or not, even if it doesn't have an icon state to do this for you. - /// - private void OnExamined(EntityUid uid, ApcPowerReceiverComponent component, ExaminedEvent args) - { - args.PushMarkup(Loc.GetString("power-receiver-component-on-examine-main", - ("stateText", Loc.GetString( component.Powered - ? "power-receiver-component-on-examine-powered" - : "power-receiver-component-on-examine-unpowered")))); - } - private void OnProviderShutdown(EntityUid uid, ApcPowerProviderComponent component, ComponentShutdown args) { foreach (var receiver in component.LinkedReceivers) diff --git a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs index be2a9dc3abc..37ac7518896 100644 --- a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs +++ b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs @@ -1,6 +1,15 @@ -namespace Content.Shared.Power.EntitySystems; +using Content.Shared.Examine; +using Content.Shared.Power.Components; + +namespace Content.Shared.Power.EntitySystems; public abstract class SharedPowerReceiverSystem : EntitySystem { - + protected string GetExamineText(bool powered) + { + return Loc.GetString("power-receiver-component-on-examine-main", + ("stateText", Loc.GetString(powered + ? "power-receiver-component-on-examine-powered" + : "power-receiver-component-on-examine-unpowered"))); + } } From 9824a9ea1bc09248a60d3645cc61b94f0cb33a30 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 25 Aug 2024 22:18:42 +1000 Subject: [PATCH 014/366] Power stuff (#31314) * Power stuff - Add shared IsPowered - Add shared ResolveApc - Move PowerChangedEvent to shared for now - Add SlimPoweredLight that actually functions how you'd expect a PoweredLight to function it id didn't have a bunch of bloat on it. * big update * boing --- .../UI/BanList/BanListWindow.xaml.cs | 1 + .../UI/Bwoink/BwoinkControl.xaml.cs | 3 +- .../UI/SetOutfit/SetOutfitMenu.xaml.cs | 1 + .../Cartridges/CrewManifestUiFragment.xaml.cs | 3 +- .../UI/FlatpackCreatorMenu.xaml.cs | 1 + .../CrewManifest/CrewManifestUi.xaml.cs | 1 + .../UI/LobbyCharacterPreviewPanel.xaml.cs | 1 + Content.Client/Lobby/UI/LobbyGui.xaml.cs | 1 + .../UI/SignalTimerWindow.xaml.cs | 1 + Content.Client/Options/UI/OptionsMenu.xaml.cs | 1 + .../EntitySystems/PowerReceiverSystem.cs | 13 ++++ .../Power/Generator/GeneratorWindow.xaml.cs | 1 + Content.Client/Radio/Ui/IntercomMenu.xaml.cs | 1 + .../StationRecords/GeneralRecord.xaml.cs | 1 + .../Ui/AnalysisConsoleMenu.xaml.cs | 1 + .../AlertLevel/AlertLevelDisplaySystem.cs | 1 + .../Ame/EntitySystems/AmeControllerSystem.cs | 1 + .../Anomaly/AnomalySynchronizerSystem.cs | 1 + .../Anomaly/AnomalySystem.Generator.cs | 1 + .../Arcade/BlockGame/BlockGameArcadeSystem.cs | 1 + .../SpaceVillainArcadeSystem.cs | 1 + .../Atmos/Monitor/Systems/AirAlarmSystem.cs | 1 + .../Monitor/Systems/AtmosAlarmableSystem.cs | 1 + .../Monitor/Systems/AtmosMonitoringSystem.cs | 1 + .../Unary/EntitySystems/GasVentPumpSystem.cs | 1 + .../EntitySystems/GasVentScrubberSystem.cs | 1 + .../Atmos/Portable/PortableScrubberSystem.cs | 1 + .../Atmos/Portable/SpaceHeaterSystem.cs | 1 + Content.Server/Audio/AmbientSoundSystem.cs | 1 + Content.Server/Audio/Jukebox/JukeboxSystem.cs | 1 + Content.Server/Bed/BedSystem.cs | 1 + .../Buckle/Systems/AntiRotOnBuckleSystem.cs | 1 + .../Cargo/Systems/CargoSystem.Telepad.cs | 1 + .../SolutionContainerMixerSystem.cs | 1 + .../EntitySystems/SolutionHeaterSystem.cs | 1 + .../Cloning/CloningConsoleSystem.cs | 1 + .../ConstructionSystem.Computer.cs | 1 + Content.Server/Construction/FlatpackSystem.cs | 2 + .../DeviceNetworkRequiresPowerSystem.cs | 1 + .../Systems/SingletonDeviceNetServerSystem.cs | 1 + .../Unit/EntitySystems/DisposalUnitSystem.cs | 2 +- Content.Server/Doors/Systems/AirlockSystem.cs | 1 + Content.Server/Doors/Systems/DoorSystem.cs | 2 +- .../Doors/Systems/FirelockSystem.cs | 1 + Content.Server/Fax/FaxSystem.cs | 2 + .../EntitySystems/ReagentGrinderSystem.cs | 1 + Content.Server/Lathe/LatheSystem.cs | 1 + .../EntitySystems/EmergencyLightSystem.cs | 2 + .../Light/EntitySystems/LitOnPoweredSystem.cs | 2 + .../Light/EntitySystems/PoweredLightSystem.cs | 4 ++ .../Materials/MaterialReclaimerSystem.cs | 4 ++ .../BiomassReclaimerSystem.cs | 1 + Content.Server/Medical/CryoPodSystem.cs | 1 + .../EntitySystems/FatExtractorSystem.cs | 1 + .../Paper/PaperRandomStoryComponent.cs | 4 ++ .../ParticleAcceleratorSystem.ControlBox.cs | 1 + .../Physics/Controllers/ConveyorController.cs | 1 + .../Components/ApcPowerReceiverComponent.cs | 7 -- .../Power/Components/CableComponent.cs | 9 +-- .../ActivatableUIRequiresPowerSystem.cs | 3 + .../EntitySystems/CableMultitoolSystem.cs | 5 +- .../Power/EntitySystems/ChargerSystem.cs | 1 + .../Power/EntitySystems/PowerNetSystem.cs | 1 + .../EntitySystems/PowerReceiverSystem.cs | 13 ++++ .../Power/Generation/Teg/TegSystem.cs | 2 + .../Power/Generator/GasPowerReceiverSystem.cs | 2 + .../Radio/EntitySystems/RadioDeviceSystem.cs | 1 + .../Shuttles/Systems/ShuttleConsoleSystem.cs | 1 + .../Shuttles/Systems/ThrusterSystem.cs | 2 + .../EntitySystems/EmitterSystem.cs | 1 + .../Sound/SpamEmitSoundRequirePowerSystem.cs | 2 + .../SurveillanceCameraMonitorSystem.cs | 1 + .../Systems/SurveillanceCameraRouterSystem.cs | 1 + .../Systems/SurveillanceCameraSystem.cs | 1 + .../Temperature/Systems/EntityHeaterSystem.cs | 1 + .../VendingMachines/VendingMachineSystem.cs | 1 + Content.Server/Wires/WiresSystem.cs | 1 + .../Systems/ArtifactAnalyzerSystem.cs | 1 + .../Systems/ArtifactCrusherSystem.cs | 1 + .../Cabinet/ItemCabinetComponent.cs | 1 - .../Components/PilotedByClothingComponent.cs | 1 + .../Components/ToggleClothingComponent.cs | 2 + .../Components/SlimPoweredLightComponent.cs | 17 +++++ .../EntitySystems/SlimPoweredLightSystem.cs | 67 +++++++++++++++++++ .../UnpoweredFlashlightSystem.cs | 2 + .../SharedPowerReceiverSystem.cs | 11 +++ Content.Shared/Power/PowerChangedEvent.cs | 8 +++ .../PowerCell/PowerCellDrawComponent.cs | 1 + .../Tools/Systems/SharedToolSystem.cs | 3 + 89 files changed, 240 insertions(+), 19 deletions(-) create mode 100644 Content.Shared/Light/Components/SlimPoweredLightComponent.cs create mode 100644 Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs create mode 100644 Content.Shared/Power/PowerChangedEvent.cs diff --git a/Content.Client/Administration/UI/BanList/BanListWindow.xaml.cs b/Content.Client/Administration/UI/BanList/BanListWindow.xaml.cs index c95f8f204dd..fad55f96273 100644 --- a/Content.Client/Administration/UI/BanList/BanListWindow.xaml.cs +++ b/Content.Client/Administration/UI/BanList/BanListWindow.xaml.cs @@ -1,4 +1,5 @@ using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; diff --git a/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs b/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs index af977f763c6..6ffb7147661 100644 --- a/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs +++ b/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs @@ -11,9 +11,8 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Network; -using Robust.Shared.Utility; -using Robust.Shared.Timing; using Robust.Shared.Configuration; +using Robust.Shared.Utility; namespace Content.Client.Administration.UI.Bwoink { diff --git a/Content.Client/Administration/UI/SetOutfit/SetOutfitMenu.xaml.cs b/Content.Client/Administration/UI/SetOutfit/SetOutfitMenu.xaml.cs index a2faf208d92..7cb32b43df5 100644 --- a/Content.Client/Administration/UI/SetOutfit/SetOutfitMenu.xaml.cs +++ b/Content.Client/Administration/UI/SetOutfit/SetOutfitMenu.xaml.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Client.UserInterface.Controls; using Content.Shared.Roles; using Robust.Client.AutoGenerated; using Robust.Client.Console; diff --git a/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs b/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs index 273707cb6ea..27ddd51815e 100644 --- a/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs +++ b/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs @@ -1,4 +1,5 @@ -using Content.Shared.CrewManifest; +using Content.Client.CrewManifest.UI; +using Content.Shared.CrewManifest; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; diff --git a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs index ad19bc30f42..fcf0ada947c 100644 --- a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs +++ b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Client.Materials; +using Content.Client.Materials.UI; using Content.Client.Message; using Content.Client.UserInterface.Controls; using Content.Shared.Construction.Components; diff --git a/Content.Client/CrewManifest/CrewManifestUi.xaml.cs b/Content.Client/CrewManifest/CrewManifestUi.xaml.cs index 4183c908141..f07e54eb65b 100644 --- a/Content.Client/CrewManifest/CrewManifestUi.xaml.cs +++ b/Content.Client/CrewManifest/CrewManifestUi.xaml.cs @@ -1,3 +1,4 @@ +using Content.Client.CrewManifest.UI; using Content.Shared.CrewManifest; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.CustomControls; diff --git a/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml.cs b/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml.cs index 14709f8b1f0..619cac68391 100644 --- a/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml.cs +++ b/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Client.UserInterface.Controls; +using Prometheus; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; diff --git a/Content.Client/Lobby/UI/LobbyGui.xaml.cs b/Content.Client/Lobby/UI/LobbyGui.xaml.cs index 3ab2caad786..d19643f20a9 100644 --- a/Content.Client/Lobby/UI/LobbyGui.xaml.cs +++ b/Content.Client/Lobby/UI/LobbyGui.xaml.cs @@ -3,6 +3,7 @@ using Content.Client.UserInterface.Systems.EscapeMenu; using Robust.Client.AutoGenerated; using Robust.Client.Console; +using Robust.Client.State; using Robust.Client.UserInterface; using Robust.Client.UserInterface.XAML; diff --git a/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs b/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs index b62595595e5..fbaaf5624b4 100644 --- a/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs +++ b/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs @@ -3,6 +3,7 @@ using Robust.Client.UserInterface.XAML; using Robust.Shared.Timing; using Content.Client.TextScreen; +using Robust.Client.UserInterface.Controls; namespace Content.Client.MachineLinking.UI; diff --git a/Content.Client/Options/UI/OptionsMenu.xaml.cs b/Content.Client/Options/UI/OptionsMenu.xaml.cs index c3a8e664705..c03e50dda00 100644 --- a/Content.Client/Options/UI/OptionsMenu.xaml.cs +++ b/Content.Client/Options/UI/OptionsMenu.xaml.cs @@ -1,3 +1,4 @@ +using Content.Client.Options.UI.Tabs; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; diff --git a/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs index 61e20f751ca..ebf6c18c953 100644 --- a/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs +++ b/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Client.Power.Components; using Content.Shared.Power.Components; using Content.Shared.Power.EntitySystems; @@ -27,4 +28,16 @@ private void OnHandleState(EntityUid uid, ApcPowerReceiverComponent component, r component.Powered = state.Powered; } + + public override bool ResolveApc(EntityUid entity, [NotNullWhen(true)] ref SharedApcPowerReceiverComponent? component) + { + if (component != null) + return true; + + if (!TryComp(entity, out ApcPowerReceiverComponent? receiver)) + return false; + + component = receiver; + return true; + } } diff --git a/Content.Client/Power/Generator/GeneratorWindow.xaml.cs b/Content.Client/Power/Generator/GeneratorWindow.xaml.cs index bd5b75de1da..edd2d7e11b0 100644 --- a/Content.Client/Power/Generator/GeneratorWindow.xaml.cs +++ b/Content.Client/Power/Generator/GeneratorWindow.xaml.cs @@ -3,6 +3,7 @@ using Content.Shared.Power.Generator; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.XAML; +using Robust.Shared.Network; namespace Content.Client.Power.Generator; diff --git a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs index 8b4b38753c1..ea040d1e44c 100644 --- a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs +++ b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs @@ -1,3 +1,4 @@ +using System.Threading.Channels; using Content.Client.UserInterface.Controls; using Content.Shared.Radio; using Robust.Client.AutoGenerated; diff --git a/Content.Client/StationRecords/GeneralRecord.xaml.cs b/Content.Client/StationRecords/GeneralRecord.xaml.cs index 6a2622fba9e..e84c2dc0982 100644 --- a/Content.Client/StationRecords/GeneralRecord.xaml.cs +++ b/Content.Client/StationRecords/GeneralRecord.xaml.cs @@ -2,6 +2,7 @@ using Robust.Client.AutoGenerated; using Robust.Client.UserInterface; using Robust.Client.UserInterface.XAML; +using Robust.Shared.Enums; namespace Content.Client.StationRecords; diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs index 2890bb3dbf7..2723db1efbf 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs @@ -1,6 +1,7 @@ using Content.Client.Stylesheets; using Content.Client.UserInterface.Controls; using Content.Shared.Xenoarchaeology.Equipment; +using Microsoft.VisualBasic; using Robust.Client.AutoGenerated; using Robust.Client.GameObjects; using Robust.Client.UserInterface.Controls; diff --git a/Content.Server/AlertLevel/AlertLevelDisplaySystem.cs b/Content.Server/AlertLevel/AlertLevelDisplaySystem.cs index 4f2108748b8..3dd216c5dce 100644 --- a/Content.Server/AlertLevel/AlertLevelDisplaySystem.cs +++ b/Content.Server/AlertLevel/AlertLevelDisplaySystem.cs @@ -1,6 +1,7 @@ using Content.Server.Power.Components; using Content.Server.Station.Systems; using Content.Shared.AlertLevel; +using Content.Shared.Power; namespace Content.Server.AlertLevel; diff --git a/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs b/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs index 1b323d66437..98f33b84244 100644 --- a/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs +++ b/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.Mind.Components; +using Content.Shared.Power; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; diff --git a/Content.Server/Anomaly/AnomalySynchronizerSystem.cs b/Content.Server/Anomaly/AnomalySynchronizerSystem.cs index 434a3fef66b..170676e08ca 100644 --- a/Content.Server/Anomaly/AnomalySynchronizerSystem.cs +++ b/Content.Server/Anomaly/AnomalySynchronizerSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Popups; +using Content.Shared.Power; using Robust.Shared.Audio.Systems; using Content.Shared.Verbs; diff --git a/Content.Server/Anomaly/AnomalySystem.Generator.cs b/Content.Server/Anomaly/AnomalySystem.Generator.cs index 056a985cbe2..6eb84b94418 100644 --- a/Content.Server/Anomaly/AnomalySystem.Generator.cs +++ b/Content.Server/Anomaly/AnomalySystem.Generator.cs @@ -13,6 +13,7 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Map; using System.Numerics; +using Content.Shared.Power; using Robust.Server.GameObjects; namespace Content.Server.Anomaly; diff --git a/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs b/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs index 561cad8d7ee..b0bf3895092 100644 --- a/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs +++ b/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Advertise; using Content.Server.Advertise.Components; using Content.Shared.Arcade; +using Content.Shared.Power; using Robust.Server.GameObjects; using Robust.Shared.Player; diff --git a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs index 07a4d044cab..624f4cac3e8 100644 --- a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs +++ b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Advertise; using Content.Server.Advertise.Components; using Content.Shared.Mood; +using Content.Shared.Power; using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent; using Robust.Server.GameObjects; using Robust.Shared.Audio; diff --git a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs index 2f56142aa60..7ac4c653d3f 100644 --- a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs @@ -18,6 +18,7 @@ using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork.Systems; using Content.Shared.Interaction; +using Content.Shared.Power; using Content.Shared.Wires; using Robust.Server.GameObjects; using Robust.Shared.Player; diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs index 2875d4a3d5d..b9ab1098977 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs @@ -7,6 +7,7 @@ using Content.Server.Power.Components; using Content.Shared.Atmos.Monitor; using Content.Shared.DeviceNetwork; +using Content.Shared.Power; using Content.Shared.Tag; using Robust.Server.Audio; using Robust.Server.GameObjects; diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs index c1a5256fdd5..2c9a3587559 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Atmos; using Content.Shared.Atmos.Monitor; using Content.Shared.DeviceNetwork; +using Content.Shared.Power; using Content.Shared.Tag; using Robust.Shared.Prototypes; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs index 7c12cf3f77f..0921a763d5a 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs @@ -18,6 +18,7 @@ using Content.Shared.Audio; using Content.Shared.DeviceNetwork; using Content.Shared.Examine; +using Content.Shared.Power; using Content.Shared.Tools.Systems; using JetBrains.Annotations; using Robust.Server.GameObjects; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs index b27689ed586..a35cf6c2e30 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Audio; using Content.Shared.DeviceNetwork; +using Content.Shared.Power; using Content.Shared.Tools.Systems; using JetBrains.Annotations; using Robust.Server.GameObjects; diff --git a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs index fbe40deedb1..7dc141f8753 100644 --- a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs +++ b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs @@ -16,6 +16,7 @@ using Content.Server.NodeContainer.EntitySystems; using Content.Shared.Atmos; using Content.Shared.Database; +using Content.Shared.Power; namespace Content.Server.Atmos.Portable { diff --git a/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs b/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs index 70eea2d9b78..b3ad5bbdb37 100644 --- a/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs +++ b/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Power.EntitySystems; using Content.Shared.Atmos.Piping.Portable.Components; using Content.Shared.Atmos.Visuals; +using Content.Shared.Power; using Content.Shared.UserInterface; using Robust.Server.GameObjects; diff --git a/Content.Server/Audio/AmbientSoundSystem.cs b/Content.Server/Audio/AmbientSoundSystem.cs index e78970d1243..1f4abf34f77 100644 --- a/Content.Server/Audio/AmbientSoundSystem.cs +++ b/Content.Server/Audio/AmbientSoundSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Power.EntitySystems; using Content.Shared.Audio; using Content.Shared.Mobs; +using Content.Shared.Power; namespace Content.Server.Audio; diff --git a/Content.Server/Audio/Jukebox/JukeboxSystem.cs b/Content.Server/Audio/Jukebox/JukeboxSystem.cs index cc9235e3d7a..3535f6b2382 100644 --- a/Content.Server/Audio/Jukebox/JukeboxSystem.cs +++ b/Content.Server/Audio/Jukebox/JukeboxSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Audio.Jukebox; +using Content.Shared.Power; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Components; diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs index 089ce322366..2335859f0b8 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -13,6 +13,7 @@ using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; using Content.Shared.Mobs.Systems; +using Content.Shared.Power; using Robust.Shared.Timing; using Content.Shared.Silicon.Components; // I shouldn't have to modify this. using Robust.Shared.Utility; diff --git a/Content.Server/Buckle/Systems/AntiRotOnBuckleSystem.cs b/Content.Server/Buckle/Systems/AntiRotOnBuckleSystem.cs index 4458b020a11..6f356baf073 100644 --- a/Content.Server/Buckle/Systems/AntiRotOnBuckleSystem.cs +++ b/Content.Server/Buckle/Systems/AntiRotOnBuckleSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Power.Components; using Content.Shared.Atmos.Rotting; using Content.Shared.Buckle.Components; +using Content.Shared.Power; namespace Content.Server.Buckle.Systems; diff --git a/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs b/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs index 223dd2ac329..a78b6b1b25f 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs @@ -8,6 +8,7 @@ using Content.Shared.Cargo; using Content.Shared.Cargo.Components; using Content.Shared.DeviceLinking; +using Content.Shared.Power; using Robust.Shared.Audio; using Robust.Shared.Random; using Robust.Shared.Utility; diff --git a/Content.Server/Chemistry/EntitySystems/SolutionContainerMixerSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionContainerMixerSystem.cs index a942d34e7a8..45a85010b19 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionContainerMixerSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionContainerMixerSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Power.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Power; namespace Content.Server.Chemistry.EntitySystems; diff --git a/Content.Server/Chemistry/EntitySystems/SolutionHeaterSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionHeaterSystem.cs index 1ef589ab5cb..a9aaa211c77 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionHeaterSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionHeaterSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Placeable; +using Content.Shared.Power; namespace Content.Server.Chemistry.EntitySystems; diff --git a/Content.Server/Cloning/CloningConsoleSystem.cs b/Content.Server/Cloning/CloningConsoleSystem.cs index c0685024aae..7a54cce31f7 100644 --- a/Content.Server/Cloning/CloningConsoleSystem.cs +++ b/Content.Server/Cloning/CloningConsoleSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Mind; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.Power; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Server.Player; diff --git a/Content.Server/Construction/ConstructionSystem.Computer.cs b/Content.Server/Construction/ConstructionSystem.Computer.cs index 0685b08f4ff..6951d44b4d5 100644 --- a/Content.Server/Construction/ConstructionSystem.Computer.cs +++ b/Content.Server/Construction/ConstructionSystem.Computer.cs @@ -1,6 +1,7 @@ using Content.Server.Construction.Components; using Content.Server.Power.Components; using Content.Shared.Computer; +using Content.Shared.Power; using Robust.Shared.Containers; namespace Content.Server.Construction; diff --git a/Content.Server/Construction/FlatpackSystem.cs b/Content.Server/Construction/FlatpackSystem.cs index be082eba307..8068783abfe 100644 --- a/Content.Server/Construction/FlatpackSystem.cs +++ b/Content.Server/Construction/FlatpackSystem.cs @@ -4,6 +4,8 @@ using Content.Shared.Construction; using Content.Shared.Construction.Components; using Content.Shared.Containers.ItemSlots; +using Content.Shared.Power; +using Robust.Shared.Prototypes; using Robust.Shared.Timing; namespace Content.Server.Construction; diff --git a/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs b/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs index 6e7bd255c5d..f47a5df8ac4 100644 --- a/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs @@ -1,6 +1,7 @@ using Content.Server.DeviceNetwork.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; +using Content.Shared.Power.EntitySystems; namespace Content.Server.DeviceNetwork.Systems; diff --git a/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs b/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs index cdc083feacd..d189afc0a02 100644 --- a/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Medical.CrewMonitoring; using Content.Server.Power.Components; using Content.Server.Station.Systems; +using Content.Shared.Power; namespace Content.Server.DeviceNetwork.Systems; diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 84a835f523c..39d10583951 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -24,7 +24,7 @@ using Content.Shared.Item; using Content.Shared.Movement.Events; using Content.Shared.Popups; -using Content.Shared.Throwing; +using Content.Shared.Power; using Content.Shared.Verbs; using Robust.Server.Audio; using Robust.Server.GameObjects; diff --git a/Content.Server/Doors/Systems/AirlockSystem.cs b/Content.Server/Doors/Systems/AirlockSystem.cs index fd5d3a9ceba..e9f1db13ffb 100644 --- a/Content.Server/Doors/Systems/AirlockSystem.cs +++ b/Content.Server/Doors/Systems/AirlockSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; using Content.Shared.Interaction; +using Content.Shared.Power; using Content.Shared.Wires; using Robust.Shared.Player; diff --git a/Content.Server/Doors/Systems/DoorSystem.cs b/Content.Server/Doors/Systems/DoorSystem.cs index 5968e445c19..292f8ec8e97 100644 --- a/Content.Server/Doors/Systems/DoorSystem.cs +++ b/Content.Server/Doors/Systems/DoorSystem.cs @@ -1,9 +1,9 @@ using Content.Server.Access; using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; -using Content.Server.Power.Components; using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; +using Content.Shared.Power; using Robust.Shared.Physics.Components; namespace Content.Server.Doors.Systems; diff --git a/Content.Server/Doors/Systems/FirelockSystem.cs b/Content.Server/Doors/Systems/FirelockSystem.cs index 3d4c8a4ec59..3c86ccc82d5 100644 --- a/Content.Server/Doors/Systems/FirelockSystem.cs +++ b/Content.Server/Doors/Systems/FirelockSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.Doors; using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; +using Content.Shared.Power; using Content.Shared.Popups; using Content.Shared.Prying.Components; using Robust.Shared.Map.Components; diff --git a/Content.Server/Fax/FaxSystem.cs b/Content.Server/Fax/FaxSystem.cs index e86dbca4a13..bd7bf0c4e08 100644 --- a/Content.Server/Fax/FaxSystem.cs +++ b/Content.Server/Fax/FaxSystem.cs @@ -29,6 +29,8 @@ using Robust.Shared.Containers; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Content.Shared.NameModifier.Components; +using Content.Shared.Power; namespace Content.Server.Fax; diff --git a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs index 0d6fb3fb90c..4a118e696fa 100644 --- a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs @@ -22,6 +22,7 @@ using System.Linq; using Content.Server.Jittering; using Content.Shared.Jittering; +using Content.Shared.Power; namespace Content.Server.Kitchen.EntitySystems { diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index 7448a9b84dd..c41909ac7c9 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -14,6 +14,7 @@ using Content.Shared.Emag.Components; using Content.Shared.Lathe; using Content.Shared.Materials; +using Content.Shared.Power; using Content.Shared.ReagentSpeed; using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; diff --git a/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs b/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs index f2c4c7dcc59..154a4081259 100644 --- a/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs +++ b/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs @@ -8,6 +8,8 @@ using Content.Shared.Examine; using Content.Shared.Light; using Content.Shared.Light.Components; +using Content.Shared.Power; +using Content.Shared.Station.Components; using Robust.Server.GameObjects; using Color = Robust.Shared.Maths.Color; diff --git a/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs b/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs index 752fb8f5fe6..3c5f7eaecb2 100644 --- a/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs +++ b/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs @@ -1,6 +1,8 @@ using Content.Server.Light.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; +using Content.Shared.Power; +using Content.Shared.Power.Components; namespace Content.Server.Light.EntitySystems { diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index e880cec5bbb..3c44ea46155 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -24,6 +24,10 @@ using Robust.Shared.Player; using Robust.Shared.Timing; using Robust.Shared.Audio.Systems; +using Content.Shared.Damage.Systems; +using Content.Shared.Damage.Components; +using Content.Shared.Power; +using Content.Shared.Power.Components; namespace Content.Server.Light.EntitySystems { diff --git a/Content.Server/Materials/MaterialReclaimerSystem.cs b/Content.Server/Materials/MaterialReclaimerSystem.cs index 3b23308758d..44f92502553 100644 --- a/Content.Server/Materials/MaterialReclaimerSystem.cs +++ b/Content.Server/Materials/MaterialReclaimerSystem.cs @@ -23,6 +23,10 @@ using System.Linq; using Content.Server.Administration.Logs; using Content.Shared.Database; +using Content.Shared.Destructible; +using Content.Shared.Emag.Components; +using Content.Shared.Power; +using Robust.Shared.Prototypes; namespace Content.Server.Materials; diff --git a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs index 97a758a5ed3..ec27c717286 100644 --- a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs +++ b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs @@ -24,6 +24,7 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Nutrition.Components; using Content.Shared.Popups; +using Content.Shared.Power; using Content.Shared.Throwing; using Robust.Server.Player; using Robust.Shared.Audio.Systems; diff --git a/Content.Server/Medical/CryoPodSystem.cs b/Content.Server/Medical/CryoPodSystem.cs index caae6c5f17e..d90e49d2016 100644 --- a/Content.Server/Medical/CryoPodSystem.cs +++ b/Content.Server/Medical/CryoPodSystem.cs @@ -29,6 +29,7 @@ using Content.Shared.Interaction; using Content.Shared.Medical.Cryogenics; using Content.Shared.MedicalScanner; +using Content.Shared.Power; using Content.Shared.Verbs; using Robust.Server.GameObjects; using Robust.Shared.Containers; diff --git a/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs b/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs index dc1f67c7400..3eaadfa0dec 100644 --- a/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Emag.Systems; using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Power; using Content.Shared.Storage.Components; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; diff --git a/Content.Server/Paper/PaperRandomStoryComponent.cs b/Content.Server/Paper/PaperRandomStoryComponent.cs index 7c5744f0878..e5eb3860024 100644 --- a/Content.Server/Paper/PaperRandomStoryComponent.cs +++ b/Content.Server/Paper/PaperRandomStoryComponent.cs @@ -1,3 +1,7 @@ +using Content.Shared.Paper; +using Content.Shared.StoryGen; +using Robust.Shared.Prototypes; + namespace Content.Server.Paper; /// diff --git a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs index f3cff9f2e72..14dca2c7bb2 100644 --- a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs +++ b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using Content.Server.Administration.Managers; using Content.Shared.CCVar; +using Content.Shared.Power; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Timing; diff --git a/Content.Server/Physics/Controllers/ConveyorController.cs b/Content.Server/Physics/Controllers/ConveyorController.cs index b3508025cb9..e459d7e87e0 100644 --- a/Content.Server/Physics/Controllers/ConveyorController.cs +++ b/Content.Server/Physics/Controllers/ConveyorController.cs @@ -6,6 +6,7 @@ using Content.Shared.Maps; using Content.Shared.Physics; using Content.Shared.Physics.Controllers; +using Content.Shared.Power; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Components; diff --git a/Content.Server/Power/Components/ApcPowerReceiverComponent.cs b/Content.Server/Power/Components/ApcPowerReceiverComponent.cs index 9a68e2aabb8..ebb3c6b42f3 100644 --- a/Content.Server/Power/Components/ApcPowerReceiverComponent.cs +++ b/Content.Server/Power/Components/ApcPowerReceiverComponent.cs @@ -59,11 +59,4 @@ public bool PowerDisabled { public float PowerReceived => NetworkLoad.ReceivingPower; } - - /// - /// Raised whenever an ApcPowerReceiver becomes powered / unpowered. - /// Does nothing on the client. - /// - [ByRefEvent] - public readonly record struct PowerChangedEvent(bool Powered, float ReceivingPower); } diff --git a/Content.Server/Power/Components/CableComponent.cs b/Content.Server/Power/Components/CableComponent.cs index a2a02a60f68..7398bc0616e 100644 --- a/Content.Server/Power/Components/CableComponent.cs +++ b/Content.Server/Power/Components/CableComponent.cs @@ -4,6 +4,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using System.Diagnostics.Tracing; +using Content.Shared.Tools.Systems; namespace Content.Server.Power.Components; @@ -14,11 +15,11 @@ namespace Content.Server.Power.Components; [Access(typeof(CableSystem))] public sealed partial class CableComponent : Component { - [DataField("cableDroppedOnCutPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string CableDroppedOnCutPrototype = "CableHVStack1"; + [DataField] + public EntProtoId CableDroppedOnCutPrototype = "CableHVStack1"; - [DataField("cuttingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] - public string CuttingQuality = "Cutting"; + [DataField] + public ProtoId CuttingQuality = SharedToolSystem.CutQuality; /// /// Checked by to determine if there is diff --git a/Content.Server/Power/EntitySystems/ActivatableUIRequiresPowerSystem.cs b/Content.Server/Power/EntitySystems/ActivatableUIRequiresPowerSystem.cs index 72843a65b84..e1c223c01e4 100644 --- a/Content.Server/Power/EntitySystems/ActivatableUIRequiresPowerSystem.cs +++ b/Content.Server/Power/EntitySystems/ActivatableUIRequiresPowerSystem.cs @@ -1,5 +1,8 @@ using Content.Shared.Popups; using Content.Server.Power.Components; +using Content.Shared.Power; +using Content.Shared.Power.Components; +using Content.Shared.Power.EntitySystems; using Content.Shared.UserInterface; using JetBrains.Annotations; using Content.Shared.Wires; diff --git a/Content.Server/Power/EntitySystems/CableMultitoolSystem.cs b/Content.Server/Power/EntitySystems/CableMultitoolSystem.cs index 15b967bb1d5..4a63be894ef 100644 --- a/Content.Server/Power/EntitySystems/CableMultitoolSystem.cs +++ b/Content.Server/Power/EntitySystems/CableMultitoolSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Tools; using Content.Shared.Examine; using Content.Shared.Interaction; +using Content.Shared.Tools.Systems; using Content.Shared.Verbs; using JetBrains.Annotations; using Robust.Shared.Utility; @@ -27,7 +28,7 @@ public override void Initialize() private void OnAfterInteractUsing(EntityUid uid, CableComponent component, AfterInteractUsingEvent args) { - if (args.Handled || args.Target == null || !args.CanReach || !_toolSystem.HasQuality(args.Used, "Pulsing")) + if (args.Handled || args.Target == null || !args.CanReach || !_toolSystem.HasQuality(args.Used, SharedToolSystem.PulseQuality)) return; var markup = FormattedMessage.FromMarkup(GenerateCableMarkup(uid)); @@ -45,7 +46,7 @@ private void OnGetExamineVerbs(EntityUid uid, CableComponent component, GetVerbs // Pulsing is hardcoded here because I don't think it needs to be more complex than that right now. // Update if I'm wrong. - var enabled = held != null && _toolSystem.HasQuality(held.Value, "Pulsing"); + var enabled = held != null && _toolSystem.HasQuality(held.Value, SharedToolSystem.PulseQuality); var verb = new ExamineVerb { Disabled = !enabled, diff --git a/Content.Server/Power/EntitySystems/ChargerSystem.cs b/Content.Server/Power/EntitySystems/ChargerSystem.cs index 038295eac11..2aa69024df3 100644 --- a/Content.Server/Power/EntitySystems/ChargerSystem.cs +++ b/Content.Server/Power/EntitySystems/ChargerSystem.cs @@ -8,6 +8,7 @@ using JetBrains.Annotations; using Robust.Shared.Containers; using System.Diagnostics.CodeAnalysis; +using Content.Shared.Power.Components; using Content.Shared.Storage.Components; using Robust.Server.Containers; using Content.Shared.Whitelist; diff --git a/Content.Server/Power/EntitySystems/PowerNetSystem.cs b/Content.Server/Power/EntitySystems/PowerNetSystem.cs index 6c35ba20083..8dcb6240a56 100644 --- a/Content.Server/Power/EntitySystems/PowerNetSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerNetSystem.cs @@ -5,6 +5,7 @@ using Content.Server.Power.Pow3r; using Content.Shared.CCVar; using Content.Shared.Power; +using Content.Shared.Power.Components; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Shared.Configuration; diff --git a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs index a314fe0ca11..47ac38b9972 100644 --- a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; using Content.Server.Power.Components; @@ -206,5 +207,17 @@ public void SetLoad(ApcPowerReceiverComponent comp, float load) { comp.Load = load; } + + public override bool ResolveApc(EntityUid entity, [NotNullWhen(true)] ref SharedApcPowerReceiverComponent? component) + { + if (component != null) + return true; + + if (!TryComp(entity, out ApcPowerReceiverComponent? receiver)) + return false; + + component = receiver; + return true; + } } } diff --git a/Content.Server/Power/Generation/Teg/TegSystem.cs b/Content.Server/Power/Generation/Teg/TegSystem.cs index 540bd6c4832..dd09467efe0 100644 --- a/Content.Server/Power/Generation/Teg/TegSystem.cs +++ b/Content.Server/Power/Generation/Teg/TegSystem.cs @@ -10,6 +10,8 @@ using Content.Shared.Atmos; using Content.Shared.DeviceNetwork; using Content.Shared.Examine; +using Content.Shared.Power; +using Content.Shared.Power.Components; using Content.Shared.Power.Generation.Teg; using Content.Shared.Rounding; using Robust.Server.GameObjects; diff --git a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs index 5f79906c995..5a1bd31a15c 100644 --- a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs +++ b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs @@ -5,6 +5,8 @@ using Content.Server.NodeContainer.Nodes; using Content.Server.Power.Components; using Content.Shared.Atmos; +using Content.Shared.Power; +using Content.Shared.Power.Components; namespace Content.Server.Power.Generator; diff --git a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs index 02e46e6b11d..0cb20b3187b 100644 --- a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs +++ b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.Chat; using Content.Shared.Examine; using Content.Shared.Interaction; +using Content.Shared.Power; using Content.Shared.Radio; using Content.Shared.Radio.Components; using Robust.Server.GameObjects; diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs index 5550201202f..e37be3d1a52 100644 --- a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Shuttles.Systems; using Content.Shared.Tag; using Content.Shared.Movement.Systems; +using Content.Shared.Power; using Content.Shared.Shuttles.UI.MapObjects; using Content.Shared.Timing; using Robust.Server.GameObjects; diff --git a/Content.Server/Shuttles/Systems/ThrusterSystem.cs b/Content.Server/Shuttles/Systems/ThrusterSystem.cs index 2454856a701..b1426802135 100644 --- a/Content.Server/Shuttles/Systems/ThrusterSystem.cs +++ b/Content.Server/Shuttles/Systems/ThrusterSystem.cs @@ -19,6 +19,8 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Timing; using Robust.Shared.Utility; +using Content.Shared.Localizations; +using Content.Shared.Power; namespace Content.Server.Shuttles.Systems; diff --git a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs index 652ca236e44..e728f4986bb 100644 --- a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Interaction; using Content.Shared.Lock; using Content.Shared.Popups; +using Content.Shared.Power; using Content.Shared.Projectiles; using Content.Shared.Singularity.Components; using Content.Shared.Singularity.EntitySystems; diff --git a/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs b/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs index 9cc85060c6e..d2c2a8a1ca7 100644 --- a/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs +++ b/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs @@ -1,5 +1,7 @@ using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; +using Content.Shared.Power; +using Content.Shared.Power.Components; using Content.Shared.Sound; using Content.Shared.Sound.Components; diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs index ca0f59cd14d..5cc3c52be45 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs @@ -3,6 +3,7 @@ using Content.Server.DeviceNetwork.Systems; using Content.Server.Power.Components; using Content.Shared.DeviceNetwork; +using Content.Shared.Power; using Content.Shared.UserInterface; using Content.Shared.SurveillanceCamera; using Robust.Server.GameObjects; diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs index d0c2cd78d32..315273a0cc4 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Power.Components; using Content.Shared.ActionBlocker; using Content.Shared.DeviceNetwork; +using Content.Shared.Power; using Content.Shared.SurveillanceCamera; using Content.Shared.Verbs; using Robust.Server.GameObjects; diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs index accaa75d1c3..f84039276f4 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Power.Components; using Content.Shared.ActionBlocker; using Content.Shared.DeviceNetwork; +using Content.Shared.Power; using Content.Shared.SurveillanceCamera; using Content.Shared.Verbs; using Robust.Server.GameObjects; diff --git a/Content.Server/Temperature/Systems/EntityHeaterSystem.cs b/Content.Server/Temperature/Systems/EntityHeaterSystem.cs index 6da774ba076..cf86400b0ea 100644 --- a/Content.Server/Temperature/Systems/EntityHeaterSystem.cs +++ b/Content.Server/Temperature/Systems/EntityHeaterSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Examine; using Content.Shared.Placeable; using Content.Shared.Popups; +using Content.Shared.Power; using Content.Shared.Temperature; using Content.Shared.Verbs; diff --git a/Content.Server/VendingMachines/VendingMachineSystem.cs b/Content.Server/VendingMachines/VendingMachineSystem.cs index 60be1ad6f1b..6b233cd43f1 100644 --- a/Content.Server/VendingMachines/VendingMachineSystem.cs +++ b/Content.Server/VendingMachines/VendingMachineSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.Emag.Systems; using Content.Shared.Emp; using Content.Shared.Popups; +using Content.Shared.Power; using Content.Shared.Throwing; using Content.Shared.UserInterface; using Content.Shared.VendingMachines; diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index fce81db3277..440b1f6dce7 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Popups; +using Content.Shared.Power; using Content.Shared.Tools.Components; using Content.Shared.UserInterface; using Content.Shared.Wires; diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs index 72d99460fe2..a9f224ad5d9 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.DeviceLinking.Events; using Content.Shared.Placeable; using Content.Shared.Popups; +using Content.Shared.Power; using Content.Shared.Research.Components; using Content.Shared.Xenoarchaeology.Equipment; using Content.Shared.Xenoarchaeology.XenoArtifacts; diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs index 6606f284327..b4303f97d81 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs @@ -7,6 +7,7 @@ using Content.Server.Xenoarchaeology.XenoArtifacts; using Content.Shared.Body.Components; using Content.Shared.Damage; +using Content.Shared.Power; using Content.Shared.Verbs; using Content.Shared.Xenoarchaeology.Equipment; using Robust.Shared.Collections; diff --git a/Content.Shared/Cabinet/ItemCabinetComponent.cs b/Content.Shared/Cabinet/ItemCabinetComponent.cs index dcc276e5651..0ea30a8ca54 100644 --- a/Content.Shared/Cabinet/ItemCabinetComponent.cs +++ b/Content.Shared/Cabinet/ItemCabinetComponent.cs @@ -1,5 +1,4 @@ using Content.Shared.Containers.ItemSlots; -using Robust.Shared.Audio; using Robust.Shared.GameStates; namespace Content.Shared.Cabinet; diff --git a/Content.Shared/Clothing/Components/PilotedByClothingComponent.cs b/Content.Shared/Clothing/Components/PilotedByClothingComponent.cs index cd4d0d62030..a5303ac1212 100644 --- a/Content.Shared/Clothing/Components/PilotedByClothingComponent.cs +++ b/Content.Shared/Clothing/Components/PilotedByClothingComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Clothing.EntitySystems; using Robust.Shared.GameStates; namespace Content.Shared.Clothing.Components; diff --git a/Content.Shared/Clothing/Components/ToggleClothingComponent.cs b/Content.Shared/Clothing/Components/ToggleClothingComponent.cs index c77aa03475f..04bc3ed4e83 100644 --- a/Content.Shared/Clothing/Components/ToggleClothingComponent.cs +++ b/Content.Shared/Clothing/Components/ToggleClothingComponent.cs @@ -1,5 +1,7 @@ using Content.Shared.Actions; using Content.Shared.Clothing.EntitySystems; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Toggleable; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; diff --git a/Content.Shared/Light/Components/SlimPoweredLightComponent.cs b/Content.Shared/Light/Components/SlimPoweredLightComponent.cs new file mode 100644 index 00000000000..bf6ae0e5251 --- /dev/null +++ b/Content.Shared/Light/Components/SlimPoweredLightComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Light.Components; + +// All content light code is terrible and everything is baked-in. Power code got predicted before light code did. +/// +/// Handles turning a pointlight on / off based on power. Nothing else +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class SlimPoweredLightComponent : Component +{ + /// + /// Used to make this as being lit. If unpowered then the light will still be off. + /// + [DataField, AutoNetworkedField] + public bool Enabled = true; +} diff --git a/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs b/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs new file mode 100644 index 00000000000..6d984ed19a3 --- /dev/null +++ b/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs @@ -0,0 +1,67 @@ +using Content.Shared.Light.Components; +using Content.Shared.Power; +using Content.Shared.Power.Components; +using Content.Shared.Power.EntitySystems; + +namespace Content.Shared.Light.EntitySystems; + +public sealed class SlimPoweredLightSystem : EntitySystem +{ + [Dependency] private readonly SharedPowerReceiverSystem _receiver = default!; + [Dependency] private readonly SharedPointLightSystem _lights = default!; + + private bool _setting; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnLightAttempt); + SubscribeLocalEvent(OnLightPowerChanged); + } + + private void OnLightAttempt(Entity ent, ref AttemptPointLightToggleEvent args) + { + // Early-out to avoid having to trycomp stuff if we're the caller setting it + if (_setting) + return; + + if (args.Enabled && !_receiver.IsPowered(ent.Owner)) + args.Cancelled = true; + } + + private void OnLightPowerChanged(Entity ent, ref PowerChangedEvent args) + { + // Early out if we don't need to trycomp. + if (args.Powered) + { + if (!ent.Comp.Enabled) + return; + } + else + { + if (!ent.Comp.Enabled) + return; + } + + if (!_lights.TryGetLight(ent.Owner, out var light)) + return; + + var enabled = ent.Comp.Enabled && args.Powered; + _setting = true; + _lights.SetEnabled(ent.Owner, enabled, light); + _setting = false; + } + + public void SetEnabled(Entity entity, bool enabled) + { + if (!Resolve(entity.Owner, ref entity.Comp, false)) + return; + + if (entity.Comp.Enabled == enabled) + return; + + entity.Comp.Enabled = enabled; + Dirty(entity); + _lights.SetEnabled(entity.Owner, enabled); + } +} diff --git a/Content.Shared/Light/EntitySystems/UnpoweredFlashlightSystem.cs b/Content.Shared/Light/EntitySystems/UnpoweredFlashlightSystem.cs index 42e55bea55d..8754de50583 100644 --- a/Content.Shared/Light/EntitySystems/UnpoweredFlashlightSystem.cs +++ b/Content.Shared/Light/EntitySystems/UnpoweredFlashlightSystem.cs @@ -13,6 +13,8 @@ namespace Content.Shared.Light.EntitySystems; public sealed class UnpoweredFlashlightSystem : EntitySystem { + // TODO: Split some of this to ItemTogglePointLight + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; diff --git a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs index 37ac7518896..2bc2af78314 100644 --- a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs +++ b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Shared.Examine; using Content.Shared.Power.Components; @@ -5,6 +6,16 @@ namespace Content.Shared.Power.EntitySystems; public abstract class SharedPowerReceiverSystem : EntitySystem { + public abstract bool ResolveApc(EntityUid entity, [NotNullWhen(true)] ref SharedApcPowerReceiverComponent? component); + + public bool IsPowered(Entity entity) + { + if (!ResolveApc(entity.Owner, ref entity.Comp)) + return true; + + return entity.Comp.Powered; + } + protected string GetExamineText(bool powered) { return Loc.GetString("power-receiver-component-on-examine-main", diff --git a/Content.Shared/Power/PowerChangedEvent.cs b/Content.Shared/Power/PowerChangedEvent.cs new file mode 100644 index 00000000000..578a34142a8 --- /dev/null +++ b/Content.Shared/Power/PowerChangedEvent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared.Power; + +/// +/// Raised whenever an ApcPowerReceiver becomes powered / unpowered. +/// Does nothing on the client. +/// +[ByRefEvent] +public readonly record struct PowerChangedEvent(bool Powered, float ReceivingPower); \ No newline at end of file diff --git a/Content.Shared/PowerCell/PowerCellDrawComponent.cs b/Content.Shared/PowerCell/PowerCellDrawComponent.cs index 94de7c77878..e4402e9eff4 100644 --- a/Content.Shared/PowerCell/PowerCellDrawComponent.cs +++ b/Content.Shared/PowerCell/PowerCellDrawComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Item.ItemToggle.Components; using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.cs b/Content.Shared/Tools/Systems/SharedToolSystem.cs index aa69d3db78a..1336fe1527c 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.cs @@ -33,6 +33,9 @@ public abstract partial class SharedToolSystem : EntitySystem [Dependency] private readonly TurfSystem _turfs = default!; [Dependency] protected readonly SharedSolutionContainerSystem SolutionContainer = default!; + public const string CutQuality = "Cutting"; + public const string PulseQuality = "Pulsing"; + public override void Initialize() { InitializeMultipleTool(); From 2257634fc9c0c53f6607853e258d01cf26449632 Mon Sep 17 00:00:00 2001 From: Simon <63975668+Simyon264@users.noreply.github.com> Date: Fri, 9 Aug 2024 08:16:22 +0200 Subject: [PATCH 015/366] UI to edit silicon laws from admin verb (#28483) * UI to edit silicon laws from admin verb (peak shitcode) * Improve UI * Use Moderator admin flag * Reviews --- .../SiliconLawEditUi/SiliconLawContainer.xaml | 31 +++++++ .../SiliconLawContainer.xaml.cs | 61 +++++++++++++ .../Laws/SiliconLawEditUi/SiliconLawEui.cs | 38 ++++++++ .../Laws/SiliconLawEditUi/SiliconLawUi.xaml | 22 +++++ .../SiliconLawEditUi/SiliconLawUi.xaml.cs | 89 +++++++++++++++++++ Content.Client/Stylesheets/StyleNano.cs | 5 ++ .../Administration/Systems/AdminVerbSystem.cs | 25 ++++++ Content.Server/Silicons/Laws/SiliconLawEui.cs | 70 +++++++++++++++ .../Silicons/Laws/SiliconLawSystem.cs | 16 ++++ .../Silicons/Laws/SiliconLawEditEuiState.cs | 29 ++++++ .../administration/ui/silicon-law-ui.ftl | 10 +++ 11 files changed, 396 insertions(+) create mode 100644 Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml create mode 100644 Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml.cs create mode 100644 Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs create mode 100644 Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml create mode 100644 Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml.cs create mode 100644 Content.Server/Silicons/Laws/SiliconLawEui.cs create mode 100644 Content.Shared/Silicons/Laws/SiliconLawEditEuiState.cs create mode 100644 Resources/Locale/en-US/administration/ui/silicon-law-ui.ftl diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml new file mode 100644 index 00000000000..1bcac4bca65 --- /dev/null +++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml.cs b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml.cs new file mode 100644 index 00000000000..2e44b820df9 --- /dev/null +++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml.cs @@ -0,0 +1,61 @@ +using Content.Shared.Silicons.Laws; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Utility; + +namespace Content.Client.Silicons.Laws.SiliconLawEditUi; + +[GenerateTypedNameReferences] +public sealed partial class SiliconLawContainer : BoxContainer +{ + public const string StyleClassSiliconLawPositionLabel = "SiliconLawPositionLabel"; + + public static readonly string CorruptedString = + Loc.GetString("ion-storm-law-scrambled-number", ("length", 5)); + + private SiliconLaw? _law; + + public event Action? MoveLawUp; + public event Action? MoveLawDown; + public event Action? DeleteAction; + + + public SiliconLawContainer() + { + RobustXamlLoader.Load(this); + + MoveUp.OnPressed += _ => MoveLawUp?.Invoke(_law!); + MoveDown.OnPressed += _ => MoveLawDown?.Invoke(_law!); + Corrupted.OnPressed += _ => + { + if (Corrupted.Pressed) + { + _law!.LawIdentifierOverride = CorruptedString; + } + else + { + _law!.LawIdentifierOverride = null; + } + }; + + LawContent.OnTextChanged += _ => _law!.LawString = Rope.Collapse(LawContent.TextRope).Trim(); + LawContent.Placeholder = new Rope.Leaf(Loc.GetString("silicon-law-ui-placeholder")); + Delete.OnPressed += _ => DeleteAction?.Invoke(_law!); + } + + public void SetLaw(SiliconLaw law) + { + _law = law; + LawContent.TextRope = new Rope.Leaf(Loc.GetString(law.LawString)); + PositionText.Text = law.Order.ToString(); + if (!string.IsNullOrEmpty(law.LawIdentifierOverride)) + { + Corrupted.Pressed = true; + } + else + { + Corrupted.Pressed = false; + } + } +} diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs new file mode 100644 index 00000000000..a4d59d1f315 --- /dev/null +++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs @@ -0,0 +1,38 @@ +using Content.Client.Eui; +using Content.Shared.Eui; +using Content.Shared.Silicons.Laws; + +namespace Content.Client.Silicons.Laws.SiliconLawEditUi; + +public sealed class SiliconLawEui : BaseEui +{ + public readonly EntityManager _entityManager = default!; + + private SiliconLawUi _siliconLawUi; + private EntityUid _target; + + public SiliconLawEui() + { + _entityManager = IoCManager.Resolve(); + + _siliconLawUi = new SiliconLawUi(); + _siliconLawUi.OnClose += () => SendMessage(new CloseEuiMessage()); + _siliconLawUi.Save.OnPressed += _ => SendMessage(new SiliconLawsSaveMessage(_siliconLawUi.GetLaws(), _entityManager.GetNetEntity(_target))); + } + + public override void HandleState(EuiStateBase state) + { + if (state is not SiliconLawsEuiState s) + { + return; + } + + _target = _entityManager.GetEntity(s.Target); + _siliconLawUi.SetLaws(s.Laws); + } + + public override void Opened() + { + _siliconLawUi.OpenCentered(); + } +} diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml new file mode 100644 index 00000000000..19dcbac6202 --- /dev/null +++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml @@ -0,0 +1,22 @@ + + + this shit does not layout properly unless I put the horizontal boxcontainer inside of a vertical one + ???? + + + + + + + + + + + + + diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml.cs b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml.cs new file mode 100644 index 00000000000..372961ea9a3 --- /dev/null +++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml.cs @@ -0,0 +1,89 @@ +using System.Linq; +using Content.Client.UserInterface.Controls; +using Content.Shared.FixedPoint; +using Content.Shared.Silicons.Laws; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.Silicons.Laws.SiliconLawEditUi; + +[GenerateTypedNameReferences] +public sealed partial class SiliconLawUi : FancyWindow +{ + private List _laws = new(); + + public SiliconLawUi() + { + RobustXamlLoader.Load(this); + NewLawButton.OnPressed += _ => AddNewLaw(); + } + + private void AddNewLaw() + { + var newLaw = new SiliconLaw(); + newLaw.Order = FixedPoint2.New(_laws.Count + 1); + _laws.Add(newLaw); + SetLaws(_laws); + } + + public void SetLaws(List sLaws) + { + _laws = sLaws; + LawContainer.RemoveAllChildren(); + foreach (var law in sLaws.OrderBy(l => l.Order)) + { + var lawControl = new SiliconLawContainer(); + lawControl.SetLaw(law); + lawControl.MoveLawDown += MoveLawDown; + lawControl.MoveLawUp += MoveLawUp; + lawControl.DeleteAction += DeleteLaw; + + LawContainer.AddChild(lawControl); + } + } + + public void DeleteLaw(SiliconLaw law) + { + _laws.Remove(law); + SetLaws(_laws); + } + + public void MoveLawDown(SiliconLaw law) + { + if (_laws.Count == 0) + { + return; + } + + var index = _laws.IndexOf(law); + if (index == -1) + { + return; + } + + _laws[index].Order += FixedPoint2.New(1); + SetLaws(_laws); + } + + public void MoveLawUp(SiliconLaw law) + { + if (_laws.Count == 0) + { + return; + } + + var index = _laws.IndexOf(law); + if (index == -1) + { + return; + } + + _laws[index].Order += FixedPoint2.New(-1); + SetLaws(_laws); + } + + public List GetLaws() + { + return _laws; + } +} diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs index 67b81c11454..aa170c9cf5a 100644 --- a/Content.Client/Stylesheets/StyleNano.cs +++ b/Content.Client/Stylesheets/StyleNano.cs @@ -4,6 +4,7 @@ using Content.Client.Examine; using Content.Client.PDA; using Content.Client.Resources; +using Content.Client.Silicons.Laws.SiliconLawEditUi; using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Controls.FancyTree; using Content.Client.Verbs.UI; @@ -1626,6 +1627,10 @@ public StyleNano(IResourceCache resCache) : base(resCache) { BackgroundColor = FancyTreeSelectedRowColor, }), + + // Silicon law edit ui + Element + private bool IsCorner( + HashSet tiles, + HashSet blocked, + Dictionary vis1, + Vector2i index, + Vector2i delta) + { + var diagonalIndex = index + delta; + + if (!tiles.TryGetValue(diagonalIndex, out var diagonal)) + return false; + + var cardinal1 = new Vector2i(index.X, diagonal.Y); + var cardinal2 = new Vector2i(diagonal.X, index.Y); + + return vis1.GetValueOrDefault(diagonal) != 0 && + vis1.GetValueOrDefault(cardinal1) != 0 && + vis1.GetValueOrDefault(cardinal2) != 0 && + blocked.Contains(cardinal1) && + blocked.Contains(cardinal2) && + !blocked.Contains(diagonal); + } + + /// + /// Gets the relevant vision seeds for later. + /// + private record struct SeedJob() : IRobustJob + { + public StationAiVisionSystem System; + + public Entity Grid; + public Box2 ExpandedBounds; + + public void Execute() + { + System._lookup.GetLocalEntitiesIntersecting(Grid.Owner, ExpandedBounds, System._seeds); + } + } + + private record struct ViewJob() : IParallelRobustJob + { + public int BatchSize => 1; + + public IEntityManager EntManager; + public SharedMapSystem Maps; + public StationAiVisionSystem System; + + public Entity Grid; + public List> Data = new(); + + // If we're doing range-checks might be able to early out + public Vector2i? TargetTile; + + public HashSet VisibleTiles; + + public readonly List> Vis1 = new(); + public readonly List> Vis2 = new(); + + public readonly List> SeedTiles = new(); + public readonly List> BoundaryTiles = new(); + + public void Execute(int index) + { + // If we're looking for a single tile then early-out if someone else has found it. + if (TargetTile != null) + { + lock (System) + { + if (System.TargetFound) + { + return; + } + } + } + + var seed = Data[index]; + var seedXform = EntManager.GetComponent(seed); + + // Fastpath just get tiles in range. + // Either xray-vision or system is doing a quick-and-dirty check. + if (!seed.Comp.Occluded || System.FastPath) + { + var squircles = Maps.GetLocalTilesIntersecting(Grid.Owner, + Grid.Comp, + new Circle(System._xforms.GetWorldPosition(seedXform), seed.Comp.Range), ignoreEmpty: false); + + // Try to find the target tile. + if (TargetTile != null) + { + foreach (var tile in squircles) + { + if (tile.GridIndices == TargetTile) + { + lock (System) + { + System.TargetFound = true; + } + + return; + } + } + } + else + { + lock (VisibleTiles) + { + foreach (var tile in squircles) + { + VisibleTiles.Add(tile.GridIndices); + } + } + } + + return; + } + + // Code based upon https://github.com/OpenDreamProject/OpenDream/blob/c4a3828ccb997bf3722673620460ebb11b95ccdf/OpenDreamShared/Dream/ViewAlgorithm.cs + + var range = seed.Comp.Range; + var vis1 = Vis1[index]; + var vis2 = Vis2[index]; + + var seedTiles = SeedTiles[index]; + var boundary = BoundaryTiles[index]; + + // Cleanup last run + vis1.Clear(); + vis2.Clear(); + + seedTiles.Clear(); + boundary.Clear(); + + var maxDepthMax = 0; + var sumDepthMax = 0; + + var eyePos = Maps.GetTileRef(Grid.Owner, Grid, seedXform.Coordinates).GridIndices; + + for (var x = Math.Floor(eyePos.X - range); x <= eyePos.X + range; x++) + { + for (var y = Math.Floor(eyePos.Y - range); y <= eyePos.Y + range; y++) + { + var tile = new Vector2i((int)x, (int)y); + var delta = tile - eyePos; + var xDelta = Math.Abs(delta.X); + var yDelta = Math.Abs(delta.Y); + + var deltaSum = xDelta + yDelta; + + maxDepthMax = Math.Max(maxDepthMax, Math.Max(xDelta, yDelta)); + sumDepthMax = Math.Max(sumDepthMax, deltaSum); + seedTiles.Add(tile); + } + } + + // Step 3, Diagonal shadow loop + for (var d = 0; d < maxDepthMax; d++) + { + foreach (var tile in seedTiles) + { + var maxDelta = System.GetMaxDelta(tile, eyePos); + + if (maxDelta == d + 1 && System.CheckNeighborsVis(vis2, tile, d)) + { + vis2[tile] = (System._opaque.Contains(tile) ? -1 : d + 1); + } + } + } + + // Step 4, Straight shadow loop + for (var d = 0; d < sumDepthMax; d++) + { + foreach (var tile in seedTiles) + { + var sumDelta = System.GetSumDelta(tile, eyePos); + + if (sumDelta == d + 1 && System.CheckNeighborsVis(vis1, tile, d)) + { + if (System._opaque.Contains(tile)) + { + vis1[tile] = -1; + } + else if (vis2.GetValueOrDefault(tile) != 0) + { + vis1[tile] = d + 1; + } + } + } + } + + // Add the eye itself + vis1[eyePos] = 1; + + // Step 6. + + // Step 7. + + // Step 8. + foreach (var tile in seedTiles) + { + vis2[tile] = vis1.GetValueOrDefault(tile, 0); + } + + // Step 9 + foreach (var tile in seedTiles) + { + if (!System._opaque.Contains(tile)) + continue; + + var tileVis1 = vis1.GetValueOrDefault(tile); + + if (tileVis1 != 0) + continue; + + if (System.IsCorner(seedTiles, System._opaque, vis1, tile, Vector2i.UpRight) || + System.IsCorner(seedTiles, System._opaque, vis1, tile, Vector2i.UpLeft) || + System.IsCorner(seedTiles, System._opaque, vis1, tile, Vector2i.DownLeft) || + System.IsCorner(seedTiles, System._opaque, vis1, tile, Vector2i.DownRight)) + { + boundary.Add(tile); + } + } + + // Make all wall/corner tiles visible + foreach (var tile in boundary) + { + vis1[tile] = -1; + } + + if (TargetTile != null) + { + if (vis2.TryGetValue(TargetTile.Value, out var tileVis2)) + { + DebugTools.Assert(seedTiles.Contains(TargetTile.Value)); + + if (tileVis2 != 0) + { + lock (System) + { + System.TargetFound = true; + return; + } + } + } + } + else + { + // vis2 is what we care about for LOS. + foreach (var tile in seedTiles) + { + // If not in viewport don't care. + if (!System._viewportTiles.Contains(tile)) + continue; + + var tileVis2 = vis2.GetValueOrDefault(tile, 0); + + if (tileVis2 != 0) + { + // No idea if it's better to do this inside or out. + lock (VisibleTiles) + { + VisibleTiles.Add(tile); + } + } + } + } + } + } +} diff --git a/Resources/Locale/en-US/sandbox/sandbox-manager.ftl b/Resources/Locale/en-US/sandbox/sandbox-manager.ftl index b7f4d03451f..b6f97367323 100644 --- a/Resources/Locale/en-US/sandbox/sandbox-manager.ftl +++ b/Resources/Locale/en-US/sandbox/sandbox-manager.ftl @@ -1,4 +1,5 @@ sandbox-window-title = Sandbox Panel +sandbox-window-ai-overlay-button = AI Overlay sandbox-window-respawn-button = Respawn sandbox-window-spawn-entities-button = Spawn Entities sandbox-window-spawn-tiles-button = Spawn Tiles diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml index 3347b0e4b1c..954caf67aeb 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml @@ -4,6 +4,7 @@ name: camera description: A surveillance camera. It's watching you. Kinda. components: + - type: StationAiVision - type: Clickable - type: InteractionOutline - type: Construction From 1c74d0bfd4e3c942d79aa71482ff7f95df3cda25 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:57:57 +1000 Subject: [PATCH 017/366] Fix AI vision occlusion (#31341) Forgot to do this, but how tf do doors work then. --- .../StationAi/StationAiVisionSystem.cs | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs index c1f5d98afdb..33771cb963a 100644 --- a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs +++ b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs @@ -95,12 +95,7 @@ public bool IsAccessible(Entity grid, Vector2i tile, float exp // Get all other relevant tiles. while (tileEnumerator.MoveNext(out var tileRef)) { - var tileBounds = _lookup.GetLocalBounds(tileRef.GridIndices, grid.Comp.TileSize).Enlarged(-0.05f); - - _occluders.Clear(); - _lookup.GetLocalEntitiesIntersecting(grid.Owner, tileBounds, _occluders, LookupFlags.Static); - - if (_occluders.Count > 0) + if (IsOccluded(grid, tileRef.GridIndices)) { _opaque.Add(tileRef.GridIndices); } @@ -125,6 +120,25 @@ public bool IsAccessible(Entity grid, Vector2i tile, float exp return TargetFound; } + private bool IsOccluded(Entity grid, Vector2i tile) + { + var tileBounds = _lookup.GetLocalBounds(tile, grid.Comp.TileSize).Enlarged(-0.05f); + _occluders.Clear(); + _lookup.GetLocalEntitiesIntersecting(grid.Owner, tileBounds, _occluders, LookupFlags.Static); + var anyOccluders = false; + + foreach (var occluder in _occluders) + { + if (!occluder.Comp.Enabled) + continue; + + anyOccluders = true; + break; + } + + return anyOccluders; + } + /// /// Gets a byond-equivalent for tiles in the specified worldAABB. /// @@ -160,12 +174,7 @@ public void GetView(Entity grid, Box2Rotated worldBounds, Hash while (tileEnumerator.MoveNext(out var tileRef)) { - var tileBounds = _lookup.GetLocalBounds(tileRef.GridIndices, grid.Comp.TileSize).Enlarged(-0.05f); - - _occluders.Clear(); - _lookup.GetLocalEntitiesIntersecting(grid.Owner, tileBounds, _occluders, LookupFlags.Static); - - if (_occluders.Count > 0) + if (IsOccluded(grid, tileRef.GridIndices)) { _opaque.Add(tileRef.GridIndices); } @@ -181,12 +190,7 @@ public void GetView(Entity grid, Box2Rotated worldBounds, Hash if (_viewportTiles.Contains(tileRef.GridIndices)) continue; - var tileBounds = _lookup.GetLocalBounds(tileRef.GridIndices, grid.Comp.TileSize).Enlarged(-0.05f); - - _occluders.Clear(); - _lookup.GetLocalEntitiesIntersecting(grid.Owner, tileBounds, _occluders, LookupFlags.Static); - - if (_occluders.Count > 0) + if (IsOccluded(grid, tileRef.GridIndices)) { _opaque.Add(tileRef.GridIndices); } From e36d65e8f7d6267ffd15135465ba45244221d55d Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 23 Aug 2024 18:34:39 +1000 Subject: [PATCH 018/366] Make AI overlay use vis1 (#31348) * Make AI overlay use vis1 I think this is what get_hear in 13 uses idk this shit is esoteric. vis1 is luminosity vis2 is line of sight. * boark --- .../Silicons/StationAi/StationAiVisionSystem.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs index 33771cb963a..c144f330e11 100644 --- a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs +++ b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs @@ -482,11 +482,11 @@ public void Execute(int index) if (TargetTile != null) { - if (vis2.TryGetValue(TargetTile.Value, out var tileVis2)) + if (vis1.TryGetValue(TargetTile.Value, out var tileVis)) { DebugTools.Assert(seedTiles.Contains(TargetTile.Value)); - if (tileVis2 != 0) + if (tileVis != 0) { lock (System) { @@ -505,9 +505,9 @@ public void Execute(int index) if (!System._viewportTiles.Contains(tile)) continue; - var tileVis2 = vis2.GetValueOrDefault(tile, 0); + var tileVis = vis1.GetValueOrDefault(tile, 0); - if (tileVis2 != 0) + if (tileVis != 0) { // No idea if it's better to do this inside or out. lock (VisibleTiles) From 076b6e133a75f5ae1837159f938e683878f29239 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 02:24:42 -0400 Subject: [PATCH 019/366] Implement GridCast for AI --- .../NPC/SharedPathfindingSystem.Line.cs | 74 +++++++++++++++++++ Content.Shared/NPC/SharedPathfindingSystem.cs | 31 +++++++- 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 Content.Shared/NPC/SharedPathfindingSystem.Line.cs diff --git a/Content.Shared/NPC/SharedPathfindingSystem.Line.cs b/Content.Shared/NPC/SharedPathfindingSystem.Line.cs new file mode 100644 index 00000000000..7e7bdd082b1 --- /dev/null +++ b/Content.Shared/NPC/SharedPathfindingSystem.Line.cs @@ -0,0 +1,74 @@ +namespace Content.Shared.NPC; + +public abstract partial class SharedPathfindingSystem +{ + public static void GridCast(Vector2i start, Vector2i end, Vector2iCallback callback) + { + // https://gist.github.com/Pyr3z/46884d67641094d6cf353358566db566 + // declare all locals at the top so it's obvious how big the footprint is + int dx, dy, xinc, yinc, side, i, error; + + // starting cell is always returned + if (!callback(start)) + return; + + xinc = (end.X < start.X) ? -1 : 1; + yinc = (end.Y < start.Y) ? -1 : 1; + dx = xinc * (end.X - start.X); + dy = yinc * (end.Y - start.Y); + var ax = start.X; + var ay = start.Y; + + if (dx == dy) // Handle perfect diagonals + { + // I include this "optimization" for more aesthetic reasons, actually. + // While Bresenham's Line can handle perfect diagonals just fine, it adds + // additional cells to the line that make it not a perfect diagonal + // anymore. So, while this branch is ~twice as fast as the next branch, + // the real reason it is here is for style. + + // Also, there *is* the reason of performance. If used for cell-based + // raycasts, for example, then perfect diagonals will check half as many + // cells. + + while (dx --> 0) + { + ax += xinc; + ay += yinc; + if (!callback(new Vector2i(ax, ay))) + return; + } + + return; + } + + // Handle all other lines + + side = -1 * ((dx == 0 ? yinc : xinc) - 1); + + i = dx + dy; + error = dx - dy; + + dx *= 2; + dy *= 2; + + while (i --> 0) + { + if (error > 0 || error == side) + { + ax += xinc; + error -= dy; + } + else + { + ay += yinc; + error += dx; + } + + if (!callback(new Vector2i(ax, ay))) + return; + } + } + + public delegate bool Vector2iCallback(Vector2i index); +} \ No newline at end of file diff --git a/Content.Shared/NPC/SharedPathfindingSystem.cs b/Content.Shared/NPC/SharedPathfindingSystem.cs index 8831acc1ddb..f59e6d3c462 100644 --- a/Content.Shared/NPC/SharedPathfindingSystem.cs +++ b/Content.Shared/NPC/SharedPathfindingSystem.cs @@ -2,7 +2,7 @@ namespace Content.Shared.NPC; -public abstract class SharedPathfindingSystem : EntitySystem +public abstract partial class SharedPathfindingSystem : EntitySystem { /// /// This is equivalent to agent radii for navmeshes. In our case it's preferable that things are cleanly @@ -37,4 +37,31 @@ public static float OctileDistance(Vector2i start, Vector2i end) var ab = Vector2.Abs(diff); return ab.X + ab.Y + (1.41f - 2) * Math.Min(ab.X, ab.Y); } -} + + public static IEnumerable GetTileOutline(Vector2i center, float radius) + { + // https://www.redblobgames.com/grids/circle-drawing/ + var vecCircle = center + Vector2.One / 2f; + + for (var r = 0; r <= Math.Floor(radius * MathF.Sqrt(0.5f)); r++) + { + var d = MathF.Floor(MathF.Sqrt(radius * radius - r * r)); + + yield return new Vector2(vecCircle.X - d, vecCircle.Y + r).Floored(); + + yield return new Vector2(vecCircle.X + d, vecCircle.Y + r).Floored(); + + yield return new Vector2(vecCircle.X - d, vecCircle.Y - r).Floored(); + + yield return new Vector2(vecCircle.X + d, vecCircle.Y - r).Floored(); + + yield return new Vector2(vecCircle.X + r, vecCircle.Y - d).Floored(); + + yield return new Vector2(vecCircle.X + r, vecCircle.Y + d).Floored(); + + yield return new Vector2(vecCircle.X - r, vecCircle.Y - d).Floored(); + + yield return new Vector2(vecCircle.X - r, vecCircle.Y + d).Floored(); + } + } +} \ No newline at end of file From af9c4036900cf5fb9017874c9a93149c90acd24a Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 25 Aug 2024 22:18:42 +1000 Subject: [PATCH 020/366] Power stuff (#31314) * Power stuff - Add shared IsPowered - Add shared ResolveApc - Move PowerChangedEvent to shared for now - Add SlimPoweredLight that actually functions how you'd expect a PoweredLight to function it id didn't have a bunch of bloat on it. * big update * boing --- Content.Server/Doors/Systems/FirelockSystem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Server/Doors/Systems/FirelockSystem.cs b/Content.Server/Doors/Systems/FirelockSystem.cs index 3c86ccc82d5..6c1e711b6f1 100644 --- a/Content.Server/Doors/Systems/FirelockSystem.cs +++ b/Content.Server/Doors/Systems/FirelockSystem.cs @@ -14,6 +14,7 @@ using Content.Shared.Power; using Content.Shared.Popups; using Content.Shared.Prying.Components; +using Robust.Server.GameObjects; using Robust.Shared.Map.Components; namespace Content.Server.Doors.Systems From 45f440007d8f2ff3b3fb1326f03d2706c08c39f7 Mon Sep 17 00:00:00 2001 From: TGRCDev Date: Sat, 21 Sep 2024 00:33:22 -0700 Subject: [PATCH 021/366] Added a directory to station maps (#31156) * Added directory to station maps * Add null checks to map directory sorting/filtering * Reworked station map directory to be more readable and responsive --- .../UI/StationMapBeaconControl.xaml | 19 ++++++ .../UI/StationMapBeaconControl.xaml.cs | 50 ++++++++++++++++ .../UI/StationMapBoundUserInterface.cs | 20 ++++--- .../Pinpointer/UI/StationMapWindow.xaml | 23 +++++++- .../Pinpointer/UI/StationMapWindow.xaml.cs | 58 +++++++++++++++++-- .../en-US/navmap-beacons/station_map.ftl | 1 + 6 files changed, 156 insertions(+), 15 deletions(-) create mode 100644 Content.Client/Pinpointer/UI/StationMapBeaconControl.xaml create mode 100644 Content.Client/Pinpointer/UI/StationMapBeaconControl.xaml.cs diff --git a/Content.Client/Pinpointer/UI/StationMapBeaconControl.xaml b/Content.Client/Pinpointer/UI/StationMapBeaconControl.xaml new file mode 100644 index 00000000000..e1c55131cd6 --- /dev/null +++ b/Content.Client/Pinpointer/UI/StationMapBeaconControl.xaml @@ -0,0 +1,19 @@ + + + + + + diff --git a/Content.Client/Pinpointer/UI/StationMapBeaconControl.xaml.cs b/Content.Client/Pinpointer/UI/StationMapBeaconControl.xaml.cs new file mode 100644 index 00000000000..a4d4055c7df --- /dev/null +++ b/Content.Client/Pinpointer/UI/StationMapBeaconControl.xaml.cs @@ -0,0 +1,50 @@ +using Content.Shared.Pinpointer; +using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Map; + +namespace Content.Client.Pinpointer.UI; + +[GenerateTypedNameReferences] +public sealed partial class StationMapBeaconControl : Control, IComparable +{ + [Dependency] private readonly IEntityManager _entMan = default!; + + public readonly EntityCoordinates BeaconPosition; + public Action? OnPressed; + public string? Label => BeaconNameLabel.Text; + private StyleBoxFlat _styleBox; + public Color Color => _styleBox.BackgroundColor; + + public StationMapBeaconControl(EntityUid mapUid, SharedNavMapSystem.NavMapBeacon beacon) + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + BeaconPosition = new EntityCoordinates(mapUid, beacon.Position); + + _styleBox = new StyleBoxFlat { BackgroundColor = beacon.Color }; + ColorPanel.PanelOverride = _styleBox; + BeaconNameLabel.Text = beacon.Text; + + MainButton.OnPressed += args => OnPressed?.Invoke(BeaconPosition); + } + + public int CompareTo(StationMapBeaconControl? other) + { + if (other == null) + return 1; + + // Group by color + var colorCompare = Color.ToArgb().CompareTo(other.Color.ToArgb()); + if (colorCompare != 0) + { + return colorCompare; + } + + // If same color, sort by text + return string.Compare(Label, other.Label); + } +} diff --git a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs index 1483e75e732..6e62dbfda9e 100644 --- a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs +++ b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs @@ -22,14 +22,18 @@ protected override void Open() gridUid = xform.GridUid; } - _window = new StationMapWindow(gridUid, Owner); - _window.OpenCentered(); - _window.OnClose += Close; - } + _window = this.CreateWindow(); + _window.Title = EntMan.GetComponent(Owner).EntityName; - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - _window?.Dispose(); + string stationName = string.Empty; + if(EntMan.TryGetComponent(gridUid, out var gridMetaData)) + { + stationName = gridMetaData.EntityName; + } + + if (EntMan.TryGetComponent(Owner, out var comp) && comp.ShowLocation) + _window.Set(stationName, gridUid, Owner); + else + _window.Set(stationName, gridUid, null); } } diff --git a/Content.Client/Pinpointer/UI/StationMapWindow.xaml b/Content.Client/Pinpointer/UI/StationMapWindow.xaml index 00424a3566a..c79fc8f9e7b 100644 --- a/Content.Client/Pinpointer/UI/StationMapWindow.xaml +++ b/Content.Client/Pinpointer/UI/StationMapWindow.xaml @@ -3,11 +3,28 @@ xmlns:ui="clr-namespace:Content.Client.Pinpointer.UI" Title="{Loc 'station-map-window-title'}" Resizable="False" - SetSize="668 713" - MinSize="668 713"> + SetSize="868 748" + MinSize="868 748"> - + + + + + + + + + + + + + + + + + diff --git a/Content.Client/Pinpointer/UI/StationMapWindow.xaml.cs b/Content.Client/Pinpointer/UI/StationMapWindow.xaml.cs index 1b01fe4e304..52ef2ab7da4 100644 --- a/Content.Client/Pinpointer/UI/StationMapWindow.xaml.cs +++ b/Content.Client/Pinpointer/UI/StationMapWindow.xaml.cs @@ -3,25 +3,75 @@ using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.XAML; using Robust.Shared.Map; +using Content.Shared.Pinpointer; namespace Content.Client.Pinpointer.UI; [GenerateTypedNameReferences] public sealed partial class StationMapWindow : FancyWindow { - public StationMapWindow(EntityUid? mapUid, EntityUid? trackedEntity) + [Dependency] private readonly IEntityManager _entMan = default!; + + private readonly List _buttons = new(); + + public StationMapWindow() { RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + FilterBar.OnTextChanged += (bar) => OnFilterChanged(bar.Text); + } + + public void Set(string stationName, EntityUid? mapUid, EntityUid? trackedEntity) + { NavMapScreen.MapUid = mapUid; if (trackedEntity != null) NavMapScreen.TrackedCoordinates.Add(new EntityCoordinates(trackedEntity.Value, Vector2.Zero), (true, Color.Cyan)); - if (IoCManager.Resolve().TryGetComponent(mapUid, out var metadata)) + if (!string.IsNullOrEmpty(stationName)) { - Title = metadata.EntityName; + StationName.Text = stationName; } NavMapScreen.ForceNavMapUpdate(); + UpdateBeaconList(mapUid); + } + + public void OnFilterChanged(string newFilter) + { + foreach (var button in _buttons) + { + button.Visible = string.IsNullOrEmpty(newFilter) || ( + !string.IsNullOrEmpty(button.Label) && + button.Label.Contains(newFilter, StringComparison.OrdinalIgnoreCase) + ); + }; + } + + public void UpdateBeaconList(EntityUid? mapUid) + { + BeaconButtons.Children.Clear(); + _buttons.Clear(); + + if (!mapUid.HasValue) + return; + + if (!_entMan.TryGetComponent(mapUid, out var navMap)) + return; + + foreach (var beacon in navMap.Beacons.Values) + { + var button = new StationMapBeaconControl(mapUid.Value, beacon); + + button.OnPressed += NavMapScreen.CenterToCoordinates; + + _buttons.Add(button); + } + + _buttons.Sort(); + + foreach (var button in _buttons) + BeaconButtons.AddChild(button); } -} +} \ No newline at end of file diff --git a/Resources/Locale/en-US/navmap-beacons/station_map.ftl b/Resources/Locale/en-US/navmap-beacons/station_map.ftl index 1563e0abaf2..e2528515566 100644 --- a/Resources/Locale/en-US/navmap-beacons/station_map.ftl +++ b/Resources/Locale/en-US/navmap-beacons/station_map.ftl @@ -1,6 +1,7 @@ station-map-window-title = Station map station-map-user-interface-flavor-left = Don't panic station-map-user-interface-flavor-right = v1.42 +station-map-filter-placeholder = Search by name nav-beacon-window-title = Station Beacon nav-beacon-toggle-visible = Visible From c19b06efdbd00cd89d84947dc947f6f2147182b2 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 21 Jul 2024 14:48:13 +1000 Subject: [PATCH 022/366] Reapply "Remove some BUI boilerplate" (#30214) (#30219) * Reapply "Remove some BUI boilerplate" (#30214) This reverts commit cb0ba66be38677d248ce11f809221230ebe89642. * Fix gas tank * Fix PA * Fix microwave * Comms console underwrap * Fix rcd * log wehs --- .../UI/AccessOverriderBoundUserInterface.cs | 45 ++--- .../Access/UI/AccessOverriderWindow.xaml.cs | 39 ++-- .../UI/AgentIDCardBoundUserInterface.cs | 20 +-- .../Access/UI/AgentIDCardWindow.xaml.cs | 10 +- .../Ame/UI/AmeControllerBoundUserInterface.cs | 16 +- Content.Client/Ame/UI/AmeWindow.xaml.cs | 19 +- .../Ui/AnomalyGeneratorBoundUserInterface.cs | 20 +-- .../Anomaly/Ui/AnomalyGeneratorWindow.xaml.cs | 8 +- Content.Client/Arcade/BlockGameMenu.cs | 45 ++--- .../Arcade/SpaceVillainArcadeMenu.cs | 45 ++--- .../Arcade/UI/BlockGameBoundUserInterface.cs | 5 +- .../SpaceVillainArcadeBoundUserInterface.cs | 16 +- .../Monitor/UI/AirAlarmBoundUserInterface.cs | 12 +- .../Atmos/Monitor/UI/AirAlarmWindow.xaml.cs | 7 +- .../Atmos/UI/GasCanisterBoundUserInterface.cs | 9 +- .../Atmos/UI/GasFilterBoundUserInterface.cs | 11 +- .../Atmos/UI/GasFilterWindow.xaml.cs | 7 +- .../Atmos/UI/GasMixerBoundUserInteface.cs | 18 +- .../UI/GasPressurePumpBoundUserInterface.cs | 17 +- .../UI/GasThermomachineBoundUserInterface.cs | 17 +- .../UI/GasVolumePumpBoundUserInterface.cs | 19 +- .../Atmos/UI/SpaceHeaterBoundUserInterface.cs | 10 +- .../Jukebox/JukeboxBoundUserInterface.cs | 21 +-- .../CryostorageBoundUserInterface.cs | 15 +- .../CargoBountyConsoleBoundUserInterface.cs | 17 +- .../CargoPalletConsoleBoundUserInterface.cs | 15 +- .../CargoShuttleConsoleBoundUserInterface.cs | 24 +-- .../Cargo/UI/CargoShuttleMenu.xaml.cs | 13 +- .../UI/ChemMasterBoundUserInterface.cs | 20 +-- .../UI/ReagentDispenserBoundUserInterface.cs | 27 +-- .../UI/TransferAmountBoundUserInterface.cs | 12 +- .../UI/CloningConsoleBoundUserInterface.cs | 25 +-- .../UI/ChameleonBoundUserInterface.cs | 16 +- ...CommunicationsConsoleBoundUserInterface.cs | 72 +++----- .../UI/CommunicationsConsoleMenu.xaml.cs | 83 ++++----- .../Computer/ComputerBoundUserInterface.cs | 15 +- .../UI/ConfigurationBoundUserInterface.cs | 28 +-- .../Configurable/UI/ConfigurationMenu.cs | 18 +- .../UI/FlatpackCreatorBoundUserInterface.cs | 14 +- .../UI/FlatpackCreatorMenu.xaml.cs | 13 +- .../Crayon/UI/CrayonBoundUserInterface.cs | 40 +++-- Content.Client/Crayon/UI/CrayonWindow.xaml.cs | 18 +- .../UI/DisposalRouterBoundUserInterface.cs | 22 +-- .../UI/DisposalTaggerBoundUserInterface.cs | 23 +-- .../DoorElectronicsBoundUserInterface.cs | 31 ++-- .../DoorElectronicsConfigurationMenu.xaml.cs | 21 +-- Content.Client/Fax/UI/FaxBoundUi.cs | 12 +- .../ForensicScannerBoundUserInterface.cs | 15 +- .../Gateway/UI/GatewayBoundUserInterface.cs | 16 +- .../Gateway/UI/GatewayWindow.xaml.cs | 10 +- .../UI/GravityGeneratorBoundUserInterface.cs | 23 +-- .../Gravity/UI/GravityGeneratorWindow.xaml.cs | 15 +- .../UI/HealthAnalyzerBoundUserInterface.cs | 29 +-- ...manoidMarkingModifierBoundUserInterface.cs | 4 +- .../Instruments/UI/BandMenu.xaml.cs | 6 +- .../Instruments/UI/ChannelsMenu.xaml.cs | 5 +- .../UI/InstrumentBoundUserInterface.cs | 36 ++-- .../Instruments/UI/InstrumentMenu.xaml.cs | 169 ++++++++++-------- .../Inventory/StrippableBoundUserInterface.cs | 21 +-- Content.Client/Kitchen/UI/GrinderMenu.xaml.cs | 42 ++--- .../Kitchen/UI/MicrowaveBoundUserInterface.cs | 43 +---- .../Kitchen/UI/MicrowaveMenu.xaml.cs | 25 +-- .../UI/ReagentGrinderBoundUserInterface.cs | 34 ++-- .../UI/HandLabelerBoundUserInterface.cs | 16 +- .../Lathe/UI/LatheBoundUserInterface.cs | 17 +- Content.Client/Lathe/UI/LatheMenu.xaml.cs | 41 +++-- .../UI/SignalTimerBoundUserInterface.cs | 24 +-- .../UI/SignalTimerWindow.xaml.cs | 24 +-- .../MagicMirrorBoundUserInterface.cs | 15 +- .../Ui/NewsWriterBoundUserInterface.cs | 18 +- .../MassMedia/Ui/NewsWriterMenu.xaml.cs | 6 +- .../Mech/Ui/MechBoundUserInterface.cs | 16 +- Content.Client/Mech/Ui/MechMenu.xaml.cs | 9 +- .../CrewMonitoringBoundUserInterface.cs | 18 +- .../CrewMonitoringWindow.xaml.cs | 17 +- .../NetworkConfiguratorBoundUserInterface.cs | 39 ++-- ...tworkConfiguratorConfigurationMenu.xaml.cs | 8 +- .../NetworkConfiguratorDeviceList.xaml.cs | 12 +- .../NetworkConfiguratorLinkMenu.xaml.cs | 16 +- .../NetworkConfiguratorListMenu.xaml.cs | 13 +- Content.Client/Nuke/NukeBoundUserInterface.cs | 17 +- .../WarDeclaratorBoundUserInterface.cs | 18 +- .../NukeOps/WarDeclaratorWindow.xaml.cs | 9 +- Content.Client/PDA/PdaBoundUserInterface.cs | 21 +-- .../PDA/Ringer/RingerBoundUserInterface.cs | 4 +- .../Paper/UI/PaperBoundUserInterface.cs | 17 +- Content.Client/Paper/UI/PaperWindow.xaml.cs | 6 +- .../ParticleAcceleratorBoundUserInterface.cs | 16 +- .../UI/ParticleAcceleratorControlMenu.cs | 31 ++-- .../UI/NavMapBeaconBoundUserInterface.cs | 16 +- .../Pinpointer/UI/NavMapBeaconWindow.xaml.cs | 25 ++- .../UI/StationMapBoundUserInterface.cs | 15 +- .../UI/UntrackedMapBoundUserInterface.cs | 15 +- .../Power/APC/ApcBoundUserInterface.cs | 16 +- Content.Client/Power/APC/UI/ApcMenu.xaml.cs | 12 +- .../Power/Generator/GeneratorWindow.xaml.cs | 52 +++--- .../PortableGeneratorBoundUserInterface.cs | 25 ++- ...owerMonitoringConsoleBoundUserInterface.cs | 19 +- .../PowerMonitoringWindow.xaml.Widgets.cs | 6 +- .../Power/PowerMonitoringWindow.xaml.cs | 84 +++++---- Content.Client/RCD/RCDMenu.xaml.cs | 40 ++--- .../RCD/RCDMenuBoundUserInterface.cs | 16 +- .../Radio/Ui/IntercomBoundUserInterface.cs | 28 +-- .../UI/DiskConsoleBoundUserInterface.cs | 14 +- .../UI/ResearchClientBoundUserInterface.cs | 15 +- .../ResearchClientServerSelectionMenu.xaml.cs | 11 +- .../UI/ResearchConsoleBoundUserInterface.cs | 29 +-- .../Research/UI/ResearchConsoleMenu.xaml.cs | 29 +-- .../UI/RoboticsConsoleBoundUserInterface.cs | 18 +- .../Robotics/UI/RoboticsConsoleWindow.xaml.cs | 27 ++- ...vageExpeditionConsoleBoundUserInterface.cs | 12 +- .../BUI/IFFConsoleBoundUserInterface.cs | 4 +- .../BUI/RadarConsoleBoundUserInterface.cs | 14 +- .../BUI/ShuttleConsoleBoundUserInterface.cs | 5 +- .../Silicons/Borgs/BorgBoundUserInterface.cs | 18 +- Content.Client/Silicons/Borgs/BorgMenu.xaml | 2 +- .../Silicons/Borgs/BorgMenu.xaml.cs | 62 ++++--- .../Laws/Ui/SiliconLawBoundUserInterface.cs | 15 +- .../UI/SprayPainterBoundUserInterface.cs | 27 +-- ...lStationRecordConsoleBoundUserInterface.cs | 13 +- .../Store/Ui/StoreBoundUserInterface.cs | 18 +- Content.Client/Strip/StrippingMenu.cs | 11 +- .../UI/SurveillanceCameraMonitorBoundUi.cs | 11 +- .../Thief/ThiefBackpackBoundUserInterface.cs | 19 +- .../Thief/ThiefBackpackMenu.xaml.cs | 28 ++- .../GasTank/GasTankBoundUserInterface.cs | 10 +- .../Systems/Atmos/GasTank/GasTankWindow.cs | 124 +++++++------ .../VendingMachineBoundUserInterface.cs | 8 +- .../VoiceMask/VoiceMaskBoundUserInterface.cs | 8 +- .../VoiceMaskNameChangeWindow.xaml.cs | 6 +- .../Melee/UI/MeleeSpeechBoundUserInterface.cs | 10 +- .../Wires/UI/WiresBoundUserInterface.cs | 7 +- Content.Client/Wires/UI/WiresMenu.cs | 16 +- .../Ui/AnalysisConsoleBoundUserInterface.cs | 6 +- 134 files changed, 1151 insertions(+), 1786 deletions(-) diff --git a/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs b/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs index c1b63dc4d05..d80c600c03e 100644 --- a/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs +++ b/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Content.Shared.Containers.ItemSlots; +using Robust.Client.UserInterface; using Robust.Shared.Prototypes; using static Content.Shared.Access.Components.AccessOverriderComponent; @@ -23,6 +24,28 @@ protected override void Open() { base.Open(); + _window = this.CreateWindow(); + RefreshAccess(); + _window.Title = EntMan.GetComponent(Owner).EntityName; + _window.OnSubmit += SubmitData; + + _window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId)); + } + + public override void OnProtoReload(PrototypesReloadedEventArgs args) + { + base.OnProtoReload(args); + if (!args.WasModified()) + return; + + RefreshAccess(); + + if (State != null) + _window?.UpdateState(_prototypeManager, (AccessOverriderBoundUserInterfaceState) State); + } + + private void RefreshAccess() + { List> accessLevels; if (EntMan.TryGetComponent(Owner, out var accessOverrider)) @@ -30,38 +53,20 @@ protected override void Open() accessLevels = accessOverrider.AccessLevels; accessLevels.Sort(); } - else { accessLevels = new List>(); _accessOverriderSystem.Log.Error($"No AccessOverrider component found for {EntMan.ToPrettyString(Owner)}!"); } - _window = new AccessOverriderWindow(this, _prototypeManager, accessLevels) - { - Title = EntMan.GetComponent(Owner).EntityName - }; - - _window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId)); - - _window.OnClose += Close; - _window.OpenCentered(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - _window?.Dispose(); + _window?.SetAccessLevels(_prototypeManager, accessLevels); } protected override void UpdateState(BoundUserInterfaceState state) { base.UpdateState(state); var castState = (AccessOverriderBoundUserInterfaceState) state; - _window?.UpdateState(castState); + _window?.UpdateState(_prototypeManager, castState); } public void SubmitData(List> newAccessList) diff --git a/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs b/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs index b5c480ff71b..ef6a3bb6717 100644 --- a/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs +++ b/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs @@ -13,26 +13,24 @@ namespace Content.Client.Access.UI [GenerateTypedNameReferences] public sealed partial class AccessOverriderWindow : DefaultWindow { - [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - private readonly AccessOverriderBoundUserInterface _owner; private readonly Dictionary _accessButtons = new(); - public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototypeManager prototypeManager, - List> accessLevels) + public event Action>>? OnSubmit; + + public AccessOverriderWindow() { RobustXamlLoader.Load(this); - IoCManager.InjectDependencies(this); - var logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill); + } - _owner = owner; + public void SetAccessLevels(IPrototypeManager protoManager, List> accessLevels) + { + _accessButtons.Clear(); + AccessLevelGrid.DisposeAllChildren(); foreach (var access in accessLevels) { - if (!prototypeManager.TryIndex(access, out var accessLevel)) + if (!protoManager.TryIndex(access, out var accessLevel)) { - logMill.Error($"Unable to find access level for {access}"); continue; } @@ -44,11 +42,16 @@ public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototype AccessLevelGrid.AddChild(newButton); _accessButtons.Add(accessLevel.ID, newButton); - newButton.OnPressed += _ => SubmitData(); + newButton.OnPressed += _ => + { + OnSubmit?.Invoke( + // Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair + _accessButtons.Where(x => x.Value.Pressed).Select(x => new ProtoId(x.Key)).ToList()); + }; } } - public void UpdateState(AccessOverriderBoundUserInterfaceState state) + public void UpdateState(IPrototypeManager protoManager, AccessOverriderBoundUserInterfaceState state) { PrivilegedIdLabel.Text = state.PrivilegedIdName; PrivilegedIdButton.Text = state.IsPrivilegedIdPresent @@ -70,7 +73,7 @@ public void UpdateState(AccessOverriderBoundUserInterfaceState state) foreach (string tag in state.MissingPrivilegesList) { - var privilege = Loc.GetString(_prototypeManager.Index(tag)?.Name ?? "generic-unknown"); + var privilege = Loc.GetString(protoManager.Index(tag)?.Name ?? "generic-unknown"); missingPrivileges.Add(privilege); } @@ -90,13 +93,5 @@ public void UpdateState(AccessOverriderBoundUserInterfaceState state) button.Disabled = (!state.AllowedModifyAccessList?.Contains>(accessName)) ?? true; } } - - private void SubmitData() => - _owner.SubmitData( - // Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair - _accessButtons.Where(x => x.Value.Pressed) - .Select(x => new ProtoId(x.Key)) - .ToList() - ); } } diff --git a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs index c3fac8cb92a..ee5ed26d2e3 100644 --- a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs +++ b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs @@ -1,5 +1,7 @@ using Content.Shared.Access.Systems; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; +using Robust.Shared.Prototypes; namespace Content.Client.Access.UI { @@ -18,16 +20,11 @@ protected override void Open() { base.Open(); - _window?.Dispose(); - _window = new AgentIDCardWindow(this); - if (State != null) - UpdateState(State); + _window = this.CreateWindow(); - _window.OpenCentered(); - - _window.OnClose += Close; _window.OnNameChanged += OnNameChanged; _window.OnJobChanged += OnJobChanged; + _window.OnJobIconChanged += OnJobIconChanged; } private void OnNameChanged(string newName) @@ -59,14 +56,5 @@ protected override void UpdateState(BoundUserInterfaceState state) _window.SetCurrentJob(cast.CurrentJob); _window.SetAllowedIcons(cast.Icons, cast.CurrentJobIconId); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - _window?.Dispose(); - } } } diff --git a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs index 9a38c0c4853..91cb8b0ebdb 100644 --- a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs +++ b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs @@ -17,19 +17,19 @@ public sealed partial class AgentIDCardWindow : DefaultWindow [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IEntitySystemManager _entitySystem = default!; private readonly SpriteSystem _spriteSystem; - private readonly AgentIDCardBoundUserInterface _bui; private const int JobIconColumnCount = 10; public event Action? OnNameChanged; public event Action? OnJobChanged; - public AgentIDCardWindow(AgentIDCardBoundUserInterface bui) + public event Action>? OnJobIconChanged; + + public AgentIDCardWindow() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); _spriteSystem = _entitySystem.GetEntitySystem(); - _bui = bui; NameLineEdit.OnTextEntered += e => OnNameChanged?.Invoke(e.Text); NameLineEdit.OnFocusExit += e => OnNameChanged?.Invoke(e.Text); @@ -69,7 +69,7 @@ public void SetAllowedIcons(HashSet icons, string currentJobIconId) }; // Generate buttons textures - TextureRect jobIconTexture = new TextureRect + var jobIconTexture = new TextureRect { Texture = _spriteSystem.Frame0(jobIcon.Icon), TextureScale = new Vector2(2.5f, 2.5f), @@ -77,7 +77,7 @@ public void SetAllowedIcons(HashSet icons, string currentJobIconId) }; jobIconButton.AddChild(jobIconTexture); - jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID); + jobIconButton.OnPressed += _ => OnJobIconChanged?.Invoke(jobIcon.ID); IconGrid.AddChild(jobIconButton); if (jobIconId.Equals(currentJobIconId)) diff --git a/Content.Client/Ame/UI/AmeControllerBoundUserInterface.cs b/Content.Client/Ame/UI/AmeControllerBoundUserInterface.cs index e84cf5d34de..3d65f751899 100644 --- a/Content.Client/Ame/UI/AmeControllerBoundUserInterface.cs +++ b/Content.Client/Ame/UI/AmeControllerBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Ame.Components; using JetBrains.Annotations; +using Robust.Client.UserInterface; namespace Content.Client.Ame.UI { @@ -16,9 +17,8 @@ protected override void Open() { base.Open(); - _window = new AmeWindow(this); - _window.OnClose += Close; - _window.OpenCentered(); + _window = this.CreateWindow(); + _window.OnAmeButton += ButtonPressed; } /// @@ -40,15 +40,5 @@ public void ButtonPressed(UiButton button) { SendMessage(new UiButtonPressedMessage(button)); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - _window?.Dispose(); - } - } } } diff --git a/Content.Client/Ame/UI/AmeWindow.xaml.cs b/Content.Client/Ame/UI/AmeWindow.xaml.cs index 8b91ec59660..d6d580bcdaf 100644 --- a/Content.Client/Ame/UI/AmeWindow.xaml.cs +++ b/Content.Client/Ame/UI/AmeWindow.xaml.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Client.UserInterface; using Content.Shared.Ame.Components; using Robust.Client.AutoGenerated; @@ -9,15 +10,17 @@ namespace Content.Client.Ame.UI [GenerateTypedNameReferences] public sealed partial class AmeWindow : DefaultWindow { - public AmeWindow(AmeControllerBoundUserInterface ui) + public event Action? OnAmeButton; + + public AmeWindow() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - EjectButton.OnPressed += _ => ui.ButtonPressed(UiButton.Eject); - ToggleInjection.OnPressed += _ => ui.ButtonPressed(UiButton.ToggleInjection); - IncreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.IncreaseFuel); - DecreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.DecreaseFuel); + EjectButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.Eject); + ToggleInjection.OnPressed += _ => OnAmeButton?.Invoke(UiButton.ToggleInjection); + IncreaseFuelButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.IncreaseFuel); + DecreaseFuelButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.DecreaseFuel); } /// @@ -29,7 +32,7 @@ public void UpdateState(BoundUserInterfaceState state) var castState = (AmeControllerBoundUserInterfaceState) state; // Disable all buttons if not powered - if (Contents.Children != null) + if (Contents.Children.Any()) { ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower); EjectButton.Disabled = false; @@ -65,8 +68,8 @@ public void UpdateState(BoundUserInterfaceState state) CoreCount.Text = $"{castState.CoreCount}"; InjectionAmount.Text = $"{castState.InjectionAmount}"; // format power statistics to pretty numbers - CurrentPowerSupply.Text = $"{castState.CurrentPowerSupply.ToString("N1")}"; - TargetedPowerSupply.Text = $"{castState.TargetedPowerSupply.ToString("N1")}"; + CurrentPowerSupply.Text = $"{castState.CurrentPowerSupply:N1}"; + TargetedPowerSupply.Text = $"{castState.TargetedPowerSupply:N1}"; } } } diff --git a/Content.Client/Anomaly/Ui/AnomalyGeneratorBoundUserInterface.cs b/Content.Client/Anomaly/Ui/AnomalyGeneratorBoundUserInterface.cs index 5764d0a097d..5d1985485c4 100644 --- a/Content.Client/Anomaly/Ui/AnomalyGeneratorBoundUserInterface.cs +++ b/Content.Client/Anomaly/Ui/AnomalyGeneratorBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Gravity; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Anomaly.Ui; @@ -18,10 +19,8 @@ protected override void Open() { base.Open(); - _window = new(Owner); - - _window.OpenCentered(); - _window.OnClose += Close; + _window = this.CreateWindow(); + _window.SetEntity(Owner); _window.OnGenerateButtonPressed += () => { @@ -37,18 +36,5 @@ protected override void UpdateState(BoundUserInterfaceState state) return; _window?.UpdateState(msg); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - - _window?.Dispose(); - } - - public void SetPowerSwitch(bool on) - { - SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(on)); - } } diff --git a/Content.Client/Anomaly/Ui/AnomalyGeneratorWindow.xaml.cs b/Content.Client/Anomaly/Ui/AnomalyGeneratorWindow.xaml.cs index 08438e2a1b2..82d41192dd0 100644 --- a/Content.Client/Anomaly/Ui/AnomalyGeneratorWindow.xaml.cs +++ b/Content.Client/Anomaly/Ui/AnomalyGeneratorWindow.xaml.cs @@ -18,17 +18,21 @@ public sealed partial class AnomalyGeneratorWindow : FancyWindow public Action? OnGenerateButtonPressed; - public AnomalyGeneratorWindow(EntityUid gen) + public AnomalyGeneratorWindow() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - EntityView.SetEntity(gen); EntityView.SpriteOffset = false; GenerateButton.OnPressed += _ => OnGenerateButtonPressed?.Invoke(); } + public void SetEntity(EntityUid uid) + { + EntityView.SetEntity(uid); + } + public void UpdateState(AnomalyGeneratorUserInterfaceState state) { _cooldownEnd = state.CooldownEndTime; diff --git a/Content.Client/Arcade/BlockGameMenu.cs b/Content.Client/Arcade/BlockGameMenu.cs index eeda2a31020..4a579fc4bf4 100644 --- a/Content.Client/Arcade/BlockGameMenu.cs +++ b/Content.Client/Arcade/BlockGameMenu.cs @@ -28,8 +28,6 @@ public sealed class BlockGameMenu : DefaultWindow private static readonly Vector2 BlockSize = new(15, 15); - private readonly BlockGameBoundUserInterface _owner; - private readonly PanelContainer _mainPanel; private readonly BoxContainer _gameRootContainer; @@ -58,10 +56,11 @@ public sealed class BlockGameMenu : DefaultWindow private bool _isPlayer = false; private bool _gameOver = false; - public BlockGameMenu(BlockGameBoundUserInterface owner) + public event Action? OnAction; + + public BlockGameMenu() { Title = Loc.GetString("blockgame-menu-title"); - _owner = owner; MinSize = SetSize = new Vector2(410, 490); @@ -176,7 +175,7 @@ public BlockGameMenu(BlockGameBoundUserInterface owner) }; _newGameButton.OnPressed += (e) => { - _owner.SendAction(BlockGamePlayerAction.NewGame); + OnAction?.Invoke(BlockGamePlayerAction.NewGame); }; pauseMenuContainer.AddChild(_newGameButton); pauseMenuContainer.AddChild(new Control { MinSize = new Vector2(1, 10) }); @@ -186,7 +185,10 @@ public BlockGameMenu(BlockGameBoundUserInterface owner) Text = Loc.GetString("blockgame-menu-button-scoreboard"), TextAlign = Label.AlignMode.Center }; - _scoreBoardButton.OnPressed += (e) => _owner.SendAction(BlockGamePlayerAction.ShowHighscores); + _scoreBoardButton.OnPressed += (e) => + { + OnAction?.Invoke(BlockGamePlayerAction.ShowHighscores); + }; pauseMenuContainer.AddChild(_scoreBoardButton); _unpauseButtonMargin = new Control { MinSize = new Vector2(1, 10), Visible = false }; pauseMenuContainer.AddChild(_unpauseButtonMargin); @@ -199,7 +201,7 @@ public BlockGameMenu(BlockGameBoundUserInterface owner) }; _unpauseButton.OnPressed += (e) => { - _owner.SendAction(BlockGamePlayerAction.Unpause); + OnAction?.Invoke(BlockGamePlayerAction.Unpause); }; pauseMenuContainer.AddChild(_unpauseButton); @@ -257,7 +259,7 @@ public BlockGameMenu(BlockGameBoundUserInterface owner) }; _finalNewGameButton.OnPressed += (e) => { - _owner.SendAction(BlockGamePlayerAction.NewGame); + OnAction?.Invoke(BlockGamePlayerAction.NewGame); }; gameOverMenuContainer.AddChild(_finalNewGameButton); @@ -327,7 +329,10 @@ public BlockGameMenu(BlockGameBoundUserInterface owner) Text = Loc.GetString("blockgame-menu-button-back"), TextAlign = Label.AlignMode.Center }; - _highscoreBackButton.OnPressed += (e) => _owner.SendAction(BlockGamePlayerAction.Pause); + _highscoreBackButton.OnPressed += (e) => + { + OnAction?.Invoke(BlockGamePlayerAction.Pause); + }; menuContainer.AddChild(_highscoreBackButton); menuInnerPanel.AddChild(menuContainer); @@ -473,7 +478,7 @@ protected override void KeyboardFocusExited() private void TryPause() { - _owner.SendAction(BlockGamePlayerAction.Pause); + OnAction?.Invoke(BlockGamePlayerAction.Pause); } public void SetStarted() @@ -576,19 +581,19 @@ protected override void KeyBindDown(GUIBoundKeyEventArgs args) return; else if (args.Function == ContentKeyFunctions.ArcadeLeft) - _owner.SendAction(BlockGamePlayerAction.StartLeft); + OnAction?.Invoke(BlockGamePlayerAction.StartLeft); else if (args.Function == ContentKeyFunctions.ArcadeRight) - _owner.SendAction(BlockGamePlayerAction.StartRight); + OnAction?.Invoke(BlockGamePlayerAction.StartRight); else if (args.Function == ContentKeyFunctions.ArcadeUp) - _owner.SendAction(BlockGamePlayerAction.Rotate); + OnAction?.Invoke(BlockGamePlayerAction.Rotate); else if (args.Function == ContentKeyFunctions.Arcade3) - _owner.SendAction(BlockGamePlayerAction.CounterRotate); + OnAction?.Invoke(BlockGamePlayerAction.CounterRotate); else if (args.Function == ContentKeyFunctions.ArcadeDown) - _owner.SendAction(BlockGamePlayerAction.SoftdropStart); + OnAction?.Invoke(BlockGamePlayerAction.SoftdropStart); else if (args.Function == ContentKeyFunctions.Arcade2) - _owner.SendAction(BlockGamePlayerAction.Hold); + OnAction?.Invoke(BlockGamePlayerAction.Hold); else if (args.Function == ContentKeyFunctions.Arcade1) - _owner.SendAction(BlockGamePlayerAction.Harddrop); + OnAction?.Invoke(BlockGamePlayerAction.Harddrop); } protected override void KeyBindUp(GUIBoundKeyEventArgs args) @@ -599,11 +604,11 @@ protected override void KeyBindUp(GUIBoundKeyEventArgs args) return; else if (args.Function == ContentKeyFunctions.ArcadeLeft) - _owner.SendAction(BlockGamePlayerAction.EndLeft); + OnAction?.Invoke(BlockGamePlayerAction.EndLeft); else if (args.Function == ContentKeyFunctions.ArcadeRight) - _owner.SendAction(BlockGamePlayerAction.EndRight); + OnAction?.Invoke(BlockGamePlayerAction.EndRight); else if (args.Function == ContentKeyFunctions.ArcadeDown) - _owner.SendAction(BlockGamePlayerAction.SoftdropEnd); + OnAction?.Invoke(BlockGamePlayerAction.SoftdropEnd); } public void UpdateNextBlock(BlockGameBlock[] blocks) diff --git a/Content.Client/Arcade/SpaceVillainArcadeMenu.cs b/Content.Client/Arcade/SpaceVillainArcadeMenu.cs index e5542a5848e..1ee4c268184 100644 --- a/Content.Client/Arcade/SpaceVillainArcadeMenu.cs +++ b/Content.Client/Arcade/SpaceVillainArcadeMenu.cs @@ -8,8 +8,6 @@ namespace Content.Client.Arcade { public sealed class SpaceVillainArcadeMenu : DefaultWindow { - public SpaceVillainArcadeBoundUserInterface Owner { get; set; } - private readonly Label _enemyNameLabel; private readonly Label _playerInfoLabel; private readonly Label _enemyInfoLabel; @@ -17,11 +15,13 @@ public sealed class SpaceVillainArcadeMenu : DefaultWindow private readonly Label _enemyActionLabel; private readonly Button[] _gameButtons = new Button[3]; //used to disable/enable all game buttons - public SpaceVillainArcadeMenu(SpaceVillainArcadeBoundUserInterface owner) + + public event Action? OnPlayerAction; + + public SpaceVillainArcadeMenu() { MinSize = SetSize = new Vector2(300, 225); Title = Loc.GetString("spacevillain-menu-title"); - Owner = owner; var grid = new GridContainer { Columns = 1 }; @@ -47,32 +47,43 @@ public SpaceVillainArcadeMenu(SpaceVillainArcadeBoundUserInterface owner) grid.AddChild(_enemyActionLabel); var buttonGrid = new GridContainer { Columns = 3 }; - _gameButtons[0] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Attack) + _gameButtons[0] = new Button() { Text = Loc.GetString("spacevillain-menu-button-attack") }; + + _gameButtons[0].OnPressed += + _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Attack); buttonGrid.AddChild(_gameButtons[0]); - _gameButtons[1] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Heal) + _gameButtons[1] = new Button() { Text = Loc.GetString("spacevillain-menu-button-heal") }; + + _gameButtons[1].OnPressed += + _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Heal); buttonGrid.AddChild(_gameButtons[1]); - _gameButtons[2] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Recharge) + _gameButtons[2] = new Button() { Text = Loc.GetString("spacevillain-menu-button-recharge") }; + + _gameButtons[2].OnPressed += + _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Recharge); buttonGrid.AddChild(_gameButtons[2]); centerContainer = new CenterContainer(); centerContainer.AddChild(buttonGrid); grid.AddChild(centerContainer); - var newGame = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.NewGame) + var newGame = new Button() { Text = Loc.GetString("spacevillain-menu-button-new-game") }; + + newGame.OnPressed += _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.NewGame); grid.AddChild(newGame); Contents.AddChild(grid); @@ -99,23 +110,5 @@ public void UpdateInfo(SharedSpaceVillainArcadeComponent.SpaceVillainArcadeDataU _playerActionLabel.Text = message.PlayerActionMessage; _enemyActionLabel.Text = message.EnemyActionMessage; } - - private sealed class ActionButton : Button - { - private readonly SpaceVillainArcadeBoundUserInterface _owner; - private readonly SharedSpaceVillainArcadeComponent.PlayerAction _playerAction; - - public ActionButton(SpaceVillainArcadeBoundUserInterface owner, SharedSpaceVillainArcadeComponent.PlayerAction playerAction) - { - _owner = owner; - _playerAction = playerAction; - OnPressed += Clicked; - } - - private void Clicked(ButtonEventArgs e) - { - _owner.SendAction(_playerAction); - } - } } } diff --git a/Content.Client/Arcade/UI/BlockGameBoundUserInterface.cs b/Content.Client/Arcade/UI/BlockGameBoundUserInterface.cs index 1a3422dec0f..8fa8035afd6 100644 --- a/Content.Client/Arcade/UI/BlockGameBoundUserInterface.cs +++ b/Content.Client/Arcade/UI/BlockGameBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Arcade; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Arcade.UI; @@ -15,9 +16,7 @@ protected override void Open() { base.Open(); - _menu = new BlockGameMenu(this); - _menu.OnClose += Close; - _menu.OpenCentered(); + _menu = this.CreateWindow(); } protected override void ReceiveMessage(BoundUserInterfaceMessage message) diff --git a/Content.Client/Arcade/UI/SpaceVillainArcadeBoundUserInterface.cs b/Content.Client/Arcade/UI/SpaceVillainArcadeBoundUserInterface.cs index 40bbe8b2d8c..c0704530de2 100644 --- a/Content.Client/Arcade/UI/SpaceVillainArcadeBoundUserInterface.cs +++ b/Content.Client/Arcade/UI/SpaceVillainArcadeBoundUserInterface.cs @@ -1,4 +1,5 @@ using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Shared.GameObjects; using Robust.Shared.ViewVariables; using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent; @@ -9,8 +10,6 @@ public sealed class SpaceVillainArcadeBoundUserInterface : BoundUserInterface { [ViewVariables] private SpaceVillainArcadeMenu? _menu; - //public SharedSpaceVillainArcadeComponent SpaceVillainArcade; - public SpaceVillainArcadeBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { SendAction(PlayerAction.RequestData); @@ -25,10 +24,7 @@ protected override void Open() { base.Open(); - _menu = new SpaceVillainArcadeMenu(this); - - _menu.OnClose += Close; - _menu.OpenCentered(); + _menu = this.CreateWindow(); } protected override void ReceiveMessage(BoundUserInterfaceMessage message) @@ -36,12 +32,4 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message) if (message is SpaceVillainArcadeDataUpdateMessage msg) _menu?.UpdateInfo(msg); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - _menu?.Dispose(); - } } diff --git a/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs b/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs index 8f3b507c806..2ae15188355 100644 --- a/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs +++ b/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor.Components; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; @@ -20,16 +21,9 @@ protected override void Open() { base.Open(); - _window = new AirAlarmWindow(this); + _window = this.CreateWindow(); + _window.SetEntity(Owner); - if (State != null) - { - UpdateState(State); - } - - _window.OpenCentered(); - - _window.OnClose += Close; _window.AtmosDeviceDataChanged += OnDeviceDataChanged; _window.AtmosDeviceDataCopied += OnDeviceDataCopied; _window.AtmosAlarmThresholdChanged += OnThresholdChanged; diff --git a/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs b/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs index 43be67c9d6b..eeec11c7660 100644 --- a/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs +++ b/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs @@ -47,7 +47,7 @@ public sealed partial class AirAlarmWindow : FancyWindow private CheckBox _autoMode => AutoModeCheckBox; - public AirAlarmWindow(BoundUserInterface owner) + public AirAlarmWindow() { RobustXamlLoader.Load(this); @@ -95,8 +95,11 @@ public AirAlarmWindow(BoundUserInterface owner) _sensors.Clear(); ResyncAllRequested!.Invoke(); }; + } - EntityView.SetEntity(owner.Owner); + public void SetEntity(EntityUid uid) + { + EntityView.SetEntity(uid); } public void UpdateState(AirAlarmUIState state) diff --git a/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs b/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs index a5e316a8def..7bf9b396d5e 100644 --- a/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Atmos.Piping.Binary.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Atmos.UI { @@ -21,14 +22,8 @@ protected override void Open() { base.Open(); - _window = new GasCanisterWindow(); + _window = this.CreateWindow(); - if (State != null) - UpdateState(State); - - _window.OpenCentered(); - - _window.OnClose += Close; _window.ReleaseValveCloseButtonPressed += OnReleaseValveClosePressed; _window.ReleaseValveOpenButtonPressed += OnReleaseValveOpenPressed; _window.ReleasePressureSet += OnReleasePressureSet; diff --git a/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs b/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs index 1904e2b3402..2b8020924cf 100644 --- a/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs @@ -3,6 +3,7 @@ using Content.Shared.Atmos.Piping.Trinary.Components; using Content.Shared.Localizations; using JetBrains.Annotations; +using Robust.Client.UserInterface; namespace Content.Client.Atmos.UI { @@ -28,14 +29,8 @@ protected override void Open() var atmosSystem = EntMan.System(); - _window = new GasFilterWindow(atmosSystem.Gases); - - if (State != null) - UpdateState(State); - - _window.OpenCentered(); - - _window.OnClose += Close; + _window = this.CreateWindow(); + _window.PopulateGasList(atmosSystem.Gases); _window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed; _window.FilterTransferRateChanged += OnFilterTransferRatePressed; diff --git a/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs b/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs index 28766c688a0..62748b52592 100644 --- a/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs +++ b/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs @@ -26,10 +26,9 @@ public sealed partial class GasFilterWindow : DefaultWindow public event Action? FilterTransferRateChanged; public event Action? SelectGasPressed; - public GasFilterWindow(IEnumerable gases) + public GasFilterWindow() { RobustXamlLoader.Load(this); - PopulateGasList(gases); ToggleStatusButton.OnPressed += _ => SetFilterStatus(!FilterStatus); ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke(); @@ -73,7 +72,7 @@ public void SetGasFiltered(string? id, string name) SelectGasButton.Disabled = true; } - private void PopulateGasList(IEnumerable gases) + public void PopulateGasList(IEnumerable gases) { GasList.Add(new ItemList.Item(GasList) { @@ -81,7 +80,7 @@ private void PopulateGasList(IEnumerable gases) Text = Loc.GetString("comp-gas-filter-ui-filter-gas-none") }); - foreach (GasPrototype gas in gases) + foreach (var gas in gases) { var gasName = Loc.GetString(gas.Name); GasList.Add(GetGasItem(gas.ID, gasName, GasList)); diff --git a/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs b/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs index 709c06517cb..392fbf1cd9a 100644 --- a/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs +++ b/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs @@ -2,7 +2,7 @@ using Content.Shared.Atmos.Piping.Trinary.Components; using Content.Shared.Localizations; using JetBrains.Annotations; -using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Atmos.UI { @@ -26,14 +26,7 @@ protected override void Open() { base.Open(); - _window = new GasMixerWindow(); - - if (State != null) - UpdateState(State); - - _window.OpenCentered(); - - _window.OnClose += Close; + _window = this.CreateWindow(); _window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed; _window.MixerOutputPressureChanged += OnMixerOutputPressurePressed; @@ -83,12 +76,5 @@ protected override void UpdateState(BoundUserInterfaceState state) _window.SetOutputPressure(cast.OutputPressure); _window.SetNodePercentages(cast.NodeOne); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - _window?.Dispose(); - } } } diff --git a/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs b/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs index 6eba2e0d215..220fdbe875c 100644 --- a/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs @@ -3,6 +3,7 @@ using Content.Shared.Localizations; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Atmos.UI { @@ -26,14 +27,7 @@ protected override void Open() { base.Open(); - _window = new GasPressurePumpWindow(); - - if (State != null) - UpdateState(State); - - _window.OpenCentered(); - - _window.OnClose += Close; + _window = this.CreateWindow(); _window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed; _window.PumpOutputPressureChanged += OnPumpOutputPressurePressed; @@ -67,12 +61,5 @@ protected override void UpdateState(BoundUserInterfaceState state) _window.SetPumpStatus(cast.Enabled); _window.SetOutputPressure(cast.OutputPressure); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - _window?.Dispose(); - } } } diff --git a/Content.Client/Atmos/UI/GasThermomachineBoundUserInterface.cs b/Content.Client/Atmos/UI/GasThermomachineBoundUserInterface.cs index 1664c8b9d75..d62be8f4bb4 100644 --- a/Content.Client/Atmos/UI/GasThermomachineBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasThermomachineBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Atmos.Piping.Unary.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Atmos.UI { @@ -31,14 +32,7 @@ protected override void Open() { base.Open(); - _window = new GasThermomachineWindow(); - - if (State != null) - UpdateState(State); - - _window.OpenCentered(); - - _window.OnClose += Close; + _window = this.CreateWindow(); _window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed(); _window.TemperatureSpinbox.OnValueChanged += _ => OnTemperatureChanged(_window.TemperatureSpinbox.Value); @@ -91,12 +85,5 @@ protected override void UpdateState(BoundUserInterfaceState state) true => Loc.GetString("comp-gas-thermomachine-ui-title-heater") }; } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - _window?.Dispose(); - } } } diff --git a/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs b/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs index 1b39306181a..642f34c2f92 100644 --- a/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs @@ -3,6 +3,7 @@ using Content.Shared.Localizations; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Atmos.UI { @@ -26,14 +27,7 @@ protected override void Open() { base.Open(); - _window = new GasVolumePumpWindow(); - - if (State != null) - UpdateState(State); - - _window.OpenCentered(); - - _window.OnClose += Close; + _window = this.CreateWindow(); _window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed; _window.PumpTransferRateChanged += OnPumpTransferRatePressed; @@ -64,16 +58,9 @@ protected override void UpdateState(BoundUserInterfaceState state) if (_window == null || state is not GasVolumePumpBoundUserInterfaceState cast) return; - _window.Title = (cast.PumpLabel); + _window.Title = cast.PumpLabel; _window.SetPumpStatus(cast.Enabled); _window.SetTransferRate(cast.TransferRate); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - _window?.Dispose(); - } } } diff --git a/Content.Client/Atmos/UI/SpaceHeaterBoundUserInterface.cs b/Content.Client/Atmos/UI/SpaceHeaterBoundUserInterface.cs index 4d8d1191e91..e70426575d4 100644 --- a/Content.Client/Atmos/UI/SpaceHeaterBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/SpaceHeaterBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Atmos.Piping.Portable.Components; using JetBrains.Annotations; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; namespace Content.Client.Atmos.UI; @@ -21,14 +22,7 @@ protected override void Open() { base.Open(); - _window = new SpaceHeaterWindow(); - - if (State != null) - UpdateState(State); - - _window.OpenCentered(); - - _window.OnClose += Close; + _window = this.CreateWindow(); _window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed(); _window.IncreaseTempRange.OnPressed += _ => OnTemperatureRangeChanged(_window.TemperatureChangeDelta); diff --git a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs index 60fe339069a..865dfc478d0 100644 --- a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs +++ b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs @@ -1,8 +1,7 @@ using Content.Shared.Audio.Jukebox; using Robust.Client.Audio; -using Robust.Client.Player; +using Robust.Client.UserInterface; using Robust.Shared.Audio.Components; -using Robust.Shared.Player; using Robust.Shared.Prototypes; namespace Content.Client.Audio.Jukebox; @@ -23,9 +22,7 @@ protected override void Open() { base.Open(); - _menu = new JukeboxMenu(); - _menu.OnClose += Close; - _menu.OpenCentered(); + _menu = this.CreateWindow(); _menu.OnPlayPressed += args => { @@ -100,19 +97,5 @@ public void SetTime(float time) SendMessage(new JukeboxSetTimeMessage(sentTime)); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - if (_menu == null) - return; - - _menu.OnClose -= Close; - _menu.Dispose(); - _menu = null; - } } diff --git a/Content.Client/Bed/Cryostorage/CryostorageBoundUserInterface.cs b/Content.Client/Bed/Cryostorage/CryostorageBoundUserInterface.cs index ffab1625483..09f3cec8fbf 100644 --- a/Content.Client/Bed/Cryostorage/CryostorageBoundUserInterface.cs +++ b/Content.Client/Bed/Cryostorage/CryostorageBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Bed.Cryostorage; using JetBrains.Annotations; +using Robust.Client.UserInterface; namespace Content.Client.Bed.Cryostorage; @@ -17,9 +18,7 @@ protected override void Open() { base.Open(); - _menu = new(); - - _menu.OnClose += Close; + _menu = this.CreateWindow(); _menu.SlotRemoveButtonPressed += (ent, slot) => { @@ -30,8 +29,6 @@ protected override void Open() { SendMessage(new CryostorageRemoveItemBuiMessage(ent, hand, CryostorageRemoveItemBuiMessage.RemovalType.Hand)); }; - - _menu.OpenCentered(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -45,12 +42,4 @@ protected override void UpdateState(BoundUserInterfaceState state) break; } } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - _menu?.Dispose(); - } } diff --git a/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs b/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs index d3365702bcf..44c40143d83 100644 --- a/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs +++ b/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Client.Cargo.UI; using Content.Shared.Cargo.Components; using JetBrains.Annotations; +using Robust.Client.UserInterface; namespace Content.Client.Cargo.BUI; @@ -18,9 +19,7 @@ protected override void Open() { base.Open(); - _menu = new(); - - _menu.OnClose += Close; + _menu = this.CreateWindow(); _menu.OnLabelButtonPressed += id => { @@ -31,8 +30,6 @@ protected override void Open() { SendMessage(new BountySkipMessage(id)); }; - - _menu.OpenCentered(); } protected override void UpdateState(BoundUserInterfaceState message) @@ -44,14 +41,4 @@ protected override void UpdateState(BoundUserInterfaceState message) _menu?.UpdateEntries(state.Bounties, state.UntilNextSkip); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (!disposing) - return; - - _menu?.Dispose(); - } } diff --git a/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs b/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs index 20c23a48a0d..2461dafb5f3 100644 --- a/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs +++ b/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Cargo.BUI; using Content.Shared.Cargo.Events; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Cargo.BUI; @@ -18,21 +19,9 @@ protected override void Open() { base.Open(); - _menu = new CargoPalletMenu(); + _menu = this.CreateWindow(); _menu.AppraiseRequested += OnAppraisal; _menu.SellRequested += OnSell; - _menu.OnClose += Close; - - _menu.OpenCentered(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - _menu?.Dispose(); - } } private void OnAppraisal() diff --git a/Content.Client/Cargo/BUI/CargoShuttleConsoleBoundUserInterface.cs b/Content.Client/Cargo/BUI/CargoShuttleConsoleBoundUserInterface.cs index 422d03707a0..02b721b9020 100644 --- a/Content.Client/Cargo/BUI/CargoShuttleConsoleBoundUserInterface.cs +++ b/Content.Client/Cargo/BUI/CargoShuttleConsoleBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Cargo.BUI; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Shared.Prototypes; namespace Content.Client.Cargo.BUI; @@ -9,6 +10,8 @@ namespace Content.Client.Cargo.BUI; [UsedImplicitly] public sealed class CargoShuttleConsoleBoundUserInterface : BoundUserInterface { + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [ViewVariables] private CargoShuttleMenu? _menu; @@ -19,24 +22,7 @@ public CargoShuttleConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base protected override void Open() { base.Open(); - var collection = IoCManager.Instance; - - if (collection == null) - return; - - _menu = new CargoShuttleMenu(collection.Resolve(), collection.Resolve().GetEntitySystem()); - _menu.OnClose += Close; - - _menu.OpenCentered(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - _menu?.Dispose(); - } + _menu = this.CreateWindow(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -45,6 +31,6 @@ protected override void UpdateState(BoundUserInterfaceState state) if (state is not CargoShuttleConsoleBoundUserInterfaceState cargoState) return; _menu?.SetAccountName(cargoState.AccountName); _menu?.SetShuttleName(cargoState.ShuttleName); - _menu?.SetOrders(cargoState.Orders); + _menu?.SetOrders(EntMan.System(), _protoManager, cargoState.Orders); } } diff --git a/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs b/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs index c591f917da3..43b00089e16 100644 --- a/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs +++ b/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs @@ -12,14 +12,9 @@ namespace Content.Client.Cargo.UI [GenerateTypedNameReferences] public sealed partial class CargoShuttleMenu : FancyWindow { - private readonly IPrototypeManager _protoManager; - private readonly SpriteSystem _spriteSystem; - - public CargoShuttleMenu(IPrototypeManager protoManager, SpriteSystem spriteSystem) + public CargoShuttleMenu() { RobustXamlLoader.Load(this); - _protoManager = protoManager; - _spriteSystem = spriteSystem; Title = Loc.GetString("cargo-shuttle-console-menu-title"); } @@ -33,19 +28,19 @@ public void SetShuttleName(string name) ShuttleNameLabel.Text = name; } - public void SetOrders(List orders) + public void SetOrders(SpriteSystem sprites, IPrototypeManager protoManager, List orders) { Orders.DisposeAllChildren(); foreach (var order in orders) { - var product = _protoManager.Index(order.ProductId); + var product = protoManager.Index(order.ProductId); var productName = product.Name; var row = new CargoOrderRow { Order = order, - Icon = { Texture = _spriteSystem.Frame0(product) }, + Icon = { Texture = sprites.Frame0(product) }, ProductName = { Text = Loc.GetString( diff --git a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs index 988fea7978b..3ef7f0ae73e 100644 --- a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs +++ b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Containers.ItemSlots; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Chemistry.UI { @@ -27,13 +28,8 @@ protected override void Open() base.Open(); // Setup window layout/elements - _window = new ChemMasterWindow - { - Title = EntMan.GetComponent(Owner).EntityName, - }; - - _window.OpenCentered(); - _window.OnClose += Close; + _window = this.CreateWindow(); + _window.Title = EntMan.GetComponent(Owner).EntityName; // Setup static button actions. _window.InputEjectButton.OnPressed += _ => SendMessage( @@ -75,15 +71,5 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(castState); // Update window state } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - _window?.Dispose(); - } - } } } diff --git a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs index 99e5a3d3953..2ad1b718887 100644 --- a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs +++ b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs @@ -3,6 +3,7 @@ using Content.Shared.Containers.ItemSlots; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Chemistry.UI { @@ -15,9 +16,6 @@ public sealed class ReagentDispenserBoundUserInterface : BoundUserInterface [ViewVariables] private ReagentDispenserWindow? _window; - [ViewVariables] - private ReagentDispenserBoundUserInterfaceState? _lastState; - public ReagentDispenserBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { } @@ -32,14 +30,9 @@ protected override void Open() base.Open(); // Setup window layout/elements - _window = new() - { - Title = EntMan.GetComponent(Owner).EntityName, - HelpGuidebookIds = EntMan.GetComponent(Owner).Guides - }; - - _window.OpenCentered(); - _window.OnClose += Close; + _window = this.CreateWindow(); + _window.Title = EntMan.GetComponent(Owner).EntityName; + _window.HelpGuidebookIds = EntMan.GetComponent(Owner).Guides; // Setup static button actions. _window.EjectButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(SharedReagentDispenser.OutputSlotName)); @@ -63,19 +56,7 @@ protected override void UpdateState(BoundUserInterfaceState state) base.UpdateState(state); var castState = (ReagentDispenserBoundUserInterfaceState) state; - _lastState = castState; - _window?.UpdateState(castState); //Update window state } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - _window?.Dispose(); - } - } } } diff --git a/Content.Client/Chemistry/UI/TransferAmountBoundUserInterface.cs b/Content.Client/Chemistry/UI/TransferAmountBoundUserInterface.cs index 35df131312d..f1cb27a62a4 100644 --- a/Content.Client/Chemistry/UI/TransferAmountBoundUserInterface.cs +++ b/Content.Client/Chemistry/UI/TransferAmountBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.FixedPoint; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Chemistry.UI { @@ -18,7 +19,7 @@ public TransferAmountBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne protected override void Open() { base.Open(); - _window = new TransferAmountWindow(); + _window = this.CreateWindow(); _window.ApplyButton.OnPressed += _ => { @@ -28,15 +29,6 @@ protected override void Open() _window.Close(); } }; - _window.OnClose += Close; - _window.OpenCentered(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - _window?.Dispose(); } } } diff --git a/Content.Client/CloningConsole/UI/CloningConsoleBoundUserInterface.cs b/Content.Client/CloningConsole/UI/CloningConsoleBoundUserInterface.cs index 26f0994701e..62a02f37186 100644 --- a/Content.Client/CloningConsole/UI/CloningConsoleBoundUserInterface.cs +++ b/Content.Client/CloningConsole/UI/CloningConsoleBoundUserInterface.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Robust.Client.GameObjects; using Content.Shared.Cloning.CloningConsole; +using Robust.Client.UserInterface; namespace Content.Client.CloningConsole.UI { @@ -17,13 +18,11 @@ public CloningConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne protected override void Open() { base.Open(); - _window = new CloningConsoleWindow - { - Title = Loc.GetString("cloning-console-window-title") - }; - _window.OnClose += Close; + + _window = this.CreateWindow(); + _window.Title = Loc.GetString("cloning-console-window-title"); + _window.CloneButton.OnPressed += _ => SendMessage(new UiButtonPressedMessage(UiButton.Clone)); - _window.OpenCentered(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -32,19 +31,5 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.Populate((CloningConsoleBoundUserInterfaceState) state); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - if (_window != null) - { - _window.OnClose -= Close; - _window.CloneButton.OnPressed -= _ => SendMessage(new UiButtonPressedMessage(UiButton.Clone)); - } - _window?.Dispose(); - } } } diff --git a/Content.Client/Clothing/UI/ChameleonBoundUserInterface.cs b/Content.Client/Clothing/UI/ChameleonBoundUserInterface.cs index 5b0d5fcf21f..83f6ba15662 100644 --- a/Content.Client/Clothing/UI/ChameleonBoundUserInterface.cs +++ b/Content.Client/Clothing/UI/ChameleonBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Clothing.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Clothing.UI; @@ -22,10 +23,8 @@ protected override void Open() { base.Open(); - _menu = new ChameleonMenu(); - _menu.OnClose += Close; + _menu = this.CreateWindow(); _menu.OnIdSelected += OnIdSelected; - _menu.OpenCentered(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -42,15 +41,4 @@ private void OnIdSelected(string selectedId) { SendMessage(new ChameleonPrototypeSelectedMessage(selectedId)); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - _menu?.Close(); - _menu = null; - } - } } diff --git a/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs b/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs index 1c94d32bf8d..0310e91eeb0 100644 --- a/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs +++ b/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.CCVar; using Content.Shared.Chat; using Content.Shared.Communications; +using Robust.Client.UserInterface; using Robust.Shared.Configuration; using Robust.Shared.Timing; @@ -8,34 +9,11 @@ namespace Content.Client.Communications.UI { public sealed class CommunicationsConsoleBoundUserInterface : BoundUserInterface { - [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [ViewVariables] private CommunicationsConsoleMenu? _menu; - [ViewVariables] - public bool CanAnnounce { get; private set; } - [ViewVariables] - public bool CanBroadcast { get; private set; } - - [ViewVariables] - public bool CanCall { get; private set; } - - [ViewVariables] - public bool CountdownStarted { get; private set; } - - [ViewVariables] - public bool AlertLevelSelectable { get; private set; } - - [ViewVariables] - public string CurrentLevel { get; private set; } = default!; - - [ViewVariables] - private TimeSpan? _expectedCountdownTime; - - public int Countdown => _expectedCountdownTime == null ? 0 : Math.Max((int) _expectedCountdownTime.Value.Subtract(_gameTiming.CurTime).TotalSeconds, 0); - public CommunicationsConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { } @@ -44,23 +22,25 @@ protected override void Open() { base.Open(); - _menu = new CommunicationsConsoleMenu(this); - _menu.OnClose += Close; - _menu.OpenCentered(); + _menu = this.CreateWindow(); + _menu.OnAnnounce += AnnounceButtonPressed; + _menu.OnBroadcast += BroadcastButtonPressed; + _menu.OnAlertLevel += AlertLevelSelected; + _menu.OnEmergencyLevel += EmergencyShuttleButtonPressed; } public void AlertLevelSelected(string level) { - if (AlertLevelSelectable) + if (_menu!.AlertLevelSelectable) { - CurrentLevel = level; + _menu.CurrentLevel = level; SendMessage(new CommunicationsConsoleSelectAlertLevelMessage(level)); } } public void EmergencyShuttleButtonPressed() { - if (CountdownStarted) + if (_menu!.CountdownStarted) RecallShuttle(); else CallShuttle(); @@ -95,31 +75,23 @@ protected override void UpdateState(BoundUserInterfaceState state) if (state is not CommunicationsConsoleInterfaceState commsState) return; - CanAnnounce = commsState.CanAnnounce; - CanBroadcast = commsState.CanBroadcast; - CanCall = commsState.CanCall; - _expectedCountdownTime = commsState.ExpectedCountdownEnd; - CountdownStarted = commsState.CountdownStarted; - AlertLevelSelectable = commsState.AlertLevels != null && !float.IsNaN(commsState.CurrentAlertDelay) && commsState.CurrentAlertDelay <= 0; - CurrentLevel = commsState.CurrentAlert; - if (_menu != null) { + _menu.CanAnnounce = commsState.CanAnnounce; + _menu.CanBroadcast = commsState.CanBroadcast; + _menu.CanCall = commsState.CanCall; + _menu.CountdownStarted = commsState.CountdownStarted; + _menu.AlertLevelSelectable = commsState.AlertLevels != null && !float.IsNaN(commsState.CurrentAlertDelay) && commsState.CurrentAlertDelay <= 0; + _menu.CurrentLevel = commsState.CurrentAlert; + _menu.CountdownEnd = commsState.ExpectedCountdownEnd; + _menu.UpdateCountdown(); - _menu.UpdateAlertLevels(commsState.AlertLevels, CurrentLevel); - _menu.AlertLevelButton.Disabled = !AlertLevelSelectable; - _menu.EmergencyShuttleButton.Disabled = !CanCall; - _menu.AnnounceButton.Disabled = !CanAnnounce; - _menu.BroadcastButton.Disabled = !CanBroadcast; + _menu.UpdateAlertLevels(commsState.AlertLevels, _menu.CurrentLevel); + _menu.AlertLevelButton.Disabled = !_menu.AlertLevelSelectable; + _menu.EmergencyShuttleButton.Disabled = !_menu.CanCall; + _menu.AnnounceButton.Disabled = !_menu.CanAnnounce; + _menu.BroadcastButton.Disabled = !_menu.CanBroadcast; } } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - - _menu?.Dispose(); - } } } diff --git a/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs b/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs index 4d8dd86a4dc..63868e7a93e 100644 --- a/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs +++ b/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs @@ -1,31 +1,40 @@ -using Content.Client.UserInterface.Controls; -using System.Threading; +using System.Globalization; +using Content.Client.UserInterface.Controls; using Content.Shared.CCVar; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.XAML; using Robust.Shared.Configuration; +using Robust.Shared.Timing; using Robust.Shared.Utility; -using Timer = Robust.Shared.Timing.Timer; namespace Content.Client.Communications.UI { [GenerateTypedNameReferences] public sealed partial class CommunicationsConsoleMenu : FancyWindow { - private CommunicationsConsoleBoundUserInterface Owner { get; set; } - private readonly CancellationTokenSource _timerCancelTokenSource = new(); - [Dependency] private readonly IConfigurationManager _cfg = default!; - - public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner) + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ILocalizationManager _loc = default!; + + public bool CanAnnounce; + public bool CanBroadcast; + public bool CanCall; + public bool AlertLevelSelectable; + public bool CountdownStarted; + public string CurrentLevel = string.Empty; + public TimeSpan? CountdownEnd; + + public event Action? OnEmergencyLevel; + public event Action? OnAlertLevel; + public event Action? OnAnnounce; + public event Action? OnBroadcast; + + public CommunicationsConsoleMenu() { IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); - Owner = owner; - - var loc = IoCManager.Resolve(); - MessageInput.Placeholder = new Rope.Leaf(loc.GetString("comms-console-menu-announcement-placeholder")); + MessageInput.Placeholder = new Rope.Leaf(_loc.GetString("comms-console-menu-announcement-placeholder")); var maxAnnounceLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength); MessageInput.OnTextChanged += (args) => @@ -37,33 +46,38 @@ public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner) } else { - AnnounceButton.Disabled = !owner.CanAnnounce; + AnnounceButton.Disabled = !CanAnnounce; AnnounceButton.ToolTip = null; } }; - AnnounceButton.OnPressed += (_) => Owner.AnnounceButtonPressed(Rope.Collapse(MessageInput.TextRope)); - AnnounceButton.Disabled = !owner.CanAnnounce; + AnnounceButton.OnPressed += _ => OnAnnounce?.Invoke(Rope.Collapse(MessageInput.TextRope)); + AnnounceButton.Disabled = !CanAnnounce; - BroadcastButton.OnPressed += (_) => Owner.BroadcastButtonPressed(Rope.Collapse(MessageInput.TextRope)); - BroadcastButton.Disabled = !owner.CanBroadcast; + BroadcastButton.OnPressed += _ => OnBroadcast?.Invoke(Rope.Collapse(MessageInput.TextRope)); + BroadcastButton.Disabled = !CanBroadcast; AlertLevelButton.OnItemSelected += args => { var metadata = AlertLevelButton.GetItemMetadata(args.Id); if (metadata != null && metadata is string cast) { - Owner.AlertLevelSelected(cast); + OnAlertLevel?.Invoke(cast); } }; - AlertLevelButton.Disabled = !owner.AlertLevelSelectable; - EmergencyShuttleButton.OnPressed += (_) => Owner.EmergencyShuttleButtonPressed(); - EmergencyShuttleButton.Disabled = !owner.CanCall; + AlertLevelButton.Disabled = !AlertLevelSelectable; + + EmergencyShuttleButton.OnPressed += _ => OnEmergencyLevel?.Invoke(); + EmergencyShuttleButton.Disabled = !CanCall; + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); UpdateCountdown(); - Timer.SpawnRepeating(1000, UpdateCountdown, _timerCancelTokenSource.Token); } // The current alert could make levels unselectable, so we need to ensure that the UI reacts properly. @@ -105,30 +119,19 @@ public void UpdateAlertLevels(List? alerts, string currentAlert) public void UpdateCountdown() { - if (!Owner.CountdownStarted) + if (!CountdownStarted) { - CountdownLabel.SetMessage(""); + CountdownLabel.SetMessage(string.Empty); EmergencyShuttleButton.Text = Loc.GetString("comms-console-menu-call-shuttle"); return; } - EmergencyShuttleButton.Text = Loc.GetString("comms-console-menu-recall-shuttle"); - CountdownLabel.SetMessage($"Time remaining\n{Owner.Countdown.ToString()}s"); - } - - public override void Close() - { - base.Close(); - - _timerCancelTokenSource.Cancel(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); + var diff = MathHelper.Max((CountdownEnd - _timing.CurTime) ?? TimeSpan.Zero, TimeSpan.Zero); - if (disposing) - _timerCancelTokenSource.Cancel(); + EmergencyShuttleButton.Text = Loc.GetString("comms-console-menu-recall-shuttle"); + var infoText = Loc.GetString($"comms-console-menu-time-remaining", + ("time", diff.TotalSeconds.ToString(CultureInfo.CurrentCulture))); + CountdownLabel.SetMessage(infoText); } } } diff --git a/Content.Client/Computer/ComputerBoundUserInterface.cs b/Content.Client/Computer/ComputerBoundUserInterface.cs index bdbfe03fa10..11c26b252e9 100644 --- a/Content.Client/Computer/ComputerBoundUserInterface.cs +++ b/Content.Client/Computer/ComputerBoundUserInterface.cs @@ -1,4 +1,5 @@ using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.CustomControls; namespace Content.Client.Computer @@ -19,10 +20,8 @@ protected override void Open() { base.Open(); - _window = (TWindow) _dynamicTypeFactory.CreateInstance(typeof(TWindow)); + _window = this.CreateWindow(); _window.SetupComputerWindow(this); - _window.OnClose += Close; - _window.OpenCentered(); } // Alas, this constructor has to be copied to the subclass. :( @@ -42,16 +41,6 @@ protected override void UpdateState(BoundUserInterfaceState state) _window.UpdateState((TState) state); } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - _window?.Dispose(); - } - } - protected override void ReceiveMessage(BoundUserInterfaceMessage message) { _window?.ReceiveMessage(message); diff --git a/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs b/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs index 4fea44f2253..e4966f1ec43 100644 --- a/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs +++ b/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs @@ -1,5 +1,6 @@ using System.Text.RegularExpressions; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using static Content.Shared.Configurable.ConfigurationComponent; namespace Content.Client.Configurable.UI @@ -9,9 +10,6 @@ public sealed class ConfigurationBoundUserInterface : BoundUserInterface [ViewVariables] private ConfigurationMenu? _menu; - [ViewVariables] - public Regex? Validation { get; internal set; } - public ConfigurationBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { } @@ -19,10 +17,8 @@ public ConfigurationBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner protected override void Open() { base.Open(); - _menu = new ConfigurationMenu(this); - - _menu.OnClose += Close; - _menu.OpenCentered(); + _menu = this.CreateWindow(); + _menu.OnConfiguration += SendConfiguration; } protected override void UpdateState(BoundUserInterfaceState state) @@ -30,9 +26,7 @@ protected override void UpdateState(BoundUserInterfaceState state) base.UpdateState(state); if (state is not ConfigurationBoundUserInterfaceState configurationState) - { return; - } _menu?.Populate(configurationState); } @@ -41,9 +35,12 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message) { base.ReceiveMessage(message); + if (_menu == null) + return; + if (message is ValidationUpdateMessage msg) { - Validation = new Regex(msg.ValidationString, RegexOptions.Compiled); + _menu.Validation = new Regex(msg.ValidationString, RegexOptions.Compiled); } } @@ -51,16 +48,5 @@ public void SendConfiguration(Dictionary config) { SendMessage(new ConfigurationUpdatedMessage(config)); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing && _menu != null) - { - _menu.OnClose -= Close; - _menu.Close(); - } - } } } diff --git a/Content.Client/Configurable/UI/ConfigurationMenu.cs b/Content.Client/Configurable/UI/ConfigurationMenu.cs index cc24af28692..29217eef7be 100644 --- a/Content.Client/Configurable/UI/ConfigurationMenu.cs +++ b/Content.Client/Configurable/UI/ConfigurationMenu.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Numerics; +using System.Text.RegularExpressions; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; @@ -13,23 +14,25 @@ namespace Content.Client.Configurable.UI { public sealed class ConfigurationMenu : DefaultWindow { - public ConfigurationBoundUserInterface Owner { get; } - private readonly BoxContainer _column; private readonly BoxContainer _row; private readonly List<(string name, LineEdit input)> _inputs; - public ConfigurationMenu(ConfigurationBoundUserInterface owner) + [ViewVariables] + public Regex? Validation { get; internal set; } + + public event Action>? OnConfiguration; + + public ConfigurationMenu() { MinSize = SetSize = new Vector2(300, 250); - Owner = owner; _inputs = new List<(string name, LineEdit input)>(); Title = Loc.GetString("configuration-menu-device-title"); - BoxContainer baseContainer = new BoxContainer + var baseContainer = new BoxContainer { Orientation = LayoutOrientation.Vertical, VerticalExpand = true, @@ -116,14 +119,13 @@ public void Populate(ConfigurationBoundUserInterfaceState state) private void OnConfirm(ButtonEventArgs args) { var config = GenerateDictionary(_inputs, "Text"); - - Owner.SendConfiguration(config); + OnConfiguration?.Invoke(config); Close(); } private bool Validate(string value) { - return Owner.Validation == null || Owner.Validation.IsMatch(value); + return Validation?.IsMatch(value) != false; } private Dictionary GenerateDictionary(IEnumerable<(string name, LineEdit input)> inputs, string propertyName) diff --git a/Content.Client/Construction/UI/FlatpackCreatorBoundUserInterface.cs b/Content.Client/Construction/UI/FlatpackCreatorBoundUserInterface.cs index 86f1b8b83c7..887492955e9 100644 --- a/Content.Client/Construction/UI/FlatpackCreatorBoundUserInterface.cs +++ b/Content.Client/Construction/UI/FlatpackCreatorBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Construction.Components; using JetBrains.Annotations; +using Robust.Client.UserInterface; namespace Content.Client.Construction.UI { @@ -17,8 +18,8 @@ protected override void Open() { base.Open(); - _menu = new FlatpackCreatorMenu(Owner); - _menu.OnClose += Close; + _menu = this.CreateWindow(); + _menu.SetEntity(Owner); _menu.PackButtonPressed += () => { @@ -27,14 +28,5 @@ protected override void Open() _menu.OpenCentered(); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - _menu?.Dispose(); - } } } diff --git a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs index fcf0ada947c..f020991224c 100644 --- a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs +++ b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs @@ -26,7 +26,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow private readonly MaterialStorageSystem _materialStorage; private readonly SpriteSystem _spriteSystem; - private readonly EntityUid _owner; + private EntityUid _owner; [ValidatePrototypeId] public const string NoBoardEffectId = "FlatpackerNoBoardEffect"; @@ -36,7 +36,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow public event Action? PackButtonPressed; - public FlatpackCreatorMenu(EntityUid uid) + public FlatpackCreatorMenu() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); @@ -46,14 +46,17 @@ public FlatpackCreatorMenu(EntityUid uid) _materialStorage = _entityManager.System(); _spriteSystem = _entityManager.System(); - _owner = uid; - PackButton.OnPressed += _ => PackButtonPressed?.Invoke(); - MaterialStorageControl.SetOwner(uid); InsertLabel.SetMarkup(Loc.GetString("flatpacker-ui-insert-board")); } + public void SetEntity(EntityUid uid) + { + _owner = uid; + MaterialStorageControl.SetOwner(uid); + } + protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); diff --git a/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs b/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs index e2c4d51ecd1..e5be0b1811f 100644 --- a/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs +++ b/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs @@ -2,12 +2,15 @@ using Content.Shared.Crayon; using Content.Shared.Decals; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Shared.Prototypes; namespace Content.Client.Crayon.UI { public sealed class CrayonBoundUserInterface : BoundUserInterface { + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [ViewVariables] private CrayonWindow? _menu; @@ -18,15 +21,29 @@ public CrayonBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey protected override void Open() { base.Open(); - _menu = new CrayonWindow(this); - - _menu.OnClose += Close; - var prototypeManager = IoCManager.Resolve(); - var crayonDecals = prototypeManager.EnumeratePrototypes().Where(x => x.Tags.Contains("crayon")); - _menu.Populate(crayonDecals); + _menu = this.CreateWindow(); + _menu.OnColorSelected += SelectColor; + _menu.OnSelected += Select; + PopulateCrayons(); _menu.OpenCenteredLeft(); } + private void PopulateCrayons() + { + var crayonDecals = _protoManager.EnumeratePrototypes().Where(x => x.Tags.Contains("crayon")); + _menu?.Populate(crayonDecals); + } + + public override void OnProtoReload(PrototypesReloadedEventArgs args) + { + base.OnProtoReload(args); + + if (!args.WasModified()) + return; + + PopulateCrayons(); + } + protected override void UpdateState(BoundUserInterfaceState state) { base.UpdateState(state); @@ -43,16 +60,5 @@ public void SelectColor(Color color) { SendMessage(new CrayonColorMessage(color)); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - _menu?.Close(); - _menu = null; - } - } } } diff --git a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs index 2a5801ccf2d..b97786cd41a 100644 --- a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs +++ b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs @@ -18,18 +18,17 @@ namespace Content.Client.Crayon.UI [GenerateTypedNameReferences] public sealed partial class CrayonWindow : DefaultWindow { - public CrayonBoundUserInterface Owner { get; } - private Dictionary? _decals; private string? _selected; private Color _color; - public CrayonWindow(CrayonBoundUserInterface owner) + public event Action? OnColorSelected; + public event Action? OnSelected; + + public CrayonWindow() { RobustXamlLoader.Load(this); - Owner = owner; - Search.OnTextChanged += _ => RefreshList(); ColorSelector.OnColorChanged += SelectColor; } @@ -38,16 +37,16 @@ private void SelectColor(Color color) { _color = color; - Owner.SelectColor(color); - + OnColorSelected?.Invoke(color); RefreshList(); } private void RefreshList() { // Clear - Grid.RemoveAllChildren(); - if (_decals == null) return; + Grid.DisposeAllChildren(); + if (_decals == null) + return; var filter = Search.Text; foreach (var (decal, tex) in _decals) @@ -89,7 +88,6 @@ private void ButtonOnPressed(ButtonEventArgs obj) { if (obj.Button.Name == null) return; - Owner.Select(obj.Button.Name); _selected = obj.Button.Name; RefreshList(); } diff --git a/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs b/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs index e8e77217ea5..296e71d3a95 100644 --- a/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs +++ b/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; namespace Content.Client.Disposal.UI @@ -21,20 +22,16 @@ protected override void Open() { base.Open(); - _window = new DisposalRouterWindow(); - - _window.OpenCentered(); - _window.OnClose += Close; + _window = this.CreateWindow(); _window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text); _window.TagInput.OnTextEntered += args => ButtonPressed(UiAction.Ok, args.Text); - } private void ButtonPressed(UiAction action, string tag) { SendMessage(new UiActionMessage(action, tag)); - _window?.Close(); + Close(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -48,18 +45,5 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(cast); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - _window?.Dispose(); - } - } - - } - } diff --git a/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs b/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs index 3aeed8dc802..7fc0eb85401 100644 --- a/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs +++ b/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; namespace Content.Client.Disposal.UI @@ -21,20 +22,17 @@ protected override void Open() { base.Open(); - _window = new DisposalTaggerWindow(); - - _window.OpenCentered(); - _window.OnClose += Close; + _window = this.CreateWindow(); _window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text); _window.TagInput.OnTextEntered += args => ButtonPressed(UiAction.Ok, args.Text); - } private void ButtonPressed(UiAction action, string tag) { + // TODO: This looks copy-pasted with the other mailing stuff... SendMessage(new UiActionMessage(action, tag)); - _window?.Close(); + Close(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -48,18 +46,5 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(cast); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - _window?.Dispose(); - } - } - - } - } diff --git a/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs b/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs index cd7ea717ce3..9b7e23c03aa 100644 --- a/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs +++ b/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Access; using Content.Shared.Doors.Electronics; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Shared.Prototypes; namespace Content.Client.Doors.Electronics; @@ -18,6 +19,23 @@ public DoorElectronicsBoundUserInterface(EntityUid owner, Enum uiKey) : base(own protected override void Open() { base.Open(); + _window = this.CreateWindow(); + _window.OnAccessChanged += UpdateConfiguration; + Reset(); + } + + public override void OnProtoReload(PrototypesReloadedEventArgs args) + { + base.OnProtoReload(args); + + if (!args.WasModified()) + return; + + Reset(); + } + + private void Reset() + { List> accessLevels = new(); foreach (var accessLevel in _prototypeManager.EnumeratePrototypes()) @@ -29,10 +47,7 @@ protected override void Open() } accessLevels.Sort(); - - _window = new DoorElectronicsConfigurationMenu(this, accessLevels, _prototypeManager); - _window.OnClose += Close; - _window.OpenCentered(); + _window?.Reset(_prototypeManager, accessLevels); } protected override void UpdateState(BoundUserInterfaceState state) @@ -44,14 +59,6 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(castState); } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - - _window?.Dispose(); - } - public void UpdateConfiguration(List> newAccessList) { SendMessage(new DoorElectronicsUpdateConfigurationMessage(newAccessList)); diff --git a/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs b/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs index c01f13a462e..2112a562971 100644 --- a/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs +++ b/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs @@ -15,22 +15,23 @@ namespace Content.Client.Doors.Electronics; [GenerateTypedNameReferences] public sealed partial class DoorElectronicsConfigurationMenu : FancyWindow { - private readonly DoorElectronicsBoundUserInterface _owner; - private AccessLevelControl _buttonsList = new(); + private readonly AccessLevelControl _buttonsList = new(); - public DoorElectronicsConfigurationMenu(DoorElectronicsBoundUserInterface ui, List> accessLevels, IPrototypeManager prototypeManager) + public event Action>>? OnAccessChanged; + + public DoorElectronicsConfigurationMenu() { RobustXamlLoader.Load(this); - - _owner = ui; - - _buttonsList.Populate(accessLevels, prototypeManager); AccessLevelControlContainer.AddChild(_buttonsList); + } + + public void Reset(IPrototypeManager protoManager, List> accessLevels) + { + _buttonsList.Populate(accessLevels, protoManager); - foreach (var (id, button) in _buttonsList.ButtonsList) + foreach (var button in _buttonsList.ButtonsList.Values) { - button.OnPressed += _ => _owner.UpdateConfiguration( - _buttonsList.ButtonsList.Where(x => x.Value.Pressed).Select(x => x.Key).ToList()); + button.OnPressed += _ => OnAccessChanged?.Invoke(_buttonsList.ButtonsList.Where(x => x.Value.Pressed).Select(x => x.Key).ToList()); } } diff --git a/Content.Client/Fax/UI/FaxBoundUi.cs b/Content.Client/Fax/UI/FaxBoundUi.cs index a95066a3b58..ca2e834b4fe 100644 --- a/Content.Client/Fax/UI/FaxBoundUi.cs +++ b/Content.Client/Fax/UI/FaxBoundUi.cs @@ -25,10 +25,7 @@ protected override void Open() { base.Open(); - _window = new FaxWindow(); - _window.OpenCentered(); - - _window.OnClose += Close; + _window = this.CreateWindow(); _window.FileButtonPressed += OnFileButtonPressed; _window.CopyButtonPressed += OnCopyButtonPressed; _window.SendButtonPressed += OnSendButtonPressed; @@ -104,11 +101,4 @@ protected override void UpdateState(BoundUserInterfaceState state) _window.UpdateState(cast); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - _window?.Dispose(); - } } diff --git a/Content.Client/Forensics/ForensicScannerBoundUserInterface.cs b/Content.Client/Forensics/ForensicScannerBoundUserInterface.cs index ba49f11ea0f..08596b04e6e 100644 --- a/Content.Client/Forensics/ForensicScannerBoundUserInterface.cs +++ b/Content.Client/Forensics/ForensicScannerBoundUserInterface.cs @@ -1,6 +1,7 @@ using Robust.Client.GameObjects; using Robust.Shared.Timing; using Content.Shared.Forensics; +using Robust.Client.UserInterface; namespace Content.Client.Forensics { @@ -21,11 +22,9 @@ public ForensicScannerBoundUserInterface(EntityUid owner, Enum uiKey) : base(own protected override void Open() { base.Open(); - _window = new ForensicScannerMenu(); - _window.OnClose += Close; + _window = this.CreateWindow(); _window.Print.OnPressed += _ => Print(); _window.Clear.OnPressed += _ => Clear(); - _window.OpenCentered(); } private void Print() @@ -62,6 +61,7 @@ protected override void UpdateState(BoundUserInterfaceState state) _printCooldown = cast.PrintCooldown; + // TODO: Fix this if (cast.PrintReadyAt > _gameTiming.CurTime) Timer.Spawn(cast.PrintReadyAt - _gameTiming.CurTime, () => { @@ -71,14 +71,5 @@ protected override void UpdateState(BoundUserInterfaceState state) _window.UpdateState(cast); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - _window?.Dispose(); - } } } diff --git a/Content.Client/Gateway/UI/GatewayBoundUserInterface.cs b/Content.Client/Gateway/UI/GatewayBoundUserInterface.cs index fdb3cdbc010..457b70ca7ca 100644 --- a/Content.Client/Gateway/UI/GatewayBoundUserInterface.cs +++ b/Content.Client/Gateway/UI/GatewayBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Gateway; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Gateway.UI; @@ -17,24 +18,13 @@ protected override void Open() { base.Open(); - _window = new GatewayWindow(EntMan.GetNetEntity(Owner)); + _window = this.CreateWindow(); + _window.SetEntity(EntMan.GetNetEntity(Owner)); _window.OpenPortal += destination => { SendMessage(new GatewayOpenPortalMessage(destination)); }; - _window.OnClose += Close; - _window?.OpenCentered(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - _window?.Dispose(); - _window = null; - } } protected override void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Client/Gateway/UI/GatewayWindow.xaml.cs b/Content.Client/Gateway/UI/GatewayWindow.xaml.cs index b3e1333a7a9..0cc03a36149 100644 --- a/Content.Client/Gateway/UI/GatewayWindow.xaml.cs +++ b/Content.Client/Gateway/UI/GatewayWindow.xaml.cs @@ -22,7 +22,7 @@ public sealed partial class GatewayWindow : FancyWindow, public event Action? OpenPortal; private List _destinations = new(); - public readonly NetEntity Owner; + public NetEntity Owner; private NetEntity? _current; private TimeSpan _nextReady; @@ -46,16 +46,20 @@ public sealed partial class GatewayWindow : FancyWindow, /// private bool _isCooldownPending = true; - public GatewayWindow(NetEntity netEntity) + public GatewayWindow() { RobustXamlLoader.Load(this); var dependencies = IoCManager.Instance!; _timing = dependencies.Resolve(); - Owner = netEntity; NextUnlockBar.ForegroundStyleBoxOverride = new StyleBoxFlat(Color.FromHex("#C74EBD")); } + public void SetEntity(NetEntity entity) + { + + } + public void UpdateState(GatewayBoundUserInterfaceState state) { _destinations = state.Destinations; diff --git a/Content.Client/Gravity/UI/GravityGeneratorBoundUserInterface.cs b/Content.Client/Gravity/UI/GravityGeneratorBoundUserInterface.cs index d72da3e8120..32b40747d55 100644 --- a/Content.Client/Gravity/UI/GravityGeneratorBoundUserInterface.cs +++ b/Content.Client/Gravity/UI/GravityGeneratorBoundUserInterface.cs @@ -1,6 +1,6 @@ using Content.Shared.Gravity; using JetBrains.Annotations; -using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Gravity.UI { @@ -18,17 +18,8 @@ protected override void Open() { base.Open(); - _window = new GravityGeneratorWindow(this); - - /* - _window.Switch.OnPressed += _ => - { - SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(!IsOn)); - }; - */ - - _window.OpenCentered(); - _window.OnClose += Close; + _window = this.CreateWindow(); + _window.SetEntity(Owner); } protected override void UpdateState(BoundUserInterfaceState state) @@ -39,14 +30,6 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(castState); } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - - _window?.Dispose(); - } - public void SetPowerSwitch(bool on) { SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(on)); diff --git a/Content.Client/Gravity/UI/GravityGeneratorWindow.xaml.cs b/Content.Client/Gravity/UI/GravityGeneratorWindow.xaml.cs index 75f8eb479b5..6f04133b594 100644 --- a/Content.Client/Gravity/UI/GravityGeneratorWindow.xaml.cs +++ b/Content.Client/Gravity/UI/GravityGeneratorWindow.xaml.cs @@ -12,22 +12,23 @@ public sealed partial class GravityGeneratorWindow : FancyWindow { private readonly ButtonGroup _buttonGroup = new(); - private readonly GravityGeneratorBoundUserInterface _owner; + public event Action? OnPowerSwitch; - public GravityGeneratorWindow(GravityGeneratorBoundUserInterface owner) + public GravityGeneratorWindow() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - _owner = owner; - OnButton.Group = _buttonGroup; OffButton.Group = _buttonGroup; - OnButton.OnPressed += _ => _owner.SetPowerSwitch(true); - OffButton.OnPressed += _ => _owner.SetPowerSwitch(false); + OnButton.OnPressed += _ => OnPowerSwitch?.Invoke(true); + OffButton.OnPressed += _ => OnPowerSwitch?.Invoke(false); + } - EntityView.SetEntity(owner.Owner); + public void SetEntity(EntityUid uid) + { + EntityView.SetEntity(uid); } public void UpdateState(SharedGravityGeneratorComponent.GeneratorState state) diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs index 39bb52d72c0..dba7e39b63a 100644 --- a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs +++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs @@ -1,7 +1,7 @@ using Content.Shared.MedicalScanner; using Content.Shared.Targeting; using JetBrains.Annotations; -using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.HealthAnalyzer.UI { @@ -18,13 +18,9 @@ public HealthAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne protected override void Open() { base.Open(); - _window = new HealthAnalyzerWindow - { - Title = EntMan.GetComponent(Owner).EntityName, - }; - _window.OnClose += Close; - _window.OnBodyPartSelected += SendBodyPartMessage; - _window.OpenCentered(); + _window = this.CreateWindow(); + + _window.Title = EntMan.GetComponent(Owner).EntityName; } protected override void ReceiveMessage(BoundUserInterfaceMessage message) @@ -37,22 +33,5 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message) _window.Populate(cast); } - - private void SendBodyPartMessage(TargetBodyPart? part, EntityUid target) => SendMessage(new HealthAnalyzerPartMessage(EntMan.GetNetEntity(target), part ?? null)); - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - if (_window != null) - { - _window.OnClose -= Close; - _window.OnBodyPartSelected -= SendBodyPartMessage; - } - - _window?.Dispose(); - } } } diff --git a/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs b/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs index a8872604a4c..53977eb636b 100644 --- a/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs +++ b/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Humanoid; using Content.Shared.Humanoid.Markings; +using Robust.Client.UserInterface; namespace Content.Client.Humanoid; @@ -20,8 +21,7 @@ protected override void Open() { base.Open(); - _window = new(); - _window.OnClose += Close; + _window = this.CreateWindow(); _window.OnMarkingAdded += SendMarkingSet; _window.OnMarkingRemoved += SendMarkingSet; _window.OnMarkingColorChange += SendMarkingSetNoResend; diff --git a/Content.Client/Instruments/UI/BandMenu.xaml.cs b/Content.Client/Instruments/UI/BandMenu.xaml.cs index 5fb293a194d..26cd1369e55 100644 --- a/Content.Client/Instruments/UI/BandMenu.xaml.cs +++ b/Content.Client/Instruments/UI/BandMenu.xaml.cs @@ -11,7 +11,9 @@ public sealed partial class BandMenu : DefaultWindow { private readonly InstrumentBoundUserInterface _owner; - public BandMenu(InstrumentBoundUserInterface owner) : base() + public EntityUid? Master; + + public BandMenu(InstrumentBoundUserInterface owner) { RobustXamlLoader.Load(this); @@ -40,7 +42,7 @@ public void Populate((NetEntity, string)[] nearby, IEntityManager entManager) { var uid = entManager.GetEntity(nent); var item = BandList.AddItem(name, null, true, uid); - item.Selected = _owner.Instrument?.Master == uid; + item.Selected = Master == uid; } } } diff --git a/Content.Client/Instruments/UI/ChannelsMenu.xaml.cs b/Content.Client/Instruments/UI/ChannelsMenu.xaml.cs index 2814d415365..c175e67842f 100644 --- a/Content.Client/Instruments/UI/ChannelsMenu.xaml.cs +++ b/Content.Client/Instruments/UI/ChannelsMenu.xaml.cs @@ -51,7 +51,7 @@ private void OnClearPressed(BaseButton.ButtonEventArgs obj) } } - public void Populate() + public void Populate(InstrumentComponent? instrument) { ChannelList.Clear(); @@ -60,7 +60,8 @@ public void Populate() var item = ChannelList.AddItem(_owner.Loc.GetString("instrument-component-channel-name", ("number", i)), null, true, i); - item.Selected = !_owner.Instrument?.FilteredChannels[i] ?? false; + + item.Selected = !instrument?.FilteredChannels[i] ?? false; } } } diff --git a/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs b/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs index 0f5729f55b1..4816ce8c365 100644 --- a/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs +++ b/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs @@ -24,8 +24,6 @@ public sealed class InstrumentBoundUserInterface : BoundUserInterface [ViewVariables] private BandMenu? _bandMenu; [ViewVariables] private ChannelsMenu? _channelsMenu; - [ViewVariables] public InstrumentComponent? Instrument { get; private set; } - public InstrumentBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { IoCManager.InjectDependencies(this); @@ -43,14 +41,20 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message) protected override void Open() { - if (!EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument)) - return; + _instrumentMenu = this.CreateWindow(); + _instrumentMenu.Title = EntMan.GetComponent(Owner).EntityName; - Instrument = instrument; - _instrumentMenu = new InstrumentMenu(this); - _instrumentMenu.OnClose += Close; + _instrumentMenu.OnOpenBand += OpenBandMenu; + _instrumentMenu.OnOpenChannels += OpenChannelsMenu; + _instrumentMenu.OnCloseChannels += CloseChannelsMenu; + _instrumentMenu.OnCloseBands += CloseBandMenu; - _instrumentMenu.OpenCentered(); + _instrumentMenu.SetMIDI(MidiManager.IsAvailable); + + if (EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument)) + { + _instrumentMenu.SetInstrument((Owner, instrument)); + } } protected override void Dispose(bool disposing) @@ -58,7 +62,12 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); if (!disposing) return; - _instrumentMenu?.Dispose(); + + if (EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument)) + { + _instrumentMenu?.RemoveInstrument(instrument); + } + _bandMenu?.Dispose(); _channelsMenu?.Dispose(); } @@ -72,6 +81,11 @@ public void OpenBandMenu() { _bandMenu ??= new BandMenu(this); + if (EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument)) + { + _bandMenu.Master = instrument.Master; + } + // Refresh cache... RefreshBands(); @@ -87,7 +101,9 @@ public void CloseBandMenu() public void OpenChannelsMenu() { _channelsMenu ??= new ChannelsMenu(this); - _channelsMenu.Populate(); + EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument); + + _channelsMenu.Populate(instrument); _channelsMenu.OpenCenteredRight(); } diff --git a/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs b/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs index b54a91ee975..9b14e01fb57 100644 --- a/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs +++ b/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs @@ -1,7 +1,10 @@ using System.IO; using System.Numerics; using System.Threading.Tasks; +using Content.Client.Interactable; +using Content.Shared.ActionBlocker; using Robust.Client.AutoGenerated; +using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; @@ -16,33 +19,23 @@ namespace Content.Client.Instruments.UI [GenerateTypedNameReferences] public sealed partial class InstrumentMenu : DefaultWindow { - private readonly InstrumentBoundUserInterface _owner; + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IFileDialogManager _dialogs = default!; + [Dependency] private readonly IPlayerManager _player = default!; private bool _isMidiFileDialogueWindowOpen; - public InstrumentMenu(InstrumentBoundUserInterface owner) - { - RobustXamlLoader.Load(this); - - _owner = owner; + public event Action? OnOpenBand; + public event Action? OnOpenChannels; + public event Action? OnCloseBands; + public event Action? OnCloseChannels; - if (_owner.Instrument != null) - { - _owner.Instrument.OnMidiPlaybackEnded += InstrumentOnMidiPlaybackEnded; - Title = _owner.Entities.GetComponent(_owner.Owner).EntityName; - LoopButton.Disabled = !_owner.Instrument.IsMidiOpen; - LoopButton.Pressed = _owner.Instrument.LoopMidi; - ChannelsButton.Disabled = !_owner.Instrument.IsRendererAlive; - StopButton.Disabled = !_owner.Instrument.IsMidiOpen; - PlaybackSlider.MouseFilter = _owner.Instrument.IsMidiOpen ? MouseFilterMode.Pass : MouseFilterMode.Ignore; - } + public EntityUid Entity; - if (!_owner.MidiManager.IsAvailable) - { - UnavailableOverlay.Visible = true; - // We return early as to not give the buttons behavior. - return; - } + public InstrumentMenu() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); InputButton.OnToggled += MidiInputButtonOnOnToggled; BandButton.OnPressed += BandButtonOnPressed; @@ -57,12 +50,34 @@ public InstrumentMenu(InstrumentBoundUserInterface owner) MinSize = SetSize = new Vector2(400, 150); } + public void SetInstrument(Entity entity) + { + Entity = entity; + var component = entity.Comp; + component.OnMidiPlaybackEnded += InstrumentOnMidiPlaybackEnded; + LoopButton.Disabled = !component.IsMidiOpen; + LoopButton.Pressed = component.LoopMidi; + ChannelsButton.Disabled = !component.IsRendererAlive; + StopButton.Disabled = !component.IsMidiOpen; + PlaybackSlider.MouseFilter = component.IsMidiOpen ? MouseFilterMode.Pass : MouseFilterMode.Ignore; + } + + public void RemoveInstrument(InstrumentComponent component) + { + component.OnMidiPlaybackEnded -= InstrumentOnMidiPlaybackEnded; + } + + public void SetMIDI(bool available) + { + UnavailableOverlay.Visible = !available; + } + private void BandButtonOnPressed(ButtonEventArgs obj) { if (!PlayCheck()) return; - _owner.OpenBandMenu(); + OnOpenBand?.Invoke(); } private void BandButtonOnToggled(ButtonToggledEventArgs obj) @@ -70,12 +85,15 @@ private void BandButtonOnToggled(ButtonToggledEventArgs obj) if (obj.Pressed) return; - _owner.Instruments.SetMaster(_owner.Owner, null); + if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument)) + { + _entManager.System().SetMaster(Entity, instrument.Master); + } } private void ChannelsButtonOnPressed(ButtonEventArgs obj) { - _owner.OpenChannelsMenu(); + OnOpenChannels?.Invoke(); } private void InstrumentOnMidiPlaybackEnded() @@ -85,8 +103,10 @@ private void InstrumentOnMidiPlaybackEnded() public void MidiPlaybackSetButtonsDisabled(bool disabled) { - if(disabled) - _owner.CloseChannelsMenu(); + if (disabled) + { + OnCloseChannels?.Invoke(); + } LoopButton.Disabled = disabled; StopButton.Disabled = disabled; @@ -100,7 +120,7 @@ private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj) if (_isMidiFileDialogueWindowOpen) return; - _owner.CloseBandMenu(); + OnCloseBands?.Invoke(); var filters = new FileDialogFilters(new FileDialogFilters.Group("mid", "midi")); @@ -108,7 +128,7 @@ private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj) // or focus the previously-opened window. _isMidiFileDialogueWindowOpen = true; - await using var file = await _owner.FileDialogManager.OpenFile(filters); + await using var file = await _dialogs.OpenFile(filters); _isMidiFileDialogueWindowOpen = false; @@ -129,9 +149,18 @@ private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj) await file.CopyToAsync(memStream); - if (_owner.Instrument is not {} instrument - || !_owner.Instruments.OpenMidi(_owner.Owner, memStream.GetBuffer().AsSpan(0, (int) memStream.Length), instrument)) + if (!_entManager.TryGetComponent(Entity, out var instrument)) + { return; + } + + if (!_entManager.System() + .OpenMidi(Entity, + memStream.GetBuffer().AsSpan(0, (int) memStream.Length), + instrument)) + { + return; + } MidiPlaybackSetButtonsDisabled(false); if (InputButton.Pressed) @@ -140,7 +169,7 @@ private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj) private void MidiInputButtonOnOnToggled(ButtonToggledEventArgs obj) { - _owner.CloseBandMenu(); + OnCloseBands?.Invoke(); if (obj.Pressed) { @@ -148,109 +177,99 @@ private void MidiInputButtonOnOnToggled(ButtonToggledEventArgs obj) return; MidiStopButtonOnPressed(null); - if(_owner.Instrument is {} instrument) - _owner.Instruments.OpenInput(_owner.Owner, instrument); + + if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument)) + _entManager.System().OpenInput(Entity, instrument); } - else if (_owner.Instrument is { } instrument) + else { - _owner.Instruments.CloseInput(_owner.Owner, false, instrument); - _owner.CloseChannelsMenu(); + _entManager.System().CloseInput(Entity, false); + OnCloseChannels?.Invoke(); } } private bool PlayCheck() { // TODO all of these checks should also be done server-side. - - var instrumentEnt = _owner.Owner; - var instrument = _owner.Instrument; - - if (instrument == null) + if (!_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument)) return false; - var localEntity = _owner.PlayerManager.LocalEntity; + var localEntity = _player.LocalEntity; // If we don't have a player or controlled entity, we return. if (localEntity == null) return false; // By default, allow an instrument to play itself and skip all other checks - if (localEntity == instrumentEnt) + if (localEntity == Entity) return true; - var container = _owner.Entities.System(); + var container = _entManager.System(); // If we're a handheld instrument, we might be in a container. Get it just in case. container.TryGetContainingContainer((Entity, null, null), out var conMan); // If the instrument is handheld and we're not holding it, we return. - if ((instrument.Handheld && (conMan == null || conMan.Owner != localEntity))) + if (instrument.Handheld && (conMan == null || conMan.Owner != localEntity)) return false; - if (!_owner.ActionBlocker.CanInteract(localEntity.Value, instrumentEnt)) + if (!_entManager.System().CanInteract(localEntity.Value, Entity)) return false; // We check that we're in range unobstructed just in case. - return _owner.Interactions.InRangeUnobstructed(localEntity.Value, instrumentEnt); + return _entManager.System().InRangeUnobstructed(localEntity.Value, Entity); } private void MidiStopButtonOnPressed(ButtonEventArgs? obj) { MidiPlaybackSetButtonsDisabled(true); - if (_owner.Instrument is not {} instrument) - return; - - _owner.Instruments.CloseMidi(_owner.Owner, false, instrument); - _owner.CloseChannelsMenu(); + _entManager.System().CloseMidi(Entity, false); + OnCloseChannels?.Invoke(); } private void MidiLoopButtonOnOnToggled(ButtonToggledEventArgs obj) { - if (_owner.Instrument == null) - return; + var instrument = _entManager.System(); + + if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrumentComp)) + { + instrumentComp.LoopMidi = obj.Pressed; + } - _owner.Instrument.LoopMidi = obj.Pressed; - _owner.Instruments.UpdateRenderer(_owner.Owner, _owner.Instrument); + instrument.UpdateRenderer(Entity); } private void PlaybackSliderSeek(Range _) { // Do not seek while still grabbing. - if (PlaybackSlider.Grabbed || _owner.Instrument is not {} instrument) + if (PlaybackSlider.Grabbed) return; - _owner.Instruments.SetPlayerTick(_owner.Owner, (int)Math.Ceiling(PlaybackSlider.Value), instrument); + _entManager.System().SetPlayerTick(Entity, (int)Math.Ceiling(PlaybackSlider.Value)); } private void PlaybackSliderKeyUp(GUIBoundKeyEventArgs args) { - if (args.Function != EngineKeyFunctions.UIClick || _owner.Instrument is not {} instrument) + if (args.Function != EngineKeyFunctions.UIClick) return; - _owner.Instruments.SetPlayerTick(_owner.Owner, (int)Math.Ceiling(PlaybackSlider.Value), instrument); - } - - public override void Close() - { - base.Close(); - _owner.CloseBandMenu(); - _owner.CloseChannelsMenu(); + _entManager.System().SetPlayerTick(Entity, (int)Math.Ceiling(PlaybackSlider.Value)); } protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); - if (_owner.Instrument == null) + if (!_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument)) return; - var hasMaster = _owner.Instrument.Master != null; + var hasMaster = instrument.Master != null; BandButton.ToggleMode = hasMaster; BandButton.Pressed = hasMaster; - BandButton.Disabled = _owner.Instrument.IsMidiOpen || _owner.Instrument.IsInputOpen; - ChannelsButton.Disabled = !_owner.Instrument.IsRendererAlive; + BandButton.Disabled = instrument.IsMidiOpen || instrument.IsInputOpen; + ChannelsButton.Disabled = !instrument.IsRendererAlive; - if (!_owner.Instrument.IsMidiOpen) + if (!instrument.IsMidiOpen) { PlaybackSlider.MaxValue = 1; PlaybackSlider.SetValueWithoutEvent(0); @@ -260,8 +279,8 @@ protected override void FrameUpdate(FrameEventArgs args) if (PlaybackSlider.Grabbed) return; - PlaybackSlider.MaxValue = _owner.Instrument.PlayerTotalTick; - PlaybackSlider.SetValueWithoutEvent(_owner.Instrument.PlayerTick); + PlaybackSlider.MaxValue = instrument.PlayerTotalTick; + PlaybackSlider.SetValueWithoutEvent(instrument.PlayerTick); } } } diff --git a/Content.Client/Inventory/StrippableBoundUserInterface.cs b/Content.Client/Inventory/StrippableBoundUserInterface.cs index 4ccede7f5d4..468c325822d 100644 --- a/Content.Client/Inventory/StrippableBoundUserInterface.cs +++ b/Content.Client/Inventory/StrippableBoundUserInterface.cs @@ -44,7 +44,7 @@ public sealed class StrippableBoundUserInterface : BoundUserInterface public const string HiddenPocketEntityId = "StrippingHiddenEntity"; [ViewVariables] - private readonly StrippingMenu? _strippingMenu; + private StrippingMenu? _strippingMenu; [ViewVariables] private readonly EntityUid _virtualHiddenEntity; @@ -54,29 +54,30 @@ public StrippableBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, u _examine = EntMan.System(); _inv = EntMan.System(); _cuffable = EntMan.System(); - - var title = Loc.GetString("strippable-bound-user-interface-stripping-menu-title", ("ownerName", Identity.Name(Owner, EntMan))); - _strippingMenu = new StrippingMenu(title, this); - _strippingMenu.OnClose += Close; _virtualHiddenEntity = EntMan.SpawnEntity(HiddenPocketEntityId, MapCoordinates.Nullspace); } protected override void Open() { base.Open(); + + _strippingMenu = this.CreateWindow(); + _strippingMenu.OnDirty += UpdateMenu; + _strippingMenu.Title = Loc.GetString("strippable-bound-user-interface-stripping-menu-title", ("ownerName", Identity.Name(Owner, EntMan))); + _strippingMenu?.OpenCenteredLeft(); } protected override void Dispose(bool disposing) { - base.Dispose(disposing); - - EntMan.DeleteEntity(_virtualHiddenEntity); - if (!disposing) return; - _strippingMenu?.Dispose(); + if (_strippingMenu != null) + _strippingMenu.OnDirty -= UpdateMenu; + + EntMan.DeleteEntity(_virtualHiddenEntity); + base.Dispose(disposing); } public void DirtyMenu() diff --git a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs index f97d8a73302..7884268c428 100644 --- a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs +++ b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs @@ -12,42 +12,34 @@ namespace Content.Client.Kitchen.UI [GenerateTypedNameReferences] public sealed partial class GrinderMenu : FancyWindow { - private readonly IEntityManager _entityManager; - private readonly IPrototypeManager _prototypeManager; - private readonly ReagentGrinderBoundUserInterface _owner; + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private readonly Dictionary _chamberVisualContents = new(); - public GrinderMenu(ReagentGrinderBoundUserInterface owner, IEntityManager entityManager, IPrototypeManager prototypeManager) + public event Action? OnToggleAuto; + public event Action? OnGrind; + public event Action? OnJuice; + public event Action? OnEjectAll; + public event Action? OnEjectBeaker; + public event Action? OnEjectChamber; + + public GrinderMenu() { RobustXamlLoader.Load(this); - _entityManager = entityManager; - _prototypeManager = prototypeManager; - _owner = owner; - AutoModeButton.OnPressed += owner.ToggleAutoMode; - GrindButton.OnPressed += owner.StartGrinding; - JuiceButton.OnPressed += owner.StartJuicing; - ChamberContentBox.EjectButton.OnPressed += owner.EjectAll; - BeakerContentBox.EjectButton.OnPressed += owner.EjectBeaker; + IoCManager.InjectDependencies(this); + AutoModeButton.OnPressed += _ => OnToggleAuto?.Invoke(); + GrindButton.OnPressed += _ => OnGrind?.Invoke(); + JuiceButton.OnPressed += _ => OnJuice?.Invoke(); + ChamberContentBox.EjectButton.OnPressed += _ => OnEjectAll?.Invoke(); + BeakerContentBox.EjectButton.OnPressed += _ => OnEjectBeaker?.Invoke(); ChamberContentBox.BoxContents.OnItemSelected += OnChamberBoxContentsItemSelected; BeakerContentBox.BoxContents.SelectMode = ItemList.ItemListSelectMode.None; } private void OnChamberBoxContentsItemSelected(ItemList.ItemListSelectedEventArgs args) { - _owner.EjectChamberContent(_chamberVisualContents[args.ItemIndex]); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - _chamberVisualContents.Clear(); - GrindButton.OnPressed -= _owner.StartGrinding; - JuiceButton.OnPressed -= _owner.StartJuicing; - ChamberContentBox.EjectButton.OnPressed -= _owner.EjectAll; - BeakerContentBox.EjectButton.OnPressed -= _owner.EjectBeaker; - ChamberContentBox.BoxContents.OnItemSelected -= OnChamberBoxContentsItemSelected; + OnEjectChamber?.Invoke(_chamberVisualContents[args.ItemIndex]); } public void UpdateState(ReagentGrinderInterfaceState state) diff --git a/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs b/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs index 7e7dd2d6935..643ac47054b 100644 --- a/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs +++ b/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs @@ -3,6 +3,7 @@ using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Client.Graphics; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; @@ -19,28 +20,15 @@ public sealed class MicrowaveBoundUserInterface : BoundUserInterface [ViewVariables] private readonly Dictionary _reagents = new(); - [Dependency] private readonly IGameTiming _gameTiming = default!; - - public MicrowaveUpdateUserInterfaceState currentState = default!; - - private IEntityManager _entManager; public MicrowaveBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { - _entManager = IoCManager.Resolve(); - } - - public TimeSpan GetCurrentTime() - { - return _gameTiming.CurTime; } protected override void Open() { base.Open(); - _menu = new MicrowaveMenu(this); - _menu.OpenCentered(); - _menu.OnClose += Close; + _menu = this.CreateWindow(); _menu.StartButton.OnPressed += _ => SendPredictedMessage(new MicrowaveStartCookMessage()); _menu.EjectButton.OnPressed += _ => SendPredictedMessage(new MicrowaveEjectMessage()); _menu.IngredientsList.OnItemSelected += args => @@ -74,38 +62,23 @@ protected override void Open() }; } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (!disposing) - { - return; - } - - _solids.Clear(); - _menu?.Dispose(); - } - protected override void UpdateState(BoundUserInterfaceState state) { base.UpdateState(state); - if (state is not MicrowaveUpdateUserInterfaceState cState) + if (state is not MicrowaveUpdateUserInterfaceState cState || _menu == null) { return; } + _menu.IsBusy = cState.IsMicrowaveBusy; + _menu.CurrentCooktimeEnd = cState.CurrentCookTimeEnd; - _menu?.ToggleBusyDisableOverlayPanel(cState.IsMicrowaveBusy || cState.ContainedSolids.Length == 0); - currentState = cState; - + _menu.ToggleBusyDisableOverlayPanel(cState.IsMicrowaveBusy || cState.ContainedSolids.Length == 0); // TODO move this to a component state and ensure the net ids. - RefreshContentsDisplay(_entManager.GetEntityArray(cState.ContainedSolids)); - - if (_menu == null) return; + RefreshContentsDisplay(EntMan.GetEntityArray(cState.ContainedSolids)); //Set the cook time info label - var cookTime = cState.ActiveButtonIndex == 0 + var cookTime = cState.ActiveButtonIndex == 0 ? Loc.GetString("microwave-menu-instant-button") : cState.CurrentCookTime.ToString(); diff --git a/Content.Client/Kitchen/UI/MicrowaveMenu.xaml.cs b/Content.Client/Kitchen/UI/MicrowaveMenu.xaml.cs index b292e9f1465..13029e38469 100644 --- a/Content.Client/Kitchen/UI/MicrowaveMenu.xaml.cs +++ b/Content.Client/Kitchen/UI/MicrowaveMenu.xaml.cs @@ -9,22 +9,21 @@ namespace Content.Client.Kitchen.UI [GenerateTypedNameReferences] public sealed partial class MicrowaveMenu : FancyWindow { - public sealed class MicrowaveCookTimeButton : Button - { - public uint CookTime; - } + [Dependency] private readonly IGameTiming _timing = default!; public event Action? OnCookTimeSelected; public ButtonGroup CookTimeButtonGroup { get; } - private readonly MicrowaveBoundUserInterface _owner; - public MicrowaveMenu(MicrowaveBoundUserInterface owner) + public bool IsBusy; + public TimeSpan CurrentCooktimeEnd; + + public MicrowaveMenu() { RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); CookTimeButtonGroup = new ButtonGroup(); InstantCookButton.Group = CookTimeButtonGroup; - _owner = owner; InstantCookButton.OnPressed += args => { OnCookTimeSelected?.Invoke(args, 0); @@ -65,14 +64,20 @@ public void ToggleBusyDisableOverlayPanel(bool shouldDisable) protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); - if(!_owner.currentState.IsMicrowaveBusy) + + if (!IsBusy) return; - if(_owner.currentState.CurrentCookTimeEnd > _owner.GetCurrentTime()) + if (CurrentCooktimeEnd > _timing.CurTime) { CookTimeInfoLabel.Text = Loc.GetString("microwave-bound-user-interface-cook-time-label", - ("time",_owner.currentState.CurrentCookTimeEnd.Subtract(_owner.GetCurrentTime()).Seconds)); + ("time", CurrentCooktimeEnd.Subtract(_timing.CurTime).Seconds)); } } + + public sealed class MicrowaveCookTimeButton : Button + { + public uint CookTime; + } } } diff --git a/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs b/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs index e6f108b3050..bc4cc75b4d1 100644 --- a/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs +++ b/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Containers.ItemSlots; using Content.Shared.Kitchen; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.Prototypes; @@ -8,8 +9,6 @@ namespace Content.Client.Kitchen.UI { public sealed class ReagentGrinderBoundUserInterface : BoundUserInterface { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [ViewVariables] private GrinderMenu? _menu; @@ -21,20 +20,13 @@ protected override void Open() { base.Open(); - _menu = new GrinderMenu(this, EntMan, _prototypeManager); - _menu.OpenCentered(); - _menu.OnClose += Close; - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - { - return; - } - - _menu?.Dispose(); + _menu = this.CreateWindow(); + _menu.OnToggleAuto += ToggleAutoMode; + _menu.OnGrind += StartGrinding; + _menu.OnJuice += StartJuicing; + _menu.OnEjectAll += EjectAll; + _menu.OnEjectBeaker += EjectBeaker; + _menu.OnEjectChamber += EjectChamberContent; } protected override void UpdateState(BoundUserInterfaceState state) @@ -52,27 +44,27 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message) _menu?.HandleMessage(message); } - public void ToggleAutoMode(BaseButton.ButtonEventArgs args) + public void ToggleAutoMode() { SendMessage(new ReagentGrinderToggleAutoModeMessage()); } - public void StartGrinding(BaseButton.ButtonEventArgs? _ = null) + public void StartGrinding() { SendMessage(new ReagentGrinderStartMessage(GrinderProgram.Grind)); } - public void StartJuicing(BaseButton.ButtonEventArgs? _ = null) + public void StartJuicing() { SendMessage(new ReagentGrinderStartMessage(GrinderProgram.Juice)); } - public void EjectAll(BaseButton.ButtonEventArgs? _ = null) + public void EjectAll() { SendMessage(new ReagentGrinderEjectChamberAllMessage()); } - public void EjectBeaker(BaseButton.ButtonEventArgs? _ = null) + public void EjectBeaker() { SendMessage(new ItemSlotButtonPressedEvent(SharedReagentGrinder.BeakerSlotId)); } diff --git a/Content.Client/Labels/UI/HandLabelerBoundUserInterface.cs b/Content.Client/Labels/UI/HandLabelerBoundUserInterface.cs index 555f1ff09e6..6b656123412 100644 --- a/Content.Client/Labels/UI/HandLabelerBoundUserInterface.cs +++ b/Content.Client/Labels/UI/HandLabelerBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Labels; using Content.Shared.Labels.Components; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Labels.UI { @@ -23,13 +24,8 @@ protected override void Open() { base.Open(); - _window = new HandLabelerWindow(); - if (State != null) - UpdateState(State); + _window = this.CreateWindow(); - _window.OpenCentered(); - - _window.OnClose += Close; _window.OnLabelChanged += OnLabelChanged; Reload(); } @@ -51,13 +47,5 @@ public void Reload() _window.SetCurrentLabel(component.AssignedLabel); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - _window?.Dispose(); - } } - } diff --git a/Content.Client/Lathe/UI/LatheBoundUserInterface.cs b/Content.Client/Lathe/UI/LatheBoundUserInterface.cs index 6e6d1b91761..a599f79152e 100644 --- a/Content.Client/Lathe/UI/LatheBoundUserInterface.cs +++ b/Content.Client/Lathe/UI/LatheBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Lathe; using Content.Shared.Research.Components; using JetBrains.Annotations; +using Robust.Client.UserInterface; namespace Content.Client.Lathe.UI { @@ -17,9 +18,9 @@ protected override void Open() { base.Open(); - _menu = new LatheMenu(this); - _menu.OnClose += Close; - + _menu = this.CreateWindow(); + _menu.SetEntity(Owner); + _menu.OpenCenteredRight(); _menu.OnServerListButtonPressed += _ => { @@ -30,8 +31,6 @@ protected override void Open() { SendMessage(new LatheQueueRecipeMessage(recipe, amount)); }; - - _menu.OpenCenteredRight(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -50,13 +49,5 @@ protected override void UpdateState(BoundUserInterfaceState state) break; } } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - _menu?.Dispose(); - } } } diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml.cs b/Content.Client/Lathe/UI/LatheMenu.xaml.cs index 9e15f8239e5..649c8acc859 100644 --- a/Content.Client/Lathe/UI/LatheMenu.xaml.cs +++ b/Content.Client/Lathe/UI/LatheMenu.xaml.cs @@ -1,3 +1,4 @@ +using System.Buffers; using System.Linq; using System.Text; using Content.Client.Materials; @@ -23,7 +24,6 @@ public sealed partial class LatheMenu : DefaultWindow [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IResourceCache _resources = default!; - private EntityUid _owner; private readonly SpriteSystem _spriteSystem; private readonly LatheSystem _lathe; private readonly MaterialStorageSystem _materialStorage; @@ -37,9 +37,10 @@ public sealed partial class LatheMenu : DefaultWindow public ProtoId? CurrentCategory; - public LatheMenu(LatheBoundUserInterface owner) + public EntityUid Entity; + + public LatheMenu() { - _owner = owner.Owner; RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); @@ -47,8 +48,6 @@ public LatheMenu(LatheBoundUserInterface owner) _lathe = _entityManager.System(); _materialStorage = _entityManager.System(); - Title = _entityManager.GetComponent(owner.Owner).EntityName; - SearchBar.OnTextChanged += _ => { PopulateRecipes(); @@ -61,8 +60,13 @@ public LatheMenu(LatheBoundUserInterface owner) FilterOption.OnItemSelected += OnItemSelected; ServerListButton.OnPressed += a => OnServerListButtonPressed?.Invoke(a); + } - if (_entityManager.TryGetComponent(owner.Owner, out var latheComponent)) + public void SetEntity(EntityUid uid) + { + Entity = uid; + + if (_entityManager.TryGetComponent(Entity, out var latheComponent)) { if (!latheComponent.DynamicRecipes.Any()) { @@ -70,7 +74,7 @@ public LatheMenu(LatheBoundUserInterface owner) } } - MaterialsList.SetOwner(owner.Owner); + MaterialsList.SetOwner(Entity); } /// @@ -106,21 +110,15 @@ public void PopulateRecipes() var sortedRecipesToShow = recipesToShow.OrderBy(p => p.Name); RecipeList.Children.Clear(); + _entityManager.TryGetComponent(Entity, out LatheComponent? lathe); + foreach (var prototype in sortedRecipesToShow) { - List textures; - if (_prototypeManager.TryIndex(prototype.Result, out EntityPrototype? entityProto) && entityProto != null) - { - textures = SpriteComponent.GetPrototypeTextures(entityProto, _resources).Select(o => o.Default).ToList(); - } - else - { - textures = prototype.Icon == null - ? new List { _spriteSystem.GetPrototypeIcon(prototype.Result).Default } - : new List { _spriteSystem.Frame0(prototype.Icon) }; - } + EntityPrototype? recipeProto = null; + if (_prototypeManager.TryIndex(prototype.Result, out EntityPrototype? entityProto)) + recipeProto = entityProto; - var canProduce = _lathe.CanProduce(_owner, prototype, quantity); + var canProduce = _lathe.CanProduce(Entity, prototype, quantity, component: lathe); var control = new RecipeControl(prototype, () => GenerateTooltipText(prototype), canProduce, textures); control.OnButtonPressed += s => @@ -136,19 +134,20 @@ public void PopulateRecipes() private string GenerateTooltipText(LatheRecipePrototype prototype) { StringBuilder sb = new(); + var multiplier = _entityManager.GetComponent(Entity).MaterialUseMultiplier; foreach (var (id, amount) in prototype.RequiredMaterials) { if (!_prototypeManager.TryIndex(id, out var proto)) continue; - var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, _entityManager.GetComponent(_owner).MaterialUseMultiplier); + var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, multiplier); var sheetVolume = _materialStorage.GetSheetVolume(proto); var unit = Loc.GetString(proto.Unit); var sheets = adjustedAmount / (float) sheetVolume; - var availableAmount = _materialStorage.GetMaterialAmount(_owner, id); + var availableAmount = _materialStorage.GetMaterialAmount(Entity, id); var missingAmount = Math.Max(0, adjustedAmount - availableAmount); var missingSheets = missingAmount / (float) sheetVolume; diff --git a/Content.Client/MachineLinking/UI/SignalTimerBoundUserInterface.cs b/Content.Client/MachineLinking/UI/SignalTimerBoundUserInterface.cs index 09bdedfd94c..11abe8c2451 100644 --- a/Content.Client/MachineLinking/UI/SignalTimerBoundUserInterface.cs +++ b/Content.Client/MachineLinking/UI/SignalTimerBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.MachineLinking; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Shared.Timing; namespace Content.Client.MachineLinking.UI; @@ -19,19 +20,14 @@ protected override void Open() { base.Open(); - _window = new SignalTimerWindow(this); - - if (State != null) - UpdateState(State); - - _window.OpenCentered(); - _window.OnClose += Close; + _window = this.CreateWindow(); + _window.OnStartTimer += StartTimer; _window.OnCurrentTextChanged += OnTextChanged; _window.OnCurrentDelayMinutesChanged += OnDelayChanged; _window.OnCurrentDelaySecondsChanged += OnDelayChanged; } - public void OnStartTimer() + public void StartTimer() { SendMessage(new SignalTimerStartMessage()); } @@ -48,11 +44,6 @@ private void OnDelayChanged(string newDelay) SendMessage(new SignalTimerDelayChangedMessage(_window.GetDelay())); } - public TimeSpan GetCurrentTime() - { - return _gameTiming.CurTime; - } - /// /// Update the UI state based on server-sent info /// @@ -72,11 +63,4 @@ protected override void UpdateState(BoundUserInterfaceState state) _window.SetTimerStarted(cast.TimerStarted); _window.SetHasAccess(cast.HasAccess); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - _window?.Dispose(); - } } diff --git a/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs b/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs index fbaaf5624b4..441ca9ea365 100644 --- a/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs +++ b/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs @@ -10,42 +10,44 @@ namespace Content.Client.MachineLinking.UI; [GenerateTypedNameReferences] public sealed partial class SignalTimerWindow : DefaultWindow { + [Dependency] private readonly IGameTiming _timing = default!; + private const int MaxTextLength = 5; public event Action? OnCurrentTextChanged; public event Action? OnCurrentDelayMinutesChanged; public event Action? OnCurrentDelaySecondsChanged; - private readonly SignalTimerBoundUserInterface _owner; - private TimeSpan? _triggerTime; private bool _timerStarted; - public SignalTimerWindow(SignalTimerBoundUserInterface owner) + public event Action? OnStartTimer; + + public SignalTimerWindow() { RobustXamlLoader.Load(this); - - _owner = owner; + IoCManager.InjectDependencies(this); CurrentTextEdit.OnTextChanged += e => OnCurrentTextChange(e.Text); CurrentDelayEditMinutes.OnTextChanged += e => OnCurrentDelayMinutesChange(e.Text); CurrentDelayEditSeconds.OnTextChanged += e => OnCurrentDelaySecondsChange(e.Text); - StartTimer.OnPressed += _ => OnStartTimer(); + StartTimer.OnPressed += _ => StartTimerWeh(); } - public void OnStartTimer() + private void StartTimerWeh() { if (!_timerStarted) { _timerStarted = true; - _triggerTime = _owner.GetCurrentTime() + GetDelay(); + _triggerTime = _timing.CurTime + GetDelay(); } else { SetTimerStarted(false); } - _owner.OnStartTimer(); + + OnStartTimer?.Invoke(); } protected override void FrameUpdate(FrameEventArgs args) @@ -55,9 +57,9 @@ protected override void FrameUpdate(FrameEventArgs args) if (!_timerStarted || _triggerTime == null) return; - if (_owner.GetCurrentTime() < _triggerTime.Value) + if (_timing.CurTime < _triggerTime.Value) { - StartTimer.Text = TextScreenSystem.TimeToString(_triggerTime.Value - _owner.GetCurrentTime()); + StartTimer.Text = TextScreenSystem.TimeToString(_triggerTime.Value - _timing.CurTime); } else { diff --git a/Content.Client/MagicMirror/MagicMirrorBoundUserInterface.cs b/Content.Client/MagicMirror/MagicMirrorBoundUserInterface.cs index f6979bf8d7b..0a87948ff62 100644 --- a/Content.Client/MagicMirror/MagicMirrorBoundUserInterface.cs +++ b/Content.Client/MagicMirror/MagicMirrorBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Humanoid.Markings; using Content.Shared.MagicMirror; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.MagicMirror; @@ -17,7 +18,7 @@ protected override void Open() { base.Open(); - _window = new(); + _window = this.CreateWindow(); _window.OnHairSelected += tuple => SelectHair(MagicMirrorCategory.Hair, tuple.id, tuple.slot); _window.OnHairColorChanged += args => ChangeColor(MagicMirrorCategory.Hair, args.marking, args.slot); @@ -29,9 +30,6 @@ protected override void Open() args => ChangeColor(MagicMirrorCategory.FacialHair, args.marking, args.slot); _window.OnFacialHairSlotAdded += delegate () { AddSlot(MagicMirrorCategory.FacialHair); }; _window.OnFacialHairSlotRemoved += args => RemoveSlot(MagicMirrorCategory.FacialHair, args); - - _window.OnClose += Close; - _window.OpenCentered(); } private void SelectHair(MagicMirrorCategory category, string marking, int slot) @@ -65,14 +63,5 @@ protected override void UpdateState(BoundUserInterfaceState state) _window.UpdateState(data); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - _window?.Dispose(); - } } diff --git a/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs b/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs index 80eca82e324..22e5bc452a0 100644 --- a/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs +++ b/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Content.Shared.MassMedia.Systems; using Content.Shared.MassMedia.Components; +using Robust.Client.UserInterface; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -9,8 +10,6 @@ namespace Content.Client.MassMedia.Ui; [UsedImplicitly] public sealed class NewsWriterBoundUserInterface : BoundUserInterface { - [Dependency] private readonly IGameTiming _gameTiming = default!; - [ViewVariables] private NewsWriterMenu? _menu; @@ -21,10 +20,7 @@ public NewsWriterBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, u protected override void Open() { - _menu = new NewsWriterMenu(_gameTiming); - - _menu.OpenCentered(); - _menu.OnClose += Close; + _menu = this.CreateWindow(); _menu.ArticleEditorPanel.PublishButtonPressed += OnPublishButtonPressed; _menu.DeleteButtonPressed += OnDeleteButtonPressed; @@ -32,16 +28,6 @@ protected override void Open() SendMessage(new NewsWriterArticlesRequestMessage()); } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - _menu?.Close(); - _menu?.Dispose(); - } - protected override void UpdateState(BoundUserInterfaceState state) { base.UpdateState(state); diff --git a/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs b/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs index e2d57935e3a..c059ce785af 100644 --- a/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs +++ b/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs @@ -10,17 +10,17 @@ namespace Content.Client.MassMedia.Ui; [GenerateTypedNameReferences] public sealed partial class NewsWriterMenu : FancyWindow { - private readonly IGameTiming _gameTiming; + [Dependency] private readonly IGameTiming _gameTiming = default!; private TimeSpan? _nextPublish; public event Action? DeleteButtonPressed; - public NewsWriterMenu(IGameTiming gameTiming) + public NewsWriterMenu() { RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); - _gameTiming = gameTiming; ContentsContainer.RectClipContent = false; // Customize scrollbar width and margin. This is not possible in xaml diff --git a/Content.Client/Mech/Ui/MechBoundUserInterface.cs b/Content.Client/Mech/Ui/MechBoundUserInterface.cs index 4172bdc90f1..2130a8c6099 100644 --- a/Content.Client/Mech/Ui/MechBoundUserInterface.cs +++ b/Content.Client/Mech/Ui/MechBoundUserInterface.cs @@ -3,6 +3,7 @@ using Content.Shared.Mech.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Mech.Ui; @@ -20,9 +21,8 @@ protected override void Open() { base.Open(); - _menu = new(Owner); - - _menu.OnClose += Close; + _menu = this.CreateWindow(); + _menu.SetEntity(Owner); _menu.OpenCenteredLeft(); _menu.OnRemoveButtonPressed += uid => @@ -60,16 +60,6 @@ public void UpdateEquipmentControls(MechBoundUiState state) } } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (!disposing) - return; - - _menu?.Close(); - } - public UIFragment? GetEquipmentUi(EntityUid? uid) { var component = EntMan.GetComponentOrNull(uid); diff --git a/Content.Client/Mech/Ui/MechMenu.xaml.cs b/Content.Client/Mech/Ui/MechMenu.xaml.cs index fad76488086..6f39bc386ee 100644 --- a/Content.Client/Mech/Ui/MechMenu.xaml.cs +++ b/Content.Client/Mech/Ui/MechMenu.xaml.cs @@ -16,14 +16,15 @@ public sealed partial class MechMenu : FancyWindow public event Action? OnRemoveButtonPressed; - public MechMenu(EntityUid mech) + public MechMenu() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); + } - _mech = mech; - - MechView.SetEntity(mech); + public void SetEntity(EntityUid uid) + { + MechView.SetEntity(uid); } public void UpdateMechStats() diff --git a/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs b/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs index 39788809871..b1f239cd78e 100644 --- a/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs +++ b/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs @@ -1,4 +1,5 @@ using Content.Shared.Medical.CrewMonitoring; +using Robust.Client.UserInterface; namespace Content.Client.Medical.CrewMonitoring; @@ -14,7 +15,7 @@ public CrewMonitoringBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne protected override void Open() { EntityUid? gridUid = null; - string stationName = string.Empty; + var stationName = string.Empty; if (EntMan.TryGetComponent(Owner, out var xform)) { @@ -26,10 +27,8 @@ protected override void Open() } } - _menu = new CrewMonitoringWindow(stationName, gridUid); - - _menu.OpenCentered(); - _menu.OnClose += Close; + _menu = this.CreateWindow(); + _menu.Set(stationName, gridUid); } protected override void UpdateState(BoundUserInterfaceState state) @@ -44,13 +43,4 @@ protected override void UpdateState(BoundUserInterfaceState state) break; } } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - _menu?.Dispose(); - } } diff --git a/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs b/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs index 863412e5532..e861864c144 100644 --- a/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs +++ b/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs @@ -23,22 +23,27 @@ namespace Content.Client.Medical.CrewMonitoring; [GenerateTypedNameReferences] public sealed partial class CrewMonitoringWindow : FancyWindow { - private readonly IEntityManager _entManager; - private readonly IPrototypeManager _prototypeManager; + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private readonly SpriteSystem _spriteSystem; private NetEntity? _trackedEntity; private bool _tryToScrollToListFocus; private Texture? _blipTexture; - public CrewMonitoringWindow(string stationName, EntityUid? mapUid) + public CrewMonitoringWindow() { RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); - _entManager = IoCManager.Resolve(); - _prototypeManager = IoCManager.Resolve(); _spriteSystem = _entManager.System(); + NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap; + + } + + public void Set(string stationName, EntityUid? mapUid) + { _blipTexture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png"))); if (_entManager.TryGetComponent(mapUid, out var xform)) @@ -49,8 +54,6 @@ public CrewMonitoringWindow(string stationName, EntityUid? mapUid) StationName.AddStyleClass("LabelBig"); StationName.Text = stationName; - - NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap; NavMap.ForceNavMapUpdate(); } diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorBoundUserInterface.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorBoundUserInterface.cs index 80c98f143b9..f85220a9266 100644 --- a/Content.Client/NetworkConfigurator/NetworkConfiguratorBoundUserInterface.cs +++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Client.NetworkConfigurator.Systems; using Content.Shared.DeviceNetwork; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; namespace Content.Client.NetworkConfigurator; @@ -35,14 +36,12 @@ protected override void Open() switch (UiKey) { case NetworkConfiguratorUiKey.List: - _listMenu = new NetworkConfiguratorListMenu(this); - _listMenu.OnClose += Close; + _listMenu = this.CreateWindow(); _listMenu.ClearButton.OnPressed += _ => OnClearButtonPressed(); - _listMenu.OpenCenteredRight(); + _listMenu.OnRemoveAddress += OnRemoveButtonPressed; break; case NetworkConfiguratorUiKey.Configure: - _configurationMenu = new NetworkConfiguratorConfigurationMenu(); - _configurationMenu.OnClose += Close; + _configurationMenu = this.CreateWindow(); _configurationMenu.Set.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Set); _configurationMenu.Add.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Add); //_configurationMenu.Edit.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Edit); @@ -50,12 +49,24 @@ protected override void Open() _configurationMenu.Copy.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Copy); _configurationMenu.Show.OnPressed += OnShowPressed; _configurationMenu.Show.Pressed = _netConfig.ConfiguredListIsTracked(Owner); - _configurationMenu.OpenCentered(); + _configurationMenu.OnRemoveAddress += OnRemoveButtonPressed; break; case NetworkConfiguratorUiKey.Link: - _linkMenu = new NetworkConfiguratorLinkMenu(this); - _linkMenu.OnClose += Close; - _linkMenu.OpenCentered(); + _linkMenu = this.CreateWindow(); + _linkMenu.OnLinkDefaults += args => + { + SendMessage(new NetworkConfiguratorLinksSaveMessage(args)); + }; + + _linkMenu.OnToggleLink += (left, right) => + { + SendMessage(new NetworkConfiguratorToggleLinkMessage(left, right)); + }; + + _linkMenu.OnClearLinks += () => + { + SendMessage(new NetworkConfiguratorClearLinksMessage()); + }; break; } } @@ -83,16 +94,6 @@ protected override void UpdateState(BoundUserInterfaceState state) } } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - - _linkMenu?.Dispose(); - _listMenu?.Dispose(); - _configurationMenu?.Dispose(); - } - private void OnClearButtonPressed() { SendMessage(new NetworkConfiguratorClearDevicesMessage()); diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorConfigurationMenu.xaml.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorConfigurationMenu.xaml.cs index 19d04cd3464..fcd2f759187 100644 --- a/Content.Client/NetworkConfigurator/NetworkConfiguratorConfigurationMenu.xaml.cs +++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorConfigurationMenu.xaml.cs @@ -9,17 +9,23 @@ namespace Content.Client.NetworkConfigurator; [GenerateTypedNameReferences] public sealed partial class NetworkConfiguratorConfigurationMenu : FancyWindow { + public event Action? OnRemoveAddress; + public NetworkConfiguratorConfigurationMenu() { RobustXamlLoader.Load(this); Clear.StyleClasses.Add(StyleBase.ButtonOpenLeft); Clear.StyleClasses.Add(StyleNano.StyleClassButtonColorRed); + DeviceList.OnRemoveAddress += args => + { + OnRemoveAddress?.Invoke(args); + }; } public void UpdateState(DeviceListUserInterfaceState state) { - DeviceList.UpdateState(null, state.DeviceList); + DeviceList.UpdateState(state.DeviceList, false); Count.Text = Loc.GetString("network-configurator-ui-count-label", ("count", state.DeviceList.Count)); } diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorDeviceList.xaml.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorDeviceList.xaml.cs index 8cfa97dc6c2..e75c60058cb 100644 --- a/Content.Client/NetworkConfigurator/NetworkConfiguratorDeviceList.xaml.cs +++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorDeviceList.xaml.cs @@ -7,17 +7,19 @@ namespace Content.Client.NetworkConfigurator; [GenerateTypedNameReferences] public sealed partial class NetworkConfiguratorDeviceList : ScrollContainer { - public void UpdateState(NetworkConfiguratorBoundUserInterface? ui, HashSet<(string address, string name)> devices) + public event Action? OnRemoveAddress; + + public void UpdateState(HashSet<(string address, string name)> devices, bool ui) { DeviceList.RemoveAllChildren(); foreach (var device in devices) { - DeviceList.AddChild(BuildDeviceListRow(ui, device)); + DeviceList.AddChild(BuildDeviceListRow(device, ui)); } } - private static BoxContainer BuildDeviceListRow(NetworkConfiguratorBoundUserInterface? ui, (string address, string name) savedDevice) + private BoxContainer BuildDeviceListRow((string address, string name) savedDevice, bool ui) { var row = new BoxContainer() { @@ -48,10 +50,10 @@ private static BoxContainer BuildDeviceListRow(NetworkConfiguratorBoundUserInter row.AddChild(name); row.AddChild(address); - if (ui != null) + if (ui) { row.AddChild(removeButton); - removeButton.OnPressed += _ => ui.OnRemoveButtonPressed(savedDevice.address); + removeButton.OnPressed += _ => OnRemoveAddress?.Invoke(savedDevice.address); } return row; diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs index c04b42f249b..8cdffd16af6 100644 --- a/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs +++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs @@ -18,20 +18,20 @@ public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow private readonly LinksRender _links; - private readonly List _sources = new(); private readonly List _sinks = new(); - private readonly NetworkConfiguratorBoundUserInterface _userInterface; - private (ButtonPosition position, string id, int index)? _selectedButton; private List<(string left, string right)>? _defaults; - public NetworkConfiguratorLinkMenu(NetworkConfiguratorBoundUserInterface userInterface) + public event Action? OnClearLinks; + public event Action? OnToggleLink; + public event Action>? OnLinkDefaults; + + public NetworkConfiguratorLinkMenu() { - _userInterface = userInterface; RobustXamlLoader.Load(this); var footerStyleBox = new StyleBoxFlat() @@ -52,7 +52,7 @@ public NetworkConfiguratorLinkMenu(NetworkConfiguratorBoundUserInterface userInt ButtonOk.OnPressed += _ => Close(); ButtonLinkDefault.OnPressed += _ => LinkDefaults(); - ButtonClear.OnPressed += _ => _userInterface.SendMessage(new NetworkConfiguratorClearLinksMessage()); + ButtonClear.OnPressed += _ => OnClearLinks?.Invoke(); } public void UpdateState(DeviceLinkUserInterfaceState linkState) @@ -98,7 +98,7 @@ private void LinkDefaults() if (_defaults == default) return; - _userInterface.SendMessage(new NetworkConfiguratorLinksSaveMessage(_defaults)); + OnLinkDefaults?.Invoke(_defaults); } private Button CreateButton(ButtonPosition position, string name, string description, string id, int index) @@ -138,7 +138,7 @@ private void OnButtonPressed(BaseButton.ButtonEventArgs args, ButtonPosition pos var left = _selectedButton.Value.position == ButtonPosition.Left ? _selectedButton.Value.id : id; var right = _selectedButton.Value.position == ButtonPosition.Left ? id : _selectedButton.Value.id; - _userInterface.SendMessage(new NetworkConfiguratorToggleLinkMessage(left, right)); + OnToggleLink?.Invoke(left, right); args.Button.Pressed = false; diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorListMenu.xaml.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorListMenu.xaml.cs index fb4aec1974b..6294facaeed 100644 --- a/Content.Client/NetworkConfigurator/NetworkConfiguratorListMenu.xaml.cs +++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorListMenu.xaml.cs @@ -9,17 +9,20 @@ namespace Content.Client.NetworkConfigurator; [GenerateTypedNameReferences] public sealed partial class NetworkConfiguratorListMenu : FancyWindow { - private readonly NetworkConfiguratorBoundUserInterface _ui; - public NetworkConfiguratorListMenu(NetworkConfiguratorBoundUserInterface ui) + public event Action? OnRemoveAddress; + + public NetworkConfiguratorListMenu() { RobustXamlLoader.Load(this); - - _ui = ui; + DeviceList.OnRemoveAddress += args => + { + OnRemoveAddress?.Invoke(args); + }; } public void UpdateState(NetworkConfiguratorUserInterfaceState state) { DeviceCountLabel.Text = Loc.GetString("network-configurator-ui-count-label", ("count", state.DeviceList.Count)); - DeviceList.UpdateState(_ui, state.DeviceList); + DeviceList.UpdateState(state.DeviceList, true); } } diff --git a/Content.Client/Nuke/NukeBoundUserInterface.cs b/Content.Client/Nuke/NukeBoundUserInterface.cs index 59fbc5b319b..2e150423734 100644 --- a/Content.Client/Nuke/NukeBoundUserInterface.cs +++ b/Content.Client/Nuke/NukeBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Nuke; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Nuke { @@ -11,15 +12,13 @@ public sealed class NukeBoundUserInterface : BoundUserInterface [ViewVariables] private NukeMenu? _menu; - public NukeBoundUserInterface([NotNull] EntityUid owner, [NotNull] Enum uiKey) : base(owner, uiKey) + public NukeBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { } protected override void Open() { - _menu = new NukeMenu(); - _menu.OpenCentered(); - _menu.OnClose += Close; + _menu = this.CreateWindow(); _menu.OnKeypadButtonPressed += i => { @@ -62,15 +61,5 @@ protected override void UpdateState(BoundUserInterfaceState state) break; } } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - _menu?.Close(); - _menu?.Dispose(); - } } } diff --git a/Content.Client/NukeOps/WarDeclaratorBoundUserInterface.cs b/Content.Client/NukeOps/WarDeclaratorBoundUserInterface.cs index ec055b3240c..ad4f1a75d47 100644 --- a/Content.Client/NukeOps/WarDeclaratorBoundUserInterface.cs +++ b/Content.Client/NukeOps/WarDeclaratorBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Chat; using Content.Shared.NukeOps; using JetBrains.Annotations; +using Robust.Client.UserInterface; using Robust.Shared.Configuration; using Robust.Shared.Timing; @@ -11,8 +12,6 @@ namespace Content.Client.NukeOps; public sealed class WarDeclaratorBoundUserInterface : BoundUserInterface { [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly ILocalizationManager _localizationManager = default!; [ViewVariables] private WarDeclaratorWindow? _window; @@ -23,13 +22,7 @@ protected override void Open() { base.Open(); - _window = new WarDeclaratorWindow(_gameTiming, _localizationManager); - if (State != null) - UpdateState(State); - - _window.OpenCentered(); - - _window.OnClose += Close; + _window = this.CreateWindow(); _window.OnActivated += OnWarDeclaratorActivated; } @@ -42,13 +35,6 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(cast); } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - _window?.Dispose(); - } - private void OnWarDeclaratorActivated(string message) { var maxLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength); diff --git a/Content.Client/NukeOps/WarDeclaratorWindow.xaml.cs b/Content.Client/NukeOps/WarDeclaratorWindow.xaml.cs index b4a3f1c7fa5..aeceae13275 100644 --- a/Content.Client/NukeOps/WarDeclaratorWindow.xaml.cs +++ b/Content.Client/NukeOps/WarDeclaratorWindow.xaml.cs @@ -11,7 +11,8 @@ namespace Content.Client.NukeOps; [GenerateTypedNameReferences] public sealed partial class WarDeclaratorWindow : FancyWindow { - private readonly IGameTiming _gameTiming; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly ILocalizationManager _localizationManager = default!; public event Action? OnActivated; @@ -19,15 +20,13 @@ public sealed partial class WarDeclaratorWindow : FancyWindow private TimeSpan _shuttleDisabledTime; private WarConditionStatus _status; - public WarDeclaratorWindow(IGameTiming gameTiming, ILocalizationManager localizationManager) + public WarDeclaratorWindow() { RobustXamlLoader.Load(this); - _gameTiming = gameTiming; - WarButton.OnPressed += (_) => OnActivated?.Invoke(Rope.Collapse(MessageEdit.TextRope)); - MessageEdit.Placeholder = new Rope.Leaf(localizationManager.GetString("war-declarator-message-placeholder")); + MessageEdit.Placeholder = new Rope.Leaf(_localizationManager.GetString("war-declarator-message-placeholder")); } protected override void FrameUpdate(FrameEventArgs args) diff --git a/Content.Client/PDA/PdaBoundUserInterface.cs b/Content.Client/PDA/PdaBoundUserInterface.cs index 07352b512b0..37ce9c4280f 100644 --- a/Content.Client/PDA/PdaBoundUserInterface.cs +++ b/Content.Client/PDA/PdaBoundUserInterface.cs @@ -21,9 +21,16 @@ public PdaBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) protected override void Open() { base.Open(); - _menu = new PdaMenu(); + + if (_menu == null) + CreateMenu(); + } + + private void CreateMenu() + { + _menu = this.CreateWindow(); _menu.OpenCenteredLeft(); - _menu.OnClose += Close; + _menu.FlashLightToggleButton.OnToggled += _ => { SendMessage(new PdaToggleFlashlightMessage()); @@ -88,7 +95,6 @@ protected override void UpdateState(BoundUserInterfaceState state) _menu?.UpdateState(updateState); } - protected override void AttachCartridgeUI(Control cartridgeUIFragment, string? title) { _menu?.ProgramView.AddChild(cartridgeUIFragment); @@ -110,15 +116,6 @@ protected override void UpdateAvailablePrograms(List<(EntityUid, CartridgeCompon _menu?.UpdateAvailablePrograms(programs); } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - _menu?.Dispose(); - } - private PdaBorderColorComponent? GetBorderColorComponent() { return EntMan.GetComponentOrNull(Owner); diff --git a/Content.Client/PDA/Ringer/RingerBoundUserInterface.cs b/Content.Client/PDA/Ringer/RingerBoundUserInterface.cs index a0688523f1e..170a296ac2e 100644 --- a/Content.Client/PDA/Ringer/RingerBoundUserInterface.cs +++ b/Content.Client/PDA/Ringer/RingerBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.PDA; using Content.Shared.PDA.Ringer; using JetBrains.Annotations; +using Robust.Client.UserInterface; using Robust.Shared.Timing; namespace Content.Client.PDA.Ringer @@ -18,9 +19,8 @@ public RingerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey protected override void Open() { base.Open(); - _menu = new RingtoneMenu(); + _menu = this.CreateWindow(); _menu.OpenToLeft(); - _menu.OnClose += Close; _menu.TestRingerButton.OnPressed += _ => { diff --git a/Content.Client/Paper/UI/PaperBoundUserInterface.cs b/Content.Client/Paper/UI/PaperBoundUserInterface.cs index 4b0ac868f01..f3ad1e347e7 100644 --- a/Content.Client/Paper/UI/PaperBoundUserInterface.cs +++ b/Content.Client/Paper/UI/PaperBoundUserInterface.cs @@ -1,4 +1,5 @@ using JetBrains.Annotations; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.Utility; using static Content.Shared.Paper.SharedPaperComponent; @@ -19,16 +20,13 @@ protected override void Open() { base.Open(); - _window = new PaperWindow(); - _window.OnClose += Close; - _window.OnSaved += Input_OnTextEntered; + _window = this.CreateWindow(); + _window.OnSaved += InputOnTextEntered; if (EntMan.TryGetComponent(Owner, out var visuals)) { _window.InitVisuals(Owner, visuals); } - - _window.OpenCentered(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -37,7 +35,7 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.Populate((PaperBoundUserInterfaceState) state); } - private void Input_OnTextEntered(string text) + private void InputOnTextEntered(string text) { SendMessage(new PaperInputTextMessage(text)); @@ -47,11 +45,4 @@ private void Input_OnTextEntered(string text) _window.Input.CursorPosition = new TextEdit.CursorPos(0, TextEdit.LineBreakBias.Top); } } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - _window?.Dispose(); - } } diff --git a/Content.Client/Paper/UI/PaperWindow.xaml.cs b/Content.Client/Paper/UI/PaperWindow.xaml.cs index 7a5fd652643..f7cace642ce 100644 --- a/Content.Client/Paper/UI/PaperWindow.xaml.cs +++ b/Content.Client/Paper/UI/PaperWindow.xaml.cs @@ -17,6 +17,7 @@ namespace Content.Client.Paper.UI public sealed partial class PaperWindow : BaseWindow { [Dependency] private readonly IInputManager _inputManager = default!; + [Dependency] private readonly IResourceCache _resCache = default!; private static Color DefaultTextColor = new(25, 25, 25); @@ -85,11 +86,10 @@ public void InitVisuals(EntityUid entity, PaperVisualsComponent visuals) // Randomize the placement of any stamps based on the entity UID // so that there's some variety in different papers. StampDisplay.PlacementSeed = (int)entity; - var resCache = IoCManager.Resolve(); // Initialize the background: PaperBackground.ModulateSelfOverride = visuals.BackgroundModulate; - var backgroundImage = visuals.BackgroundImagePath != null? resCache.GetResource(visuals.BackgroundImagePath) : null; + var backgroundImage = visuals.BackgroundImagePath != null? _resCache.GetResource(visuals.BackgroundImagePath) : null; if (backgroundImage != null) { var backgroundImageMode = visuals.BackgroundImageTile ? StyleBoxTexture.StretchMode.Tile : StyleBoxTexture.StretchMode.Stretch; @@ -127,7 +127,7 @@ public void InitVisuals(EntityUid entity, PaperVisualsComponent visuals) PaperContent.ModulateSelfOverride = visuals.ContentImageModulate; WrittenTextLabel.ModulateSelfOverride = visuals.FontAccentColor; - var contentImage = visuals.ContentImagePath != null ? resCache.GetResource(visuals.ContentImagePath) : null; + var contentImage = visuals.ContentImagePath != null ? _resCache.GetResource(visuals.ContentImagePath) : null; if (contentImage != null) { // Setup the paper content texture, but keep a reference to it, as we can't set diff --git a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs index cde5ba9ef79..ff1eae36f55 100644 --- a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs +++ b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Singularity.Components; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.ParticleAccelerator.UI { @@ -16,9 +17,10 @@ protected override void Open() { base.Open(); - _menu = new ParticleAcceleratorControlMenu(this); - _menu.OnClose += Close; - _menu.OpenCentered(); + _menu = this.CreateWindow(); + _menu.OnOverallState += SendEnableMessage; + _menu.OnPowerState += SendPowerStateMessage; + _menu.OnScanPartsRequested += SendScanPartsMessage; } public void SendEnableMessage(bool enable) @@ -40,13 +42,5 @@ protected override void UpdateState(BoundUserInterfaceState state) { _menu?.DataUpdate((ParticleAcceleratorUIState) state); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - _menu?.Dispose(); - _menu = null; - } } } diff --git a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs index c69e0271372..05a296edf39 100644 --- a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs +++ b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs @@ -18,9 +18,10 @@ namespace Content.Client.ParticleAccelerator.UI { public sealed class ParticleAcceleratorControlMenu : BaseWindow { - private readonly ShaderInstance _greyScaleShader; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly IResourceCache _cache = default!; - private readonly ParticleAcceleratorBoundUserInterface _owner; + private readonly ShaderInstance _greyScaleShader; private readonly Label _drawLabel; private readonly FastNoiseLite _drawNoiseGenerator; @@ -50,19 +51,22 @@ public sealed class ParticleAcceleratorControlMenu : BaseWindow private bool _shouldContinueAnimating; private int _maxStrength = 3; - public ParticleAcceleratorControlMenu(ParticleAcceleratorBoundUserInterface owner) + public event Action? OnOverallState; + public event Action? OnPowerState; + public event Action? OnScanPartsRequested; + + public ParticleAcceleratorControlMenu() { + IoCManager.InjectDependencies(this); SetSize = new Vector2(400, 320); - _greyScaleShader = IoCManager.Resolve().Index("Greyscale").Instance(); + _greyScaleShader = _protoManager.Index("Greyscale").Instance(); - _owner = owner; _drawNoiseGenerator = new(); _drawNoiseGenerator.SetFractalType(FastNoiseLite.FractalType.FBm); _drawNoiseGenerator.SetFrequency(0.5f); - var resourceCache = IoCManager.Resolve(); - var font = resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13); - var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); + var font = _cache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13); + var panelTex = _cache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); MouseFilter = MouseFilterMode.Stop; @@ -112,7 +116,8 @@ public ParticleAcceleratorControlMenu(ParticleAcceleratorBoundUserInterface owne Text = Loc.GetString("particle-accelerator-control-menu-off-button"), StyleClasses = { StyleBase.ButtonOpenRight }, }; - _offButton.OnPressed += args => owner.SendEnableMessage(false); + + _offButton.OnPressed += args => OnOverallState?.Invoke(false); _onButton = new Button { @@ -120,7 +125,7 @@ public ParticleAcceleratorControlMenu(ParticleAcceleratorBoundUserInterface owne Text = Loc.GetString("particle-accelerator-control-menu-on-button"), StyleClasses = { StyleBase.ButtonOpenLeft }, }; - _onButton.OnPressed += args => owner.SendEnableMessage(true); + _onButton.OnPressed += args => OnOverallState?.Invoke(true); var closeButton = new TextureButton { @@ -316,7 +321,7 @@ public ParticleAcceleratorControlMenu(ParticleAcceleratorBoundUserInterface owne } }); - _scanButton.OnPressed += args => _owner.SendScanPartsMessage(); + _scanButton.OnPressed += args => OnScanPartsRequested?.Invoke(); _alarmControl.AnimationCompleted += s => { @@ -332,7 +337,7 @@ public ParticleAcceleratorControlMenu(ParticleAcceleratorBoundUserInterface owne PASegmentControl Segment(string name) { - return new(this, resourceCache, name); + return new(this, _cache, name); } UpdateUI(false, false, false, false); @@ -368,7 +373,7 @@ private void PowerStateChanged(ValueChangedEventArgs e) } _stateSpinBox.SetButtonDisabled(true); - _owner.SendPowerStateMessage(newState); + OnPowerState?.Invoke(newState); } protected override DragMode GetDragModeFor(Vector2 relativeMousePos) diff --git a/Content.Client/Pinpointer/UI/NavMapBeaconBoundUserInterface.cs b/Content.Client/Pinpointer/UI/NavMapBeaconBoundUserInterface.cs index 3ebcf7cbced..0df6787170a 100644 --- a/Content.Client/Pinpointer/UI/NavMapBeaconBoundUserInterface.cs +++ b/Content.Client/Pinpointer/UI/NavMapBeaconBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Pinpointer; using JetBrains.Annotations; +using Robust.Client.UserInterface; namespace Content.Client.Pinpointer.UI; @@ -16,19 +17,16 @@ public NavMapBeaconBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, protected override void Open() { base.Open(); - _window = new NavMapBeaconWindow(Owner); - _window.OpenCentered(); - _window.OnClose += Close; + _window = this.CreateWindow(); + + if (EntMan.TryGetComponent(Owner, out NavMapBeaconComponent? beacon)) + { + _window.SetEntity(Owner, beacon); + } _window.OnApplyButtonPressed += (label, enabled, color) => { SendMessage(new NavMapBeaconConfigureBuiMessage(label, enabled, color)); }; } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - _window?.Dispose(); - } } diff --git a/Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml.cs b/Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml.cs index 968fe188f75..b77f1af0472 100644 --- a/Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml.cs +++ b/Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml.cs @@ -10,38 +10,37 @@ namespace Content.Client.Pinpointer.UI; [GenerateTypedNameReferences] public sealed partial class NavMapBeaconWindow : FancyWindow { - [Dependency] private readonly IEntityManager _entityManager = default!; - private string? _defaultLabel; private bool _defaultEnabled; private Color _defaultColor; public event Action? OnApplyButtonPressed; - public NavMapBeaconWindow(EntityUid beaconEntity) + public NavMapBeaconWindow() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - if (!_entityManager.TryGetComponent(beaconEntity, out var navMap)) - return; - _defaultLabel = navMap.Text; - _defaultEnabled = navMap.Enabled; - _defaultColor = navMap.Color; - UpdateVisibleButton(navMap.Enabled); VisibleButton.OnPressed += args => UpdateVisibleButton(args.Button.Pressed); - - LabelLineEdit.Text = navMap.Text ?? string.Empty; LabelLineEdit.OnTextChanged += OnTextChanged; - - ColorSelector.Color = navMap.Color; ColorSelector.OnColorChanged += _ => TryEnableApplyButton(); TryEnableApplyButton(); ApplyButton.OnPressed += OnApplyPressed; } + public void SetEntity(EntityUid uid, NavMapBeaconComponent navMap) + { + _defaultLabel = navMap.Text; + _defaultEnabled = navMap.Enabled; + _defaultColor = navMap.Color; + + UpdateVisibleButton(navMap.Enabled); + LabelLineEdit.Text = navMap.Text ?? string.Empty; + ColorSelector.Color = navMap.Color; + } + private void UpdateVisibleButton(bool value) { VisibleButton.Pressed = value; diff --git a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs index 6e62dbfda9e..7417fafede5 100644 --- a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs +++ b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs @@ -1,4 +1,4 @@ -using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Pinpointer.UI; @@ -14,7 +14,6 @@ public StationMapBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, u protected override void Open() { base.Open(); - _window?.Close(); EntityUid? gridUid = null; if (EntMan.TryGetComponent(Owner, out var xform)) @@ -24,16 +23,6 @@ protected override void Open() _window = this.CreateWindow(); _window.Title = EntMan.GetComponent(Owner).EntityName; - - string stationName = string.Empty; - if(EntMan.TryGetComponent(gridUid, out var gridMetaData)) - { - stationName = gridMetaData.EntityName; - } - - if (EntMan.TryGetComponent(Owner, out var comp) && comp.ShowLocation) - _window.Set(stationName, gridUid, Owner); - else - _window.Set(stationName, gridUid, null); + _window.Set(gridUid, Owner); } } diff --git a/Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs b/Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs index 57965b030a2..a3ca6f65da2 100644 --- a/Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs +++ b/Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs @@ -1,4 +1,4 @@ -using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Pinpointer.UI; @@ -14,22 +14,15 @@ public UntrackedStationMapBoundUserInterface(EntityUid owner, Enum uiKey) : base protected override void Open() { base.Open(); - _window?.Close(); EntityUid? gridUid = null; + // TODO: What this just looks like it's been copy-pasted wholesale from StationMapBoundUserInterface? if (EntMan.TryGetComponent(Owner, out var xform)) { gridUid = xform.GridUid; } - _window = new StationMapWindow(gridUid, null); - _window.OpenCentered(); - _window.OnClose += Close; - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - _window?.Dispose(); + _window = this.CreateWindow(); + _window.Set(gridUid, Owner); } } diff --git a/Content.Client/Power/APC/ApcBoundUserInterface.cs b/Content.Client/Power/APC/ApcBoundUserInterface.cs index fbcbf011569..759a5949ba6 100644 --- a/Content.Client/Power/APC/ApcBoundUserInterface.cs +++ b/Content.Client/Power/APC/ApcBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.APC; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Power.APC { @@ -19,9 +20,8 @@ protected override void Open() { base.Open(); - _menu = new ApcMenu(this); - _menu.OnClose += Close; - _menu.OpenCentered(); + _menu = this.CreateWindow(); + _menu.OnBreaker += BreakerPressed; } protected override void UpdateState(BoundUserInterfaceState state) @@ -36,15 +36,5 @@ public void BreakerPressed() { SendMessage(new ApcToggleMainBreakerMessage()); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - _menu?.Dispose(); - } - } } } diff --git a/Content.Client/Power/APC/UI/ApcMenu.xaml.cs b/Content.Client/Power/APC/UI/ApcMenu.xaml.cs index dbf68ea07b0..2f61ea63a86 100644 --- a/Content.Client/Power/APC/UI/ApcMenu.xaml.cs +++ b/Content.Client/Power/APC/UI/ApcMenu.xaml.cs @@ -17,13 +17,19 @@ namespace Content.Client.Power.APC.UI [GenerateTypedNameReferences] public sealed partial class ApcMenu : FancyWindow { - public ApcMenu(ApcBoundUserInterface owner) + public event Action? OnBreaker; + + public ApcMenu() { IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); - EntityView.SetEntity(owner.Owner); - BreakerButton.OnPressed += _ => owner.BreakerPressed(); + BreakerButton.OnPressed += _ => OnBreaker?.Invoke(); + } + + public void SetEntity(EntityUid entity) + { + EntityView.SetEntity(entity); } public void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Client/Power/Generator/GeneratorWindow.xaml.cs b/Content.Client/Power/Generator/GeneratorWindow.xaml.cs index edd2d7e11b0..161482e0905 100644 --- a/Content.Client/Power/Generator/GeneratorWindow.xaml.cs +++ b/Content.Client/Power/Generator/GeneratorWindow.xaml.cs @@ -10,35 +10,39 @@ namespace Content.Client.Power.Generator; [GenerateTypedNameReferences] public sealed partial class GeneratorWindow : FancyWindow { - private readonly EntityUid _entity; - [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly ILocalizationManager _loc = default!; - private readonly SharedPowerSwitchableSystem _switchable; - private readonly FuelGeneratorComponent? _component; - private PortableGeneratorComponentBuiState? _lastState; + private EntityUid _entity; + + public float? MaximumPower; + + public event Action? OnPower; + public event Action? OnState; + public event Action? OnSwitchOutput; + public event Action? OnEjectFuel; - public GeneratorWindow(PortableGeneratorBoundUserInterface bui, EntityUid entity) + public GeneratorWindow() { - _entity = entity; RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - _entityManager.TryGetComponent(entity, out _component); - _switchable = _entityManager.System(); - - EntityView.SetEntity(entity); TargetPower.IsValid += IsValid; TargetPower.ValueChanged += (args) => { - bui.SetTargetPower(args.Value); + OnPower?.Invoke(args.Value); }; - StartButton.OnPressed += _ => bui.Start(); - StopButton.OnPressed += _ => bui.Stop(); - OutputSwitchButton.OnPressed += _ => bui.SwitchOutput(); - FuelEject.OnPressed += _ => bui.EjectFuel(); + StartButton.OnPressed += _ => OnState?.Invoke(true); + StopButton.OnPressed += _ => OnState?.Invoke(false); + OutputSwitchButton.OnPressed += _ => OnSwitchOutput?.Invoke(); + FuelEject.OnPressed += _ => OnEjectFuel?.Invoke(); + } + + public void SetEntity(EntityUid entity) + { + _entity = entity; + EntityView.SetEntity(entity); } private bool IsValid(int arg) @@ -46,7 +50,7 @@ private bool IsValid(int arg) if (arg < 0) return false; - if (arg > (_lastState?.MaximumPower / 1000.0f ?? 0)) + if (arg > (MaximumPower / 1000.0f ?? 0)) return false; return true; @@ -54,16 +58,17 @@ private bool IsValid(int arg) public void Update(PortableGeneratorComponentBuiState state) { - if (_component == null) + MaximumPower = state.MaximumPower; + + if (!_entityManager.TryGetComponent(_entity, out FuelGeneratorComponent? component)) return; - _lastState = state; if (!TargetPower.LineEditControl.HasKeyboardFocus()) TargetPower.OverrideValue((int)(state.TargetPower / 1000.0f)); - var efficiency = SharedGeneratorSystem.CalcFuelEfficiency(state.TargetPower, state.OptimalPower, _component); + var efficiency = SharedGeneratorSystem.CalcFuelEfficiency(state.TargetPower, state.OptimalPower, component); Efficiency.Text = efficiency.ToString("P1"); - var burnRate = _component.OptimalBurnRate / efficiency; + var burnRate = component.OptimalBurnRate / efficiency; var left = state.RemainingFuel / burnRate; Eta.Text = Loc.GetString( @@ -103,14 +108,15 @@ public void Update(PortableGeneratorComponentBuiState state) } var canSwitch = _entityManager.TryGetComponent(_entity, out PowerSwitchableComponent? switchable); + var switcher = _entityManager.System(); OutputSwitchLabel.Visible = canSwitch; OutputSwitchButton.Visible = canSwitch; if (switchable != null) { - var voltage = _switchable.VoltageString(_switchable.GetVoltage(_entity, switchable)); + var voltage = switcher.VoltageString(switcher.GetVoltage(_entity, switchable)); OutputSwitchLabel.Text = Loc.GetString("portable-generator-ui-current-output", ("voltage", voltage)); - var nextVoltage = _switchable.VoltageString(_switchable.GetNextVoltage(_entity, switchable)); + var nextVoltage = switcher.VoltageString(switcher.GetNextVoltage(_entity, switchable)); OutputSwitchButton.Text = Loc.GetString("power-switchable-switch-voltage", ("voltage", nextVoltage)); OutputSwitchButton.Disabled = state.On; } diff --git a/Content.Client/Power/Generator/PortableGeneratorBoundUserInterface.cs b/Content.Client/Power/Generator/PortableGeneratorBoundUserInterface.cs index 30679d71fd6..550e1041b62 100644 --- a/Content.Client/Power/Generator/PortableGeneratorBoundUserInterface.cs +++ b/Content.Client/Power/Generator/PortableGeneratorBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Power.Generator; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Power.Generator; @@ -16,10 +17,25 @@ public PortableGeneratorBoundUserInterface(EntityUid owner, Enum uiKey) : base(o protected override void Open() { base.Open(); - _window = new GeneratorWindow(this, Owner); + _window = this.CreateWindow(); + _window.SetEntity(Owner); + _window.OnState += args => + { + if (args) + { + Start(); + } + else + { + Stop(); + } + }; + + _window.OnPower += SetTargetPower; + _window.OnEjectFuel += EjectFuel; + _window.OnSwitchOutput += SwitchOutput; _window.OpenCenteredLeft(); - _window.OnClose += Close; } protected override void UpdateState(BoundUserInterfaceState state) @@ -30,11 +46,6 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.Update(msg); } - protected override void Dispose(bool disposing) - { - _window?.Dispose(); - } - public void SetTargetPower(int target) { SendMessage(new PortableGeneratorSetTargetPowerMessage(target)); diff --git a/Content.Client/Power/PowerMonitoringConsoleBoundUserInterface.cs b/Content.Client/Power/PowerMonitoringConsoleBoundUserInterface.cs index dc1dcd03ef1..cbc343c06c6 100644 --- a/Content.Client/Power/PowerMonitoringConsoleBoundUserInterface.cs +++ b/Content.Client/Power/PowerMonitoringConsoleBoundUserInterface.cs @@ -1,4 +1,5 @@ using Content.Shared.Power; +using Robust.Client.UserInterface; namespace Content.Client.Power; @@ -11,9 +12,9 @@ public PowerMonitoringConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : b protected override void Open() { - _menu = new PowerMonitoringWindow(this, Owner); - _menu.OpenCentered(); - _menu.OnClose += Close; + _menu = this.CreateWindow(); + _menu.SetEntity(Owner); + _menu.SendPowerMonitoringConsoleMessageAction += SendPowerMonitoringConsoleMessage; } protected override void UpdateState(BoundUserInterfaceState state) @@ -22,9 +23,6 @@ protected override void UpdateState(BoundUserInterfaceState state) var castState = (PowerMonitoringConsoleBoundInterfaceState) state; - if (castState == null) - return; - EntMan.TryGetComponent(Owner, out var xform); _menu?.ShowEntites (castState.TotalSources, @@ -40,13 +38,4 @@ public void SendPowerMonitoringConsoleMessage(NetEntity? netEntity, PowerMonitor { SendMessage(new PowerMonitoringConsoleMessage(netEntity, group)); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - _menu?.Dispose(); - } } diff --git a/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs b/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs index 25a586a75de..0fb49df5344 100644 --- a/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs +++ b/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs @@ -32,7 +32,7 @@ private void UpdateWindowConsoleEntry if (windowEntry == null) return; - // Update sources and loads + // Update sources and loads UpdateEntrySourcesOrLoads(masterContainer, windowEntry.SourcesContainer, focusSources, _sourceIcon); UpdateEntrySourcesOrLoads(masterContainer, windowEntry.LoadsContainer, focusLoads, _loadIconPath); @@ -133,7 +133,7 @@ private void UpdateEntrySourcesOrLoads(BoxContainer masterContainer, BoxContaine subEntry.Button.OnButtonUp += args => { ButtonAction(subEntry, masterContainer); }; } - if (!_entManager.TryGetComponent(_owner, out var console)) + if (!_entManager.TryGetComponent(Entity, out var console)) return; // Update all children @@ -378,7 +378,7 @@ public PowerMonitoringWindowEntry(PowerMonitoringConsoleEntry entry) : base(entr AddChild(MainContainer); - // Grid container to hold the list of sources when selected + // Grid container to hold the list of sources when selected SourcesContainer = new BoxContainer() { Orientation = LayoutOrientation.Vertical, diff --git a/Content.Client/Power/PowerMonitoringWindow.xaml.cs b/Content.Client/Power/PowerMonitoringWindow.xaml.cs index 81fe1f4d047..e3043252486 100644 --- a/Content.Client/Power/PowerMonitoringWindow.xaml.cs +++ b/Content.Client/Power/PowerMonitoringWindow.xaml.cs @@ -15,13 +15,12 @@ namespace Content.Client.Power; [GenerateTypedNameReferences] public sealed partial class PowerMonitoringWindow : FancyWindow { - private readonly IEntityManager _entManager; + [Dependency] private IEntityManager _entManager = default!; private readonly SpriteSystem _spriteSystem; - private readonly IGameTiming _gameTiming; + [Dependency] private IGameTiming _gameTiming = default!; private const float BlinkFrequency = 1f; - private EntityUid? _owner; private NetEntity? _focusEntity; public event Action? SendPowerMonitoringConsoleMessageAction; @@ -34,31 +33,56 @@ public sealed partial class PowerMonitoringWindow : FancyWindow { PowerMonitoringConsoleGroup.APC, (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_triangle.png")), Color.LimeGreen) }, }; - public PowerMonitoringWindow(PowerMonitoringConsoleBoundUserInterface userInterface, EntityUid? owner) + public EntityUid Entity; + + public PowerMonitoringWindow() { RobustXamlLoader.Load(this); - _entManager = IoCManager.Resolve(); - _gameTiming = IoCManager.Resolve(); + IoCManager.InjectDependencies(this); _spriteSystem = _entManager.System(); - _owner = owner; + + // Set trackable entity selected action + NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap; + + // Update nav map + NavMap.ForceNavMapUpdate(); + + // Set UI tab titles + MasterTabContainer.SetTabTitle(0, Loc.GetString("power-monitoring-window-label-sources")); + MasterTabContainer.SetTabTitle(1, Loc.GetString("power-monitoring-window-label-smes")); + MasterTabContainer.SetTabTitle(2, Loc.GetString("power-monitoring-window-label-substation")); + MasterTabContainer.SetTabTitle(3, Loc.GetString("power-monitoring-window-label-apc")); + + // Track when the MasterTabContainer changes its tab + MasterTabContainer.OnTabChanged += OnTabChanged; + + // Set UI toggles + ShowHVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.HighVoltage); + ShowMVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.MediumVoltage); + ShowLVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.Apc); + } + + public void SetEntity(EntityUid uid) + { + Entity = uid; // Pass owner to nav map - NavMap.Owner = _owner; + NavMap.Owner = uid; // Set nav map grid uid var stationName = Loc.GetString("power-monitoring-window-unknown-location"); - if (_entManager.TryGetComponent(owner, out var xform)) + if (_entManager.TryGetComponent(uid, out var xform)) { NavMap.MapUid = xform.GridUid; - // Assign station name + // Assign station name if (_entManager.TryGetComponent(xform.GridUid, out var stationMetaData)) stationName = stationMetaData.EntityName; var msg = new FormattedMessage(); - msg.AddMarkup(Loc.GetString("power-monitoring-window-station-name", ("stationName", stationName))); + msg.AddMarkupOrThrow(Loc.GetString("power-monitoring-window-station-name", ("stationName", stationName))); StationName.SetMessage(msg); } @@ -68,29 +92,6 @@ public PowerMonitoringWindow(PowerMonitoringConsoleBoundUserInterface userInterf StationName.SetMessage(stationName); NavMap.Visible = false; } - - // Set trackable entity selected action - NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap; - - // Update nav map - NavMap.ForceNavMapUpdate(); - - // Set UI tab titles - MasterTabContainer.SetTabTitle(0, Loc.GetString("power-monitoring-window-label-sources")); - MasterTabContainer.SetTabTitle(1, Loc.GetString("power-monitoring-window-label-smes")); - MasterTabContainer.SetTabTitle(2, Loc.GetString("power-monitoring-window-label-substation")); - MasterTabContainer.SetTabTitle(3, Loc.GetString("power-monitoring-window-label-apc")); - - // Track when the MasterTabContainer changes its tab - MasterTabContainer.OnTabChanged += OnTabChanged; - - // Set UI toggles - ShowHVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.HighVoltage); - ShowMVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.MediumVoltage); - ShowLVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.Apc); - - // Set power monitoring message action - SendPowerMonitoringConsoleMessageAction += userInterface.SendPowerMonitoringConsoleMessage; } private void OnTabChanged(int tab) @@ -113,10 +114,7 @@ public void ShowEntites PowerMonitoringConsoleEntry[] focusLoads, EntityCoordinates? monitorCoords) { - if (_owner == null) - return; - - if (!_entManager.TryGetComponent(_owner.Value, out var console)) + if (!_entManager.TryGetComponent(Entity, out var console)) return; // Update power status text @@ -161,13 +159,13 @@ public void ShowEntites } // Show monitor location - var mon = _entManager.GetNetEntity(_owner); + var mon = _entManager.GetNetEntity(Entity); - if (monitorCoords != null && mon != null) + if (monitorCoords != null && mon.IsValid()) { var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png"))); var blip = new NavMapBlip(monitorCoords.Value, texture, Color.Cyan, true, false); - NavMap.TrackedEntities[mon.Value] = blip; + NavMap.TrackedEntities[mon] = blip; } // If the entry group doesn't match the current tab, the data is out dated, do not use it @@ -239,7 +237,7 @@ private void SetTrackedEntityFromNavMap(NetEntity? netEntity) if (netEntity == null) return; - if (!_entManager.TryGetComponent(_owner, out var console)) + if (!_entManager.TryGetComponent(Entity, out var console)) return; if (!console.PowerMonitoringDeviceMetaData.TryGetValue(netEntity.Value, out var metaData)) @@ -266,7 +264,7 @@ protected override void FrameUpdate(FrameEventArgs args) { AutoScrollToFocus(); - // Warning sign pulse + // Warning sign pulse var lit = _gameTiming.RealTime.TotalSeconds % BlinkFrequency > BlinkFrequency / 2f; SystemWarningPanel.Modulate = lit ? Color.White : new Color(178, 178, 178); } diff --git a/Content.Client/RCD/RCDMenu.xaml.cs b/Content.Client/RCD/RCDMenu.xaml.cs index 3eb0397a690..f0d27d6b1fb 100644 --- a/Content.Client/RCD/RCDMenu.xaml.cs +++ b/Content.Client/RCD/RCDMenu.xaml.cs @@ -20,31 +20,37 @@ public sealed partial class RCDMenu : RadialMenu [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; - private readonly SpriteSystem _spriteSystem; - private readonly SharedPopupSystem _popup; + private SharedPopupSystem _popup; + private SpriteSystem _sprites; public event Action>? SendRCDSystemMessageAction; private EntityUid _owner; - public RCDMenu(EntityUid owner, RCDMenuBoundUserInterface bui) + public RCDMenu() { IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); - _spriteSystem = _entManager.System(); _popup = _entManager.System(); + _sprites = _entManager.System(); - _owner = owner; + OnChildAdded += AddRCDMenuButtonOnClickActions; + } + + public void SetEntity(EntityUid uid) + { + _owner = uid; + Refresh(); + } + public void Refresh() + { // Find the main radial container var main = FindControl("Main"); - if (main == null) - return; - // Populate secondary radial containers - if (!_entManager.TryGetComponent(owner, out var rcd)) + if (!_entManager.TryGetComponent(_owner, out var rcd)) return; foreach (var protoId in rcd.AvailablePrototypes) @@ -56,14 +62,10 @@ public RCDMenu(EntityUid owner, RCDMenuBoundUserInterface bui) continue; var parent = FindControl(proto.Category); - - if (parent == null) - continue; - var tooltip = Loc.GetString(proto.SetName); if ((proto.Mode == RcdMode.ConstructTile || proto.Mode == RcdMode.ConstructObject) && - proto.Prototype != null && _protoManager.TryIndex(proto.Prototype, out var entProto)) + proto.Prototype != null && _protoManager.TryIndex(proto.Prototype, out var entProto, logError: false)) { tooltip = Loc.GetString(entProto.Name); } @@ -84,7 +86,7 @@ public RCDMenu(EntityUid owner, RCDMenuBoundUserInterface bui) { VerticalAlignment = VAlignment.Center, HorizontalAlignment = HAlignment.Center, - Texture = _spriteSystem.Frame0(proto.Sprite), + Texture = _sprites.Frame0(proto.Sprite), TextureScale = new Vector2(2f, 2f), }; @@ -112,11 +114,9 @@ public RCDMenu(EntityUid owner, RCDMenuBoundUserInterface bui) // Set up menu actions foreach (var child in Children) + { AddRCDMenuButtonOnClickActions(child); - - OnChildAdded += AddRCDMenuButtonOnClickActions; - - SendRCDSystemMessageAction += bui.SendRCDSystemMessage; + } } private static string OopsConcat(string a, string b) @@ -153,7 +153,7 @@ private void AddRCDMenuButtonOnClickActions(Control control) var name = Loc.GetString(proto.SetName); if (proto.Prototype != null && - _protoManager.TryIndex(proto.Prototype, out var entProto)) + _protoManager.TryIndex(proto.Prototype, out var entProto, logError: false)) name = entProto.Name; msg = Loc.GetString("rcd-component-change-build-mode", ("name", name)); diff --git a/Content.Client/RCD/RCDMenuBoundUserInterface.cs b/Content.Client/RCD/RCDMenuBoundUserInterface.cs index a37dbcecf8c..1dd03626ae6 100644 --- a/Content.Client/RCD/RCDMenuBoundUserInterface.cs +++ b/Content.Client/RCD/RCDMenuBoundUserInterface.cs @@ -3,6 +3,7 @@ using JetBrains.Annotations; using Robust.Client.Graphics; using Robust.Client.Input; +using Robust.Client.UserInterface; using Robust.Shared.Prototypes; namespace Content.Client.RCD; @@ -24,8 +25,9 @@ protected override void Open() { base.Open(); - _menu = new(Owner, this); - _menu.OnClose += Close; + _menu = this.CreateWindow(); + _menu.SetEntity(Owner); + _menu.SendRCDSystemMessageAction += SendRCDSystemMessage; // Open the menu, centered on the mouse var vpSize = _displayManager.ScreenSize; @@ -34,16 +36,8 @@ protected override void Open() public void SendRCDSystemMessage(ProtoId protoId) { - // A predicted message cannot be used here as the RCD UI is closed immediately + // A predicted message cannot be used here as the RCD UI is closed immediately // after this message is sent, which will stop the server from receiving it SendMessage(new RCDSystemMessage(protoId)); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - - _menu?.Dispose(); - } } diff --git a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs index abbb1d58ec4..922715bdadc 100644 --- a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs +++ b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Radio; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Radio.Ui; @@ -19,7 +20,12 @@ protected override void Open() { base.Open(); - _menu = new(); + _menu = this.CreateWindow(); + + if (EntMan.TryGetComponent(Owner, out IntercomComponent? intercom)) + { + _menu.Update((Owner, intercom)); + } _menu.OnMicPressed += enabled => { @@ -33,26 +39,10 @@ protected override void Open() { SendMessage(new SelectIntercomChannelMessage(channel)); }; - - _menu.OnClose += Close; - _menu.OpenCentered(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - _menu?.Close(); } - protected override void UpdateState(BoundUserInterfaceState state) + public void Update(Entity ent) { - base.UpdateState(state); - - if (state is not IntercomBoundUIState msg) - return; - - _menu?.Update(msg); + _menu?.Update(ent); } } diff --git a/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs b/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs index da008ca04c9..9641adb5b2d 100644 --- a/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs +++ b/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Research; using Content.Shared.Research.Components; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Research.UI { @@ -17,10 +18,7 @@ protected override void Open() { base.Open(); - _menu = new(); - - _menu.OnClose += Close; - _menu.OpenCentered(); + _menu = this.CreateWindow(); _menu.OnServerButtonPressed += () => { @@ -32,14 +30,6 @@ protected override void Open() }; } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - _menu?.Close(); - } - protected override void UpdateState(BoundUserInterfaceState state) { base.UpdateState(state); diff --git a/Content.Client/Research/UI/ResearchClientBoundUserInterface.cs b/Content.Client/Research/UI/ResearchClientBoundUserInterface.cs index 07dd35a0056..288445e4dea 100644 --- a/Content.Client/Research/UI/ResearchClientBoundUserInterface.cs +++ b/Content.Client/Research/UI/ResearchClientBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Research.Components; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Research.UI { @@ -16,10 +17,9 @@ public ResearchClientBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne protected override void Open() { base.Open(); - - _menu = new ResearchClientServerSelectionMenu(this); - _menu.OnClose += Close; - _menu.OpenCentered(); + _menu = this.CreateWindow(); + _menu.OnServerSelected += SelectServer; + _menu.OnServerDeselected += DeselectServer; } public void SelectServer(int serverId) @@ -38,12 +38,5 @@ protected override void UpdateState(BoundUserInterfaceState state) if (state is not ResearchClientBoundInterfaceState rState) return; _menu?.Populate(rState.ServerCount, rState.ServerNames, rState.ServerIds, rState.SelectedServerId); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - _menu?.Dispose(); - } } } diff --git a/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs b/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs index ceaa965e59f..d10f8b39f48 100644 --- a/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs +++ b/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs @@ -13,27 +13,26 @@ public sealed partial class ResearchClientServerSelectionMenu : DefaultWindow private int[] _serverIds = Array.Empty(); private int _selectedServerId = -1; - private ResearchClientBoundUserInterface Owner { get; } + public event Action? OnServerSelected; + public event Action? OnServerDeselected; - public ResearchClientServerSelectionMenu(ResearchClientBoundUserInterface owner) + public ResearchClientServerSelectionMenu() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - Owner = owner; - Servers.OnItemSelected += OnItemSelected; Servers.OnItemDeselected += OnItemDeselected; } public void OnItemSelected(ItemList.ItemListSelectedEventArgs itemListSelectedEventArgs) { - Owner.SelectServer(_serverIds[itemListSelectedEventArgs.ItemIndex]); + OnServerSelected?.Invoke(_serverIds[itemListSelectedEventArgs.ItemIndex]); } public void OnItemDeselected(ItemList.ItemListDeselectedEventArgs itemListDeselectedEventArgs) { - Owner.DeselectServer(); + OnServerDeselected?.Invoke(); } public void Populate(int serverCount, string[] serverNames, int[] serverIds, int selectedServerId) diff --git a/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs b/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs index f29e66baaeb..2895ada61fb 100644 --- a/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs +++ b/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs @@ -1,6 +1,8 @@ using Content.Shared.Research.Components; +using Content.Shared.Research.Prototypes; using JetBrains.Annotations; -using Robust.Client.GameObjects; +using Robust.Client.UserInterface; +using Robust.Shared.Prototypes; namespace Content.Client.Research.UI; @@ -20,7 +22,8 @@ protected override void Open() var owner = Owner; - _consoleMenu = new ResearchConsoleMenu(owner); + _consoleMenu = this.CreateWindow(); + _consoleMenu.SetEntity(owner); _consoleMenu.OnTechnologyCardPressed += id => { @@ -31,10 +34,20 @@ protected override void Open() { SendMessage(new ConsoleServerSelectionMessage()); }; + } + + public override void OnProtoReload(PrototypesReloadedEventArgs args) + { + base.OnProtoReload(args); + + if (!args.WasModified()) + return; - _consoleMenu.OnClose += Close; + if (State is not ResearchConsoleBoundInterfaceState rState) + return; - _consoleMenu.OpenCentered(); + _consoleMenu?.UpdatePanels(rState); + _consoleMenu?.UpdateInformationPanel(rState); } protected override void UpdateState(BoundUserInterfaceState state) @@ -46,12 +59,4 @@ protected override void UpdateState(BoundUserInterfaceState state) _consoleMenu?.UpdatePanels(castState); _consoleMenu?.UpdateInformationPanel(castState); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - _consoleMenu?.Dispose(); - } } diff --git a/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs b/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs index b364b833124..d1021f82e56 100644 --- a/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs +++ b/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs @@ -25,14 +25,13 @@ public sealed partial class ResearchConsoleMenu : FancyWindow [Dependency] private readonly IEntityManager _entity = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPlayerManager _player = default!; - private readonly TechnologyDatabaseComponent? _technologyDatabase; private readonly ResearchSystem _research; private readonly SpriteSystem _sprite; private readonly AccessReaderSystem _accessReader; - public readonly EntityUid Entity; + public EntityUid Entity; - public ResearchConsoleMenu(EntityUid entity) + public ResearchConsoleMenu() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); @@ -40,21 +39,23 @@ public ResearchConsoleMenu(EntityUid entity) _research = _entity.System(); _sprite = _entity.System(); _accessReader = _entity.System(); - Entity = entity; ServerButton.OnPressed += _ => OnServerButtonPressed?.Invoke(); + } - _entity.TryGetComponent(entity, out _technologyDatabase); + public void SetEntity(EntityUid entity) + { + Entity = entity; } - public void UpdatePanels(ResearchConsoleBoundInterfaceState state) + public void UpdatePanels(ResearchConsoleBoundInterfaceState state) { TechnologyCardsContainer.Children.Clear(); var availableTech = _research.GetAvailableTechnologies(Entity); SyncTechnologyList(AvailableCardsContainer, availableTech); - if (_technologyDatabase == null) + if (!_entity.TryGetComponent(Entity, out TechnologyDatabaseComponent? database)) return; // i can't figure out the spacing so here you go @@ -66,7 +67,7 @@ public void UpdatePanels(ResearchConsoleBoundInterfaceState state) var hasAccess = _player.LocalEntity is not { } local || !_entity.TryGetComponent(Entity, out var access) || _accessReader.IsAllowed(local, Entity, access); - foreach (var techId in _technologyDatabase.CurrentTechnologyCards) + foreach (var techId in database.CurrentTechnologyCards) { var tech = _prototype.Index(techId); var cardControl = new TechnologyCardControl(tech, _prototype, _sprite, _research.GetTechnologyDescription(tech, includeTier: false), state.Points, hasAccess); @@ -74,7 +75,7 @@ public void UpdatePanels(ResearchConsoleBoundInterfaceState state) TechnologyCardsContainer.AddChild(cardControl); } - var unlockedTech = _technologyDatabase.UnlockedTechnologies.Select(x => _prototype.Index(x)); + var unlockedTech = database.UnlockedTechnologies.Select(x => _prototype.Index(x)); SyncTechnologyList(UnlockedCardsContainer, unlockedTech); } @@ -85,14 +86,14 @@ public void UpdateInformationPanel(ResearchConsoleBoundInterfaceState state) ("points", state.Points))); ResearchAmountLabel.SetMessage(amountMsg); - if (_technologyDatabase == null) + if (!_entity.TryGetComponent(Entity, out TechnologyDatabaseComponent? database)) return; var disciplineText = Loc.GetString("research-discipline-none"); var disciplineColor = Color.Gray; - if (_technologyDatabase.MainDiscipline != null) + if (database.MainDiscipline != null) { - var discipline = _prototype.Index(_technologyDatabase.MainDiscipline); + var discipline = _prototype.Index(database.MainDiscipline); disciplineText = Loc.GetString(discipline.Name); disciplineColor = discipline.Color; } @@ -103,10 +104,10 @@ public void UpdateInformationPanel(ResearchConsoleBoundInterfaceState state) MainDisciplineLabel.SetMessage(msg); TierDisplayContainer.Children.Clear(); - foreach (var disciplineId in _technologyDatabase.SupportedDisciplines) + foreach (var disciplineId in database.SupportedDisciplines) { var discipline = _prototype.Index(disciplineId); - var tier = _research.GetHighestDisciplineTier(_technologyDatabase, discipline); + var tier = _research.GetHighestDisciplineTier(database, discipline); // don't show tiers with no available tech if (tier == 0) diff --git a/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs b/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs index 6185979eee6..9a5159880f9 100644 --- a/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs +++ b/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Robotics; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Robotics.UI; @@ -16,7 +17,9 @@ protected override void Open() { base.Open(); - _window = new RoboticsConsoleWindow(Owner); + _window = this.CreateWindow(); + _window.SetEntity(Owner); + _window.OnDisablePressed += address => { SendMessage(new RoboticsConsoleDisableMessage(address)); @@ -25,9 +28,6 @@ protected override void Open() { SendMessage(new RoboticsConsoleDestroyMessage(address)); }; - _window.OnClose += Close; - - _window.OpenCentered(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -37,14 +37,6 @@ protected override void UpdateState(BoundUserInterfaceState state) if (state is not RoboticsConsoleState cast) return; - _window?.UpdateState(cast); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - _window?.Dispose(); + _window.UpdateState(cast); } } diff --git a/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs b/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs index 367114f2aa6..87d7e62c392 100644 --- a/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs +++ b/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs @@ -23,11 +23,12 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow public Action? OnDisablePressed; public Action? OnDestroyPressed; - private Entity _console; private string? _selected; private Dictionary _cyborgs = new(); - public RoboticsConsoleWindow(EntityUid console) + public EntityUid Entity; + + public RoboticsConsoleWindow() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); @@ -35,9 +36,6 @@ public RoboticsConsoleWindow(EntityUid console) _lock = _entMan.System(); _sprite = _entMan.System(); - _console = (console, _entMan.GetComponent(console), null); - _entMan.TryGetComponent(_console, out _console.Comp2); - Cyborgs.OnItemSelected += args => { if (Cyborgs[args.ItemIndex].Metadata is not string address) @@ -66,6 +64,11 @@ public RoboticsConsoleWindow(EntityUid console) DestroyButton.StyleClasses.Add(StyleBase.ButtonCaution); } + public void SetEntity(EntityUid uid) + { + Entity = uid; + } + public void UpdateState(RoboticsConsoleState state) { _cyborgs = state.Cyborgs; @@ -81,7 +84,7 @@ public void UpdateState(RoboticsConsoleState state) PopulateData(); - var locked = _lock.IsLocked((_console, _console.Comp2)); + var locked = _lock.IsLocked(Entity); DangerZone.Visible = !locked; LockedMessage.Visible = locked; } @@ -134,14 +137,20 @@ private void PopulateData() BorgInfo.SetMessage(text); // how the turntables - DisableButton.Disabled = !data.HasBrain; - DestroyButton.Disabled = _timing.CurTime < _console.Comp1.NextDestroy; + DisableButton.Disabled = !(data.HasBrain && data.CanDisable); } protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); - DestroyButton.Disabled = _timing.CurTime < _console.Comp1.NextDestroy; + if (_entMan.TryGetComponent(Entity, out RoboticsConsoleComponent? console)) + { + DestroyButton.Disabled = _timing.CurTime < console.NextDestroy; + } + else + { + DestroyButton.Disabled = true; + } } } diff --git a/Content.Client/Salvage/UI/SalvageExpeditionConsoleBoundUserInterface.cs b/Content.Client/Salvage/UI/SalvageExpeditionConsoleBoundUserInterface.cs index 8f1723d1f22..663bde15b0d 100644 --- a/Content.Client/Salvage/UI/SalvageExpeditionConsoleBoundUserInterface.cs +++ b/Content.Client/Salvage/UI/SalvageExpeditionConsoleBoundUserInterface.cs @@ -30,17 +30,9 @@ public SalvageExpeditionConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : protected override void Open() { base.Open(); - _window = new OfferingWindow(); + _window = this.CreateWindow(); _window.Title = Loc.GetString("salvage-expedition-window-title"); - _window.OnClose += Close; - _window?.OpenCenteredLeft(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - _window?.Dispose(); - _window = null; + _window.OpenCenteredLeft(); } protected override void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Client/Shuttles/BUI/IFFConsoleBoundUserInterface.cs b/Content.Client/Shuttles/BUI/IFFConsoleBoundUserInterface.cs index 086369aa264..b8b4fb8a746 100644 --- a/Content.Client/Shuttles/BUI/IFFConsoleBoundUserInterface.cs +++ b/Content.Client/Shuttles/BUI/IFFConsoleBoundUserInterface.cs @@ -3,6 +3,7 @@ using Content.Shared.Shuttles.Events; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Shuttles.BUI; @@ -20,8 +21,7 @@ protected override void Open() { base.Open(); - _window = new IFFConsoleWindow(); - _window.OnClose += Close; + _window = this.CreateWindow(); _window.ShowIFF += SendIFFMessage; _window.ShowVessel += SendVesselMessage; _window.OpenCenteredLeft(); diff --git a/Content.Client/Shuttles/BUI/RadarConsoleBoundUserInterface.cs b/Content.Client/Shuttles/BUI/RadarConsoleBoundUserInterface.cs index 4bd44a47a8e..f75759b042f 100644 --- a/Content.Client/Shuttles/BUI/RadarConsoleBoundUserInterface.cs +++ b/Content.Client/Shuttles/BUI/RadarConsoleBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Shuttles.BUIStates; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using RadarConsoleWindow = Content.Client.Shuttles.UI.RadarConsoleWindow; namespace Content.Client.Shuttles.BUI; @@ -20,18 +21,7 @@ protected override void Open() { base.Open(); - _window = new RadarConsoleWindow(); - _window.OnClose += Close; - _window.OpenCentered(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - _window?.Dispose(); - } + _window = this.CreateWindow(); } protected override void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs b/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs index af7b6055c80..e677181419e 100644 --- a/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs +++ b/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.Shuttles.BUIStates; using Content.Shared.Shuttles.Events; using JetBrains.Annotations; +using Robust.Client.UserInterface; using Robust.Shared.Map; namespace Content.Client.Shuttles.BUI; @@ -19,9 +20,7 @@ public ShuttleConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne protected override void Open() { base.Open(); - _window = new ShuttleConsoleWindow(); - _window.OpenCentered(); - _window.OnClose += Close; + _window = this.CreateWindow(); _window.RequestFTL += OnFTLRequest; _window.RequestBeaconFTL += OnFTLBeaconRequest; diff --git a/Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs b/Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs index 3cc2a35d795..ed9bf40a481 100644 --- a/Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs +++ b/Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Silicons.Borgs; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Silicons.Borgs; @@ -18,9 +19,8 @@ protected override void Open() { base.Open(); - var owner = Owner; - - _menu = new BorgMenu(owner); + _menu = this.CreateWindow(); + _menu.SetEntity(Owner); _menu.BrainButtonPressed += () => { @@ -41,10 +41,6 @@ protected override void Open() { SendMessage(new BorgRemoveModuleBuiMessage(EntMan.GetNetEntity(module))); }; - - _menu.OnClose += Close; - - _menu.OpenCentered(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -55,12 +51,4 @@ protected override void UpdateState(BoundUserInterfaceState state) return; _menu?.UpdateState(msg); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - _menu?.Dispose(); - } } diff --git a/Content.Client/Silicons/Borgs/BorgMenu.xaml b/Content.Client/Silicons/Borgs/BorgMenu.xaml index 7d8fd9fe57d..4cc2e41a8fb 100644 --- a/Content.Client/Silicons/Borgs/BorgMenu.xaml +++ b/Content.Client/Silicons/Borgs/BorgMenu.xaml @@ -10,7 +10,7 @@ VerticalExpand="True"> - + diff --git a/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs index 046d8e299fe..08f15d9de22 100644 --- a/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs +++ b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs @@ -21,24 +21,33 @@ public sealed partial class BorgMenu : FancyWindow public Action? NameChanged; public Action? RemoveModuleButtonPressed; - private readonly BorgChassisComponent? _chassis; - public readonly EntityUid Entity; public float AccumulatedTime; private string _lastValidName; + private List _modules = new(); - public BorgMenu(EntityUid entity) + public EntityUid Entity; + + public BorgMenu() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - Entity = entity; + _lastValidName = NameLineEdit.Text; - if (_entity.TryGetComponent(Entity, out var chassis)) - _chassis = chassis; + EjectBatteryButton.OnPressed += _ => EjectBatteryButtonPressed?.Invoke(); + BrainButton.OnPressed += _ => BrainButtonPressed?.Invoke(); + NameLineEdit.OnTextChanged += OnNameChanged; + NameLineEdit.OnTextEntered += OnNameEntered; + NameLineEdit.OnFocusExit += OnNameFocusExit; + + UpdateBrainButton(); + } + + public void SetEntity(EntityUid entity) + { + Entity = entity; BorgSprite.SetEntity(entity); - ChargeBar.MaxValue = 1f; - ChargeBar.Value = 1f; if (_entity.TryGetComponent(Entity, out var nameIdentifierComponent)) { @@ -54,17 +63,6 @@ public BorgMenu(EntityUid entity) NameIdentifierLabel.Visible = false; NameLineEdit.Text = _entity.GetComponent(Entity).EntityName; } - - _lastValidName = NameLineEdit.Text; - - EjectBatteryButton.OnPressed += _ => EjectBatteryButtonPressed?.Invoke(); - BrainButton.OnPressed += _ => BrainButtonPressed?.Invoke(); - - NameLineEdit.OnTextChanged += OnNameChanged; - NameLineEdit.OnTextEntered += OnNameEntered; - NameLineEdit.OnFocusExit += OnNameFocusExit; - - UpdateBrainButton(); } protected override void FrameUpdate(FrameEventArgs args) @@ -88,7 +86,7 @@ public void UpdateState(BorgBuiState state) private void UpdateBrainButton() { - if (_chassis?.BrainEntity is { } brain) + if (_entity.TryGetComponent(Entity, out BorgChassisComponent? chassis) && chassis.BrainEntity is { } brain) { BrainButton.Text = _entity.GetComponent(brain).EntityName; BrainView.Visible = true; @@ -107,15 +105,31 @@ private void UpdateBrainButton() private void UpdateModulePanel() { - if (_chassis == null) + if (!_entity.TryGetComponent(Entity, out BorgChassisComponent? chassis)) return; ModuleCounter.Text = Loc.GetString("borg-ui-module-counter", - ("actual", _chassis.ModuleCount), - ("max", _chassis.MaxModules)); + ("actual", chassis.ModuleCount), + ("max", chassis.MaxModules)); + + if (chassis.ModuleContainer.Count == _modules.Count) + { + var isSame = true; + foreach (var module in chassis.ModuleContainer.ContainedEntities) + { + if (_modules.Contains(module)) + continue; + isSame = false; + break; + } + + if (isSame) + return; + } ModuleContainer.Children.Clear(); - foreach (var module in _chassis.ModuleContainer.ContainedEntities) + _modules.Clear(); + foreach (var module in chassis.ModuleContainer.ContainedEntities) { var control = new BorgModuleControl(module, _entity); control.RemoveButtonPressed += () => diff --git a/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs index 2aee0a38c0f..93349794b82 100644 --- a/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs +++ b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs @@ -1,6 +1,6 @@ using Content.Shared.Silicons.Laws.Components; using JetBrains.Annotations; -using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Silicons.Laws.Ui; @@ -20,18 +20,7 @@ protected override void Open() { base.Open(); - _menu = new(); - - _menu.OnClose += Close; - _menu.OpenCentered(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - _menu?.Close(); + _menu = this.CreateWindow(); } protected override void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs b/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs index e8442d23908..7d6a6cf2a5a 100644 --- a/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs +++ b/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs @@ -1,6 +1,6 @@ using Content.Shared.SprayPainter; using Content.Shared.SprayPainter.Components; -using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; namespace Content.Client.SprayPainter.UI; @@ -10,9 +10,6 @@ public sealed class SprayPainterBoundUserInterface : BoundUserInterface [ViewVariables] private SprayPainterWindow? _window; - [ViewVariables] - private SprayPainterSystem? _painter; - public SprayPainterBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { } @@ -21,27 +18,15 @@ protected override void Open() { base.Open(); - if (!EntMan.TryGetComponent(Owner, out var comp)) - return; - - _window = new SprayPainterWindow(); + _window = this.CreateWindow(); - _painter = EntMan.System(); - - _window.OnClose += Close; _window.OnSpritePicked = OnSpritePicked; _window.OnColorPicked = OnColorPicked; - _window.Populate(_painter.Entries, comp.Index, comp.PickedColor, comp.ColorPalette); - - _window.OpenCentered(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - _window?.Dispose(); + if (EntMan.TryGetComponent(Owner, out SprayPainterComponent? comp)) + { + _window.Populate(EntMan.System().Entries, comp.Index, comp.PickedColor, comp.ColorPalette); + } } private void OnSpritePicked(ItemList.ItemListSelectedEventArgs args) diff --git a/Content.Client/StationRecords/GeneralStationRecordConsoleBoundUserInterface.cs b/Content.Client/StationRecords/GeneralStationRecordConsoleBoundUserInterface.cs index 720a2efb9dd..e7bab71e38e 100644 --- a/Content.Client/StationRecords/GeneralStationRecordConsoleBoundUserInterface.cs +++ b/Content.Client/StationRecords/GeneralStationRecordConsoleBoundUserInterface.cs @@ -1,4 +1,5 @@ using Content.Shared.StationRecords; +using Robust.Client.UserInterface; namespace Content.Client.StationRecords; @@ -15,15 +16,12 @@ protected override void Open() { base.Open(); - _window = new(); + _window = this.CreateWindow(); _window.OnKeySelected += key => SendMessage(new SelectStationRecord(key)); _window.OnFiltersChanged += (type, filterValue) => SendMessage(new SetStationRecordFilter(type, filterValue)); _window.OnDeleted += id => SendMessage(new DeleteStationRecord(id)); - _window.OnClose += Close; - - _window.OpenCentered(); } protected override void UpdateState(BoundUserInterfaceState state) @@ -35,11 +33,4 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(cast); } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - _window?.Close(); - } } diff --git a/Content.Client/Store/Ui/StoreBoundUserInterface.cs b/Content.Client/Store/Ui/StoreBoundUserInterface.cs index 88ad0e3de8b..5eb5507f678 100644 --- a/Content.Client/Store/Ui/StoreBoundUserInterface.cs +++ b/Content.Client/Store/Ui/StoreBoundUserInterface.cs @@ -1,6 +1,8 @@ using Content.Shared.Store; using JetBrains.Annotations; using System.Linq; +using Content.Shared.Store.Components; +using Robust.Client.UserInterface; using Robust.Shared.Prototypes; namespace Content.Client.Store.Ui; @@ -28,10 +30,9 @@ public StoreBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) protected override void Open() { - _menu = new StoreMenu(_windowName); - - _menu.OpenCentered(); - _menu.OnClose += Close; + _menu = this.CreateWindow(); + if (EntMan.TryGetComponent(Owner, out var store)) + _menu.Title = Loc.GetString(store.Name); _menu.OnListingButtonPressed += (_, listing) => { @@ -87,15 +88,6 @@ protected override void UpdateState(BoundUserInterfaceState state) } } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - _menu?.Close(); - _menu?.Dispose(); - } - private void UpdateListingsWithSearchFilter() { if (_menu == null) diff --git a/Content.Client/Strip/StrippingMenu.cs b/Content.Client/Strip/StrippingMenu.cs index eea867b7948..1c46b4be35c 100644 --- a/Content.Client/Strip/StrippingMenu.cs +++ b/Content.Client/Strip/StrippingMenu.cs @@ -1,4 +1,3 @@ -using Content.Client.Inventory; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.Timing; @@ -11,14 +10,12 @@ public sealed class StrippingMenu : DefaultWindow public LayoutContainer InventoryContainer = new(); public BoxContainer HandsContainer = new() { Orientation = LayoutOrientation.Horizontal }; public BoxContainer SnareContainer = new(); - private StrippableBoundUserInterface _bui; public bool Dirty = true; - public StrippingMenu(string title, StrippableBoundUserInterface bui) - { - Title = title; - _bui = bui; + public event Action? OnDirty; + public StrippingMenu() + { var box = new BoxContainer() { Orientation = LayoutOrientation.Vertical, Margin = new Thickness(0, 8) }; Contents.AddChild(box); box.AddChild(SnareContainer); @@ -39,7 +36,7 @@ protected override void FrameUpdate(FrameEventArgs args) return; Dirty = false; - _bui.UpdateMenu(); + OnDirty?.Invoke(); } } } diff --git a/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorBoundUi.cs b/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorBoundUi.cs index 9132dd6ed5f..e3646c00cc3 100644 --- a/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorBoundUi.cs +++ b/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorBoundUi.cs @@ -1,6 +1,7 @@ using Content.Client.Eye; using Content.Shared.SurveillanceCamera; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.SurveillanceCamera.UI; @@ -25,20 +26,12 @@ protected override void Open() { base.Open(); - _window = new SurveillanceCameraMonitorWindow(); - - if (State != null) - { - UpdateState(State); - } - - _window.OpenCentered(); + _window = this.CreateWindow(); _window.CameraSelected += OnCameraSelected; _window.SubnetOpened += OnSubnetRequest; _window.CameraRefresh += OnCameraRefresh; _window.SubnetRefresh += OnSubnetRefresh; - _window.OnClose += Close; _window.CameraSwitchTimer += OnCameraSwitchTimer; _window.CameraDisconnect += OnCameraDisconnect; } diff --git a/Content.Client/Thief/ThiefBackpackBoundUserInterface.cs b/Content.Client/Thief/ThiefBackpackBoundUserInterface.cs index 37384daafef..0631d98993a 100644 --- a/Content.Client/Thief/ThiefBackpackBoundUserInterface.cs +++ b/Content.Client/Thief/ThiefBackpackBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Thief; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Thief; @@ -15,21 +16,9 @@ protected override void Open() { base.Open(); - _window = new ThiefBackpackMenu(this); - _window.OnClose += Close; - _window.OpenCentered(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; - - if (_window != null) - _window.OnClose -= Close; - - _window?.Dispose(); + _window = this.CreateWindow(); + _window.OnApprove += SendApprove; + _window.OnSetChange += SendChangeSelected; } protected override void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Client/Thief/ThiefBackpackMenu.xaml.cs b/Content.Client/Thief/ThiefBackpackMenu.xaml.cs index b2314cf3fe2..d9be1a6305a 100644 --- a/Content.Client/Thief/ThiefBackpackMenu.xaml.cs +++ b/Content.Client/Thief/ThiefBackpackMenu.xaml.cs @@ -12,45 +12,41 @@ public sealed partial class ThiefBackpackMenu : FancyWindow [Dependency] private readonly IEntitySystemManager _sysMan = default!; private readonly SpriteSystem _spriteSystem; - private readonly ThiefBackpackBoundUserInterface _owner; + public event Action? OnApprove; + public event Action? OnSetChange; - public ThiefBackpackMenu(ThiefBackpackBoundUserInterface owner) + public ThiefBackpackMenu() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); _spriteSystem = _sysMan.GetEntitySystem(); - _owner = owner; - - ApproveButton.OnButtonDown += (args) => + ApproveButton.OnPressed += args => { - _owner.SendApprove(); + OnApprove?.Invoke(); }; } public void UpdateState(ThiefBackpackBoundUserInterfaceState state) { - SetsGrid.RemoveAllChildren(); - int count = 0; - int selectedNumber = 0; - foreach (var set in state.Sets) + SetsGrid.DisposeAllChildren(); + var selectedNumber = 0; + foreach (var (set, info) in state.Sets) { - var child = new ThiefBackpackSet(set.Value, _spriteSystem); + var child = new ThiefBackpackSet(info, _spriteSystem); child.SetButton.OnButtonDown += (args) => { - _owner.SendChangeSelected(set.Key); + OnSetChange?.Invoke(set); }; SetsGrid.AddChild(child); - count++; - - if (set.Value.Selected) + if (info.Selected) selectedNumber++; } SelectedSets.Text = Loc.GetString("thief-backpack-window-selected", ("selectedCount", selectedNumber), ("maxCount", state.MaxSelectedSets)); - ApproveButton.Disabled = selectedNumber == state.MaxSelectedSets ? false : true; + ApproveButton.Disabled = selectedNumber != state.MaxSelectedSets; } } diff --git a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs index ee8cb28d2cc..4ae74a5d65e 100644 --- a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs +++ b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Atmos.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.UserInterface.Systems.Atmos.GasTank { @@ -14,7 +15,7 @@ public GasTankBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKe { } - public void SetOutputPressure(in float value) + public void SetOutputPressure(float value) { SendMessage(new GasTankSetPressureMessage { @@ -30,9 +31,10 @@ public void ToggleInternals() protected override void Open() { base.Open(); - _window = new GasTankWindow(this); - _window.OnClose += Close; - _window.OpenCentered(); + _window = this.CreateWindow(); + _window.SetTitle(EntMan.GetComponent(Owner).EntityName); + _window.OnOutputPressure += SetOutputPressure; + _window.OnToggleInternals += ToggleInternals; } protected override void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs index 7797a096de3..bf0e93b36c8 100644 --- a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs +++ b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs @@ -10,39 +10,39 @@ using Robust.Client.UserInterface.CustomControls; using static Robust.Client.UserInterface.Controls.BoxContainer; -namespace Content.Client.UserInterface.Systems.Atmos.GasTank +namespace Content.Client.UserInterface.Systems.Atmos.GasTank; + +public sealed class GasTankWindow + : BaseWindow { - public sealed class GasTankWindow - : BaseWindow - { - private GasTankBoundUserInterface _owner; - private readonly Label _lblName; - private readonly BoxContainer _topContainer; - private readonly Control _contentContainer; + [Dependency] private readonly IResourceCache _cache = default!; + private readonly RichTextLabel _lblPressure; + private readonly FloatSpinBox _spbPressure; + private readonly RichTextLabel _lblInternals; + private readonly Button _btnInternals; + private readonly Label _topLabel; - private readonly IResourceCache _resourceCache = default!; - private readonly RichTextLabel _lblPressure; - private readonly FloatSpinBox _spbPressure; - private readonly RichTextLabel _lblInternals; - private readonly Button _btnInternals; + public event Action? OnOutputPressure; + public event Action? OnToggleInternals; - public GasTankWindow(GasTankBoundUserInterface owner) - { - TextureButton btnClose; - _resourceCache = IoCManager.Resolve(); - _owner = owner; - var rootContainer = new LayoutContainer {Name = "GasTankRoot"}; - AddChild(rootContainer); + public GasTankWindow() + { + IoCManager.InjectDependencies(this); + Control contentContainer; + BoxContainer topContainer; + TextureButton btnClose; + var rootContainer = new LayoutContainer { Name = "GasTankRoot" }; + AddChild(rootContainer); MouseFilter = MouseFilterMode.Stop; - var panelTex = _resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); - var back = new StyleBoxTexture - { - Texture = panelTex, - Modulate = Color.FromHex("#25252A"), - }; + var panelTex = _cache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); + var back = new StyleBoxTexture + { + Texture = panelTex, + Modulate = Color.FromHex("#25252A"), + }; back.SetPatchMargin(StyleBox.Margin.All, 10); @@ -84,31 +84,32 @@ public GasTankWindow(GasTankBoundUserInterface owner) LayoutContainer.SetAnchorPreset(topContainerWrap, LayoutContainer.LayoutPreset.Wide); - var font = _resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13); + var font = _cache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13); - var topRow = new BoxContainer + _topLabel = new Label + { + FontOverride = font, + FontColorOverride = StyleNano.NanoGold, + VerticalAlignment = VAlignment.Center, + HorizontalExpand = true, + HorizontalAlignment = HAlignment.Left, + Margin = new Thickness(0, 0, 20, 0), + }; + + var topRow = new BoxContainer + { + Orientation = LayoutOrientation.Horizontal, + Margin = new Thickness(4, 2, 12, 2), + Children = { - Orientation = LayoutOrientation.Horizontal, - Margin = new Thickness(4, 2, 12, 2), - Children = + _topLabel, + (btnClose = new TextureButton { - (_lblName = new Label - { - Text = Loc.GetString("gas-tank-window-label"), - FontOverride = font, - FontColorOverride = StyleNano.NanoGold, - VerticalAlignment = VAlignment.Center, - HorizontalExpand = true, - HorizontalAlignment = HAlignment.Left, - Margin = new Thickness(0, 0, 20, 0), - }), - (btnClose = new TextureButton - { - StyleClasses = {DefaultWindow.StyleClassWindowCloseButton}, - VerticalAlignment = VAlignment.Center - }) - } - }; + StyleClasses = {DefaultWindow.StyleClassWindowCloseButton}, + VerticalAlignment = VAlignment.Center + }) + } + }; var middle = new PanelContainer { @@ -171,19 +172,24 @@ public GasTankWindow(GasTankBoundUserInterface owner) }; _contentContainer.AddChild(_spbPressure); - // Handlers - _spbPressure.OnValueChanged += args => - { - _owner.SetOutputPressure(args.Value); - }; + // Handlers + _spbPressure.OnValueChanged += args => + { + OnOutputPressure?.Invoke(args.Value); + }; - _btnInternals.OnPressed += args => - { - _owner.ToggleInternals(); - }; + _btnInternals.OnPressed += args => + { + OnToggleInternals?.Invoke(); + }; - btnClose.OnPressed += _ => Close(); - } + btnClose.OnPressed += _ => Close(); + } + + public void SetTitle(string name) + { + _topLabel.Text = name; + } public void UpdateState(GasTankBoundUserInterfaceState state) { diff --git a/Content.Client/VendingMachines/VendingMachineBoundUserInterface.cs b/Content.Client/VendingMachines/VendingMachineBoundUserInterface.cs index 17ddba77ffc..eafab84ed63 100644 --- a/Content.Client/VendingMachines/VendingMachineBoundUserInterface.cs +++ b/Content.Client/VendingMachines/VendingMachineBoundUserInterface.cs @@ -2,6 +2,7 @@ using Content.Shared.VendingMachines; using Robust.Client.UserInterface.Controls; using System.Linq; +using Robust.Client.UserInterface; namespace Content.Client.VendingMachines { @@ -28,15 +29,14 @@ protected override void Open() _cachedInventory = vendingMachineSys.GetAllInventory(Owner); - _menu = new VendingMachineMenu { Title = EntMan.GetComponent(Owner).EntityName }; + _menu = this.CreateWindow(); + _menu.OpenCenteredLeft(); + _menu.Title = EntMan.GetComponent(Owner).EntityName; - _menu.OnClose += Close; _menu.OnItemSelected += OnItemSelected; _menu.OnSearchChanged += OnSearchChanged; _menu.Populate(_cachedInventory, out _cachedFilteredIndex); - - _menu.OpenCenteredLeft(); } protected override void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs index f700c6663b9..891804674d3 100644 --- a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs +++ b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs @@ -1,12 +1,13 @@ using Content.Shared.VoiceMask; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; using Robust.Shared.Prototypes; namespace Content.Client.VoiceMask; public sealed class VoiceMaskBoundUserInterface : BoundUserInterface { - [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IPrototypeManager _protomanager = default!; [ViewVariables] private VoiceMaskNameChangeWindow? _window; @@ -19,12 +20,11 @@ protected override void Open() { base.Open(); - _window = new(_proto); + _window = this.CreateWindow(); + _window.ReloadVerbs(_protomanager); - _window.OpenCentered(); _window.OnNameChange += OnNameSelected; _window.OnVerbChange += verb => SendMessage(new VoiceMaskChangeVerbMessage(verb)); - _window.OnClose += Close; } private void OnNameSelected(string name) diff --git a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs index 16a28f9d9b3..0dc41f807ab 100644 --- a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs +++ b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs @@ -17,7 +17,7 @@ public sealed partial class VoiceMaskNameChangeWindow : FancyWindow private string? _verb; - public VoiceMaskNameChangeWindow(IPrototypeManager proto) + public VoiceMaskNameChangeWindow() { RobustXamlLoader.Load(this); @@ -32,12 +32,10 @@ public VoiceMaskNameChangeWindow(IPrototypeManager proto) SpeechVerbSelector.SelectId(args.Id); }; - ReloadVerbs(proto); - AddVerbs(); } - private void ReloadVerbs(IPrototypeManager proto) + public void ReloadVerbs(IPrototypeManager proto) { foreach (var verb in proto.EnumeratePrototypes()) { diff --git a/Content.Client/Weapons/Melee/UI/MeleeSpeechBoundUserInterface.cs b/Content.Client/Weapons/Melee/UI/MeleeSpeechBoundUserInterface.cs index f3e0c0a539a..3f01808c422 100644 --- a/Content.Client/Weapons/Melee/UI/MeleeSpeechBoundUserInterface.cs +++ b/Content.Client/Weapons/Melee/UI/MeleeSpeechBoundUserInterface.cs @@ -1,5 +1,6 @@ using Robust.Client.GameObjects; using Content.Shared.Speech.Components; +using Robust.Client.UserInterface; namespace Content.Client.Weapons.Melee.UI; @@ -19,17 +20,10 @@ protected override void Open() { base.Open(); - _window = new MeleeSpeechWindow(); - if (State != null) - UpdateState(State); - - _window.OpenCentered(); - - _window.OnClose += Close; + _window = this.CreateWindow(); _window.OnBattlecryEntered += OnBattlecryChanged; } - private void OnBattlecryChanged(string newBattlecry) { SendMessage(new MeleeSpeechBattlecryChangedMessage(newBattlecry)); diff --git a/Content.Client/Wires/UI/WiresBoundUserInterface.cs b/Content.Client/Wires/UI/WiresBoundUserInterface.cs index 5a8869a204e..edf1a2d3770 100644 --- a/Content.Client/Wires/UI/WiresBoundUserInterface.cs +++ b/Content.Client/Wires/UI/WiresBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Wires; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Wires.UI { @@ -15,10 +16,8 @@ public WiresBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) protected override void Open() { base.Open(); - - _menu = new WiresMenu(this); - _menu.OnClose += Close; - _menu.OpenCenteredLeft(); + _menu = this.CreateWindow(); + _menu.OnAction += PerformAction; } protected override void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Client/Wires/UI/WiresMenu.cs b/Content.Client/Wires/UI/WiresMenu.cs index 7bccc208616..eccc548297c 100644 --- a/Content.Client/Wires/UI/WiresMenu.cs +++ b/Content.Client/Wires/UI/WiresMenu.cs @@ -1,4 +1,3 @@ -using System; using System.Numerics; using Content.Client.Examine; using Content.Client.Resources; @@ -12,10 +11,6 @@ using Robust.Client.UserInterface.CustomControls; using Robust.Shared.Animations; using Robust.Shared.Input; -using Robust.Shared.IoC; -using Robust.Shared.Localization; -using Robust.Shared.Maths; -using Robust.Shared.Random; using static Robust.Client.UserInterface.Controls.BoxContainer; namespace Content.Client.Wires.UI @@ -24,8 +19,6 @@ public sealed class WiresMenu : BaseWindow { [Dependency] private readonly IResourceCache _resourceCache = default!; - public WiresBoundUserInterface Owner { get; } - private readonly Control _wiresHBox; private readonly Control _topContainer; private readonly Control _statusContainer; @@ -35,11 +28,12 @@ public sealed class WiresMenu : BaseWindow public TextureButton CloseButton { get; set; } - public WiresMenu(WiresBoundUserInterface owner) + public event Action? OnAction; + + public WiresMenu() { IoCManager.InjectDependencies(this); - Owner = owner; var rootContainer = new LayoutContainer {Name = "WireRoot"}; AddChild(rootContainer); @@ -257,12 +251,12 @@ public void Populate(WiresBoundUserInterfaceState state) control.WireClicked += () => { - Owner.PerformAction(wire.Id, wire.IsCut ? WiresAction.Mend : WiresAction.Cut); + OnAction?.Invoke(wire.Id, wire.IsCut ? WiresAction.Mend : WiresAction.Cut); }; control.ContactsClicked += () => { - Owner.PerformAction(wire.Id, WiresAction.Pulse); + OnAction?.Invoke(wire.Id, WiresAction.Pulse); }; } diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs index 2538caf6eb8..c7a74815b6b 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Xenoarchaeology.Equipment; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.UserInterface; namespace Content.Client.Xenoarchaeology.Ui; @@ -18,10 +19,7 @@ protected override void Open() { base.Open(); - _consoleMenu = new AnalysisConsoleMenu(); - - _consoleMenu.OnClose += Close; - _consoleMenu.OpenCentered(); + _consoleMenu = this.CreateWindow(); _consoleMenu.OnServerSelectionButtonPressed += () => { From c7ef8b46a6e671d1134e5eb3b3735bd9597dea7c Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 02:44:20 -0400 Subject: [PATCH 023/366] Use station name --- .../Pinpointer/UI/StationMapBoundUserInterface.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs index 7417fafede5..4421f8ec08e 100644 --- a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs +++ b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs @@ -17,12 +17,19 @@ protected override void Open() EntityUid? gridUid = null; if (EntMan.TryGetComponent(Owner, out var xform)) - { gridUid = xform.GridUid; - } _window = this.CreateWindow(); _window.Title = EntMan.GetComponent(Owner).EntityName; - _window.Set(gridUid, Owner); + + string stationName = string.Empty; + + if(EntMan.TryGetComponent(gridUid, out var gridMetaData)) + stationName = gridMetaData.EntityName; + + if (EntMan.TryGetComponent(Owner, out var comp) && comp.ShowLocation) + _window.Set(stationName, gridUid, Owner); + else + _window.Set(stationName, gridUid, null); } } From 230d0101e91d1cfcd1a13eaa15fa03a463dfd4b6 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 02:49:22 -0400 Subject: [PATCH 024/366] Silly. --- .../Weapons/Reflect/ReflectSystem.cs | 85 +++---------------- 1 file changed, 10 insertions(+), 75 deletions(-) diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 7f20974a144..881b547f27f 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -3,9 +3,7 @@ using Content.Shared.Administration.Logs; using Content.Shared.Alert; using Content.Shared.Audio; -using Content.Shared.Damage.Components; using Content.Shared.Database; -using Content.Shared.Gravity; using Content.Shared.Hands; using Content.Shared.Inventory; using Content.Shared.Inventory.Events; @@ -13,11 +11,9 @@ using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Popups; using Content.Shared.Projectiles; -using Content.Shared.Standing; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; -using Content.Shared.WhiteDream.BloodCult.BloodCultist; -using Content.Shared.WhiteDream.BloodCult.Items; +using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; using Robust.Shared.Physics.Components; @@ -42,12 +38,6 @@ public sealed class ReflectSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; - [Dependency] private readonly SharedGravitySystem _gravity = default!; - [Dependency] private readonly StandingStateSystem _standing = default!; - [Dependency] private readonly AlertsSystem _alerts = default!; - - [ValidatePrototypeId] - private const string DeflectingAlert = "Deflecting"; public override void Initialize() { @@ -70,7 +60,7 @@ private void OnReflectUserHitscan(EntityUid uid, ReflectUserComponent component, if (args.Reflected) return; - foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.WITHOUT_POCKET)) + foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.All & ~SlotFlags.POCKET)) { if (!TryReflectHitscan(uid, ent, args.Shooter, args.SourceItem, args.Direction, out var dir)) continue; @@ -83,7 +73,7 @@ private void OnReflectUserHitscan(EntityUid uid, ReflectUserComponent component, private void OnReflectUserCollide(EntityUid uid, ReflectUserComponent component, ref ProjectileReflectAttemptEvent args) { - foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.WITHOUT_POCKET)) + foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.All & ~SlotFlags.POCKET)) { if (!TryReflectProjectile(uid, ent, args.ProjUid)) continue; @@ -104,23 +94,15 @@ private void OnReflectCollide(EntityUid uid, ReflectComponent component, ref Pro private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid projectile, ProjectileComponent? projectileComp = null, ReflectComponent? reflect = null) { - if ( - !Resolve(reflector, ref reflect, false) || + if (!Resolve(reflector, ref reflect, false) || !_toggle.IsActivated(reflector) || !TryComp(projectile, out var reflective) || (reflect.Reflects & reflective.Reflective) == 0x0 || - !TryComp(projectile, out var physics) || - TryComp(reflector, out var staminaComponent) && staminaComponent.Critical || - _standing.IsDown(reflector) - ) - return false; - - // Non cultists can't use cult items to reflect anything. - if (HasComp(reflector) && !HasComp(user)) - return false; - - if (!_random.Prob(CalcReflectChance(reflector, reflect))) + !_random.Prob(reflect.ReflectProb) || + !TryComp(projectile, out var physics)) + { return false; + } var rotation = _random.NextAngle(-reflect.Spread / 2, reflect.Spread / 2).Opposite(); var existingVelocity = _physics.GetMapLinearVelocity(projectile, component: physics); @@ -158,34 +140,6 @@ private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid return true; } - private float CalcReflectChance(EntityUid reflector, ReflectComponent reflect) - { - /* - * The rules of deflection are as follows: - * If you innately reflect things via magic, biology etc., you always have a full chance. - * If you are standing up and standing still, you're prepared to deflect and have full chance. - * If you have velocity, your deflection chance depends on your velocity, clamped. - * If you are floating, your chance is the minimum value possible. - * You cannot deflect if you are knocked down or stunned. - */ - - if (reflect.Innate) - return reflect.ReflectProb; - - if (_gravity.IsWeightless(reflector)) - return reflect.MinReflectProb; - - if (!TryComp(reflector, out var reflectorPhysics)) - return reflect.ReflectProb; - - return MathHelper.Lerp( - reflect.MinReflectProb, - reflect.ReflectProb, - // Inverse progression between velocities fed in as progression between probabilities. We go high -> low so the output here needs to be _inverted_. - 1 - Math.Clamp((reflectorPhysics.LinearVelocity.Length() - reflect.VelocityBeforeNotMaxProb) / (reflect.VelocityBeforeMinProb - reflect.VelocityBeforeNotMaxProb), 0, 1) - ); - } - private void OnReflectHitscan(EntityUid uid, ReflectComponent component, ref HitScanReflectAttemptEvent args) { if (args.Reflected || @@ -209,13 +163,13 @@ private bool TryReflectHitscan( Vector2 direction, [NotNullWhen(true)] out Vector2? newDirection) { - newDirection = null; if (!TryComp(reflector, out var reflect) || !_toggle.IsActivated(reflector) || !_random.Prob(reflect.ReflectProb)) { newDirection = null; return false; + } if (_netManager.IsServer) { @@ -240,9 +194,6 @@ private void OnReflectEquipped(EntityUid uid, ReflectComponent component, GotEqu return; EnsureComp(args.Equipee); - - if (component.Enabled) - EnableAlert(args.Equipee); } private void OnReflectUnequipped(EntityUid uid, ReflectComponent comp, GotUnequippedEvent args) @@ -256,9 +207,6 @@ private void OnReflectHandEquipped(EntityUid uid, ReflectComponent component, Go return; EnsureComp(args.User); - - if (component.Enabled) - EnableAlert(args.User); } private void OnReflectHandUnequipped(EntityUid uid, ReflectComponent component, GotUnequippedHandEvent args) @@ -277,28 +225,15 @@ private void OnToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggl /// private void RefreshReflectUser(EntityUid user) { - foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(user, SlotFlags.WITHOUT_POCKET)) + foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(user, SlotFlags.All & ~SlotFlags.POCKET)) { if (!HasComp(ent) || !_toggle.IsActivated(ent)) continue; EnsureComp(user); - EnableAlert(user); - return; } RemCompDeferred(user); - DisableAlert(user); - } - - private void EnableAlert(EntityUid alertee) - { - _alerts.ShowAlert(alertee, DeflectingAlert); - } - - private void DisableAlert(EntityUid alertee) - { - _alerts.ClearAlert(alertee, DeflectingAlert); } } From 1defdceca265237f9a9a459d0ea289cefe49b3ef Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Fri, 14 Jun 2024 23:43:23 -0400 Subject: [PATCH 025/366] Add moon boots (#29009) --- Content.Client/Clothing/MagbootsSystem.cs | 8 --- .../Components/MovedByPressureComponent.cs | 31 ----------- .../AtmosphereSystem.HighPressureDelta.cs | 2 +- Content.Server/Clothing/MagbootsSystem.cs | 49 ------------------ .../Damage/Systems/GodmodeSystem.cs | 2 +- Content.Server/Gravity/GravitySystem.cs | 1 - .../EntitySystems/GravityWellSystem.cs | 2 +- .../Components/MovedByPressureComponent.cs | 31 +++++++++++ .../AntiGravityClothingComponent.cs | 9 ++++ .../AntiGravityClothingSystem.cs | 23 ++++++++ Content.Shared/Gravity/SharedGravitySystem.cs | 39 +++++++------- .../Inventory/InventorySystem.Relay.cs | 2 + .../Entities/Clothing/Shoes/misc.yml | 6 ++- .../Entities/Structures/Machines/lathe.yml | 1 + Resources/Prototypes/Recipes/Lathes/misc.yml | 7 +++ .../Prototypes/Research/experimental.yml | 1 + .../Boots/moonboots.rsi/equipped-FEET-vox.png | Bin 0 -> 412 bytes .../Boots/moonboots.rsi/equipped-FEET.png | Bin 0 -> 316 bytes .../Shoes/Boots/moonboots.rsi/icon.png | Bin 0 -> 340 bytes .../Shoes/Boots/moonboots.rsi/inhand-left.png | Bin 0 -> 323 bytes .../Boots/moonboots.rsi/inhand-right.png | Bin 0 -> 340 bytes .../Shoes/Boots/moonboots.rsi/meta.json | 30 +++++++++++ 22 files changed, 130 insertions(+), 114 deletions(-) delete mode 100644 Content.Client/Clothing/MagbootsSystem.cs delete mode 100644 Content.Server/Atmos/Components/MovedByPressureComponent.cs delete mode 100644 Content.Server/Clothing/MagbootsSystem.cs create mode 100644 Content.Shared/Atmos/Components/MovedByPressureComponent.cs create mode 100644 Content.Shared/Clothing/Components/AntiGravityClothingComponent.cs create mode 100644 Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs create mode 100644 Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/equipped-FEET-vox.png create mode 100644 Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/equipped-FEET.png create mode 100644 Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/meta.json diff --git a/Content.Client/Clothing/MagbootsSystem.cs b/Content.Client/Clothing/MagbootsSystem.cs deleted file mode 100644 index a3d39eafded..00000000000 --- a/Content.Client/Clothing/MagbootsSystem.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Clothing; - -namespace Content.Client.Clothing; - -public sealed class MagbootsSystem : SharedMagbootsSystem -{ - -} diff --git a/Content.Server/Atmos/Components/MovedByPressureComponent.cs b/Content.Server/Atmos/Components/MovedByPressureComponent.cs deleted file mode 100644 index ca830767bda..00000000000 --- a/Content.Server/Atmos/Components/MovedByPressureComponent.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Content.Server.Atmos.Components -{ - // Unfortunately can't be friends yet due to magboots. - [RegisterComponent] - public sealed partial class MovedByPressureComponent : Component - { - public const float MoveForcePushRatio = 1f; - public const float MoveForceForcePushRatio = 1f; - public const float ProbabilityOffset = 25f; - public const float ProbabilityBasePercent = 10f; - public const float ThrowForce = 100f; - - /// - /// Accumulates time when yeeted by high pressure deltas. - /// - [DataField("accumulator")] - public float Accumulator = 0f; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("enabled")] - public bool Enabled { get; set; } = true; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("pressureResistance")] - public float PressureResistance { get; set; } = 1f; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("moveResist")] - public float MoveResist { get; set; } = 100f; - [ViewVariables(VVAccess.ReadWrite)] - public int LastHighPressureMovementAirCycle { get; set; } = 0; - } -} diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs index 461435f0624..42f98089408 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs @@ -1,6 +1,6 @@ using Content.Server.Atmos.Components; using Content.Shared.Atmos; -using Content.Shared.Humanoid; +using Content.Shared.Atmos.Components; using Content.Shared.Mobs.Components; using Content.Shared.Physics; using Robust.Shared.Audio; diff --git a/Content.Server/Clothing/MagbootsSystem.cs b/Content.Server/Clothing/MagbootsSystem.cs deleted file mode 100644 index 3838ad168d1..00000000000 --- a/Content.Server/Clothing/MagbootsSystem.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Content.Server.Atmos.Components; -using Content.Shared.Alert; -using Content.Shared.Clothing; - -namespace Content.Server.Clothing; - -public sealed class MagbootsSystem : SharedMagbootsSystem -{ - [Dependency] private readonly AlertsSystem _alerts = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnGotEquipped); - SubscribeLocalEvent(OnGotUnequipped); - } - - protected override void UpdateMagbootEffects(EntityUid parent, EntityUid uid, bool state, MagbootsComponent? component) - { - if (!Resolve(uid, ref component)) - return; - state = state && component.On; - - if (TryComp(parent, out MovedByPressureComponent? movedByPressure)) - { - movedByPressure.Enabled = !state; - } - - if (state) - { - _alerts.ShowAlert(parent, component.MagbootsAlert); - } - else - { - _alerts.ClearAlert(parent, component.MagbootsAlert); - } - } - - private void OnGotUnequipped(EntityUid uid, MagbootsComponent component, ref ClothingGotUnequippedEvent args) - { - UpdateMagbootEffects(args.Wearer, uid, false, component); - } - - private void OnGotEquipped(EntityUid uid, MagbootsComponent component, ref ClothingGotEquippedEvent args) - { - UpdateMagbootEffects(args.Wearer, uid, true, component); - } -} diff --git a/Content.Server/Damage/Systems/GodmodeSystem.cs b/Content.Server/Damage/Systems/GodmodeSystem.cs index 404cc639057..d896fba71c4 100644 --- a/Content.Server/Damage/Systems/GodmodeSystem.cs +++ b/Content.Server/Damage/Systems/GodmodeSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Atmos.Components; +using Content.Shared.Atmos.Components; using Content.Shared.Damage.Components; using Content.Shared.Damage.Systems; diff --git a/Content.Server/Gravity/GravitySystem.cs b/Content.Server/Gravity/GravitySystem.cs index ea62d4a8195..6807b9df4a4 100644 --- a/Content.Server/Gravity/GravitySystem.cs +++ b/Content.Server/Gravity/GravitySystem.cs @@ -1,7 +1,6 @@ using Content.Shared.Gravity; using JetBrains.Annotations; using Robust.Shared.Map.Components; -using Robust.Shared.Utility; namespace Content.Server.Gravity { diff --git a/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs b/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs index 779b2f59719..f53d658ebd7 100644 --- a/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs +++ b/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs @@ -1,6 +1,6 @@ using System.Numerics; -using Content.Server.Atmos.Components; using Content.Server.Singularity.Components; +using Content.Shared.Atmos.Components; using Content.Shared.Ghost; using Content.Shared.Singularity.EntitySystems; using Robust.Shared.Map; diff --git a/Content.Shared/Atmos/Components/MovedByPressureComponent.cs b/Content.Shared/Atmos/Components/MovedByPressureComponent.cs new file mode 100644 index 00000000000..8a4e2c6d4c9 --- /dev/null +++ b/Content.Shared/Atmos/Components/MovedByPressureComponent.cs @@ -0,0 +1,31 @@ +namespace Content.Shared.Atmos.Components; + +// Unfortunately can't be friends yet due to magboots. +[RegisterComponent] +public sealed partial class MovedByPressureComponent : Component +{ + public const float MoveForcePushRatio = 1f; + public const float MoveForceForcePushRatio = 1f; + public const float ProbabilityOffset = 25f; + public const float ProbabilityBasePercent = 10f; + public const float ThrowForce = 100f; + + /// + /// Accumulates time when yeeted by high pressure deltas. + /// + [DataField] + public float Accumulator; + + [DataField] + public bool Enabled { get; set; } = true; + + [DataField] + public float PressureResistance { get; set; } = 1f; + + [DataField] + public float MoveResist { get; set; } = 100f; + + [ViewVariables(VVAccess.ReadWrite)] + public int LastHighPressureMovementAirCycle { get; set; } = 0; +} + diff --git a/Content.Shared/Clothing/Components/AntiGravityClothingComponent.cs b/Content.Shared/Clothing/Components/AntiGravityClothingComponent.cs new file mode 100644 index 00000000000..a8fcbdd2eb3 --- /dev/null +++ b/Content.Shared/Clothing/Components/AntiGravityClothingComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Clothing.Components; + +/// +/// This is used for clothing that makes an entity weightless when worn. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class AntiGravityClothingComponent : Component; diff --git a/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs new file mode 100644 index 00000000000..c5b2ee3dfc8 --- /dev/null +++ b/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs @@ -0,0 +1,23 @@ +using Content.Shared.Clothing.Components; +using Content.Shared.Gravity; +using Content.Shared.Inventory; + +namespace Content.Shared.Clothing.EntitySystems; + +public sealed class AntiGravityClothingSystem : EntitySystem +{ + /// + public override void Initialize() + { + SubscribeLocalEvent>(OnIsWeightless); + } + + private void OnIsWeightless(Entity ent, ref InventoryRelayedEvent args) + { + if (args.Args.Handled) + return; + + args.Args.Handled = true; + args.Args.IsWeightless = true; + } +} diff --git a/Content.Shared/Gravity/SharedGravitySystem.cs b/Content.Shared/Gravity/SharedGravitySystem.cs index 59d75e453af..f9c65e9477b 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.cs @@ -1,9 +1,7 @@ using Content.Shared.Alert; -using Content.Shared.Clothing; using Content.Shared.Inventory; using Content.Shared.Movement.Components; using Robust.Shared.GameStates; -using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Serialization; @@ -16,7 +14,6 @@ public abstract partial class SharedGravitySystem : EntitySystem { [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] private readonly AlertsSystem _alerts = default!; - [Dependency] private readonly InventorySystem _inventory = default!; [ValidatePrototypeId] public const string WeightlessAlert = "Weightless"; @@ -34,6 +31,11 @@ public bool IsWeightless(EntityUid uid, PhysicsComponent? body = null, Transform if (TryComp(uid, out var ignoreGravityComponent)) return ignoreGravityComponent.Weightless; + var ev = new IsWeightlessEvent(uid); + RaiseLocalEvent(uid, ref ev); + if (ev.Handled) + return ev.IsWeightless; + if (!Resolve(uid, ref xform)) return true; @@ -44,18 +46,6 @@ public bool IsWeightless(EntityUid uid, PhysicsComponent? body = null, Transform return false; } - var hasGrav = gravity != null || mapGravity != null; - - // Check for something holding us down - // If the planet has gravity component and no gravity it will still give gravity - // If there's no gravity comp at all (i.e. space) then they don't work. - if (hasGrav && _inventory.TryGetSlotEntity(uid, "shoes", out var ent)) - { - // TODO this should just be a event that gets relayed instead of a specific slot & component check. - if (TryComp(ent, out var boots) && boots.On) - return false; - } - return true; } @@ -78,9 +68,11 @@ public override void Update(float frameTime) private void OnHandleState(EntityUid uid, GravityComponent component, ref ComponentHandleState args) { - if (args.Current is not GravityComponentState state) return; + if (args.Current is not GravityComponentState state) + return; - if (component.EnabledVV == state.Enabled) return; + if (component.EnabledVV == state.Enabled) + return; component.EnabledVV = state.Enabled; var ev = new GravityChangedEvent(uid, component.EnabledVV); RaiseLocalEvent(uid, ref ev, true); @@ -94,9 +86,10 @@ private void OnGetState(EntityUid uid, GravityComponent component, ref Component private void OnGravityChange(ref GravityChangedEvent ev) { var alerts = AllEntityQuery(); - while(alerts.MoveNext(out var uid, out var comp, out var xform)) + while(alerts.MoveNext(out var uid, out _, out var xform)) { - if (xform.GridUid != ev.ChangedGridIndex) continue; + if (xform.GridUid != ev.ChangedGridIndex) + continue; if (!ev.HasGravity) { @@ -149,4 +142,10 @@ public GravityComponentState(bool enabled) } } } -} \ No newline at end of file + + [ByRefEvent] + public record struct IsWeightlessEvent(EntityUid Entity, bool IsWeightless = false, bool Handled = false) : IInventoryRelayEvent + { + SlotFlags IInventoryRelayEvent.TargetSlots => ~SlotFlags.POCKET; + } +} diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index 39e10415f8e..1f1e59bff34 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -4,6 +4,7 @@ using Content.Shared.Electrocution; using Content.Shared.Explosion; using Content.Shared.Eye.Blinding.Systems; +using Content.Shared.Gravity; using Content.Shared.IdentityManagement.Components; using Content.Shared.Inventory.Events; using Content.Shared.Movement.Systems; @@ -34,6 +35,7 @@ public void InitializeRelay() // by-ref events SubscribeLocalEvent(RefRelayInventoryEvent); + SubscribeLocalEvent(RefRelayInventoryEvent); // Eye/vision events SubscribeLocalEvent(RelayInventoryEvent); diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml index 5d81a416199..4683388dc26 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml @@ -130,8 +130,10 @@ categories: [ HideSpawnMenu ] components: - type: InstantAction - itemIconStyle: BigItem - event: !type:ToggleActionEvent + itemIconStyle: NoItem + event: !type:ToggleClothingSpeedEvent + icon: { sprite: Clothing/Shoes/Boots/speedboots.rsi, state: icon } + iconOn: { sprite: Clothing/Shoes/Boots/speedboots.rsi, state: icon-on } - type: entity parent: ClothingShoesBase diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 27717049050..573717def82 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -321,6 +321,7 @@ - SynthesizerInstrument - RPED - ClothingShoesBootsMagSci + - ClothingShoesBootsMoon - ClothingShoesBootsSpeed - NodeScanner - HolofanProjector diff --git a/Resources/Prototypes/Recipes/Lathes/misc.yml b/Resources/Prototypes/Recipes/Lathes/misc.yml index 2c0e1eeec38..571e9bc97b9 100644 --- a/Resources/Prototypes/Recipes/Lathes/misc.yml +++ b/Resources/Prototypes/Recipes/Lathes/misc.yml @@ -126,6 +126,13 @@ Steel: 1000 Plastic: 500 +- type: latheRecipe + id: ClothingShoesBootsMoon + result: ClothingShoesBootsMoon + completetime: 2 + materials: + Steel: 600 + - type: latheRecipe id: ClothingShoesBootsSpeed result: ClothingShoesBootsSpeed diff --git a/Resources/Prototypes/Research/experimental.yml b/Resources/Prototypes/Research/experimental.yml index 60017fc98d1..423ec0f84da 100644 --- a/Resources/Prototypes/Research/experimental.yml +++ b/Resources/Prototypes/Research/experimental.yml @@ -69,6 +69,7 @@ cost: 7500 recipeUnlocks: - ClothingShoesBootsMagSci + - ClothingShoesBootsMoon - type: technology id: AnomalyCoreHarnessing diff --git a/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/equipped-FEET-vox.png b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/equipped-FEET-vox.png new file mode 100644 index 0000000000000000000000000000000000000000..a65deca7604c8869eb12f083f48fd87f017e4117 GIT binary patch literal 412 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%z!#rIaLn`LH zy=9np$U&g>p}WY$tQJ@2!}AopUQ9S@XrXbuLqN9EqH*B@$L^Tcv|!1bd`|aRtd)N4 z&s!6F-k^DPEGN)N2ymD?vo&%Sn=yBqSXcVf=ijI87msdG*=u9(J?Y-G)Aw|In^kIa z%qA_dIluT`rT@?KcgxO&TD_gK+Gpw6KhxFjp0rP}n#<;CCBt{?{r9PUvm+&X-K6;1 zCoQ>}Rr*=;%=6D5_Qk#aTrx=|HL~nnAkUMP_piTBS#&h%qDB4U*2f=rT)O$T?00Vb zWK;XE*MBm?9Q8roMP7a?zrtsm_g5cBi(U)m-ESqQ-?!v+;cZ`o%>}yquY6K?B`)%+ zQ2f+%CB37Da!KYXyYF7z;k|vuQ^T_6?{S`!L=r#T%Qldh^H|TWQEC47IH32c_I$ti zzFY5_W%&P#R&xDQO><2b{+oF<+hfv_qQXU;`d?yB_!|71w;1FKPgg&ebxsLQ0A0k44ofy`glX(f`Xa)F$xE?rg zfR~ro%ECfWP*7G@*38UIMMWhjC`d(FSw=>txv|m5$EP4SHzp>=U3}FEpf0|WAirRS z|HvTR>$V6`W|^mpV@O2n+iAP`4jXW|*5)d_|NsAPl2YiZlsl_}+p5`YWSS0gYqknF zaa3xpa$pPzUjNSY>%Ux)4-GznhIxM}%3-&Rr;HPtJcPTGce)?8Dix zPQH|_W--5vseA`>g@J=9EAw9)=wb#>S3j3^P6Px$4oO5oR9J=Wl)a8eKom#MZd3|Hrjn=>Dlagt-ottby;4G>(D*13jV(6Beq5@GL9p09RJ9HRV8s* z0HEtS`o2G%tGFc~gg^*E+qM7W$PEX%??&j5IyN7FP|mh~k!O>^&n>$>E5PF>gcr{GvT48#2! z*LBS?-{||Es;UTr;LP3B0#}P~*tShoRsR>5rip2qkWx|<1pxCr|C%4!#<3_0Ow&{f mDF1;gD_6x|>sX`Fyfz<2V56wR^>uOp0000I$f@PVL01S#5KR6_@~B8%R?PTznI>+OyP??wOI4i|Lq%X9=10=+$;$+2M8Ke z@9fRf{O&9H>|N>oZKqD`uD!i3sA&yD`Fc%N>yo)zpJqE>kM#14Yg*Fq^W^3SYy!(3R zd)*@$mgz4aojoG&Sjw5LUuXI*ZmM;{+IR)98$iUil$TdLtfXt&mKvWpD=v@}Q<9c& zne|NfKa06_dxgt>-TqZQe|qQ5S4--~J<)#0dDO3&e78sm}`&b`8=^x+yjj^}? zMJTV|WX534=lm(PMP@DMfwJ7{-8~Ae^3A4)*{%t>c6Be$tM?*f_W>FVdQ&MBb@0M3k$`v3p{ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/meta.json b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/meta.json new file mode 100644 index 00000000000..2c2d49e0310 --- /dev/null +++ b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Tau Ceti Station at commit https://github.com/TauCetiStation/TauCetiClassic/blob/HEAD/icons/obj/clothing/shoes.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-FEET", + "directions": 4 + }, + { + "name": "equipped-FEET-vox", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} From 02fba60755102dd12557c6900df6b209e539bb3f Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:55:43 +0200 Subject: [PATCH 026/366] Traitor activation fix for missing PDA (#30359) * Implant the uplink if no PDA is found * comments * tidy up loose ends * Whoops usually I start with the namespace, how did I forget it, shame shame * Consistent data type for starting TC balance, misc changes * Implant briefing, guidebook * Update AutoTraitor, add uplink, codeword and briefing parameters to TraitorRuleComponent, no pda for reinforcements * engine 5c0ce43 * pass pda to AddUplink Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * nicer string handling Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * case typo 1 Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * case typo 2 Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * case typo 3 Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * minor layout changes * removed redundant implant check * minor cleanup --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Rules/Components/TraitorRuleComponent.cs | 22 ++- .../GameTicking/Rules/TraitorRuleSystem.cs | 87 +++++--- .../Components/AutoTraitorComponent.cs | 13 +- .../Traitor/Systems/AutoTraitorSystem.cs | 5 +- Content.Server/Traitor/Uplink/UplinkSystem.cs | 185 +++++++++++------- .../Implants/SharedSubdermalImplantSystem.cs | 38 ++-- .../game-presets/preset-traitor.ftl | 6 +- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 6 +- .../Prototypes/Entities/Mobs/Player/human.yml | 7 +- Resources/Prototypes/GameRules/roundstart.yml | 9 + .../Guidebook/Antagonist/Traitors.xml | 52 ++--- 11 files changed, 262 insertions(+), 168 deletions(-) diff --git a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs index cec635b4caa..23de66ae35b 100644 --- a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs @@ -1,5 +1,7 @@ using Content.Server.NPC.Components; using Content.Shared.Dataset; +using Content.Shared.FixedPoint; +using Content.Shared.NPC.Prototypes; using Content.Shared.Random; using Content.Shared.Roles; using Robust.Shared.Audio; @@ -31,6 +33,24 @@ public sealed partial class TraitorRuleComponent : Component [DataField] public ProtoId ObjectiveIssuers = "TraitorCorporations"; + /// + /// Give this traitor an Uplink on spawn. + /// + [DataField] + public bool GiveUplink = true; + + /// + /// Give this traitor the codewords. + /// + [DataField] + public bool GiveCodewords = true; + + /// + /// Give this traitor a briefing in chat. + /// + [DataField] + public bool GiveBriefing = true; + public int TotalTraitors => TraitorMinds.Count; public string[] Codewords = new string[3]; @@ -68,5 +88,5 @@ public enum SelectionState /// The amount of TC traitors start with. /// [DataField] - public int StartingBalance = 20; + public FixedPoint2 StartingBalance = 20; } diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index abf46b7b967..46fee4abfe1 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -7,6 +7,8 @@ using Content.Server.Roles; using Content.Server.Traitor.Components; using Content.Server.Traitor.Uplink; +using Content.Shared.Database; +using Content.Shared.FixedPoint; using Content.Shared.GameTicking.Components; using Content.Shared.Mind; using Content.Shared.Mood; @@ -66,44 +68,45 @@ private void MakeCodewords(TraitorRuleComponent component) } } - public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true) + public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component) { //Grab the mind if it wasnt provided if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind)) return false; - var briefing = Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", component.Codewords))); - var issuer = _random.Pick(_prototypeManager.Index(component.ObjectiveIssuers).Values); + var briefing = ""; - if (TryComp(traitor, out var autoTraitorComponent)) - { - giveUplink = autoTraitorComponent.GiveUplink; - giveObjectives = autoTraitorComponent.GiveObjectives; - } + if (component.GiveCodewords) + briefing = Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", component.Codewords))); + + var issuer = _random.Pick(_prototypeManager.Index(component.ObjectiveIssuers).Values); Note[]? code = null; - if (giveUplink) + + if (component.GiveUplink) { // Calculate the amount of currency on the uplink. var startingBalance = component.StartingBalance; - if (_jobs.MindTryGetJob(mindId, out _, out var prototype)) - startingBalance = Math.Max(startingBalance - prototype.AntagAdvantage, 0); - - // creadth: we need to create uplink for the antag. - // PDA should be in place already - var pda = _uplink.FindUplinkTarget(traitor); - if (pda == null || !_uplink.AddUplink(traitor, startingBalance)) - return false; - - // Give traitors their codewords and uplink code to keep in their character info menu - code = EnsureComp(pda.Value).Code; + if (_jobs.MindTryGetJob(mindId, out var prototype)) + { + if (startingBalance < prototype.AntagAdvantage) // Can't use Math functions on FixedPoint2 + startingBalance = 0; + else + startingBalance = startingBalance - prototype.AntagAdvantage; + } - // If giveUplink is false the uplink code part is omitted - briefing = string.Format("{0}\n{1}", briefing, - Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#")))); + // Choose and generate an Uplink, and return the uplink code if applicable + var uplinkParams = RequestUplink(traitor, startingBalance, briefing); + code = uplinkParams.Item1; + briefing = uplinkParams.Item2; } - _antag.SendBriefing(traitor, GenerateBriefing(component.Codewords, code, issuer), null, component.GreetSoundNotification); + string[]? codewords = null; + if (component.GiveCodewords) + codewords = component.Codewords; + + if (component.GiveBriefing) + _antag.SendBriefing(traitor, GenerateBriefing(codewords, code, issuer), null, component.GreetSoundNotification); component.TraitorMinds.Add(mindId); @@ -122,6 +125,32 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool return true; } + private (Note[]?, string) RequestUplink(EntityUid traitor, FixedPoint2 startingBalance, string briefing) + { + var pda = _uplink.FindUplinkTarget(traitor); + Note[]? code = null; + + var uplinked = _uplink.AddUplink(traitor, startingBalance, pda, true); + + if (pda is not null && uplinked) + { + // Codes are only generated if the uplink is a PDA + code = EnsureComp(pda.Value).Code; + + // If giveUplink is false the uplink code part is omitted + briefing = string.Format("{0}\n{1}", + briefing, + Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#")))); + return (code, briefing); + } + else if (pda is null && uplinked) + { + briefing += "\n" + Loc.GetString("traitor-role-uplink-implant-short"); + } + + return (null, briefing); + } + // TODO: AntagCodewordsComponent private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args) { @@ -129,13 +158,17 @@ private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, r } // TODO: figure out how to handle this? add priority to briefing event? - private string GenerateBriefing(string[] codewords, Note[]? uplinkCode, string? objectiveIssuer = null) + private string GenerateBriefing(string[]? codewords, Note[]? uplinkCode, string? objectiveIssuer = null) { var sb = new StringBuilder(); sb.AppendLine(Loc.GetString("traitor-role-greeting", ("corporation", objectiveIssuer ?? Loc.GetString("objective-issuer-unknown")))); - sb.AppendLine(Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", codewords)))); + if (codewords != null) + sb.AppendLine(Loc.GetString("traitor-role-codewords", ("codewords", string.Join(", ", codewords)))); if (uplinkCode != null) - sb.AppendLine(Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", uplinkCode).Replace("sharp", "#")))); + sb.AppendLine(Loc.GetString("traitor-role-uplink-code", ("code", string.Join("-", uplinkCode).Replace("sharp", "#")))); + else + sb.AppendLine(Loc.GetString("traitor-role-uplink-implant")); + return sb.ToString(); } diff --git a/Content.Server/Traitor/Components/AutoTraitorComponent.cs b/Content.Server/Traitor/Components/AutoTraitorComponent.cs index 473441ccec2..a4710afd8eb 100644 --- a/Content.Server/Traitor/Components/AutoTraitorComponent.cs +++ b/Content.Server/Traitor/Components/AutoTraitorComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Traitor.Systems; +using Robust.Shared.Prototypes; namespace Content.Server.Traitor.Components; @@ -9,14 +10,8 @@ namespace Content.Server.Traitor.Components; public sealed partial class AutoTraitorComponent : Component { /// - /// Whether to give the traitor an uplink or not. + /// The traitor profile to use /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public bool GiveUplink = true; - - /// - /// Whether to give the traitor objectives or not. - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public bool GiveObjectives = true; + [DataField] + public EntProtoId Profile = "Traitor"; } diff --git a/Content.Server/Traitor/Systems/AutoTraitorSystem.cs b/Content.Server/Traitor/Systems/AutoTraitorSystem.cs index e9307effbc6..d5a4db591a7 100644 --- a/Content.Server/Traitor/Systems/AutoTraitorSystem.cs +++ b/Content.Server/Traitor/Systems/AutoTraitorSystem.cs @@ -12,9 +12,6 @@ public sealed class AutoTraitorSystem : EntitySystem { [Dependency] private readonly AntagSelectionSystem _antag = default!; - [ValidatePrototypeId] - private const string DefaultTraitorRule = "Traitor"; - public override void Initialize() { base.Initialize(); @@ -24,6 +21,6 @@ public override void Initialize() private void OnMindAdded(EntityUid uid, AutoTraitorComponent comp, MindAddedMessage args) { - _antag.ForceMakeAntag(args.Mind.Comp.Session, DefaultTraitorRule); + _antag.ForceMakeAntag(args.Mind.Comp.Session, comp.Profile); } } diff --git a/Content.Server/Traitor/Uplink/UplinkSystem.cs b/Content.Server/Traitor/Uplink/UplinkSystem.cs index 5670e28ec99..0e726f2a2b2 100644 --- a/Content.Server/Traitor/Uplink/UplinkSystem.cs +++ b/Content.Server/Traitor/Uplink/UplinkSystem.cs @@ -1,94 +1,135 @@ using Content.Server.Store.Systems; +using Content.Server.StoreDiscount.Systems; +using Content.Shared.FixedPoint; using Content.Shared.Hands.EntitySystems; +using Content.Shared.Implants; using Content.Shared.Inventory; using Content.Shared.PDA; -using Content.Server.Store.Components; -using Content.Shared.FixedPoint; using Content.Shared.Store; +using Content.Shared.Store.Components; +using Robust.Shared.Prototypes; + +namespace Content.Server.Traitor.Uplink; -namespace Content.Server.Traitor.Uplink +public sealed class UplinkSystem : EntitySystem { - public sealed class UplinkSystem : EntitySystem + [Dependency] private readonly InventorySystem _inventorySystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly StoreSystem _store = default!; + [Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!; + + [ValidatePrototypeId] + public const string TelecrystalCurrencyPrototype = "Telecrystal"; + private const string FallbackUplinkImplant = "UplinkImplant"; + private const string FallbackUplinkCatalog = "UplinkUplinkImplanter"; + + /// + /// Adds an uplink to the target + /// + /// The person who is getting the uplink + /// The amount of currency on the uplink. If null, will just use the amount specified in the preset. + /// The entity that will actually have the uplink functionality. Defaults to the PDA if null. + /// Marker that enables discounts for uplink items. + /// Whether or not the uplink was added successfully + public bool AddUplink( + EntityUid user, + FixedPoint2 balance, + EntityUid? uplinkEntity = null, + bool giveDiscounts = false) { - [Dependency] private readonly InventorySystem _inventorySystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly StoreSystem _store = default!; - - [ValidatePrototypeId] - public const string TelecrystalCurrencyPrototype = "Telecrystal"; - - /// - /// Gets the amount of TC on an "uplink" - /// Mostly just here for legacy systems based on uplink. - /// - /// - /// the amount of TC - public int GetTCBalance(StoreComponent component) - { - FixedPoint2? tcBalance = component.Balance.GetValueOrDefault(TelecrystalCurrencyPrototype); - return tcBalance?.Int() ?? 0; - } + // Try to find target item if none passed - /// - /// Adds an uplink to the target - /// - /// The person who is getting the uplink - /// The amount of currency on the uplink. If null, will just use the amount specified in the preset. - /// The id of the storepreset - /// The entity that will actually have the uplink functionality. Defaults to the PDA if null. - /// Whether or not the uplink was added successfully - public bool AddUplink(EntityUid user, FixedPoint2? balance, string uplinkPresetId = "StorePresetUplink", EntityUid? uplinkEntity = null) - { - // Try to find target item - if (uplinkEntity == null) - { - uplinkEntity = FindUplinkTarget(user); - if (uplinkEntity == null) - return false; - } + uplinkEntity ??= FindUplinkTarget(user); - var store = EnsureComp(uplinkEntity.Value); - _store.InitializeFromPreset(uplinkPresetId, uplinkEntity.Value, store); - store.AccountOwner = user; - store.Balance.Clear(); + if (uplinkEntity == null) + return ImplantUplink(user, balance, giveDiscounts); - if (balance != null) - { - store.Balance.Clear(); - _store.TryAddCurrency(new Dictionary { { TelecrystalCurrencyPrototype, balance.Value } }, uplinkEntity.Value, store); - } + EnsureComp(uplinkEntity.Value); - // TODO add BUI. Currently can't be done outside of yaml -_- + SetUplink(user, uplinkEntity.Value, balance, giveDiscounts); - return true; - } + // TODO add BUI. Currently can't be done outside of yaml -_- + // ^ What does this even mean? - /// - /// Finds the entity that can hold an uplink for a user. - /// Usually this is a pda in their pda slot, but can also be in their hands. (but not pockets or inside bag, etc.) - /// - public EntityUid? FindUplinkTarget(EntityUid user) - { - // Try to find PDA in inventory - if (_inventorySystem.TryGetContainerSlotEnumerator(user, out var containerSlotEnumerator)) - { - while (containerSlotEnumerator.MoveNext(out var pdaUid)) - { - if (!pdaUid.ContainedEntity.HasValue) continue; + return true; + } - if (HasComp(pdaUid.ContainedEntity.Value) || HasComp(pdaUid.ContainedEntity.Value)) - return pdaUid.ContainedEntity.Value; - } - } + /// + /// Configure TC for the uplink + /// + private void SetUplink(EntityUid user, EntityUid uplink, FixedPoint2 balance, bool giveDiscounts) + { + var store = EnsureComp(uplink); + store.AccountOwner = user; - // Also check hands - foreach (var item in _handsSystem.EnumerateHeld(user)) + store.Balance.Clear(); + _store.TryAddCurrency(new Dictionary { { TelecrystalCurrencyPrototype, balance } }, + uplink, + store); + + var uplinkInitializedEvent = new StoreInitializedEvent( + TargetUser: user, + Store: uplink, + UseDiscounts: giveDiscounts, + Listings: _store.GetAvailableListings(user, uplink, store) + .ToArray()); + RaiseLocalEvent(ref uplinkInitializedEvent); + } + + /// + /// Implant an uplink as a fallback measure if the traitor had no PDA + /// + private bool ImplantUplink(EntityUid user, FixedPoint2 balance, bool giveDiscounts) + { + var implantProto = new string(FallbackUplinkImplant); + + if (!_proto.TryIndex(FallbackUplinkCatalog, out var catalog)) + return false; + + if (!catalog.Cost.TryGetValue(TelecrystalCurrencyPrototype, out var cost)) + return false; + + if (balance < cost) // Can't use Math functions on FixedPoint2 + balance = 0; + else + balance = balance - cost; + + var implant = _subdermalImplant.AddImplant(user, implantProto); + + if (!HasComp(implant)) + return false; + + SetUplink(user, implant.Value, balance, giveDiscounts); + return true; + } + + /// + /// Finds the entity that can hold an uplink for a user. + /// Usually this is a pda in their pda slot, but can also be in their hands. (but not pockets or inside bag, etc.) + /// + public EntityUid? FindUplinkTarget(EntityUid user) + { + // Try to find PDA in inventory + if (_inventorySystem.TryGetContainerSlotEnumerator(user, out var containerSlotEnumerator)) + { + while (containerSlotEnumerator.MoveNext(out var pdaUid)) { - if (HasComp(item) || HasComp(item)) - return item; + if (!pdaUid.ContainedEntity.HasValue) + continue; + + if (HasComp(pdaUid.ContainedEntity.Value) || HasComp(pdaUid.ContainedEntity.Value)) + return pdaUid.ContainedEntity.Value; } + } - return null; + // Also check hands + foreach (var item in _handsSystem.EnumerateHeld(user)) + { + if (HasComp(item) || HasComp(item)) + return item; } + + return null; } } diff --git a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs index a43d4fca723..e1d65003a65 100644 --- a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs +++ b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs @@ -96,20 +96,36 @@ private void OnRemove(EntityUid uid, SubdermalImplantComponent component, EntGot /// public void AddImplants(EntityUid uid, IEnumerable implants) { - var coords = Transform(uid).Coordinates; foreach (var id in implants) { - var ent = Spawn(id, coords); - if (TryComp(ent, out var implant)) - { - ForceImplant(uid, ent, implant); - } - else - { - Log.Warning($"Found invalid starting implant '{id}' on {uid} {ToPrettyString(uid):implanted}"); - Del(ent); - } + AddImplant(uid, id); + } + } + + /// + /// Adds a single implant to a person, and returns the implant. + /// Logs any implant ids that don't have . + /// + /// + /// The implant, if it was successfully created. Otherwise, null. + /// > + public EntityUid? AddImplant(EntityUid uid, String implantId) + { + var coords = Transform(uid).Coordinates; + var ent = Spawn(implantId, coords); + + if (TryComp(ent, out var implant)) + { + ForceImplant(uid, ent, implant); + } + else + { + Log.Warning($"Found invalid starting implant '{implantId}' on {uid} {ToPrettyString(uid):implanted}"); + Del(ent); + return null; } + + return ent; } /// diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl index e92676a2160..c32718d6a8b 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl @@ -26,7 +26,7 @@ traitor-death-match-end-round-description-entry = {$originalName}'s PDA, with {$ traitor-role-greeting = You are an agent sent by {$corporation} on behalf of The Syndicate. Your objectives and codewords are listed in the character menu. - Use the uplink loaded into your PDA to buy the tools you'll need for this mission. + Use your uplink to buy the tools you'll need for this mission. Death to Nanotrasen! traitor-role-codewords = The codewords are: @@ -36,9 +36,13 @@ traitor-role-codewords = traitor-role-uplink-code = Set your ringtone to the notes {$code} to lock or unlock your uplink. Remember to lock it after, or the stations crew will easily open it too! +traitor-role-uplink-implant = + Your uplink implant has been activated, access it from your hotbar. + The uplink is secure unless someone removes it from your body. # don't need all the flavour text for character menu traitor-role-codewords-short = The codewords are: {$codewords}. traitor-role-uplink-code-short = Your uplink code is {$code}. Set it as your PDA ringtone to access uplink. +traitor-role-uplink-implant-short = Your uplink was implanted. Access it from your hotbar. diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 5aa41301d77..c7018ae0e1b 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1418,8 +1418,7 @@ components: # make the player a traitor once its taken - type: AutoTraitor - giveUplink: false - giveObjectives: false + profile: TraitorReinforcement - type: entity id: MobMonkeySyndicateAgentNukeops # Reinforcement exclusive to nukeops uplink @@ -1583,8 +1582,7 @@ components: # make the player a traitor once its taken - type: AutoTraitor - giveUplink: false - giveObjectives: false + profile: TraitorReinforcement - type: entity id: MobKoboldSyndicateAgentNukeops # Reinforcement exclusive to nukeops uplink diff --git a/Resources/Prototypes/Entities/Mobs/Player/human.yml b/Resources/Prototypes/Entities/Mobs/Player/human.yml index a7fb7de2f87..d76e698183e 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/human.yml @@ -25,10 +25,9 @@ name: syndicate agent suffix: Human, Traitor components: - # make the player a traitor once its taken - - type: AutoTraitor - giveUplink: false - giveObjectives: false + # make the player a traitor once its taken + - type: AutoTraitor + profile: TraitorReinforcement - type: entity parent: MobHumanSyndicateAgentBase diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 80354861ade..f47c2651c91 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -176,6 +176,15 @@ - type: TraitorRole prototype: Traitor +- type: entity + id: TraitorReinforcement + parent: Traitor + components: + - type: TraitorRule + giveUplink: false + giveCodewords: false # It would actually give them a different set of codewords than the regular traitors, anyway + giveBriefing: false + - type: entity id: Revolutionary parent: BaseGameRule diff --git a/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml b/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml index 45d41631c2e..e19f2ae6547 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml @@ -14,47 +14,29 @@ - - [color=#a4885c]Telecrystals[/color] are given at the start to give traitors an edge on the station. Other traitors can trade their [color=#a4885c]telecrystals[/color] to each other exceeding the normal given amount. - - Using your [color=#a4885c]PDA[/color] and setting the ringtone as your uplink code gives you a variety of options to use at your disposal against the station and its crew. - - - - Make sure to relock your [color=#a4885c]PDA[/color] to prevent anyone else from seeing it! - - Various gear include: - - - - - - - + Traitors are antagonists employed by the [color=#ff0000]Syndicate.[/color] You are a sleeper agent who has access to various tools and weapons through your [bold]uplink[/bold]. + You also receive [bold]codewords[/bold] to identify other agents, and a coordinated team of traitors can have [italic]brutal results.[/italic] - ## Objectives + Anyone besides [textlink="department heads" link="Command"] or members of [textlink="Security" link="Security"] can be a traitor. - - When becoming a Traitor, you will have a list of objectives, ranging from escape alive, stealing something, and killing someone. Using the [color=#a4885c]Uplink[/color] will help you with most of these tasks. + ## Uplink & Activation + The [color=cyan]uplink[/color] is your most important tool as a traitor. You can exchange the 20 [color=red]telecrystals[/color] (TC) you start with for items that will help you with your objectives. - ## List of Possible Tasks + By pressing [color=yellow][bold][keybind="OpenCharacterMenu"][/bold][/color], you'll see your personal uplink code. [bold]Setting your PDA's ringtone as this code will open the uplink.[/bold] + Pressing [color=yellow][bold][keybind="OpenCharacterMenu"][/bold][/color] also lets you view your objectives and the codewords. - - Kill or maroon a randomly selected non-traitor. - - Kill or maroon a randomly selected traitor. - - Kill or maroon a randomly selected department head. - - Keep a randomly selected traitor alive. - - Escape on the evacuation shuttle alive and uncuffed. - - Help a randomly selected traitor finish 2/3 of their objectives. - - Die a glorious death. - - Steal the Captain's [color=#a4885c]ID Card[/color]. - - - - - Steal the Captain's [color=#a4885c]Antique Laser Pistol[/color]. + If you do not have a PDA when you are activated, an [color=cyan]uplink implant[/color] is provided [bold]for the full [color=red]TC[/color] price of the implant.[/bold] + It can be accessed from your hotbar. - - - - Steal the Captain's [color=#a4885c]Jetpack[/color]. - - + + + - - Steal the Chief Medical Officer's [color=#a4885c]Hypospray[/color]. + + [bold]Make sure to close your PDA uplink to prevent anyone else from seeing it.[/bold] You don't want [color=#cb0000]Security[/color] to get their hands on this premium selection of contraband! + + Implanted uplinks are not normally accessible to other people, so they do not have any security measures. They can, however, be removed from you with an empty implanter. + From dec945050470d29b9bdfbadfb6e23f10c1759434 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 03:07:49 -0400 Subject: [PATCH 027/366] Shhh... --- .../GameTicking/Rules/AntagLoadProfileRuleSystem.cs | 2 +- Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs b/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs index fd3fb6cd655..c640c912a5f 100644 --- a/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs @@ -34,6 +34,6 @@ private void OnSelectEntity(Entity ent, ref Antag species = _proto.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); args.Entity = Spawn(species.Prototype); - _humanoid.LoadProfile(args.Entity.Value, profile); + _humanoid.LoadProfile(args.Entity.Value, profile!); } } diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index c3e8e90f843..3190a50701c 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -22,6 +22,10 @@ using Robust.Shared.Random; using Robust.Shared.Utility; using System.Linq; +using Content.Server.NPC.Components; +using Content.Server.NPC.Systems; +using Content.Shared.GameTicking.Components; + namespace Content.Server.GameTicking.Rules; From 9cba113b469e6151824ff4b13ce13209b4a86fc2 Mon Sep 17 00:00:00 2001 From: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:46:01 -0800 Subject: [PATCH 028/366] Clumsy system refactor (#31147) * First commit * Fixes * Added the noise * Renames * Timespan * Fixed space * entity -> ent * This shouldn't work * opps.... * Datafield name change * Better comments * small comment * Personal skill issue * Event renames and stuff * Couple fixes * Defib ref fixes (Silly me) * Added clumsy back! * no hard code clumsy! * Identity fix * Event name change * Comment change * Function name change * opp * Update names * Damage stuff! * Fixes! * Fixes * opps * This was hidden away!! * negative diff feeds me --- .../Systems/AdminVerbSystem.Smites.cs | 1 + .../Administration/Systems/SuperBonkSystem.cs | 22 +-- .../EntitySystems/HypospraySystem.cs | 43 +++++- Content.Server/Cluwne/CluwneSystem.cs | 2 + Content.Server/Medical/DefibrillatorSystem.cs | 74 +++++++-- .../Weapons/Ranged/Systems/GunSystem.cs | 30 +--- .../Components/HyposprayComponent.cs | 5 - .../Chemistry/Events/HyposprayEvents.cs | 38 +++++ .../Climbing/Components/BonkableComponent.cs | 34 +--- .../Climbing/Events/BeforeClimbEvents.cs | 36 +++++ Content.Shared/Climbing/Systems/BonkSystem.cs | 131 ---------------- .../Climbing/Systems/ClimbSystem.cs | 12 ++ Content.Shared/Clumsy/ClumsyComponent.cs | 61 ++++++++ Content.Shared/Clumsy/ClumsySystem.cs | 146 ++++++++++++++++++ .../Interaction/Components/ClumsyComponent.cs | 24 --- .../SharedInteractionSystem.Clumsy.cs | 26 ---- .../Inventory/InventorySystem.Relay.cs | 12 +- Content.Shared/Medical/DefibrillatorEvents.cs | 39 +++++ .../Ranged/Events/BeforeGunShootEvent.cs | 20 +++ .../bonk/components/bonkable-component.ftl | 6 +- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 4 +- .../Entities/Mobs/Player/guardian.yml | 2 +- .../Furniture/Tables/base_structuretables.yml | 2 - .../Prototypes/Roles/Jobs/Civilian/clown.yml | 2 +- 24 files changed, 490 insertions(+), 282 deletions(-) create mode 100644 Content.Shared/Chemistry/Events/HyposprayEvents.cs create mode 100644 Content.Shared/Climbing/Events/BeforeClimbEvents.cs delete mode 100644 Content.Shared/Climbing/Systems/BonkSystem.cs create mode 100644 Content.Shared/Clumsy/ClumsyComponent.cs create mode 100644 Content.Shared/Clumsy/ClumsySystem.cs delete mode 100644 Content.Shared/Interaction/Components/ClumsyComponent.cs delete mode 100644 Content.Shared/Interaction/SharedInteractionSystem.Clumsy.cs create mode 100644 Content.Shared/Medical/DefibrillatorEvents.cs create mode 100644 Content.Shared/Weapons/Ranged/Events/BeforeGunShootEvent.cs diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index bda60e9449a..183607db1a1 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -22,6 +22,7 @@ using Content.Shared.Administration.Components; using Content.Shared.Body.Components; using Content.Shared.Body.Part; +using Content.Shared.Clumsy; using Content.Shared.Clothing.Components; using Content.Shared.Cluwne; using Content.Shared.Damage; diff --git a/Content.Server/Administration/Systems/SuperBonkSystem.cs b/Content.Server/Administration/Systems/SuperBonkSystem.cs index 5488a8d6f46..5cd62d83572 100644 --- a/Content.Server/Administration/Systems/SuperBonkSystem.cs +++ b/Content.Server/Administration/Systems/SuperBonkSystem.cs @@ -1,27 +1,27 @@ using Content.Server.Administration.Components; using Content.Shared.Climbing.Components; -using Content.Shared.Climbing.Events; -using Content.Shared.Climbing.Systems; -using Content.Shared.Interaction.Components; +using Content.Shared.Clumsy; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; +using Robust.Shared.Audio.Systems; namespace Content.Server.Administration.Systems; -public sealed class SuperBonkSystem: EntitySystem +public sealed class SuperBonkSystem : EntitySystem { [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - [Dependency] private readonly BonkSystem _bonkSystem = default!; + [Dependency] private readonly ClumsySystem _clumsySystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnBonkShutdown); SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnBonkShutdown); } - public void StartSuperBonk(EntityUid target, float delay = 0.1f, bool stopWhenDead = false ) + public void StartSuperBonk(EntityUid target, float delay = 0.1f, bool stopWhenDead = false) { //The other check in the code to stop when the target dies does not work if the target is already dead. @@ -31,7 +31,6 @@ public void StartSuperBonk(EntityUid target, float delay = 0.1f, bool stopWhenDe return; } - var hadClumsy = EnsureComp(target, out _); var tables = EntityQueryEnumerator(); @@ -79,16 +78,17 @@ public override void Update(float frameTime) private void Bonk(SuperBonkComponent comp) { var uid = comp.Tables.Current.Key; - var bonkComp = comp.Tables.Current.Value; // It would be very weird for something without a transform component to have a bonk component // but just in case because I don't want to crash the server. - if (!HasComp(uid)) + if (!HasComp(uid) || !TryComp(comp.Target, out var clumsyComp)) return; _transformSystem.SetCoordinates(comp.Target, Transform(uid).Coordinates); - _bonkSystem.TryBonk(comp.Target, uid, bonkComp); + _clumsySystem.HitHeadClumsy((comp.Target, clumsyComp), uid); + + _audioSystem.PlayPvs(clumsyComp.TableBonkSound, comp.Target); } private void OnMobStateChanged(EntityUid uid, SuperBonkComponent comp, MobStateChangedEvent args) diff --git a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs index 56cc0f96709..7f0b797468c 100644 --- a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs +++ b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs @@ -1,7 +1,8 @@ using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; -using Content.Shared.Chemistry.Reagent; +using Content.Shared.Chemistry.Hypospray.Events; +using Content.Shared.Chemistry; using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Forensics; @@ -88,14 +89,44 @@ public bool TryDoInject(Entity entity, EntityUid target, Ent string? msgFormat = null; - if (target == user) - msgFormat = "hypospray-component-inject-self-message"; - else if (EligibleEntity(user, EntityManager, component) && _interaction.TryRollClumsy(user, component.ClumsyFailChance)) + // Self event + var selfEvent = new SelfBeforeHyposprayInjectsEvent(user, entity.Owner, target); + RaiseLocalEvent(user, selfEvent); + + if (selfEvent.Cancelled) + { + _popup.PopupEntity(Loc.GetString(selfEvent.InjectMessageOverride ?? "hypospray-cant-inject", ("owner", Identity.Entity(target, EntityManager))), target, user); + return false; + } + + target = selfEvent.TargetGettingInjected; + + if (!EligibleEntity(target, EntityManager, component)) + return false; + + // Target event + var targetEvent = new TargetBeforeHyposprayInjectsEvent(user, entity.Owner, target); + RaiseLocalEvent(target, targetEvent); + + if (targetEvent.Cancelled) { - msgFormat = "hypospray-component-inject-self-clumsy-message"; - target = user; + _popup.PopupEntity(Loc.GetString(targetEvent.InjectMessageOverride ?? "hypospray-cant-inject", ("owner", Identity.Entity(target, EntityManager))), target, user); + return false; } + target = targetEvent.TargetGettingInjected; + + if (!EligibleEntity(target, EntityManager, component)) + return false; + + // The target event gets priority for the overriden message. + if (targetEvent.InjectMessageOverride != null) + msgFormat = targetEvent.InjectMessageOverride; + else if (selfEvent.InjectMessageOverride != null) + msgFormat = selfEvent.InjectMessageOverride; + else if (target == user) + msgFormat = "hypospray-component-inject-self-message"; + if (!_solutionContainers.TryGetSolution(uid, component.SolutionName, out var hypoSpraySoln, out var hypoSpraySolution) || hypoSpraySolution.Volume == 0) { _popup.PopupEntity(Loc.GetString("hypospray-component-empty-message"), target, user); diff --git a/Content.Server/Cluwne/CluwneSystem.cs b/Content.Server/Cluwne/CluwneSystem.cs index bd7b7a66201..0e8669a0314 100644 --- a/Content.Server/Cluwne/CluwneSystem.cs +++ b/Content.Server/Cluwne/CluwneSystem.cs @@ -17,6 +17,8 @@ using Content.Shared.Interaction.Components; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Content.Shared.NameModifier.EntitySystems; +using Content.Shared.Clumsy; namespace Content.Server.Cluwne; diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index d0c650c4e84..45e31cef5bb 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -78,7 +78,20 @@ private void OnDoAfter(EntityUid uid, DefibrillatorComponent component, Defibril Zap(uid, target, args.User, component); } - public bool CanZap(EntityUid uid, EntityUid target, EntityUid? user = null, DefibrillatorComponent? component = null) + /// + /// Checks if you can actually defib a target. + /// + /// Uid of the defib + /// Uid of the target getting defibbed + /// Uid of the entity using the defibrillator + /// Defib component + /// + /// If true, the target can be alive. If false, the function will check if the target is alive and will return false if they are. + /// + /// + /// Returns true if the target is valid to be defibed, false otherwise. + /// + public bool CanZap(EntityUid uid, EntityUid target, EntityUid? user = null, DefibrillatorComponent? component = null, bool targetCanBeAlive = false) { if (!Resolve(uid, ref component)) return false; @@ -99,12 +112,25 @@ public bool CanZap(EntityUid uid, EntityUid target, EntityUid? user = null, Defi if (!_powerCell.HasActivatableCharge(uid, user: user)) return false; - if (_mobState.IsAlive(target, mobState)) + if (!targetCanBeAlive && _mobState.IsAlive(target, mobState)) + return false; + + if (!targetCanBeAlive && !component.CanDefibCrit && _mobState.IsCritical(target, mobState)) return false; return true; } + /// + /// Tries to start defibrillating the target. If the target is valid, will start the defib do-after. + /// + /// Uid of the defib + /// Uid of the target getting defibbed + /// Uid of the entity using the defibrillator + /// Defib component + /// + /// Returns true if the defibrillation do-after started, otherwise false. + /// public bool TryStartZap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorComponent? component = null) { if (!Resolve(uid, ref component)) @@ -116,28 +142,42 @@ public bool TryStartZap(EntityUid uid, EntityUid target, EntityUid user, Defibri _audio.PlayPvs(component.ChargeSound, uid); return _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.DoAfterDuration, new DefibrillatorZapDoAfterEvent(), uid, target, uid) - { - BlockDuplicate = true, - BreakOnUserMove = true, - BreakOnTargetMove = true, - BreakOnHandChange = true, - NeedHand = true - }); + { + NeedHand = true, + BreakOnMove = !component.AllowDoAfterMovement + }); } - public void Zap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorComponent? component = null, MobStateComponent? mob = null, MobThresholdsComponent? thresholds = null) + /// + /// Tries to defibrillate the target with the given defibrillator. + /// + public void Zap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorComponent? component = null) { - if (!Resolve(uid, ref component) || !Resolve(target, ref mob, ref thresholds, false)) + if (!Resolve(uid, ref component)) return; - // clowns zap themselves - if (HasComp(user) && user != target) - { - Zap(uid, user, user, component); + if (!_powerCell.TryUseActivatableCharge(uid, user: user)) return; - } - if (!_powerCell.TryUseActivatableCharge(uid, user: user)) + var selfEvent = new SelfBeforeDefibrillatorZapsEvent(user, uid, target); + RaiseLocalEvent(user, selfEvent); + + target = selfEvent.DefibTarget; + + // Ensure thet new target is still valid. + if (selfEvent.Cancelled || !CanZap(uid, target, user, component, true)) + return; + + var targetEvent = new TargetBeforeDefibrillatorZapsEvent(user, uid, target); + RaiseLocalEvent(target, targetEvent); + + target = targetEvent.DefibTarget; + + if (targetEvent.Cancelled || !CanZap(uid, target, user, component, true)) + return; + + if (!TryComp(target, out var mob) || + !TryComp(target, out var thresholds)) return; _audio.PlayPvs(component.ZapSound, uid); diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index cbbfc289cf5..e472d146de6 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -1,16 +1,13 @@ using System.Linq; using System.Numerics; using Content.Server.Cargo.Systems; -using Content.Server.Interaction; using Content.Server.Power.EntitySystems; -using Content.Server.Stunnable; using Content.Server.Weapons.Ranged.Components; using Content.Shared.Contests; using Content.Shared.Damage; using Content.Shared.Damage.Systems; using Content.Shared.Database; using Content.Shared.Effects; -using Content.Shared.Interaction.Components; using Content.Shared.Projectiles; using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Ranged; @@ -32,16 +29,13 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly DamageExamineSystem _damageExamine = default!; - [Dependency] private readonly InteractionSystem _interaction = default!; [Dependency] private readonly PricingSystem _pricing = default!; [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly StaminaSystem _stamina = default!; - [Dependency] private readonly StunSystem _stun = default!; [Dependency] private readonly ContestsSystem _contests = default!; private const float DamagePitchVariation = 0.05f; - public const float GunClumsyChance = 0.5f; public override void Initialize() { @@ -70,26 +64,14 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? { userImpulse = true; - // Try a clumsy roll - // TODO: Who put this here - if (TryComp(user, out var clumsy) && gun.ClumsyProof == false) + if (user != null) { - for (var i = 0; i < ammo.Count; i++) + var selfEvent = new SelfBeforeGunShotEvent(user.Value, (gunUid, gun), ammo); + RaiseLocalEvent(user.Value, selfEvent); + if (selfEvent.Cancelled) { - if (_interaction.TryRollClumsy(user.Value, GunClumsyChance, clumsy)) - { - // Wound them - Damageable.TryChangeDamage(user, clumsy.ClumsyDamage, origin: user); - _stun.TryParalyze(user.Value, TimeSpan.FromSeconds(3f), true); - - // Apply salt to the wound ("Honk!") - Audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg"), gunUid); - Audio.PlayPvs(clumsy.ClumsySound, gunUid); - - PopupSystem.PopupEntity(Loc.GetString("gun-clumsy"), user.Value); - userImpulse = false; - return; - } + userImpulse = false; + return; } } diff --git a/Content.Shared/Chemistry/Components/HyposprayComponent.cs b/Content.Shared/Chemistry/Components/HyposprayComponent.cs index 05ef84bbaf7..17d52f0ad93 100644 --- a/Content.Shared/Chemistry/Components/HyposprayComponent.cs +++ b/Content.Shared/Chemistry/Components/HyposprayComponent.cs @@ -11,11 +11,6 @@ public sealed partial class HyposprayComponent : Component [DataField] public string SolutionName = "hypospray"; - // TODO: This should be on clumsycomponent. - [DataField] - [ViewVariables(VVAccess.ReadWrite)] - public float ClumsyFailChance = 0.5f; - [DataField] [ViewVariables(VVAccess.ReadWrite)] public FixedPoint2 TransferAmount = FixedPoint2.New(5); diff --git a/Content.Shared/Chemistry/Events/HyposprayEvents.cs b/Content.Shared/Chemistry/Events/HyposprayEvents.cs new file mode 100644 index 00000000000..e8ed081a577 --- /dev/null +++ b/Content.Shared/Chemistry/Events/HyposprayEvents.cs @@ -0,0 +1,38 @@ +using Content.Shared.Inventory; + +namespace Content.Shared.Chemistry.Hypospray.Events; + +public abstract partial class BeforeHyposprayInjectsTargetEvent : CancellableEntityEventArgs, IInventoryRelayEvent +{ + public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET; + public EntityUid EntityUsingHypospray; + public readonly EntityUid Hypospray; + public EntityUid TargetGettingInjected; + public string? InjectMessageOverride; + + public BeforeHyposprayInjectsTargetEvent(EntityUid user, EntityUid hypospray, EntityUid target) + { + EntityUsingHypospray = user; + Hypospray = hypospray; + TargetGettingInjected = target; + InjectMessageOverride = null; + } +} + +/// +/// This event is raised on the user using the hypospray before the hypospray is injected. +/// The event is triggered on the user and all their clothing. +/// +public sealed class SelfBeforeHyposprayInjectsEvent : BeforeHyposprayInjectsTargetEvent +{ + public SelfBeforeHyposprayInjectsEvent(EntityUid user, EntityUid hypospray, EntityUid target) : base(user, hypospray, target) { } +} + +/// +/// This event is raised on the target before the hypospray is injected. +/// The event is triggered on the target itself and all its clothing. +/// +public sealed class TargetBeforeHyposprayInjectsEvent : BeforeHyposprayInjectsTargetEvent +{ + public TargetBeforeHyposprayInjectsEvent (EntityUid user, EntityUid hypospray, EntityUid target) : base(user, hypospray, target) { } +} diff --git a/Content.Shared/Climbing/Components/BonkableComponent.cs b/Content.Shared/Climbing/Components/BonkableComponent.cs index 5e97396fbad..cb4839cae71 100644 --- a/Content.Shared/Climbing/Components/BonkableComponent.cs +++ b/Content.Shared/Climbing/Components/BonkableComponent.cs @@ -1,5 +1,4 @@ using Content.Shared.Damage; -using Robust.Shared.Audio; using Robust.Shared.GameStates; namespace Content.Shared.Climbing.Components; @@ -8,39 +7,18 @@ namespace Content.Shared.Climbing.Components; /// Makes entity do damage and stun entities with ClumsyComponent /// upon DragDrop or Climb interactions. /// -[RegisterComponent, NetworkedComponent, Access(typeof(Systems.BonkSystem))] +[RegisterComponent, NetworkedComponent] public sealed partial class BonkableComponent : Component { /// - /// Chance of bonk triggering if the user is clumsy. + /// How long to stun players on bonk, in seconds. /// - [DataField("bonkClumsyChance")] - public float BonkClumsyChance = 0.5f; + [DataField] + public TimeSpan BonkTime = TimeSpan.FromSeconds(2); /// - /// Sound to play when bonking. + /// How much damage to apply on bonk. /// - /// - [DataField("bonkSound")] - public SoundSpecifier? BonkSound; - - /// - /// How long to stun players on bonk, in seconds. - /// - /// - [DataField("bonkTime")] - public float BonkTime = 2; - - /// - /// How much damage to apply on bonk. - /// - /// - [DataField("bonkDamage")] + [DataField] public DamageSpecifier? BonkDamage; - - /// - /// How long it takes to bonk. - /// - [DataField("bonkDelay")] - public float BonkDelay = 1.5f; } diff --git a/Content.Shared/Climbing/Events/BeforeClimbEvents.cs b/Content.Shared/Climbing/Events/BeforeClimbEvents.cs new file mode 100644 index 00000000000..85c40f9427c --- /dev/null +++ b/Content.Shared/Climbing/Events/BeforeClimbEvents.cs @@ -0,0 +1,36 @@ +using Content.Shared.Inventory; +using Content.Shared.Climbing.Components; + +namespace Content.Shared.Climbing.Events; + +public abstract partial class BeforeClimbEvent : CancellableEntityEventArgs +{ + public readonly EntityUid GettingPutOnTable; + public readonly EntityUid PuttingOnTable; + public readonly Entity BeingClimbedOn; + + public BeforeClimbEvent(EntityUid gettingPutOntable, EntityUid puttingOnTable, Entity beingClimbedOn) + { + GettingPutOnTable = gettingPutOntable; + PuttingOnTable = puttingOnTable; + BeingClimbedOn = beingClimbedOn; + } +} + +/// +/// This event is raised on the the person either getting put on or going on the table. +/// The event is also called on their clothing as well. +/// +public sealed class SelfBeforeClimbEvent : BeforeClimbEvent, IInventoryRelayEvent +{ + public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET; + public SelfBeforeClimbEvent(EntityUid gettingPutOntable, EntityUid puttingOnTable, Entity beingClimbedOn) : base(gettingPutOntable, puttingOnTable, beingClimbedOn) { } +} + +/// +/// This event is raised on the thing being climbed on. +/// +public sealed class TargetBeforeClimbEvent : BeforeClimbEvent +{ + public TargetBeforeClimbEvent(EntityUid gettingPutOntable, EntityUid puttingOnTable, Entity beingClimbedOn) : base(gettingPutOntable, puttingOnTable, beingClimbedOn) { } +} diff --git a/Content.Shared/Climbing/Systems/BonkSystem.cs b/Content.Shared/Climbing/Systems/BonkSystem.cs deleted file mode 100644 index c7c89a3b7fa..00000000000 --- a/Content.Shared/Climbing/Systems/BonkSystem.cs +++ /dev/null @@ -1,131 +0,0 @@ -using Content.Shared.CCVar; -using Content.Shared.Climbing.Components; -using Content.Shared.Climbing.Events; -using Content.Shared.Damage; -using Content.Shared.DoAfter; -using Content.Shared.DragDrop; -using Content.Shared.Hands.Components; -using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Components; -using Content.Shared.Popups; -using Content.Shared.Stunnable; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Configuration; -using Robust.Shared.Player; -using Robust.Shared.Serialization; - -namespace Content.Shared.Climbing.Systems; - -public sealed partial class BonkSystem : EntitySystem -{ - [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; - [Dependency] private readonly SharedStunSystem _stunSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnBonkDoAfter); - SubscribeLocalEvent(OnAttemptClimb); - } - - private void OnBonkDoAfter(EntityUid uid, BonkableComponent component, BonkDoAfterEvent args) - { - if (args.Handled || args.Cancelled || args.Args.Used == null) - return; - - TryBonk(args.Args.Used.Value, uid, component, source: args.Args.User); - - args.Handled = true; - } - - - public bool TryBonk(EntityUid user, EntityUid bonkableUid, BonkableComponent? bonkableComponent = null, EntityUid? source = null) - { - if (!Resolve(bonkableUid, ref bonkableComponent, false)) - return false; - - // BONK! - var userName = Identity.Entity(user, EntityManager); - var bonkableName = Identity.Entity(bonkableUid, EntityManager); - - if (user == source) - { - // Non-local, non-bonking players - var othersMessage = Loc.GetString("bonkable-success-message-others", ("user", userName), ("bonkable", bonkableName)); - // Local, bonking player - var selfMessage = Loc.GetString("bonkable-success-message-user", ("user", userName), ("bonkable", bonkableName)); - - _popupSystem.PopupPredicted(selfMessage, othersMessage, user, user); - } - else if (source != null) - { - // Local, non-bonking player (dragger) - _popupSystem.PopupClient(Loc.GetString("bonkable-success-message-others", ("user", userName), ("bonkable", bonkableName)), user, source.Value); - // Non-local, non-bonking players - _popupSystem.PopupEntity(Loc.GetString("bonkable-success-message-others", ("user", userName), ("bonkable", bonkableName)), user, Filter.Pvs(user).RemoveWhereAttachedEntity(e => e == user || e == source.Value), true); - // Non-local, bonking player - _popupSystem.PopupEntity(Loc.GetString("bonkable-success-message-user", ("user", userName), ("bonkable", bonkableName)), user, user); - } - - - - if (source != null) - _audioSystem.PlayPredicted(bonkableComponent.BonkSound, bonkableUid, source); - else - _audioSystem.PlayPvs(bonkableComponent.BonkSound, bonkableUid); - - _stunSystem.TryParalyze(user, TimeSpan.FromSeconds(bonkableComponent.BonkTime), true); - - if (bonkableComponent.BonkDamage is { } bonkDmg) - _damageableSystem.TryChangeDamage(user, bonkDmg, true, origin: user); - - return true; - - } - - private bool TryStartBonk(EntityUid uid, EntityUid user, EntityUid climber, BonkableComponent? bonkableComponent = null) - { - if (!Resolve(uid, ref bonkableComponent, false)) - return false; - - if (!HasComp(climber) || !HasComp(user)) - return false; - - if (!_cfg.GetCVar(CCVars.GameTableBonk)) - { - // Not set to always bonk, try clumsy roll. - if (!_interactionSystem.TryRollClumsy(climber, bonkableComponent.BonkClumsyChance)) - return false; - } - - var doAfterArgs = new DoAfterArgs(EntityManager, user, bonkableComponent.BonkDelay, new BonkDoAfterEvent(), uid, target: uid, used: climber) - { - BreakOnTargetMove = true, - BreakOnUserMove = true, - BreakOnDamage = true, - DuplicateCondition = DuplicateConditions.SameTool | DuplicateConditions.SameTarget - }; - - return _doAfter.TryStartDoAfter(doAfterArgs); - } - - private void OnAttemptClimb(EntityUid uid, BonkableComponent component, ref AttemptClimbEvent args) - { - if (args.Cancelled) - return; - - if (TryStartBonk(uid, args.User, args.Climber, component)) - args.Cancelled = true; - } - - [Serializable, NetSerializable] - private sealed partial class BonkDoAfterEvent : SimpleDoAfterEvent - { - } -} diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index d2b5a25aee7..36dc7c059e4 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -254,6 +254,18 @@ private void Climb(EntityUid uid, EntityUid user, EntityUid climbable, bool sile if (!Resolve(climbable, ref comp, false)) return; + var selfEvent = new SelfBeforeClimbEvent(uid, user, (climbable, comp)); + RaiseLocalEvent(uid, selfEvent); + + if (selfEvent.Cancelled) + return; + + var targetEvent = new TargetBeforeClimbEvent(uid, user, (climbable, comp)); + RaiseLocalEvent(climbable, targetEvent); + + if (targetEvent.Cancelled) + return; + if (!ReplaceFixtures(uid, climbing, fixtures)) return; diff --git a/Content.Shared/Clumsy/ClumsyComponent.cs b/Content.Shared/Clumsy/ClumsyComponent.cs new file mode 100644 index 00000000000..c71f5d0008a --- /dev/null +++ b/Content.Shared/Clumsy/ClumsyComponent.cs @@ -0,0 +1,61 @@ +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Clumsy; + +/// +/// A simple clumsy tag-component. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ClumsyComponent : Component +{ + + // Standard options. Try to fit these in if you can! + + /// + /// Sound to play when clumsy interactions fail. + /// + [DataField] + public SoundSpecifier ClumsySound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg"); + + /// + /// Default chance to fail a clumsy interaction. + /// If a system needs to use something else, add a new variable in the component, do not modify this percentage. + /// + [DataField, AutoNetworkedField] + public float ClumsyDefaultCheck = 0.5f; + + /// + /// Default stun time. + /// If a system needs to use something else, add a new variable in the component, do not modify this number. + /// + [DataField, AutoNetworkedField] + public TimeSpan ClumsyDefaultStunTime = TimeSpan.FromSeconds(2.5); + + // Specific options + + /// + /// Sound to play after hitting your head on a table. Ouch! + /// + [DataField] + public SoundCollectionSpecifier TableBonkSound = new SoundCollectionSpecifier("TrayHit"); + + /// + /// Stun time after failing to shoot a gun. + /// + [DataField, AutoNetworkedField] + public TimeSpan GunShootFailStunTime = TimeSpan.FromSeconds(3); + + /// + /// Stun time after failing to shoot a gun. + /// + [DataField, AutoNetworkedField] + public DamageSpecifier? GunShootFailDamage; + + /// + /// Noise to play after failing to shoot a gun. Boom! + /// + [DataField] + public SoundSpecifier GunShootFailSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg"); +} diff --git a/Content.Shared/Clumsy/ClumsySystem.cs b/Content.Shared/Clumsy/ClumsySystem.cs new file mode 100644 index 00000000000..e034458197f --- /dev/null +++ b/Content.Shared/Clumsy/ClumsySystem.cs @@ -0,0 +1,146 @@ +using Content.Shared.CCVar; +using Content.Shared.Chemistry.Hypospray.Events; +using Content.Shared.Climbing.Components; +using Content.Shared.Climbing.Events; +using Content.Shared.Damage; +using Content.Shared.IdentityManagement; +using Content.Shared.Medical; +using Content.Shared.Popups; +using Content.Shared.Stunnable; +using Content.Shared.Weapons.Ranged.Events; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Shared.Clumsy; + +public sealed class ClumsySystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + + public override void Initialize() + { + SubscribeLocalEvent(BeforeHyposprayEvent); + SubscribeLocalEvent(BeforeDefibrillatorZapsEvent); + SubscribeLocalEvent(BeforeGunShotEvent); + SubscribeLocalEvent(OnBeforeClimbEvent); + } + + // If you add more clumsy interactions add them in this section! + #region Clumsy interaction events + private void BeforeHyposprayEvent(Entity ent, ref SelfBeforeHyposprayInjectsEvent args) + { + // Clumsy people sometimes inject themselves! Apparently syringes are clumsy proof... + if (!_random.Prob(ent.Comp.ClumsyDefaultCheck)) + return; + + args.TargetGettingInjected = args.EntityUsingHypospray; + args.InjectMessageOverride = "hypospray-component-inject-self-clumsy-message"; + _audio.PlayPvs(ent.Comp.ClumsySound, ent); + } + + private void BeforeDefibrillatorZapsEvent(Entity ent, ref SelfBeforeDefibrillatorZapsEvent args) + { + // Clumsy people sometimes defib themselves! + if (!_random.Prob(ent.Comp.ClumsyDefaultCheck)) + return; + + args.DefibTarget = args.EntityUsingDefib; + _audio.PlayPvs(ent.Comp.ClumsySound, ent); + + } + + private void BeforeGunShotEvent(Entity ent, ref SelfBeforeGunShotEvent args) + { + // Clumsy people sometimes can't shoot :( + + if (args.Gun.Comp.ClumsyProof) + return; + + if (!_random.Prob(ent.Comp.ClumsyDefaultCheck)) + return; + + if (ent.Comp.GunShootFailDamage != null) + _damageable.TryChangeDamage(ent, ent.Comp.GunShootFailDamage, origin: ent); + + _stun.TryParalyze(ent, ent.Comp.GunShootFailStunTime, true); + + // Apply salt to the wound ("Honk!") (No idea what this comment means) + _audio.PlayPvs(ent.Comp.GunShootFailSound, ent); + _audio.PlayPvs(ent.Comp.ClumsySound, ent); + + _popup.PopupEntity(Loc.GetString("gun-clumsy"), ent, ent); + args.Cancel(); + } + + private void OnBeforeClimbEvent(Entity ent, ref SelfBeforeClimbEvent args) + { + // This event is called in shared, thats why it has all the extra prediction stuff. + var rand = new System.Random((int)_timing.CurTick.Value); + + // If someone is putting you on the table, always get past the guard. + if (!_cfg.GetCVar(CCVars.GameTableBonk) && args.PuttingOnTable == ent.Owner && !rand.Prob(ent.Comp.ClumsyDefaultCheck)) + return; + + HitHeadClumsy(ent, args.BeingClimbedOn); + + _audio.PlayPredicted(ent.Comp.ClumsySound, ent, ent); + + _audio.PlayPredicted(ent.Comp.TableBonkSound, ent, ent); + + var gettingPutOnTableName = Identity.Entity(args.GettingPutOnTable, EntityManager); + var puttingOnTableName = Identity.Entity(args.PuttingOnTable, EntityManager); + + if (args.PuttingOnTable == ent.Owner) + { + // You are slamming yourself onto the table. + _popup.PopupPredicted( + Loc.GetString("bonkable-success-message-user", ("bonkable", args.BeingClimbedOn)), + Loc.GetString("bonkable-success-message-others", ("victim", gettingPutOnTableName), ("bonkable", args.BeingClimbedOn)), + ent, + ent); + } + else + { + // Someone else slamed you onto the table. + // This is only run in server so you need to use popup entity. + _popup.PopupPredicted( + Loc.GetString("forced-bonkable-success-message", + ("bonker", puttingOnTableName), + ("victim", gettingPutOnTableName), + ("bonkable", args.BeingClimbedOn)), + ent, + null); + } + + args.Cancel(); + } + #endregion + + #region Helper functions + /// + /// "Hits" an entites head against the given table. + /// + // Oh this fucntion is public le- NO!! This is only public for the one admin command if you use this anywhere else I will cry. + public void HitHeadClumsy(Entity target, EntityUid table) + { + var stunTime = target.Comp.ClumsyDefaultStunTime; + + if (TryComp(table, out var bonkComp)) + { + stunTime = bonkComp.BonkTime; + if (bonkComp.BonkDamage != null) + _damageable.TryChangeDamage(target, bonkComp.BonkDamage, true); + } + + _stun.TryParalyze(target, stunTime, true); + } + #endregion +} diff --git a/Content.Shared/Interaction/Components/ClumsyComponent.cs b/Content.Shared/Interaction/Components/ClumsyComponent.cs deleted file mode 100644 index 824696c8385..00000000000 --- a/Content.Shared/Interaction/Components/ClumsyComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Content.Shared.Damage; -using Robust.Shared.Audio; -using Robust.Shared.GameStates; - -namespace Content.Shared.Interaction.Components; - -/// -/// A simple clumsy tag-component. -/// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -public sealed partial class ClumsyComponent : Component -{ - /// - /// Damage dealt to a clumsy character when they try to fire a gun. - /// - [DataField(required: true), AutoNetworkedField] - public DamageSpecifier ClumsyDamage = default!; - - /// - /// Sound to play when clumsy interactions fail. - /// - [DataField] - public SoundSpecifier ClumsySound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg"); -} diff --git a/Content.Shared/Interaction/SharedInteractionSystem.Clumsy.cs b/Content.Shared/Interaction/SharedInteractionSystem.Clumsy.cs deleted file mode 100644 index 9e45847e078..00000000000 --- a/Content.Shared/Interaction/SharedInteractionSystem.Clumsy.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Content.Shared.Interaction.Components; -using Robust.Shared.Random; - -namespace Content.Shared.Interaction -{ - public partial class SharedInteractionSystem - { - public bool RollClumsy(ClumsyComponent component, float chance) - { - return component.Running && _random.Prob(chance); - } - - /// - /// Rolls a probability chance for a "bad action" if the target entity is clumsy. - /// - /// The entity that the clumsy check is happening for. - /// - /// The chance that a "bad action" happens if the user is clumsy, between 0 and 1 inclusive. - /// - /// True if a "bad action" happened, false if the normal action should happen. - public bool TryRollClumsy(EntityUid entity, float chance, ClumsyComponent? component = null) - { - return Resolve(entity, ref component, false) && RollClumsy(component, chance); - } - } -} diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index 1f1e59bff34..6cd05437424 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -1,4 +1,7 @@ +using Content.Shared.Chat; using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Hypospray.Events; +using Content.Shared.Climbing.Events; using Content.Shared.Damage; using Content.Shared.Damage.Events; using Content.Shared.Electrocution; @@ -14,7 +17,7 @@ using Content.Shared.Strip.Components; using Content.Shared.Temperature; using Content.Shared.Verbs; -using Content.Shared.Chat; +using Content.Shared.Weapons.Ranged.Events; namespace Content.Shared.Inventory; @@ -31,7 +34,12 @@ public void InitializeRelay() SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); - SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); // by-ref events SubscribeLocalEvent(RefRelayInventoryEvent); diff --git a/Content.Shared/Medical/DefibrillatorEvents.cs b/Content.Shared/Medical/DefibrillatorEvents.cs new file mode 100644 index 00000000000..54a21a40d42 --- /dev/null +++ b/Content.Shared/Medical/DefibrillatorEvents.cs @@ -0,0 +1,39 @@ +using Content.Shared.Inventory; + +namespace Content.Shared.Medical; + +[ByRefEvent] +public readonly record struct TargetDefibrillatedEvent(EntityUid User, Entity Defibrillator); + +public abstract class BeforeDefibrillatorZapsEvent : CancellableEntityEventArgs, IInventoryRelayEvent +{ + public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET; + public EntityUid EntityUsingDefib; + public readonly EntityUid Defib; + public EntityUid DefibTarget; + + public BeforeDefibrillatorZapsEvent(EntityUid entityUsingDefib, EntityUid defib, EntityUid defibTarget) + { + EntityUsingDefib = entityUsingDefib; + Defib = defib; + DefibTarget = defibTarget; + } +} + +/// +/// This event is raised on the user using the defibrillator before is actually zaps someone. +/// The event is triggered on the user and all their clothing. +/// +public sealed class SelfBeforeDefibrillatorZapsEvent : BeforeDefibrillatorZapsEvent +{ + public SelfBeforeDefibrillatorZapsEvent(EntityUid entityUsingDefib, EntityUid defib, EntityUid defibtarget) : base(entityUsingDefib, defib, defibtarget) { } +} + +/// +/// This event is raised on the target before it gets zapped with the defibrillator. +/// The event is triggered on the target itself and all its clothing. +/// +public sealed class TargetBeforeDefibrillatorZapsEvent : BeforeDefibrillatorZapsEvent +{ + public TargetBeforeDefibrillatorZapsEvent(EntityUid entityUsingDefib, EntityUid defib, EntityUid defibtarget) : base(entityUsingDefib, defib, defibtarget) { } +} diff --git a/Content.Shared/Weapons/Ranged/Events/BeforeGunShootEvent.cs b/Content.Shared/Weapons/Ranged/Events/BeforeGunShootEvent.cs new file mode 100644 index 00000000000..1d3317c840f --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Events/BeforeGunShootEvent.cs @@ -0,0 +1,20 @@ +using Content.Shared.Inventory; +using Content.Shared.Weapons.Ranged.Components; + +namespace Content.Shared.Weapons.Ranged.Events; +/// +/// This event is triggered on an entity right before they shoot a gun. +/// +public sealed partial class SelfBeforeGunShotEvent : CancellableEntityEventArgs, IInventoryRelayEvent +{ + public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET; + public readonly EntityUid Shooter; + public readonly Entity Gun; + public readonly List<(EntityUid? Entity, IShootable Shootable)> Ammo; + public SelfBeforeGunShotEvent(EntityUid shooter, Entity gun, List<(EntityUid? Entity, IShootable Shootable)> ammo) + { + Shooter = shooter; + Gun = gun; + Ammo = ammo; + } +} diff --git a/Resources/Locale/en-US/bonk/components/bonkable-component.ftl b/Resources/Locale/en-US/bonk/components/bonkable-component.ftl index 560b10c46ec..1a79da3509f 100644 --- a/Resources/Locale/en-US/bonk/components/bonkable-component.ftl +++ b/Resources/Locale/en-US/bonk/components/bonkable-component.ftl @@ -1,2 +1,4 @@ -bonkable-success-message-others = { CAPITALIZE(THE($user)) } bonks { POSS-ADJ($user) } head against { THE($bonkable) } -bonkable-success-message-user = You bonk your head against { THE($bonkable) } +forced-bonkable-success-message = { CAPITALIZE($bonker) } bonks {$victim}s head against { THE($bonkable) }! + +bonkable-success-message-user = You bonk your head against { THE($bonkable) }! +bonkable-success-message-others = {$victim} bonks their head against { THE($bonkable) }! diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index c7018ae0e1b..077e076959b 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1367,7 +1367,7 @@ description: ghost-role-information-monkey-description - type: GhostTakeoverAvailable - type: Clumsy - clumsyDamage: + gunShootFailDamage: types: Blunt: 5 Piercing: 4 @@ -1550,7 +1550,7 @@ description: Cousins to the sentient race of lizard people, kobolds blend in with their natural habitat and are as nasty as monkeys; ready to pull out your hair and stab you to death. components: - type: Clumsy - clumsyDamage: + gunShootFailDamage: types: Blunt: 2 Piercing: 7 diff --git a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml index 51078d1b100..baa0df7b816 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml @@ -230,7 +230,7 @@ - type: Hands - type: ComplexInteraction - type: Clumsy - clumsyDamage: + gunShootFailDamage: types: Blunt: 5 Piercing: 4 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml b/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml index 7a926d66d37..2b8a3d889d6 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml @@ -33,8 +33,6 @@ bonkDamage: types: Blunt: 4 - bonkSound: !type:SoundCollectionSpecifier - collection: TrayHit - type: Clickable - type: FootstepModifier footstepSoundCollection: diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml b/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml index 22b8cfd48ac..8cc8f5c6a72 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml @@ -17,7 +17,7 @@ - !type:AddComponentSpecial components: - type: Clumsy - clumsyDamage: + gunShootFailDamage: types: #literally just picked semi random valus. i tested this once and tweaked it. Blunt: 5 Piercing: 4 From d33da91e5e7fc785315fff8997147fba3f9d2ef1 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 03:17:52 -0400 Subject: [PATCH 029/366] Maow --- .../EntitySystems/AtmosphereSystem.HighPressureDelta.cs | 1 + .../Piping/Unary/EntitySystems/GasThermoMachineSystem.cs | 5 ++--- Content.Server/Atmos/Portable/SpaceHeaterSystem.cs | 2 +- Content.Server/Cloning/CloningSystem.cs | 2 ++ .../Disposal/Unit/EntitySystems/DisposalUnitSystem.cs | 2 +- Content.Server/Fax/FaxSystem.cs | 1 - Content.Server/GameTicking/Rules/TraitorRuleSystem.cs | 9 +++------ .../Power/EntitySystems/PowerReceiverSystem.cs | 2 +- 8 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs index 42f98089408..4fb0f3fe5ff 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs @@ -1,6 +1,7 @@ using Content.Server.Atmos.Components; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; +using Content.Shared.Humanoid; using Content.Shared.Mobs.Components; using Content.Shared.Physics; using Robust.Shared.Audio; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs index 01eab560a16..c753d0a2445 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs @@ -5,15 +5,14 @@ using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; -using Content.Server.NodeContainer; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.Nodes; using Content.Server.Power.Components; +using Content.Server.Power.EntitySystems; using Content.Shared.Atmos; using Content.Shared.Atmos.Piping.Unary.Components; using JetBrains.Annotations; using Robust.Server.GameObjects; -using Content.Server.Power.EntitySystems; using Content.Shared.UserInterface; using Content.Shared.Administration.Logs; using Content.Shared.Database; @@ -143,7 +142,7 @@ private bool IsHeater(GasThermoMachineComponent comp) private void OnToggleMessage(EntityUid uid, GasThermoMachineComponent thermoMachine, GasThermomachineToggleMessage args) { - var powerState = _power.TryTogglePower(uid); + var powerState = _power.IsPowered(uid); _adminLogger.Add(LogType.AtmosPowerChanged, $"{ToPrettyString(args.Actor)} turned {(powerState ? "On" : "Off")} {ToPrettyString(uid)}"); DirtyUI(uid, thermoMachine); } diff --git a/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs b/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs index b3ad5bbdb37..36ef59e743b 100644 --- a/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs +++ b/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs @@ -99,7 +99,7 @@ private void OnToggle(EntityUid uid, SpaceHeaterComponent spaceHeater, SpaceHeat if (!Resolve(uid, ref powerReceiver)) return; - _power.TryTogglePower(uid); + _power.TogglePower(uid); UpdateAppearance(uid); DirtyUI(uid, spaceHeater); diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index 72104bc381f..373696844f9 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -46,6 +46,8 @@ using Content.Server.Power.Components; using Content.Shared.Drunk; using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Power; + namespace Content.Server.Cloning; diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 39d10583951..eb9871673b6 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -236,7 +236,7 @@ private void OnUiButtonPressed(EntityUid uid, SharedDisposalUnitComponent compon _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit flush button on {ToPrettyString(uid)}, it's now {(component.Engaged ? "on" : "off")}"); break; case SharedDisposalUnitComponent.UiButton.Power: - _power.TryTogglePower(uid, user: args.Actor); + _power.TogglePower(uid, user: args.Actor); break; default: throw new ArgumentOutOfRangeException($"{ToPrettyString(player):player} attempted to hit a nonexistant button on {ToPrettyString(uid)}"); diff --git a/Content.Server/Fax/FaxSystem.cs b/Content.Server/Fax/FaxSystem.cs index bd7bf0c4e08..cc21b21b55b 100644 --- a/Content.Server/Fax/FaxSystem.cs +++ b/Content.Server/Fax/FaxSystem.cs @@ -29,7 +29,6 @@ using Robust.Shared.Containers; using Robust.Shared.Player; using Robust.Shared.Prototypes; -using Content.Shared.NameModifier.Components; using Content.Shared.Power; namespace Content.Server.Fax; diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 46fee4abfe1..42ea278c742 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -5,14 +5,11 @@ using Content.Server.Objectives; using Content.Server.PDA.Ringer; using Content.Server.Roles; -using Content.Server.Traitor.Components; using Content.Server.Traitor.Uplink; -using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.GameTicking.Components; using Content.Shared.Mind; using Content.Shared.Mood; -using Content.Shared.Objectives.Components; using Content.Shared.PDA; using Content.Shared.Roles; using Content.Shared.Roles.Jobs; @@ -87,12 +84,12 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component) { // Calculate the amount of currency on the uplink. var startingBalance = component.StartingBalance; - if (_jobs.MindTryGetJob(mindId, out var prototype)) + if (_jobs.MindTryGetJob(mindId, out var prototype, out var job)) { - if (startingBalance < prototype.AntagAdvantage) // Can't use Math functions on FixedPoint2 + if (startingBalance < job.AntagAdvantage) // Can't use Math functions on FixedPoint2 startingBalance = 0; else - startingBalance = startingBalance - prototype.AntagAdvantage; + startingBalance = startingBalance - job.AntagAdvantage; } // Choose and generate an Uplink, and return the uplink code if applicable diff --git a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs index 47ac38b9972..925ba63f916 100644 --- a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs @@ -139,7 +139,7 @@ private void AddSwitchPowerVerb(EntityUid uid, PowerSwitchComponent component, G { Act = () => { - TryTogglePower(uid, user: args.User); + TogglePower(uid, user: args.User); }, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/Spare/poweronoff.svg.192dpi.png")), Text = Loc.GetString("power-switch-component-toggle-verb"), From 09129a7500542a5f941c1980d3c9615c10842a60 Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Tue, 19 Mar 2024 12:09:00 +0200 Subject: [PATCH 030/366] Simplify DoAfterArgs behavior for movement and distance checks (#25226) * Merge BreakOnWeightlessMove and BreakOnMove. Provide different theshold for weightless movement. * Adjust WeightlessMovementThresholds. Put a thing I forgot to put in the doafterargs. * Make DoAfterArgs only use OnMove to determine whether to check for movement and MoveThreshold to determine the threshold regardless of weightlessness. Gave DistanceThreshold a default value which will always be checked now. * Fix issue introduced by merge. * Use interaction system for determining whether a distance is within range * Fix incorrect doafter args introduced by previous merge. Forgor to commit these. * Exorcise ghost. The execution system should have been deleted when I merged previously. For a reason I cannot comprehend it came back, but only the execution system. * Exorcise ghost Pt. 2 * Allow for movement check to be overriden in zero g and adjust doafter args where needed. You can now override checking for movement in zero g with the BreakOnWeightlessMove bool. By default it will check. The following doafters were made to ignore the movement check in zero g: - Healing yourself with healing items, - Removing embedded projectiles, - Using tools like welders and crowbars * Adjust distance for cuffing/uncuffing to work. Make injections not break on weightless movement. * Fix evil incorrect and uneeded comments --- .../Access/Systems/AccessOverriderSystem.cs | 3 +- Content.Server/Animals/Systems/UdderSystem.cs | 5 +- .../Body/Systems/InternalsSystem.cs | 3 +- .../Botany/Systems/BotanySwabSystem.cs | 3 +- .../Chemistry/EntitySystems/InjectorSystem.cs | 6 +- .../Communications/CommsHackerSystem.cs | 2 +- .../ConstructionSystem.Initial.cs | 3 +- .../ConstructionSystem.Interactions.cs | 3 +- .../Construction/PartExchangerSystem.cs | 2 +- .../Unit/EntitySystems/DisposalUnitSystem.cs | 3 +- .../DisassembleOnAltVerbSystem.cs | 2 +- .../EntitySystems/SpawnAfterInteractSystem.cs | 2 +- .../Ensnaring/EnsnareableSystem.Ensnaring.cs | 3 +- .../Fluids/EntitySystems/DrainSystem.cs | 3 +- .../EntitySystems/PuddleSystem.Spillable.cs | 51 +++ .../Forensics/Systems/ForensicPadSystem.cs | 5 +- .../Systems/ForensicScannerSystem.cs | 3 +- .../Forensics/Systems/ForensicsSystem.cs | 2 +- Content.Server/Guardian/GuardianSystem.cs | 6 +- Content.Server/Implants/ImplanterSystem.cs | 6 +- .../EntitySystems/KitchenSpikeSystem.cs | 3 +- .../Kitchen/EntitySystems/SharpSystem.cs | 3 +- .../Light/EntitySystems/PoweredLightSystem.cs | 2 +- Content.Server/Magic/MagicSystem.cs | 297 +++++++++++++++++- .../MagicMirror/MagicMirrorSystem.cs | 15 +- .../EntitySystems/MechGrabberSystem.cs | 3 +- .../Mech/Systems/MechEquipmentSystem.cs | 3 +- Content.Server/Mech/Systems/MechSystem.cs | 15 +- .../BiomassReclaimerSystem.cs | 5 +- Content.Server/Medical/CryoPodSystem.cs | 3 +- Content.Server/Medical/DefibrillatorSystem.cs | 14 +- Content.Server/Medical/HealingSystem.cs | 5 +- .../Medical/HealthAnalyzerSystem.cs | 5 +- .../Medical/Stethoscope/StethoscopeSystem.cs | 5 +- .../Ninja/Systems/BatteryDrainerSystem.cs | 3 +- Content.Server/Nuke/NukeSystem.cs | 3 +- .../Nutrition/EntitySystems/DrinkSystem.cs | 3 +- .../Nutrition/EntitySystems/FoodSystem.cs | 3 +- .../EntitySystems/SmokingSystem.Vape.cs | 3 +- .../Generator/PortableGeneratorSystem.cs | 2 +- .../Resist/EscapeInventorySystem.cs | 3 +- Content.Server/Resist/ResistLockerSystem.cs | 3 +- .../EntitySystems/RevenantSystem.Abilities.cs | 4 +- .../SprayPainter/SprayPainterSystem.cs | 3 +- Content.Server/Sticky/Systems/StickySystem.cs | 6 +- Content.Server/Strip/StrippableSystem.cs | 12 +- .../Teleportation/HandTeleporterSystem.cs | 2 +- Content.Server/Wires/WiresSystem.cs | 2 +- Content.Shared/Burial/BurialSystem.cs | 6 +- .../Climbing/Systems/ClimbSystem.cs | 3 +- .../EntitySystems/ToggleableClothingSystem.cs | 2 +- Content.Shared/Cuffs/SharedCuffableSystem.cs | 12 +- Content.Shared/Devour/SharedDevourSystem.cs | 6 +- Content.Shared/DoAfter/DoAfter.cs | 6 - Content.Shared/DoAfter/DoAfterArgs.cs | 25 +- .../DoAfter/SharedDoAfterSystem.Update.cs | 71 +++-- Content.Shared/DoAfter/SharedDoAfterSystem.cs | 5 +- .../Inventory/InventorySystem.Equip.cs | 6 +- Content.Shared/Lock/LockSystem.cs | 4 +- .../Mech/EntitySystems/SharedMechSystem.cs | 2 +- .../Projectiles/SharedProjectileSystem.cs | 3 +- Content.Shared/Prying/Systems/PryingSystem.cs | 3 +- Content.Shared/RCD/Systems/RCDSystem.cs | 3 +- Content.Shared/RatKing/SharedRatKingSystem.cs | 2 +- .../Systems/SharedResearchStealerSystem.cs | 4 +- .../Salvage/Fulton/SharedFultonSystem.cs | 3 +- .../Sericulture/SericultureSystem.cs | 2 +- .../Species/Systems/ReformSystem.cs | 6 +- .../SprayPainter/SharedSprayPainterSystem.cs | 3 +- .../Storage/EntitySystems/DumpableSystem.cs | 3 +- .../EntitySystems/SharedStorageSystem.cs | 2 +- .../Tools/Systems/SharedToolSystem.cs | 4 +- .../SharedVendingMachineSystem.Restock.cs | 3 +- .../Systems/SharedGunSystem.Ballistic.cs | 3 +- 74 files changed, 510 insertions(+), 228 deletions(-) diff --git a/Content.Server/Access/Systems/AccessOverriderSystem.cs b/Content.Server/Access/Systems/AccessOverriderSystem.cs index 4e7b796503f..c7b20513f42 100644 --- a/Content.Server/Access/Systems/AccessOverriderSystem.cs +++ b/Content.Server/Access/Systems/AccessOverriderSystem.cs @@ -57,8 +57,7 @@ private void AfterInteractOn(EntityUid uid, AccessOverriderComponent component, var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.DoAfter, new AccessOverriderDoAfterEvent(), uid, target: args.Target, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, NeedHand = true, }; diff --git a/Content.Server/Animals/Systems/UdderSystem.cs b/Content.Server/Animals/Systems/UdderSystem.cs index ef43c2c89d0..b7856e90423 100644 --- a/Content.Server/Animals/Systems/UdderSystem.cs +++ b/Content.Server/Animals/Systems/UdderSystem.cs @@ -15,7 +15,7 @@ namespace Content.Server.Animals.Systems; /// -/// Gives ability to produce milkable reagents, produces endless if the +/// Gives ability to produce milkable reagents, produces endless if the /// owner has no HungerComponent /// internal sealed class UdderSystem : EntitySystem @@ -76,9 +76,8 @@ private void AttemptMilk(Entity udder, EntityUid userUid, Entit var doargs = new DoAfterArgs(EntityManager, userUid, 5, new MilkingDoAfterEvent(), udder, udder, used: containerUid) { - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, - BreakOnTargetMove = true, MovementThreshold = 1.0f, }; diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index fdcc76718cf..c1e1de2baad 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -126,9 +126,8 @@ private void StartToggleInternalsDoAfter(EntityUid user, Entity injector, EntityUid target, DoAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, actualDelay, new InjectorDoAfterEvent(), injector.Owner, target: target, used: injector.Owner) { - BreakOnUserMove = true, + BreakOnMove = true, + BreakOnWeightlessMove = false, BreakOnDamage = true, - BreakOnTargetMove = true, + NeedHand = true, + BreakOnHandChange = true, MovementThreshold = 0.1f, }); } diff --git a/Content.Server/Communications/CommsHackerSystem.cs b/Content.Server/Communications/CommsHackerSystem.cs index bbe64a7987a..4d149ca1ad4 100644 --- a/Content.Server/Communications/CommsHackerSystem.cs +++ b/Content.Server/Communications/CommsHackerSystem.cs @@ -48,7 +48,7 @@ private void OnBeforeInteractHand(EntityUid uid, CommsHackerComponent comp, Befo var doAfterArgs = new DoAfterArgs(EntityManager, uid, comp.Delay, new TerrorDoAfterEvent(), target: target, used: uid, eventTarget: uid) { BreakOnDamage = true, - BreakOnUserMove = true, + BreakOnMove = true, MovementThreshold = 0.5f, CancelDuplicate = false }; diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index 39705fc1974..ede8d3064fa 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -247,8 +247,7 @@ void ShutdownContainers() var doAfterArgs = new DoAfterArgs(EntityManager, user, doAfterTime, new AwaitedDoAfterEvent(), null) { BreakOnDamage = true, - BreakOnTargetMove = false, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = false, // allow simultaneously starting several construction jobs using the same stack of materials. CancelDuplicate = false, diff --git a/Content.Server/Construction/ConstructionSystem.Interactions.cs b/Content.Server/Construction/ConstructionSystem.Interactions.cs index 5361b65b1ff..ad7b2a11b0e 100644 --- a/Content.Server/Construction/ConstructionSystem.Interactions.cs +++ b/Content.Server/Construction/ConstructionSystem.Interactions.cs @@ -286,8 +286,7 @@ private HandleResult HandleInteraction(EntityUid uid, object ev, ConstructionGra var doAfterEventArgs = new DoAfterArgs(EntityManager, interactUsing.User, step.DoAfter, doAfterEv, uid, uid, interactUsing.Used) { BreakOnDamage = false, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }; diff --git a/Content.Server/Construction/PartExchangerSystem.cs b/Content.Server/Construction/PartExchangerSystem.cs index c84d65b75e0..97de3ee4860 100644 --- a/Content.Server/Construction/PartExchangerSystem.cs +++ b/Content.Server/Construction/PartExchangerSystem.cs @@ -180,7 +180,7 @@ private void OnAfterInteract(EntityUid uid, PartExchangerComponent component, Af _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.ExchangeDuration, new ExchangerDoAfterEvent(), uid, target: args.Target, used: uid) { BreakOnDamage = true, - BreakOnUserMove = true + BreakOnMove = true }); } } diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index eb9871673b6..1b9ab97be6b 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -486,8 +486,7 @@ public bool TryInsert(EntityUid unitId, EntityUid toInsertId, EntityUid? userId, var doAfterArgs = new DoAfterArgs(EntityManager, userId.Value, delay, new DisposalDoAfterEvent(), unitId, target: toInsertId, used: unitId) { BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = false }; diff --git a/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs b/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs index 08bf68c4d29..61b6f3d93d2 100644 --- a/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs +++ b/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs @@ -45,7 +45,7 @@ public async void AttemptDisassemble(EntityUid uid, EntityUid user, EntityUid ta { var doAfterArgs = new DoAfterArgs(EntityManager, user, component.DoAfterTime, new AwaitedDoAfterEvent(), null) { - BreakOnUserMove = true, + BreakOnMove = true, }; var result = await doAfterSystem.WaitDoAfter(doAfterArgs); diff --git a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs index a0bbbdf350a..281bbc47211 100644 --- a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs +++ b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs @@ -46,7 +46,7 @@ bool IsTileClear() { var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.DoAfterTime, new AwaitedDoAfterEvent(), null) { - BreakOnUserMove = true, + BreakOnMove = true, }; var result = await _doAfterSystem.WaitDoAfter(doAfterArgs); diff --git a/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs b/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs index 8dd3d56a1ee..a5ebb0fd9e1 100644 --- a/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs +++ b/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs @@ -121,8 +121,7 @@ public void TryFree(EntityUid target, EntityUid user, EntityUid ensnare, Ensnari var doAfterEventArgs = new DoAfterArgs(EntityManager, user, freeTime, new EnsnareableDoAfterEvent(), target, target: target, used: ensnare) { - BreakOnUserMove = breakOnMove, - BreakOnTargetMove = breakOnMove, + BreakOnMove = breakOnMove, BreakOnDamage = false, NeedHand = true, BlockDuplicate = true, diff --git a/Content.Server/Fluids/EntitySystems/DrainSystem.cs b/Content.Server/Fluids/EntitySystems/DrainSystem.cs index 27ad2178f93..5fc406dca53 100644 --- a/Content.Server/Fluids/EntitySystems/DrainSystem.cs +++ b/Content.Server/Fluids/EntitySystems/DrainSystem.cs @@ -245,9 +245,8 @@ private void OnInteract(Entity entity, ref AfterInteractUsingEve var doAfterArgs = new DoAfterArgs(EntityManager, args.User, entity.Comp.UnclogDuration, new DrainDoAfterEvent(), entity, args.Target, args.Used) { - BreakOnTargetMove = true, - BreakOnUserMove = true, BreakOnDamage = true, + BreakOnMove = true, BreakOnHandChange = true }; diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs index d02dd44e81f..e4fd3fb491c 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs @@ -161,6 +161,57 @@ private void OnAttemptPacifiedThrow(Entity ent, ref AttemptP args.Cancel("pacified-cannot-throw-spill"); } + private void AddSpillVerb(Entity entity, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + if (!_solutionContainerSystem.TryGetSolution(args.Target, entity.Comp.SolutionName, out var soln, out var solution)) + return; + + if (_openable.IsClosed(args.Target)) + return; + + if (solution.Volume == FixedPoint2.Zero) + return; + + if (_entityManager.HasComponent(args.User)) + return; + + + Verb verb = new() + { + Text = Loc.GetString("spill-target-verb-get-data-text") + }; + + // TODO VERB ICONS spill icon? pouring out a glass/beaker? + if (entity.Comp.SpillDelay == null) + { + var target = args.Target; + verb.Act = () => + { + var puddleSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume); + TrySpillAt(Transform(target).Coordinates, puddleSolution, out _); + }; + } + else + { + var user = args.User; + verb.Act = () => + { + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, entity.Comp.SpillDelay ?? 0, new SpillDoAfterEvent(), entity.Owner, target: entity.Owner) + { + BreakOnDamage = true, + BreakOnMove = true, + NeedHand = true, + }); + }; + } + verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately. + verb.DoContactInteraction = true; + args.Verbs.Add(verb); + } + private void OnDoAfter(Entity entity, ref SpillDoAfterEvent args) { if (args.Handled || args.Cancelled || args.Args.Target == null) diff --git a/Content.Server/Forensics/Systems/ForensicPadSystem.cs b/Content.Server/Forensics/Systems/ForensicPadSystem.cs index ad39817f8b0..42512cb1fdc 100644 --- a/Content.Server/Forensics/Systems/ForensicPadSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicPadSystem.cs @@ -83,9 +83,8 @@ private void StartScan(EntityUid used, EntityUid user, EntityUid target, Forensi var doAfterEventArgs = new DoAfterArgs(EntityManager, user, pad.ScanDelay, ev, used, target: target, used: used) { - BreakOnTargetMove = true, - BreakOnUserMove = true, - NeedHand = true + NeedHand = true, + BreakOnMove = true, }; _doAfterSystem.TryStartDoAfter(doAfterEventArgs); diff --git a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs index e83cde7456c..5e2a562577d 100644 --- a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs @@ -94,8 +94,7 @@ private void StartScan(EntityUid uid, ForensicScannerComponent component, Entity { _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.ScanDelay, new ForensicScannerDoAfterEvent(), uid, target: target, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }); } diff --git a/Content.Server/Forensics/Systems/ForensicsSystem.cs b/Content.Server/Forensics/Systems/ForensicsSystem.cs index 8f91ec41e80..fe1844a3ed5 100644 --- a/Content.Server/Forensics/Systems/ForensicsSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicsSystem.cs @@ -148,7 +148,7 @@ private void OnAfterInteract(EntityUid uid, CleansForensicsComponent component, BreakOnHandChange = true, NeedHand = true, BreakOnDamage = true, - BreakOnTargetMove = true, + BreakOnMove = true, MovementThreshold = 0.01f, DistanceThreshold = forensicsComp.CleanDistance, }; diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs index 4ad6c1f8359..97d4eb06803 100644 --- a/Content.Server/Guardian/GuardianSystem.cs +++ b/Content.Server/Guardian/GuardianSystem.cs @@ -194,11 +194,7 @@ private void UseCreator(EntityUid user, EntityUid target, EntityUid injector, Gu return; } - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.InjectionDelay, new GuardianCreatorDoAfterEvent(), injector, target: target, used: injector) - { - BreakOnTargetMove = true, - BreakOnUserMove = true - }); + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.InjectionDelay, new GuardianCreatorDoAfterEvent(), injector, target: target, used: injector){BreakOnMove = true}); } private void OnDoAfter(EntityUid uid, GuardianCreatorComponent component, DoAfterEvent args) diff --git a/Content.Server/Implants/ImplanterSystem.cs b/Content.Server/Implants/ImplanterSystem.cs index 3cfa3a9f5f8..e441574213e 100644 --- a/Content.Server/Implants/ImplanterSystem.cs +++ b/Content.Server/Implants/ImplanterSystem.cs @@ -99,9 +99,8 @@ public void TryImplant(ImplanterComponent component, EntityUid user, EntityUid t { var args = new DoAfterArgs(EntityManager, user, component.ImplantTime, new ImplantEvent(), implanter, target: target, used: implanter) { - BreakOnUserMove = true, - BreakOnTargetMove = true, BreakOnDamage = true, + BreakOnMove = true, NeedHand = true, }; @@ -126,9 +125,8 @@ public void TryDraw(ImplanterComponent component, EntityUid user, EntityUid targ { var args = new DoAfterArgs(EntityManager, user, component.DrawTime, new DrawEvent(), implanter, target: target, used: implanter) { - BreakOnUserMove = true, - BreakOnTargetMove = true, BreakOnDamage = true, + BreakOnMove = true, NeedHand = true, }; diff --git a/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs b/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs index 0419e13d230..b145bec0148 100644 --- a/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs @@ -257,9 +257,8 @@ public bool TrySpike(EntityUid uid, EntityUid userUid, EntityUid victimUid, Kitc var doAfterArgs = new DoAfterArgs(EntityManager, userUid, component.SpikeDelay + butcherable.ButcherDelay, new SpikeDoAfterEvent(), uid, target: victimUid, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, BreakOnDamage = true, + BreakOnMove = true, NeedHand = true }; diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index 81806a0c818..431e438fd8d 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -73,9 +73,8 @@ private bool TryStartButcherDoafter(EntityUid knife, EntityUid target, EntityUid var doAfter = new DoAfterArgs(EntityManager, user, sharp.ButcherDelayModifier * butcher.ButcherDelay, new SharpDoAfterEvent(), knife, target: target, used: knife) { - BreakOnTargetMove = true, - BreakOnUserMove = true, BreakOnDamage = true, + BreakOnMove = true, NeedHand = true }; _doAfterSystem.TryStartDoAfter(doAfter); diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index 3c44ea46155..c4a07b56a86 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -154,7 +154,7 @@ private void OnInteractHand(EntityUid uid, PoweredLightComponent light, Interact // removing a working bulb, so require a delay _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, userUid, light.EjectBulbDelay, new PoweredLightDoAfterEvent(), uid, target: uid) { - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, }); diff --git a/Content.Server/Magic/MagicSystem.cs b/Content.Server/Magic/MagicSystem.cs index 5997c56e240..30c01063b0e 100644 --- a/Content.Server/Magic/MagicSystem.cs +++ b/Content.Server/Magic/MagicSystem.cs @@ -18,6 +18,301 @@ public override void Initialize() private void OnSpellSpoken(ref SpeakSpellEvent args) { - _chat.TrySendInGameICMessage(args.Performer, Loc.GetString(args.Speech), args.ChatType, false); + if (args.Handled) + return; + + AttemptLearn(uid, component, args); + + args.Handled = true; + } + + private void AttemptLearn(EntityUid uid, SpellbookComponent component, UseInHandEvent args) + { + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.LearnTime, new SpellbookDoAfterEvent(), uid, target: uid) + { + BreakOnDamage = true, + BreakOnMove = true, + NeedHand = true //What, are you going to read with your eyes only?? + }; + + _doAfter.TryStartDoAfter(doAfterEventArgs); + } + + #region Spells + + /// + /// Handles the instant action (i.e. on the caster) attempting to spawn an entity. + /// + private void OnInstantSpawn(InstantSpawnSpellEvent args) + { + if (args.Handled) + return; + + var transform = Transform(args.Performer); + + foreach (var position in GetSpawnPositions(transform, args.Pos)) + { + var ent = Spawn(args.Prototype, position.SnapToGrid(EntityManager, _mapManager)); + + if (args.PreventCollideWithCaster) + { + var comp = EnsureComp(ent); + comp.Uid = args.Performer; + } + } + + Speak(args); + args.Handled = true; + } + + private void OnProjectileSpell(ProjectileSpellEvent ev) + { + if (ev.Handled) + return; + + ev.Handled = true; + Speak(ev); + + var xform = Transform(ev.Performer); + var userVelocity = _physics.GetMapLinearVelocity(ev.Performer); + + foreach (var pos in GetSpawnPositions(xform, ev.Pos)) + { + // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. + var mapPos = pos.ToMap(EntityManager); + var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out _) + ? pos.WithEntityId(gridUid, EntityManager) + : new(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position); + + var ent = Spawn(ev.Prototype, spawnCoords); + var direction = ev.Target.ToMapPos(EntityManager, _transformSystem) - + spawnCoords.ToMapPos(EntityManager, _transformSystem); + _gunSystem.ShootProjectile(ent, direction, userVelocity, ev.Performer, ev.Performer); + } + } + + private void OnChangeComponentsSpell(ChangeComponentsSpellEvent ev) + { + if (ev.Handled) + return; + ev.Handled = true; + Speak(ev); + + foreach (var toRemove in ev.ToRemove) + { + if (_compFact.TryGetRegistration(toRemove, out var registration)) + RemComp(ev.Target, registration.Type); + } + + foreach (var (name, data) in ev.ToAdd) + { + if (HasComp(ev.Target, data.Component.GetType())) + continue; + + var component = (Component) _compFact.GetComponent(name); + component.Owner = ev.Target; + var temp = (object) component; + _seriMan.CopyTo(data.Component, ref temp); + EntityManager.AddComponent(ev.Target, (Component) temp!); + } + } + + private List GetSpawnPositions(TransformComponent casterXform, MagicSpawnData data) + { + switch (data) + { + case TargetCasterPos: + return new List(1) {casterXform.Coordinates}; + case TargetInFront: + { + // This is shit but you get the idea. + var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized()); + + if (!_mapManager.TryGetGrid(casterXform.GridUid, out var mapGrid)) + return new List(); + + if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager)) + return new List(); + + var tileIndex = tileReference.Value.GridIndices; + var coords = mapGrid.GridTileToLocal(tileIndex); + EntityCoordinates coordsPlus; + EntityCoordinates coordsMinus; + + var dir = casterXform.LocalRotation.GetCardinalDir(); + switch (dir) + { + case Direction.North: + case Direction.South: + { + coordsPlus = mapGrid.GridTileToLocal(tileIndex + (1, 0)); + coordsMinus = mapGrid.GridTileToLocal(tileIndex + (-1, 0)); + return new List(3) + { + coords, + coordsPlus, + coordsMinus, + }; + } + case Direction.East: + case Direction.West: + { + coordsPlus = mapGrid.GridTileToLocal(tileIndex + (0, 1)); + coordsMinus = mapGrid.GridTileToLocal(tileIndex + (0, -1)); + return new List(3) + { + coords, + coordsPlus, + coordsMinus, + }; + } + } + + return new List(); + } + default: + throw new ArgumentOutOfRangeException(); + } + } + + /// + /// Teleports the user to the clicked location + /// + /// + private void OnTeleportSpell(TeleportSpellEvent args) + { + if (args.Handled) + return; + + var transform = Transform(args.Performer); + + if (transform.MapID != args.Target.GetMapId(EntityManager)) return; + + _transformSystem.SetCoordinates(args.Performer, args.Target); + transform.AttachToGridOrMap(); + _audio.PlayPvs(args.BlinkSound, args.Performer, AudioParams.Default.WithVolume(args.BlinkVolume)); + Speak(args); + args.Handled = true; + } + + /// + /// Opens all doors within range + /// + /// + private void OnKnockSpell(KnockSpellEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + Speak(args); + + //Get the position of the player + var transform = Transform(args.Performer); + var coords = transform.Coordinates; + + _audio.PlayPvs(args.KnockSound, args.Performer, AudioParams.Default.WithVolume(args.KnockVolume)); + + //Look for doors and don't open them if they're already open. + foreach (var entity in _lookup.GetEntitiesInRange(coords, args.Range)) + { + if (TryComp(entity, out var bolts)) + _doorSystem.SetBoltsDown((entity, bolts), false); + + if (TryComp(entity, out var doorComp) && doorComp.State is not DoorState.Open) + _doorSystem.StartOpening(entity); + } + } + + private void OnSmiteSpell(SmiteSpellEvent ev) + { + if (ev.Handled) + return; + + ev.Handled = true; + Speak(ev); + + var direction = Transform(ev.Target).MapPosition.Position - Transform(ev.Performer).MapPosition.Position; + var impulseVector = direction * 10000; + + _physics.ApplyLinearImpulse(ev.Target, impulseVector); + + if (!TryComp(ev.Target, out var body)) + return; + + var ents = _bodySystem.GibBody(ev.Target, true, body); + + if (!ev.DeleteNonBrainParts) + return; + + foreach (var part in ents) + { + // just leaves a brain and clothes + if (HasComp(part) && !HasComp(part)) + { + QueueDel(part); + } + } + } + + /// + /// Spawns entity prototypes from a list within range of click. + /// + /// + /// It will offset mobs after the first mob based on the OffsetVector2 property supplied. + /// + /// The Spawn Spell Event args. + private void OnWorldSpawn(WorldSpawnSpellEvent args) + { + if (args.Handled) + return; + + var targetMapCoords = args.Target; + + SpawnSpellHelper(args.Contents, targetMapCoords, args.Lifetime, args.Offset); + Speak(args); + args.Handled = true; + } + + /// + /// Loops through a supplied list of entity prototypes and spawns them + /// + /// + /// If an offset of 0, 0 is supplied then the entities will all spawn on the same tile. + /// Any other offset will spawn entities starting from the source Map Coordinates and will increment the supplied + /// offset + /// + /// The list of Entities to spawn in + /// Map Coordinates where the entities will spawn + /// Check to see if the entities should self delete + /// A Vector2 offset that the entities will spawn in + private void SpawnSpellHelper(List entityEntries, EntityCoordinates entityCoords, float? lifetime, Vector2 offsetVector2) + { + var getProtos = EntitySpawnCollection.GetSpawns(entityEntries, _random); + + var offsetCoords = entityCoords; + foreach (var proto in getProtos) + { + // TODO: Share this code with instant because they're both doing similar things for positioning. + var entity = Spawn(proto, offsetCoords); + offsetCoords = offsetCoords.Offset(offsetVector2); + + if (lifetime != null) + { + var comp = EnsureComp(entity); + comp.Lifetime = lifetime.Value; + } + } + } + + #endregion + + private void Speak(BaseActionEvent args) + { + if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech)) + return; + + _chat.TrySendInGameICMessage(args.Performer, Loc.GetString(speak.Speech), + InGameICChatType.Speak, false); } } diff --git a/Content.Server/MagicMirror/MagicMirrorSystem.cs b/Content.Server/MagicMirror/MagicMirrorSystem.cs index b11e8ca707c..8d8a6bfa3b4 100644 --- a/Content.Server/MagicMirror/MagicMirrorSystem.cs +++ b/Content.Server/MagicMirror/MagicMirrorSystem.cs @@ -59,11 +59,9 @@ private void OnMagicMirrorSelect(EntityUid uid, MagicMirrorComponent component, _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, component.SelectSlotTime, doAfter, uid, target: target, used: uid) { DistanceThreshold = SharedInteractionSystem.InteractionRange, - BreakOnTargetMove = true, BreakOnDamage = true, + BreakOnMove = true, BreakOnHandChange = false, - BreakOnUserMove = true, - BreakOnWeightlessMove = false, NeedHand = true }, out var doAfterId); @@ -116,11 +114,9 @@ private void OnTryMagicMirrorChangeColor(EntityUid uid, MagicMirrorComponent com _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, component.ChangeSlotTime, doAfter, uid, target: target, used: uid) { - BreakOnTargetMove = true, BreakOnDamage = true, + BreakOnMove = true, BreakOnHandChange = false, - BreakOnUserMove = true, - BreakOnWeightlessMove = false, NeedHand = true }, out var doAfterId); @@ -172,11 +168,8 @@ private void OnTryMagicMirrorRemoveSlot(EntityUid uid, MagicMirrorComponent comp _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, component.RemoveSlotTime, doAfter, uid, target: target, used: uid) { DistanceThreshold = SharedInteractionSystem.InteractionRange, - BreakOnTargetMove = true, BreakOnDamage = true, BreakOnHandChange = false, - BreakOnUserMove = true, - BreakOnWeightlessMove = false, NeedHand = true }, out var doAfterId); @@ -227,11 +220,9 @@ private void OnTryMagicMirrorAddSlot(EntityUid uid, MagicMirrorComponent compone _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, component.AddSlotTime, doAfter, uid, target: component.Target.Value, used: uid) { - BreakOnTargetMove = true, BreakOnDamage = true, + BreakOnMove = true, BreakOnHandChange = false, - BreakOnUserMove = true, - BreakOnWeightlessMove = false, NeedHand = true }, out var doAfterId); diff --git a/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs b/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs index 194ef532ba2..ae0495fb278 100644 --- a/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs +++ b/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs @@ -163,8 +163,7 @@ private void OnInteract(EntityUid uid, MechGrabberComponent component, UserActiv component.AudioStream = audio!.Value.Entity; var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.GrabDelay, new GrabberDoAfterEvent(), uid, target: target, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true + BreakOnMove = true }; _doAfter.TryStartDoAfter(doAfterArgs, out component.DoAfter); diff --git a/Content.Server/Mech/Systems/MechEquipmentSystem.cs b/Content.Server/Mech/Systems/MechEquipmentSystem.cs index 5191f53004e..f51c0444e69 100644 --- a/Content.Server/Mech/Systems/MechEquipmentSystem.cs +++ b/Content.Server/Mech/Systems/MechEquipmentSystem.cs @@ -47,8 +47,7 @@ private void OnUsed(EntityUid uid, MechEquipmentComponent component, AfterIntera var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.InstallDuration, new InsertEquipmentEvent(), uid, target: mech, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true + BreakOnMove = true, }; _doAfter.TryStartDoAfter(doAfterEventArgs); diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index a728ee7de5e..53c6c62cdb8 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -89,10 +89,10 @@ private void OnInteractUsing(EntityUid uid, MechComponent component, InteractUsi if (TryComp(args.Used, out var tool) && tool.Qualities.Contains("Prying") && component.BatterySlot.ContainedEntity != null) { - var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.BatteryRemovalDelay, new RemoveBatteryEvent(), uid, target: uid, used: args.Target) + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.BatteryRemovalDelay, + new RemoveBatteryEvent(), uid, target: uid, used: args.Target) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true }; _doAfter.TryStartDoAfter(doAfterEventArgs); @@ -179,7 +179,7 @@ private void OnAlternativeVerb(EntityUid uid, MechComponent component, GetVerbsE { var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.EntryDelay, new MechEntryEvent(), uid, target: uid) { - BreakOnUserMove = true, + BreakOnMove = true, }; _doAfter.TryStartDoAfter(doAfterEventArgs); @@ -207,11 +207,8 @@ private void OnAlternativeVerb(EntityUid uid, MechComponent component, GetVerbsE return; } - var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.ExitDelay, new MechExitEvent(), uid, target: uid) - { - BreakOnUserMove = true, - BreakOnTargetMove = true, - }; + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.ExitDelay, + new MechExitEvent(), uid, target: uid); _doAfter.TryStartDoAfter(doAfterEventArgs); } diff --git a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs index ec27c717286..227283c95d0 100644 --- a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs +++ b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs @@ -158,9 +158,8 @@ private void OnAfterInteractUsing(Entity reclaimer, r : 1); _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, delay, new ReclaimerDoAfterEvent(), reclaimer, target: args.Target, used: args.Used) { - BreakOnTargetMove = true, - BreakOnUserMove = true, - NeedHand = true + NeedHand = true, + BreakOnMove = true }); } diff --git a/Content.Server/Medical/CryoPodSystem.cs b/Content.Server/Medical/CryoPodSystem.cs index d90e49d2016..db7eef0afa8 100644 --- a/Content.Server/Medical/CryoPodSystem.cs +++ b/Content.Server/Medical/CryoPodSystem.cs @@ -144,8 +144,7 @@ private void HandleDragDropOn(Entity entity, ref DragDropTarge var doAfterArgs = new DoAfterArgs(EntityManager, args.User, entity.Comp.EntryDelay, new CryoPodDragFinished(), entity, target: args.Dragged, used: entity) { BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = false, }; _doAfterSystem.TryStartDoAfter(doAfterArgs); diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index 45e31cef5bb..9f145173aca 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -142,16 +142,14 @@ public bool TryStartZap(EntityUid uid, EntityUid target, EntityUid user, Defibri _audio.PlayPvs(component.ChargeSound, uid); return _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.DoAfterDuration, new DefibrillatorZapDoAfterEvent(), uid, target, uid) - { - NeedHand = true, - BreakOnMove = !component.AllowDoAfterMovement - }); + { + BlockDuplicate = true, + BreakOnHandChange = true, + NeedHand = true + }); } - /// - /// Tries to defibrillate the target with the given defibrillator. - /// - public void Zap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorComponent? component = null) + public void Zap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorComponent? component = null, MobStateComponent? mob = null, MobThresholdsComponent? thresholds = null) { if (!Resolve(uid, ref component)) return; diff --git a/Content.Server/Medical/HealingSystem.cs b/Content.Server/Medical/HealingSystem.cs index c64bb2a485d..8191243356b 100644 --- a/Content.Server/Medical/HealingSystem.cs +++ b/Content.Server/Medical/HealingSystem.cs @@ -218,12 +218,11 @@ targetDamage.DamageContainerID is not null && var doAfterEventArgs = new DoAfterArgs(EntityManager, user, delay, new HealingDoAfterEvent(), target, target: target, used: uid) { - //Raise the event on the target if it's not self, otherwise raise it on self. - BreakOnUserMove = true, - BreakOnTargetMove = true, // Didn't break on damage as they may be trying to prevent it and // not being able to heal your own ticking damage would be frustrating. NeedHand = true, + BreakOnMove = true, + BreakOnWeightlessMove = false, }; _doAfter.TryStartDoAfter(doAfterEventArgs); diff --git a/Content.Server/Medical/HealthAnalyzerSystem.cs b/Content.Server/Medical/HealthAnalyzerSystem.cs index f284a214e65..870788cd4de 100644 --- a/Content.Server/Medical/HealthAnalyzerSystem.cs +++ b/Content.Server/Medical/HealthAnalyzerSystem.cs @@ -111,9 +111,8 @@ private void OnAfterInteract(Entity uid, ref AfterInter _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, uid.Comp.ScanDelay, new HealthAnalyzerDoAfterEvent(), uid, target: args.Target, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, - NeedHand = true + NeedHand = true, + BreakOnMove = true }); } diff --git a/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs b/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs index 96bfc7c904c..b8304c562a4 100644 --- a/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs +++ b/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs @@ -100,9 +100,8 @@ private void StartListening(EntityUid scope, EntityUid user, EntityUid target, S { _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, comp.Delay, new StethoscopeDoAfterEvent(), scope, target: target, used: scope) { - BreakOnTargetMove = true, - BreakOnUserMove = true, - NeedHand = true + NeedHand = true, + BreakOnMove = true, }); } diff --git a/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs b/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs index c4b5c6dc2bf..4baf0913cec 100644 --- a/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs +++ b/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs @@ -51,9 +51,8 @@ private void OnBeforeInteractHand(Entity ent, ref Befor var doAfterArgs = new DoAfterArgs(EntityManager, uid, comp.DrainTime, new DrainDoAfterEvent(), target: target, eventTarget: uid) { - BreakOnUserMove = true, - BreakOnWeightlessMove = true, // prevent a ninja on a pod remotely draining it MovementThreshold = 0.5f, + BreakOnMove = true, CancelDuplicate = false, AttemptFrequency = AttemptFrequency.StartAndEnd }; diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index 9cd24b9af6c..4bca7a1eac6 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -591,8 +591,7 @@ private void DisarmBombDoafter(EntityUid uid, EntityUid user, NukeComponent nuke var doAfter = new DoAfterArgs(EntityManager, user, nuke.DisarmDoafterLength, new NukeDisarmDoAfterEvent(), uid, target: uid) { BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }; diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index cd05adc7948..6781d07956b 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -213,9 +213,8 @@ private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, En target: target, used: item) { - BreakOnUserMove = forceDrink, + BreakOnMove = forceDrink, BreakOnDamage = true, - BreakOnTargetMove = forceDrink, MovementThreshold = 0.01f, DistanceThreshold = 1.0f, // Mice and the like can eat without hands. diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 84355f03c16..feb7c494841 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -189,9 +189,8 @@ private void OnFeedFood(Entity entity, ref AfterInteractEvent arg target: target, used: food) { - BreakOnUserMove = forceFeed, + BreakOnMove = forceFeed, BreakOnDamage = true, - BreakOnTargetMove = forceFeed, MovementThreshold = 0.01f, DistanceThreshold = MaxFeedDistance, // Mice and the like can eat without hands. diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs index d5bff967b3c..f7650f599b4 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs @@ -114,8 +114,7 @@ private void OnVapeInteraction(Entity entity, ref AfterInteractEv var vapeDoAfterEvent = new VapeDoAfterEvent(solution, forced); _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, delay, vapeDoAfterEvent, entity.Owner, target: args.Target, used: entity.Owner) { - BreakOnTargetMove = true, - BreakOnUserMove = false, + BreakOnMove = false, BreakOnDamage = true }); } diff --git a/Content.Server/Power/Generator/PortableGeneratorSystem.cs b/Content.Server/Power/Generator/PortableGeneratorSystem.cs index e2996a54d71..f7d259b122a 100644 --- a/Content.Server/Power/Generator/PortableGeneratorSystem.cs +++ b/Content.Server/Power/Generator/PortableGeneratorSystem.cs @@ -73,7 +73,7 @@ private void StartGenerator(EntityUid uid, PortableGeneratorComponent component, _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.StartTime, new GeneratorStartedEvent(), uid, uid) { - BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, RequireCanInteract = true, + BreakOnDamage = true, BreakOnMove = true, RequireCanInteract = true, NeedHand = true }); } diff --git a/Content.Server/Resist/EscapeInventorySystem.cs b/Content.Server/Resist/EscapeInventorySystem.cs index a6eb703ab0f..63e8173117c 100644 --- a/Content.Server/Resist/EscapeInventorySystem.cs +++ b/Content.Server/Resist/EscapeInventorySystem.cs @@ -86,8 +86,7 @@ private void OnRelayMovement(EntityUid uid, CanEscapeInventoryComponent componen var doAfterEventArgs = new DoAfterArgs(EntityManager, user, component.BaseResistTime * multiplier, new EscapeInventoryEvent(), user, target: container) { - BreakOnTargetMove = false, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, NeedHand = false }; diff --git a/Content.Server/Resist/ResistLockerSystem.cs b/Content.Server/Resist/ResistLockerSystem.cs index 7a17a2eba19..2ab277d0f1a 100644 --- a/Content.Server/Resist/ResistLockerSystem.cs +++ b/Content.Server/Resist/ResistLockerSystem.cs @@ -47,8 +47,7 @@ private void AttemptResist(EntityUid user, EntityUid target, EntityStorageCompon var doAfterEventArgs = new DoAfterArgs(EntityManager, user, resistLockerComponent.ResistTime, new ResistLockerDoAfterEvent(), target, target: target) { - BreakOnTargetMove = false, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, NeedHand = false //No hands 'cause we be kickin' }; diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index 0a72e48071e..bf87cf5a600 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -91,7 +91,7 @@ private void BeginSoulSearchDoAfter(EntityUid uid, EntityUid target, RevenantCom { var searchDoAfter = new DoAfterArgs(EntityManager, uid, revenant.SoulSearchDuration, new SoulEvent(), uid, target: target) { - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, DistanceThreshold = 2 }; @@ -152,7 +152,7 @@ private void BeginHarvestDoAfter(EntityUid uid, EntityUid target, RevenantCompon var doAfter = new DoAfterArgs(EntityManager, uid, revenant.HarvestDebuffs.X, new HarvestEvent(), uid, target: target) { DistanceThreshold = 2, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, RequireCanInteract = false, // stuns itself }; diff --git a/Content.Server/SprayPainter/SprayPainterSystem.cs b/Content.Server/SprayPainter/SprayPainterSystem.cs index e49c49c1da0..a8024e2d77b 100644 --- a/Content.Server/SprayPainter/SprayPainterSystem.cs +++ b/Content.Server/SprayPainter/SprayPainterSystem.cs @@ -55,8 +55,7 @@ private void OnPipeInteract(Entity ent, ref InteractUsi var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.PipeSprayTime, new SprayPainterPipeDoAfterEvent(color), args.Used, target: ent, used: args.Used) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, CancelDuplicate = true, // multiple pipes can be sprayed at once just not the same one diff --git a/Content.Server/Sticky/Systems/StickySystem.cs b/Content.Server/Sticky/Systems/StickySystem.cs index 2d1104e2ceb..effe1b72f71 100644 --- a/Content.Server/Sticky/Systems/StickySystem.cs +++ b/Content.Server/Sticky/Systems/StickySystem.cs @@ -93,8 +93,7 @@ private bool StartSticking(EntityUid uid, EntityUid user, EntityUid target, Stic // start sticking object to target _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, delay, new StickyDoAfterEvent(), uid, target: target, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }); } @@ -148,8 +147,7 @@ private void StartUnsticking(EntityUid uid, EntityUid user, StickyComponent? com // start unsticking object _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, delay, new StickyDoAfterEvent(), uid, target: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }); } diff --git a/Content.Server/Strip/StrippableSystem.cs b/Content.Server/Strip/StrippableSystem.cs index 73f2ce17a66..64e96e1467e 100644 --- a/Content.Server/Strip/StrippableSystem.cs +++ b/Content.Server/Strip/StrippableSystem.cs @@ -263,8 +263,7 @@ private void StartStripInsertInventory( Hidden = hidden, AttemptFrequency = AttemptFrequency.EveryTick, BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true, DuplicateCondition = DuplicateConditions.SameTool }; @@ -359,8 +358,7 @@ private void StartStripRemoveInventory( Hidden = hidden, AttemptFrequency = AttemptFrequency.EveryTick, BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true, BreakOnHandChange = false, // Allow simultaneously removing multiple items. DuplicateCondition = DuplicateConditions.SameTool @@ -459,8 +457,7 @@ private void StartStripInsertHand( Hidden = hidden, AttemptFrequency = AttemptFrequency.EveryTick, BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true, DuplicateCondition = DuplicateConditions.SameTool }; @@ -561,8 +558,7 @@ private void StartStripRemoveHand( Hidden = hidden, AttemptFrequency = AttemptFrequency.EveryTick, BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true, BreakOnHandChange = false, // Allow simultaneously removing multiple items. DuplicateCondition = DuplicateConditions.SameTool diff --git a/Content.Server/Teleportation/HandTeleporterSystem.cs b/Content.Server/Teleportation/HandTeleporterSystem.cs index 29cde5d741d..3d988b09161 100644 --- a/Content.Server/Teleportation/HandTeleporterSystem.cs +++ b/Content.Server/Teleportation/HandTeleporterSystem.cs @@ -58,7 +58,7 @@ private void OnUseInHand(EntityUid uid, HandTeleporterComponent component, UseIn var doafterArgs = new DoAfterArgs(EntityManager, args.User, component.PortalCreationDelay, new TeleporterDoAfterEvent(), uid, used: uid) { BreakOnDamage = true, - BreakOnUserMove = true, + BreakOnMove = true, MovementThreshold = 0.5f, }; diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index 440b1f6dce7..526a14b1ab4 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -703,7 +703,7 @@ private void TryDoWireAction(EntityUid target, EntityUid user, EntityUid toolEnt { NeedHand = true, BreakOnDamage = true, - BreakOnUserMove = true + BreakOnMove = true }; _doAfter.TryStartDoAfter(args); diff --git a/Content.Shared/Burial/BurialSystem.cs b/Content.Shared/Burial/BurialSystem.cs index 1116c6797b2..45a89f3be97 100644 --- a/Content.Shared/Burial/BurialSystem.cs +++ b/Content.Shared/Burial/BurialSystem.cs @@ -45,8 +45,7 @@ private void OnInteractUsing(EntityUid uid, GraveComponent component, InteractUs { var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.DigDelay / shovel.SpeedModifier, new GraveDiggingDoAfterEvent(), uid, target: args.Target, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, NeedHand = true, BreakOnHandChange = true @@ -166,8 +165,7 @@ private void OnRelayMovement(EntityUid uid, GraveComponent component, ref Contai var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Entity, component.DigDelay / component.DigOutByHandModifier, new GraveDiggingDoAfterEvent(), uid, target: uid) { NeedHand = false, - BreakOnUserMove = true, - BreakOnTargetMove = false, + BreakOnMove = true, BreakOnHandChange = false, BreakOnDamage = false }; diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index 36dc7c059e4..722d97213f5 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -226,8 +226,7 @@ public bool TryClimb( target: climbable, used: entityToMove) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, DuplicateCondition = DuplicateConditions.SameTool | DuplicateConditions.SameTarget }; diff --git a/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs index 4abe7bc876a..4cb127bb102 100644 --- a/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs @@ -103,7 +103,7 @@ private void StartDoAfter(EntityUid user, EntityUid item, EntityUid wearer, Togg var args = new DoAfterArgs(EntityManager, user, time, new ToggleClothingDoAfterEvent(), item, wearer, item) { BreakOnDamage = true, - BreakOnTargetMove = true, + BreakOnMove = true, // This should just re-use the BUI range checks & cancel the do after if the BUI closes. But that is all // server-side at the moment. // TODO BUI REFACTOR. diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index 297fe095f8c..1c8e2ef2b0d 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -511,10 +511,11 @@ public bool TryCuffing(EntityUid user, EntityUid target, EntityUid handcuff, Han var doAfterEventArgs = new DoAfterArgs(EntityManager, user, cuffTime, new AddCuffDoAfterEvent(), handcuff, target, handcuff) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, + BreakOnWeightlessMove = false, BreakOnDamage = true, - NeedHand = true + NeedHand = true, + DistanceThreshold = 0.3f }; if (!_doAfter.TryStartDoAfter(doAfterEventArgs)) @@ -606,11 +607,12 @@ public void TryUncuff(EntityUid target, EntityUid user, EntityUid? cuffsToRemove var doAfterEventArgs = new DoAfterArgs(EntityManager, user, uncuffTime, new UnCuffDoAfterEvent(), target, target, cuffsToRemove) { - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, + BreakOnWeightlessMove = false, BreakOnDamage = true, NeedHand = true, RequireCanInteract = false, // Trust in UncuffAttemptEvent + DistanceThreshold = 0.3f }; if (!_doAfter.TryStartDoAfter(doAfterEventArgs)) diff --git a/Content.Shared/Devour/SharedDevourSystem.cs b/Content.Shared/Devour/SharedDevourSystem.cs index 124daeffaaa..14047fba7dd 100644 --- a/Content.Shared/Devour/SharedDevourSystem.cs +++ b/Content.Shared/Devour/SharedDevourSystem.cs @@ -59,8 +59,7 @@ protected void OnDevourAction(EntityUid uid, DevourerComponent component, Devour _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, component.DevourTime, new DevourDoAfterEvent(), uid, target: target, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, }); break; default: @@ -78,8 +77,7 @@ protected void OnDevourAction(EntityUid uid, DevourerComponent component, Devour _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, component.StructureDevourTime, new DevourDoAfterEvent(), uid, target: target, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, }); } } diff --git a/Content.Shared/DoAfter/DoAfter.cs b/Content.Shared/DoAfter/DoAfter.cs index d999b370b3f..f77a6dc272e 100644 --- a/Content.Shared/DoAfter/DoAfter.cs +++ b/Content.Shared/DoAfter/DoAfter.cs @@ -82,12 +82,6 @@ public DoAfter(ushort index, DoAfterArgs args, TimeSpan startTime) { Index = index; - if (args.Target == null) - { - DebugTools.Assert(!args.BreakOnTargetMove); - args.BreakOnTargetMove = false; - } - Args = args; StartTime = startTime; } diff --git a/Content.Shared/DoAfter/DoAfterArgs.cs b/Content.Shared/DoAfter/DoAfterArgs.cs index 73334deddbe..d88f72c965f 100644 --- a/Content.Shared/DoAfter/DoAfterArgs.cs +++ b/Content.Shared/DoAfter/DoAfterArgs.cs @@ -92,29 +92,23 @@ public sealed partial class DoAfterArgs public bool BreakOnHandChange = true; /// - /// If do_after stops when the user moves + /// If do_after stops when the user or target moves /// - [DataField("breakOnUserMove")] - public bool BreakOnUserMove; - - /// - /// If this is true then any movement, even when weightless, will break the doafter. - /// When there is no gravity, BreakOnUserMove is ignored. If it is false to begin with nothing will change. - /// - [DataField("breakOnWeightlessMove")] - public bool BreakOnWeightlessMove; + [DataField] + public bool BreakOnMove; /// - /// If do_after stops when the target moves (if there is a target) + /// Whether to break on movement when the user is weightless. + /// This does nothing if is false. /// - [DataField("breakOnTargetMove")] - public bool BreakOnTargetMove; + [DataField] + public bool BreakOnWeightlessMove = true; /// /// Threshold for user and target movement /// [DataField("movementThreshold")] - public float MovementThreshold = 0.1f; + public float MovementThreshold = 0.3f; /// /// Threshold for distance user from the used OR target entities. @@ -254,9 +248,8 @@ public DoAfterArgs(DoAfterArgs other) Broadcast = other.Broadcast; NeedHand = other.NeedHand; BreakOnHandChange = other.BreakOnHandChange; - BreakOnUserMove = other.BreakOnUserMove; + BreakOnMove = other.BreakOnMove; BreakOnWeightlessMove = other.BreakOnWeightlessMove; - BreakOnTargetMove = other.BreakOnTargetMove; MovementThreshold = other.MovementThreshold; DistanceThreshold = other.DistanceThreshold; BreakOnDamage = other.BreakOnDamage; diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs index 24b38417596..4f77a271b37 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs @@ -1,5 +1,7 @@ using Content.Shared.Gravity; using Content.Shared.Hands.Components; +using Content.Shared.Interaction; +using Content.Shared.Physics; using Robust.Shared.Utility; namespace Content.Shared.DoAfter; @@ -8,6 +10,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem { [Dependency] private readonly IDynamicTypeFactory _factory = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; private DoAfter[] _doAfters = Array.Empty(); @@ -164,24 +167,53 @@ private bool ShouldCancel(DoAfter doAfter, return true; // TODO: Re-use existing xform query for these calculations. - // when there is no gravity you will be drifting 99% of the time making many doafters impossible - // so this just ignores your movement if you are weightless (unless the doafter sets BreakOnWeightlessMove then moving will still break it) - if (args.BreakOnUserMove - && !userXform.Coordinates.InRange(EntityManager, _transform, doAfter.UserPosition, args.MovementThreshold) - && (args.BreakOnWeightlessMove || !_gravity.IsWeightless(args.User, xform: userXform))) - return true; - - if (args.BreakOnTargetMove) + if (args.BreakOnMove && !(!args.BreakOnWeightlessMove && _gravity.IsWeightless(args.User, xform: userXform))) { - DebugTools.Assert(targetXform != null, "Break on move is true, but no target specified?"); - if (targetXform != null && targetXform.Coordinates.TryDistance(EntityManager, userXform.Coordinates, out var distance)) + // Whether the user has moved too much from their original position. + if (!userXform.Coordinates.InRange(EntityManager, _transform, doAfter.UserPosition, args.MovementThreshold)) + return true; + + // Whether the distance between the user and target(if any) has changed too much. + if (targetXform != null && + targetXform.Coordinates.TryDistance(EntityManager, userXform.Coordinates, out var distance)) { - // once the target moves too far from you the do after breaks if (Math.Abs(distance - doAfter.TargetDistance) > args.MovementThreshold) return true; } } + // Whether the user and the target are too far apart. + if (args.Target != null) + { + if (args.DistanceThreshold != null) + { + if (!_interaction.InRangeUnobstructed(args.User, args.Target.Value, args.DistanceThreshold.Value)) + return true; + } + else + { + if (!_interaction.InRangeUnobstructed(args.User, args.Target.Value)) + return true; + } + } + + // Whether the distance between the tool and the user has grown too much. + if (args.Used != null) + { + if (args.DistanceThreshold != null) + { + if (!_interaction.InRangeUnobstructed(args.User, + args.Used.Value, + args.DistanceThreshold.Value)) + return true; + } + else + { + if (!_interaction.InRangeUnobstructed(args.User,args.Used.Value)) + return true; + } + } + if (args.AttemptFrequency == AttemptFrequency.EveryTick && !TryAttemptEvent(doAfter)) return true; @@ -200,23 +232,6 @@ private bool ShouldCancel(DoAfter doAfter, if (args.RequireCanInteract && !_actionBlocker.CanInteract(args.User, args.Target)) return true; - if (args.DistanceThreshold != null) - { - if (targetXform != null - && !args.User.Equals(args.Target) - && !userXform.Coordinates.InRange(EntityManager, _transform, targetXform.Coordinates, - args.DistanceThreshold.Value)) - { - return true; - } - - if (usedXform != null - && !userXform.Coordinates.InRange(EntityManager, _transform, usedXform.Coordinates, - args.DistanceThreshold.Value)) - { - return true; - } - } return false; } diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.cs index 81c8c4f3823..ed8be1ad657 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.cs @@ -215,12 +215,11 @@ public bool TryStartDoAfter(DoAfterArgs args, [NotNullWhen(true)] out DoAfterId? args.NetUser = GetNetEntity(args.User); args.NetEventTarget = GetNetEntity(args.EventTarget); - if (args.BreakOnUserMove || args.BreakOnTargetMove) + if (args.BreakOnMove) doAfter.UserPosition = Transform(args.User).Coordinates; - if (args.Target != null && args.BreakOnTargetMove) + if (args.Target != null && args.BreakOnMove) { - // Target should never be null if the bool is set. var targetPosition = Transform(args.Target.Value).Coordinates; doAfter.UserPosition.TryDistance(EntityManager, targetPosition, out doAfter.TargetDistance); } diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index 12435eba890..f6879bb1a5d 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -169,8 +169,7 @@ public bool TryEquip(EntityUid actor, EntityUid target, EntityUid itemUid, strin { BlockDuplicate = true, BreakOnHandChange = true, - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, CancelDuplicate = true, RequireCanInteract = true, NeedHand = true @@ -418,8 +417,7 @@ public bool TryUnequip( { BlockDuplicate = true, BreakOnHandChange = true, - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, CancelDuplicate = true, RequireCanInteract = true, NeedHand = true diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index bfdfc77bc21..9735d71a519 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -121,7 +121,7 @@ public bool TryLock(EntityUid uid, EntityUid user, LockComponent? lockComp = nul return _doAfter.TryStartDoAfter( new DoAfterArgs(EntityManager, user, lockComp.LockTime, new LockDoAfter(), uid, uid) { - BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, RequireCanInteract = true, + BreakOnDamage = true, BreakOnMove = true, RequireCanInteract = true, NeedHand = true }); } @@ -197,7 +197,7 @@ public bool TryUnlock(EntityUid uid, EntityUid user, LockComponent? lockComp = n return _doAfter.TryStartDoAfter( new DoAfterArgs(EntityManager, user, lockComp.LockTime, new UnlockDoAfter(), uid, uid) { - BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, RequireCanInteract = true, + BreakOnDamage = true, BreakOnMove = true, RequireCanInteract = true, NeedHand = true }); } diff --git a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs index 04926c34562..73b7c0847fc 100644 --- a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs +++ b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs @@ -434,7 +434,7 @@ private void OnDragDrop(EntityUid uid, MechComponent component, ref DragDropTarg var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Dragged, component.EntryDelay, new MechEntryEvent(), uid, target: uid) { - BreakOnUserMove = true, + BreakOnMove = true, }; _doAfter.TryStartDoAfter(doAfterEventArgs); diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 960dab84611..31a305dba99 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -90,8 +90,7 @@ private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent compon new RemoveEmbeddedProjectileEvent(), eventTarget: uid, target: uid) { DistanceThreshold = SharedInteractionSystem.InteractionRange, - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, NeedHand = true, }); } diff --git a/Content.Shared/Prying/Systems/PryingSystem.cs b/Content.Shared/Prying/Systems/PryingSystem.cs index a59c09ca535..ab87585c706 100644 --- a/Content.Shared/Prying/Systems/PryingSystem.cs +++ b/Content.Shared/Prying/Systems/PryingSystem.cs @@ -137,8 +137,7 @@ private bool StartPry(EntityUid target, EntityUid user, EntityUid? tool, float t var doAfterArgs = new DoAfterArgs(EntityManager, user, TimeSpan.FromSeconds(modEv.BaseTime * modEv.PryTimeModifier / toolModifier), new DoorPryDoAfterEvent(), target, target, tool) { BreakOnDamage = true, - BreakOnUserMove = true, - BreakOnWeightlessMove = true, + BreakOnMove = true, }; if (tool != null) diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index bbdfc5d0218..f2c18f43f25 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -211,8 +211,7 @@ private void OnAfterInteract(EntityUid uid, RCDComponent component, AfterInterac { BreakOnDamage = true, BreakOnHandChange = true, - BreakOnUserMove = true, - BreakOnTargetMove = args.Target != null, + BreakOnMove = true, AttemptFrequency = AttemptFrequency.EveryTick, CancelDuplicate = false, BlockDuplicate = false, diff --git a/Content.Shared/RatKing/SharedRatKingSystem.cs b/Content.Shared/RatKing/SharedRatKingSystem.cs index d7a8ee5460d..ea489e332db 100644 --- a/Content.Shared/RatKing/SharedRatKingSystem.cs +++ b/Content.Shared/RatKing/SharedRatKingSystem.cs @@ -119,7 +119,7 @@ private void OnGetVerb(EntityUid uid, RatKingRummageableComponent component, Get { BlockDuplicate = true, BreakOnDamage = true, - BreakOnUserMove = true, + BreakOnMove = true, DistanceThreshold = 2f }); } diff --git a/Content.Shared/Research/Systems/SharedResearchStealerSystem.cs b/Content.Shared/Research/Systems/SharedResearchStealerSystem.cs index 1fffa8c0257..070ff40b70d 100644 --- a/Content.Shared/Research/Systems/SharedResearchStealerSystem.cs +++ b/Content.Shared/Research/Systems/SharedResearchStealerSystem.cs @@ -46,8 +46,8 @@ private void OnBeforeInteractHand(EntityUid uid, ResearchStealerComponent comp, var doAfterArgs = new DoAfterArgs(EntityManager, uid, comp.Delay, new ResearchStealDoAfterEvent(), target: target, used: uid, eventTarget: uid) { BreakOnDamage = true, - BreakOnUserMove = true, - MovementThreshold = 0.5f + BreakOnMove = true, + MovementThreshold = 0.5f, }; _doAfter.TryStartDoAfter(doAfterArgs); diff --git a/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs b/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs index 1ada22876b6..f94558b0b30 100644 --- a/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs +++ b/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs @@ -155,8 +155,7 @@ private void OnFultonInteract(EntityUid uid, FultonComponent component, AfterInt { CancelDuplicate = true, MovementThreshold = 0.5f, - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, Broadcast = true, NeedHand = true, }); diff --git a/Content.Shared/Sericulture/SericultureSystem.cs b/Content.Shared/Sericulture/SericultureSystem.cs index 514ec79f68e..f7586cc1ec3 100644 --- a/Content.Shared/Sericulture/SericultureSystem.cs +++ b/Content.Shared/Sericulture/SericultureSystem.cs @@ -61,7 +61,7 @@ private void OnSericultureStart(EntityUid uid, SericultureComponent comp, Sericu var doAfter = new DoAfterArgs(EntityManager, uid, comp.ProductionLength, new SericultureDoAfterEvent(), uid) { // I'm not sure if more things should be put here, but imo ideally it should probably be set in the component/YAML. Not sure if this is currently possible. - BreakOnUserMove = true, + BreakOnMove = true, BlockDuplicate = true, BreakOnDamage = true, CancelDuplicate = true, diff --git a/Content.Shared/Species/Systems/ReformSystem.cs b/Content.Shared/Species/Systems/ReformSystem.cs index dc2ce3fb900..d2ceecf28e8 100644 --- a/Content.Shared/Species/Systems/ReformSystem.cs +++ b/Content.Shared/Species/Systems/ReformSystem.cs @@ -69,7 +69,7 @@ private void OnReform(EntityUid uid, ReformComponent comp, ReformEvent args) // Create a doafter & start it var doAfter = new DoAfterArgs(EntityManager, uid, comp.ReformTime, new ReformDoAfterEvent(), uid) { - BreakOnUserMove = true, + BreakOnMove = true, BlockDuplicate = true, BreakOnDamage = true, CancelDuplicate = true, @@ -89,7 +89,7 @@ private void OnDoAfter(EntityUid uid, ReformComponent comp, ReformDoAfterEvent a return; // Spawn a new entity - // This is, to an extent, taken from polymorph. I don't use polymorph for various reasons- most notably that this is permanent. + // This is, to an extent, taken from polymorph. I don't use polymorph for various reasons- most notably that this is permanent. var child = Spawn(comp.ReformPrototype, Transform(uid).Coordinates); // This transfers the mind to the new entity @@ -105,7 +105,7 @@ private void OnZombified(EntityUid uid, ReformComponent comp, ref EntityZombifie _actionsSystem.RemoveAction(uid, comp.ActionEntity); // Zombies can't reform } - public sealed partial class ReformEvent : InstantActionEvent { } + public sealed partial class ReformEvent : InstantActionEvent { } [Serializable, NetSerializable] public sealed partial class ReformDoAfterEvent : SimpleDoAfterEvent { } diff --git a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs index fa04a50f8b0..feb1cebb8e1 100644 --- a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs +++ b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs @@ -134,8 +134,7 @@ private void OnAirlockInteract(Entity ent, ref Intera var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.AirlockSprayTime, new SprayPainterDoorDoAfterEvent(sprite, style.Department), args.Used, target: ent, used: args.Used) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, NeedHand = true }; diff --git a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs index da7c3aa89a0..91acde47e19 100644 --- a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs +++ b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs @@ -132,8 +132,7 @@ private void StartDoAfter(EntityUid storageUid, EntityUid targetUid, EntityUid u _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, userUid, delay, new DumpableDoAfterEvent(), storageUid, target: targetUid, used: storageUid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }); } diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 9fd8bdf5e7b..d149ed5e0ab 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -430,7 +430,7 @@ private void AfterInteract(EntityUid uid, StorageComponent storageComp, AfterInt var doAfterArgs = new DoAfterArgs(EntityManager, args.User, delay, new AreaPickupDoAfterEvent(GetNetEntityList(_entList)), uid, target: uid) { BreakOnDamage = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }; diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.cs b/Content.Shared/Tools/Systems/SharedToolSystem.cs index 1336fe1527c..a134e03aac6 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.cs @@ -141,8 +141,8 @@ public bool UseTool( var doAfterArgs = new DoAfterArgs(EntityManager, user, delay / toolComponent.SpeedModifier, toolEvent, tool, target: target, used: tool) { BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, + BreakOnWeightlessMove = false, NeedHand = tool != user, AttemptFrequency = fuel > 0 ? AttemptFrequency.EveryTick : AttemptFrequency.Never }; diff --git a/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs b/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs index 87e2f0890a2..6aef4d0949a 100644 --- a/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs +++ b/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs @@ -70,8 +70,7 @@ private void OnAfterInteract(EntityUid uid, VendingMachineRestockComponent compo var doAfterArgs = new DoAfterArgs(EntityManager, args.User, (float) component.RestockDelay.TotalSeconds, new RestockDoAfterEvent(), target, target: target, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, NeedHand = true }; diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 88acd81d5bd..4998b11f082 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -77,8 +77,7 @@ private void OnBallisticAfterInteract(EntityUid uid, BallisticAmmoProviderCompon _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.FillDelay, new AmmoFillDoAfterEvent(), used: uid, target: args.Target, eventTarget: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = false, NeedHand = true }); From d68532a953df9d50a3af48de8eb8990fbce9194e Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 03:25:56 -0400 Subject: [PATCH 031/366] mew --- Content.Server/Medical/DefibrillatorSystem.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index 9f145173aca..3834ae6db9c 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -174,10 +174,13 @@ public void Zap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorCo if (targetEvent.Cancelled || !CanZap(uid, target, user, component, true)) return; - if (!TryComp(target, out var mob) || - !TryComp(target, out var thresholds)) + if (!TryComp(target, out var mobState) || + !TryComp(target, out var mobThresholds)) return; + mob = mobState; + thresholds = mobThresholds; + _audio.PlayPvs(component.ZapSound, uid); _electrocution.TryDoElectrocution(target, null, component.ZapDamage, component.WritheDuration, true, ignoreInsulation: true); component.NextZapTime = _timing.CurTime + component.ZapDelay; From 08e15ef5e347a49983acfdb542c788bc6d3f317d Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Tue, 21 May 2024 05:11:49 -0700 Subject: [PATCH 032/366] Add defib event, add fields to be able to disable crit defib and do after movement (#28174) * Add defib event, add fields to be able to disable crit defib and do after movement * Fix check --- Content.Server/Medical/DefibrillatorSystem.cs | 3 ++- Content.Shared/Medical/DefibrillatorComponent.cs | 6 ++++++ Content.Shared/Medical/TargetDefibrillatedEvent.cs | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Content.Shared/Medical/TargetDefibrillatedEvent.cs diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index 3834ae6db9c..264df0e230b 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -145,7 +145,8 @@ public bool TryStartZap(EntityUid uid, EntityUid target, EntityUid user, Defibri { BlockDuplicate = true, BreakOnHandChange = true, - NeedHand = true + NeedHand = true, + BreakOnMove = !component.AllowDoAfterMovement }); } diff --git a/Content.Shared/Medical/DefibrillatorComponent.cs b/Content.Shared/Medical/DefibrillatorComponent.cs index 1d6f690bc3b..e4cd8077d26 100644 --- a/Content.Shared/Medical/DefibrillatorComponent.cs +++ b/Content.Shared/Medical/DefibrillatorComponent.cs @@ -55,6 +55,12 @@ public sealed partial class DefibrillatorComponent : Component [DataField("doAfterDuration"), ViewVariables(VVAccess.ReadWrite)] public TimeSpan DoAfterDuration = TimeSpan.FromSeconds(3); + [DataField] + public bool AllowDoAfterMovement = true; + + [DataField] + public bool CanDefibCrit = true; + /// /// The sound when someone is zapped. /// diff --git a/Content.Shared/Medical/TargetDefibrillatedEvent.cs b/Content.Shared/Medical/TargetDefibrillatedEvent.cs new file mode 100644 index 00000000000..60d1a215845 --- /dev/null +++ b/Content.Shared/Medical/TargetDefibrillatedEvent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared.Medical; + +[ByRefEvent] +public readonly record struct TargetDefibrillatedEvent(EntityUid User, Entity Defibrillator); From 86dcabab12322dec5e959ff36b77739eb044b3bf Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 03:28:52 -0400 Subject: [PATCH 033/366] c --- Content.Shared/Medical/DefibrillatorEvents.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Content.Shared/Medical/DefibrillatorEvents.cs b/Content.Shared/Medical/DefibrillatorEvents.cs index 54a21a40d42..12956d924f7 100644 --- a/Content.Shared/Medical/DefibrillatorEvents.cs +++ b/Content.Shared/Medical/DefibrillatorEvents.cs @@ -2,9 +2,6 @@ namespace Content.Shared.Medical; -[ByRefEvent] -public readonly record struct TargetDefibrillatedEvent(EntityUid User, Entity Defibrillator); - public abstract class BeforeDefibrillatorZapsEvent : CancellableEntityEventArgs, IInventoryRelayEvent { public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET; From acf597efa5e69b89bbc6091b33df7beafc811011 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 03:38:01 -0400 Subject: [PATCH 034/366] mew --- Content.Server/Language/TranslatorSystem.cs | 4 +- .../EntitySystems/EmergencyLightSystem.cs | 3 +- .../Kitchen/EntitySystems/DeepFryerSystem.cs | 4 +- .../ReverseEngineeringSystem.cs | 1 + .../Paper/PaperRandomStoryComponent.cs | 4 - .../Psionics/Glimmer/GlimmerReactiveSystem.cs | 1 + .../Structures/GlimmerStructuresSystem.cs | 1 + Content.Server/Silicons/Laws/SiliconLawEui.cs | 2 +- Content.Shared.Database/LogType.cs | 4 +- .../Cabinet/ItemCabinetComponent.cs | 1 + Content.Shared/Clothing/MagbootsSystem.cs | 2 +- .../Interaction/SharedInteractionSystem.cs | 16 +- .../EntitySystems/SharedStorageSystem.cs | 333 ++++++++++-------- 13 files changed, 206 insertions(+), 170 deletions(-) diff --git a/Content.Server/Language/TranslatorSystem.cs b/Content.Server/Language/TranslatorSystem.cs index 5cb5c8cd2e9..133146ec064 100644 --- a/Content.Server/Language/TranslatorSystem.cs +++ b/Content.Server/Language/TranslatorSystem.cs @@ -102,7 +102,7 @@ private void OnTranslatorToggle(EntityUid translator, HandheldTranslatorComponen var isEnabled = !translatorComp.Enabled && hasPower; translatorComp.Enabled = isEnabled; - _powerCell.SetPowerCellDrawEnabled(translator, isEnabled); + _powerCell.SetDrawEnabled(translator, isEnabled); if (_containers.TryGetContainingContainer(translator, out var holderCont) && holderCont.Owner is var holder @@ -130,7 +130,7 @@ private void OnTranslatorToggle(EntityUid translator, HandheldTranslatorComponen private void OnPowerCellSlotEmpty(EntityUid translator, HandheldTranslatorComponent component, PowerCellSlotEmptyEvent args) { component.Enabled = false; - _powerCell.SetPowerCellDrawEnabled(translator, false); + _powerCell.SetDrawEnabled(translator, false); OnAppearanceChange(translator, component); if (_containers.TryGetContainingContainer(translator, out var holderCont) && HasComp(holderCont.Owner)) diff --git a/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs b/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs index 154a4081259..498fe3b6f4b 100644 --- a/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs +++ b/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs @@ -9,7 +9,6 @@ using Content.Shared.Light; using Content.Shared.Light.Components; using Content.Shared.Power; -using Content.Shared.Station.Components; using Robust.Server.GameObjects; using Color = Robust.Shared.Maths.Color; @@ -236,6 +235,6 @@ private void TurnOn(Entity entity, Color color) _pointLight.SetColor(entity.Owner, color); _appearance.SetData(entity.Owner, EmergencyLightVisuals.Color, color); _appearance.SetData(entity.Owner, EmergencyLightVisuals.On, true); - _ambient.SetAmbience(entity.Owner, true); + _ambient.SetAmbience(entity.Owner, true); } } diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs index 8bfb4aec8cc..1e030a9f3db 100644 --- a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs +++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs @@ -42,6 +42,7 @@ using Content.Shared.Nyanotrasen.Kitchen.Components; using Content.Shared.Nyanotrasen.Kitchen.UI; using Content.Shared.Popups; +using Content.Shared.Power; using Content.Shared.Throwing; using Content.Shared.UserInterface; using Content.Shared.Whitelist; @@ -644,8 +645,7 @@ private void OnClearSlagStart(EntityUid uid, DeepFryerComponent component, DeepF var doAfterArgs = new DoAfterArgs(EntityManager, user, delay, ev, uid, uid, heldItem) { BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, MovementThreshold = 0.25f, NeedHand = true }; diff --git a/Content.Server/Nyanotrasen/ReverseEngineering/ReverseEngineeringSystem.cs b/Content.Server/Nyanotrasen/ReverseEngineering/ReverseEngineeringSystem.cs index 88da3093d74..98ce725f582 100644 --- a/Content.Server/Nyanotrasen/ReverseEngineering/ReverseEngineeringSystem.cs +++ b/Content.Server/Nyanotrasen/ReverseEngineering/ReverseEngineeringSystem.cs @@ -7,6 +7,7 @@ using Content.Server.Power.Components; using Content.Server.Construction; using Content.Server.Popups; +using Content.Shared.Power; using Content.Shared.UserInterface; using Robust.Shared.Containers; using Robust.Shared.Random; diff --git a/Content.Server/Paper/PaperRandomStoryComponent.cs b/Content.Server/Paper/PaperRandomStoryComponent.cs index e5eb3860024..7c5744f0878 100644 --- a/Content.Server/Paper/PaperRandomStoryComponent.cs +++ b/Content.Server/Paper/PaperRandomStoryComponent.cs @@ -1,7 +1,3 @@ -using Content.Shared.Paper; -using Content.Shared.StoryGen; -using Robust.Shared.Prototypes; - namespace Content.Server.Paper; /// diff --git a/Content.Server/Psionics/Glimmer/GlimmerReactiveSystem.cs b/Content.Server/Psionics/Glimmer/GlimmerReactiveSystem.cs index f828874aacb..85aea76bd65 100644 --- a/Content.Server/Psionics/Glimmer/GlimmerReactiveSystem.cs +++ b/Content.Server/Psionics/Glimmer/GlimmerReactiveSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Destructible; using Content.Shared.Construction.Components; using Content.Shared.Mind.Components; +using Content.Shared.Power; using Content.Shared.Weapons.Melee.Components; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; diff --git a/Content.Server/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs b/Content.Server/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs index 8694147dc0e..c0c11b083ce 100644 --- a/Content.Server/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs +++ b/Content.Server/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Anomaly.Components; +using Content.Shared.Power; using Content.Shared.Psionics.Glimmer; namespace Content.Server.Psionics.Glimmer diff --git a/Content.Server/Silicons/Laws/SiliconLawEui.cs b/Content.Server/Silicons/Laws/SiliconLawEui.cs index d5a5c4f4091..1d3c57b7cb7 100644 --- a/Content.Server/Silicons/Laws/SiliconLawEui.cs +++ b/Content.Server/Silicons/Laws/SiliconLawEui.cs @@ -59,7 +59,7 @@ public override void HandleMessage(EuiMessageBase msg) private bool IsAllowed() { var adminData = _adminManager.GetAdminData(Player); - if (adminData == null || !adminData.HasFlag(AdminFlags.Moderator)) + if (adminData == null || !adminData.HasFlag(AdminFlags.Admin)) { _sawmill.Warning("Player {0} tried to open / use silicon law UI without permission.", Player.UserId); return false; diff --git a/Content.Shared.Database/LogType.cs b/Content.Shared.Database/LogType.cs index dad042f9706..fc2dad677a7 100644 --- a/Content.Shared.Database/LogType.cs +++ b/Content.Shared.Database/LogType.cs @@ -105,5 +105,7 @@ public enum LogType /// /// This is a default value used by PlayerRateLimitManager, though users can use different log types. /// - RateLimited = 91 + RateLimited = 91, + InteractUsing = 92, + Storage = 93 } diff --git a/Content.Shared/Cabinet/ItemCabinetComponent.cs b/Content.Shared/Cabinet/ItemCabinetComponent.cs index 0ea30a8ca54..dcc276e5651 100644 --- a/Content.Shared/Cabinet/ItemCabinetComponent.cs +++ b/Content.Shared/Cabinet/ItemCabinetComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Containers.ItemSlots; +using Robust.Shared.Audio; using Robust.Shared.GameStates; namespace Content.Shared.Cabinet; diff --git a/Content.Shared/Clothing/MagbootsSystem.cs b/Content.Shared/Clothing/MagbootsSystem.cs index d41f27fefb0..ef5ac671e42 100644 --- a/Content.Shared/Clothing/MagbootsSystem.cs +++ b/Content.Shared/Clothing/MagbootsSystem.cs @@ -76,7 +76,7 @@ private void OnIsWeightless(Entity ent, ref IsWeightlessEvent return; // do not cancel weightlessness if the person is in off-grid. - if (ent.Comp.RequiresGrid && !_gravity.EntityOnGravitySupportingGridOrMap(ent.Owner)) + if (ent.Comp.RequiresGrid && !_gravity.IsWeightless(ent.Owner)) return; args.IsWeightless = false; diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index d325de92c83..97af3982d70 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -979,7 +979,7 @@ public bool InteractUsing( bool checkCanUse = true) { if (IsDeleted(user) || IsDeleted(used) || IsDeleted(target)) - return; + return false; if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) return false; @@ -1019,10 +1019,16 @@ public bool InteractUsing( /// /// /// - /// Whether the is in range of the . - /// + /// Whether the is in range of the . + /// Whether we should check if any entities were deleted. /// True if the interaction was handled. Otherwise, false. - public bool InteractDoAfter(EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool canReach) + public bool InteractDoAfter( + EntityUid user, + EntityUid used, + EntityUid? target, + EntityCoordinates clickLocation, + bool canReach, + bool checkDeletion = false) { if (target is { Valid: false }) target = null; @@ -1051,7 +1057,7 @@ public bool InteractDoAfter(EntityUid user, EntityUid used, EntityUid? target, E if (canReach) DoContactInteraction(user, target, afterInteractUsingEvent); // Contact interactions are currently only used for forensics, so we don't raise used -> target - + if (afterInteractUsingEvent.Handled) return true; return false; diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index d149ed5e0ab..183420db9d1 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -2,7 +2,9 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.ActionBlocker; +using Content.Shared.Administration.Logs; using Content.Shared.Containers.ItemSlots; +using Content.Shared.Database; using Content.Shared.Destructible; using Content.Shared.DoAfter; using Content.Shared.Hands.Components; @@ -14,7 +16,6 @@ using Content.Shared.Inventory; using Content.Shared.Item; using Content.Shared.Lock; -using Content.Shared.Nyanotrasen.Item.PseudoItem; using Content.Shared.Materials; using Content.Shared.Placeable; using Content.Shared.Popups; @@ -24,6 +25,7 @@ using Content.Shared.Storage.Events; using Content.Shared.Verbs; using Content.Shared.Whitelist; +using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -39,25 +41,26 @@ namespace Content.Shared.Storage.EntitySystems; public abstract class SharedStorageSystem : EntitySystem { - [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] protected readonly IRobustRandom Random = default!; [Dependency] protected readonly ActionBlockerSystem ActionBlocker = default!; - [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] protected readonly SharedEntityStorageSystem EntityStorage = default!; - [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; - [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] protected readonly SharedItemSystem ItemSystem = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!; - [Dependency] private readonly SharedStackSystem _stack = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!; + [Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; - [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; [Dependency] protected readonly UseDelaySystem UseDelay = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLog = default!; private EntityQuery _itemQuery; private EntityQuery _stackQuery; @@ -67,6 +70,9 @@ public abstract class SharedStorageSystem : EntitySystem public const string DefaultStorageMaxItemSize = "Normal"; public const float AreaInsertDelayPerItem = 0.075f; + private static AudioParams _audioParams = AudioParams.Default + .WithMaxDistance(7f) + .WithVolume(-2f); private ItemSizePrototype _defaultStorageMaxItemSize = default!; @@ -98,6 +104,7 @@ public override void Initialize() subs.Event(OnBoundUIClosed); }); + SubscribeLocalEvent(OnRemove); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent>(AddUiVerb); SubscribeLocalEvent(OnStorageGetState); @@ -122,7 +129,6 @@ public override void Initialize() SubscribeAllEvent(OnInteractWithItem); SubscribeAllEvent(OnSetItemLocation); SubscribeAllEvent(OnInsertItemIntoLocation); - SubscribeAllEvent(OnRemoveItem); SubscribeAllEvent(OnSaveItemLocation); SubscribeLocalEvent(OnReclaimed); @@ -135,6 +141,11 @@ public override void Initialize() UpdatePrototypeCache(); } + private void OnRemove(Entity entity, ref ComponentRemove args) + { + _ui.CloseUi(entity.Owner, StorageComponent.StorageUiKey.Key); + } + private void OnMapInit(Entity entity, ref MapInitEvent args) { UseDelay.SetLength(entity.Owner, entity.Comp.QuickInsertCooldown, QuickInsertUseDelayID); @@ -155,7 +166,9 @@ private void OnStorageGetState(EntityUid uid, StorageComponent component, ref Co Grid = new List(component.Grid), MaxItemSize = component.MaxItemSize, StoredItems = storedItems, - SavedLocations = component.SavedLocations + SavedLocations = component.SavedLocations, + Whitelist = component.Whitelist, + Blacklist = component.Blacklist }; } @@ -167,6 +180,8 @@ private void OnStorageHandleState(EntityUid uid, StorageComponent component, ref component.Grid.Clear(); component.Grid.AddRange(state.Grid); component.MaxItemSize = state.MaxItemSize; + component.Whitelist = state.Whitelist; + component.Blacklist = state.Blacklist; component.StoredItems.Clear(); @@ -348,7 +363,9 @@ private void OnInteractUsing(EntityUid uid, StorageComponent storageComp, Intera if (args.Handled || !CanInteract(args.User, (uid, storageComp), storageComp.ClickInsert, false)) return; - if (HasComp(uid)) + var attemptEv = new StorageInteractUsingAttemptEvent(); + RaiseLocalEvent(uid, ref attemptEv); + if (attemptEv.Cancelled) return; PlayerInsertHeldEntity((uid, storageComp), args.User); @@ -363,14 +380,18 @@ private void OnInteractUsing(EntityUid uid, StorageComponent storageComp, Intera /// private void OnActivate(EntityUid uid, StorageComponent storageComp, ActivateInWorldEvent args) { - if (args.Handled || !CanInteract(args.User, (uid, storageComp), storageComp.ClickInsert)) + if (args.Handled || !args.Complex || !CanInteract(args.User, (uid, storageComp), storageComp.ClickInsert)) return; // Toggle if (_ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.User)) + { _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.User); + } else + { OpenStorageUI(uid, args.User, storageComp, false); + } args.Handled = true; } @@ -383,7 +404,13 @@ private void OnImplantActivate(EntityUid uid, StorageComponent storageComp, Open if (args.Handled) return; - OpenStorageUI(uid, args.Performer, storageComp, false); + var uiOpen = _ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.Performer); + + if (uiOpen) + _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.Performer); + else + OpenStorageUI(uid, args.Performer, storageComp, false); + args.Handled = true; } @@ -425,13 +452,13 @@ private void AfterInteract(EntityUid uid, StorageComponent storageComp, AfterInt } //If there's only one then let's be generous - if (_entList.Count > 1) + if (_entList.Count >= 1) { var doAfterArgs = new DoAfterArgs(EntityManager, args.User, delay, new AreaPickupDoAfterEvent(GetNetEntityList(_entList)), uid, target: uid) { BreakOnDamage = true, BreakOnMove = true, - NeedHand = true + NeedHand = true, }; _doAfterSystem.TryStartDoAfter(doAfterArgs); @@ -454,7 +481,7 @@ private void AfterInteract(EntityUid uid, StorageComponent storageComp, AfterInt return; } - if (_xformQuery.TryGetComponent(uid, out var transformOwner) && TryComp(target, out var transformEnt)) + if (TryComp(uid, out TransformComponent? transformOwner) && TryComp(target, out TransformComponent? transformEnt)) { var parent = transformOwner.ParentUid; @@ -519,7 +546,7 @@ private void OnDoAfter(EntityUid uid, StorageComponent component, AreaPickupDoAf var angle = targetXform.LocalRotation; - if (PlayerInsertEntityInWorld((uid, component), args.Args.User, entity)) + if (PlayerInsertEntityInWorld((uid, component), args.Args.User, entity, playSound: false)) { successfullyInserted.Add(entity); successfullyInsertedPositions.Add(position); @@ -530,7 +557,7 @@ private void OnDoAfter(EntityUid uid, StorageComponent component, AreaPickupDoAf // If we picked up at least one thing, play a sound and do a cool animation! if (successfullyInserted.Count > 0) { - Audio.PlayPredicted(component.StorageInsertSound, uid, args.User); + Audio.PlayPredicted(component.StorageInsertSound, uid, args.User, _audioParams); EntityManager.RaiseSharedEvent(new AnimateInsertingEntitiesEvent( GetNetEntity(uid), GetNetEntityList(successfullyInserted), @@ -561,42 +588,31 @@ private void OnDestroy(EntityUid uid, StorageComponent storageComp, DestructionE /// private void OnInteractWithItem(StorageInteractWithItemEvent msg, EntitySessionEventArgs args) { - if (args.SenderSession.AttachedEntity is not { } player) - return; - - var uid = GetEntity(msg.StorageUid); - var entity = GetEntity(msg.InteractedItemUid); - - if (!TryComp(uid, out var storageComp)) - return; - - if (!_ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, player)) - return; - - if (!Exists(entity)) - { - Log.Error($"Player {args.SenderSession} interacted with non-existent item {msg.InteractedItemUid} stored in {ToPrettyString(uid)}"); - return; - } - - if (!ActionBlocker.CanInteract(player, entity) || !storageComp.Container.Contains(entity)) - return; - - // Does the player have hands? - if (!TryComp(player, out HandsComponent? hands) || hands.Count == 0) + if (!ValidateInput(args, msg.StorageUid, msg.InteractedItemUid, out var player, out var storage, out var item)) return; // If the user's active hand is empty, try pick up the item. - if (hands.ActiveHandEntity == null) + if (player.Comp.ActiveHandEntity == null) { - if (_sharedHandsSystem.TryPickupAnyHand(player, entity, handsComp: hands) - && storageComp.StorageRemoveSound != null) - Audio.PlayPredicted(storageComp.StorageRemoveSound, uid, player); + _adminLog.Add( + LogType.Storage, + LogImpact.Low, + $"{ToPrettyString(player):player} is attempting to take {ToPrettyString(item):item} out of {ToPrettyString(storage):storage}"); + + if (_sharedHandsSystem.TryPickupAnyHand(player, item, handsComp: player.Comp) + && storage.Comp.StorageRemoveSound != null) { - return; + Audio.PlayPredicted(storage.Comp.StorageRemoveSound, storage, player, _audioParams); } + + return; } + _adminLog.Add( + LogType.Storage, + LogImpact.Low, + $"{ToPrettyString(player):player} is interacting with {ToPrettyString(item):item} while it is stored in {ToPrettyString(storage):storage} using {ToPrettyString(player.Comp.ActiveHandEntity):used}"); + // Else, interact using the held item if (_interactionSystem.InteractUsing(player, player.Comp.ActiveHandEntity.Value, @@ -611,109 +627,35 @@ private void OnInteractWithItem(StorageInteractWithItemEvent msg, EntitySessionE private void OnSetItemLocation(StorageSetItemLocationEvent msg, EntitySessionEventArgs args) { - if (args.SenderSession.AttachedEntity is not { } player) - return; - - var storageEnt = GetEntity(msg.StorageEnt); - var itemEnt = GetEntity(msg.ItemEnt); - - if (!TryComp(storageEnt, out var storageComp)) - return; - - if (!_ui.IsUiOpen(storageEnt, StorageComponent.StorageUiKey.Key, player)) - return; - - if (!Exists(itemEnt)) - { - Log.Error($"Player {args.SenderSession} set location of non-existent item {msg.ItemEnt} stored in {ToPrettyString(storageEnt)}"); - return; - } - - if (!ActionBlocker.CanInteract(player, itemEnt)) - return; - - TrySetItemStorageLocation((itemEnt, null), (storageEnt, storageComp), msg.Location); - } - - private void OnRemoveItem(StorageRemoveItemEvent msg, EntitySessionEventArgs args) - { - if (args.SenderSession.AttachedEntity is not { } player) - return; - - var storageEnt = GetEntity(msg.StorageEnt); - var itemEnt = GetEntity(msg.ItemEnt); - - if (!TryComp(storageEnt, out var storageComp)) + if (!ValidateInput(args, msg.StorageEnt, msg.ItemEnt, out var player, out var storage, out var item)) return; - if (!_ui.IsUiOpen(storageEnt, StorageComponent.StorageUiKey.Key, player)) - return; - - if (!Exists(itemEnt)) - { - Log.Error($"Player {args.SenderSession} set location of non-existent item {msg.ItemEnt} stored in {ToPrettyString(storageEnt)}"); - return; - } - - if (!ActionBlocker.CanInteract(player, itemEnt)) - return; + _adminLog.Add( + LogType.Storage, + LogImpact.Low, + $"{ToPrettyString(player):player} is updating the location of {ToPrettyString(item):item} within {ToPrettyString(storage):storage}"); - TransformSystem.DropNextTo(itemEnt, player); - Audio.PlayPredicted(storageComp.StorageRemoveSound, storageEnt, player); + TrySetItemStorageLocation(item!, storage!, msg.Location); } private void OnInsertItemIntoLocation(StorageInsertItemIntoLocationEvent msg, EntitySessionEventArgs args) { - if (args.SenderSession.AttachedEntity is not { } player) - return; - - var storageEnt = GetEntity(msg.StorageEnt); - var itemEnt = GetEntity(msg.ItemEnt); - - if (!TryComp(storageEnt, out var storageComp)) - return; - - if (!_ui.IsUiOpen(storageEnt, StorageComponent.StorageUiKey.Key, player)) + if (!ValidateInput(args, msg.StorageEnt, msg.ItemEnt, out var player, out var storage, out var item, held: true)) return; - if (!Exists(itemEnt)) - { - Log.Error($"Player {args.SenderSession} set location of non-existent item {msg.ItemEnt} stored in {ToPrettyString(storageEnt)}"); - return; - } - - if (!ActionBlocker.CanInteract(player, itemEnt) || !_sharedHandsSystem.IsHolding(player, itemEnt, out _)) - return; - - InsertAt((storageEnt, storageComp), (itemEnt, null), msg.Location, out _, player, stackAutomatically: false); + _adminLog.Add( + LogType.Storage, + LogImpact.Low, + $"{ToPrettyString(player):player} is inserting {ToPrettyString(item):item} into {ToPrettyString(storage):storage}"); + InsertAt(storage!, item!, msg.Location, out _, player, stackAutomatically: false); } - // TODO: if/when someone cleans up this shitcode please make all these - // handlers use a shared helper for checking that the ui is open etc, thanks private void OnSaveItemLocation(StorageSaveItemLocationEvent msg, EntitySessionEventArgs args) { - if (args.SenderSession.AttachedEntity is not {} player) - return; - - var storage = GetEntity(msg.Storage); - var item = GetEntity(msg.Item); - - if (!HasComp(storage)) - return; - - if (!_ui.IsUiOpen(storage, StorageComponent.StorageUiKey.Key, player)) - return; - - if (!Exists(item)) - { - Log.Error($"Player {args.SenderSession} saved location of non-existent item {msg.Item} stored in {ToPrettyString(storage)}"); - return; - } - - if (!ActionBlocker.CanInteract(player, item)) + if (!ValidateInput(args, msg.Storage, msg.Item, out var player, out var storage, out var item)) return; - SaveItemLocation(storage, item); + SaveItemLocation(storage!, item.Owner); } private void OnBoundUIOpen(EntityUid uid, StorageComponent storageComp, BoundUIOpenedEvent args) @@ -802,7 +744,11 @@ public void UpdateAppearance(Entity ent _appearance.SetData(uid, StorageVisuals.Capacity, capacity, appearance); _appearance.SetData(uid, StorageVisuals.Open, isOpen, appearance); _appearance.SetData(uid, SharedBagOpenVisuals.BagState, isOpen ? SharedBagState.Open : SharedBagState.Closed, appearance); - _appearance.SetData(uid, StackVisuals.Hide, !isOpen, appearance); + + // HideClosedStackVisuals true sets the StackVisuals.Hide to the open state of the storage. + // This is for containers that only show their contents when open. (e.g. donut boxes) + if (storage.HideStackVisualsWhenClosed) + _appearance.SetData(uid, StackVisuals.Hide, !isOpen, appearance); } /// @@ -825,13 +771,10 @@ public void TransferEntities(EntityUid source, EntityUid target, EntityUid? user foreach (var entity in entities.ToArray()) { - if (HasComp(entity)) // Nyanotrasen - They dont transfer properly - continue; - Insert(target, entity, out _, user: user, targetComp, playSound: false); } - Audio.PlayPredicted(sourceComp.StorageInsertSound, target, user); + Audio.PlayPredicted(sourceComp.StorageInsertSound, target, user, _audioParams); } /// @@ -1010,7 +953,7 @@ public bool Insert( return false; if (playSound) - Audio.PlayPredicted(storageComp.StorageInsertSound, uid, user); + Audio.PlayPredicted(storageComp.StorageInsertSound, uid, user, _audioParams); return true; } @@ -1040,7 +983,7 @@ public bool Insert( } if (playSound) - Audio.PlayPredicted(storageComp.StorageInsertSound, uid, user); + Audio.PlayPredicted(storageComp.StorageInsertSound, uid, user, _audioParams); return true; } @@ -1083,12 +1026,12 @@ public bool PlayerInsertHeldEntity(Entity ent, EntityThe player to insert an entity with /// /// true if inserted, false otherwise - public bool PlayerInsertEntityInWorld(Entity uid, EntityUid player, EntityUid toInsert) + public bool PlayerInsertEntityInWorld(Entity uid, EntityUid player, EntityUid toInsert, bool playSound = true) { if (!Resolve(uid, ref uid.Comp) || !_interactionSystem.InRangeUnobstructed(player, uid.Owner)) return false; - if (!Insert(uid, toInsert, out _, user: player, uid.Comp)) + if (!Insert(uid, toInsert, out _, user: player, uid.Comp, playSound: playSound)) { _popupSystem.PopupClient(Loc.GetString("comp-storage-cant-insert"), uid, player); return false; @@ -1390,7 +1333,12 @@ public ItemSizePrototype GetMaxItemSize(Entity uid) // If we specify a max item size, use that if (uid.Comp.MaxItemSize != null) - return _prototype.Index(uid.Comp.MaxItemSize.Value); + { + if (_prototype.TryIndex(uid.Comp.MaxItemSize.Value, out var proto)) + return proto; + + Log.Error($"{ToPrettyString(uid.Owner)} tried to get invalid item size prototype: {uid.Comp.MaxItemSize.Value}. Stack trace:\\n{Environment.StackTrace}"); + } if (!_itemQuery.TryGetComponent(uid, out var item)) return _defaultStorageMaxItemSize; @@ -1428,20 +1376,20 @@ private void OnStackCountChanged(EntityUid uid, MetaDataComponent component, Sta private void HandleOpenBackpack(ICommonSession? session) { - HandleOpenSlotUI(session, "back"); + HandleToggleSlotUI(session, "back"); } private void HandleOpenBelt(ICommonSession? session) { - HandleOpenSlotUI(session, "belt"); + HandleToggleSlotUI(session, "belt"); } - private void HandleOpenSlotUI(ICommonSession? session, string slot) + private void HandleToggleSlotUI(ICommonSession? session, string slot) { if (session is not { } playerSession) return; - if (playerSession.AttachedEntity is not {Valid: true} playerEnt || !Exists(playerEnt)) + if (playerSession.AttachedEntity is not { Valid: true } playerEnt || !Exists(playerEnt)) return; if (!_inventory.TryGetSlotEntity(playerEnt, slot, out var storageEnt)) @@ -1451,9 +1399,13 @@ private void HandleOpenSlotUI(ICommonSession? session, string slot) return; if (!_ui.IsUiOpen(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt)) + { OpenStorageUI(storageEnt.Value, playerEnt, silent: false); + } else + { _ui.CloseUi(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt); + } } protected void ClearCantFillReasons() @@ -1483,6 +1435,79 @@ private bool CanInteract(EntityUid user, Entity storage, bool public abstract void PlayPickupAnimation(EntityUid uid, EntityCoordinates initialCoordinates, EntityCoordinates finalCoordinates, Angle initialRotation, EntityUid? user = null); + private bool ValidateInput( + EntitySessionEventArgs args, + NetEntity netStorage, + out Entity player, + out Entity storage) + { + player = default; + storage = default; + + if (args.SenderSession.AttachedEntity is not { } playerUid) + return false; + + if (!TryComp(playerUid, out HandsComponent? hands) || hands.Count == 0) + return false; + + if (!TryGetEntity(netStorage, out var storageUid)) + return false; + + if (!TryComp(storageUid, out StorageComponent? storageComp)) + return false; + + // TODO STORAGE use BUI events + // This would automatically validate that the UI is open & that the user can interact. + // However, we still need to manually validate that items being used are in the users hands or in the storage. + if (!_ui.IsUiOpen(storageUid.Value, StorageComponent.StorageUiKey.Key, playerUid)) + return false; + + if (!ActionBlocker.CanInteract(playerUid, storageUid)) + return false; + + player = new(playerUid, hands); + storage = new(storageUid.Value, storageComp); + return true; + } + + private bool ValidateInput(EntitySessionEventArgs args, + NetEntity netStorage, + NetEntity netItem, + out Entity player, + out Entity storage, + out Entity item, + bool held = false) + { + item = default!; + if (!ValidateInput(args, netStorage, out player, out storage)) + return false; + + if (!TryGetEntity(netItem, out var itemUid)) + return false; + + if (held) + { + if (!_sharedHandsSystem.IsHolding(player, itemUid, out _)) + return false; + } + else + { + if (!storage.Comp.Container.Contains(itemUid.Value)) + return false; + + DebugTools.Assert(storage.Comp.StoredItems.ContainsKey(itemUid.Value)); + } + + if (!TryComp(itemUid, out ItemComponent? itemComp)) + return false; + + if (!ActionBlocker.CanInteract(player, itemUid)) + return false; + + item = new(itemUid.Value, itemComp); + return true; + } + [Serializable, NetSerializable] protected sealed class StorageComponentState : ComponentState { @@ -1493,5 +1518,9 @@ protected sealed class StorageComponentState : ComponentState public List Grid = new(); public ProtoId? MaxItemSize; + + public EntityWhitelist? Whitelist; + + public EntityWhitelist? Blacklist; } } From 019ea2c8331c95b8008e31444a65e5744324b764 Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:42:49 -0700 Subject: [PATCH 035/366] Move PlaceableSurfaceComponent usages to PlaceableSurfaceSystem (#28384) * Move placeable check to PlaceableSurfaceSystem This check stops entities from being inserted into a storage entity when it has a PlaceableSurfaceComponent. The entity is instead placed on top of the entity like a table. * Move SetPlaceable to PlaceableSurfaceSystem * Update to transform system and consolidate code * Fix interaction with storage that has a placeable component * deadlock --------- Co-authored-by: metalgearsloth --- .../Placeable/PlaceableSurfaceSystem.cs | 50 +++++++++++++------ .../SharedEntityStorageSystem.cs | 3 -- Content.Shared/Storage/StorageComponent.cs | 3 ++ 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/Content.Shared/Placeable/PlaceableSurfaceSystem.cs b/Content.Shared/Placeable/PlaceableSurfaceSystem.cs index b0031cfa33f..a1c87f0de71 100644 --- a/Content.Shared/Placeable/PlaceableSurfaceSystem.cs +++ b/Content.Shared/Placeable/PlaceableSurfaceSystem.cs @@ -1,29 +1,37 @@ using System.Numerics; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; +using Content.Shared.Storage; using Content.Shared.Storage.Components; -namespace Content.Shared.Placeable +namespace Content.Shared.Placeable; + +public sealed class PlaceableSurfaceSystem : EntitySystem { - public sealed class PlaceableSurfaceSystem : EntitySystem - { - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnAfterInteractUsing); - } + SubscribeLocalEvent(OnAfterInteractUsing); + SubscribeLocalEvent(OnStorageInteractUsingAttempt); + SubscribeLocalEvent(OnStorageAfterOpen); + SubscribeLocalEvent(OnStorageAfterClose); + } public void SetPlaceable(EntityUid uid, bool isPlaceable, PlaceableSurfaceComponent? surface = null) { if (!Resolve(uid, ref surface, false)) return; - surface.IsPlaceable = isPlaceable; - Dirty(uid, surface); - } + if (surface.IsPlaceable == isPlaceable) + return; + + surface.IsPlaceable = isPlaceable; + Dirty(uid, surface); + } public void SetPlaceCentered(EntityUid uid, bool placeCentered, PlaceableSurfaceComponent? surface = null) { @@ -59,12 +67,24 @@ private void OnAfterInteractUsing(EntityUid uid, PlaceableSurfaceComponent surfa if (!_handsSystem.TryDrop(args.User, args.Used)) return; - if (surface.PlaceCentered) - Transform(args.Used).LocalPosition = Transform(uid).LocalPosition + surface.PositionOffset; - else - Transform(args.Used).Coordinates = args.ClickLocation; + _transformSystem.SetCoordinates(args.Used, + surface.PlaceCentered ? Transform(uid).Coordinates.Offset(surface.PositionOffset) : args.ClickLocation); - args.Handled = true; - } + args.Handled = true; + } + + private void OnStorageInteractUsingAttempt(Entity ent, ref StorageInteractUsingAttemptEvent args) + { + args.Cancelled = true; + } + + private void OnStorageAfterOpen(Entity ent, ref StorageAfterOpenEvent args) + { + SetPlaceable(ent.Owner, true, ent.Comp); + } + + private void OnStorageAfterClose(Entity ent, ref StorageAfterCloseEvent args) + { + SetPlaceable(ent.Owner, false, ent.Comp); } } diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs index bb49725e047..60ae813e580 100644 --- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs @@ -487,9 +487,6 @@ private void ModifyComponents(EntityUid uid, SharedEntityStorageComponent? compo } } - if (TryComp(uid, out var surface)) - _placeableSurface.SetPlaceable(uid, component.Open, surface); - _appearance.SetData(uid, StorageVisuals.Open, component.Open); _appearance.SetData(uid, StorageVisuals.HasContents, component.Contents.ContainedEntities.Count > 0); } diff --git a/Content.Shared/Storage/StorageComponent.cs b/Content.Shared/Storage/StorageComponent.cs index 2860f8dacfe..4b5c2158e1c 100644 --- a/Content.Shared/Storage/StorageComponent.cs +++ b/Content.Shared/Storage/StorageComponent.cs @@ -230,6 +230,9 @@ public AnimateInsertingEntitiesEvent(NetEntity storage, List storedEn [ByRefEvent] public record struct StorageInteractAttemptEvent(bool Silent, bool Cancelled = false); + [ByRefEvent] + public record struct StorageInteractUsingAttemptEvent(bool Cancelled = false); + [NetSerializable] [Serializable] public enum StorageVisuals : byte From 56eb2c1083de6e1f6188091285908d7d699cb46a Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:15:41 +1200 Subject: [PATCH 036/366] Add more storage admin logs & fix some interaction validation (#30725) --- Content.Shared.Database/LogType.cs | 6 ++++- .../EntitySystems/SharedStorageSystem.cs | 25 +++++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Content.Shared.Database/LogType.cs b/Content.Shared.Database/LogType.cs index fc2dad677a7..265965af94e 100644 --- a/Content.Shared.Database/LogType.cs +++ b/Content.Shared.Database/LogType.cs @@ -107,5 +107,9 @@ public enum LogType /// RateLimited = 91, InteractUsing = 92, - Storage = 93 + + /// + /// Storage & entity-storage related interactions + /// + Storage = 93, } diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 183420db9d1..db2337a921a 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -614,15 +614,7 @@ private void OnInteractWithItem(StorageInteractWithItemEvent msg, EntitySessionE $"{ToPrettyString(player):player} is interacting with {ToPrettyString(item):item} while it is stored in {ToPrettyString(storage):storage} using {ToPrettyString(player.Comp.ActiveHandEntity):used}"); // Else, interact using the held item - if (_interactionSystem.InteractUsing(player, - player.Comp.ActiveHandEntity.Value, - item, - Transform(item).Coordinates, - checkCanInteract: false)) - return; - - var failedEv = new StorageInsertFailedEvent((storage, storage.Comp), (player, player.Comp)); - RaiseLocalEvent(storage, ref failedEv); + _interactionSystem.InteractUsing(player, player.Comp.ActiveHandEntity.Value, item, Transform(item).Coordinates, checkCanInteract: false); } private void OnSetItemLocation(StorageSetItemLocationEvent msg, EntitySessionEventArgs args) @@ -638,6 +630,19 @@ private void OnSetItemLocation(StorageSetItemLocationEvent msg, EntitySessionEve TrySetItemStorageLocation(item!, storage!, msg.Location); } + private void OnRemoveItem(StorageRemoveItemEvent msg, EntitySessionEventArgs args) + { + if (!ValidateInput(args, msg.StorageEnt, msg.ItemEnt, out var player, out var storage, out var item)) + return; + + _adminLog.Add( + LogType.Storage, + LogImpact.Low, + $"{ToPrettyString(player):player} is removing {ToPrettyString(item):item} from {ToPrettyString(storage):storage}"); + TransformSystem.DropNextTo(item.Owner, player.Owner); + Audio.PlayPredicted(storage.Comp.StorageRemoveSound, storage, player, _audioParams); + } + private void OnInsertItemIntoLocation(StorageInsertItemIntoLocationEvent msg, EntitySessionEventArgs args) { if (!ValidateInput(args, msg.StorageEnt, msg.ItemEnt, out var player, out var storage, out var item, held: true)) @@ -652,7 +657,7 @@ private void OnInsertItemIntoLocation(StorageInsertItemIntoLocationEvent msg, En private void OnSaveItemLocation(StorageSaveItemLocationEvent msg, EntitySessionEventArgs args) { - if (!ValidateInput(args, msg.Storage, msg.Item, out var player, out var storage, out var item)) + if (!ValidateInput(args, msg.Storage, msg.Item, out var player, out var storage, out var item, held: true)) return; SaveItemLocation(storage!, item.Owner); From 76dbb4cb15bf50910ef7644c5620452d4ecc44ff Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 03:40:34 -0400 Subject: [PATCH 037/366] nice --- .../EntitySystems/SharedStorageSystem.cs | 77 ++++++++----------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index db2337a921a..868d26c3aea 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -22,7 +22,6 @@ using Content.Shared.Stacks; using Content.Shared.Storage.Components; using Content.Shared.Timing; -using Content.Shared.Storage.Events; using Content.Shared.Verbs; using Content.Shared.Whitelist; using Robust.Shared.Audio; @@ -41,23 +40,23 @@ namespace Content.Shared.Storage.EntitySystems; public abstract class SharedStorageSystem : EntitySystem { - [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] protected readonly IRobustRandom Random = default!; [Dependency] protected readonly ActionBlockerSystem ActionBlocker = default!; - [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] protected readonly SharedEntityStorageSystem EntityStorage = default!; - [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; - [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] protected readonly SharedItemSystem ItemSystem = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!; - [Dependency] private readonly SharedStackSystem _stack = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!; + [Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; - [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; [Dependency] protected readonly UseDelaySystem UseDelay = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly ISharedAdminLogManager _adminLog = default!; @@ -129,6 +128,7 @@ public override void Initialize() SubscribeAllEvent(OnInteractWithItem); SubscribeAllEvent(OnSetItemLocation); SubscribeAllEvent(OnInsertItemIntoLocation); + SubscribeAllEvent(OnRemoveItem); SubscribeAllEvent(OnSaveItemLocation); SubscribeLocalEvent(OnReclaimed); @@ -363,12 +363,10 @@ private void OnInteractUsing(EntityUid uid, StorageComponent storageComp, Intera if (args.Handled || !CanInteract(args.User, (uid, storageComp), storageComp.ClickInsert, false)) return; - var attemptEv = new StorageInteractUsingAttemptEvent(); - RaiseLocalEvent(uid, ref attemptEv); - if (attemptEv.Cancelled) + if (HasComp(uid)) return; - PlayerInsertHeldEntity((uid, storageComp), args.User); + PlayerInsertHeldEntity(uid, args.User, storageComp); // Always handle it, even if insertion fails. // We don't want to trigger any AfterInteract logic here. // Example issue would be placing wires if item doesn't fit in backpack. @@ -404,13 +402,7 @@ private void OnImplantActivate(EntityUid uid, StorageComponent storageComp, Open if (args.Handled) return; - var uiOpen = _ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.Performer); - - if (uiOpen) - _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.Performer); - else - OpenStorageUI(uid, args.Performer, storageComp, false); - + OpenStorageUI(uid, args.Performer, storageComp, false); args.Handled = true; } @@ -452,13 +444,13 @@ private void AfterInteract(EntityUid uid, StorageComponent storageComp, AfterInt } //If there's only one then let's be generous - if (_entList.Count >= 1) + if (_entList.Count > 1) { var doAfterArgs = new DoAfterArgs(EntityManager, args.User, delay, new AreaPickupDoAfterEvent(GetNetEntityList(_entList)), uid, target: uid) { BreakOnDamage = true, BreakOnMove = true, - NeedHand = true, + NeedHand = true }; _doAfterSystem.TryStartDoAfter(doAfterArgs); @@ -546,7 +538,7 @@ private void OnDoAfter(EntityUid uid, StorageComponent component, AreaPickupDoAf var angle = targetXform.LocalRotation; - if (PlayerInsertEntityInWorld((uid, component), args.Args.User, entity, playSound: false)) + if (PlayerInsertEntityInWorld((uid, component), args.Args.User, entity)) { successfullyInserted.Add(entity); successfullyInsertedPositions.Add(position); @@ -996,47 +988,46 @@ public bool Insert( /// /// Inserts an entity into storage from the player's active hand /// - /// The storage entity and component to insert into. - /// The player and hands component to insert the held entity from. - /// True if inserted, otherwise false. - public bool PlayerInsertHeldEntity(Entity ent, Entity player) + /// + /// The player to insert an entity from + /// + /// true if inserted, false otherwise + public bool PlayerInsertHeldEntity(EntityUid uid, EntityUid player, StorageComponent? storageComp = null) { - if (!Resolve(ent.Owner, ref ent.Comp) - || !Resolve(player.Owner, ref player.Comp) - || player.Comp.ActiveHandEntity == null) + if (!Resolve(uid, ref storageComp) || !TryComp(player, out HandsComponent? hands) || hands.ActiveHandEntity == null) return false; - var toInsert = player.Comp.ActiveHandEntity; + var toInsert = hands.ActiveHandEntity; - if (!CanInsert(ent, toInsert.Value, out var reason, ent.Comp)) + if (!CanInsert(uid, toInsert.Value, out var reason, storageComp)) { - _popupSystem.PopupClient(Loc.GetString(reason ?? "comp-storage-cant-insert"), ent, player); + _popupSystem.PopupClient(Loc.GetString(reason ?? "comp-storage-cant-insert"), uid, player); return false; } - if (!_sharedHandsSystem.CanDrop(player, toInsert.Value, player.Comp)) + if (!_sharedHandsSystem.CanDrop(player, toInsert.Value, hands)) { - _popupSystem.PopupClient(Loc.GetString("comp-storage-cant-drop", ("entity", toInsert.Value)), ent, player); + _popupSystem.PopupClient(Loc.GetString("comp-storage-cant-drop", ("entity", toInsert.Value)), uid, player); return false; } - return PlayerInsertEntityInWorld((ent, ent.Comp), player, toInsert.Value); + return PlayerInsertEntityInWorld((uid, storageComp), player, toInsert.Value); } /// /// Inserts an Entity () in the world into storage, informing if it fails. - /// is *NOT* held, see . + /// is *NOT* held, see . /// /// /// The player to insert an entity with /// /// true if inserted, false otherwise - public bool PlayerInsertEntityInWorld(Entity uid, EntityUid player, EntityUid toInsert, bool playSound = true) + public bool PlayerInsertEntityInWorld(Entity uid, EntityUid player, EntityUid toInsert) { if (!Resolve(uid, ref uid.Comp) || !_interactionSystem.InRangeUnobstructed(player, uid.Owner)) return false; - if (!Insert(uid, toInsert, out _, user: player, uid.Comp, playSound: playSound)) + if (!Insert(uid, toInsert, out _, user: player, uid.Comp)) { _popupSystem.PopupClient(Loc.GetString("comp-storage-cant-insert"), uid, player); return false; @@ -1394,7 +1385,7 @@ private void HandleToggleSlotUI(ICommonSession? session, string slot) if (session is not { } playerSession) return; - if (playerSession.AttachedEntity is not { Valid: true } playerEnt || !Exists(playerEnt)) + if (playerSession.AttachedEntity is not {Valid: true} playerEnt || !Exists(playerEnt)) return; if (!_inventory.TryGetSlotEntity(playerEnt, slot, out var storageEnt)) From 24a649fe035105327972d19ac316acef42aec1d0 Mon Sep 17 00:00:00 2001 From: Crude Oil <124208219+CroilBird@users.noreply.github.com> Date: Sun, 21 Jul 2024 07:49:48 +0200 Subject: [PATCH 038/366] Add ability to show stack visuals on closed containers (#29309) * add ability to show stack visuals on closed containers * remove container stack visuals logic from sharedstoragesystem * improve comments a bit * move logic for open/closed containers into itemcountersystem * move behavior to storage component * remove unused import * remove old comment * fix comments * fix wrong property name * Update Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs * Rename variable for clarity --------- Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> --- Content.Client/Storage/Systems/ItemCounterSystem.cs | 2 +- Content.Shared/Storage/Components/ItemCounterComponent.cs | 2 +- Content.Shared/Storage/StorageComponent.cs | 8 ++++++++ .../Entities/Objects/Consumable/Drinks/drinks_cans.yml | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Content.Client/Storage/Systems/ItemCounterSystem.cs b/Content.Client/Storage/Systems/ItemCounterSystem.cs index 605f47d3b8a..fcb1ca17dc6 100644 --- a/Content.Client/Storage/Systems/ItemCounterSystem.cs +++ b/Content.Client/Storage/Systems/ItemCounterSystem.cs @@ -31,7 +31,7 @@ private void OnAppearanceChange(EntityUid uid, ItemCounterComponent comp, ref Ap if (!_appearanceSystem.TryGetData(uid, StackVisuals.Hide, out var hidden, args.Component)) hidden = false; - + if (comp.IsComposite) ProcessCompositeSprite(uid, actual, maxCount, comp.LayerStates, hidden, sprite: args.Sprite); else diff --git a/Content.Shared/Storage/Components/ItemCounterComponent.cs b/Content.Shared/Storage/Components/ItemCounterComponent.cs index 890bc84e721..6a1444ebf62 100644 --- a/Content.Shared/Storage/Components/ItemCounterComponent.cs +++ b/Content.Shared/Storage/Components/ItemCounterComponent.cs @@ -1,4 +1,4 @@ -using Content.Shared.Storage.EntitySystems; +using Content.Shared.Storage.EntitySystems; using Content.Shared.Whitelist; namespace Content.Shared.Storage.Components diff --git a/Content.Shared/Storage/StorageComponent.cs b/Content.Shared/Storage/StorageComponent.cs index 4b5c2158e1c..d2c607e57f7 100644 --- a/Content.Shared/Storage/StorageComponent.cs +++ b/Content.Shared/Storage/StorageComponent.cs @@ -123,6 +123,14 @@ public sealed partial class StorageComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public StorageDefaultOrientation? DefaultStorageOrientation; + /// + /// If true, sets StackVisuals.Hide to true when the container is closed + /// Used in cases where there are sprites that are shown when the container is open but not + /// when it is closed + /// + [DataField] + public bool HideStackVisualsWhenClosed = true; + [Serializable, NetSerializable] public enum StorageUiKey : byte { diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml index 728ca962f9f..52ffb8dd1cc 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml @@ -444,6 +444,7 @@ whitelist: tags: - Cola + hideStackVisualsWhenClosed: false - type: StorageFill contents: - id: DrinkColaCan From c8e1e5c5e116b81c26f81f169c7d04c4e83b4013 Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Sun, 25 Feb 2024 13:07:10 +0200 Subject: [PATCH 039/366] Cleanup ExecutionSystem (#24382) * Creat Execution Component and add to sharp items * Kill Server ExecutionSystem. Create ExecutionSystem in shared. Create ActiveExecution Component. Transferred the Execution system into shared. Heavily re-wrote the system in order to reduce duplication, and remove gun code from the system. The melee weapon modifier which was dependant on swing rate was removed. The ActiveExecutionComponent was created in order to apply the damage modifier to the shot from a gun execution. It is added just before the gun fires and removed after an attempt is made. * Fix bugs The execution completed text will now only show up if the gun fires. The client also no longer crashes because I forgot to network the component. * Remove clumsy text * Make BaseSword abstract * Add ExecutionComponent to every weapon * Fix bug * Remove execution comp from battery weapons Currently the gun system does not have a way to alter hitscan damage like it does with projectiles. * Cleanup * Revert "Remove clumsy text" This reverts commit a46da6448d5d179a4e936f9213d5622bedb58a16. * Actually fix the ExecutionSystem Everything about the shot goes through the gun system now. The Damage multiplier is only applied when a projectile impacts the target so people that get in the way don't get hit with 9 times damage for no reason. In order to make suicides work I needed to create fake EntityCoordinates because the gun system and the projectile system do not play well with a projectile that has the same start and end position. * Make launchers able to execute * Fix prediction bug The OnAmmoShotEvent is only raised on the server. * Readd ability for clowns to accidentally shoot themselves while executing * Cleanup * Reset melee cooldown to initial value * Address reviews fix bug Addressed reviews on overriding messages. Now I actually mark doafters as handled. Return normal cooldown to some meleeweapons I forgot on the previous commit. * Address Reviews Remove duplication * Exorcise codebase Remove evil null coercion that I was sure I removed a while ago * Address reviews again * Remove melee weapon attack logic and rely on the system. Remove gun and melee checks. * Make system functional again and cleanup * Remove code I forgot to remove * Cleanup * stalled * Selectively revert gun penetration The collision layer check doesn't work and I don't have time to fix it. * Fixes --------- Co-authored-by: metalgearsloth --- Content.Server/Execution/ExecutionSystem.cs | 400 ------------------ .../Projectiles/ProjectileSystem.cs | 27 +- .../Weapons/Ranged/Systems/GunSystem.cs | 147 ++++++- .../Execution/ExecutionComponent.cs | 26 ++ Content.Shared/Execution/ExecutionSystem.cs | 241 +++++++++++ .../Projectiles/SharedProjectileSystem.cs | 6 + .../Ranged/Events/ShotAttemptedEvent.cs | 4 +- .../Weapons/Ranged/Systems/SharedGunSystem.cs | 102 ++++- .../Locale/en-US/execution/execution.ftl | 10 - .../components/butcherable-component.ftl | 2 +- .../Objects/Materials/crystal_shard.yml | 2 + .../Entities/Objects/Materials/shards.yml | 2 + .../Entities/Objects/Misc/broken_bottle.yml | 2 + .../Objects/Weapons/Guns/HMGs/hmgs.yml | 1 + .../Objects/Weapons/Guns/LMGs/lmgs.yml | 1 + .../Weapons/Guns/Launchers/launchers.yml | 1 + .../Objects/Weapons/Guns/Pistols/pistols.yml | 1 + .../Objects/Weapons/Guns/Rifles/rifles.yml | 1 + .../Objects/Weapons/Guns/SMGs/smgs.yml | 1 + .../Weapons/Guns/Shotguns/shotguns.yml | 2 + .../Objects/Weapons/Guns/Snipers/snipers.yml | 1 + .../Objects/Weapons/Guns/flare_gun.yml | 1 + .../Objects/Weapons/Guns/pneumatic_cannon.yml | 1 + .../Objects/Weapons/Melee/armblade.yml | 2 + .../Objects/Weapons/Melee/fireaxe.yml | 2 + .../Entities/Objects/Weapons/Melee/knife.yml | 2 + .../Entities/Objects/Weapons/Melee/sword.yml | 35 +- 27 files changed, 569 insertions(+), 454 deletions(-) delete mode 100644 Content.Server/Execution/ExecutionSystem.cs create mode 100644 Content.Shared/Execution/ExecutionComponent.cs create mode 100644 Content.Shared/Execution/ExecutionSystem.cs diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs deleted file mode 100644 index 453a05e8039..00000000000 --- a/Content.Server/Execution/ExecutionSystem.cs +++ /dev/null @@ -1,400 +0,0 @@ -using Content.Server.Interaction; -using Content.Server.Kitchen.Components; -using Content.Server.Weapons.Ranged.Systems; -using Content.Shared.ActionBlocker; -using Content.Shared.Damage; -using Content.Shared.Database; -using Content.Shared.DoAfter; -using Content.Shared.Execution; -using Content.Shared.Interaction.Components; -using Content.Shared.Mobs.Components; -using Content.Shared.Mobs.Systems; -using Content.Shared.Popups; -using Content.Shared.Projectiles; -using Content.Shared.Verbs; -using Content.Shared.Weapons.Melee; -using Content.Shared.Weapons.Ranged; -using Content.Shared.Weapons.Ranged.Components; -using Content.Shared.Weapons.Ranged.Events; -using Content.Shared.Weapons.Ranged.Systems; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; - -namespace Content.Server.Execution; - -/// -/// Verb for violently murdering cuffed creatures. -/// -public sealed class ExecutionSystem : EntitySystem -{ - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly InteractionSystem _interactionSystem = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IComponentFactory _componentFactory = default!; - [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly GunSystem _gunSystem = default!; - - private const float MeleeExecutionTimeModifier = 5.0f; - private const float GunExecutionTime = 6.0f; - private const float DamageModifier = 9.0f; - - /// - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent>(OnGetInteractionVerbsMelee); - SubscribeLocalEvent>(OnGetInteractionVerbsGun); - - SubscribeLocalEvent(OnDoafterMelee); - SubscribeLocalEvent(OnDoafterGun); - } - - private void OnGetInteractionVerbsMelee( - EntityUid uid, - SharpComponent component, - GetVerbsEvent args) - { - if (args.Hands == null || args.Using == null || !args.CanAccess || !args.CanInteract) - return; - - var attacker = args.User; - var weapon = args.Using!.Value; - var victim = args.Target; - - if (!CanExecuteWithMelee(weapon, victim, attacker)) - return; - - UtilityVerb verb = new() - { - Act = () => - { - TryStartMeleeExecutionDoafter(weapon, victim, attacker); - }, - Impact = LogImpact.High, - Text = Loc.GetString("execution-verb-name"), - Message = Loc.GetString("execution-verb-message"), - }; - - args.Verbs.Add(verb); - } - - private void OnGetInteractionVerbsGun( - EntityUid uid, - GunComponent component, - GetVerbsEvent args) - { - if (args.Hands == null || args.Using == null || !args.CanAccess || !args.CanInteract) - return; - - var attacker = args.User; - var weapon = args.Using!.Value; - var victim = args.Target; - - if (!CanExecuteWithGun(weapon, victim, attacker)) - return; - - UtilityVerb verb = new() - { - Act = () => - { - TryStartGunExecutionDoafter(weapon, victim, attacker); - }, - Impact = LogImpact.High, - Text = Loc.GetString("execution-verb-name"), - Message = Loc.GetString("execution-verb-message"), - }; - - args.Verbs.Add(verb); - } - - private bool CanExecuteWithAny(EntityUid weapon, EntityUid victim, EntityUid attacker) - { - // No point executing someone if they can't take damage - if (!TryComp(victim, out var damage)) - return false; - - // You can't execute something that cannot die - if (!TryComp(victim, out var mobState)) - return false; - - // You're not allowed to execute dead people (no fun allowed) - if (_mobStateSystem.IsDead(victim, mobState)) - return false; - - // You must be able to attack people to execute - if (!_actionBlockerSystem.CanAttack(attacker, victim)) - return false; - - // The victim must be incapacitated to be executed - if (victim != attacker && _actionBlockerSystem.CanInteract(victim, null)) - return false; - - if (victim == attacker) - return false; // DeltaV - Fucking seriously? - - // All checks passed - return true; - } - - private bool CanExecuteWithMelee(EntityUid weapon, EntityUid victim, EntityUid user) - { - if (!CanExecuteWithAny(weapon, victim, user)) return false; - - // We must be able to actually hurt people with the weapon - if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) - return false; - - return true; - } - - private bool CanExecuteWithGun(EntityUid weapon, EntityUid victim, EntityUid user) - { - if (!CanExecuteWithAny(weapon, victim, user)) return false; - - // We must be able to actually fire the gun - if (!TryComp(weapon, out var gun) && _gunSystem.CanShoot(gun!)) - return false; - - return true; - } - - private void TryStartMeleeExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid attacker) - { - if (!CanExecuteWithMelee(weapon, victim, attacker)) - return; - - var executionTime = (1.0f / Comp(weapon).AttackRate) * MeleeExecutionTimeModifier; - - if (attacker == victim) - { - ShowExecutionPopup("suicide-popup-melee-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("suicide-popup-melee-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); - } - else - { - ShowExecutionPopup("execution-popup-melee-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("execution-popup-melee-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); - } - - var doAfter = - new DoAfterArgs(EntityManager, attacker, executionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) - { - BreakOnTargetMove = true, - BreakOnUserMove = true, - BreakOnDamage = true, - NeedHand = true - }; - - _doAfterSystem.TryStartDoAfter(doAfter); - } - - private void TryStartGunExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid attacker) - { - if (!CanExecuteWithGun(weapon, victim, attacker)) - return; - - if (attacker == victim) - { - ShowExecutionPopup("suicide-popup-gun-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("suicide-popup-gun-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); - } - else - { - ShowExecutionPopup("execution-popup-gun-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("execution-popup-gun-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); - } - - var doAfter = - new DoAfterArgs(EntityManager, attacker, GunExecutionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) - { - BreakOnTargetMove = true, - BreakOnUserMove = true, - BreakOnDamage = true, - NeedHand = true - }; - - _doAfterSystem.TryStartDoAfter(doAfter); - } - - private bool OnDoafterChecks(EntityUid uid, DoAfterEvent args) - { - if (args.Handled || args.Cancelled || args.Used == null || args.Target == null) - return false; - - if (!CanExecuteWithAny(args.Used.Value, args.Target.Value, uid)) - return false; - - // All checks passed - return true; - } - - private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEvent args) - { - if (args.Handled || args.Cancelled || args.Used == null || args.Target == null) - return; - - var attacker = args.User; - var victim = args.Target!.Value; - var weapon = args.Used!.Value; - - if (!CanExecuteWithMelee(weapon, victim, attacker)) return; - - if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) - return; - - _damageableSystem.TryChangeDamage(victim, melee.Damage * DamageModifier, true, origin: attacker); - _audioSystem.PlayEntity(melee.SoundHit, Filter.Pvs(weapon), weapon, true, AudioParams.Default); - - if (attacker == victim) - { - ShowExecutionPopup("suicide-popup-melee-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("suicide-popup-melee-complete-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); - } - else - { - ShowExecutionPopup("execution-popup-melee-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("execution-popup-melee-complete-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); - } - } - - // TODO: This repeats a lot of the code of the serverside GunSystem, make it not do that - private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent args) - { - if (args.Handled || args.Cancelled || args.Used == null || args.Target == null) - return; - - var attacker = args.User; - var weapon = args.Used!.Value; - var victim = args.Target!.Value; - - if (!CanExecuteWithGun(weapon, victim, attacker)) return; - - // Check if any systems want to block our shot - var prevention = new ShotAttemptedEvent - { - User = attacker, - Used = new Entity(uid, component) - }; - - RaiseLocalEvent(weapon, ref prevention); - if (prevention.Cancelled) - return; - - RaiseLocalEvent(attacker, ref prevention); - if (prevention.Cancelled) - return; - - // Not sure what this is for but gunsystem uses it so ehhh - var attemptEv = new AttemptShootEvent(attacker, null); - RaiseLocalEvent(weapon, ref attemptEv); - - if (attemptEv.Cancelled) - { - if (attemptEv.Message != null) - { - _popupSystem.PopupClient(attemptEv.Message, weapon, attacker); - return; - } - } - - // Take some ammunition for the shot (one bullet) - var fromCoordinates = Transform(attacker).Coordinates; - var ev = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, attacker); - RaiseLocalEvent(weapon, ev); - - // Check if there's any ammo left - if (ev.Ammo.Count <= 0) - { - _audioSystem.PlayEntity(component.SoundEmpty, Filter.Pvs(weapon), weapon, true, AudioParams.Default); - ShowExecutionPopup("execution-popup-gun-empty", Filter.Pvs(weapon), PopupType.Medium, attacker, victim, weapon); - return; - } - - // Information about the ammo like damage - DamageSpecifier damage = new DamageSpecifier(); - - // Get some information from IShootable - var ammoUid = ev.Ammo[0].Entity; - switch (ev.Ammo[0].Shootable) - { - case CartridgeAmmoComponent cartridge: - // Get the damage value - var prototype = _prototypeManager.Index(cartridge.Prototype); - prototype.TryGetComponent(out var projectileA, _componentFactory); // sloth forgive me - if (projectileA != null) - { - damage = projectileA.Damage * cartridge.Count; - } - - // Expend the cartridge - cartridge.Spent = true; - _appearanceSystem.SetData(ammoUid!.Value, AmmoVisuals.Spent, true); - Dirty(ammoUid.Value, cartridge); - - break; - - case AmmoComponent newAmmo: - TryComp(ammoUid, out var projectileB); - if (projectileB != null) - { - damage = projectileB.Damage; - } - Del(ammoUid); - break; - - case HitscanPrototype hitscan: - damage = hitscan.Damage!; - break; - - default: - throw new ArgumentOutOfRangeException(); - } - - // Clumsy people have a chance to shoot themselves - if (TryComp(attacker, out var clumsy) && component.ClumsyProof == false) - { - if (_interactionSystem.TryRollClumsy(attacker, 0.33333333f, clumsy)) - { - ShowExecutionPopup("execution-popup-gun-clumsy-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("execution-popup-gun-clumsy-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); - - // You shoot yourself with the gun (no damage multiplier) - _damageableSystem.TryChangeDamage(attacker, damage, origin: attacker); - _audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, true, AudioParams.Default); - return; - } - } - - // Gun successfully fired, deal damage - _damageableSystem.TryChangeDamage(victim, damage * DamageModifier, true, origin: attacker); - _audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, false, AudioParams.Default); - - // Popups - if (attacker != victim) - { - ShowExecutionPopup("execution-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("execution-popup-gun-complete-external", Filter.PvsExcept(attacker), PopupType.LargeCaution, attacker, victim, weapon); - } - else - { - ShowExecutionPopup("suicide-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.LargeCaution, attacker, victim, weapon); - ShowExecutionPopup("suicide-popup-gun-complete-external", Filter.PvsExcept(attacker), PopupType.LargeCaution, attacker, victim, weapon); - } - } - - private void ShowExecutionPopup(string locString, Filter filter, PopupType type, - EntityUid attacker, EntityUid victim, EntityUid weapon) - { - _popupSystem.PopupEntity(Loc.GetString( - locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), - attacker, filter, true, type); - } -} diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index c5ec2d76ad5..b899ae5197a 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -31,10 +31,14 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St { // This is so entities that shouldn't get a collision are ignored. if (args.OurFixtureId != ProjectileFixture || !args.OtherFixture.Hard - || component.DamagedEntity || component is { Weapon: null, OnlyCollideWhenShot: true }) + || component.DamagedEntity || component is + { Weapon: null, OnlyCollideWhenShot: true }) + { return; + } var target = args.OtherEntity; + // it's here so this check is only done once before possible hit var attemptEv = new ProjectileReflectAttemptEvent(uid, component, false); RaiseLocalEvent(target, ref attemptEv); @@ -44,11 +48,26 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St return; } + if (TryHandleProjectile(target, (uid, component))) + { + var direction = args.OurBody.LinearVelocity.Normalized(); + _sharedCameraRecoil.KickCamera(target, direction); + } + } + + /// + /// Tries to handle a projectile interacting with the target. + /// + /// True if the target isn't deleted. + public bool TryHandleProjectile(EntityUid target, Entity projectile) + { + var uid = projectile.Owner; + var component = projectile.Comp; + var ev = new ProjectileHitEvent(component.Damage, target, component.Shooter); RaiseLocalEvent(uid, ref ev); var otherName = ToPrettyString(target); - var direction = args.OurBody.LinearVelocity.Normalized(); var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: component.Shooter); var deleted = Deleted(target); @@ -67,11 +86,13 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St if (!deleted) { _guns.PlayImpactSound(target, modifiedDamage, component.SoundHit, component.ForceSound); - _sharedCameraRecoil.KickCamera(target, direction); } component.DamagedEntity = true; + var afterProjectileHitEvent = new AfterProjectileHitEvent(component.Damage, target); + RaiseLocalEvent(uid, ref afterProjectileHitEvent); + if (component.DeleteOnCollide) QueueDel(uid); diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index e472d146de6..03f10f153d1 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -2,6 +2,8 @@ using System.Numerics; using Content.Server.Cargo.Systems; using Content.Server.Power.EntitySystems; +using Content.Server.Projectiles; +using Content.Server.Stunnable; using Content.Server.Weapons.Ranged.Components; using Content.Shared.Contests; using Content.Shared.Damage; @@ -31,6 +33,7 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly DamageExamineSystem _damageExamine = default!; [Dependency] private readonly PricingSystem _pricing = default!; [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; + [Dependency] private readonly ProjectileSystem _projectile = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly ContestsSystem _contests = default!; @@ -59,6 +62,137 @@ private void OnBallisticPrice(EntityUid uid, BallisticAmmoProviderComponent comp args.Price += price * component.UnspawnedCount; } + protected override bool ShootDirect(EntityUid gunUid, GunComponent gun, EntityUid target, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityUid user) + { + var result = false; + + // TODO: This is dogshit. I just want to get executions slightly better. + // Ideally you'd pull out cartridge + ammo to separate handling functions and re-use it here, then hitscan you need to bypass entirely. + // You should also make shooting into a struct of args given how many there are now. + var fromCoordinates = Transform(gunUid).Coordinates; + var toCoordinates = Transform(target).Coordinates; + + var fromMap = fromCoordinates.ToMap(EntityManager, TransformSystem); + var toMap = toCoordinates.ToMapPos(EntityManager, TransformSystem); + var mapDirection = toMap - fromMap.Position; + var angle = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle()); + + // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. + var fromEnt = MapManager.TryFindGridAt(fromMap, out var gridUid, out _) + ? fromCoordinates.WithEntityId(gridUid, EntityManager) + : new EntityCoordinates(MapManager.GetMapEntityId(fromMap.MapId), fromMap.Position); + + // I must be high because this was getting tripped even when true. + // DebugTools.Assert(direction != Vector2.Zero); + var shotProjectiles = new List(ammo.Count); + var cartridgeBullets = new List(); + + foreach (var (ent, shootable) in ammo) + { + switch (shootable) + { + // Cartridge shoots something else + case CartridgeAmmoComponent cartridge: + if (!cartridge.Spent) + { + for (var i = 0; i < cartridge.Count; i++) + { + var uid = Spawn(cartridge.Prototype, fromEnt); + cartridgeBullets.Add(uid); + } + + RaiseLocalEvent(ent!.Value, new AmmoShotEvent() + { + FiredProjectiles = cartridgeBullets, + }); + + shotProjectiles.AddRange(cartridgeBullets); + cartridgeBullets.Clear(); + SetCartridgeSpent(ent.Value, cartridge, true); + MuzzleFlash(gunUid, cartridge, user); + Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); + + if (cartridge.DeleteOnSpawn) + Del(ent.Value); + } + else + { + Audio.PlayPredicted(gun.SoundEmpty, gunUid, user); + } + + // Something like ballistic might want to leave it in the container still + if (!cartridge.DeleteOnSpawn && !Containers.IsEntityInContainer(ent!.Value)) + EjectCartridge(ent.Value, angle); + + result = true; + Dirty(ent!.Value, cartridge); + break; + // Ammo shoots itself + case AmmoComponent newAmmo: + result = true; + shotProjectiles.Add(ent!.Value); + MuzzleFlash(gunUid, newAmmo, user); + Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); + break; + case HitscanPrototype hitscan: + result = true; + var hitEntity = target; + if (hitscan.StaminaDamage > 0f) + _stamina.TakeStaminaDamage(hitEntity, hitscan.StaminaDamage, source: user); + + var dmg = hitscan.Damage; + + var hitName = ToPrettyString(hitEntity); + if (dmg != null) + dmg = Damageable.TryChangeDamage(hitEntity, dmg, origin: user); + + // check null again, as TryChangeDamage returns modified damage values + if (dmg != null) + { + if (!Deleted(hitEntity)) + { + if (dmg.Any()) + { + _color.RaiseEffect(Color.Red, new List() { hitEntity }, Filter.Pvs(hitEntity, entityManager: EntityManager)); + } + + // TODO get fallback position for playing hit sound. + PlayImpactSound(hitEntity, dmg, hitscan.Sound, hitscan.ForceSound); + } + + Logs.Add(LogType.HitScanHit, + $"{ToPrettyString(user):user} hit {hitName:target} using hitscan and dealt {dmg.GetTotal():damage} damage"); + } + + Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + foreach (var ammoUid in shotProjectiles) + { + // TODO: Handle this shit + if (!TryComp(ammoUid, out ProjectileComponent? projectileComponent)) + { + QueueDel(ammoUid); + continue; + } + + _projectile.TryHandleProjectile(target, (ammoUid, projectileComponent)); + // Even this deletion handling is mega sussy. + Del(ammoUid); + } + + RaiseLocalEvent(gunUid, new AmmoShotEvent() + { + FiredProjectiles = shotProjectiles, + }); + + return result; + } + public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, out bool userImpulse, EntityUid? user = null, bool throwItems = false) { @@ -75,6 +209,8 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? } } + // As the above message wasn't obvious stop putting stuff here and use events + var fromMap = fromCoordinates.ToMap(EntityManager, TransformSystem); var toMap = toCoordinates.ToMapPos(EntityManager, TransformSystem); var mapDirection = toMap - fromMap.Position; @@ -82,7 +218,7 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? var angle = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle(), user); // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. - var fromEnt = MapManager.TryFindGridAt(fromMap, out var gridUid, out var grid) + var fromEnt = MapManager.TryFindGridAt(fromMap, out var gridUid, out _) ? fromCoordinates.WithEntityId(gridUid, EntityManager) : new EntityCoordinates(MapManager.GetMapEntityId(fromMap.MapId), fromMap.Position); @@ -94,6 +230,7 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? // I must be high because this was getting tripped even when true. // DebugTools.Assert(direction != Vector2.Zero); var shotProjectiles = new List(ammo.Count); + var cartridgeBullets = new List(); foreach (var (ent, shootable) in ammo) { @@ -122,21 +259,23 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? { var uid = Spawn(cartridge.Prototype, fromEnt); ShootOrThrow(uid, angles[i].ToVec(), gunVelocity, gun, gunUid, user); - shotProjectiles.Add(uid); + cartridgeBullets.Add(uid); } } else { var uid = Spawn(cartridge.Prototype, fromEnt); ShootOrThrow(uid, mapDirection, gunVelocity, gun, gunUid, user); - shotProjectiles.Add(uid); + cartridgeBullets.Add(uid); } RaiseLocalEvent(ent!.Value, new AmmoShotEvent() { - FiredProjectiles = shotProjectiles, + FiredProjectiles = cartridgeBullets, }); + shotProjectiles.AddRange(cartridgeBullets); + cartridgeBullets.Clear(); SetCartridgeSpent(ent.Value, cartridge, true); MuzzleFlash(gunUid, cartridge, mapDirection.ToAngle(), user); Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); diff --git a/Content.Shared/Execution/ExecutionComponent.cs b/Content.Shared/Execution/ExecutionComponent.cs new file mode 100644 index 00000000000..f9c5111d63a --- /dev/null +++ b/Content.Shared/Execution/ExecutionComponent.cs @@ -0,0 +1,26 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Execution; + +/// +/// Added to entities that can be used to execute another target. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ExecutionComponent : Component +{ + /// + /// How long the execution duration lasts. + /// + [DataField, AutoNetworkedField] + public float DoAfterDuration = 5f; + + [DataField, AutoNetworkedField] + public float DamageModifier = 9f; + + // Not networked because this is transient inside of a tick. + /// + /// True if it is currently executing for handlers. + /// + [DataField] + public bool Executing = true; +} diff --git a/Content.Shared/Execution/ExecutionSystem.cs b/Content.Shared/Execution/ExecutionSystem.cs new file mode 100644 index 00000000000..de6db205be4 --- /dev/null +++ b/Content.Shared/Execution/ExecutionSystem.cs @@ -0,0 +1,241 @@ +using Content.Shared.Weapons.Ranged.Systems; +using Content.Shared.ActionBlocker; +using Content.Shared.CombatMode; +using Content.Shared.Damage; +using Content.Shared.Database; +using Content.Shared.DoAfter; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Content.Shared.Weapons.Melee; +using Content.Shared.Weapons.Melee.Events; +using Content.Shared.Weapons.Ranged.Components; +using Robust.Shared.Network; +using Robust.Shared.Player; + +namespace Content.Shared.Execution; + +/// +/// Verb for violently murdering cuffed creatures. +/// +public sealed class ExecutionSystem : EntitySystem +{ + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; + [Dependency] private readonly SharedGunSystem _gunSystem = default!; + [Dependency] private readonly SharedCombatModeSystem _combatSystem = default!; + [Dependency] private readonly SharedMeleeWeaponSystem _meleeSystem = default!; + + // TODO: Still needs more cleaning up. + private const string DefaultInternalMeleeExecutionMessage = "execution-popup-melee-initial-internal"; + private const string DefaultExternalMeleeExecutionMessage = "execution-popup-melee-initial-external"; + private const string DefaultCompleteInternalMeleeExecutionMessage = "execution-popup-melee-complete-internal"; + private const string DefaultCompleteExternalMeleeExecutionMessage = "execution-popup-melee-complete-external"; + private const string DefaultInternalGunExecutionMessage = "execution-popup-gun-initial-internal"; + private const string DefaultExternalGunExecutionMessage = "execution-popup-gun-initial-external"; + private const string DefaultCompleteInternalGunExecutionMessage = "execution-popup-gun-complete-internal"; + private const string DefaultCompleteExternalGunExecutionMessage = "execution-popup-gun-complete-external"; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnGetInteractionsVerbs); + SubscribeLocalEvent(OnExecutionDoAfter); + SubscribeLocalEvent(OnGetMeleeDamage); + } + + private void OnGetInteractionsVerbs(EntityUid uid, ExecutionComponent comp, GetVerbsEvent args) + { + if (args.Hands == null || args.Using == null || !args.CanAccess || !args.CanInteract) + return; + + var attacker = args.User; + var weapon = args.Using.Value; + var victim = args.Target; + + if (!CanExecuteWithAny(victim, attacker)) + return; + + UtilityVerb verb = new() + { + Act = () => TryStartExecutionDoAfter(weapon, victim, attacker, comp), + Impact = LogImpact.High, + Text = Loc.GetString("execution-verb-name"), + Message = Loc.GetString("execution-verb-message"), + }; + + args.Verbs.Add(verb); + } + + private void TryStartExecutionDoAfter(EntityUid weapon, EntityUid victim, EntityUid attacker, ExecutionComponent comp) + { + if (!CanExecuteWithAny(victim, attacker)) + return; + + // TODO: This should just be on the weapons as a single execution message. + var defaultExecutionInternal = DefaultInternalMeleeExecutionMessage; + var defaultExecutionExternal = DefaultExternalMeleeExecutionMessage; + + if (HasComp(weapon)) + { + defaultExecutionExternal = DefaultInternalGunExecutionMessage; + defaultExecutionInternal = DefaultExternalGunExecutionMessage; + } + + var internalMsg = defaultExecutionInternal; + var externalMsg = defaultExecutionExternal; + ShowExecutionInternalPopup(internalMsg, attacker, victim, weapon); + ShowExecutionExternalPopup(externalMsg, attacker, victim, weapon); + + var doAfter = + new DoAfterArgs(EntityManager, attacker, comp.DoAfterDuration, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) + { + BreakOnTargetMove = true, + BreakOnUserMove = true, + BreakOnDamage = true, + NeedHand = true + }; + + _doAfterSystem.TryStartDoAfter(doAfter); + + } + + private bool CanExecuteWithAny(EntityUid victim, EntityUid attacker) + { + // Use suicide. + if (victim == attacker) + return false; + + // No point executing someone if they can't take damage + if (!TryComp(victim, out _)) + return false; + + // You can't execute something that cannot die + if (!TryComp(victim, out var mobState)) + return false; + + // You're not allowed to execute dead people (no fun allowed) + if (_mobStateSystem.IsDead(victim, mobState)) + return false; + + // You must be able to attack people to execute + if (!_actionBlockerSystem.CanAttack(attacker, victim)) + return false; + + // The victim must be incapacitated to be executed + if (victim != attacker && _actionBlockerSystem.CanInteract(victim, null)) + return false; + + // All checks passed + return true; + } + + private void OnExecutionDoAfter(EntityUid uid, ExecutionComponent component, ExecutionDoAfterEvent args) + { + if (args.Handled || args.Cancelled || args.Used == null || args.Target == null) + return; + + var attacker = args.User; + var victim = args.Target.Value; + var weapon = args.Used.Value; + + if (!CanExecuteWithAny(victim, attacker)) + return; + + // This is needed so the melee system does not stop it. + var prev = _combatSystem.IsInCombatMode(attacker); + _combatSystem.SetInCombatMode(attacker, true); + component.Executing = true; + string? internalMsg = null; + string? externalMsg = null; + + if (TryComp(uid, out MeleeWeaponComponent? melee)) + { + _meleeSystem.AttemptLightAttack(attacker, weapon, melee, victim); + internalMsg = DefaultCompleteInternalMeleeExecutionMessage; + externalMsg = DefaultCompleteExternalMeleeExecutionMessage; + } + else if (TryComp(uid, out GunComponent? gun)) + { + var clumsyShot = false; + + // TODO: This should just be an event or something instead to get this. + // TODO: Handle clumsy. + if (!_gunSystem.AttemptDirectShoot(args.User, uid, args.Target.Value, gun)) + { + internalMsg = null; + externalMsg = null; + } + else + { + internalMsg = DefaultCompleteInternalGunExecutionMessage; + externalMsg = DefaultCompleteExternalGunExecutionMessage; + } + args.Handled = true; + } + + _combatSystem.SetInCombatMode(attacker, prev); + component.Executing = false; + args.Handled = true; + + if (internalMsg != null && externalMsg != null) + { + ShowExecutionInternalPopup(internalMsg, attacker, victim, uid); + ShowExecutionExternalPopup(externalMsg, attacker, victim, uid); + } + } + + private void OnGetMeleeDamage(EntityUid uid, ExecutionComponent comp, ref GetMeleeDamageEvent args) + { + if (!TryComp(uid, out var melee) || + !TryComp(uid, out var execComp) || + !execComp.Executing) + { + return; + } + + var bonus = melee.Damage * execComp.DamageModifier - melee.Damage; + args.Damage += bonus; + } + + private void ShowExecutionInternalPopup(string locString, + EntityUid attacker, EntityUid victim, EntityUid weapon, bool predict = true) + { + if (predict) + { + _popupSystem.PopupClient( + Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), + attacker, + attacker, + PopupType.Medium + ); + } + else + { + _popupSystem.PopupEntity( + Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), + attacker, + Filter.Entities(attacker), + true, + PopupType.Medium + ); + } + + } + + private void ShowExecutionExternalPopup(string locString, EntityUid attacker, EntityUid victim, EntityUid weapon) + { + _popupSystem.PopupEntity( + Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), + attacker, + Filter.PvsExcept(attacker), + true, + PopupType.MediumCaution + ); + } +} diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 31a305dba99..81180ac7470 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -269,3 +269,9 @@ public record struct ProjectileReflectAttemptEvent(EntityUid ProjUid, Projectile /// [ByRefEvent] public record struct ProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, EntityUid? Shooter = null); + +/// +/// Raised after a projectile has dealt it's damage. +/// +[ByRefEvent] +public record struct AfterProjectileHitEvent(DamageSpecifier Damage, EntityUid Target); diff --git a/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs b/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs index d61862bf1a0..1626c5a63fb 100644 --- a/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs +++ b/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs @@ -21,7 +21,7 @@ public record struct ShotAttemptedEvent public bool Cancelled { get; private set; } - /// + /// /// Prevent the gun from shooting /// public void Cancel() @@ -29,7 +29,7 @@ public void Cancel() Cancelled = true; } - /// + /// /// Allow the gun to shoot again, only use if you know what you are doing /// public void Uncancel() diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 7afb41239c6..539b756a632 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -23,6 +23,7 @@ using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Whitelist; +using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -145,7 +146,7 @@ private void OnShootRequest(RequestShootEvent msg, EntitySessionEventArgs args) gun.ShootCoordinates = GetCoordinates(msg.Coordinates); gun.Target = GetEntity(msg.Target); - AttemptShoot(user.Value, ent, gun); + AttemptShootInternal(user.Value, ent, gun); } private void OnStopShootRequest(RequestStopShootEvent ev, EntitySessionEventArgs args) @@ -209,6 +210,31 @@ private void StopShooting(EntityUid uid, GunComponent gun) Dirty(uid, gun); } + /// + /// Attempts to shoot the specified target directly. + /// This may bypass projectiles firing etc. + /// + public bool AttemptDirectShoot(EntityUid user, EntityUid gunUid, EntityUid target, GunComponent gun) + { + // Unique name so people don't think it's "shoot towards" and not "I will teleport a bullet into them". + gun.ShootCoordinates = Transform(target).Coordinates; + + if (!TryTakeAmmo(user, gunUid, gun, out _, out _, out var args)) + { + gun.ShootCoordinates = null; + return false; + } + + var result = ShootDirect(gunUid, gun, target, args.Ammo, user: user); + gun.ShootCoordinates = null; + return result; + } + + protected virtual bool ShootDirect(EntityUid gunUid, GunComponent gun, EntityUid target, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityUid user) + { + return false; + } + /// /// Sets the targeted entity of the gun. Should be called before attempting to shoot to avoid shooting over the target. /// @@ -223,7 +249,7 @@ public void SetTarget(GunComponent gun, EntityUid target) public void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, EntityCoordinates toCoordinates) { gun.ShootCoordinates = toCoordinates; - AttemptShoot(user, gunUid, gun); + AttemptShootInternal(user, gunUid, gun); gun.ShotCounter = 0; } @@ -234,20 +260,35 @@ public void AttemptShoot(EntityUid gunUid, GunComponent gun) { var coordinates = new EntityCoordinates(gunUid, new Vector2(0, -1)); gun.ShootCoordinates = coordinates; - AttemptShoot(gunUid, gunUid, gun); + AttemptShootInternal(gunUid, gunUid, gun); gun.ShotCounter = 0; } - private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) + private void AttemptShootInternal(EntityUid user, EntityUid gunUid, GunComponent gun) { - if (gun.FireRateModified <= 0f || - !_actionBlockerSystem.CanAttack(user)) + if (!TryTakeAmmo(user, gunUid, gun, out var fromCoordinates, out var toCoordinates, out var args)) return; - var toCoordinates = gun.ShootCoordinates; + Shoot(gunUid, gun, args.Ammo, fromCoordinates, toCoordinates, out var userImpulse, user: user); - if (toCoordinates == null) - return; + if (userImpulse && TryComp(user, out var userPhysics)) + { + if (_gravity.IsWeightless(user, userPhysics)) + CauseImpulse(fromCoordinates, toCoordinates, user, userPhysics); + } + } + + /// + /// Validates if a gun can currently shoot. + /// + [Pure] + private bool CanShoot(EntityUid user, EntityUid gunUid, GunComponent gun) + { + if (gun.FireRateModified <= 0f || + !_actionBlockerSystem.CanAttack(user)) + { + return false; + } var curTime = Timing.CurTime; @@ -259,17 +300,42 @@ private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) }; RaiseLocalEvent(gunUid, ref prevention); if (prevention.Cancelled) - return; + return false; RaiseLocalEvent(user, ref prevention); if (prevention.Cancelled) - return; + return false; // Need to do this to play the clicking sound for empty automatic weapons // but not play anything for burst fire. if (gun.NextFire > curTime) - return; + return false; + return true; + } + + /// + /// Tries to return ammo prepped for shooting if a gun is available to shoot. + /// + private bool TryTakeAmmo( + EntityUid user, + EntityUid gunUid, GunComponent gun, + out EntityCoordinates fromCoordinates, + out EntityCoordinates toCoordinates, + [NotNullWhen(true)] out TakeAmmoEvent? args) + { + toCoordinates = EntityCoordinates.Invalid; + fromCoordinates = EntityCoordinates.Invalid; + args = null; + + if (!CanShoot(user, gunUid, gun)) + return false; + + if (gun.ShootCoordinates == null) + return false; + + toCoordinates = gun.ShootCoordinates.Value; + var curTime = Timing.CurTime; var fireRate = TimeSpan.FromSeconds(1f / gun.FireRateModified); // First shot @@ -317,10 +383,11 @@ private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) } gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds)); - return; + return false; } - var fromCoordinates = Transform(user).Coordinates; + fromCoordinates = Transform(user).Coordinates; + // Remove ammo var ev = new TakeAmmoEvent(shots, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, user); @@ -355,14 +422,13 @@ private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) // May cause prediction issues? Needs more tweaking gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds)); Audio.PlayPredicted(gun.SoundEmpty, gunUid, user); - return; + return false; } - return; + return false; } // Shoot confirmed - sounds also played here in case it's invalid (e.g. cartridge already spent). - Shoot(gunUid, gun, ev.Ammo, fromCoordinates, toCoordinates.Value, out var userImpulse, user, throwItems: attemptEv.ThrowItems); var shotEv = new GunShotEvent(user, ev.Ammo); RaiseLocalEvent(gunUid, ref shotEv); @@ -373,6 +439,8 @@ private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics); Dirty(gunUid, gun); + args = ev; + return true; } public void Shoot( diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl index 8bdf3261666..5bd4613e8c3 100644 --- a/Resources/Locale/en-US/execution/execution.ftl +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -14,17 +14,7 @@ execution-popup-gun-clumsy-internal = You miss {$victim}'s head and shoot your f execution-popup-gun-clumsy-external = {$attacker} misses {$victim} and shoots {POSS-ADJ($attacker)} foot instead! execution-popup-gun-empty = {CAPITALIZE(THE($weapon))} clicks. -suicide-popup-gun-initial-internal = You place the muzzle of {THE($weapon)} in your mouth. -suicide-popup-gun-initial-external = {$attacker} places the muzzle of {THE($weapon)} in {POSS-ADJ($attacker)} mouth. -suicide-popup-gun-complete-internal = You shoot yourself in the head! -suicide-popup-gun-complete-external = {$attacker} shoots {REFLEXIVE($attacker)} in the head! - execution-popup-melee-initial-internal = You ready {THE($weapon)} against {$victim}'s throat. execution-popup-melee-initial-external = {$attacker} readies {POSS-ADJ($attacker)} {$weapon} against the throat of {$victim}. execution-popup-melee-complete-internal = You slit the throat of {$victim}! execution-popup-melee-complete-external = {$attacker} slits the throat of {$victim}! - -suicide-popup-melee-initial-internal = You ready {THE($weapon)} against your throat. -suicide-popup-melee-initial-external = {$attacker} readies {POSS-ADJ($attacker)} {$weapon} against {POSS-ADJ($attacker)} throat. -suicide-popup-melee-complete-internal = You slit your throat with {THE($weapon)}! -suicide-popup-melee-complete-external = {$attacker} slits {POSS-ADJ($attacker)} throat with {THE($weapon)}! \ No newline at end of file diff --git a/Resources/Locale/en-US/kitchen/components/butcherable-component.ftl b/Resources/Locale/en-US/kitchen/components/butcherable-component.ftl index ff28cc44db6..4a83cd455d7 100644 --- a/Resources/Locale/en-US/kitchen/components/butcherable-component.ftl +++ b/Resources/Locale/en-US/kitchen/components/butcherable-component.ftl @@ -1,4 +1,4 @@ -butcherable-different-tool = You are going to need a different tool to butcher { THE($target) }. +butcherable-different-tool = You need a different tool to butcher { THE($target) }. butcherable-knife-butchered-success = You butcher { THE($target) } with { THE($knife) }. butcherable-need-knife = Use a sharp object to butcher { THE($target) }. butcherable-not-in-container = { CAPITALIZE(THE($target)) } can't be in a container. diff --git a/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml b/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml index 2880bd00ec4..2c818e7c973 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml @@ -6,6 +6,8 @@ description: A small piece of crystal. components: - type: Sharp + - type: Execution + doAfterDuration: 4.0 - type: Sprite layers: - sprite: Objects/Materials/Shards/crystal.rsi diff --git a/Resources/Prototypes/Entities/Objects/Materials/shards.yml b/Resources/Prototypes/Entities/Objects/Materials/shards.yml index e90aafa4142..b328265e212 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/shards.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/shards.yml @@ -5,6 +5,8 @@ description: It's a shard of some unknown material. components: - type: Sharp + - type: Execution + doAfterDuration: 4.0 - type: Sprite layers: - sprite: Objects/Materials/Shards/shard.rsi diff --git a/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml b/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml index dc9ee0d09df..e38cb94d202 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml @@ -5,6 +5,8 @@ description: In Space Glasgow this is called a conversation starter. components: - type: Sharp + - type: Execution + doAfterDuration: 4.0 - type: MeleeWeapon attackRate: .71 range: 1.4 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml index 72df09ac508..cb557e05974 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml @@ -19,6 +19,7 @@ path: /Audio/Weapons/Guns/Empty/lmg_empty.ogg - type: StaticPrice price: 500 + - type: Execution # No chamber because HMG may want its own - type: MeleeWeapon attackRate: 1.5 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml index 4b6e21a4922..0a412a5480f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml @@ -62,6 +62,7 @@ price: 500 - type: UseDelay delay: 1 + - type: Execution - type: MeleeWeapon attackRate: 1.4 damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index e95db1c6e9b..0fe74553694 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -33,6 +33,7 @@ collection: MetalThud - type: DamageOtherOnHit staminaCost: 14 + - type: Execution - type: entity name: china lake diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml index fefc41ae865..32a0e8b4df4 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml @@ -65,6 +65,7 @@ - type: Appearance - type: StaticPrice price: 500 + - type: Execution - type: AmmoCounter - type: MeleeWeapon attackRate: 1.2 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml index 0aa281b95c0..e1753ce81e0 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml @@ -51,6 +51,7 @@ gun_chamber: !type:ContainerSlot - type: StaticPrice price: 500 + - type: Execution - type: MeleeWeapon attackRate: 1.3333 damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml index 8d43953a07b..aa0de1918b6 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml @@ -54,6 +54,7 @@ gun_chamber: !type:ContainerSlot - type: StaticPrice price: 500 + - type: Execution - type: MeleeWeapon attackRate: 1.3333 damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml index 40c85374123..818d3597193 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml @@ -43,6 +43,7 @@ ents: [] - type: StaticPrice price: 500 + - type: Execution - type: MeleeWeapon attackRate: 1.4 damage: @@ -120,6 +121,7 @@ - type: Appearance - type: StaticPrice price: 500 + - type: Execution - type: MeleeWeapon attackRate: 1.4 damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml index 86b90f2bdea..7099ce6eea9 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml @@ -39,6 +39,7 @@ ents: [] - type: StaticPrice price: 500 + - type: Execution - type: MeleeWeapon attackRate: 1.3333 damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml index 0ad30e9ed6e..53f92eef66b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml @@ -50,3 +50,4 @@ collection: MetalThud - type: DamageOtherOnHit staminaCost: 4.5 + - type: Execution diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml index 2f1527d3592..aef5cfbf8d4 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml @@ -116,6 +116,7 @@ containers: storagebase: !type:Container ents: [] + - type: Execution # shoots bullets instead of throwing them, no other changes - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml index de22ef22c74..bf61e39f9eb 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml @@ -5,6 +5,8 @@ description: A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter. components: - type: Sharp + - type: Execution + doAfterDuration: 4.0 - type: Sprite sprite: Objects/Weapons/Melee/armblade.rsi state: icon diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml index df060c2503e..2136cbdcb88 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml @@ -8,6 +8,8 @@ tags: - FireAxe - type: Sharp + - type: Execution + doAfterDuration: 4.0 - type: Sprite sprite: Objects/Weapons/Melee/fireaxe.rsi state: icon diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml index 8d6496e013f..1d9302305c3 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml @@ -7,6 +7,8 @@ tags: - Knife - type: Sharp + - type: Execution + doAfterDuration: 4.0 - type: Utensil types: - Knife diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index d36f0704751..2ac023edae4 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -1,10 +1,21 @@ - type: entity - name: captain's sabre + name: Sword parent: BaseItem + id: BaseSword + description: A sharp sword. + abstract: true + components: + - type: Sharp + - type: Execution + doAfterDuration: 4.0 + - type: DisarmMalus + +- type: entity + name: captain's sabre + parent: BaseSword id: CaptainSabre description: A ceremonial weapon belonging to the captain of the station. components: - - type: Sharp - type: Sprite sprite: Objects/Weapons/Melee/captain_sabre.rsi state: icon @@ -38,15 +49,13 @@ - type: Tag tags: - CaptainSabre - - type: DisarmMalus - type: entity name: katana - parent: BaseItem + parent: BaseSword id: Katana description: Ancient craftwork made with not so ancient plasteel. components: - - type: Sharp - type: Tag tags: - Katana @@ -116,11 +125,10 @@ - type: entity name: machete - parent: BaseItem + parent: BaseSword id: Machete description: A large, vicious looking blade. components: - - type: Sharp - type: Tag tags: - Machete @@ -149,15 +157,13 @@ - type: Item size: Normal sprite: Objects/Weapons/Melee/machete.rsi - - type: DisarmMalus - type: entity name: claymore - parent: BaseItem + parent: BaseSword id: Claymore description: An ancient war blade. components: - - type: Sharp - type: Sprite sprite: Objects/Weapons/Melee/claymore.rsi state: icon @@ -190,15 +196,13 @@ sprite: Objects/Weapons/Melee/claymore.rsi slots: - back - - type: DisarmMalus - type: entity name: cutlass - parent: BaseItem + parent: BaseSword id: Cutlass description: A wickedly curved blade, often seen in the hands of space pirates. components: - - type: Sharp - type: Tag tags: - Machete @@ -228,15 +232,13 @@ - type: Item size: Normal sprite: Objects/Weapons/Melee/cutlass.rsi - - type: DisarmMalus - type: entity name: The Throngler - parent: BaseItem + parent: BaseSword id: Throngler description: Why would someone make this? components: - - type: Sharp - type: Sprite sprite: Objects/Weapons/Melee/Throngler2.rsi state: icon @@ -260,4 +262,3 @@ - type: Item size: Ginormous sprite: Objects/Weapons/Melee/Throngler-in-hand.rsi - - type: DisarmMalus From 18ee396facf04ff9dee9053f3b5d9eb3e8562890 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 04:02:58 -0400 Subject: [PATCH 040/366] maow --- .../Abilities/HealOtherPowerSystem.cs | 3 +- Content.Server/Carrying/CarryingSystem.cs | 3 +- .../Chapel/SacrificialAltarSystem.cs | 3 +- .../EntitySystems/HypospraySystem.cs | 1 + Content.Server/Cluwne/CluwneSystem.cs | 1 - Content.Server/Cocoon/CocoonerSystem.cs | 6 +- Content.Server/Flight/FlightSystem.cs | 4 +- .../EntitySystems/PuddleSystem.Spillable.cs | 96 +++---------------- .../Forensics/Systems/ScentTrackerSystem.cs | 4 +- .../LifeDrainer/LifeDrainerSystem.cs | 4 +- Content.Server/Medical/PenLightSystem.cs | 4 +- Content.Server/Paint/PaintSystem.cs | 4 +- .../Power/Systems/BatteryDrinkerSystem.cs | 4 +- .../BlindHealing/BlindHealingSystem.cs | 2 +- .../Supermatter/Systems/SupermatterSystem.cs | 4 +- Content.Server/Vampire/BloodSuckerSystem.cs | 2 +- .../BloodCult/BloodRites/BloodRitesSystem.cs | 3 +- .../Apocalypse/CultRuneApocalypseSystem.cs | 2 +- .../BloodCult/Runes/CultRuneBaseSystem.cs | 4 +- .../Runes/Rending/CultRuneRendingSystem.cs | 2 +- .../BloodCult/Spells/BloodCultSpellsSystem.cs | 2 +- .../PsionicHealOtherPowerActionEvent.cs | 5 +- .../SharedCriminalRecordsHackerSystem.cs | 3 +- .../Fluids/SharedPuddleSystem.Spillable.cs | 2 +- .../InteractionVerbPrototype.cs | 5 +- Content.Shared/Magic/SpellbookSystem.cs | 4 +- .../Medical/CPR/Systems/CPRSystem.cs | 4 +- .../Surgery/SharedSurgerySystem.Steps.cs | 4 +- .../Item/PseudoItem/SharedPseudoItemSystem.cs | 4 +- Content.Shared/Paint/PaintRemoverSystem.cs | 8 +- .../Plunger/Systems/PlungerSystem.cs | 2 +- .../SharedDeadStartupButtonSystem.cs | 2 +- 32 files changed, 58 insertions(+), 143 deletions(-) diff --git a/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs index 6a2e90dd88d..0c44a680ff1 100644 --- a/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs @@ -88,8 +88,7 @@ private void AttemptDoAfter(EntityUid uid, PsionicComponent component, PsionicHe ev.DoRevive = args.DoRevive; var doAfterArgs = new DoAfterArgs(EntityManager, uid, args.UseDelay, ev, uid, target: args.Target) { - BreakOnUserMove = args.BreakOnUserMove, - BreakOnTargetMove = args.BreakOnTargetMove, + BreakOnMove = args.BreakOnMove, Hidden = _glimmer.Glimmer > args.GlimmerDoAfterVisibilityThreshold * args.ModifiedDampening, }; diff --git a/Content.Server/Carrying/CarryingSystem.cs b/Content.Server/Carrying/CarryingSystem.cs index ca69d2f9299..9b310aa40aa 100644 --- a/Content.Server/Carrying/CarryingSystem.cs +++ b/Content.Server/Carrying/CarryingSystem.cs @@ -250,8 +250,7 @@ private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableCo var ev = new CarryDoAfterEvent(); var args = new DoAfterArgs(EntityManager, carrier, length, ev, carried, target: carried) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }; diff --git a/Content.Server/Chapel/SacrificialAltarSystem.cs b/Content.Server/Chapel/SacrificialAltarSystem.cs index e7144e1185b..f674f263f7a 100644 --- a/Content.Server/Chapel/SacrificialAltarSystem.cs +++ b/Content.Server/Chapel/SacrificialAltarSystem.cs @@ -119,8 +119,7 @@ protected override void AttemptSacrifice(Entity ent, var args = new DoAfterArgs(EntityManager, user, ent.Comp.SacrificeTime, ev, target: target, eventTarget: ent) { BreakOnDamage = true, - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, BreakOnWeightlessMove = true, NeedHand = true }; diff --git a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs index 7f0b797468c..29a03485db4 100644 --- a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs +++ b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs @@ -18,6 +18,7 @@ using Robust.Shared.GameStates; using System.Diagnostics.CodeAnalysis; using System.Linq; +using Content.Shared.Chemistry.Reagent; using Robust.Server.Audio; namespace Content.Server.Chemistry.EntitySystems; diff --git a/Content.Server/Cluwne/CluwneSystem.cs b/Content.Server/Cluwne/CluwneSystem.cs index 0e8669a0314..b91fcabb46f 100644 --- a/Content.Server/Cluwne/CluwneSystem.cs +++ b/Content.Server/Cluwne/CluwneSystem.cs @@ -17,7 +17,6 @@ using Content.Shared.Interaction.Components; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; -using Content.Shared.NameModifier.EntitySystems; using Content.Shared.Clumsy; namespace Content.Server.Cluwne; diff --git a/Content.Server/Cocoon/CocoonerSystem.cs b/Content.Server/Cocoon/CocoonerSystem.cs index 676eb9808b2..12fb8b52c24 100644 --- a/Content.Server/Cocoon/CocoonerSystem.cs +++ b/Content.Server/Cocoon/CocoonerSystem.cs @@ -135,8 +135,7 @@ private void StartCocooning(EntityUid uid, CocoonerComponent component, EntityUi var args = new DoAfterArgs(EntityManager, uid, delay, new CocoonDoAfterEvent(), uid, target: target) { - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true }; _doAfter.TryStartDoAfter(args); @@ -151,8 +150,7 @@ private void StartUnCocooning(EntityUid uid, CocoonerComponent component, Entity var args = new DoAfterArgs(EntityManager, uid, delay, new UnCocoonDoAfterEvent(), uid, target: target) { - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true }; _doAfter.TryStartDoAfter(args); diff --git a/Content.Server/Flight/FlightSystem.cs b/Content.Server/Flight/FlightSystem.cs index 39321b1e66c..86eed994329 100644 --- a/Content.Server/Flight/FlightSystem.cs +++ b/Content.Server/Flight/FlightSystem.cs @@ -67,8 +67,8 @@ private void OnToggleFlight(EntityUid uid, FlightComponent component, ToggleFlig new FlightDoAfterEvent(), uid, target: uid) { BlockDuplicate = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + , + BreakOnMove = true, BreakOnDamage = true, NeedHand = true }; diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs index e4fd3fb491c..6842349828d 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs @@ -2,6 +2,7 @@ using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; +using Content.Shared.Chemistry; using Content.Shared.Chemistry.Reagent; using Content.Shared.Clothing; using Content.Shared.CombatMode.Pacification; @@ -27,8 +28,6 @@ protected override void InitializeSpillable() SubscribeLocalEvent(SpillOnLand); // Openable handles the event if it's closed SubscribeLocalEvent(SplashOnMeleeHit, after: [typeof(OpenableSystem)]); - SubscribeLocalEvent(OnGotEquipped); - SubscribeLocalEvent(OnGotUnequipped); SubscribeLocalEvent(OnOverflow); SubscribeLocalEvent(OnDoAfter); SubscribeLocalEvent(OnAttemptPacifiedThrow); @@ -72,17 +71,22 @@ private void SplashOnMeleeHit(Entity entity, ref MeleeHitEve return; args.Handled = true; + + // First update the hit count so anything that is not reactive wont count towards the total! + foreach (var hit in args.HitEntities) + { + if (!HasComp(hit)) + hitCount -= 1; + } + foreach (var hit in args.HitEntities) { if (!HasComp(hit)) - { - hitCount -= 1; // so we don't undershoot solution calculation for actual reactive entities continue; - } var splitSolution = _solutionContainerSystem.SplitSolution(soln.Value, totalSplit / hitCount); - _adminLogger.Add(LogType.MeleeHit, $"{ToPrettyString(args.User)} splashed {SolutionContainerSystem.ToPrettyString(splitSolution):solution} from {ToPrettyString(entity.Owner):entity} onto {ToPrettyString(hit):target}"); + _adminLogger.Add(LogType.MeleeHit, $"{ToPrettyString(args.User)} splashed {SharedSolutionContainerSystem.ToPrettyString(splitSolution):solution} from {ToPrettyString(entity.Owner):entity} onto {ToPrettyString(hit):target}"); _reactive.DoEntityReaction(hit, splitSolution, ReactionMethod.Touch); _popups.PopupEntity( @@ -97,33 +101,6 @@ private void SplashOnMeleeHit(Entity entity, ref MeleeHitEve } } - private void OnGotEquipped(Entity entity, ref ClothingGotEquippedEvent args) - { - if (!entity.Comp.SpillWorn) - return; - - if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution)) - return; - - // block access to the solution while worn - AddComp(entity); - - if (solution.Volume == 0) - return; - - // spill all solution on the player - var drainedSolution = _solutionContainerSystem.Drain(entity.Owner, soln.Value, solution.Volume); - TrySplashSpillAt(entity.Owner, Transform(args.Wearer).Coordinates, drainedSolution, out _); - } - - private void OnGotUnequipped(Entity entity, ref ClothingGotUnequippedEvent args) - { - if (!entity.Comp.SpillWorn) - return; - - RemCompDeferred(entity); - } - private void SpillOnLand(Entity entity, ref LandEvent args) { if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution)) @@ -138,7 +115,7 @@ private void SpillOnLand(Entity entity, ref LandEvent args) if (args.User != null) { _adminLogger.Add(LogType.Landed, - $"{ToPrettyString(entity.Owner):entity} spilled a solution {SolutionContainerSystem.ToPrettyString(solution):solution} on landing"); + $"{ToPrettyString(entity.Owner):entity} spilled a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution} on landing"); } var drainedSolution = _solutionContainerSystem.Drain(entity.Owner, soln.Value, solution.Volume); @@ -161,57 +138,6 @@ private void OnAttemptPacifiedThrow(Entity ent, ref AttemptP args.Cancel("pacified-cannot-throw-spill"); } - private void AddSpillVerb(Entity entity, ref GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - if (!_solutionContainerSystem.TryGetSolution(args.Target, entity.Comp.SolutionName, out var soln, out var solution)) - return; - - if (_openable.IsClosed(args.Target)) - return; - - if (solution.Volume == FixedPoint2.Zero) - return; - - if (_entityManager.HasComponent(args.User)) - return; - - - Verb verb = new() - { - Text = Loc.GetString("spill-target-verb-get-data-text") - }; - - // TODO VERB ICONS spill icon? pouring out a glass/beaker? - if (entity.Comp.SpillDelay == null) - { - var target = args.Target; - verb.Act = () => - { - var puddleSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume); - TrySpillAt(Transform(target).Coordinates, puddleSolution, out _); - }; - } - else - { - var user = args.User; - verb.Act = () => - { - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, entity.Comp.SpillDelay ?? 0, new SpillDoAfterEvent(), entity.Owner, target: entity.Owner) - { - BreakOnDamage = true, - BreakOnMove = true, - NeedHand = true, - }); - }; - } - verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately. - verb.DoContactInteraction = true; - args.Verbs.Add(verb); - } - private void OnDoAfter(Entity entity, ref SpillDoAfterEvent args) { if (args.Handled || args.Cancelled || args.Args.Target == null) diff --git a/Content.Server/Forensics/Systems/ScentTrackerSystem.cs b/Content.Server/Forensics/Systems/ScentTrackerSystem.cs index e3542eaba47..14373856121 100644 --- a/Content.Server/Forensics/Systems/ScentTrackerSystem.cs +++ b/Content.Server/Forensics/Systems/ScentTrackerSystem.cs @@ -49,9 +49,9 @@ private void AttemptTrackScent(EntityUid user, EntityUid target, ScentTrackerCom var doAfterEventArgs = new DoAfterArgs(EntityManager, user, component.SniffDelay, new ScentTrackerDoAfterEvent(), user, target: target) { - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, - BreakOnTargetMove = true + }; _popupSystem.PopupEntity(Loc.GetString("start-tracking-scent", ("user", Identity.Name(user, EntityManager)), ("target", Identity.Name(target, EntityManager))), user); diff --git a/Content.Server/LifeDrainer/LifeDrainerSystem.cs b/Content.Server/LifeDrainer/LifeDrainerSystem.cs index c5955d78039..12d3bedad99 100644 --- a/Content.Server/LifeDrainer/LifeDrainerSystem.cs +++ b/Content.Server/LifeDrainer/LifeDrainerSystem.cs @@ -122,8 +122,8 @@ public bool TryDrain(Entity ent, EntityUid target) var ev = new LifeDrainDoAfterEvent(); var args = new DoAfterArgs(EntityManager, uid, comp.Delay, ev, target: target, eventTarget: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + , + BreakOnMove = true, MovementThreshold = 2f, NeedHand = false }; diff --git a/Content.Server/Medical/PenLightSystem.cs b/Content.Server/Medical/PenLightSystem.cs index f1df356cab0..6d7157b38c3 100644 --- a/Content.Server/Medical/PenLightSystem.cs +++ b/Content.Server/Medical/PenLightSystem.cs @@ -91,8 +91,8 @@ public bool TryStartExam(EntityUid uid, EntityUid target, EntityUid user, PenLig uid, target, uid) { BlockDuplicate = true, - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, + , BreakOnHandChange = true, NeedHand = true }); diff --git a/Content.Server/Paint/PaintSystem.cs b/Content.Server/Paint/PaintSystem.cs index e8ef4d21e12..a03de99e681 100644 --- a/Content.Server/Paint/PaintSystem.cs +++ b/Content.Server/Paint/PaintSystem.cs @@ -82,8 +82,8 @@ private void PrepPaint(EntityUid uid, PaintComponent component, EntityUid target target: target, used: uid) { - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, + , NeedHand = true, BreakOnHandChange = true, }); diff --git a/Content.Server/Power/Systems/BatteryDrinkerSystem.cs b/Content.Server/Power/Systems/BatteryDrinkerSystem.cs index e42783c4d8d..0080e754386 100644 --- a/Content.Server/Power/Systems/BatteryDrinkerSystem.cs +++ b/Content.Server/Power/Systems/BatteryDrinkerSystem.cs @@ -72,8 +72,8 @@ private void DrinkBattery(EntityUid target, EntityUid user, BatteryDrinkerCompon var args = new DoAfterArgs(EntityManager, user, doAfterTime, new BatteryDrinkerDoAfterEvent(), user, target) // TODO: Make this doafter loop, once we merge Upstream. { BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + , + BreakOnMove = true, Broadcast = false, DistanceThreshold = 1.35f, RequireCanInteract = true, diff --git a/Content.Server/Silicon/BlindHealing/BlindHealingSystem.cs b/Content.Server/Silicon/BlindHealing/BlindHealingSystem.cs index b9d26b59f7c..e43a690881b 100644 --- a/Content.Server/Silicon/BlindHealing/BlindHealingSystem.cs +++ b/Content.Server/Silicon/BlindHealing/BlindHealingSystem.cs @@ -57,7 +57,7 @@ private bool TryHealBlindness(EntityUid uid, EntityUid user, EntityUid target, f new DoAfterArgs(EntityManager, user, delay, new HealingDoAfterEvent(), uid, target: target, used: uid) { NeedHand = true, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnWeightlessMove = false, }; diff --git a/Content.Server/Supermatter/Systems/SupermatterSystem.cs b/Content.Server/Supermatter/Systems/SupermatterSystem.cs index 3d86f57fb84..019996374b2 100644 --- a/Content.Server/Supermatter/Systems/SupermatterSystem.cs +++ b/Content.Server/Supermatter/Systems/SupermatterSystem.cs @@ -176,8 +176,8 @@ private void OnItemInteract(EntityUid uid, SupermatterComponent sm, ref Interact { BreakOnDamage = true, BreakOnHandChange = false, - BreakOnTargetMove = true, - BreakOnUserMove = true, + , + BreakOnMove = true, BreakOnWeightlessMove = false, NeedHand = true, RequireCanInteract = true, diff --git a/Content.Server/Vampire/BloodSuckerSystem.cs b/Content.Server/Vampire/BloodSuckerSystem.cs index 41afe0d666b..b0e966a620c 100644 --- a/Content.Server/Vampire/BloodSuckerSystem.cs +++ b/Content.Server/Vampire/BloodSuckerSystem.cs @@ -134,7 +134,7 @@ public void StartSuccDoAfter(EntityUid bloodsucker, EntityUid victim, BloodSucke var args = new DoAfterArgs(EntityManager, bloodsucker, bloodSuckerComponent.Delay, new BloodSuckDoAfterEvent(), bloodsucker, target: victim) { - BreakOnTargetMove = true, + , BreakOnUserMove = false, DistanceThreshold = 2f, NeedHand = false diff --git a/Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesSystem.cs b/Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesSystem.cs index 63b1c08aaca..d22b2ced2e0 100644 --- a/Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesSystem.cs +++ b/Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesSystem.cs @@ -77,8 +77,7 @@ private void OnAfterInteract(Entity rites, ref AfterInt var time = rites.Comp.BloodExtractionTime; var doAfterArgs = new DoAfterArgs(EntityManager, args.User, time, ev, rites, args.Target) { - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, BreakOnDamage = true }; if (_doAfter.TryStartDoAfter(doAfterArgs, out var doAfterId)) diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Apocalypse/CultRuneApocalypseSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/Apocalypse/CultRuneApocalypseSystem.cs index 745ea042caf..ae72ebf1504 100644 --- a/Content.Server/WhiteDream/BloodCult/Runes/Apocalypse/CultRuneApocalypseSystem.cs +++ b/Content.Server/WhiteDream/BloodCult/Runes/Apocalypse/CultRuneApocalypseSystem.cs @@ -40,7 +40,7 @@ private void OnApocalypseRuneInvoked(Entity ent, re var doAfter = new DoAfterArgs(EntityManager, args.User, ent.Comp.InvokeTime, new ApocalypseRuneDoAfter(), ent) { - BreakOnUserMove = true + BreakOnMove = true }; _doAfter.TryStartDoAfter(doAfter); diff --git a/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseSystem.cs index 013b8df6cdd..cedf021d5f1 100644 --- a/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseSystem.cs +++ b/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseSystem.cs @@ -101,7 +101,7 @@ private void OnRuneSelected(Entity ent, ref RuneDrawerSelec var argsDoAfterEvent = new DoAfterArgs(EntityManager, args.Actor, timeToDraw, ev, args.Actor) { - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }; @@ -147,7 +147,7 @@ private void EraseOnInteractUsing(Entity rune, ref Intera var argsDoAfterEvent = new DoAfterArgs(EntityManager, args.User, runeDrawer.EraseTime, new RuneEraseDoAfterEvent(), rune) { - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, NeedHand = true }; diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Rending/CultRuneRendingSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/Rending/CultRuneRendingSystem.cs index 91347bcdd95..6bc69aba58e 100644 --- a/Content.Server/WhiteDream/BloodCult/Runes/Rending/CultRuneRendingSystem.cs +++ b/Content.Server/WhiteDream/BloodCult/Runes/Rending/CultRuneRendingSystem.cs @@ -66,7 +66,7 @@ private void OnRendingRuneInvoked(Entity rune, ref Try var ev = new RendingRuneDoAfter(); var argsDoAfterEvent = new DoAfterArgs(EntityManager, args.User, rune.Comp.SummonTime, ev, rune) { - BreakOnUserMove = true + BreakOnMove = true }; if (!_doAfter.TryStartDoAfter(argsDoAfterEvent, out rune.Comp.CurrentDoAfter)) diff --git a/Content.Server/WhiteDream/BloodCult/Spells/BloodCultSpellsSystem.cs b/Content.Server/WhiteDream/BloodCult/Spells/BloodCultSpellsSystem.cs index c69bf6abd4e..18a04392e09 100644 --- a/Content.Server/WhiteDream/BloodCult/Spells/BloodCultSpellsSystem.cs +++ b/Content.Server/WhiteDream/BloodCult/Spells/BloodCultSpellsSystem.cs @@ -148,7 +148,7 @@ private void OnSpellSelected(Entity cultist, ref createSpellEvent, cultist.Owner) { - BreakOnUserMove = true + BreakOnMove = true }; if (_doAfter.TryStartDoAfter(doAfter, out var doAfterId)) diff --git a/Content.Shared/Actions/Events/PsionicHealOtherPowerActionEvent.cs b/Content.Shared/Actions/Events/PsionicHealOtherPowerActionEvent.cs index cdcba6740c2..80ef0b0f6ce 100644 --- a/Content.Shared/Actions/Events/PsionicHealOtherPowerActionEvent.cs +++ b/Content.Shared/Actions/Events/PsionicHealOtherPowerActionEvent.cs @@ -35,10 +35,7 @@ public sealed partial class PsionicHealOtherPowerActionEvent : EntityTargetActio public bool DoRevive; [DataField] - public bool BreakOnUserMove = true; - - [DataField] - public bool BreakOnTargetMove = false; + public bool BreakOnMove = true; [DataField] public float UseDelay = 8f; diff --git a/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsHackerSystem.cs b/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsHackerSystem.cs index a626db19e5f..10faf3ebe23 100644 --- a/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsHackerSystem.cs +++ b/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsHackerSystem.cs @@ -30,8 +30,7 @@ private void OnBeforeInteractHand(Entity ent, re var doAfterArgs = new DoAfterArgs(EntityManager, ent, ent.Comp.Delay, new CriminalRecordsHackDoAfterEvent(), target: target, used: ent, eventTarget: ent) { BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnWeightlessMove = true, MovementThreshold = 0.5f, }; diff --git a/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs b/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs index 767d30389ac..2ce008da262 100644 --- a/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs +++ b/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs @@ -75,7 +75,7 @@ private void AddSpillVerb(Entity entity, ref GetVerbsEvent [DataField] - public DoAfterArgs DoAfter = new DoAfterArgs() + public DoAfterArgs DoAfter = new() { User = EntityUid.Invalid, NetUser = NetEntity.Invalid, BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, + BreakOnMove = true, BreakOnWeightlessMove = true, RequireCanInteract = false, // Never used, but must be present because the field is non-nullable and will error during serialization if not set. diff --git a/Content.Shared/Magic/SpellbookSystem.cs b/Content.Shared/Magic/SpellbookSystem.cs index a7c82746249..bb774db1c25 100644 --- a/Content.Shared/Magic/SpellbookSystem.cs +++ b/Content.Shared/Magic/SpellbookSystem.cs @@ -86,8 +86,8 @@ private void AttemptLearn(Entity ent, UseInHandEvent args) { var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.LearnTime, new SpellbookDoAfterEvent(), ent, target: ent) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + , + BreakOnMove = true, BreakOnWeightlessMove = true, BreakOnDamage = true, NeedHand = true, // What, are you going to read with your eyes only?? diff --git a/Content.Shared/Medical/CPR/Systems/CPRSystem.cs b/Content.Shared/Medical/CPR/Systems/CPRSystem.cs index e050f1b4e1d..7238fe5a097 100644 --- a/Content.Shared/Medical/CPR/Systems/CPRSystem.cs +++ b/Content.Shared/Medical/CPR/Systems/CPRSystem.cs @@ -95,8 +95,8 @@ private void StartCPR(EntityUid performer, EntityUid target, CPRTrainingComponen _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, performer, cprComponent.DoAfterDuration, new CPRDoAfterEvent(), performer, target, performer) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + , + BreakOnMove = true, NeedHand = true, BlockDuplicate = true }); diff --git a/Content.Shared/Medical/Surgery/SharedSurgerySystem.Steps.cs b/Content.Shared/Medical/Surgery/SharedSurgerySystem.Steps.cs index c914c135142..0ab76cb42f7 100644 --- a/Content.Shared/Medical/Surgery/SharedSurgerySystem.Steps.cs +++ b/Content.Shared/Medical/Surgery/SharedSurgerySystem.Steps.cs @@ -728,8 +728,8 @@ private void OnSurgeryTargetStepChosen(Entity ent, ref S var duration = GetSurgeryDuration(step, user, body, speed); var doAfter = new DoAfterArgs(EntityManager, user, TimeSpan.FromSeconds(duration), ev, body, part) { - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, + , CancelDuplicate = true, DuplicateCondition = DuplicateConditions.SameEvent, NeedHand = true, diff --git a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs index 5f4e6184346..227c13c3d2c 100644 --- a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs +++ b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs @@ -163,8 +163,8 @@ protected void StartInsertDoAfter(EntityUid inserter, EntityUid toInsert, Entity var ev = new PseudoItemInsertDoAfterEvent(); var args = new DoAfterArgs(EntityManager, inserter, 5f, ev, toInsert, toInsert, storageEntity) { - BreakOnTargetMove = true, - BreakOnUserMove = true, + , + BreakOnMove = true, NeedHand = true }; diff --git a/Content.Shared/Paint/PaintRemoverSystem.cs b/Content.Shared/Paint/PaintRemoverSystem.cs index e2565e0f220..e800a1de656 100644 --- a/Content.Shared/Paint/PaintRemoverSystem.cs +++ b/Content.Shared/Paint/PaintRemoverSystem.cs @@ -36,8 +36,8 @@ private void OnInteract(EntityUid uid, PaintRemoverComponent component, AfterInt _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new PaintRemoverDoAfterEvent(), uid, args.Target, uid) { - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, + , BreakOnDamage = true, MovementThreshold = 1.0f, }); @@ -83,8 +83,8 @@ private void OnPaintRemoveVerb(EntityUid uid, PaintRemoverComponent component, G args.Target, uid) { - BreakOnUserMove = true, - BreakOnTargetMove = true, + BreakOnMove = true, + , BreakOnDamage = true, MovementThreshold = 1.0f, }); diff --git a/Content.Shared/Plunger/Systems/PlungerSystem.cs b/Content.Shared/Plunger/Systems/PlungerSystem.cs index 6e07657941b..6117636dee9 100644 --- a/Content.Shared/Plunger/Systems/PlungerSystem.cs +++ b/Content.Shared/Plunger/Systems/PlungerSystem.cs @@ -45,7 +45,7 @@ private void OnInteract(EntityUid uid, PlungerComponent component, AfterInteract _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.PlungeDuration, new PlungerDoAfterEvent(), uid, target, uid) { - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, MovementThreshold = 1.0f, }); diff --git a/Content.Shared/Silicon/DeadStartupButton/SharedDeadStartupButtonSystem.cs b/Content.Shared/Silicon/DeadStartupButton/SharedDeadStartupButtonSystem.cs index ced89e78608..c021baa86bc 100644 --- a/Content.Shared/Silicon/DeadStartupButton/SharedDeadStartupButtonSystem.cs +++ b/Content.Shared/Silicon/DeadStartupButton/SharedDeadStartupButtonSystem.cs @@ -51,7 +51,7 @@ private void TryStartup(EntityUid user, EntityUid target, DeadStartupButtonCompo var args = new DoAfterArgs(EntityManager, user, comp.DoAfterInterval, new OnDoAfterButtonPressedEvent(), target, target:target) { BreakOnDamage = true, - BreakOnUserMove = true, + BreakOnMove = true, }; _doAfterSystem.TryStartDoAfter(args); } From 59f94a1001c6da312925a350c1242b328d63f3f5 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Fri, 9 Aug 2024 08:14:07 +0200 Subject: [PATCH 041/366] Add tooltips to the agent ID job icons and improve status icon prototypes (#28575) * add tooltips to agentid job icons * forgot to stage this * make StatusIconPrototype abstract * minor visual improvements * cleanup * use currentculture to sort job names * review --- .../UI/AgentIDCardBoundUserInterface.cs | 4 +- .../Access/UI/AgentIDCardWindow.xaml | 11 +- .../Access/UI/AgentIDCardWindow.xaml.cs | 23 ++- .../CrewManifest/UI/CrewManifestSection.cs | 2 +- .../CrewMonitoringWindow.xaml.cs | 2 +- .../Overlays/EntityHealthBarOverlay.cs | 1 + .../Overlays/ShowHealthIconsSystem.cs | 6 +- Content.Client/Overlays/ShowJobIconsSystem.cs | 4 +- .../Overlays/ShowSyndicateIconsSystem.cs | 2 +- .../Access/Components/AgentIDCardComponent.cs | 20 +- .../Access/Systems/AgentIDCardSystem.cs | 4 +- .../Medical/SuitSensors/SuitSensorSystem.cs | 3 +- .../Access/Components/IdCardComponent.cs | 16 +- .../Access/SharedAgentIDCardSystem.cs | 8 +- .../Access/Systems/SharedIdCardSystem.cs | 2 +- .../Damage/Components/DamageableComponent.cs | 7 +- .../Components/MindShieldComponent.cs | 2 +- .../NukeOps/NukeOperativeComponent.cs | 2 +- .../Nutrition/EntitySystems/HungerSystem.cs | 14 +- .../Nutrition/EntitySystems/ThirstSystem.cs | 14 +- .../Overlays/ShowHealthBarsComponent.cs | 3 + .../Components/HeadRevolutionaryComponent.cs | 2 +- .../Components/RevolutionaryComponent.cs | 2 +- Content.Shared/Roles/JobPrototype.cs | 4 +- .../SSDIndicator/SSDIndicatorComponent.cs | 4 +- .../Components/CriminalRecordComponent.cs | 2 +- .../StatusIcon/StatusIconPrototype.cs | 136 +++++++++++++- .../Zombies/InitialInfectedComponent.cs | 2 +- Content.Shared/Zombies/ZombieComponent.cs | 2 +- Resources/Locale/en-US/job/job-names.ftl | 12 ++ Resources/Prototypes/StatusEffects/health.yml | 10 +- Resources/Prototypes/StatusEffects/hunger.yml | 16 +- Resources/Prototypes/StatusEffects/job.yml | 177 ++++++++++++------ .../Prototypes/StatusEffects/satiation.yml | 57 ++++++ .../Prototypes/StatusEffects/security.yml | 22 ++- Resources/Prototypes/StatusEffects/ssd.yml | 2 +- Resources/Prototypes/StatusIcon/debug.yml | 6 +- .../StatusIcon/{antag.yml => faction.yml} | 20 +- 38 files changed, 439 insertions(+), 187 deletions(-) create mode 100644 Resources/Prototypes/StatusEffects/satiation.yml rename Resources/Prototypes/StatusIcon/{antag.yml => faction.yml} (69%) diff --git a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs index ee5ed26d2e3..c167ad660a9 100644 --- a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs +++ b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs @@ -37,7 +37,7 @@ private void OnJobChanged(string newJob) SendMessage(new AgentIDCardJobChangedMessage(newJob)); } - public void OnJobIconChanged(string newJobIconId) + public void OnJobIconChanged(ProtoId newJobIconId) { SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId)); } @@ -54,7 +54,7 @@ protected override void UpdateState(BoundUserInterfaceState state) _window.SetCurrentName(cast.CurrentName); _window.SetCurrentJob(cast.CurrentJob); - _window.SetAllowedIcons(cast.Icons, cast.CurrentJobIconId); + _window.SetAllowedIcons(cast.CurrentJobIconId); } } } diff --git a/Content.Client/Access/UI/AgentIDCardWindow.xaml b/Content.Client/Access/UI/AgentIDCardWindow.xaml index 89de793714d..7d091e4e165 100644 --- a/Content.Client/Access/UI/AgentIDCardWindow.xaml +++ b/Content.Client/Access/UI/AgentIDCardWindow.xaml @@ -6,12 +6,9 @@ [DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer)), AutoNetworkedField] public List DamageContainers = new(); + + [DataField] + public ProtoId? HealthStatusIcon = "HealthIconFine"; } diff --git a/Content.Shared/Revolutionary/Components/HeadRevolutionaryComponent.cs b/Content.Shared/Revolutionary/Components/HeadRevolutionaryComponent.cs index d2c8374fefe..060c4d022f0 100644 --- a/Content.Shared/Revolutionary/Components/HeadRevolutionaryComponent.cs +++ b/Content.Shared/Revolutionary/Components/HeadRevolutionaryComponent.cs @@ -15,7 +15,7 @@ public sealed partial class HeadRevolutionaryComponent : Component, IAntagStatus /// The status icon corresponding to the head revolutionary. /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public ProtoId StatusIcon { get; set; } = "HeadRevolutionaryFaction"; + public ProtoId StatusIcon { get; set; } = "HeadRevolutionaryFaction"; /// /// How long the stun will last after the user is converted. diff --git a/Content.Shared/Revolutionary/Components/RevolutionaryComponent.cs b/Content.Shared/Revolutionary/Components/RevolutionaryComponent.cs index 73f533cf690..322645bc0fa 100644 --- a/Content.Shared/Revolutionary/Components/RevolutionaryComponent.cs +++ b/Content.Shared/Revolutionary/Components/RevolutionaryComponent.cs @@ -16,7 +16,7 @@ public sealed partial class RevolutionaryComponent : Component, IAntagStatusIcon /// The status icon prototype displayed for revolutionaries /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public ProtoId StatusIcon { get; set; } = "RevolutionaryFaction"; + public ProtoId StatusIcon { get; set; } = "RevolutionaryFaction"; /// /// Sound that plays when you are chosen as Rev. (Placeholder until I find something cool I guess) diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index 5cf8cf38fb3..2313cc4bd08 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -106,8 +106,8 @@ public sealed partial class JobPrototype : IPrototype [DataField("jobEntity", customTypeSerializer: typeof(PrototypeIdSerializer))] public string? JobEntity = null; - [DataField("icon", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Icon { get; private set; } = "JobIconUnknown"; + [DataField] + public ProtoId Icon { get; private set; } = "JobIconUnknown"; [DataField("special", serverOnly: true)] public JobSpecial[] Special { get; private set; } = Array.Empty(); diff --git a/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs b/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs index 66310505a1a..711abdb3bfe 100644 --- a/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs +++ b/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs @@ -16,6 +16,6 @@ public sealed partial class SSDIndicatorComponent : Component public bool IsSSD = true; [ViewVariables(VVAccess.ReadWrite)] - [DataField("icon", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Icon = "SSDIcon"; + [DataField] + public ProtoId Icon = "SSDIcon"; } diff --git a/Content.Shared/Security/Components/CriminalRecordComponent.cs b/Content.Shared/Security/Components/CriminalRecordComponent.cs index 25fab3f1350..aa6bd6ad5bd 100644 --- a/Content.Shared/Security/Components/CriminalRecordComponent.cs +++ b/Content.Shared/Security/Components/CriminalRecordComponent.cs @@ -11,5 +11,5 @@ public sealed partial class CriminalRecordComponent : Component /// The icon that should be displayed based on the criminal status of the entity. /// [DataField, AutoNetworkedField] - public ProtoId StatusIcon = "SecurityIconWanted"; + public ProtoId StatusIcon = "SecurityIconWanted"; } diff --git a/Content.Shared/StatusIcon/StatusIconPrototype.cs b/Content.Shared/StatusIcon/StatusIconPrototype.cs index c5a5fd8a2c5..4ba543a2f58 100644 --- a/Content.Shared/StatusIcon/StatusIconPrototype.cs +++ b/Content.Shared/StatusIcon/StatusIconPrototype.cs @@ -45,17 +45,32 @@ public int CompareTo(StatusIconData? other) /// Sets if the icon should be rendered with or without the effect of lighting. /// [DataField] - public bool IsShaded; + public bool IsShaded = false; + + public int CompareTo(StatusIconData? other) + { + return Priority.CompareTo(other?.Priority ?? int.MaxValue); + } } /// /// but in new convenient prototype form! /// -[Prototype("statusIcon")] -public sealed partial class StatusIconPrototype : StatusIconData, IPrototype, IInheritingPrototype +public abstract partial class StatusIconPrototype : StatusIconData, IPrototype +{ + /// + [IdDataField] + public string ID { get; private set; } = default!; +} + +/// +/// StatusIcons for showing jobs on the sec HUD +/// +[Prototype] +public sealed partial class JobIconPrototype : StatusIconPrototype, IInheritingPrototype { /// - [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] public string[]? Parents { get; } /// @@ -63,9 +78,116 @@ public sealed partial class StatusIconPrototype : StatusIconData, IPrototype, II [AbstractDataField] public bool Abstract { get; } - /// - [IdDataField] - public string ID { get; private set; } = default!; + /// + /// Name of the icon used for menu tooltips. + /// + [DataField] + public string JobName { get; private set; } = string.Empty; + + [ViewVariables(VVAccess.ReadOnly)] + public string LocalizedJobName => Loc.GetString(JobName); + + /// + /// Should the agent ID or ID card console be able to use this job icon? + /// + [DataField] + public bool AllowSelection = true; +} + +/// +/// StatusIcons for the med HUD +/// +[Prototype] +public sealed partial class HealthIconPrototype : StatusIconPrototype, IInheritingPrototype +{ + /// + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; } + + /// + [NeverPushInheritance] + [AbstractDataField] + public bool Abstract { get; } +} + +/// +/// StatusIcons for the beer goggles and fried onion goggles +/// +[Prototype] +public sealed partial class SatiationIconPrototype : StatusIconPrototype, IInheritingPrototype +{ + /// + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; } + + /// + [NeverPushInheritance] + [AbstractDataField] + public bool Abstract { get; } +} + +/// +/// StatusIcons for showing the wanted status on the sec HUD +/// +[Prototype] +public sealed partial class SecurityIconPrototype : StatusIconPrototype, IInheritingPrototype +{ + /// + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; } + + /// + [NeverPushInheritance] + [AbstractDataField] + public bool Abstract { get; } +} + +/// +/// StatusIcons for faction membership +/// +[Prototype] +public sealed partial class FactionIconPrototype : StatusIconPrototype, IInheritingPrototype +{ + /// + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; } + + /// + [NeverPushInheritance] + [AbstractDataField] + public bool Abstract { get; } +} + +/// +/// StatusIcons for debugging purposes +/// +[Prototype] +public sealed partial class DebugIconPrototype : StatusIconPrototype, IInheritingPrototype +{ + /// + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; } + + /// + [NeverPushInheritance] + [AbstractDataField] + public bool Abstract { get; } +} + +/// +/// StatusIcons for the SSD indicator +/// +[Prototype] +public sealed partial class SsdIconPrototype : StatusIconPrototype, IInheritingPrototype +{ + /// + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; } + + /// + [NeverPushInheritance] + [AbstractDataField] + public bool Abstract { get; } } [Serializable, NetSerializable] diff --git a/Content.Shared/Zombies/InitialInfectedComponent.cs b/Content.Shared/Zombies/InitialInfectedComponent.cs index 3200dd7f5ee..987f9a09577 100644 --- a/Content.Shared/Zombies/InitialInfectedComponent.cs +++ b/Content.Shared/Zombies/InitialInfectedComponent.cs @@ -12,5 +12,5 @@ public sealed partial class InitialInfectedComponent : Component, IAntagStatusIc public ProtoId StatusIcon { get; set; } = "InitialInfectedFaction"; [DataField] - public bool IconVisibleToGhost { get; set; } = true; + public ProtoId StatusIcon = "InitialInfectedFaction"; } diff --git a/Content.Shared/Zombies/ZombieComponent.cs b/Content.Shared/Zombies/ZombieComponent.cs index 3673a2c51d5..0020ff17da8 100644 --- a/Content.Shared/Zombies/ZombieComponent.cs +++ b/Content.Shared/Zombies/ZombieComponent.cs @@ -95,7 +95,7 @@ public sealed partial class ZombieComponent : Component, IAntagStatusIconCompone public TimeSpan NextTick; [DataField("zombieStatusIcon")] - public ProtoId StatusIcon { get; set; } = "ZombieFaction"; + public ProtoId StatusIcon { get; set; } = "ZombieFaction"; [DataField] public bool IconVisibleToGhost { get; set; } = true; diff --git a/Resources/Locale/en-US/job/job-names.ftl b/Resources/Locale/en-US/job/job-names.ftl index 2e66ec56c5a..d65eb5c3d90 100644 --- a/Resources/Locale/en-US/job/job-names.ftl +++ b/Resources/Locale/en-US/job/job-names.ftl @@ -52,6 +52,18 @@ job-name-boxer = Boxer job-name-zookeeper = Zookeeper job-name-visitor = Visitor +# unused jobs +# these are required for the agent ID job icon tooltips +# I am keeping them for roleplaying opportunities +job-name-geneticist = Geneticist +job-name-no-id = No ID +job-name-prisoner = Prisoner +job-name-roboticist = Roboticist +job-name-syndicate = Syndicate +job-name-unknown = Unknown +job-name-virologist = Virologist +job-name-zombie = Zombie + # Role timers - Make these alphabetical or I cut you JobAtmosphericTechnician = Atmospheric Technician JobBartender = Bartender diff --git a/Resources/Prototypes/StatusEffects/health.yml b/Resources/Prototypes/StatusEffects/health.yml index 073b03a2aeb..de1b6684809 100644 --- a/Resources/Prototypes/StatusEffects/health.yml +++ b/Resources/Prototypes/StatusEffects/health.yml @@ -1,32 +1,32 @@ -- type: statusIcon +- type: healthIcon id: HealthIcon abstract: true priority: 3 locationPreference: Left isShaded: true -- type: statusIcon +- type: healthIcon parent: HealthIcon id: HealthIconFine icon: sprite: /Textures/Interface/Misc/health_icons.rsi state: Fine -- type: statusIcon +- type: healthIcon id: HealthIconCritical parent: HealthIcon icon: sprite: /Textures/Interface/Misc/health_icons.rsi state: Critical -- type: statusIcon +- type: healthIcon id: HealthIconDead parent: HealthIcon icon: sprite: /Textures/Interface/Misc/health_icons.rsi state: Dead -- type: statusIcon +- type: healthIcon id: HealthIconRotting parent: HealthIcon icon: diff --git a/Resources/Prototypes/StatusEffects/hunger.yml b/Resources/Prototypes/StatusEffects/hunger.yml index 9af246e06ee..749d1f88f18 100644 --- a/Resources/Prototypes/StatusEffects/hunger.yml +++ b/Resources/Prototypes/StatusEffects/hunger.yml @@ -1,26 +1,26 @@ #Hunger -- type: statusIcon +- type: satiationIcon id: HungerIcon abstract: true priority: 5 locationPreference: Right isShaded: true -- type: statusIcon +- type: satiationIcon id: HungerIconOverfed parent: HungerIcon icon: sprite: /Textures/Interface/Misc/food_icons.rsi state: overfed -- type: statusIcon +- type: satiationIcon id: HungerIconPeckish parent: HungerIcon icon: sprite: /Textures/Interface/Misc/food_icons.rsi state: peckish -- type: statusIcon +- type: satiationIcon id: HungerIconStarving parent: HungerIcon icon: @@ -28,28 +28,28 @@ state: starving #Thirst -- type: statusIcon +- type: satiationIcon id: ThirstIcon abstract: true priority: 5 locationPreference: Left isShaded: true -- type: statusIcon +- type: satiationIcon id: ThirstIconOverhydrated parent: ThirstIcon icon: sprite: /Textures/Interface/Misc/food_icons.rsi state: overhydrated -- type: statusIcon +- type: satiationIcon id: ThirstIconThirsty parent: ThirstIcon icon: sprite: /Textures/Interface/Misc/food_icons.rsi state: thirsty -- type: statusIcon +- type: satiationIcon id: ThirstIconParched parent: ThirstIcon icon: diff --git a/Resources/Prototypes/StatusEffects/job.yml b/Resources/Prototypes/StatusEffects/job.yml index 96ad930bd51..bb2ae3b64d0 100644 --- a/Resources/Prototypes/StatusEffects/job.yml +++ b/Resources/Prototypes/StatusEffects/job.yml @@ -1,377 +1,446 @@ -- type: statusIcon +- type: jobIcon id: JobIcon abstract: true priority: 1 locationPreference: Right isShaded: true -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconDetective icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Detective + jobName: job-name-detective -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconQuarterMaster icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: QuarterMaster + jobName: job-name-qm -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconBorg icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Borg + jobName: job-name-borg -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconBotanist icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Botanist + jobName: job-name-botanist -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconBoxer icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Boxer + jobName: job-name-boxer -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconAtmosphericTechnician icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: AtmosphericTechnician + jobName: job-name-atmostech -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconNanotrasen icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Nanotrasen + jobName: job-name-centcomoff -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconPrisoner icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Prisoner + jobName: job-name-prisoner -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconJanitor icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Janitor + jobName: job-name-janitor -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconChemist icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Chemist + jobName: job-name-chemist -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconStationEngineer icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: StationEngineer + jobName: job-name-engineer -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconSecurityOfficer icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: SecurityOfficer + jobName: job-name-security -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconNoId icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: NoId + jobName: job-name-no-id -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconChiefMedicalOfficer icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: ChiefMedicalOfficer + jobName: job-name-cmo -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconRoboticist icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Roboticist + jobName: job-name-roboticist -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconChaplain icon: sprite: /Textures/DeltaV/Interface/Misc/job_icons.rsi # DeltaV - Move Chaplain into Epistemics state: Chaplain # DeltaV - Move Chaplain into Epistemics + jobName: job-name-chaplain -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconLawyer icon: sprite: /Textures/DeltaV/Interface/Misc/job_icons.rsi # DeltaV - Move Lawyer into Justice state: Lawyer # DeltaV - Move Lawyer into Justice + jobName: job-name-lawyer -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconUnknown icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Unknown + jobName: job-name-unknown -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconLibrarian icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Librarian + jobName: job-name-librarian -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconCargoTechnician icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: CargoTechnician + jobName: job-name-cargotech -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconScientist icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Scientist + jobName: job-name-scientist -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconResearchAssistant icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: ResearchAssistant + jobName: job-name-research-assistant -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconGeneticist icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Geneticist + jobName: job-name-geneticist -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconClown icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Clown + jobName: job-name-clown -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconCaptain icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Captain + jobName: job-name-captain -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconHeadOfPersonnel icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: HeadOfPersonnel + jobName: job-name-hop -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconVirologist icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Virologist + jobName: job-name-virologist -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconShaftMiner icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: ShaftMiner + jobName: job-name-salvagespec -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconPassenger icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Passenger + jobName: job-name-passenger -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconChiefEngineer icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: ChiefEngineer + jobName: job-name-ce -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconBartender icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Bartender + jobName: job-name-bartender -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconHeadOfSecurity icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: HeadOfSecurity + jobName: job-name-hos -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconBrigmedic icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Brigmedic + jobName: job-name-brigmedic -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconMedicalDoctor icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: MedicalDoctor + jobName: job-name-doctor -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconParamedic icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Paramedic + jobName: job-name-paramedic -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconChef icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Chef + jobName: job-name-chef -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconWarden icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Warden + jobName: job-name-warden -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconResearchDirector icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: ResearchDirector + jobName: job-name-rd -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconMime icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Mime + jobName: job-name-mime -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconMusician icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Musician + jobName: job-name-musician -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconReporter icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Reporter + jobName: job-name-reporter -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconPsychologist icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Psychologist + jobName: job-name-psychologist -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconMedicalIntern icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: MedicalIntern + jobName: job-name-intern -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconTechnicalAssistant icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: TechnicalAssistant + jobName: job-name-technical-assistant -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconServiceWorker icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: ServiceWorker + jobName: job-name-serviceworker -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconSecurityCadet icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: SecurityCadet + jobName: job-name-cadet -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconZombie # This is a perfectly legitimate profession to pursue icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Zombie + jobName: job-name-zombie -- type: statusIcon +- type: jobIcon + parent: JobIcon + id: JobIconSyndicate # Just in case you want to make it official which side you are on + icon: + sprite: /Textures/Interface/Misc/job_icons.rsi + state: Syndicate + jobName: job-name-syndicate + +- type: jobIcon parent: JobIcon id: JobIconZookeeper icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Zookeeper + jobName: job-name-zookeeper -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconSeniorPhysician icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: SeniorPhysician + allowSelection: false -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconSeniorOfficer icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: SeniorOfficer + allowSelection: false -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconSeniorEngineer icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: SeniorEngineer + allowSelection: false -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconSeniorResearcher icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: SeniorResearcher + allowSelection: false -- type: statusIcon +- type: jobIcon parent: JobIcon id: JobIconVisitor icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Visitor + jobName: job-name-visitor + +- type: jobIcon + parent: JobIcon + id: JobIconAdmin + icon: + sprite: /Textures/Interface/Misc/job_icons.rsi + state: Admin + allowSelection: false diff --git a/Resources/Prototypes/StatusEffects/satiation.yml b/Resources/Prototypes/StatusEffects/satiation.yml new file mode 100644 index 00000000000..749d1f88f18 --- /dev/null +++ b/Resources/Prototypes/StatusEffects/satiation.yml @@ -0,0 +1,57 @@ +#Hunger +- type: satiationIcon + id: HungerIcon + abstract: true + priority: 5 + locationPreference: Right + isShaded: true + +- type: satiationIcon + id: HungerIconOverfed + parent: HungerIcon + icon: + sprite: /Textures/Interface/Misc/food_icons.rsi + state: overfed + +- type: satiationIcon + id: HungerIconPeckish + parent: HungerIcon + icon: + sprite: /Textures/Interface/Misc/food_icons.rsi + state: peckish + +- type: satiationIcon + id: HungerIconStarving + parent: HungerIcon + icon: + sprite: /Textures/Interface/Misc/food_icons.rsi + state: starving + +#Thirst +- type: satiationIcon + id: ThirstIcon + abstract: true + priority: 5 + locationPreference: Left + isShaded: true + +- type: satiationIcon + id: ThirstIconOverhydrated + parent: ThirstIcon + icon: + sprite: /Textures/Interface/Misc/food_icons.rsi + state: overhydrated + +- type: satiationIcon + id: ThirstIconThirsty + parent: ThirstIcon + icon: + sprite: /Textures/Interface/Misc/food_icons.rsi + state: thirsty + +- type: satiationIcon + id: ThirstIconParched + parent: ThirstIcon + icon: + sprite: /Textures/Interface/Misc/food_icons.rsi + state: parched diff --git a/Resources/Prototypes/StatusEffects/security.yml b/Resources/Prototypes/StatusEffects/security.yml index 31a22e5df36..e360ec11eb6 100644 --- a/Resources/Prototypes/StatusEffects/security.yml +++ b/Resources/Prototypes/StatusEffects/security.yml @@ -1,41 +1,51 @@ -- type: statusIcon +- type: securityIcon id: SecurityIcon abstract: true offset: 1 locationPreference: Left isShaded: true -- type: statusIcon +- type: securityIcon parent: SecurityIcon id: SecurityIconDischarged icon: sprite: /Textures/Interface/Misc/security_icons.rsi state: hud_discharged -- type: statusIcon +- type: securityIcon parent: SecurityIcon id: SecurityIconIncarcerated icon: sprite: /Textures/Interface/Misc/security_icons.rsi state: hud_incarcerated -- type: statusIcon +- type: securityIcon parent: SecurityIcon id: SecurityIconParoled icon: sprite: /Textures/Interface/Misc/security_icons.rsi state: hud_paroled -- type: statusIcon +- type: securityIcon parent: SecurityIcon id: SecurityIconSuspected icon: sprite: /Textures/Interface/Misc/security_icons.rsi state: hud_suspected -- type: statusIcon +- type: securityIcon parent: SecurityIcon id: SecurityIconWanted icon: sprite: /Textures/Interface/Misc/security_icons.rsi state: hud_wanted + +- type: securityIcon + id: MindShieldIcon + priority: 2 + locationPreference: Right + layer: Mod + isShaded: true + icon: + sprite: /Textures/Interface/Misc/job_icons.rsi + state: MindShield diff --git a/Resources/Prototypes/StatusEffects/ssd.yml b/Resources/Prototypes/StatusEffects/ssd.yml index 70253cc6b19..1d04a5349ef 100644 --- a/Resources/Prototypes/StatusEffects/ssd.yml +++ b/Resources/Prototypes/StatusEffects/ssd.yml @@ -1,4 +1,4 @@ -- type: statusIcon +- type: ssdIcon id: SSDIcon icon: sprite: /Textures/Effects/ssd.rsi diff --git a/Resources/Prototypes/StatusIcon/debug.yml b/Resources/Prototypes/StatusIcon/debug.yml index 2011c6ceeae..24981746e7e 100644 --- a/Resources/Prototypes/StatusIcon/debug.yml +++ b/Resources/Prototypes/StatusIcon/debug.yml @@ -1,17 +1,17 @@ -- type: statusIcon +- type: debugIcon id: DebugStatus icon: sprite: /Textures/Interface/Misc/research_disciplines.rsi state: civilianservices -- type: statusIcon +- type: debugIcon id: DebugStatus2 priority: 1 icon: sprite: /Textures/Interface/Misc/research_disciplines.rsi state: arsenal -- type: statusIcon +- type: debugIcon id: DebugStatus3 priority: 5 icon: diff --git a/Resources/Prototypes/StatusIcon/antag.yml b/Resources/Prototypes/StatusIcon/faction.yml similarity index 69% rename from Resources/Prototypes/StatusIcon/antag.yml rename to Resources/Prototypes/StatusIcon/faction.yml index 0dbdfce4f97..82f8ac7175e 100644 --- a/Resources/Prototypes/StatusIcon/antag.yml +++ b/Resources/Prototypes/StatusIcon/faction.yml @@ -1,42 +1,32 @@ -- type: statusIcon +- type: factionIcon id: ZombieFaction priority: 11 icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Zombie -- type: statusIcon +- type: factionIcon id: InitialInfectedFaction priority: 11 icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: InitialInfected -- type: statusIcon +- type: factionIcon id: RevolutionaryFaction priority: 11 icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Revolutionary -- type: statusIcon +- type: factionIcon id: HeadRevolutionaryFaction priority: 11 icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: HeadRevolutionary -- type: statusIcon - id: MindShieldIcon - priority: 2 - locationPreference: Right - layer: Mod - isShaded: true - icon: - sprite: /Textures/Interface/Misc/job_icons.rsi - state: MindShield - -- type: statusIcon +- type: factionIcon id: SyndicateFaction priority: 0 locationPreference: Left From 777c6bf72f0473cb2f9be24e47733b09c71067d6 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Fri, 9 Aug 2024 07:24:49 +0200 Subject: [PATCH 042/366] remove UntrackedMapBoundUserInterface (#30752) --- .../UI/StationMapBoundUserInterface.cs | 1 + .../UI/UntrackedMapBoundUserInterface.cs | 28 ------------------- ...omponent.cs => StationMapUserComponent.cs} | 6 ---- .../Pinpointer/StationMapComponent.cs | 11 ++++++++ .../Prototypes/Entities/Objects/Fun/pai.yml | 2 +- 5 files changed, 13 insertions(+), 35 deletions(-) delete mode 100644 Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs rename Content.Server/Pinpointer/{StationMapComponent.cs => StationMapUserComponent.cs} (77%) create mode 100644 Content.Shared/Pinpointer/StationMapComponent.cs diff --git a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs index 4421f8ec08e..d662dc91c66 100644 --- a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs +++ b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs @@ -1,3 +1,4 @@ +using Content.Shared.Pinpointer; using Robust.Client.UserInterface; namespace Content.Client.Pinpointer.UI; diff --git a/Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs b/Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs deleted file mode 100644 index a3ca6f65da2..00000000000 --- a/Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Robust.Client.UserInterface; - -namespace Content.Client.Pinpointer.UI; - -public sealed class UntrackedStationMapBoundUserInterface : BoundUserInterface -{ - [ViewVariables] - private StationMapWindow? _window; - - public UntrackedStationMapBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) - { - } - - protected override void Open() - { - base.Open(); - EntityUid? gridUid = null; - - // TODO: What this just looks like it's been copy-pasted wholesale from StationMapBoundUserInterface? - if (EntMan.TryGetComponent(Owner, out var xform)) - { - gridUid = xform.GridUid; - } - - _window = this.CreateWindow(); - _window.Set(gridUid, Owner); - } -} diff --git a/Content.Server/Pinpointer/StationMapComponent.cs b/Content.Server/Pinpointer/StationMapUserComponent.cs similarity index 77% rename from Content.Server/Pinpointer/StationMapComponent.cs rename to Content.Server/Pinpointer/StationMapUserComponent.cs index 942ea1aba8e..358e26d82e8 100644 --- a/Content.Server/Pinpointer/StationMapComponent.cs +++ b/Content.Server/Pinpointer/StationMapUserComponent.cs @@ -1,11 +1,5 @@ namespace Content.Server.Pinpointer; -[RegisterComponent] -public sealed partial class StationMapComponent : Component -{ - -} - /// /// Added to an entity using station map so when its parent changes we reset it. /// diff --git a/Content.Shared/Pinpointer/StationMapComponent.cs b/Content.Shared/Pinpointer/StationMapComponent.cs new file mode 100644 index 00000000000..07cc99605ed --- /dev/null +++ b/Content.Shared/Pinpointer/StationMapComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared.Pinpointer; + +[RegisterComponent] +public sealed partial class StationMapComponent : Component +{ + /// + /// Whether or not to show the user's location on the map. + /// + [DataField] + public bool ShowLocation = true; +} diff --git a/Resources/Prototypes/Entities/Objects/Fun/pai.yml b/Resources/Prototypes/Entities/Objects/Fun/pai.yml index 0adbc492308..806470607a0 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/pai.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/pai.yml @@ -17,7 +17,7 @@ type: InstrumentBoundUserInterface requireInputValidation: false enum.StationMapUiKey.Key: - type: UntrackedStationMapBoundUserInterface + type: StationMapBoundUserInterface requireInputValidation: false - type: Sprite sprite: Objects/Fun/pai.rsi From a00b6d04c569411e261c178b1f7d7881a0d52939 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 04:17:51 -0400 Subject: [PATCH 043/366] yeag --- Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs | 1 + Content.Client/Radio/Ui/IntercomBoundUserInterface.cs | 1 + Content.Shared/Access/SharedAgentIDCardSystem.cs | 2 ++ 3 files changed, 4 insertions(+) diff --git a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs index c167ad660a9..050756fcd14 100644 --- a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs +++ b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs @@ -1,4 +1,5 @@ using Content.Shared.Access.Systems; +using Content.Shared.StatusIcon; using Robust.Client.GameObjects; using Robust.Client.UserInterface; using Robust.Shared.Prototypes; diff --git a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs index 922715bdadc..401e7edd44a 100644 --- a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs +++ b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs @@ -1,4 +1,5 @@ using Content.Shared.Radio; +using Content.Shared.Radio.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Client.UserInterface; diff --git a/Content.Shared/Access/SharedAgentIDCardSystem.cs b/Content.Shared/Access/SharedAgentIDCardSystem.cs index 8c78edf74d4..aefd413de8b 100644 --- a/Content.Shared/Access/SharedAgentIDCardSystem.cs +++ b/Content.Shared/Access/SharedAgentIDCardSystem.cs @@ -1,3 +1,5 @@ +using Content.Shared.StatusIcon; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Access.Systems From 7ea137947594bdd9477f771d71230430a4043c55 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 7 Jul 2024 10:19:10 -0400 Subject: [PATCH 044/366] Intercom buffs and fixes (#29580) * Intercom buffs and fixes * remove unused bui state * mild sec intercom buff * reinforce sec intercoms --- .../Radio/EntitySystems/RadioDeviceSystem.cs | 23 +++ .../Radio/Ui/IntercomBoundUserInterface.cs | 1 + Content.Client/Radio/Ui/IntercomMenu.xaml.cs | 37 +++- .../Radio/EntitySystems/RadioDeviceSystem.cs | 83 ++++---- .../Radio/EntitySystems/RadioSystem.cs | 9 +- .../Radio/Components/IntercomComponent.cs | 21 +- .../Components/TelecomExemptComponent.cs | 9 + .../EntitySystems/EncryptionKeySystem.cs | 14 +- Content.Shared/Radio/SharedIntercom.cs | 20 +- Content.Shared/Wires/SharedWiresSystem.cs | 6 + .../en-US/radio/components/intercom.ftl | 3 +- Resources/Migrations/migration.yml | 26 +++ .../Entities/Objects/Devices/radio.yml | 3 +- .../Structures/Wallmounts/intercom.yml | 186 +++++++++++++----- .../Graphs/utilities/intercom.yml | 41 +++- .../Recipes/Construction/utilities.yml | 2 +- .../Wallmounts/intercom.rsi/panel.png | Bin 656 -> 316 bytes .../Wallmounts/intercom.rsi/speaker.png | Bin 157 -> 134 bytes 18 files changed, 357 insertions(+), 127 deletions(-) create mode 100644 Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs create mode 100644 Content.Shared/Radio/Components/TelecomExemptComponent.cs diff --git a/Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs new file mode 100644 index 00000000000..29d6c635ebc --- /dev/null +++ b/Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs @@ -0,0 +1,23 @@ +using Content.Client.Radio.Ui; +using Content.Shared.Radio; +using Content.Shared.Radio.Components; +using Robust.Client.GameObjects; + +namespace Content.Client.Radio.EntitySystems; + +public sealed class RadioDeviceSystem : EntitySystem +{ + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnAfterHandleState); + } + + private void OnAfterHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (_ui.TryGetOpenUi(ent.Owner, IntercomUiKey.Key, out var bui)) + bui.Update(ent); + } +} diff --git a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs index 401e7edd44a..cb95d574399 100644 --- a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs +++ b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Radio; using Content.Shared.Radio.Components; +using Content.Shared.Radio.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Client.UserInterface; diff --git a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs index ea040d1e44c..e6645acb7ba 100644 --- a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs +++ b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs @@ -1,9 +1,10 @@ using System.Threading.Channels; using Content.Client.UserInterface.Controls; -using Content.Shared.Radio; +using Content.Shared.Radio.Components; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.XAML; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Client.Radio.Ui; @@ -18,38 +19,54 @@ public sealed partial class IntercomMenu : FancyWindow private readonly List _channels = new(); - public IntercomMenu() + public IntercomMenu(Entity entity) { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); MicButton.OnPressed += args => OnMicPressed?.Invoke(args.Button.Pressed); SpeakerButton.OnPressed += args => OnSpeakerPressed?.Invoke(args.Button.Pressed); + + Update(entity); } - public void Update(IntercomBoundUIState state) + public void Update(Entity entity) { - MicButton.Pressed = state.MicEnabled; - SpeakerButton.Pressed = state.SpeakerEnabled; + MicButton.Pressed = entity.Comp.MicrophoneEnabled; + SpeakerButton.Pressed = entity.Comp.SpeakerEnabled; + + MicButton.Disabled = entity.Comp.SupportedChannels.Count == 0; + SpeakerButton.Disabled = entity.Comp.SupportedChannels.Count == 0; + ChannelOptions.Disabled = entity.Comp.SupportedChannels.Count == 0; ChannelOptions.Clear(); _channels.Clear(); - for (var i = 0; i < state.AvailableChannels.Count; i++) + for (var i = 0; i < entity.Comp.SupportedChannels.Count; i++) { - var channel = state.AvailableChannels[i]; - if (!_prototype.TryIndex(channel, out var prototype)) + var channel = entity.Comp.SupportedChannels[i]; + if (!_prototype.TryIndex(channel, out var prototype)) continue; _channels.Add(channel); ChannelOptions.AddItem(Loc.GetString(prototype.Name), i); - if (channel == state.SelectedChannel) + if (channel == entity.Comp.CurrentChannel) ChannelOptions.Select(i); } + + if (entity.Comp.SupportedChannels.Count == 0) + { + ChannelOptions.AddItem(Loc.GetString("intercom-options-none"), 0); + ChannelOptions.Select(0); + } + ChannelOptions.OnItemSelected += args => { + if (!_channels.TryGetValue(args.Id, out var proto)) + return; + ChannelOptions.SelectId(args.Id); - OnChannelSelected?.Invoke(_channels[args.Id]); + OnChannelSelected?.Invoke(proto); }; } } diff --git a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs index 0cb20b3187b..839bb669221 100644 --- a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs +++ b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Server.Chat.Systems; using Content.Server.Interaction; using Content.Server.Language; @@ -7,14 +8,11 @@ using Content.Server.Radio.Components; using Content.Server.Speech; using Content.Server.Speech.Components; -using Content.Shared.UserInterface; -using Content.Shared.Chat; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Power; using Content.Shared.Radio; using Content.Shared.Radio.Components; -using Robust.Server.GameObjects; using Robust.Shared.Prototypes; namespace Content.Server.Radio.EntitySystems; @@ -50,7 +48,7 @@ public override void Initialize() SubscribeLocalEvent(OnActivateSpeaker); SubscribeLocalEvent(OnReceiveRadio); - SubscribeLocalEvent(OnBeforeIntercomUiOpen); + SubscribeLocalEvent(OnIntercomEncryptionChannelsChanged); SubscribeLocalEvent(OnToggleIntercomMic); SubscribeLocalEvent(OnToggleIntercomSpeaker); SubscribeLocalEvent(OnSelectIntercomChannel); @@ -153,18 +151,18 @@ public void ToggleRadioSpeaker(EntityUid uid, EntityUid user, bool quiet = false SetSpeakerEnabled(uid, user, !component.Enabled, quiet, component); } - public void SetSpeakerEnabled(EntityUid uid, EntityUid user, bool enabled, bool quiet = false, RadioSpeakerComponent? component = null) + public void SetSpeakerEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioSpeakerComponent? component = null) { if (!Resolve(uid, ref component)) return; component.Enabled = enabled; - if (!quiet) + if (!quiet && user != null) { var state = Loc.GetString(component.Enabled ? "handheld-radio-component-on-state" : "handheld-radio-component-off-state"); var message = Loc.GetString("handheld-radio-component-on-use", ("radioState", state)); - _popup.PopupEntity(message, user, user); + _popup.PopupEntity(message, user.Value, user.Value); } _appearance.SetData(uid, RadioDeviceVisuals.Speaker, component.Enabled); @@ -216,7 +214,8 @@ private void OnReceiveRadio(EntityUid uid, RadioSpeakerComponent component, ref var nameEv = new TransformSpeakerSpeechEvent(args.MessageSource, Name(args.MessageSource)); RaiseLocalEvent(args.MessageSource, nameEv); - var name = Loc.GetString("speech-name-relay", ("speaker", Name(uid)), + var name = Loc.GetString("speech-name-relay", + ("speaker", Name(uid)), ("originalName", nameEv.VoiceName ?? Name(args.MessageSource))); // log to chat so people can identity the speaker/source, but avoid clogging ghost chat if there are many radios @@ -224,54 +223,66 @@ private void OnReceiveRadio(EntityUid uid, RadioSpeakerComponent component, ref _chat.TrySendInGameICMessage(uid, message, InGameICChatType.Whisper, ChatTransmitRange.GhostRangeLimit, nameOverride: name, checkRadioPrefix: false, languageOverride: args.Language); } - private void OnBeforeIntercomUiOpen(EntityUid uid, IntercomComponent component, BeforeActivatableUIOpenEvent args) + private void OnIntercomEncryptionChannelsChanged(Entity ent, ref EncryptionChannelsChangedEvent args) { - UpdateIntercomUi(uid, component); + ent.Comp.SupportedChannels = args.Component.Channels.Select(p => new ProtoId(p)).ToList(); + + var channel = args.Component.DefaultChannel; + if (ent.Comp.CurrentChannel != null && ent.Comp.SupportedChannels.Contains(ent.Comp.CurrentChannel.Value)) + channel = ent.Comp.CurrentChannel; + + SetIntercomChannel(ent, channel); } - private void OnToggleIntercomMic(EntityUid uid, IntercomComponent component, ToggleIntercomMicMessage args) + private void OnToggleIntercomMic(Entity ent, ref ToggleIntercomMicMessage args) { - if (component.RequiresPower && !this.IsPowered(uid, EntityManager)) + if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager)) return; - SetMicrophoneEnabled(uid, args.Actor, args.Enabled, true); - UpdateIntercomUi(uid, component); + SetMicrophoneEnabled(ent, args.Actor, args.Enabled, true); + ent.Comp.MicrophoneEnabled = args.Enabled; + Dirty(ent); } - private void OnToggleIntercomSpeaker(EntityUid uid, IntercomComponent component, ToggleIntercomSpeakerMessage args) + private void OnToggleIntercomSpeaker(Entity ent, ref ToggleIntercomSpeakerMessage args) { - if (component.RequiresPower && !this.IsPowered(uid, EntityManager)) + if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager)) return; - SetSpeakerEnabled(uid, args.Actor, args.Enabled, true); - UpdateIntercomUi(uid, component); + SetSpeakerEnabled(ent, args.Actor, args.Enabled, true); + ent.Comp.SpeakerEnabled = args.Enabled; + Dirty(ent); } - private void OnSelectIntercomChannel(EntityUid uid, IntercomComponent component, SelectIntercomChannelMessage args) + private void OnSelectIntercomChannel(Entity ent, ref SelectIntercomChannelMessage args) { - if (component.RequiresPower && !this.IsPowered(uid, EntityManager)) + if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager)) return; - if (!_protoMan.TryIndex(args.Channel, out _) || !component.SupportedChannels.Contains(args.Channel)) + if (!_protoMan.HasIndex(args.Channel) || !ent.Comp.SupportedChannels.Contains(args.Channel)) return; - if (TryComp(uid, out var mic)) - mic.BroadcastChannel = args.Channel; - if (TryComp(uid, out var speaker)) - speaker.Channels = new(){ args.Channel }; - UpdateIntercomUi(uid, component); + SetIntercomChannel(ent, args.Channel); } - private void UpdateIntercomUi(EntityUid uid, IntercomComponent component) + private void SetIntercomChannel(Entity ent, ProtoId? channel) { - var micComp = CompOrNull(uid); - var speakerComp = CompOrNull(uid); - - var micEnabled = micComp?.Enabled ?? false; - var speakerEnabled = speakerComp?.Enabled ?? false; - var availableChannels = component.SupportedChannels; - var selectedChannel = micComp?.BroadcastChannel ?? SharedChatSystem.CommonChannel; - var state = new IntercomBoundUIState(micEnabled, speakerEnabled, availableChannels, selectedChannel); - _ui.SetUiState(uid, IntercomUiKey.Key, state); + ent.Comp.CurrentChannel = channel; + + if (channel == null) + { + SetSpeakerEnabled(ent, null, false); + SetMicrophoneEnabled(ent, null, false); + ent.Comp.MicrophoneEnabled = false; + ent.Comp.SpeakerEnabled = false; + Dirty(ent); + return; + } + + if (TryComp(ent, out var mic)) + mic.BroadcastChannel = channel; + if (TryComp(ent, out var speaker)) + speaker.Channels = new(){ channel }; + Dirty(ent); } } diff --git a/Content.Server/Radio/EntitySystems/RadioSystem.cs b/Content.Server/Radio/EntitySystems/RadioSystem.cs index 66dadc85b5d..aff4d82d874 100644 --- a/Content.Server/Radio/EntitySystems/RadioSystem.cs +++ b/Content.Server/Radio/EntitySystems/RadioSystem.cs @@ -35,11 +35,15 @@ public sealed class RadioSystem : EntitySystem // set used to prevent radio feedback loops. private readonly HashSet _messages = new(); + private EntityQuery _exemptQuery; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnIntrinsicReceive); SubscribeLocalEvent(OnIntrinsicSpeak); + + _exemptQuery = GetEntityQuery(); } private void OnIntrinsicSpeak(EntityUid uid, IntrinsicRadioTransmitterComponent component, EntitySpokeEvent args) @@ -118,9 +122,8 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann var sourceMapId = Transform(radioSource).MapID; var hasActiveServer = HasActiveServer(sourceMapId, channel.ID); - var hasMicro = HasComp(radioSource); + var sourceServerExempt = _exemptQuery.HasComp(radioSource); - var speakerQuery = GetEntityQuery(); var radioQuery = EntityQueryEnumerator(); while (canSend && radioQuery.MoveNext(out var receiver, out var radio, out var transform)) { @@ -135,7 +138,7 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann continue; // don't need telecom server for long range channels or handheld radios and intercoms - var needServer = !channel.LongRange && (!hasMicro || !speakerQuery.HasComponent(receiver)); + var needServer = !channel.LongRange && !sourceServerExempt; if (needServer && !hasActiveServer) continue; diff --git a/Content.Shared/Radio/Components/IntercomComponent.cs b/Content.Shared/Radio/Components/IntercomComponent.cs index be2734ff168..8d7b87597b1 100644 --- a/Content.Shared/Radio/Components/IntercomComponent.cs +++ b/Content.Shared/Radio/Components/IntercomComponent.cs @@ -1,23 +1,32 @@ using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; +using Robust.Shared.Prototypes; namespace Content.Shared.Radio.Components; /// /// Handles intercom ui and is authoritative on the channels an intercom can access. /// -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] public sealed partial class IntercomComponent : Component { /// - /// Does this intercom require popwer to function + /// Does this intercom require power to function /// - [DataField("requiresPower"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public bool RequiresPower = true; + [DataField, AutoNetworkedField] + public bool SpeakerEnabled; + + [DataField, AutoNetworkedField] + public bool MicrophoneEnabled; + + [DataField, AutoNetworkedField] + public ProtoId? CurrentChannel; + /// /// The list of radio channel prototypes this intercom can choose between. /// - [DataField("supportedChannels", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List SupportedChannels = new(); + [DataField, AutoNetworkedField] + public List> SupportedChannels = new(); } diff --git a/Content.Shared/Radio/Components/TelecomExemptComponent.cs b/Content.Shared/Radio/Components/TelecomExemptComponent.cs new file mode 100644 index 00000000000..7af5c1c78ca --- /dev/null +++ b/Content.Shared/Radio/Components/TelecomExemptComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Radio.Components; + +/// +/// This is used for a radio that doesn't need a telecom server in order to broadcast. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class TelecomExemptComponent : Component; diff --git a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs index a15b6f76e90..5a384173d0c 100644 --- a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs +++ b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs @@ -31,6 +31,7 @@ public sealed partial class EncryptionKeySystem : EntitySystem [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly SharedWiresSystem _wires = default!; public override void Initialize() { @@ -150,7 +151,7 @@ private void TryRemoveKey(EntityUid uid, EncryptionKeyHolderComponent component, return; } - if (TryComp(uid, out var panel) && !panel.Open) + if (!_wires.IsPanelOpen(uid)) { _popup.PopupClient(Loc.GetString("encryption-keys-panel-locked"), uid, args.User); return; @@ -186,8 +187,15 @@ private void OnHolderExamined(EntityUid uid, EncryptionKeyHolderComponent compon if (component.Channels.Count > 0) { - args.PushMarkup(Loc.GetString("examine-encryption-channels-prefix")); - AddChannelsExamine(component.Channels, component.DefaultChannel, args, _protoManager, "examine-encryption-channel"); + using (args.PushGroup(nameof(EncryptionKeyComponent))) + { + args.PushMarkup(Loc.GetString("examine-encryption-channels-prefix")); + AddChannelsExamine(component.Channels, + component.DefaultChannel, + args, + _protoManager, + "examine-encryption-channel"); + } } } diff --git a/Content.Shared/Radio/SharedIntercom.cs b/Content.Shared/Radio/SharedIntercom.cs index 410843312fd..f697add8b90 100644 --- a/Content.Shared/Radio/SharedIntercom.cs +++ b/Content.Shared/Radio/SharedIntercom.cs @@ -1,4 +1,5 @@ -using Robust.Shared.Serialization; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; namespace Content.Shared.Radio; @@ -8,23 +9,6 @@ public enum IntercomUiKey Key, } -[Serializable, NetSerializable] -public sealed class IntercomBoundUIState : BoundUserInterfaceState -{ - public bool MicEnabled; - public bool SpeakerEnabled; - public List AvailableChannels; - public string SelectedChannel; - - public IntercomBoundUIState(bool micEnabled, bool speakerEnabled, List availableChannels, string selectedChannel) - { - MicEnabled = micEnabled; - SpeakerEnabled = speakerEnabled; - AvailableChannels = availableChannels; - SelectedChannel = selectedChannel; - } -} - [Serializable, NetSerializable] public sealed class ToggleIntercomMicMessage : BoundUserInterfaceMessage { diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index b4b0768e0f7..24f3ad8e764 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -18,11 +18,17 @@ public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnPanelDoAfter); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnExamine); } + private void OnStartup(Entity ent, ref ComponentStartup args) + { + UpdateAppearance(ent, ent); + } + private void OnPanelDoAfter(EntityUid uid, WiresPanelComponent panel, WirePanelDoAfterEvent args) { if (args.Cancelled) diff --git a/Resources/Locale/en-US/radio/components/intercom.ftl b/Resources/Locale/en-US/radio/components/intercom.ftl index e56e3cd0f73..63303999c21 100644 --- a/Resources/Locale/en-US/radio/components/intercom.ftl +++ b/Resources/Locale/en-US/radio/components/intercom.ftl @@ -1,5 +1,6 @@ intercom-menu-title = Intercom intercom-channel-label = Channel: intercom-button-text-mic = Mic. -intercom-button-text-speaker = Speak +intercom-button-text-speaker = Spkr. +intercom-options-none = No channels intercom-flavor-text-left = Keep lines free of chatter diff --git a/Resources/Migrations/migration.yml b/Resources/Migrations/migration.yml index 3ac6b353a9b..7b5b87d7865 100644 --- a/Resources/Migrations/migration.yml +++ b/Resources/Migrations/migration.yml @@ -244,3 +244,29 @@ ReinforcementRadioSyndicateMonkeyNukeops: ReinforcementRadioSyndicateAncestorNuk # 2024-05-01 DrinkBottleGoldschlager: DrinkBottleGildlager + +# 2024-05-14 +soda_dispenser: SodaDispenser +chem_master: ChemMaster + +# 2024-05-21 +CrateJanitorExplosive: ClosetJanitorBombFilled + +# 2024-05-27 +DoorRemoteFirefight: null + +# 2024-06-03 +AirlockServiceCaptainLocked: AirlockCaptainLocked + +# 2024-06-15 +ClothingOuterCoatInspector: ClothingOuterCoatDetectiveLoadout + +# 2024-06-23 +FloorTileItemReinforced: PartRodMetal1 + + +#2024-06-25 +BookChefGaming: BookHowToCookForFortySpaceman + +#2024-06-29 +IntercomAssesmbly: IntercomAssembly diff --git a/Resources/Prototypes/Entities/Objects/Devices/radio.yml b/Resources/Prototypes/Entities/Objects/Devices/radio.yml index 43f84fe404e..77b6cac2d37 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/radio.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/radio.yml @@ -4,6 +4,7 @@ parent: BaseItem id: RadioHandheld components: + - type: TelecomExempt - type: RadioMicrophone broadcastChannel: Handheld - type: RadioSpeaker @@ -39,4 +40,4 @@ sprite: Objects/Devices/securityhandy.rsi - type: Item sprite: Objects/Devices/securityhandy.rsi - heldPrefix: walkietalkie \ No newline at end of file + heldPrefix: walkietalkie diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml index 6c6b8bf57e9..9a244c2c598 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml @@ -1,5 +1,5 @@ - type: entity - id: Intercom + id: BaseIntercom name: intercom description: An intercom. For when the station just needs to know something. abstract: true @@ -9,6 +9,10 @@ - type: Electrified enabled: false usesApcPower: true + - type: TelecomExempt + - type: EncryptionKeyHolder + keySlots: 3 + keysExtractionMethod: Prying - type: RadioMicrophone powerRequired: true unobstructedRequired: true @@ -27,12 +31,14 @@ - type: InteractionOutline - type: Appearance - type: WiresVisuals + - type: WiresPanelSecurity - type: ContainerFill containers: board: [ IntercomElectronics ] - type: ContainerContainer containers: board: !type:Container + key_slots: !type:Container - type: Sprite noRot: false drawdepth: SmallObjects @@ -52,7 +58,6 @@ visible: false - state: panel map: ["enum.WiresVisualLayers.MaintenancePanel"] - shader: unshaded visible: false - type: Transform noRot: false @@ -64,6 +69,7 @@ - type: ActivatableUIRequiresPower - type: ActivatableUI key: enum.IntercomUiKey.Key + singleUser: true - type: UserInterface interfaces: enum.IntercomUiKey.Key: @@ -119,7 +125,7 @@ - Wallmount - type: entity - id: IntercomAssesmbly + id: IntercomAssembly name: intercom assembly description: An intercom. It doesn't seem very helpful right now. components: @@ -129,7 +135,18 @@ - type: Sprite drawdepth: SmallObjects sprite: Structures/Wallmounts/intercom.rsi - state: build + layers: + - state: build + - state: panel + visible: false + map: [ "wires" ] + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ConstructionVisuals.Layer: + wires: + 0: { visible: false } + 1: { visible: true } - type: Construction graph: Intercom node: assembly @@ -140,97 +157,176 @@ snap: - Wallmount +# this weird inheritance BS exists for construction shitcode +- type: entity + id: IntercomConstructed + parent: BaseIntercom + suffix: Empty, Panel Open + components: + - type: Sprite + layers: + - state: base + - state: unshaded + map: ["enum.PowerDeviceVisualLayers.Powered"] + shader: unshaded + - state: broadcasting + map: ["enum.RadioDeviceVisualLayers.Broadcasting"] + shader: unshaded + visible: false + - state: speaker + map: ["enum.RadioDeviceVisualLayers.Speaker"] + shader: unshaded + visible: false + - state: panel + map: ["enum.WiresVisualLayers.MaintenancePanel"] + visible: true + - type: WiresPanel + open: true + +- type: entity + id: Intercom + parent: IntercomConstructed + suffix: "" + components: + - type: Sprite + layers: + - state: base + - state: unshaded + map: ["enum.PowerDeviceVisualLayers.Powered"] + shader: unshaded + - state: broadcasting + map: ["enum.RadioDeviceVisualLayers.Broadcasting"] + shader: unshaded + visible: false + - state: speaker + map: ["enum.RadioDeviceVisualLayers.Speaker"] + shader: unshaded + visible: false + - state: panel + map: ["enum.WiresVisualLayers.MaintenancePanel"] + - type: WiresPanel + open: false + - type: entity id: IntercomCommon parent: Intercom suffix: Common components: - - type: Intercom - supportedChannels: - - Common + - type: ContainerFill + containers: + board: + - IntercomElectronics + key_slots: + - EncryptionKeyCommon - type: entity id: IntercomCommand parent: Intercom suffix: Command components: - - type: Intercom - supportedChannels: - - Common - - Command + - type: ContainerFill + containers: + board: + - IntercomElectronics + key_slots: + - EncryptionKeyCommon + - EncryptionKeyCommand - type: entity id: IntercomEngineering parent: Intercom suffix: Engineering components: - - type: Intercom - supportedChannels: - - Common - - Engineering + - type: ContainerFill + containers: + board: + - IntercomElectronics + key_slots: + - EncryptionKeyCommon + - EncryptionKeyEngineering - type: entity id: IntercomMedical parent: Intercom suffix: Medical components: - - type: Intercom - supportedChannels: - - Common - - Medical + - type: ContainerFill + containers: + board: + - IntercomElectronics + key_slots: + - EncryptionKeyCommon + - EncryptionKeyMedical - type: entity id: IntercomScience parent: Intercom suffix: Epistemics # DeltaV - Epistemics Department replacing Science components: - - type: Intercom - supportedChannels: - - Common - - Science + - type: ContainerFill + containers: + board: + - IntercomElectronics + key_slots: + - EncryptionKeyCommon + - EncryptionKeyScience - type: entity id: IntercomSecurity parent: Intercom suffix: Security + description: An intercom. It's been reinforced with metal from security helmets, making it a bitch-and-a-half to open. components: - - type: Intercom - supportedChannels: - - Common - - Security + - type: WiresPanel + openDelay: 5 + - type: WiresPanelSecurity + examine: wires-panel-component-on-examine-security-level2 + wiresAccessible: false + - type: Construction + node: intercomReinforced + - type: ContainerFill + containers: + board: + - IntercomElectronics + key_slots: + - EncryptionKeyCommon + - EncryptionKeySecurity - type: entity id: IntercomService parent: Intercom suffix: Service components: - - type: Intercom - supportedChannels: - - Common - - Service + - type: ContainerFill + containers: + board: + - IntercomElectronics + key_slots: + - EncryptionKeyCommon + - EncryptionKeyService - type: entity id: IntercomSupply parent: Intercom suffix: Supply components: - - type: Intercom - supportedChannels: - - Common - - Supply + - type: ContainerFill + containers: + board: + - IntercomElectronics + key_slots: + - EncryptionKeyCommon + - EncryptionKeyCargo - type: entity id: IntercomAll parent: Intercom suffix: All components: - - type: Intercom - supportedChannels: - - Common - - Command - - Engineering - - Medical - - Science - - Security - - Service - - Supply + - type: ContainerFill + containers: + board: + - IntercomElectronics + key_slots: + - EncryptionKeyCommon + - EncryptionKeyStationMaster diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml index 2247860f892..ba29d72539a 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml @@ -11,13 +11,17 @@ doAfter: 2.0 - node: assembly - entity: IntercomAssesmbly + entity: IntercomAssembly edges: - to: wired steps: - material: Cable amount: 2 doAfter: 1 + completed: + - !type:VisualizerDataInt + key: "enum.ConstructionVisuals.Layer" + data: 1 - to: start completed: - !type:GivePrototype @@ -29,7 +33,7 @@ doAfter: 2 - node: wired - entity: IntercomAssesmbly + entity: IntercomAssembly edges: - to: electronics steps: @@ -45,6 +49,9 @@ - !type:GivePrototype prototype: CableApcStack1 amount: 2 + - !type:VisualizerDataInt + key: "enum.ConstructionVisuals.Layer" + data: 0 steps: - tool: Cutting doAfter: 1 @@ -57,7 +64,11 @@ doAfter: 2 - node: intercom - entity: IntercomCommon #TODO: make this work with encryption keys + entity: IntercomConstructed + doNotReplaceInheritingEntities: true + actions: + - !type:SetWiresPanelSecurity + wiresAccessible: true edges: - to: wired conditions: @@ -72,3 +83,27 @@ steps: - tool: Prying doAfter: 1 + - to: intercomReinforced + conditions: + - !type:WirePanel + steps: + - material: Steel + amount: 1 + - tool: Welding + doAfter: 1 + + - node: intercomReinforced + actions: + - !type:SetWiresPanelSecurity + examine: wires-panel-component-on-examine-security-level2 + wiresAccessible: false + edges: + - to: intercom + conditions: + - !type:WirePanel + completed: + - !type:GivePrototype + prototype: SheetSteel1 + steps: + - tool: Welding + doAfter: 5 diff --git a/Resources/Prototypes/Recipes/Construction/utilities.yml b/Resources/Prototypes/Recipes/Construction/utilities.yml index 19f2fee1837..82c16de7b6a 100644 --- a/Resources/Prototypes/Recipes/Construction/utilities.yml +++ b/Resources/Prototypes/Recipes/Construction/utilities.yml @@ -790,7 +790,7 @@ # INTERCOM - type: construction name: intercom - id: IntercomAssesmbly + id: IntercomAssembly graph: Intercom startNode: start targetNode: intercom diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/panel.png b/Resources/Textures/Structures/Wallmounts/intercom.rsi/panel.png index 3bfeb8df5844cecfc419ac23d668a8a800cdf8fa..68f4cd1240b80165a5489ef7287b8bc3b48dc143 100644 GIT binary patch delta 290 zcmV+-0p0$P1-t@~BYyw^b5ch_0Itp)=>Px#_DMuRRCt{2+OZOXFbsg<=;^2qjx0Ql z@BapLaq`rIJb(_aa$Z`}9QOYf1_~FrmTMA30002crcHDJ7Jc8pevL^fnVxBzA5IKa zmGfl~Rh1znQB_I9=a^%4FJNgbf&6gl(`hU^|IIEpm~LOk^AFmy6389 zfQMDh0G&p;?dLc47000000DwPGT*39M6xXOiFH#fqY?gR8Vwx|CvII&l7V&MC)M&&M|0LzvEUD3m oY5p5fJOezNB^xwi0040H2AB(}cg~IGwEzGB07*qoM6N<$f(<}`Bme*a delta 632 zcmV-;0*C#)0+0oeBYy%{NklBjE6vzK^Tgi*28w$-(*@ohRaW*gM61hvi zKqik`0$Q{OLczNhL%%@3gOd){U?gN=MYeH3I=$cY^ai1K zdjETO?+zdcf*|&1U>JgeFEO>M56I2~z!(#k!=V@$hGN9%Y{lX!q-A{CWeAb3A9yt`?Zj=ZaJkI6m%fWe4|*HJ?-&003GC zXkLeys%oYUpzs;6*T-$E?V<-y5Cl<*N$Mph-C9|K{}_hQIxAn6z0b@0z#?nkZ*+i^-Ef#!9Hf6WFw&f#&6=-S?M%~K_B@@rWZMy?BBb@?g${RdN3Eo24& zK)YWDuyHy~8o6%#odd%Vo3cTgDv*|d$2n}r!Pkq6-6fEuOjRH$0o!rn%sproDC#08 zs|w^Do=CHtJ!lmuE1Rl791K;E~hzhr(peRbtwc(8&sGll0< T#mz~RK*~H_{an^LB{Ts5O_nYE From 535d91575552f4cb7cbd881b3555582e67e47944 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 04:51:05 -0400 Subject: [PATCH 045/366] use BreakOnMove --- Content.Client/Radio/Ui/IntercomMenu.xaml.cs | 5 +---- Content.Server/Flight/FlightSystem.cs | 3 +-- Content.Server/LifeDrainer/LifeDrainerSystem.cs | 1 - Content.Server/Medical/PenLightSystem.cs | 1 - Content.Server/Paint/PaintSystem.cs | 1 - Content.Server/Power/Generator/PortableGeneratorSystem.cs | 4 +++- Content.Server/Power/Systems/BatteryDrinkerSystem.cs | 1 - Content.Server/Supermatter/Systems/SupermatterSystem.cs | 1 - Content.Shared/Lock/LockSystem.cs | 8 ++++++-- Content.Shared/Magic/SpellbookSystem.cs | 1 - Content.Shared/Medical/CPR/Systems/CPRSystem.cs | 1 - .../Medical/Surgery/SharedSurgerySystem.Steps.cs | 1 - .../Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs | 1 - Content.Shared/Paint/PaintRemoverSystem.cs | 2 -- 14 files changed, 11 insertions(+), 20 deletions(-) diff --git a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs index e6645acb7ba..de659a07039 100644 --- a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs +++ b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs @@ -19,15 +19,13 @@ public sealed partial class IntercomMenu : FancyWindow private readonly List _channels = new(); - public IntercomMenu(Entity entity) + public IntercomMenu() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); MicButton.OnPressed += args => OnMicPressed?.Invoke(args.Button.Pressed); SpeakerButton.OnPressed += args => OnSpeakerPressed?.Invoke(args.Button.Pressed); - - Update(entity); } public void Update(Entity entity) @@ -70,4 +68,3 @@ public void Update(Entity entity) }; } } - diff --git a/Content.Server/Flight/FlightSystem.cs b/Content.Server/Flight/FlightSystem.cs index 86eed994329..4493967fe9e 100644 --- a/Content.Server/Flight/FlightSystem.cs +++ b/Content.Server/Flight/FlightSystem.cs @@ -67,7 +67,6 @@ private void OnToggleFlight(EntityUid uid, FlightComponent component, ToggleFlig new FlightDoAfterEvent(), uid, target: uid) { BlockDuplicate = true, - , BreakOnMove = true, BreakOnDamage = true, NeedHand = true @@ -173,4 +172,4 @@ private void OnSleep(EntityUid uid, FlightComponent component, ref SleepStateCha Dirty(uid, stamina); } #endregion -} \ No newline at end of file +} diff --git a/Content.Server/LifeDrainer/LifeDrainerSystem.cs b/Content.Server/LifeDrainer/LifeDrainerSystem.cs index 12d3bedad99..2a9c5bb4583 100644 --- a/Content.Server/LifeDrainer/LifeDrainerSystem.cs +++ b/Content.Server/LifeDrainer/LifeDrainerSystem.cs @@ -122,7 +122,6 @@ public bool TryDrain(Entity ent, EntityUid target) var ev = new LifeDrainDoAfterEvent(); var args = new DoAfterArgs(EntityManager, uid, comp.Delay, ev, target: target, eventTarget: uid) { - , BreakOnMove = true, MovementThreshold = 2f, NeedHand = false diff --git a/Content.Server/Medical/PenLightSystem.cs b/Content.Server/Medical/PenLightSystem.cs index 6d7157b38c3..c092b4c5902 100644 --- a/Content.Server/Medical/PenLightSystem.cs +++ b/Content.Server/Medical/PenLightSystem.cs @@ -92,7 +92,6 @@ public bool TryStartExam(EntityUid uid, EntityUid target, EntityUid user, PenLig { BlockDuplicate = true, BreakOnMove = true, - , BreakOnHandChange = true, NeedHand = true }); diff --git a/Content.Server/Paint/PaintSystem.cs b/Content.Server/Paint/PaintSystem.cs index a03de99e681..9d7158baab0 100644 --- a/Content.Server/Paint/PaintSystem.cs +++ b/Content.Server/Paint/PaintSystem.cs @@ -83,7 +83,6 @@ private void PrepPaint(EntityUid uid, PaintComponent component, EntityUid target used: uid) { BreakOnMove = true, - , NeedHand = true, BreakOnHandChange = true, }); diff --git a/Content.Server/Power/Generator/PortableGeneratorSystem.cs b/Content.Server/Power/Generator/PortableGeneratorSystem.cs index f7d259b122a..33033daa054 100644 --- a/Content.Server/Power/Generator/PortableGeneratorSystem.cs +++ b/Content.Server/Power/Generator/PortableGeneratorSystem.cs @@ -73,7 +73,9 @@ private void StartGenerator(EntityUid uid, PortableGeneratorComponent component, _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.StartTime, new GeneratorStartedEvent(), uid, uid) { - BreakOnDamage = true, BreakOnMove = true, RequireCanInteract = true, + BreakOnDamage = true, + BreakOnMove = true, + RequireCanInteract = true, NeedHand = true }); } diff --git a/Content.Server/Power/Systems/BatteryDrinkerSystem.cs b/Content.Server/Power/Systems/BatteryDrinkerSystem.cs index 0080e754386..4f2ddf0bbad 100644 --- a/Content.Server/Power/Systems/BatteryDrinkerSystem.cs +++ b/Content.Server/Power/Systems/BatteryDrinkerSystem.cs @@ -72,7 +72,6 @@ private void DrinkBattery(EntityUid target, EntityUid user, BatteryDrinkerCompon var args = new DoAfterArgs(EntityManager, user, doAfterTime, new BatteryDrinkerDoAfterEvent(), user, target) // TODO: Make this doafter loop, once we merge Upstream. { BreakOnDamage = true, - , BreakOnMove = true, Broadcast = false, DistanceThreshold = 1.35f, diff --git a/Content.Server/Supermatter/Systems/SupermatterSystem.cs b/Content.Server/Supermatter/Systems/SupermatterSystem.cs index 019996374b2..24039a50c2b 100644 --- a/Content.Server/Supermatter/Systems/SupermatterSystem.cs +++ b/Content.Server/Supermatter/Systems/SupermatterSystem.cs @@ -176,7 +176,6 @@ private void OnItemInteract(EntityUid uid, SupermatterComponent sm, ref Interact { BreakOnDamage = true, BreakOnHandChange = false, - , BreakOnMove = true, BreakOnWeightlessMove = false, NeedHand = true, diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index 9735d71a519..9c1d1a1c789 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -121,7 +121,9 @@ public bool TryLock(EntityUid uid, EntityUid user, LockComponent? lockComp = nul return _doAfter.TryStartDoAfter( new DoAfterArgs(EntityManager, user, lockComp.LockTime, new LockDoAfter(), uid, uid) { - BreakOnDamage = true, BreakOnMove = true, RequireCanInteract = true, + BreakOnDamage = true, + BreakOnMove = true, + RequireCanInteract = true, NeedHand = true }); } @@ -197,7 +199,9 @@ public bool TryUnlock(EntityUid uid, EntityUid user, LockComponent? lockComp = n return _doAfter.TryStartDoAfter( new DoAfterArgs(EntityManager, user, lockComp.LockTime, new UnlockDoAfter(), uid, uid) { - BreakOnDamage = true, BreakOnMove = true, RequireCanInteract = true, + BreakOnDamage = true, + BreakOnMove = true, + RequireCanInteract = true, NeedHand = true }); } diff --git a/Content.Shared/Magic/SpellbookSystem.cs b/Content.Shared/Magic/SpellbookSystem.cs index bb774db1c25..139c6a1a125 100644 --- a/Content.Shared/Magic/SpellbookSystem.cs +++ b/Content.Shared/Magic/SpellbookSystem.cs @@ -86,7 +86,6 @@ private void AttemptLearn(Entity ent, UseInHandEvent args) { var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.LearnTime, new SpellbookDoAfterEvent(), ent, target: ent) { - , BreakOnMove = true, BreakOnWeightlessMove = true, BreakOnDamage = true, diff --git a/Content.Shared/Medical/CPR/Systems/CPRSystem.cs b/Content.Shared/Medical/CPR/Systems/CPRSystem.cs index 7238fe5a097..d82a7ee202b 100644 --- a/Content.Shared/Medical/CPR/Systems/CPRSystem.cs +++ b/Content.Shared/Medical/CPR/Systems/CPRSystem.cs @@ -95,7 +95,6 @@ private void StartCPR(EntityUid performer, EntityUid target, CPRTrainingComponen _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, performer, cprComponent.DoAfterDuration, new CPRDoAfterEvent(), performer, target, performer) { - , BreakOnMove = true, NeedHand = true, BlockDuplicate = true diff --git a/Content.Shared/Medical/Surgery/SharedSurgerySystem.Steps.cs b/Content.Shared/Medical/Surgery/SharedSurgerySystem.Steps.cs index 0ab76cb42f7..b5d717eaa47 100644 --- a/Content.Shared/Medical/Surgery/SharedSurgerySystem.Steps.cs +++ b/Content.Shared/Medical/Surgery/SharedSurgerySystem.Steps.cs @@ -729,7 +729,6 @@ private void OnSurgeryTargetStepChosen(Entity ent, ref S var doAfter = new DoAfterArgs(EntityManager, user, TimeSpan.FromSeconds(duration), ev, body, part) { BreakOnMove = true, - , CancelDuplicate = true, DuplicateCondition = DuplicateConditions.SameEvent, NeedHand = true, diff --git a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs index 227c13c3d2c..7dc85781170 100644 --- a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs +++ b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs @@ -163,7 +163,6 @@ protected void StartInsertDoAfter(EntityUid inserter, EntityUid toInsert, Entity var ev = new PseudoItemInsertDoAfterEvent(); var args = new DoAfterArgs(EntityManager, inserter, 5f, ev, toInsert, toInsert, storageEntity) { - , BreakOnMove = true, NeedHand = true }; diff --git a/Content.Shared/Paint/PaintRemoverSystem.cs b/Content.Shared/Paint/PaintRemoverSystem.cs index e800a1de656..b67ef156fa7 100644 --- a/Content.Shared/Paint/PaintRemoverSystem.cs +++ b/Content.Shared/Paint/PaintRemoverSystem.cs @@ -37,7 +37,6 @@ private void OnInteract(EntityUid uid, PaintRemoverComponent component, AfterInt _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new PaintRemoverDoAfterEvent(), uid, args.Target, uid) { BreakOnMove = true, - , BreakOnDamage = true, MovementThreshold = 1.0f, }); @@ -84,7 +83,6 @@ private void OnPaintRemoveVerb(EntityUid uid, PaintRemoverComponent component, G uid) { BreakOnMove = true, - , BreakOnDamage = true, MovementThreshold = 1.0f, }); From 32470987aa7f3762e1a1b66ded3facb5027999fa Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Sun, 15 Dec 2024 06:01:14 -0400 Subject: [PATCH 046/366] hum --- Content.Client/Access/UI/AgentIDCardWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs index 8ba556ce60c..9bc255ec1d0 100644 --- a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs +++ b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs @@ -47,7 +47,7 @@ public void SetAllowedIcons(string currentJobIconId) var i = 0; var icons = _prototypeManager.EnumeratePrototypes().Where(icon => icon.AllowSelection).ToList(); icons.Sort((x, y) => string.Compare(x.LocalizedJobName, y.LocalizedJobName, StringComparison.CurrentCulture)); - foreach (var jobIcon in icons) + foreach (var jobIconId in icons) { if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon)) continue; From 21fe24f8b49c37fbe58c36196fa4bb6f3f03ba2a Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Wed, 19 Jun 2024 11:04:30 -0400 Subject: [PATCH 047/366] Add prediction for Tech Disks, cleanup (#29061) * Add prediction for Tech Disks, cleanup * Remove IsServer check in OnMapInit * Use HashSet for techs, remove LINQ --- .../Systems/ResearchSystem.Technology.cs | 19 ---- .../Components/TechnologyDiskComponent.cs | 20 ---- .../Systems/TechnologyDiskSystem.cs | 92 ------------------ .../Research/Systems/SharedResearchSystem.cs | 19 ++++ .../Components/TechnologyDiskComponent.cs | 24 +++++ .../Systems/TechnologyDiskSystem.cs | 94 +++++++++++++++++++ 6 files changed, 137 insertions(+), 131 deletions(-) delete mode 100644 Content.Server/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs delete mode 100644 Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs create mode 100644 Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs create mode 100644 Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs diff --git a/Content.Server/Research/Systems/ResearchSystem.Technology.cs b/Content.Server/Research/Systems/ResearchSystem.Technology.cs index 9bd71cf7c6e..7578d316c5e 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Technology.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Technology.cs @@ -131,25 +131,6 @@ public void AddTechnology(EntityUid uid, TechnologyPrototype technology, Technol RaiseLocalEvent(uid, ref ev); } - /// - /// Adds a lathe recipe to the specified technology database - /// without checking if it can be unlocked. - /// - public void AddLatheRecipe(EntityUid uid, string recipe, TechnologyDatabaseComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - if (component.UnlockedRecipes.Contains(recipe)) - return; - - component.UnlockedRecipes.Add(recipe); - Dirty(uid, component); - - var ev = new TechnologyDatabaseModifiedEvent(); - RaiseLocalEvent(uid, ref ev); - } - /// /// Returns whether a technology can be unlocked on this database, /// taking parent technologies into account. diff --git a/Content.Server/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs b/Content.Server/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs deleted file mode 100644 index eb5a0623a08..00000000000 --- a/Content.Server/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Content.Shared.Random; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Research.TechnologyDisk.Components; - -[RegisterComponent] -public sealed partial class TechnologyDiskComponent : Component -{ - /// - /// The recipe that will be added. If null, one will be randomly generated - /// - [DataField("recipes")] - public List? Recipes; - - /// - /// A weighted random prototype for how rare each tier should be. - /// - [DataField("tierWeightPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string TierWeightPrototype = "TechDiskTierWeights"; -} diff --git a/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs b/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs deleted file mode 100644 index 176b2b68bc9..00000000000 --- a/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Linq; -using Content.Server.Popups; -using Content.Server.Research.Systems; -using Content.Server.Research.TechnologyDisk.Components; -using Content.Shared.Examine; -using Content.Shared.Interaction; -using Content.Shared.Random; -using Content.Shared.Random.Helpers; -using Content.Shared.Research.Components; -using Content.Shared.Research.Prototypes; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; - -namespace Content.Server.Research.TechnologyDisk.Systems; - -public sealed class TechnologyDiskSystem : EntitySystem -{ - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly ResearchSystem _research = default!; - [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent(OnExamine); - SubscribeLocalEvent(OnMapInit); - } - - private void OnAfterInteract(EntityUid uid, TechnologyDiskComponent component, AfterInteractEvent args) - { - if (args.Handled || !args.CanReach || args.Target is not { } target) - return; - - if (!HasComp(target) || !TryComp(target, out var database)) - return; - - if (component.Recipes != null) - { - foreach (var recipe in component.Recipes) - { - _research.AddLatheRecipe(target, recipe, database); - } - } - _popup.PopupEntity(Loc.GetString("tech-disk-inserted"), target, args.User); - QueueDel(uid); - args.Handled = true; - } - - private void OnExamine(EntityUid uid, TechnologyDiskComponent component, ExaminedEvent args) - { - var message = Loc.GetString("tech-disk-examine-none"); - if (component.Recipes != null && component.Recipes.Any()) - { - var prototype = _prototype.Index(component.Recipes[0]); - var resultPrototype = _prototype.Index(prototype.Result); - message = Loc.GetString("tech-disk-examine", ("result", resultPrototype.Name)); - - if (component.Recipes.Count > 1) //idk how to do this well. sue me. - message += " " + Loc.GetString("tech-disk-examine-more"); - } - args.PushMarkup(message); - } - - private void OnMapInit(EntityUid uid, TechnologyDiskComponent component, MapInitEvent args) - { - if (component.Recipes != null) - return; - - var weightedRandom = _prototype.Index(component.TierWeightPrototype); - var tier = int.Parse(weightedRandom.Pick(_random)); - - //get a list of every distinct recipe in all the technologies. - var techs = new List>(); - foreach (var tech in _prototype.EnumeratePrototypes()) - { - if (tech.Tier != tier) - continue; - - techs.AddRange(tech.RecipeUnlocks); - } - techs = techs.Distinct().ToList(); - - if (!techs.Any()) - return; - - //pick one - component.Recipes = new(); - component.Recipes.Add(_random.Pick(techs)); - } -} diff --git a/Content.Shared/Research/Systems/SharedResearchSystem.cs b/Content.Shared/Research/Systems/SharedResearchSystem.cs index 9819e949b8b..81c6950f283 100644 --- a/Content.Shared/Research/Systems/SharedResearchSystem.cs +++ b/Content.Shared/Research/Systems/SharedResearchSystem.cs @@ -280,4 +280,23 @@ public void ClearTechs(EntityUid uid, TechnologyDatabaseComponent? comp = null) comp.UnlockedTechnologies.Clear(); Dirty(uid, comp); } + + /// + /// Adds a lathe recipe to the specified technology database + /// without checking if it can be unlocked. + /// + public void AddLatheRecipe(EntityUid uid, string recipe, TechnologyDatabaseComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (component.UnlockedRecipes.Contains(recipe)) + return; + + component.UnlockedRecipes.Add(recipe); + Dirty(uid, component); + + var ev = new TechnologyDatabaseModifiedEvent(); + RaiseLocalEvent(uid, ref ev); + } } diff --git a/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs b/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs new file mode 100644 index 00000000000..ce8a138bdbd --- /dev/null +++ b/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs @@ -0,0 +1,24 @@ +using Content.Shared.Random; +using Content.Shared.Research.Prototypes; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Research.TechnologyDisk.Components; + +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState] +public sealed partial class TechnologyDiskComponent : Component +{ + /// + /// The recipe that will be added. If null, one will be randomly generated + /// + [DataField] + [AutoNetworkedField] + public List>? Recipes; + + /// + /// A weighted random prototype for how rare each tier should be. + /// + [DataField] + public ProtoId TierWeightPrototype = "TechDiskTierWeights"; +} diff --git a/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs b/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs new file mode 100644 index 00000000000..b0d615fcb3b --- /dev/null +++ b/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs @@ -0,0 +1,94 @@ +using Content.Shared.Examine; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Content.Shared.Random.Helpers; +using Content.Shared.Research.Components; +using Content.Shared.Research.Prototypes; +using Content.Shared.Research.Systems; +using Content.Shared.Research.TechnologyDisk.Components; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Shared.Research.TechnologyDisk.Systems; + +public sealed class TechnologyDiskSystem : EntitySystem +{ + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly IPrototypeManager _protoMan = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedResearchSystem _research = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnExamine); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + if (ent.Comp.Recipes != null) + return; + + var weightedRandom = _protoMan.Index(ent.Comp.TierWeightPrototype); + var tier = int.Parse(weightedRandom.Pick(_random)); + + //get a list of every distinct recipe in all the technologies. + var techs = new HashSet>(); + foreach (var tech in _protoMan.EnumeratePrototypes()) + { + if (tech.Tier != tier) + continue; + + techs.UnionWith(tech.RecipeUnlocks); + } + + if (techs.Count == 0) + return; + + //pick one + ent.Comp.Recipes = []; + ent.Comp.Recipes.Add(_random.Pick(techs)); + Dirty(ent); + } + + private void OnAfterInteract(Entity ent, ref AfterInteractEvent args) + { + if (args.Handled || !args.CanReach || args.Target is not { } target) + return; + + if (!HasComp(target) || !TryComp(target, out var database)) + return; + + if (ent.Comp.Recipes != null) + { + foreach (var recipe in ent.Comp.Recipes) + { + _research.AddLatheRecipe(target, recipe, database); + } + } + _popup.PopupClient(Loc.GetString("tech-disk-inserted"), target, args.User); + if (_net.IsServer) + QueueDel(ent); + args.Handled = true; + } + + private void OnExamine(Entity ent, ref ExaminedEvent args) + { + var message = Loc.GetString("tech-disk-examine-none"); + if (ent.Comp.Recipes != null && ent.Comp.Recipes.Count > 0) + { + var prototype = _protoMan.Index(ent.Comp.Recipes[0]); + var resultPrototype = _protoMan.Index(prototype.Result); + message = Loc.GetString("tech-disk-examine", ("result", resultPrototype.Name)); + + if (ent.Comp.Recipes.Count > 1) //idk how to do this well. sue me. + message += " " + Loc.GetString("tech-disk-examine-more"); + } + args.PushMarkup(message); + } +} From f4e41bf9e6878fad31b5e85b678bf9a63cb10f8f Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Thu, 1 Aug 2024 00:15:05 -0400 Subject: [PATCH 048/366] Add support for printing reagents in lathes (#30476) * Add support for reagents in lathes * missing locale --- Content.Client/Lathe/UI/LatheMenu.xaml | 9 +- Content.Client/Lathe/UI/LatheMenu.xaml.cs | 65 +++++++++----- Content.Client/Lathe/UI/RecipeControl.xaml | 4 +- Content.Client/Lathe/UI/RecipeControl.xaml.cs | 8 +- .../Tests/MaterialArbitrageTest.cs | 6 +- Content.Server/Cargo/Systems/PricingSystem.cs | 21 +++++ Content.Server/Lathe/LatheSystem.cs | 50 +++++++++-- .../UserInterface/StatValuesCommand.cs | 7 +- .../Construction/MachinePartSystem.cs | 8 +- Content.Shared/Lathe/LatheComponent.cs | 3 + Content.Shared/Lathe/SharedLatheSystem.cs | 70 ++++++++++++++- .../Prototypes/LatheRecipePrototype.cs | 89 +++++-------------- .../Research/Systems/SharedResearchSystem.cs | 4 +- .../Systems/TechnologyDiskSystem.cs | 5 +- Resources/Locale/en-US/lathe/recipes.ftl | 8 ++ .../Locale/en-US/lathe/ui/lathe-menu.ftl | 3 + .../Prototypes/Recipes/Lathes/medical.yml | 16 ++-- 17 files changed, 243 insertions(+), 133 deletions(-) create mode 100644 Resources/Locale/en-US/lathe/recipes.ftl diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml b/Content.Client/Lathe/UI/LatheMenu.xaml index 6f484d8c7be..7e978c1ae56 100644 --- a/Content.Client/Lathe/UI/LatheMenu.xaml +++ b/Content.Client/Lathe/UI/LatheMenu.xaml @@ -100,12 +100,9 @@ Margin="5 0 0 0" Text="{Loc 'lathe-menu-fabricating-message'}"> - - +