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/408] 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 cc548fd4b1..b54d2a41fe 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/408] 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 b54d2a41fe..ab4e6c5b8c 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 aa15ecfb28..eefd6bb053 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/408] 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 ab4e6c5b8c..32d69dc8e7 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/408] 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 0000000000..5aa31f66f6 --- /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 0000000000..c935b8c064 --- /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 77f543cdcf..8893d9a476 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 8efdc2738b..b23afe2310 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 0000000000..357c138f46 --- /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 0000000000..9a551acc49 --- /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 5b6699dab7..cae56c1a7b 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 01a078625a..6ad1e17775 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 47a4adeaf3..cec635b4ca 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 81bdda706b..0367aa1460 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 083085fa0d..faec4a9e9c 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 3d0a02d6aa..abf46b7b96 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 bf013bc040..18077b413a 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 b4314b2caf..3da81fc964 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 801dcc4b85..d4a58f961a 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 fca0073b4e..ba2cd53048 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 4ae53c9b37..a433f10d06 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 fb4ce6f4a1..d7e0884130 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/408] 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 d4a58f961a..d2780011c9 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/408] 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 0000000000..fd3fb6cd65 --- /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 0000000000..5e58fd14fc --- /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 7cc51db576..c3e8e90f84 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 d2780011c9..a0649eb2d6 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 a433f10d06..80354861ad 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/408] 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 46d6f1b464..0000000000 --- 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 0000000000..9ab62cc12d --- /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 7758c3d7e2..5b07b1588f 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 fde1801b37..852ea8af46 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 aa2fa2047f..958dc6a5d9 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 0000000000..b107fd3867 --- /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 a19b62cd70..0f2c314ed0 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 457d3e3192..b3d684e01a 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 9dcf555ea9..165b181dcb 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 03e192e680..974928ee4b 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 fa352eb320..0000000000 --- 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 30fa84ed90..0000000000 --- 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 227491b16c..0000000000 --- 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 f98415eb08..0000000000 --- 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 f0b56cbd19..9d25db680e 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 7b0b411020..d0c650c4e8 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 11d2758cdd..f284a214e6 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 45bfedfee7..1848e88186 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 552ee0397f..c4b5c6dc2b 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 0000000000..d7a7be995d --- /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 ac76ae6b77..3aaf7c5d58 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 04095b549c..63054eaad5 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 0c1e88653f..28ab633227 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 64c958d6f1..c262651f27 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 970ca78e2c..822486cff5 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 7ba312f4bb..fbc58dafe8 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 4155a4f6be..9ebd677f47 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 f45a01b2e1..0d2d9012bc 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 623b6ba978..0feecad4d0 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 b6e5adbe2d..b8546f30e7 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 d758247eca..0000000000 --- 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 0000000000..a0168077fd --- /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 9cbc193ce6..6fccaaa5cf 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 c1782efaba..97dd2c7e73 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 f6aafe376d..2bf53d46f4 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 c6f56a2750..a585a9ef45 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 0000000000..60a606ac7e --- /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 0000000000..564aca0681 --- /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 54d242709c..f6efbb10f3 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 c51eef4da9..a52e19f755 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 bd857d4c29..ed3c6366c1 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 5de1383cde..7f95ef184e 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 c3c4baf19d..866ce38a57 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 66ff5c624a..c89ab827e6 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 fedf48b36e..0000000000 --- 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 0000000000..c77aa03475 --- /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 90b2d7322e..0000000000 --- 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 e96d9f866a..0000000000 --- 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 0000000000..9889376c9d --- /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 0d074ff38b..4bef74fd33 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 0000000000..88d987aae1 --- /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 27fb0c1a50..0000000000 --- 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 0000000000..760cefe27d --- /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 0000000000..20ef0a0231 --- /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 6d53471357..cdac49ae6d 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 620ddfd194..46249fdd0d 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 0000000000..b673c55e0f --- /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 d07fd5a735..c4e4150659 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 0000000000..858cd9bc11 --- /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 2da5285285..1d6f690bc3 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 55bcdd0f0a..9c39c4724c 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 bf0eaec84b..c429eb6880 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 ba4060c703..464f48f187 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 db7678f61d..ae3e85cbe4 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 33b8fc7893..84c58bb648 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 0000000000..d9f66d21a3 --- /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 7b57926330..3b9e2a5e35 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 7e7b1ffcd3..8b477b2aa5 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 91c816df5c..a19537be1c 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 dacf47bb23..3ba4494cca 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 37a27074a4..2da094291d 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 4853968b61..1385219e47 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 df9cf8ac82..ae0bacaf5f 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 d427ffa39b..281b97a648 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 0000000000..56112e9a69 --- /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 ac11063eb7..0abcca7d1b 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 f61d0c6a90..8b892190b7 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 fed41eaed8..3800d15b26 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 522f29fe42..d738f2dd8a 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 0000000000..f4b158aced --- /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 61b6e4313e..061c019c9b 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 07032a00ce..8d2c4dcfeb 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 5163112683..0000000000 --- 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 708a86a8ea..94de7c7787 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 508bfc85f0..2b2a836633 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 09cb7f06d5..7e2bb4dfe6 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 db25e8bc51..df302f9477 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 e1776873da..de0fe0bce3 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 0431d95a42..896d97bf52 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 1283b6699b..f28e62e7dd 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 0d7972a8de..aa69d3db78 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 b8a815c7a8..e494253c83 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 5d8432ac77..5edd445afc 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 76c98a427f..7f20974a14 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 51bfc33c49..3ad67844d6 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 2abe1f0a80..748e3482a8 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 c7ba6e0b32..10eaf1cb44 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 6215fb3b45..7ba33bd0da 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 13fbc08716..bb2b163f08 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 888bcd769c..5d81a41619 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 bc592616be..3d0732d1ca 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 3911a68626..f0f37822f4 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 1d0a8d9228..2989c40ff7 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 4284632d28..a7fb7de2f8 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 0000000000..259323fede --- /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 2dbcfc60ab..00df4e02cb 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 0d2f890a1d..54fc4a70c5 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 f0d9a7c0b1..ac23bd3995 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 1a90568593..0a7041ea54 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 a9d6bbb20f..93208f80c0 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 47e633276b..df9c27149d 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 3c3b215422..a90f7099ad 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 38b1f7cda9..6491f699f0 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 eae5cdbf46..3b4394bdc3 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 dc49dce0f3..e95db1c6e9 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 59c6d63c6e..e4dd7b5b41 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 2da3d3bb92..d36f070475 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 4cb76ea4b9..0485b5a517 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 a0649eb2d6..8a9c488088 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 ba2cd53048..50864930b0 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/408] 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 5f9afc78b9..97d792628a 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 da443e3fb5..b54a91ee97 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 5e60d06d0c..2b5a41c6ce 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 b7f5e48821..6f9ef83040 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 40bdf65743..bfff8a91d0 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 e132e4f12a..9922cc5a37 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 a33c944c99..7f8253efed 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 d4934ee24e..f75fd9920f 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 5c970b1a74..913299eea9 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 0d2d9012bc..f7b4cf0249 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 2e060b1b42..a6eb703ab0 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 c74a3c49d6..2aec9a45ad 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 9c9fdc5727..a67d4b1f7f 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 dc08854227..36a650863e 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 c89ab827e6..670bf99ef9 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 9e3f917e96..d03cd095bb 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 88d987aae1..d41f27fefb 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 c13ec0a1b9..78e86f5ab4 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 32d69dc8e7..53a66e5ada 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 5eaa25f484..a058cb8658 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 cd4ae2e767..d57eea7e3e 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 8d358f8db3..8eb943d715 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 fad9d72438..d5449e88f8 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 77ee419ac3..78386e8951 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/408] 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 53a66e5ada..d325de92c8 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 0000000000..578da52dfc --- /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 d5449e88f8..9fd8bdf5e7 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 0000000000..77f72806f4 --- /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 0000000000..50f759e43f --- /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/408] 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 0000000000..fbebcb7cf8 --- /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 0000000000..4d56592c23 --- /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 b8340d0be4..9a68e2aabb 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 2d80c810e2..6c35ba2008 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 9ba30813dd..0a5d475f27 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 0000000000..9b18d6ad93 --- /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 0000000000..d73993357a --- /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 0000000000..be2a9dc3ab --- /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/408] 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 0000000000..add688c3f3 --- /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 a5202285d9..87246ab675 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 860454efde..2105c53df2 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 e36bfac7cb..4516a547f4 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 c9aac35732..1366dfb76a 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/408] 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 0000000000..c04cf0a60b --- /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 0000000000..cd4d0d6203 --- /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 0000000000..a349e4e485 --- /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 0000000000..49df7aee94 --- /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 5b5d281362..6bba9320e8 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 6e369389a9..5aa41301d7 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 e7f20f8f47..415f9369dc 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/408] 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 4d56592c23..61e20f751c 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 0a5d475f27..a314fe0ca1 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 be2a9dc3ab..37ac751889 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/408] 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 c95f8f204d..fad55f9627 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 af977f763c..6ffb714766 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 a2faf208d9..7cb32b43df 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 273707cb6e..27ddd51815 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 ad19bc30f4..fcf0ada947 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 4183c90814..f07e54eb65 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 14709f8b1f..619cac6839 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 3ab2caad78..d19643f20a 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 b62595595e..fbaaf5624b 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 c3a8e66470..c03e50dda0 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 61e20f751c..ebf6c18c95 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 bd5b75de1d..edd2d7e11b 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 8b4b38753c..ea040d1e44 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 6a2622fba9..e84c2dc098 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 2890bb3dbf..2723db1efb 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 4f2108748b..3dd216c5dc 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 1b323d6643..98f33b8424 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 434a3fef66..170676e08c 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 056a985cbe..6eb84b9441 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 561cad8d7e..b0bf389509 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 07a4d044ca..624f4cac3e 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 2f56142aa6..7ac4c653d3 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 2875d4a3d5..b9ab109897 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 c1a5256fdd..2c9a358755 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 7c12cf3f77..0921a763d5 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 b27689ed58..a35cf6c2e3 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 fbe40deedb..7dc141f875 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 70eea2d9b7..b3ad5bbdb3 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 e78970d124..1f4abf34f7 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 cc9235e3d7..3535f6b238 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 089ce32236..2335859f0b 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 4458b020a1..6f356baf07 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 223dd2ac32..a78b6b1b25 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 a942d34e7a..45a85010b1 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 1ef589ab5c..a9aaa211c7 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 c0685024aa..7a54cce31f 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 0685b08f4f..6951d44b4d 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 be082eba30..8068783abf 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 6e7bd255c5..f47a5df8ac 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 cdc083feac..d189afc0a0 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 84a835f523..39d1058395 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 fd5d3a9ceb..e9f1db13ff 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 5968e445c1..292f8ec8e9 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 3d4c8a4ec5..3c86ccc82d 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 e86dbca4a1..bd7bf0c4e0 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 0d6fb3fb90..4a118e696f 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 7448a9b84d..c41909ac7c 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 f2c4c7dcc5..154a408125 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 752fb8f5fe..3c5f7eaecb 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 e880cec5bb..3c44ea4615 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 3b23308758..44f9250255 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 97a758a5ed..ec27c71728 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 caae6c5f17..d90e49d201 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 dc1f67c740..3eaadfa0de 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 7c5744f087..e5eb386002 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 f3cff9f2e7..14dca2c7bb 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 b3508025cb..e459d7e87e 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 9a68e2aabb..ebb3c6b42f 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 a2a02a60f6..7398bc0616 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 72843a65b8..e1c223c01e 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 15b967bb1d..4a63be894e 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 038295eac1..2aa69024df 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 6c35ba2008..8dcb6240a5 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 a314fe0ca1..47ac38b997 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 540bd6c483..dd09467efe 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 5f79906c99..5a1bd31a15 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 02e46e6b11..0cb20b3187 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 5550201202..e37be3d1a5 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 2454856a70..b142680213 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 652ca236e4..e728f4986b 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 9cc85060c6..d2c2a8a1ca 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 ca0f59cd14..5cc3c52be4 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 d0c2cd78d3..315273a0cc 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 accaa75d1c..f84039276f 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 6da774ba07..cf86400b0e 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 60be1ad6f1..6b233cd43f 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 fce81db327..440b1f6dce 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 72d99460fe..a9f224ad5d 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 6606f28432..b4303f97d8 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 dcc276e565..0ea30a8ca5 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 cd4d0d6203..a5303ac121 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 c77aa03475..04bc3ed4e8 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 0000000000..bf6ae0e525 --- /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 0000000000..6d984ed19a --- /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 42e55bea55..8754de5058 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 37ac751889..2bc2af7831 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 0000000000..578a34142a --- /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 94de7c7787..e4402e9eff 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 aa69d3db78..1336fe1527 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/408] 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 0000000000..1bcac4bca6 --- /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 0000000000..2e44b820df --- /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 0000000000..a4d59d1f31 --- /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 0000000000..19dcbac620 --- /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 0000000000..372961ea9a --- /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 67b81c1145..aa170c9cf5 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 b7f4d03451..b6f9736732 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 3347b0e4b1..954caf67ae 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/408] 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 c1f5d98afd..33771cb963 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/408] 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 33771cb963..c144f330e1 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/408] 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 0000000000..7e7bdd082b --- /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 8831acc1dd..f59e6d3c46 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/408] 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 3c86ccc82d..6c1e711b6f 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/408] 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 0000000000..e1c55131cd --- /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 0000000000..a4d4055c7d --- /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 1483e75e73..6e62dbfda9 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 00424a3566..c79fc8f9e7 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 1b01fe4e30..52ef2ab7da 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 1563e0abaf..e252851556 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/408] 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 c1b63dc4d0..d80c600c03 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 b5c480ff71..ef6a3bb671 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 c3fac8cb92..ee5ed26d2e 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 9a38c0c485..91cb8b0ebd 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 e84cf5d34d..3d65f75189 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 8b91ec5966..d6d580bcda 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 5764d0a097..5d1985485c 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 08438e2a1b..82d41192dd 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 eeda2a3102..4a579fc4bf 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 e5542a5848..1ee4c26818 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 1a3422dec0..8fa8035afd 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 40bbe8b2d8..c0704530de 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 8f3b507c80..2ae1518835 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 43be67c9d6..eeec11c766 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 a5e316a8de..7bf9b396d5 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 1904e2b340..2b8020924c 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 28766c688a..62748b5259 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 709c06517c..392fbf1cd9 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 6eba2e0d21..220fdbe875 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 1664c8b9d7..d62be8f4bb 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 1b39306181..642f34c2f9 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 4d8d1191e9..e70426575d 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 60fe339069..865dfc478d 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 ffab162548..09f3cec8fb 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 d3365702bc..44c40143d8 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 20c23a48a0..2461dafb5f 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 422d03707a..02b721b902 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 c591f917da..43b00089e1 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 988fea7978..3ef7f0ae73 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 99e5a3d395..2ad1b71888 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 35df131312..f1cb27a62a 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 26f0994701..62a02f3718 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 5b0d5fcf21..83f6ba1566 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 1c94d32bf8..0310e91eeb 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 4d8dd86a4d..63868e7a93 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 bdbfe03fa1..11c26b252e 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 4fea44f225..e4966f1ec4 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 cc24af2869..29217eef7b 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 86f1b8b83c..887492955e 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 fcf0ada947..f020991224 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 e2c4d51ecd..e5be0b1811 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 2a5801ccf2..b97786cd41 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 e8e77217ea..296e71d3a9 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 3aeed8dc80..7fc0eb8540 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 cd7ea717ce..9b7e23c03a 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 c01f13a462..2112a56297 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 a95066a3b5..ca2e834b4f 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 ba49f11ea0..08596b04e6 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 fdb3cdbc01..457b70ca7c 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 b3e1333a7a..0cc03a3614 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 d72da3e812..32b40747d5 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 75f8eb479b..6f04133b59 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 39bb52d72c..dba7e39b63 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 a8872604a4..53977eb636 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 5fb293a194..26cd1369e5 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 2814d41536..c175e67842 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 0f5729f55b..4816ce8c36 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 b54a91ee97..9b14e01fb5 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 4ccede7f5d..468c325822 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 f97d8a7330..7884268c42 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 7e7dd2d693..643ac47054 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 b292e9f146..13029e3846 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 e6f108b305..bc4cc75b4d 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 555f1ff09e..6b65612341 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 6e6d1b9176..a599f79152 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 9e15f8239e..649c8acc85 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 09bdedfd94..11abe8c245 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 fbaaf5624b..441ca9ea36 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 f6979bf8d7..0a87948ff6 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 80eca82e32..22e5bc452a 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 e2d57935e3..c059ce785a 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 4172bdc90f..2130a8c609 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 fad7648808..6f39bc386e 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 3978880987..b1f239cd78 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 863412e553..e861864c14 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 80c98f143b..f85220a926 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 19d04cd346..fcd2f75918 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 8cfa97dc6c..e75c60058c 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 c04b42f249..8cdffd16af 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 fb4aec1974..6294facaee 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 59fbc5b319..2e15042373 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 ec055b3240..ad4f1a75d4 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 b4a3f1c7fa..aeceae1327 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 07352b512b..37ce9c4280 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 a0688523f1..170a296ac2 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 4b0ac868f0..f3ad1e347e 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 7a5fd65264..f7cace642c 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 cde5ba9ef7..ff1eae36f5 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 c69e027137..05a296edf3 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 3ebcf7cbce..0df6787170 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 968fe188f7..b77f1af047 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 6e62dbfda9..7417fafede 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 57965b030a..a3ca6f65da 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 fbcbf01156..759a5949ba 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 dbf68ea07b..2f61ea63a8 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 edd2d7e11b..161482e090 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 30679d71fd..550e1041b6 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 dc1dcd03ef..cbc343c06c 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 25a586a75d..0fb49df534 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 81fe1f4d04..e304325248 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 3eb0397a69..f0d27d6b1f 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 a37dbcecf8..1dd03626ae 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 abbb1d58ec..922715bdad 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 da008ca04c..9641adb5b2 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 07dd35a005..288445e4de 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 ceaa965e59..d10f8b39f4 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 f29e66baae..2895ada61f 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 b364b83312..d1021f82e5 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 6185979eee..9a5159880f 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 367114f2aa..87d7e62c39 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 8f1723d1f2..663bde15b0 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 086369aa26..b8b4fb8a74 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 4bd44a47a8..f75759b042 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 af7b6055c8..e677181419 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 3cc2a35d79..ed9bf40a48 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 7d8fd9fe57..4cc2e41a8f 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 046d8e299f..08f15d9de2 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 2aee0a38c0..93349794b8 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 e8442d2390..7d6a6cf2a5 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 720a2efb9d..e7bab71e38 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 88ad0e3de8..5eb5507f67 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 eea867b794..1c46b4be35 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 9132dd6ed5..e3646c00cc 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 37384daafe..0631d98993 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 b2314cf3fe..d9be1a6305 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 ee8cb28d2c..4ae74a5d65 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 7797a096de..bf0e93b36c 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 17ddba77ff..eafab84ed6 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 f700c6663b..891804674d 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 16a28f9d9b..0dc41f807a 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 f3e0c0a539..3f01808c42 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 5a8869a204..edf1a2d377 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 7bccc20861..eccc548297 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 2538caf6eb..c7a74815b6 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/408] 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 7417fafede..4421f8ec08 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/408] 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 7f20974a14..881b547f27 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/408] 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 a3d39eafde..0000000000 --- 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 ca830767bd..0000000000 --- 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 461435f062..42f9808940 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 3838ad168d..0000000000 --- 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 404cc63905..d896fba71c 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 ea62d4a819..6807b9df4a 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 779b2f5971..f53d658ebd 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 0000000000..8a4e2c6d4c --- /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 0000000000..a8fcbdd2eb --- /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 0000000000..c5b2ee3dfc --- /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 59d75e453a..f9c65e9477 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 39e10415f8..1f1e59bff3 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 5d81a41619..4683388dc2 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 2771704905..573717def8 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 2c0e1eeec3..571e9bc97b 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 60017fc98d..423ec0f84d 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 0000000000..2c2d49e031 --- /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/408] 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 cec635b4ca..23de66ae35 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 abf46b7b96..46fee4abfe 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 473441ccec..a4710afd8e 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 e9307effbc..d5a4db591a 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 5670e28ec9..0e726f2a2b 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 a43d4fca72..e1d65003a6 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 e92676a216..c32718d6a8 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 5aa41301d7..c7018ae0e1 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 a7fb7de2f8..d76e698183 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 80354861ad..f47c2651c9 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 45d41631c2..e19f2ae654 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/408] 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 fd3fb6cd65..c640c912a5 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 c3e8e90f84..3190a50701 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/408] 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 bda60e9449..183607db1a 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 5488a8d6f4..5cd62d8357 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 56cc0f9670..7f0b797468 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 bd7b7a6620..0e8669a031 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 d0c650c4e8..45e31cef5b 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 cbbfc289cf..e472d146de 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 05ef84bbaf..17d52f0ad9 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 0000000000..e8ed081a57 --- /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 5e97396fba..cb4839cae7 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 0000000000..85c40f9427 --- /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 c7c89a3b7f..0000000000 --- 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 d2b5a25aee..36dc7c059e 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 0000000000..c71f5d0008 --- /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 0000000000..e034458197 --- /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 824696c838..0000000000 --- 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 9e45847e07..0000000000 --- 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 1f1e59bff3..6cd0543742 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 0000000000..54a21a40d4 --- /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 0000000000..1d3317c840 --- /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 560b10c46e..1a79da3509 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 c7018ae0e1..077e076959 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 51078d1b10..baa0df7b81 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 7a926d66d3..2b8a3d889d 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 22b8cfd48a..8cc8f5c6a7 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/408] 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 42f9808940..4fb0f3fe5f 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 01eab560a1..c753d0a244 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 b3ad5bbdb3..36ef59e743 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 72104bc381..373696844f 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 39d1058395..eb9871673b 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 bd7bf0c4e0..cc21b21b55 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 46fee4abfe..42ea278c74 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 47ac38b997..925ba63f91 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/408] 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 4e7b796503..c7b20513f4 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 ef43c2c89d..b7856e9042 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 fdcc76718c..c1e1de2baa 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 bbe64a7987..4d149ca1ad 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 39705fc197..ede8d3064f 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 5361b65b1f..ad7b2a11b0 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 c84d65b75e..97de3ee486 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 eb9871673b..1b9ab97be6 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 08bf68c4d2..61b6f3d93d 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 a0bbbdf350..281bbc4721 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 8dd3d56a1e..a5ebb0fd9e 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 27ad2178f9..5fc406dca5 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 d02dd44e81..e4fd3fb491 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 ad39817f8b..42512cb1fd 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 e83cde7456..5e2a562577 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 8f91ec41e8..fe1844a3ed 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 4ad6c1f835..97d4eb0680 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 3cfa3a9f5f..e441574213 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 0419e13d23..b145bec014 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 81806a0c81..431e438fd8 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 3c44ea4615..c4a07b56a8 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 5997c56e24..30c01063b0 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 b11e8ca707..8d8a6bfa3b 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 194ef532ba..ae0495fb27 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 5191f53004..f51c0444e6 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 a728ee7de5..53c6c62cdb 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 ec27c71728..227283c95d 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 d90e49d201..db7eef0afa 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 45e31cef5b..9f145173ac 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 c64bb2a485..8191243356 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 f284a214e6..870788cd4d 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 96bfc7c904..b8304c562a 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 c4b5c6dc2b..4baf0913ce 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 9cd24b9af6..4bca7a1eac 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 cd05adc794..6781d07956 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 84355f03c1..feb7c49484 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 d5bff967b3..f7650f599b 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 e2996a54d7..f7d259b122 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 a6eb703ab0..63e8173117 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 7a17a2eba1..2ab277d0f1 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 0a72e48071..bf87cf5a60 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 e49c49c1da..a8024e2d77 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 2d1104e2ce..effe1b72f7 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 73f2ce17a6..64e96e1467 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 29cde5d741..3d988b0916 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 440b1f6dce..526a14b1ab 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 1116c6797b..45a89f3be9 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 36dc7c059e..722d97213f 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 4abe7bc876..4cb127bb10 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 297fe095f8..1c8e2ef2b0 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 124daeffaa..14047fba7d 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 d999b370b3..f77a6dc272 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 73334deddb..d88f72c965 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 24b3841759..4f77a271b3 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 81c8c4f382..ed8be1ad65 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 12435eba89..f6879bb1a5 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 bfdfc77bc2..9735d71a51 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 04926c3456..73b7c0847f 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 960dab8461..31a305dba9 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 a59c09ca53..ab87585c70 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 bbdfc5d021..f2c18f43f2 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 d7a8ee5460..ea489e332d 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 1fffa8c025..070ff40b70 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 1ada22876b..f94558b0b3 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 514ec79f68..f7586cc1ec 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 dc2ce3fb90..d2ceecf28e 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 fa04a50f8b..feb1cebb8e 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 da7c3aa89a..91acde47e1 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 9fd8bdf5e7..d149ed5e0a 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 1336fe1527..a134e03aac 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 87e2f0890a..6aef4d0949 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 88acd81d5b..4998b11f08 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/408] 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 9f145173ac..3834ae6db9 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/408] 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 3834ae6db9..264df0e230 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 1d6f690bc3..e4cd8077d2 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 0000000000..60d1a21584 --- /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/408] 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 54a21a40d4..12956d924f 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/408] 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 5cb5c8cd2e..133146ec06 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 154a408125..498fe3b6f4 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 8bfb4aec8c..1e030a9f3d 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 88da3093d7..98ce725f58 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 e5eb386002..7c5744f087 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 f828874aac..85aea76bd6 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 8694147dc0..c0c11b083c 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 d5a5c4f409..1d3c57b7cb 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 dad042f970..fc2dad677a 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 0ea30a8ca5..dcc276e565 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 d41f27fefb..ef5ac671e4 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 d325de92c8..97af3982d7 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 d149ed5e0a..183420db9d 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/408] 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 b0031cfa33..a1c87f0de7 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 bb49725e04..60ae813e58 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 2860f8dacf..4b5c2158e1 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/408] 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 fc2dad677a..265965af94 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 183420db9d..db2337a921 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/408] 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 db2337a921..868d26c3ae 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/408] 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 605f47d3b8..fcb1ca17dc 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 890bc84e72..6a1444ebf6 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 4b5c2158e1..d2c607e57f 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 728ca962f9..52ffb8dd1c 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/408] 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 453a05e803..0000000000 --- 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 c5ec2d76ad..b899ae5197 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 e472d146de..03f10f153d 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 0000000000..f9c5111d63 --- /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 0000000000..de6db205be --- /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 31a305dba9..81180ac747 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 d61862bf1a..1626c5a63f 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 7afb41239c..539b756a63 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 8bdf326166..5bd4613e8c 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 ff28cc44db..4a83cd455d 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 2880bd00ec..2c818e7c97 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 e90aafa414..b328265e21 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 dc9ee0d09d..e38cb94d20 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 72df09ac50..cb557e0597 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 4b6e21a492..0a412a5480 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 e95db1c6e9..0fe7455369 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 fefc41ae86..32a0e8b4df 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 0aa281b95c..e1753ce81e 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 8d43953a07..aa0de1918b 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 40c8537412..818d359719 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 86b90f2bde..7099ce6eea 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 0ad30e9ed6..53f92eef66 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 2f1527d359..aef5cfbf8d 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 de22ef22c7..bf61e39f9e 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 df060c2503..2136cbdcb8 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 8d6496e013..1d9302305c 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 d36f070475..2ac023edae 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/408] 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 6a2e90dd88..0c44a680ff 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 ca69d2f929..9b310aa40a 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 e7144e1185..f674f263f7 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 7f0b797468..29a03485db 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 0e8669a031..b91fcabb46 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 676eb9808b..12fb8b52c2 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 39321b1e66..86eed99432 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 e4fd3fb491..6842349828 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 e3542eaba4..1437385612 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 c5955d7803..12d3bedad9 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 f1df356cab..6d7157b38c 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 e8ef4d21e1..a03de99e68 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 e42783c4d8..0080e75438 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 b9d26b59f7..e43a690881 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 3d86f57fb8..019996374b 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 41afe0d666..b0e966a620 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 63b1c08aac..d22b2ced2e 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 745ea042ca..ae72ebf150 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 013b8df6cd..cedf021d5f 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 91347bcdd9..6bc69aba58 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 c69bf6abd4..18a04392e0 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 cdcba6740c..80ef0b0f6c 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 a626db19e5..10faf3ebe2 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 767d30389a..2ce008da26 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 a7c8274624..bb774db1c2 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 e050f1b4e1..7238fe5a09 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 c914c13514..0ab76cb42f 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 5f4e618434..227c13c3d2 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 e2565e0f22..e800a1de65 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 6e07657941..6117636dee 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 ced89e7860..c021baa86b 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/408] 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 ee5ed26d2e..c167ad660a 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 89de793714..7d091e4e16 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 d2c8374fef..060c4d022f 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 73f533cf69..322645bc0f 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 5cf8cf38fb..2313cc4bd0 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 66310505a1..711abdb3bf 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 25fab3f135..aa6bd6ad5b 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 c5a5fd8a2c..4ba543a2f5 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 3200dd7f5e..987f9a0957 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 3673a2c51d..0020ff17da 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 2e66ec56c5..d65eb5c3d9 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 073b03a2ae..de1b668480 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 9af246e06e..749d1f88f1 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 96ad930bd5..bb2ae3b64d 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 0000000000..749d1f88f1 --- /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 31a22e5df3..e360ec11eb 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 70253cc6b1..1d04a5349e 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 2011c6ceea..24981746e7 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 0dbdfce4f9..82f8ac7175 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/408] 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 4421f8ec08..d662dc91c6 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 a3ca6f65da..0000000000 --- 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 942ea1aba8..358e26d82e 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 0000000000..07cc99605e --- /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 0adbc49230..806470607a 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/408] 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 c167ad660a..050756fcd1 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 922715bdad..401e7edd44 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 8c78edf74d..aefd413de8 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/408] 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 0000000000..29d6c635eb --- /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 401e7edd44..cb95d57439 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 ea040d1e44..e6645acb7b 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 0cb20b3187..839bb66922 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 66dadc85b5..aff4d82d87 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 be2734ff16..8d7b87597b 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 0000000000..7af5c1c78c --- /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 a15b6f76e9..5a384173d0 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 410843312f..f697add8b9 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 b4b0768e0f..24f3ad8e76 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 e56e3cd0f7..63303999c2 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 3ac6b353a9..7b5b87d786 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 43f84fe404..77b6cac2d3 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 6c6b8bf57e..9a244c2c59 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 2247860f89..ba29d72539 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 19f2fee183..82c16de7b6 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/408] 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 e6645acb7b..de659a0703 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 86eed99432..4493967fe9 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 12d3bedad9..2a9c5bb458 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 6d7157b38c..c092b4c590 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 a03de99e68..9d7158baab 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 f7d259b122..33033daa05 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 0080e75438..4f2ddf0bba 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 019996374b..24039a50c2 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 9735d71a51..9c1d1a1c78 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 bb774db1c2..139c6a1a12 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 7238fe5a09..d82a7ee202 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 0ab76cb42f..b5d717eaa4 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 227c13c3d2..7dc8578117 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 e800a1de65..b67ef156fa 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/408] 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 8ba556ce60..9bc255ec1d 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/408] 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 9bd71cf7c6..7578d316c5 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 eb5a0623a0..0000000000 --- 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 176b2b68bc..0000000000 --- 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 9819e949b8..81c6950f28 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 0000000000..ce8a138bdb --- /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 0000000000..b0d615fcb3 --- /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/408] 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 6f484d8c7b..7e978c1ae5 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'}"> - - +