From 267d6481720240f6a4e3687a2bdaf911046a9820 Mon Sep 17 00:00:00 2001 From: youtissoum <51883137+youtissoum@users.noreply.github.com> Date: Sat, 11 May 2024 01:49:56 +0200 Subject: [PATCH 01/68] Add slime and spider wideswinging (#27856) * Add wide swinging to spiders * Add wide swinging to slimes --- Resources/Prototypes/Entities/Mobs/NPCs/animals.yml | 2 ++ Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 535cb73470..6523419821 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -2202,6 +2202,7 @@ 0: Alive 90: Dead - type: MeleeWeapon + altDisarm: false angle: 0 animation: WeaponArcBite soundHit: @@ -2327,6 +2328,7 @@ - type: Spider webPrototype: SpiderWebClown - type: MeleeWeapon + altDisarm: false angle: 0 animation: WeaponArcBite soundHit: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml index 77cf43cdbc..d2ca780438 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml @@ -93,6 +93,7 @@ prototype: Slimes requiredLegs: 1 - type: MeleeWeapon + altDisarm: false soundHit: path: /Audio/Weapons/punch3.ogg angle: 0 From 0250cd99be75c2238a005491a47a51f11fee2062 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 10 May 2024 23:51:02 +0000 Subject: [PATCH 02/68] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 4a3ec355fc..8d226e1ccf 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: deltanedas - changes: - - message: No more raiding the jani vend for galoshes, which are back to normal - speed. - type: Remove - id: 6080 - time: '2024-03-03T07:07:58.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25795 - author: metalgearsloth changes: - message: 'Reworked shuttle consoles, including: manual FTL jumps, new map screen, @@ -3861,3 +3853,12 @@ id: 6579 time: '2024-05-10T15:57:21.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27532 +- author: youtissoum + changes: + - message: Spiders can now wideswing. + type: Tweak + - message: Slimes can now wideswing. + type: Tweak + id: 6580 + time: '2024-05-10T23:49:56.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27856 From 938c3104e7685e1910b66f60e914ccfc4d8001fb Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sat, 11 May 2024 01:56:48 +0200 Subject: [PATCH 03/68] Remove THC oil (#27889) remove THC oil --- .../Locale/en-US/reagents/meta/narcotics.ftl | 3 --- .../en-US/reagents/meta/physical-desc.ftl | 1 - .../Structures/Specific/Anomaly/anomalies.yml | 1 - Resources/Prototypes/Reagents/narcotics.yml | 19 ------------------- 4 files changed, 24 deletions(-) diff --git a/Resources/Locale/en-US/reagents/meta/narcotics.ftl b/Resources/Locale/en-US/reagents/meta/narcotics.ftl index a7cffb7f6b..d6da259501 100644 --- a/Resources/Locale/en-US/reagents/meta/narcotics.ftl +++ b/Resources/Locale/en-US/reagents/meta/narcotics.ftl @@ -13,9 +13,6 @@ reagent-desc-experimental-stimulants = A prototype version of the stimulant chem reagent-name-thc = THC reagent-desc-thc = The main psychoactive compound in cannabis. -reagent-name-thc-oil = THC oil -reagent-desc-thc-oil = Pure THC oil, extracted from the leaves of the cannabis plant. Much stronger than its natural form and can be used to numb chronic pain in patients. - reagent-name-bananadine = bananadine reagent-desc-bananadine = A mild psychedelic that is found in small traces in banana peels. diff --git a/Resources/Locale/en-US/reagents/meta/physical-desc.ftl b/Resources/Locale/en-US/reagents/meta/physical-desc.ftl index 355b76c2a4..61954e01eb 100644 --- a/Resources/Locale/en-US/reagents/meta/physical-desc.ftl +++ b/Resources/Locale/en-US/reagents/meta/physical-desc.ftl @@ -1,4 +1,3 @@ -reagent-physical-desc-skunky = skunky reagent-physical-desc-soapy = soapy reagent-physical-desc-ferrous = ferrous reagent-physical-desc-nothing = nothing diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml index aedfd262d8..8bca49c4e5 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml @@ -775,7 +775,6 @@ - Desoxyephedrine - Ephedrine - THC - - THCOil - SpaceDrugs - Nocturine - MuteToxin diff --git a/Resources/Prototypes/Reagents/narcotics.yml b/Resources/Prototypes/Reagents/narcotics.yml index 9b14fa2bc8..21d7310483 100644 --- a/Resources/Prototypes/Reagents/narcotics.yml +++ b/Resources/Prototypes/Reagents/narcotics.yml @@ -182,25 +182,6 @@ time: 16 refresh: false -- type: reagent - id: THCOil # deprecated in favor of THC, preserved here for forks - name: reagent-name-thc-oil - group: Narcotics - desc: reagent-desc-thc-oil - physicalDesc: reagent-physical-desc-skunky - flavor: bitter - flavorMinimum: 0.05 - color: "#DAA520" - metabolisms: - Narcotic: - effects: - - !type:GenericStatusEffect - key: SeeingRainbows - component: SeeingRainbows - type: Add - time: 16 - refresh: false - - type: reagent id: Nicotine name: reagent-name-nicotine From bc53a46a7392ae0bc3911634c19225610c459a8f Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Sat, 11 May 2024 01:58:19 +0200 Subject: [PATCH 04/68] Change o2/plasma ratio in TEG guidebook (#27763) --- Resources/ServerInfo/Guidebook/Engineering/TEG.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ServerInfo/Guidebook/Engineering/TEG.xml b/Resources/ServerInfo/Guidebook/Engineering/TEG.xml index 7739181f94..63a62fecf8 100644 --- a/Resources/ServerInfo/Guidebook/Engineering/TEG.xml +++ b/Resources/ServerInfo/Guidebook/Engineering/TEG.xml @@ -31,7 +31,7 @@ As stated above, there are many different layouts one can follow to heat up (or cool down) gases; this part of the guide will cover 2 common methods one will often see for the hot loop when the TEG is setup: [color=#a4885c]The Pipe Burn[/color], and [color=red]the Burn Chamber[/color]. - Side note: Plasma fires burn relatively cool compared to, for example, Tritium fires. It may be viable to extract Tritium from an extraction setup (using a 97/3 ratio of O2/Plasma) and react it with Oxygen to get truly hellish temperatures for power. Although, this is just a recommendation; I'm not ya mum. + Side note: Plasma fires burn relatively cool compared to, for example, Tritium fires. It may be viable to extract Tritium from an extraction setup (using a 96/4 ratio of O2/Plasma) and react it with Oxygen to get truly hellish temperatures for power. Although, this is just a recommendation; I'm not ya mum. ## The Pipe Burn From 9741fda672e27bc68ebed87bdaf7f23adda6f7c1 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Fri, 10 May 2024 17:04:01 -0700 Subject: [PATCH 05/68] Add ActionPerformedEvent, ActionsSystem.SetIfBiggerCooldown, action id to action events and BackgroundOn field (#27682) * Add ActionPerformedEvent and ActionsSystem.SetIfBiggerCooldown * Add action id to action events and backgroundon field to action component --- Content.Client/Actions/ActionsSystem.cs | 3 ++ .../Systems/Actions/ActionUIController.cs | 2 ++ .../Systems/Actions/Controls/ActionButton.cs | 13 ++++++-- .../Actions/ActionOnInteractSystem.cs | 5 +++ Content.Shared/Actions/ActionEvents.cs | 5 +++ Content.Shared/Actions/BaseActionComponent.cs | 8 +++-- .../Actions/Events/ActionPerformedEvent.cs | 8 +++++ Content.Shared/Actions/SharedActionsSystem.cs | 33 +++++++++++++++---- 8 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 Content.Shared/Actions/Events/ActionPerformedEvent.cs diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs index 5ff003452a..aff6c1ff7b 100644 --- a/Content.Client/Actions/ActionsSystem.cs +++ b/Content.Client/Actions/ActionsSystem.cs @@ -247,7 +247,10 @@ namespace Content.Client.Actions if (action.ClientExclusive) { if (instantAction.Event != null) + { instantAction.Event.Performer = user; + instantAction.Event.Action = actionId; + } PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime); } diff --git a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs index 5d9706452c..69ac3ab023 100644 --- a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs +++ b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs @@ -217,6 +217,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged(); _spriteSys ??= _entities.System(); - if ((_controller.SelectingTargetFor == ActionId || _action.Toggled) && _action.IconOn != null) - SetActionIcon(_spriteSys.Frame0(_action.IconOn)); + if ((_controller.SelectingTargetFor == ActionId || _action.Toggled)) + { + if (_action.IconOn != null) + SetActionIcon(_spriteSys.Frame0(_action.IconOn)); + + if (_action.BackgroundOn != null) + _buttonBackgroundTexture = _spriteSys.Frame0(_action.BackgroundOn); + } else + { SetActionIcon(_action.Icon != null ? _spriteSys.Frame0(_action.Icon) : null); + _buttonBackgroundTexture = Theme.ResolveTexture("SlotBackground"); + } } public void UpdateBackground() diff --git a/Content.Server/Actions/ActionOnInteractSystem.cs b/Content.Server/Actions/ActionOnInteractSystem.cs index eb35d41196..657ab46d60 100644 --- a/Content.Server/Actions/ActionOnInteractSystem.cs +++ b/Content.Server/Actions/ActionOnInteractSystem.cs @@ -47,7 +47,10 @@ public sealed class ActionOnInteractSystem : EntitySystem var (actId, act) = _random.Pick(options); if (act.Event != null) + { act.Event.Performer = args.User; + act.Event.Action = actId; + } _actions.PerformAction(args.User, null, actId, act, act.Event, _timing.CurTime, false); args.Handled = true; @@ -75,6 +78,7 @@ public sealed class ActionOnInteractSystem : EntitySystem if (entAct.Event != null) { entAct.Event.Performer = args.User; + entAct.Event.Action = entActId; entAct.Event.Target = args.Target.Value; } @@ -100,6 +104,7 @@ public sealed class ActionOnInteractSystem : EntitySystem if (act.Event != null) { act.Event.Performer = args.User; + act.Event.Action = actId; act.Event.Target = args.ClickLocation; } diff --git a/Content.Shared/Actions/ActionEvents.cs b/Content.Shared/Actions/ActionEvents.cs index cddb70f74d..c6002d0d4a 100644 --- a/Content.Shared/Actions/ActionEvents.cs +++ b/Content.Shared/Actions/ActionEvents.cs @@ -155,4 +155,9 @@ public abstract partial class BaseActionEvent : HandledEntityEventArgs /// The user performing the action. /// public EntityUid Performer; + + /// + /// The action that was performed. + /// + public EntityUid Action; } diff --git a/Content.Shared/Actions/BaseActionComponent.cs b/Content.Shared/Actions/BaseActionComponent.cs index 6d9242acc1..57c145a0ec 100644 --- a/Content.Shared/Actions/BaseActionComponent.cs +++ b/Content.Shared/Actions/BaseActionComponent.cs @@ -1,5 +1,4 @@ -using Content.Shared.Mobs; -using Robust.Shared.Audio; +using Robust.Shared.Audio; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -25,6 +24,11 @@ public abstract partial class BaseActionComponent : Component /// [DataField("iconOn")] public SpriteSpecifier? IconOn; + /// + /// For toggle actions only, background to show when toggled on. + /// + [DataField] public SpriteSpecifier? BackgroundOn; + /// /// If not null, this color will modulate the action icon color. /// diff --git a/Content.Shared/Actions/Events/ActionPerformedEvent.cs b/Content.Shared/Actions/Events/ActionPerformedEvent.cs new file mode 100644 index 0000000000..530d7c9335 --- /dev/null +++ b/Content.Shared/Actions/Events/ActionPerformedEvent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared.Actions.Events; + +/// +/// Raised on the action entity when it is used and . +/// +/// The entity that performed this action. +[ByRefEvent] +public readonly record struct ActionPerformedEvent(EntityUid Performer); diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index e1b76f517e..315d2725b2 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -144,9 +144,6 @@ public abstract class SharedActionsSystem : EntitySystem public void SetCooldown(EntityUid? actionId, TimeSpan start, TimeSpan end) { - if (actionId == null) - return; - if (!TryGetActionData(actionId, out var action)) return; @@ -162,9 +159,6 @@ public abstract class SharedActionsSystem : EntitySystem public void ClearCooldown(EntityUid? actionId) { - if (actionId == null) - return; - if (!TryGetActionData(actionId, out var action)) return; @@ -175,6 +169,27 @@ public abstract class SharedActionsSystem : EntitySystem Dirty(actionId.Value, action); } + /// + /// Sets the cooldown for this action only if it is bigger than the one it already has. + /// + public void SetIfBiggerCooldown(EntityUid? actionId, TimeSpan? cooldown) + { + if (cooldown == null || + cooldown.Value <= TimeSpan.Zero || + !TryGetActionData(actionId, out var action)) + { + return; + } + + var start = GameTiming.CurTime; + var end = start + cooldown; + if (action.Cooldown?.End > end) + return; + + action.Cooldown = (start, end.Value); + Dirty(actionId.Value, action); + } + public void StartUseDelay(EntityUid? actionId) { if (actionId == null) @@ -438,7 +453,10 @@ public abstract class SharedActionsSystem : EntitySystem } if (performEvent != null) + { performEvent.Performer = user; + performEvent.Action = actionEnt; + } // All checks passed. Perform the action! PerformAction(user, component, actionEnt, action, performEvent, curTime); @@ -580,6 +598,9 @@ public abstract class SharedActionsSystem : EntitySystem if (dirty && component != null) Dirty(performer, component); + + var ev = new ActionPerformedEvent(performer); + RaiseLocalEvent(actionId, ref ev); } #endregion From 5578bcc6f861a2211e9f2678d4132ae50f2a303d Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 11 May 2024 11:21:06 +1000 Subject: [PATCH 06/68] Fix votes using an audio entity (#27871) * Fix votes using an audio entity Just retains a source around and uses that. I think the audio limit is like 256 sources on the lower end so this is like whatever to persist. * Restart * weh --- Content.Client/Voting/VoteManager.cs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Content.Client/Voting/VoteManager.cs b/Content.Client/Voting/VoteManager.cs index 63c706c86b..a7c799b58f 100644 --- a/Content.Client/Voting/VoteManager.cs +++ b/Content.Client/Voting/VoteManager.cs @@ -6,12 +6,15 @@ using Robust.Client; using Robust.Client.Audio; using Robust.Client.Console; using Robust.Client.GameObjects; +using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Shared.IoC; using Robust.Shared.Network; using Robust.Shared.Timing; using Robust.Shared.Player; using Robust.Shared.Audio; +using Robust.Shared.Audio.Sources; +using Robust.Shared.ContentPack; namespace Content.Client.Voting @@ -31,16 +34,20 @@ namespace Content.Client.Voting public sealed class VoteManager : IVoteManager { + [Dependency] private readonly IAudioManager _audio = default!; + [Dependency] private readonly IBaseClient _client = default!; + [Dependency] private readonly IClientConsoleHost _console = default!; [Dependency] private readonly IClientNetManager _netManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IClientConsoleHost _console = default!; - [Dependency] private readonly IBaseClient _client = default!; + [Dependency] private readonly IResourceCache _res = default!; private readonly Dictionary _standardVoteTimeouts = new(); private readonly Dictionary _votes = new(); private readonly Dictionary _votePopups = new(); private Control? _popupContainer; + private IAudioSource? _voteSource; + public bool CanCallVote { get; private set; } public event Action? CanCallVoteChanged; @@ -49,6 +56,14 @@ namespace Content.Client.Voting public void Initialize() { + const string sound = "/Audio/Effects/voteding.ogg"; + _voteSource = _audio.CreateAudioSource(_res.GetResource(sound)); + + if (_voteSource != null) + { + _voteSource.Global = true; + } + _netManager.RegisterNetMessage(ReceiveVoteData); _netManager.RegisterNetMessage(ReceiveVoteCanCall); @@ -125,9 +140,8 @@ namespace Content.Client.Voting return; } + _voteSource?.Restart(); @new = true; - IoCManager.Resolve().GetEntitySystem() - .PlayGlobal("/Audio/Effects/voteding.ogg", Filter.Local(), false); // Refresh var container = _popupContainer; From 9845d8bd30008230d49b47dd51a3c7bbf2c9de2e Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Fri, 10 May 2024 22:20:18 -0400 Subject: [PATCH 07/68] Fix ninja suit suit storage and other armor missing their suit storage (#27897) b --- .../Entities/Clothing/OuterClothing/armor.yml | 10 ++++------ .../Clothing/OuterClothing/base_clothingouter.yml | 8 +++----- .../Entities/Clothing/OuterClothing/coats.yml | 4 +++- .../Entities/Clothing/OuterClothing/suits.yml | 8 ++++---- .../Entities/Clothing/OuterClothing/vests.yml | 4 ++-- .../Prototypes/Entities/Clothing/base_clothing.yml | 6 ++++++ 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index 88f8a4ebc2..825e729115 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -3,7 +3,7 @@ #Basic armor vest - type: entity - parent: ClothingOuterBaseMedium + parent: [ClothingOuterBaseMedium, AllowSuitStorageClothing] id: ClothingOuterArmorBasic name: armor vest description: A standard Type I armored vest that provides decent protection against most types of damage. @@ -21,7 +21,6 @@ Heat: 0.80 - type: ExplosionResistance damageCoefficient: 0.90 - - type: AllowSuitStorage #Alternate / slim basic armor vest - type: entity @@ -37,7 +36,7 @@ sprite: Clothing/OuterClothing/Armor/security_slim.rsi - type: entity - parent: ClothingOuterBaseLarge + parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing] id: ClothingOuterArmorRiot name: riot suit description: A suit of semi-flexible polycarbonate body armor with heavy padding to protect against melee attacks. Perfect for fighting delinquents around the station. @@ -121,7 +120,7 @@ - type: GroupExamine - type: entity - parent: ClothingOuterBaseLarge + parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing] id: ClothingOuterArmorHeavy name: heavy armor suit description: A heavily armored suit that protects against excessive damage. @@ -188,7 +187,7 @@ sprite: Clothing/OuterClothing/Armor/magusred.rsi - type: entity - parent: ClothingOuterBaseLarge + parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing] id: ClothingOuterArmorCaptainCarapace name: "captain's carapace" description: "An armored chestpiece that provides protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest." @@ -212,7 +211,6 @@ - type: ExplosionResistance damageCoefficient: 0.65 - type: GroupExamine - - type: AllowSuitStorage - type: entity parent: ClothingOuterBaseLarge diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml index 19d963bdeb..c6a3f01f15 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml @@ -101,7 +101,7 @@ - type: entity abstract: true - parent: [ClothingOuterBase, GeigerCounterClothing] + parent: [ClothingOuterBase, GeigerCounterClothing, AllowSuitStorageClothing] id: ClothingOuterHardsuitBase name: base hardsuit components: @@ -138,11 +138,10 @@ - WhitelistChameleon - type: ClothingRequiredStepTriggerImmune slots: WITHOUT_POCKET - - type: AllowSuitStorage - type: entity abstract: true - parent: ClothingOuterBase + parent: [ClothingOuterBase, AllowSuitStorageClothing] id: ClothingOuterEVASuitBase name: base EVA Suit components: @@ -159,7 +158,6 @@ size: Huge - type: ClothingRequiredStepTriggerImmune slots: WITHOUT_POCKET - - type: AllowSuitStorage - type: entity parent: ClothingOuterBase @@ -182,4 +180,4 @@ id: ClothingOuterBaseMedium components: - type: Item - size: Huge + size: Huge \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml index c8beb0ff8d..b5b969551c 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml @@ -10,7 +10,7 @@ sprite: Clothing/OuterClothing/Coats/bomber.rsi - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageBase, AllowSuitStorageClothing] id: ClothingOuterCoatDetective name: detective trenchcoat description: A rugged canvas trenchcoat, designed and created by TX Fabrication Corp. Wearing it makes you feel for the plight of the Tibetans. @@ -53,6 +53,7 @@ - type: entity abstract: true + parent: AllowSuitStorageClothing id: ClothingOuterArmorHoS components: - type: Armor @@ -68,6 +69,7 @@ - type: entity abstract: true + parent: AllowSuitStorageClothing id: ClothingOuterArmorWarden components: - type: Armor diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml index f9ea337764..08bbd83c48 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml @@ -41,7 +41,7 @@ sprite: Clothing/OuterClothing/Suits/janitor_bombsuit.rsi - type: entity - parent: ClothingOuterBaseLarge + parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing] id: ClothingOuterSuitFire name: fire suit description: A suit that helps protect against hazardous temperatures. @@ -71,7 +71,7 @@ slots: WITHOUT_POCKET - type: entity - parent: ClothingOuterBaseLarge + parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing] id: ClothingOuterSuitAtmosFire name: atmos fire suit description: An expensive firesuit that protects against even the most deadly of station fires. Designed to protect even if the wearer is set aflame. @@ -102,7 +102,7 @@ slots: WITHOUT_POCKET - type: entity - parent: [ClothingOuterBaseLarge, GeigerCounterClothing] + parent: [ClothingOuterBaseLarge, GeigerCounterClothing, AllowSuitStorageClothing] id: ClothingOuterSuitRad name: radiation suit description: "A suit that protects against radiation. The label reads, 'Made with lead. Please do not consume insulation.'" @@ -127,7 +127,7 @@ slots: WITHOUT_POCKET - type: entity - parent: ClothingOuterBaseLarge + parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing] id: ClothingOuterSuitSpaceNinja name: space ninja suit description: This black technologically advanced, cybernetically-enhanced suit provides many abilities like invisibility or teleportation. diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml index b867abfeed..74b6ec74fb 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml @@ -1,6 +1,6 @@ #Web vest - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageBase, AllowSuitStorageClothing] id: ClothingOuterVestWeb name: web vest description: A synthetic armor vest. This one has added webbing and ballistic plates. @@ -21,7 +21,7 @@ #Mercenary web vest - type: entity - parent: ClothingOuterStorageBase #web vest so it should have some pockets for ammo + parent: ClothingOuterVestWeb #web vest so it should have some pockets for ammo id: ClothingOuterVestWebMerc name: merc web vest description: A high-quality armored vest made from a hard synthetic material. It's surprisingly flexible and light, despite formidable armor plating. diff --git a/Resources/Prototypes/Entities/Clothing/base_clothing.yml b/Resources/Prototypes/Entities/Clothing/base_clothing.yml index 44613e042f..55bc2fd3e6 100644 --- a/Resources/Prototypes/Entities/Clothing/base_clothing.yml +++ b/Resources/Prototypes/Entities/Clothing/base_clothing.yml @@ -19,6 +19,12 @@ - type: Geiger attachedToSuit: true +- type: entity + abstract: true + id: AllowSuitStorageClothing + components: + - type: AllowSuitStorage + # for clothing that has a single item slot to insert and alt click out. # inheritors add a whitelisted slot named item - type: entity From af4c6373f68360ff56d6b9695b1323b3b00fe28b Mon Sep 17 00:00:00 2001 From: ike709 Date: Fri, 10 May 2024 19:21:18 -0700 Subject: [PATCH 08/68] Adds Support for Guidebook Buttons in UIs (#27891) * Adds Support for Guidebook Buttons in UIs * read it from the component * the code is perfect * moony review --------- Co-authored-by: ike709 --- .../UI/ReagentDispenserBoundUserInterface.cs | 2 ++ Content.Client/Guidebook/GuidebookSystem.cs | 5 ++++ Content.Client/Stylesheets/StyleBase.cs | 1 + Content.Client/Stylesheets/StyleNano.cs | 11 +++++++ .../UserInterface/Controls/FancyWindow.xaml | 1 + .../Controls/FancyWindow.xaml.cs | 27 ++++++++++++++++++ Resources/Textures/Interface/Nano/help.png | Bin 0 -> 636 bytes 7 files changed, 47 insertions(+) create mode 100644 Resources/Textures/Interface/Nano/help.png diff --git a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs index fce57a6ec5..99e5a3d395 100644 --- a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs +++ b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs @@ -1,3 +1,4 @@ +using Content.Client.Guidebook.Components; using Content.Shared.Chemistry; using Content.Shared.Containers.ItemSlots; using JetBrains.Annotations; @@ -34,6 +35,7 @@ namespace Content.Client.Chemistry.UI _window = new() { Title = EntMan.GetComponent(Owner).EntityName, + HelpGuidebookIds = EntMan.GetComponent(Owner).Guides }; _window.OpenCentered(); diff --git a/Content.Client/Guidebook/GuidebookSystem.cs b/Content.Client/Guidebook/GuidebookSystem.cs index cb13d4ca6e..0aa2c85142 100644 --- a/Content.Client/Guidebook/GuidebookSystem.cs +++ b/Content.Client/Guidebook/GuidebookSystem.cs @@ -80,6 +80,11 @@ public sealed class GuidebookSystem : EntitySystem }); } + public void OpenHelp(List guides) + { + OnGuidebookOpen?.Invoke(guides, null, null, true, guides[0]); + } + private void OnInteract(EntityUid uid, GuideHelpComponent component, ActivateInWorldEvent args) { if (!_timing.IsFirstTimePredicted) diff --git a/Content.Client/Stylesheets/StyleBase.cs b/Content.Client/Stylesheets/StyleBase.cs index 5068f97e36..76b77cbe63 100644 --- a/Content.Client/Stylesheets/StyleBase.cs +++ b/Content.Client/Stylesheets/StyleBase.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Client.Resources; +using Content.Client.UserInterface.Controls; using Robust.Client.Graphics; using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs index 8707d70766..6a5e0d82a2 100644 --- a/Content.Client/Stylesheets/StyleNano.cs +++ b/Content.Client/Stylesheets/StyleNano.cs @@ -1389,6 +1389,17 @@ namespace Content.Client.Stylesheets Element().Class("WindowHeadingBackgroundLight") .Prop("panel", new StyleBoxTexture(BaseButtonOpenLeft) { Padding = default }), + // Window Header Help Button + Element().Class(FancyWindow.StyleClassWindowHelpButton) + .Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/Interface/Nano/help.png")) + .Prop(Control.StylePropertyModulateSelf, Color.FromHex("#4B596A")), + + Element().Class(FancyWindow.StyleClassWindowHelpButton).Pseudo(ContainerButton.StylePseudoClassHover) + .Prop(Control.StylePropertyModulateSelf, Color.FromHex("#7F3636")), + + Element().Class(FancyWindow.StyleClassWindowHelpButton).Pseudo(ContainerButton.StylePseudoClassPressed) + .Prop(Control.StylePropertyModulateSelf, Color.FromHex("#753131")), + //The lengths you have to go through to change a background color smh Element().Class("PanelBackgroundBaseDark") .Prop("panel", new StyleBoxTexture(BaseButtonOpenBoth) { Padding = default }) diff --git a/Content.Client/UserInterface/Controls/FancyWindow.xaml b/Content.Client/UserInterface/Controls/FancyWindow.xaml index d076a552bf..84d0499b3a 100644 --- a/Content.Client/UserInterface/Controls/FancyWindow.xaml +++ b/Content.Client/UserInterface/Controls/FancyWindow.xaml @@ -11,6 +11,7 @@ diff --git a/Content.Client/UserInterface/Controls/FancyWindow.xaml.cs b/Content.Client/UserInterface/Controls/FancyWindow.xaml.cs index 8cdfe57dba..5912687fc3 100644 --- a/Content.Client/UserInterface/Controls/FancyWindow.xaml.cs +++ b/Content.Client/UserInterface/Controls/FancyWindow.xaml.cs @@ -1,7 +1,10 @@ using System.Numerics; +using Content.Client.Guidebook; +using Content.Client.Guidebook.Components; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; namespace Content.Client.UserInterface.Controls { @@ -9,13 +12,17 @@ namespace Content.Client.UserInterface.Controls [Virtual] public partial class FancyWindow : BaseWindow { + [Dependency] private readonly IEntitySystemManager _sysMan = default!; + private GuidebookSystem? _guidebookSystem; private const int DRAG_MARGIN_SIZE = 7; + public const string StyleClassWindowHelpButton = "windowHelpButton"; public FancyWindow() { RobustXamlLoader.Load(this); CloseButton.OnPressed += _ => Close(); + HelpButton.OnPressed += _ => Help(); XamlChildren = ContentsContainer.Children; } @@ -25,6 +32,26 @@ namespace Content.Client.UserInterface.Controls set => WindowTitle.Text = value; } + private List? _helpGuidebookIds; + public List? HelpGuidebookIds + { + get => _helpGuidebookIds; + set + { + _helpGuidebookIds = value; + HelpButton.Disabled = _helpGuidebookIds == null; + HelpButton.Visible = !HelpButton.Disabled; + } + } + + public void Help() + { + if (HelpGuidebookIds is null) + return; + _guidebookSystem ??= _sysMan.GetEntitySystem(); + _guidebookSystem.OpenHelp(HelpGuidebookIds); + } + protected override DragMode GetDragModeFor(Vector2 relativeMousePos) { var mode = DragMode.Move; diff --git a/Resources/Textures/Interface/Nano/help.png b/Resources/Textures/Interface/Nano/help.png new file mode 100644 index 0000000000000000000000000000000000000000..17f5283334170f6dd11eb561bb9e0736d6560bbf GIT binary patch literal 636 zcmV-?0)zdDP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGxh5!H^h5=oo6M+B#02y>eSaefwW^{L9 za%BK_cXuvnZfkR6VQ^(GZ*pgw?mQX*00HbtL_t(YOWl;QN*qxXhG!Nb3F(AvBU>8M zDTtMV+9eUeq!x@%kQdlGUBJ>5K7gXwgrrGpm820-lvqTv#de8yMrQr~bAMN7XT{i9 z!?IBk`4ZPd(t-&oP)7lj{r3=`<6EK9wVX zu_}Ro;{LMhbJcyJ8Sj+5pql0AwtROe%va7ICf*i zssyb`^#wD#)?IL0?#)^y5c_UCw<>{esSnSrO3?qICV9vzfzUNoW5cQht>5m~txBjT zTwa08Q!B`=yWqZY=piv^)dlxTo5*detotF!^Eh`;Yx1X%*TA(%$IKPUB=CBO^;Bg` z&Se;SNCf=)vUPx}yb8*kI^I3`z`8#SOleTC)~B*zKS#VK9;l|R@5f)yoscT8uL485 zy1K>zYq3pi5%KR|Lapn3`=Qri(<%{9)eF8&y!2>=fBq#7(T&m Date: Sat, 11 May 2024 02:22:24 +0000 Subject: [PATCH 09/68] Automatic changelog update --- Resources/Changelog/Changelog.yml | 32 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 8d226e1ccf..1ee24a441f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,19 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: 'Reworked shuttle consoles, including: manual FTL jumps, new map screen, - fixed performance issues, new docking screen.' - type: Tweak - id: 6081 - time: '2024-03-03T07:39:19.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/24430 -- author: Errant-4 - changes: - - message: Toggling your own internals no longer requires a delay. - type: Tweak - id: 6082 - time: '2024-03-03T08:31:47.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25781 - author: SlamBamActionman changes: - message: Arachnids are no longer perma-pied and can clean it off. @@ -3862,3 +3847,20 @@ id: 6580 time: '2024-05-10T23:49:56.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27856 +- author: Dutch-VanDerLinde + changes: + - message: Fix firesuits, ninja suits, and some armor not allowing suit storage. + type: Fix + id: 6581 + time: '2024-05-11T02:20:18.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27897 +- author: ike709 + changes: + - message: UIs can now link to a Guidebook entry via a help button in the top right + corner. + type: Add + - message: Added the help button to the reagent dispenser UI. + type: Add + id: 6582 + time: '2024-05-11T02:21:18.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27891 From eed560bf3ee8402c5ad150b1246dea6a943cfe19 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 11 May 2024 00:00:15 -0700 Subject: [PATCH 10/68] Make FTL constants in ShuttleSystem into cvars (#27706) * Make FTL constants in ShuttleSystem into cvars * Fix tests --- .../Tests/Station/EvacShuttleTest.cs | 5 +- .../Salvage/SalvageSystem.Runner.cs | 9 ++-- .../Shuttles/Systems/ArrivalsSystem.cs | 10 ++-- .../Systems/EmergencyShuttleSystem.Console.cs | 7 ++- .../Systems/EmergencyShuttleSystem.cs | 5 +- .../Systems/ShuttleSystem.FasterThanLight.cs | 53 +++++++++++-------- Content.Shared/CCVar/CCVars.cs | 36 +++++++++++++ SpaceStation14.sln.DotSettings | 1 + 8 files changed, 84 insertions(+), 42 deletions(-) diff --git a/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs b/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs index 532e481ac2..9e925a451a 100644 --- a/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs +++ b/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs @@ -87,8 +87,9 @@ public sealed class EvacShuttleTest Assert.That(LifeStage(salternXform.MapUid.Value), Is.EqualTo(EntityLifeStage.MapInitialized)); // Set up shuttle timing + var shuttleSys = server.System(); var evacSys = server.System(); - evacSys.TransitTime = ShuttleSystem.DefaultTravelTime; // Absolute minimum transit time, so the test has to run for at least this long + evacSys.TransitTime = shuttleSys.DefaultTravelTime; // Absolute minimum transit time, so the test has to run for at least this long // TODO SHUTTLE fix spaghetti var dockTime = server.CfgMan.GetCVar(CCVars.EmergencyShuttleDockTime); @@ -112,7 +113,7 @@ public sealed class EvacShuttleTest Assert.That(shuttleXform.MapUid, Is.EqualTo(ftl.Owner)); // Shuttle should have arrived at centcomm - await pair.RunSeconds(ShuttleSystem.DefaultTravelTime); + await pair.RunSeconds(shuttleSys.DefaultTravelTime); Assert.That(shuttleXform.MapUid, Is.EqualTo(centcommMap)); // Round should be ending now diff --git a/Content.Server/Salvage/SalvageSystem.Runner.cs b/Content.Server/Salvage/SalvageSystem.Runner.cs index e6e29eb577..23607e2bdc 100644 --- a/Content.Server/Salvage/SalvageSystem.Runner.cs +++ b/Content.Server/Salvage/SalvageSystem.Runner.cs @@ -1,9 +1,7 @@ using System.Numerics; using Content.Server.Salvage.Expeditions; -using Content.Server.Salvage.Expeditions.Structure; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; -using Content.Server.Shuttles.Systems; using Content.Server.Station.Components; using Content.Shared.Chat; using Content.Shared.Humanoid; @@ -13,7 +11,6 @@ using Content.Shared.Salvage.Expeditions; using Content.Shared.Shuttles.Components; using Robust.Shared.Map.Components; using Robust.Shared.Player; -using Robust.Shared.Utility; namespace Content.Server.Salvage; @@ -168,16 +165,16 @@ public sealed partial class SalvageSystem Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-minutes", ("duration", TimeSpan.FromMinutes(5).Minutes))); } // Auto-FTL out any shuttles - else if (remaining < TimeSpan.FromSeconds(ShuttleSystem.DefaultStartupTime) + TimeSpan.FromSeconds(0.5)) + else if (remaining < TimeSpan.FromSeconds(_shuttle.DefaultStartupTime) + TimeSpan.FromSeconds(0.5)) { var ftlTime = (float) remaining.TotalSeconds; - if (remaining < TimeSpan.FromSeconds(ShuttleSystem.DefaultStartupTime)) + if (remaining < TimeSpan.FromSeconds(_shuttle.DefaultStartupTime)) { ftlTime = MathF.Max(0, (float) remaining.TotalSeconds - 0.5f); } - ftlTime = MathF.Min(ftlTime, ShuttleSystem.DefaultStartupTime); + ftlTime = MathF.Min(ftlTime, _shuttle.DefaultStartupTime); var shuttleQuery = AllEntityQuery(); if (TryComp(comp.Station, out var data)) diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 12df965f45..23f7e10a3e 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -1,10 +1,10 @@ using System.Linq; using Content.Server.Administration; +using Content.Server.DeviceNetwork.Components; +using Content.Server.DeviceNetwork.Systems; using Content.Server.GameTicking; using Content.Server.GameTicking.Events; using Content.Server.Parallax; -using Content.Server.DeviceNetwork.Components; -using Content.Server.DeviceNetwork.Systems; using Content.Server.Screens.Components; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; @@ -188,7 +188,7 @@ public sealed class ArrivalsSystem : EntitySystem if (TryComp(shuttleUid, out var netComp)) { TryComp(shuttleUid, out var ftlComp); - var ftlTime = TimeSpan.FromSeconds(ftlComp?.TravelTime ?? ShuttleSystem.DefaultTravelTime); + var ftlTime = TimeSpan.FromSeconds(ftlComp?.TravelTime ?? _shuttles.DefaultTravelTime); var payload = new NetworkPayload { @@ -254,7 +254,7 @@ public sealed class ArrivalsSystem : EntitySystem private void OnArrivalsDocked(EntityUid uid, ArrivalsShuttleComponent component, ref FTLCompletedEvent args) { - TimeSpan dockTime = component.NextTransfer - _timing.CurTime + TimeSpan.FromSeconds(ShuttleSystem.DefaultStartupTime); + var dockTime = component.NextTransfer - _timing.CurTime + TimeSpan.FromSeconds(_shuttles.DefaultStartupTime); if (TryComp(uid, out var netComp)) { @@ -420,7 +420,7 @@ public sealed class ArrivalsSystem : EntitySystem if (comp.NextTransfer > curTime || !TryComp(comp.Station, out var data)) continue; - var tripTime = ShuttleSystem.DefaultTravelTime + ShuttleSystem.DefaultStartupTime; + var tripTime = _shuttles.DefaultTravelTime + _shuttles.DefaultStartupTime; // Go back to arrivals source if (xform.MapUid != arrivalsXform.MapUid) diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs index d45c04cdc2..887ee11c63 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs @@ -1,10 +1,8 @@ using System.Threading; -using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Components; using Content.Server.Screens.Components; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; -using Content.Shared.UserInterface; using Content.Shared.Access; using Content.Shared.CCVar; using Content.Shared.Database; @@ -13,6 +11,7 @@ using Content.Shared.Popups; using Content.Shared.Shuttles.BUIStates; using Content.Shared.Shuttles.Events; using Content.Shared.Shuttles.Systems; +using Content.Shared.UserInterface; using Robust.Shared.Map; using Robust.Shared.Player; using Timer = Robust.Shared.Timing.Timer; @@ -131,7 +130,7 @@ public sealed partial class EmergencyShuttleSystem private void UpdateEmergencyConsole(float frameTime) { // Add some buffer time so eshuttle always first. - var minTime = -(TransitTime - (ShuttleSystem.DefaultStartupTime + ShuttleSystem.DefaultTravelTime + 1f)); + var minTime = -(TransitTime - (_shuttle.DefaultStartupTime + _shuttle.DefaultTravelTime + 1f)); // TODO: I know this is shit but I already just cleaned up a billion things. @@ -157,7 +156,7 @@ public sealed partial class EmergencyShuttleSystem } // Imminent departure - if (!_launchedShuttles && _consoleAccumulator <= ShuttleSystem.DefaultStartupTime) + if (!_launchedShuttles && _consoleAccumulator <= _shuttle.DefaultStartupTime) { _launchedShuttles = true; diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index f0256aa15b..9bfe1bf986 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -200,10 +200,9 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem /// private void OnEmergencyFTL(EntityUid uid, EmergencyShuttleComponent component, ref FTLStartedEvent args) { - TimeSpan ftlTime = TimeSpan.FromSeconds + var ftlTime = TimeSpan.FromSeconds ( - TryComp(uid, out var ftlComp) ? - ftlComp.TravelTime : ShuttleSystem.DefaultTravelTime + TryComp(uid, out var ftlComp) ? ftlComp.TravelTime : _shuttle.DefaultTravelTime ); if (TryComp(uid, out var netComp)) diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index a3cc2fde9b..11cc16e0cd 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -6,6 +6,7 @@ using Content.Server.Shuttles.Events; using Content.Server.Station.Events; using Content.Shared.Body.Components; using Content.Shared.Buckle.Components; +using Content.Shared.CCVar; using Content.Shared.Database; using Content.Shared.Ghost; using Content.Shared.Maps; @@ -34,18 +35,6 @@ public sealed partial class ShuttleSystem * This is a way to move a shuttle from one location to another, via an intermediate map for fanciness. */ - public const float DefaultStartupTime = 5.5f; - public const float DefaultTravelTime = 20f; - public const float DefaultArrivalTime = 5f; - private const float FTLCooldown = 10f; - public const float FTLMassLimit = 300f; - - // I'm too lazy to make CVars. - // >:( - // Confusingly, some of them already are cvars? - // I.e., shuttle transit time??? - // TODO Shuttle: fix spaghetti - private readonly SoundSpecifier _startupSound = new SoundPathSpecifier("/Audio/Effects/Shuttle/hyperspace_begin.ogg") { Params = AudioParams.Default.WithVolume(-5f), @@ -56,7 +45,12 @@ public sealed partial class ShuttleSystem Params = AudioParams.Default.WithVolume(-5f), }; - private readonly TimeSpan _hyperspaceKnockdownTime = TimeSpan.FromSeconds(5); + public float DefaultStartupTime; + public float DefaultTravelTime; + public float DefaultArrivalTime; + private float FTLCooldown; + public float FTLMassLimit; + private TimeSpan _hyperspaceKnockdownTime = TimeSpan.FromSeconds(5); /// /// Left-side of the station we're allowed to use @@ -94,6 +88,13 @@ public sealed partial class ShuttleSystem _physicsQuery = GetEntityQuery(); _statusQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); + + _cfg.OnValueChanged(CCVars.FTLStartupTime, time => DefaultStartupTime = time, true); + _cfg.OnValueChanged(CCVars.FTLTravelTime, time => DefaultTravelTime = time, true); + _cfg.OnValueChanged(CCVars.FTLArrivalTime, time => DefaultArrivalTime = time, true); + _cfg.OnValueChanged(CCVars.FTLCooldown, time => FTLCooldown = time, true); + _cfg.OnValueChanged(CCVars.FTLMassLimit, time => FTLMassLimit = time, true); + _cfg.OnValueChanged(CCVars.HyperspaceKnockdownTime, time => _hyperspaceKnockdownTime = TimeSpan.FromSeconds(time), true); } private void OnStationPostInit(ref StationPostInitEvent ev) @@ -215,7 +216,9 @@ public sealed partial class ShuttleSystem return false; } - if (TryComp(shuttleUid, out PhysicsComponent? shuttlePhysics) && shuttlePhysics.Mass > FTLMassLimit) + if (FTLMassLimit > 0 && + TryComp(shuttleUid, out PhysicsComponent? shuttlePhysics) && + shuttlePhysics.Mass > FTLMassLimit) { reason = Loc.GetString("shuttle-console-mass"); return false; @@ -248,15 +251,18 @@ public sealed partial class ShuttleSystem ShuttleComponent component, EntityCoordinates coordinates, Angle angle, - float startupTime = DefaultStartupTime, - float hyperspaceTime = DefaultTravelTime, + float? startupTime = null, + float? hyperspaceTime = null, string? priorityTag = null) { if (!TrySetupFTL(shuttleUid, component, out var hyperspace)) return; - hyperspace.StartupTime = startupTime; - hyperspace.TravelTime = hyperspaceTime; + startupTime ??= DefaultStartupTime; + hyperspaceTime ??= DefaultTravelTime; + + hyperspace.StartupTime = startupTime.Value; + hyperspace.TravelTime = hyperspaceTime.Value; hyperspace.StateTime = StartEndTime.FromStartDuration( _gameTiming.CurTime, TimeSpan.FromSeconds(hyperspace.StartupTime)); @@ -280,16 +286,19 @@ public sealed partial class ShuttleSystem EntityUid shuttleUid, ShuttleComponent component, EntityUid target, - float startupTime = DefaultStartupTime, - float hyperspaceTime = DefaultTravelTime, + float? startupTime = null, + float? hyperspaceTime = null, string? priorityTag = null) { if (!TrySetupFTL(shuttleUid, component, out var hyperspace)) return; + startupTime ??= DefaultStartupTime; + hyperspaceTime ??= DefaultTravelTime; + var config = _dockSystem.GetDockingConfig(shuttleUid, target, priorityTag); - hyperspace.StartupTime = startupTime; - hyperspace.TravelTime = hyperspaceTime; + hyperspace.StartupTime = startupTime.Value; + hyperspace.TravelTime = hyperspaceTime.Value; hyperspace.StateTime = StartEndTime.FromStartDuration( _gameTiming.CurTime, TimeSpan.FromSeconds(hyperspace.StartupTime)); diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 9686589672..912e52c71b 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1381,6 +1381,42 @@ namespace Content.Shared.CCVar public static readonly CVarDef PreloadGrids = CVarDef.Create("shuttle.preload_grids", true, CVar.SERVERONLY); + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef FTLStartupTime = + CVarDef.Create("shuttle.startup_time", 5.5f, CVar.SERVERONLY); + + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef FTLTravelTime = + CVarDef.Create("shuttle.travel_time", 20f, CVar.SERVERONLY); + + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef FTLArrivalTime = + CVarDef.Create("shuttle.arrival_time", 5f, CVar.SERVERONLY); + + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef FTLCooldown = + CVarDef.Create("shuttle.cooldown", 10f, CVar.SERVERONLY); + + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef FTLMassLimit = + CVarDef.Create("shuttle.mass_limit", 300f, CVar.SERVERONLY); + + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef HyperspaceKnockdownTime = + CVarDef.Create("shuttle.hyperspace_knockdown_time", 5f, CVar.SERVERONLY); + /* * Emergency */ diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 620de42253..0decb9a11b 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -55,6 +55,7 @@ AL BB CC + FTL GC GD GL From 7524fb93b68bd610df108d1f19d40730f8b3f744 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 11 May 2024 01:20:48 -0700 Subject: [PATCH 11/68] Fix const data field in BlindableComponent (#27919) * Fix const data field in BlindableComponent * Fix usages --- Content.Shared/Eye/Blinding/Components/BlindableComponent.cs | 2 +- Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs | 4 ++-- Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs b/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs index 39718bd4cf..c2e73bd6b8 100644 --- a/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs +++ b/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs @@ -25,7 +25,7 @@ public sealed partial class BlindableComponent : Component public int EyeDamage = 0; [ViewVariables(VVAccess.ReadOnly), DataField] - public const int MaxDamage = 9; + public int MaxDamage = 9; [ViewVariables(VVAccess.ReadOnly), DataField] public int MinDamage = 0; diff --git a/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs b/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs index 0232bcf937..24eed3adcf 100644 --- a/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs @@ -37,7 +37,7 @@ public sealed class BlindableSystem : EntitySystem var old = blindable.Comp.IsBlind; // Don't bother raising an event if the eye is too damaged. - if (blindable.Comp.EyeDamage >= BlindableComponent.MaxDamage) + if (blindable.Comp.EyeDamage >= blindable.Comp.MaxDamage) { blindable.Comp.IsBlind = true; } @@ -70,7 +70,7 @@ public sealed class BlindableSystem : EntitySystem return; var previousDamage = blindable.Comp.EyeDamage; - blindable.Comp.EyeDamage = Math.Clamp(blindable.Comp.EyeDamage, blindable.Comp.MinDamage, BlindableComponent.MaxDamage); + blindable.Comp.EyeDamage = Math.Clamp(blindable.Comp.EyeDamage, blindable.Comp.MinDamage, blindable.Comp.MaxDamage); Dirty(blindable); if (!isDamageChanged && previousDamage == blindable.Comp.EyeDamage) return; diff --git a/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs b/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs index 5326af9636..9a4afaec3a 100644 --- a/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs @@ -1,4 +1,3 @@ - using Content.Shared.Actions; using Content.Shared.Eye.Blinding.Components; using Robust.Shared.Audio.Systems; @@ -124,7 +123,7 @@ public sealed class EyeClosingSystem : EntitySystem if (_entityManager.TryGetComponent(blindable, out var eyelids) && !eyelids.NaturallyCreated) return; - if (ev.Blur < BlurryVisionComponent.MaxMagnitude || ev.Blur >= BlindableComponent.MaxDamage) + if (ev.Blur < BlurryVisionComponent.MaxMagnitude || ev.Blur >= blindable.Comp.MaxDamage) { RemCompDeferred(blindable); return; From 1db48b86d1355cc2653989d2eaf5d7b179fa76af Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Sat, 11 May 2024 02:25:40 -0700 Subject: [PATCH 12/68] Fix collection modified error when locking storage (#27913) --- Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index ba840cd67a..8ad588baab 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -1411,11 +1411,11 @@ public abstract class SharedStorageSystem : EntitySystem return; // Gets everyone looking at the UI - foreach (var actor in _ui.GetActors(uid, StorageComponent.StorageUiKey.Key)) + foreach (var actor in _ui.GetActors(uid, StorageComponent.StorageUiKey.Key).ToList()) { if (_admin.HasAdminFlag(actor, AdminFlags.Admin)) continue; - + // And closes it unless they're an admin _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, actor); } From 9a04170e8667052716a73acffbb4a1c33d6273d5 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 11 May 2024 05:11:20 -0700 Subject: [PATCH 13/68] Fix shuttle cvars comments (#27923) * Fix shuttle cvars comments * Add another line to ftl mass limit --- Content.Shared/CCVar/CCVars.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 912e52c71b..67122463ac 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1,6 +1,7 @@ using Content.Shared.Maps; using Robust.Shared; using Robust.Shared.Configuration; +using Robust.Shared.Physics.Components; namespace Content.Shared.CCVar { @@ -1382,37 +1383,38 @@ namespace Content.Shared.CCVar CVarDef.Create("shuttle.preload_grids", true, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// How long the warmup time before FTL start should be. /// public static readonly CVarDef FTLStartupTime = CVarDef.Create("shuttle.startup_time", 5.5f, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// How long a shuttle spends in FTL. /// public static readonly CVarDef FTLTravelTime = CVarDef.Create("shuttle.travel_time", 20f, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// How long the final stage of FTL before arrival should be. /// public static readonly CVarDef FTLArrivalTime = CVarDef.Create("shuttle.arrival_time", 5f, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// How much time needs to pass before a shuttle can FTL again. /// public static readonly CVarDef FTLCooldown = CVarDef.Create("shuttle.cooldown", 10f, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// The maximum a grid can have before it becomes unable to FTL. + /// Any value equal to or less than zero will disable this check. /// public static readonly CVarDef FTLMassLimit = CVarDef.Create("shuttle.mass_limit", 300f, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// How long to knock down entities for if they aren't buckled when FTL starts and stops. /// public static readonly CVarDef HyperspaceKnockdownTime = CVarDef.Create("shuttle.hyperspace_knockdown_time", 5f, CVar.SERVERONLY); From 9ee42e11389e9d006588c67edd66e1bb2d526cab Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Sat, 11 May 2024 09:52:57 -0400 Subject: [PATCH 14/68] Fix security jumpsuit sprite's asymmetry (#27925) * inital * Update meta.json --- .../security.rsi/equipped-INNERCLOTHING.png | Bin 1266 -> 663 bytes .../Uniforms/Jumpsuit/security.rsi/meta.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/equipped-INNERCLOTHING.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/equipped-INNERCLOTHING.png index b3977c974892c805bea08700ae201a943f3ce16e..2fb771d84c3133abcc7c0cfd62cb6fe93f5016bb 100644 GIT binary patch delta 650 zcmV;50(JfJ36}+s8Gi!+005o0f$RVP00DDSM?wIu&K&6g000DMK}|sb0I`n?{9y$E z0016POjJdEC@fqbBs~`#!7VVKDlRWBF(V=-R8vqG7a0Hm0F-c3UpFVQ~&?} z|NsC0|Nj88%q-6U000SaNLh0L01m?d01m?e$8V@)0005(NqLSs&?B^5HY1|o=l_+U{_)X&3ftvLctpmn#25i^ z9StGmpxNmc%YUZDy6GOtGuH^d(8-9v9o??eh}X{l00h>L0H#n|M}TR4@gwiQH!L~; zme{B(-SZj{b~T-?*VNMntc?2jC%oyyeFo?jy3fA5wOy_H2WxxATLM2 z2;*`JykRM`ADxZ>TLGZ+mae9djSZgz;P3SC*h0wzYJb2KK<{gSHGm7~^d2w@==2_d zZkMr!{vPm=PQVF}PQVcmEVb5D=}Kh? z0XX!PJ%66+0g=4<4FF-ymK=c8sYPB+L0SV`5$8qmP&EZq&GUkC!Y!uRl3xQv2`~px z9Vli75J4HfSp$*RAoS0BK%KK~#90?V3$XBS{#?|LMF?F=3cdM_~~f z8v`14P@}J$1uwh79uST!q_{ji7$zb znyx*>YAZd_bX7X-E>pjYrfuv0)Km35RjGOag+ifFC=?3Cw|_}Y`n%>w-A~=y%-EG#GJBSU=E^OK&$Ux_d$$v!XPT)JwYcY76jJSrMVljOBqaH|K8}-84YJui*I!c``4zC=TJr@9A z#5H8I8P6cI=X!rHCKK3NUZH~h9EyMB8HT0}`~(2gby4bck;~~=TP+0APmFrunf(Rz z%MP;H3@rN#Do%gumFH%?ci#W~&%F`p`xy}ytn;;28-K=KxG@cW0BWr^vhy!duQ%{r zryHp4M=jl2UZLf=S-eZ7VB5z4fVVH6qck%;@Lp+V8gE}b2LRMsZ7&qYodw?L-*q0q zz%1Zl&u#k{?@}q0=Vk-xj~uGKn_JXcZ5-R5y_oWkF#mbqK8i6Xj0+2?TB|*<>O5oz z036$&g@5!P4FR^~$-qWzTq-B0Up?cKA^SW`UngW_`;R(+Uu#7eHd-<~q;I-D2l&E* z>F)>7kjhqmj}IGxNy9Y9)fO75_4x$~VCd9hp%T{UN2Nb<8T7+vd;ZyCnuHejLG=CW zzpTDg2pF2??E}hlvjfIN0o(SnTwrLLv<+if9)BduhE1*2?suNsc^YwT=xPxJX$S0_ zUt-rfi0zzT$~GYd+k{m+UF=#1vFhE9uQW3)H6U^!@W@7Jl1yU830XeMfTswFaSmKglzHH(^%V+*LZMJ76bgmn+huEcg>LFg^ke>YC>NmP@G<|I zZhz`aRQ4K9_z0V?6WBSw^zP<#; z@8A7CVtDP zF)ikZ-{Tqzg;#wW>kXl{gwC-Fg;!`cEq~POjefm=jm#9-?3g4UQb}xN zrcg{KP&qk;runHTuFSRqQxp4-0{te6 zFzPY#XBXJH`#p?M6n23B`irf{!2PCZ(y+~snI;1jqkyK|>H@*Lq1VCaN+UqYVhZjM!{?3`ap z=Y#*}m2QYgJ0PkrK0z4A7HJ3km+Aq=u|+Be=!6Wtr;i#w_vnNKsti>q6a)MVGVDot T-(-B;00000NkvXXu0mjf-@sgV diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/meta.json b/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/meta.json index 3f72fb4460..c11c48fe74 100644 --- a/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/meta.json +++ b/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039. In hand sprite scaled down by potato1234_x, monkey made by brainfood1183 (github) for ss14", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039. In hand sprite scaled down by potato1234_x, monkey made by brainfood1183 (github) for ss14. equipped-INNERCLOTHING edited by Dutch-VanDerLinde (github).", "size": { "x": 32, "y": 32 From 5da04e1cfc040a40bce0a6178b3143a7a36c02c8 Mon Sep 17 00:00:00 2001 From: RiceMar1244 <138547931+RiceMar1244@users.noreply.github.com> Date: Sat, 11 May 2024 10:40:01 -0400 Subject: [PATCH 15/68] Fixes oversight in wieldable smg accuracy (#27907) --- .../Entities/Objects/Weapons/Guns/SMGs/smgs.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml index f5193164e7..dd1e3e21f4 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml @@ -80,7 +80,7 @@ - type: entity name: C-20r sub machine gun - parent: [BaseWeaponSubMachineGun, BaseGunWieldable] + parent: BaseWeaponSubMachineGun id: WeaponSubMachineGunC20r description: A firearm that is often used by the infamous nuclear operatives. Uses .35 auto ammo. components: @@ -93,9 +93,13 @@ map: ["enum.GunVisualLayers.Mag"] - type: Clothing sprite: Objects/Weapons/Guns/SMGs/c20r.rsi + - type: Wieldable + - type: GunWieldBonus + minAngle: -19 + maxAngle: -16 - type: Gun minAngle: 21 - maxAngle: 32 + maxAngle: 32 shotsPerBurst: 5 availableModes: - SemiAuto @@ -113,7 +117,7 @@ - type: entity name: Drozd - parent: [BaseWeaponSubMachineGun, BaseGunWieldable] + parent: BaseWeaponSubMachineGun id: WeaponSubMachineGunDrozd description: An excellent fully automatic Heavy SMG. components: @@ -126,9 +130,13 @@ map: ["enum.GunVisualLayers.Mag"] - type: Clothing sprite: Objects/Weapons/Guns/SMGs/drozd.rsi + - type: Wieldable + - type: GunWieldBonus + minAngle: -19 + maxAngle: -16 - type: Gun minAngle: 21 - maxAngle: 32 + maxAngle: 32 fireRate: 6 selectedMode: FullAuto soundGunshot: From 8366c743f25367930ba9a53f0b7523edcee570cf Mon Sep 17 00:00:00 2001 From: Flareguy <78941145+Flareguy@users.noreply.github.com> Date: Sat, 11 May 2024 09:54:56 -0500 Subject: [PATCH 16/68] Gives vox The Brows in their front facing sprite (#27922) * gives vox The Brows in their front facing sprite * JASON --- .../Mobs/Species/Vox/parts.rsi/full.png | Bin 789 -> 493 bytes .../Mobs/Species/Vox/parts.rsi/head.png | Bin 689 -> 429 bytes .../Mobs/Species/Vox/parts.rsi/meta.json | 2 +- .../Mobs/Species/Vox/parts.rsi/vox_m.png | Bin 2545 -> 1123 bytes 4 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Textures/Mobs/Species/Vox/parts.rsi/full.png b/Resources/Textures/Mobs/Species/Vox/parts.rsi/full.png index 864a6f00376be96bbf8ad96a633fc2cd946d03c1..6338e4d1128571aba60319383e188837560825cd 100644 GIT binary patch delta 478 zcmbQr_Lg~qWIZzj1B1(wu46!ou{g-xiDBJ2nU_EgOS+@4BLl<6e(pbstU$g^fKQ04 zXP|Istawg|tas%^@xQ+-QtT(!*~X+wM0wc;MTt4O3whhxdD+@}*w|R=0gxv+2Ib1yMw4hPKKf|d%9rzXUY^^mlIHhGJHUhSMdoXsdKLk%h2g&34ZpcHIn!QR zubVkJlB3CB^VhBW9>ttk)ZwBW*}>V7qh{!~(RZ4o+cx8dXB#-**Os$R5{uyA=~_1b zz_~7_+9T-~G7T9d9>@tkJh0`(fq4%aZdY>)oN9h?Ao^{Bn4sO`Hy0bf_utva(x)19 z@+VV`QbE#>{Rv^KN-u{qOb9D~|9H0efv5Kv_h-ya&NyjWbZnC4qeITz6C$-2O#OZB z)wE*PSH&5uKUN$#xHcz>;TqoyFP{fX*DqFM-!#i%X$|8QM;rI-j>JAtD0{m4xvX|27rl58 zK}$i}z+z)7)V8rro_=1O^(F3h-`j&;99Ujv-pqWzJ8#}>M1TIvKdwMj1poS-IWrme z^l`6Hu2Lugz-MNTNwZlKcTFSZlRJpDg4gTEC62!*`BmT9Ih4>!1XsWXZ&uG+5|F`O zM1l#dRqo1*2kWBXtmxQ_NTDMD2;j4gp=@+Zvf6)dvH!{uRHvc@98gF=273_+IszK3 zw|O9+y2CLnbbsuj4M>t80t9gNwyN)>^LoUA6&-sK@vfWRbATu}Z#>&yvfy*`73uFA z+XF~QCA98i5+GC1N-oR8!Z zAPK`}xUnG5pM8n8dWX{O_Q!IgV`m>f{C(v}&zr5w=Gs+b?|u8pOWS=@waE5@E9lq} z{O~7^_&g%HRMXyuYo}#rMV}PkDr6FX{l&$|j#?Eu8?fDPLwo)RD>`{n6;#~4sN0CWjXpFR1jI6DXLMP{eHX^IufBq$D6^LuE z=d&3V&!azI?plV(px4uP(`>hq0{;D>`=e z@e2`e0yGsM`afV21UviMw7w*~K1L+#yk!!$GAA3rJR?7U@vZ+8Gi!+005o0f$RVP00DDSM?wIu&K&6g000DMK}|sb0I`n?{9y$E z000lr3`}?r4u#S$70001FEo-j;000PdQchF<|NsC0|Nj8q zgS}Y*000SaNLh0L01FWS01FWTe`H^g0003BNklb z32L@1OSXjLMJvit+Y2n$hy0qL2==~WF+Oz*G1}fxsG_+w?I}n;9Sq2?T%BPS=oa?^ zMn2kJ!0=a|Cp=Lw2I^iwm{JcrATgYdpewX_g(pNWZ7aA+hoJBBF^(;q{(>3~`C2BR01_q{N zPZ!6KinzD4V*4B&1zM}~be#4GaDO<^>{^keDVC&JH|=4gDhK}&IS%9HlP7PnW(&aOXRKMEq`t`ftPp##1RAc%->p;8u?~fHVdcS$un8VlDFYkXM-hEVZTHbock1FNo z+kf4DYxb)6^;P|MoXf5&2CcvDy!$TS>8D0tj7Sb&!zW@b*MCt|Kua`SqW_EQC7*c)HjHO= z{j23WnOV+PN+o=~`TqOG)|1Kd%8k$0emoP*@LBh5slx%o=S#OTUi-ap@;8g+2UmZ2 z+?;1M_tTbsZx0mupPAs;Ac9vi@(x){{a%i=HXzrvA;VXAN=eW=<_@>KM6yC!WP8=M8!!JxDl;^0FkrMj7?)ijJ?o oH}8ud{N7i@GLTxA$bd65_W`*>tOlYTu~NpIkV!;AR9HvNR@)YvAQ0q|07`9%{{O#rX5Brdh?yxKfn&ThwzX=#Z%Oec88j_D}&4$Tbx>OoBj~`GZFY-)7;k zP)NpV?G7Xub2JqI?M*W1VjP1pep$R=@=fdu~RiTo4|lou=$ z?-ulQSD?Q`_xLpmmw*Q$)3mI9FcBgE5;kLj9H}f3tbaacZM~4AM&KY&@s~hI2SPxI z>yg4hfV@P|@^6706Dk;oA_*x+DVz9@vdP7lu zCJ>Vvq_M)7zXZ0l?NZoLl0u7=cIEd}Ww9Q@Uw`306u>KICnp2nq`SR7-!O7~jwG<> zle;{o2_QfozuM4&$2$f&5LgMO8VUF^ z1{nd;zzI^9Q()x>F1#?=%;L8Qm_sfR7OkZn1XkRfeh9=B24><=_;&(0b(`;(8yNlk0|UJmxu+NyGyR%AwslP76gU7RGTF$6Y-Ris8V?73u>jlRQ(6~oX7qH{Rb*V z;w6=c8YP6jAg~%M5vkOCpytEt6>Hqi#s;Gr8)H0oABHo&;}7s1+o`mhpA?Z9=HA~q z=bn4cxgP*;@fQErU``s%&V_jWyQRgPqrU!Ze(6TDb0O~cyAg?;g03G}KMRG!H(~_h zSG)}6eQy19%I|VQPD%r?jNtdXn~qF3Au105k;o|kfZ}D2`K1g%q@;XQ83j-?!kYl` z1O`0F?^X~P@YvqVLfQZ+&(~P{E^R3vRYtM)T^cFR*Ny~`g{ZXnGrwDL)St8g)bux) z{^BeEU{Esv0MlQbg_{1xkpQwN@AC#_^Sc$4_npl?X#*%;hNv=XDL<$gh$^GjyzDg; zFSCs}d4yAbDFZAm=2$LU!Dhy=z2D3j$Ym=%&jQ3xF^Hh+2afrrJ;3jGV@KIQV%>aB ztOGmB2K;`vY*&s^C>&ZYPbttT+O`;_7)nFAYz4vc70vqKAfEk@&5ik06<@#*`&S@sa9)2s8p(U)d;h5As&sY$Yjdc z$sHmXazpjGEJFaW-fNnIWU|;c|A|w8lvjN&luA`ptK3$A0JR)kDpjHSTsnD% zul)Fg&r)9XxhyXj96E)a+#xcVGNMtnZT_|M+`A5{@w)xx20$UjujIDE6S>9&kW+KQvSC+S#v2_kx8R_J5BEL63f|ccM_Uy?X z0s|gv8Wi^oruoDOvr1mTjL4LS#Zt7mXCM^z*dp(sW?*83Irf59{8R7N~J17VGlG-!R#FP(qfLK z(m*N=oE~s&11#JE5|85+KY3q$>02lqf@GVW3-P7J99y^>M=BjVk)x#n9=%N9wO(as zB7K$%QNStg8MZ*!-{04;CV!>hZ)h)rGm$>|4NFRT#n_QIt0oaT{$_EBKRxE1P zz8Ox;7q9iI<$i$yk1Yj?eV4Fyp<{B2hZ!SWaa z10Ia1!^kJcaPx-F0N~~go#m5b7*U7Kd|V!Dw@WDm%VT)-<1ns&b+IY`^{+1C(T~H@ z{B0W`l{QOE+6SmdV%_|GDlNs|Hd1L|{u++upT7oa{u+oA)0MGR$9->CGAb*lW(;>itP*+W_BQkMUOSM@X!j-#L<` z4gTXs9{}DzE6?Aq0r*ix2B6W+=y~C8oaSg(jWB=3G(L@TEB-(Bm>CPM^kxv=;w}CM z(Q(QWx627sq;wmfejc&rcDJ_RJ8Dcjwj#uHx^5YQ6sKb`5i(og&EU8pDc|Z;me%Q= z8sXE=BRmp0bu3ROM(FB!mbfiezeVO0g~DOO`acwIT=Y0aN87hFD!FV0xoib~zZ-hQm-_6dm;~i~`UX4w2+TQ;8``_Ujy$-+MZSjdRI7P=XAQUz?A!t-cuxM0;KmP#+ zH3Ron81BT^q_}8Dcq`*VA@EOY$t&^@cPBBR<0(+y=awLVsW<>^Zw$k)b+$W9`}^A) z!-$Uk3jl0}aC#|1gyShd=}(-WON%*{Ocs$$7O|sjU^8Ph+$E3!wck-TK>doNP28QF zLpt(+`m1W6=JIJi zKH;;1AIab}9ZKI5tXDpltynw4;}bs2Tt3~im6)hJ?EsSp|KJH-40xUw98c^Y(_ zE(DtaW-g!R;lI?B5N=;`YB;DFKxP!bY-L>7|II8> z>7sHps=})>OilJ~J8Eh&#=R;tt4!h2^!{(-*vhzYZ-t?#KDB~%vs>g#i#a9^=!tcJ z;$_hF1FOPIJ*b`fQV^tX-W16F#rlKXUvzuL%TOpB-&ei6^7?mF?^Cqh`Sq`u7%@Lt zeP|$h;iWYvBau@`CX2`?$GUz#Ff|$Df#?RJQPtwg?^cj}a1NLLW;*hd=N!S&7q%4L zq%N@OSf2(Pgr@7MG#!GYFYx3!x4eMX0Afi@0(BE&pTtGn=IGayFBA@sTO_s40<9nh zkU{}ifAJHxcQ4|Vejgu216C12>1p+$^ZLIPRv#J&MA5+YO23bVTNm+4{|O(312%cv zf%Ii*>iq2a7uNgw`$Dhu`!e0O1?sb&OO!>FuU$CtRsQ$Ue*ggJ;sFUnqjKDZ1gAKO z>qw^F{mBfaw@)rj;~gKz%^NyC`|@2xqbhRQh6|Ius0A2M^MG8|oC=?P`7UnW(D9Cs z$M{{#gc&3^1g*Y=Nf! zx!0JQjB(N3>z{jy`71Ggq%1!=hi`c9A$?2EsmU03axdV&bmRruP=F!s00000NkvXX Hu0mjf1AW{Q From b860774d7cd7ee2eb03558e9d02e400916953091 Mon Sep 17 00:00:00 2001 From: Vasilis Date: Sat, 11 May 2024 18:01:28 +0300 Subject: [PATCH 17/68] Do not wake up NPC if there is still a mind attached. (#27651) * Do not wake up NPC if there is still a mind attached. This became apparent with diona nymphs (?) and slime gyras (?). This caused players that disconnected while a nymph, gyras or other npc to resume their NPC behavior. Which I would call unwanted. This fixes that. * Zombies become AI anyway * Update Content.Server/NPC/Systems/NPCSystem.cs --------- Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --- Content.Server/NPC/Systems/NPCSystem.cs | 6 ++++++ Content.Server/Zombies/ZombieSystem.Transform.cs | 10 ++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Content.Server/NPC/Systems/NPCSystem.cs b/Content.Server/NPC/Systems/NPCSystem.cs index 9108629435..bc21916976 100644 --- a/Content.Server/NPC/Systems/NPCSystem.cs +++ b/Content.Server/NPC/Systems/NPCSystem.cs @@ -2,6 +2,8 @@ using System.Diagnostics.CodeAnalysis; using Content.Server.NPC.Components; using Content.Server.NPC.HTN; using Content.Shared.CCVar; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Systems; using Content.Shared.NPC; @@ -49,6 +51,10 @@ namespace Content.Server.NPC.Systems if (_mobState.IsIncapacitated(uid) || TerminatingOrDeleted(uid)) return; + // This NPC has an attached mind, so it should not wake up. + if (TryComp(uid, out var mindContainer) && mindContainer.HasMind) + return; + WakeNPC(uid, component); } diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index 23e1c2bb92..a2c13ed71c 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -79,7 +79,7 @@ namespace Content.Server.Zombies /// the entity being zombified /// /// - /// ALRIGHT BIG BOY. YOU'VE COME TO THE LAYER OF THE BEAST. THIS IS YOUR WARNING. + /// ALRIGHT BIG BOYS, GIRLS AND ANYONE ELSE. YOU'VE COME TO THE LAYER OF THE BEAST. THIS IS YOUR WARNING. /// This function is the god function for zombie stuff, and it is cursed. I have /// attempted to label everything thouroughly for your sanity. I have attempted to /// rewrite this, but this is how it shall lie eternal. Turn back now. @@ -226,6 +226,11 @@ namespace Content.Server.Zombies _identity.QueueIdentityUpdate(target); + var htn = EnsureComp(target); + htn.RootTask = new HTNCompoundTask() { Task = "SimpleHostileCompound" }; + htn.Blackboard.SetValue(NPCBlackboard.Owner, target); + _npc.SleepNPC(target, htn); + //He's gotta have a mind var hasMind = _mind.TryGetMind(target, out var mindId, out _); if (hasMind && _mind.TryGetSession(mindId, out var session)) @@ -241,9 +246,6 @@ namespace Content.Server.Zombies } else { - var htn = EnsureComp(target); - htn.RootTask = new HTNCompoundTask() { Task = "SimpleHostileCompound" }; - htn.Blackboard.SetValue(NPCBlackboard.Owner, target); _npc.WakeNPC(target, htn); } From 742a1a5fbd3ad24b48c9e7cd92c29e184cdf9288 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 May 2024 15:02:36 +0000 Subject: [PATCH 18/68] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 1ee24a441f..24d0e6436f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: SlamBamActionman - changes: - - message: Arachnids are no longer perma-pied and can clean it off. - type: Fix - id: 6083 - time: '2024-03-03T08:54:36.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25713 - author: Dutch-VanDerLinde changes: - message: Monkeys and kobolds can now wear blood-red hardsuits. @@ -3864,3 +3857,11 @@ id: 6582 time: '2024-05-11T02:21:18.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27891 +- author: Vasilis + changes: + - message: NPC's activly being controlled by a player wont return to their AI behevior + if the player disconnects. + type: Fix + id: 6583 + time: '2024-05-11T15:01:29.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27651 From a985c5e83ead6098783ed2129eed516dbd619586 Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Sat, 11 May 2024 08:03:40 -0700 Subject: [PATCH 19/68] Fix ghosts getting spawned in nullspace (#27617) * Add tests for ghost spawn position * Make ghosts spawn immediately * Format mind system * Move ghost spawning to GhostSystem * Spawn ghost on grid or map This fixes the ghosts being attached the parent entity instead of the grid. * Move logging out of the ghost system * Make round start observer spawn using GhostSystem * Move GameTicker ghost spawning to GhostSystem Moved the more robust character name selection code over. Moved the TimeOfDeath code over. Added canReturn logic. * Add overrides and default for ghost spawn coordinates * Add warning log to ghost spawn fail * Clean up test * Dont spawn ghost on map delete * Minor changes to the role test * Fix role test failing to spawn ghost It was failing the map check due to using Nullspace * Fix ghost tests when running in parallel Not sure what happened, but it seems to be because they were running simultaneously and overwriting values. * Clean up ghost tests * Test that map deletion does not spawn ghosts * Spawn ghost on the next available map * Disallow spawning on deleted maps * Fix map deletion ghost test * Cleanup --- .../Tests/Minds/GhostRoleTests.cs | 12 +- .../Tests/Minds/GhostTests.cs | 159 ++++++++++++++++++ .../Tests/Minds/MindTests.EntityDeletion.cs | 17 +- .../GameTicking/GameTicker.GamePreset.cs | 28 +-- .../GameTicking/GameTicker.Spawning.cs | 40 ++--- Content.Server/Ghost/GhostSystem.cs | 57 +++++++ Content.Server/Mind/MindSystem.cs | 47 +----- 7 files changed, 261 insertions(+), 99 deletions(-) create mode 100644 Content.IntegrationTests/Tests/Minds/GhostTests.cs diff --git a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs index ca97e435a7..150bc951f8 100644 --- a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs +++ b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs @@ -1,5 +1,6 @@ #nullable enable using System.Linq; +using Content.IntegrationTests.Pair; using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles.Components; using Content.Server.Players; @@ -26,7 +27,7 @@ public sealed class GhostRoleTests "; /// - /// This is a simple test that just checks if a player can take a ghost roll and then regain control of their + /// This is a simple test that just checks if a player can take a ghost role and then regain control of their /// original entity without encountering errors. /// [Test] @@ -34,12 +35,15 @@ public sealed class GhostRoleTests { await using var pair = await PoolManager.GetServerClient(new PoolSettings { + Dirty = true, DummyTicker = false, Connected = true }); var server = pair.Server; var client = pair.Client; + var mapData = await pair.CreateTestMap(); + var entMan = server.ResolveDependency(); var sPlayerMan = server.ResolveDependency(); var conHost = client.ResolveDependency(); @@ -51,7 +55,7 @@ public sealed class GhostRoleTests EntityUid originalMob = default; await server.WaitPost(() => { - originalMob = entMan.SpawnEntity(null, MapCoordinates.Nullspace); + originalMob = entMan.SpawnEntity(null, mapData.GridCoords); mindSystem.TransferTo(originalMindId, originalMob, true); }); @@ -69,12 +73,12 @@ public sealed class GhostRoleTests Assert.That(entMan.HasComponent(ghost)); Assert.That(ghost, Is.Not.EqualTo(originalMob)); Assert.That(session.ContentData()?.Mind, Is.EqualTo(originalMindId)); - Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob)); + Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob), $"Original mob: {originalMob}, Ghost: {ghost}"); Assert.That(originalMind.VisitingEntity, Is.EqualTo(ghost)); // Spawn ghost takeover entity. EntityUid ghostRole = default; - await server.WaitPost(() => ghostRole = entMan.SpawnEntity("GhostRoleTestEntity", MapCoordinates.Nullspace)); + await server.WaitPost(() => ghostRole = entMan.SpawnEntity("GhostRoleTestEntity", mapData.GridCoords)); // Take the ghost role await server.WaitPost(() => diff --git a/Content.IntegrationTests/Tests/Minds/GhostTests.cs b/Content.IntegrationTests/Tests/Minds/GhostTests.cs new file mode 100644 index 0000000000..ad9d53a70d --- /dev/null +++ b/Content.IntegrationTests/Tests/Minds/GhostTests.cs @@ -0,0 +1,159 @@ +using System.Numerics; +using Content.IntegrationTests.Pair; +using Content.Shared.Ghost; +using Content.Shared.Mind; +using Content.Shared.Players; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Player; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.Tests.Minds; + +[TestFixture] +public sealed class GhostTests +{ + struct GhostTestData + { + public IEntityManager SEntMan; + public Robust.Server.Player.IPlayerManager SPlayerMan; + public Server.Mind.MindSystem SMindSys; + public SharedTransformSystem STransformSys = default!; + + public TestPair Pair = default!; + + public TestMapData MapData => Pair.TestMap!; + + public RobustIntegrationTest.ServerIntegrationInstance Server => Pair.Server; + public RobustIntegrationTest.ClientIntegrationInstance Client => Pair.Client; + + /// + /// Initial player coordinates. Note that this does not necessarily correspond to the position of the + /// entity. + /// + public NetCoordinates PlayerCoords = default!; + + public NetEntity Player = default!; + public EntityUid SPlayerEnt = default!; + + public ICommonSession ClientSession = default!; + public ICommonSession ServerSession = default!; + + public GhostTestData() + { + } + } + + private async Task SetupData() + { + var data = new GhostTestData(); + + // Client is needed to create a session for the ghost system. Creating a dummy session was too difficult. + data.Pair = await PoolManager.GetServerClient(new PoolSettings + { + DummyTicker = false, + Connected = true, + Dirty = true + }); + + data.SEntMan = data.Pair.Server.ResolveDependency(); + data.SPlayerMan = data.Pair.Server.ResolveDependency(); + data.SMindSys = data.SEntMan.System(); + data.STransformSys = data.SEntMan.System(); + + // Setup map. + await data.Pair.CreateTestMap(); + data.PlayerCoords = data.SEntMan.GetNetCoordinates(data.MapData.GridCoords.Offset(new Vector2(0.5f, 0.5f)).WithEntityId(data.MapData.MapUid, data.STransformSys, data.SEntMan)); + + if (data.Client.Session == null) + Assert.Fail("No player"); + data.ClientSession = data.Client.Session!; + data.ServerSession = data.SPlayerMan.GetSessionById(data.ClientSession.UserId); + + Entity mind = default!; + await data.Pair.Server.WaitPost(() => + { + data.Player = data.SEntMan.GetNetEntity(data.SEntMan.SpawnEntity(null, data.SEntMan.GetCoordinates(data.PlayerCoords))); + mind = data.SMindSys.CreateMind(data.ServerSession.UserId, "DummyPlayerEntity"); + data.SPlayerEnt = data.SEntMan.GetEntity(data.Player); + data.SMindSys.TransferTo(mind, data.SPlayerEnt, mind: mind.Comp); + data.Server.PlayerMan.SetAttachedEntity(data.ServerSession, data.SPlayerEnt); + }); + + await data.Pair.RunTicksSync(5); + + Assert.Multiple(() => + { + Assert.That(data.ServerSession.ContentData()?.Mind, Is.EqualTo(mind.Owner)); + Assert.That(data.ServerSession.AttachedEntity, Is.EqualTo(data.SPlayerEnt)); + Assert.That(data.ServerSession.AttachedEntity, Is.EqualTo(mind.Comp.CurrentEntity), + "Player is not attached to the mind's current entity."); + Assert.That(data.SEntMan.EntityExists(mind.Comp.OwnedEntity), + "The mind's current entity does not exist"); + Assert.That(mind.Comp.VisitingEntity == null || data.SEntMan.EntityExists(mind.Comp.VisitingEntity), + "The minds visited entity does not exist."); + }); + + Assert.That(data.SPlayerEnt, Is.Not.EqualTo(null)); + + return data; + } + + /// + /// Test that a ghost gets created when the player entity is deleted. + /// 1. Delete mob + /// 2. Assert is ghost + /// + [Test] + public async Task TestGridGhostOnDelete() + { + var data = await SetupData(); + + var oldPosition = data.SEntMan.GetComponent(data.SPlayerEnt).Coordinates; + + Assert.That(!data.SEntMan.HasComponent(data.SPlayerEnt), "Player was initially a ghost?"); + + // Delete entity + await data.Server.WaitPost(() => data.SEntMan.DeleteEntity(data.SPlayerEnt)); + await data.Pair.RunTicksSync(5); + + var ghost = data.ServerSession.AttachedEntity!.Value; + Assert.That(data.SEntMan.HasComponent(ghost), "Player did not become a ghost"); + + // Ensure the position is the same + var ghostPosition = data.SEntMan.GetComponent(ghost).Coordinates; + Assert.That(ghostPosition, Is.EqualTo(oldPosition)); + + await data.Pair.CleanReturnAsync(); + } + + /// + /// Test that a ghost gets created when the player entity is queue deleted. + /// 1. Delete mob + /// 2. Assert is ghost + /// + [Test] + public async Task TestGridGhostOnQueueDelete() + { + var data = await SetupData(); + + var oldPosition = data.SEntMan.GetComponent(data.SPlayerEnt).Coordinates; + + Assert.That(!data.SEntMan.HasComponent(data.SPlayerEnt), "Player was initially a ghost?"); + + // Delete entity + await data.Server.WaitPost(() => data.SEntMan.QueueDeleteEntity(data.SPlayerEnt)); + await data.Pair.RunTicksSync(5); + + var ghost = data.ServerSession.AttachedEntity!.Value; + Assert.That(data.SEntMan.HasComponent(ghost), "Player did not become a ghost"); + + // Ensure the position is the same + var ghostPosition = data.SEntMan.GetComponent(ghost).Coordinates; + Assert.That(ghostPosition, Is.EqualTo(oldPosition)); + + await data.Pair.CleanReturnAsync(); + } + +} diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs index 2ebe750f98..de7739b2ad 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs @@ -1,3 +1,4 @@ +#nullable enable using System.Linq; using Content.Server.GameTicking; using Content.Shared.Ghost; @@ -77,7 +78,7 @@ public sealed partial class MindTests await using var pair = await SetupPair(dirty: true); var server = pair.Server; var testMap = await pair.CreateTestMap(); - var coordinates = testMap.GridCoords; + var testMap2 = await pair.CreateTestMap(); var entMan = server.ResolveDependency(); var mapManager = server.ResolveDependency(); @@ -91,7 +92,7 @@ public sealed partial class MindTests MindComponent mind = default!; await server.WaitAssertion(() => { - playerEnt = entMan.SpawnEntity(null, coordinates); + playerEnt = entMan.SpawnEntity(null, testMap.GridCoords); mindId = player.ContentData()!.Mind!.Value; mind = entMan.GetComponent(mindId); mindSystem.TransferTo(mindId, playerEnt); @@ -100,14 +101,20 @@ public sealed partial class MindTests }); await pair.RunTicksSync(5); - await server.WaitPost(() => mapManager.DeleteMap(testMap.MapId)); + await server.WaitAssertion(() => mapManager.DeleteMap(testMap.MapId)); await pair.RunTicksSync(5); await server.WaitAssertion(() => { #pragma warning disable NUnit2045 // Interdependent assertions. - Assert.That(entMan.EntityExists(mind.CurrentEntity), Is.True); - Assert.That(mind.CurrentEntity, Is.Not.EqualTo(playerEnt)); + // Spawn ghost on the second map + var attachedEntity = player.AttachedEntity; + Assert.That(entMan.EntityExists(attachedEntity), Is.True); + Assert.That(attachedEntity, Is.Not.EqualTo(playerEnt)); + Assert.That(entMan.HasComponent(attachedEntity)); + var transform = entMan.GetComponent(attachedEntity.Value); + Assert.That(transform.MapID, Is.Not.EqualTo(MapId.Nullspace)); + Assert.That(transform.MapID, Is.Not.EqualTo(testMap.MapId)); #pragma warning restore NUnit2045 }); diff --git a/Content.Server/GameTicking/GameTicker.GamePreset.cs b/Content.Server/GameTicking/GameTicker.GamePreset.cs index a1946d34a0..fffacb59de 100644 --- a/Content.Server/GameTicking/GameTicker.GamePreset.cs +++ b/Content.Server/GameTicking/GameTicker.GamePreset.cs @@ -274,35 +274,13 @@ namespace Content.Server.GameTicking } } - var xformQuery = GetEntityQuery(); - var coords = _transform.GetMoverCoordinates(position, xformQuery); - - var ghost = Spawn(ObserverPrototypeName, coords); - - // Try setting the ghost entity name to either the character name or the player name. - // If all else fails, it'll default to the default entity prototype name, "observer". - // However, that should rarely happen. - if (!string.IsNullOrWhiteSpace(mind.CharacterName)) - _metaData.SetEntityName(ghost, mind.CharacterName); - else if (!string.IsNullOrWhiteSpace(mind.Session?.Name)) - _metaData.SetEntityName(ghost, mind.Session.Name); - - var ghostComponent = Comp(ghost); - - if (mind.TimeOfDeath.HasValue) - { - _ghost.SetTimeOfDeath(ghost, mind.TimeOfDeath!.Value, ghostComponent); - } + var ghost = _ghost.SpawnGhost((mindId, mind), position, canReturn); + if (ghost == null) + return false; if (playerEntity != null) _adminLogger.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} ghosted{(!canReturn ? " (non-returnable)" : "")}"); - _ghost.SetCanReturnToBody(ghostComponent, canReturn); - - if (canReturn) - _mind.Visit(mindId, ghost, mind); - else - _mind.TransferTo(mindId, ghost, mind: mind); return true; } diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 869b8b92f7..7478560379 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -8,6 +8,7 @@ using Content.Server.Speech.Components; using Content.Server.Station.Components; using Content.Shared.CCVar; using Content.Shared.Database; +using Content.Shared.Mind; using Content.Shared.Players; using Content.Shared.Preferences; using Content.Shared.Roles; @@ -96,8 +97,7 @@ namespace Content.Server.GameTicking if (job == null) { var playerSession = _playerManager.GetSessionById(netUser); - _chatManager.DispatchServerMessage(playerSession, - Loc.GetString("job-not-available-wait-in-lobby")); + _chatManager.DispatchServerMessage(playerSession, Loc.GetString("job-not-available-wait-in-lobby")); } else { @@ -315,10 +315,7 @@ namespace Content.Server.GameTicking /// The station they're spawning on /// An optional job for them to spawn as /// Whether or not the player should be greeted upon joining - public void MakeJoinGame(ICommonSession player, - EntityUid station, - string? jobId = null, - bool silent = false) + public void MakeJoinGame(ICommonSession player, EntityUid station, string? jobId = null, bool silent = false) { if (!_playerGameStatuses.ContainsKey(player.UserId)) return; @@ -351,42 +348,29 @@ namespace Content.Server.GameTicking if (DummyTicker) return; - var mind = player.GetMind(); + Entity? mind = player.GetMind(); if (mind == null) { - mind = _mind.CreateMind(player.UserId); + var name = GetPlayerProfile(player).Name; + var (mindId, mindComp) = _mind.CreateMind(player.UserId, name); + mind = (mindId, mindComp); _mind.SetUserId(mind.Value, player.UserId); _roles.MindAddRole(mind.Value, new ObserverRoleComponent()); } - var name = GetPlayerProfile(player).Name; - var ghost = SpawnObserverMob(); - _metaData.SetEntityName(ghost, name); - _ghost.SetCanReturnToBody(ghost, false); - _mind.TransferTo(mind.Value, ghost); + var ghost = _ghost.SpawnGhost(mind.Value); _adminLogger.Add(LogType.LateJoin, LogImpact.Low, $"{player.Name} late joined the round as an Observer with {ToPrettyString(ghost):entity}."); } - #region Mob Spawning Helpers - - private EntityUid SpawnObserverMob() - { - var coordinates = GetObserverSpawnPoint(); - return EntityManager.SpawnEntity(ObserverPrototypeName, coordinates); - } - - #endregion - #region Spawn Points public EntityCoordinates GetObserverSpawnPoint() { _possiblePositions.Clear(); - foreach (var (point, transform) in EntityManager - .EntityQuery(true)) + foreach (var (point, transform) in EntityManager.EntityQuery(true)) { if (point.SpawnType != SpawnPointType.Observer) continue; @@ -402,7 +386,7 @@ namespace Content.Server.GameTicking var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var grid)) { - if (!metaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused) + if (!metaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused || TerminatingOrDeleted(uid)) { continue; } @@ -439,7 +423,9 @@ namespace Content.Server.GameTicking { var mapUid = _mapManager.GetMapEntityId(map); - if (!metaQuery.TryGetComponent(mapUid, out var meta) || meta.EntityPaused) + if (!metaQuery.TryGetComponent(mapUid, out var meta) + || meta.EntityPaused + || TerminatingOrDeleted(mapUid)) { continue; } diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 0be93d2054..ac519b4c2e 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -19,6 +19,7 @@ using Content.Shared.Movement.Systems; using Content.Shared.Storage.Components; using Robust.Server.GameObjects; using Robust.Server.Player; +using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; using Robust.Shared.Player; @@ -42,6 +43,8 @@ namespace Content.Server.Ghost [Dependency] private readonly GameTicker _ticker = default!; [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly IMapManager _mapManager = default!; private EntityQuery _ghostQuery; private EntityQuery _physicsQuery; @@ -389,5 +392,59 @@ namespace Content.Server.Ghost return ghostBoo.Handled; } + + public EntityUid? SpawnGhost(Entity mind, EntityUid targetEntity, + bool canReturn = false) + { + _transformSystem.TryGetMapOrGridCoordinates(targetEntity, out var spawnPosition); + return SpawnGhost(mind, spawnPosition, canReturn); + } + + public EntityUid? SpawnGhost(Entity mind, EntityCoordinates? spawnPosition = null, + bool canReturn = false) + { + if (!Resolve(mind, ref mind.Comp)) + return null; + + // Test if the map is being deleted + var mapUid = spawnPosition?.GetMapUid(EntityManager); + if (mapUid == null || TerminatingOrDeleted(mapUid.Value)) + spawnPosition = null; + + spawnPosition ??= _ticker.GetObserverSpawnPoint(); + + if (!spawnPosition.Value.IsValid(EntityManager)) + { + Log.Warning($"No spawn valid ghost spawn position found for {mind.Comp.CharacterName}" + + " \"{ToPrettyString(mind)}\""); + _minds.TransferTo(mind.Owner, null, createGhost: false, mind: mind.Comp); + return null; + } + + var ghost = SpawnAtPosition(GameTicker.ObserverPrototypeName, spawnPosition.Value); + var ghostComponent = Comp(ghost); + + // Try setting the ghost entity name to either the character name or the player name. + // If all else fails, it'll default to the default entity prototype name, "observer". + // However, that should rarely happen. + if (!string.IsNullOrWhiteSpace(mind.Comp.CharacterName)) + _metaData.SetEntityName(ghost, mind.Comp.CharacterName); + else if (!string.IsNullOrWhiteSpace(mind.Comp.Session?.Name)) + _metaData.SetEntityName(ghost, mind.Comp.Session.Name); + + if (mind.Comp.TimeOfDeath.HasValue) + { + SetTimeOfDeath(ghost, mind.Comp.TimeOfDeath!.Value, ghostComponent); + } + + SetCanReturnToBody(ghostComponent, canReturn); + + if (canReturn) + _minds.Visit(mind.Owner, ghost, mind.Comp); + else + _minds.TransferTo(mind.Owner, ghost, mind: mind.Comp); + Log.Debug($"Spawned ghost \"{ToPrettyString(ghost)}\" for {mind.Comp.CharacterName}."); + return ghost; + } } } diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs index dc12836d90..4271d76b44 100644 --- a/Content.Server/Mind/MindSystem.cs +++ b/Content.Server/Mind/MindSystem.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Server.Administration.Logs; using Content.Server.GameTicking; +using Content.Server.Ghost; using Content.Server.Mind.Commands; using Content.Shared.Database; using Content.Shared.Ghost; @@ -9,10 +10,8 @@ using Content.Shared.Mind.Components; using Content.Shared.Players; using Robust.Server.GameStates; using Robust.Server.Player; -using Robust.Shared.Map.Components; using Robust.Shared.Network; using Robust.Shared.Player; -using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Mind; @@ -22,8 +21,7 @@ public sealed class MindSystem : SharedMindSystem [Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IPlayerManager _players = default!; - [Dependency] private readonly MetaDataSystem _metaData = default!; - [Dependency] private readonly SharedGhostSystem _ghosts = default!; + [Dependency] private readonly GhostSystem _ghosts = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly PvsOverrideSystem _pvsOverride = default!; @@ -63,8 +61,8 @@ public sealed class MindSystem : SharedMindSystem && !Terminating(visiting)) { TransferTo(mindId, visiting, mind: mind); - if (TryComp(visiting, out GhostComponent? ghost)) - _ghosts.SetCanReturnToBody(ghost, false); + if (TryComp(visiting, out GhostComponent? ghostComp)) + _ghosts.SetCanReturnToBody(ghostComp, false); return; } @@ -74,40 +72,13 @@ public sealed class MindSystem : SharedMindSystem if (!component.GhostOnShutdown || mind.Session == null || _gameTicker.RunLevel == GameRunLevel.PreRoundLobby) return; - var xform = Transform(uid); - var gridId = xform.GridUid; - var spawnPosition = Transform(uid).Coordinates; - - // Use a regular timer here because the entity has probably been deleted. - Timer.Spawn(0, () => - { - // Make extra sure the round didn't end between spawning the timer and it being executed. - if (_gameTicker.RunLevel == GameRunLevel.PreRoundLobby) - return; - - // Async this so that we don't throw if the grid we're on is being deleted. - if (!HasComp(gridId)) - spawnPosition = _gameTicker.GetObserverSpawnPoint(); - - // TODO refactor observer spawning. - // please. - if (!spawnPosition.IsValid(EntityManager)) - { - // This should be an error, if it didn't cause tests to start erroring when they delete a player. - Log.Warning($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, and no applicable spawn location is available."); - TransferTo(mindId, null, createGhost: false, mind: mind); - return; - } - - var ghost = Spawn(GameTicker.ObserverPrototypeName, spawnPosition); - var ghostComponent = Comp(ghost); - _ghosts.SetCanReturnToBody(ghostComponent, false); - + var ghost = _ghosts.SpawnGhost((mindId, mind), uid); + if (ghost != null) // Log these to make sure they're not causing the GameTicker round restart bugs... Log.Debug($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, spawned \"{ToPrettyString(ghost)}\"."); - _metaData.SetEntityName(ghost, mind.CharacterName ?? string.Empty); - TransferTo(mindId, ghost, mind: mind); - }); + else + // This should be an error, if it didn't cause tests to start erroring when they delete a player. + Log.Warning($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, and no applicable spawn location is available."); } public override bool TryGetMind(NetUserId user, [NotNullWhen(true)] out EntityUid? mindId, [NotNullWhen(true)] out MindComponent? mind) From 4e26be8617d309c49e7127892d0f0cdc56a468b0 Mon Sep 17 00:00:00 2001 From: icekot8 <93311212+icekot8@users.noreply.github.com> Date: Sat, 11 May 2024 18:04:17 +0300 Subject: [PATCH 20/68] revenant can no longer harvest souls while in solid objects (#27612) meow --- .../Revenant/EntitySystems/RevenantSystem.Abilities.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index 68a2624500..4106630d52 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -15,6 +15,7 @@ using Content.Shared.Bed.Sleep; using System.Linq; using System.Numerics; using Content.Server.Revenant.Components; +using Content.Shared.Physics; using Content.Shared.DoAfter; using Content.Shared.Emag.Systems; using Content.Shared.FixedPoint; @@ -135,6 +136,12 @@ public sealed partial class RevenantSystem return; } + if(_physics.GetEntitiesIntersectingBody(uid, (int) CollisionGroup.Impassable).Count > 0) + { + _popup.PopupEntity(Loc.GetString("revenant-in-solid"), uid, uid); + return; + } + var doAfter = new DoAfterArgs(EntityManager, uid, revenant.HarvestDebuffs.X, new HarvestEvent(), uid, target: target) { DistanceThreshold = 2, From 1698e1cfab6ab1bdb7de00ba9e1c6f1bc8f4563f Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 May 2024 15:04:48 +0000 Subject: [PATCH 21/68] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 24d0e6436f..cc784ecdcf 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Dutch-VanDerLinde - changes: - - message: Monkeys and kobolds can now wear blood-red hardsuits. - type: Add - id: 6084 - time: '2024-03-04T03:56:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/22273 - author: Plykiya changes: - message: Syringes now prevent injecting on empty and drawing on full. @@ -3865,3 +3858,10 @@ id: 6583 time: '2024-05-11T15:01:29.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27651 +- author: ShadowCommander + changes: + - message: Fixed screen freeze in some cases when getting gibbed or butchered. + type: Fix + id: 6584 + time: '2024-05-11T15:03:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27617 From 8b88fe4bb7a75856f724aadfa0dd2482ff2454cc Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 11 May 2024 08:42:11 -0700 Subject: [PATCH 22/68] Make projectiles not hit crit mobs unless clicked on (#27905) --- .../Weapons/Ranged/Systems/GunSystem.cs | 8 ++++++++ .../Weapons/Ranged/Systems/GunSystem.cs | 7 +++++++ .../Systems/MobStateSystem.Subscribers.cs | 20 +++++++++++++++++++ .../Weapons/Ranged/Components/GunComponent.cs | 8 ++++++-- .../Components/TargetedProjectileComponent.cs | 12 +++++++++++ .../Ranged/Events/RequestShootEvent.cs | 1 + .../Weapons/Ranged/Systems/SharedGunSystem.cs | 2 ++ 7 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index 5aba04bdf8..4a7711032e 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Client.Animations; +using Content.Client.Gameplay; using Content.Client.Items; using Content.Client.Weapons.Ranged.Components; using Content.Shared.Camera; @@ -13,6 +14,7 @@ using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Client.Player; +using Robust.Client.State; using Robust.Shared.Animations; using Robust.Shared.Input; using Robust.Shared.Map; @@ -30,6 +32,7 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly IEyeManager _eyeManager = default!; [Dependency] private readonly IInputManager _inputManager = default!; [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IStateManager _state = default!; [Dependency] private readonly AnimationPlayerSystem _animPlayer = default!; [Dependency] private readonly InputSystem _inputSystem = default!; [Dependency] private readonly SharedCameraRecoilSystem _recoil = default!; @@ -174,10 +177,15 @@ public sealed partial class GunSystem : SharedGunSystem // Define target coordinates relative to gun entity, so that network latency on moving grids doesn't fuck up the target location. var coordinates = EntityCoordinates.FromMap(entity, mousePos, TransformSystem, EntityManager); + NetEntity? target = null; + if (_state.CurrentState is GameplayStateBase screen) + target = GetNetEntity(screen.GetClickedEntity(mousePos)); + Log.Debug($"Sending shoot request tick {Timing.CurTick} / {Timing.CurTime}"); EntityManager.RaisePredictiveEvent(new RequestShootEvent { + Target = target, Coordinates = GetNetCoordinates(coordinates), Gun = GetNetEntity(gunUid), }); diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index f495f29e4a..986cac98dd 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -278,6 +278,13 @@ public sealed partial class GunSystem : SharedGunSystem private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, GunComponent gun, EntityUid gunUid, EntityUid? user) { + if (gun.Target is { } target && !TerminatingOrDeleted(target)) + { + var targeted = EnsureComp(uid); + targeted.Target = target; + Dirty(uid, targeted); + } + // Do a throw if (!HasComp(uid)) { diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs index 0c2fcc0579..ee747554e1 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -10,12 +10,15 @@ using Content.Shared.Item; using Content.Shared.Mobs.Components; using Content.Shared.Movement.Events; using Content.Shared.Pointing; +using Content.Shared.Projectiles; using Content.Shared.Pulling.Events; using Content.Shared.Speech; using Content.Shared.Standing; using Content.Shared.Strip.Components; using Content.Shared.Throwing; +using Content.Shared.Weapons.Ranged.Components; using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Events; namespace Content.Shared.Mobs.Systems; @@ -43,6 +46,7 @@ public partial class MobStateSystem SubscribeLocalEvent(OnSleepAttempt); SubscribeLocalEvent(OnCombatModeShouldHandInteract); SubscribeLocalEvent(OnAttemptPacifiedAttack); + SubscribeLocalEvent(OnPreventCollide); } private void OnStateExitSubscribers(EntityUid target, MobStateComponent component, MobState state) @@ -175,5 +179,21 @@ public partial class MobStateSystem args.Cancelled = true; } + private void OnPreventCollide(Entity ent, ref PreventCollideEvent args) + { + if (args.Cancelled) + return; + + if (IsAlive(ent, ent)) + return; + + var other = args.OtherEntity; + if (HasComp(other) && + CompOrNull(other)?.Target != ent.Owner) + { + args.Cancelled = true; + } + } + #endregion } diff --git a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs index 5a335e7e5c..b404221abf 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs @@ -1,5 +1,3 @@ -using Content.Shared.Damage; -using Content.Shared.Tag; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Audio; @@ -139,6 +137,12 @@ public sealed partial class GunComponent : Component [ViewVariables] public EntityCoordinates? ShootCoordinates = null; + /// + /// Who the gun is being requested to shoot at directly. + /// + [ViewVariables] + public EntityUid? Target = null; + /// /// The base value for how many shots to fire per burst. /// diff --git a/Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs b/Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs new file mode 100644 index 0000000000..b804176497 --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs @@ -0,0 +1,12 @@ +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Ranged.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedGunSystem))] +public sealed partial class TargetedProjectileComponent : Component +{ + [DataField, AutoNetworkedField] + public EntityUid Target; +} diff --git a/Content.Shared/Weapons/Ranged/Events/RequestShootEvent.cs b/Content.Shared/Weapons/Ranged/Events/RequestShootEvent.cs index 21e90b2108..f5c4dd72b4 100644 --- a/Content.Shared/Weapons/Ranged/Events/RequestShootEvent.cs +++ b/Content.Shared/Weapons/Ranged/Events/RequestShootEvent.cs @@ -11,4 +11,5 @@ public sealed class RequestShootEvent : EntityEventArgs { public NetEntity Gun; public NetCoordinates Coordinates; + public NetEntity? Target; } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 51e2e1358f..4e51ca2b62 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -136,6 +136,7 @@ public abstract partial class SharedGunSystem : EntitySystem return; gun.ShootCoordinates = GetCoordinates(msg.Coordinates); + gun.Target = GetEntity(msg.Target); AttemptShoot(user.Value, ent, gun); } @@ -196,6 +197,7 @@ public abstract partial class SharedGunSystem : EntitySystem gun.ShotCounter = 0; gun.ShootCoordinates = null; + gun.Target = null; Dirty(uid, gun); } From 73c142064830f1028c42ac619296d092a6603b85 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 May 2024 15:43:17 +0000 Subject: [PATCH 23/68] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index cc784ecdcf..ae8a6fe600 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: Plykiya - changes: - - message: Syringes now prevent injecting on empty and drawing on full. - type: Tweak - - message: You can now tell if you are being drawn from or injected by a syringe. - type: Tweak - id: 6085 - time: '2024-03-04T05:07:11.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25480 - author: metalgearsloth changes: - message: Fix expedition FTL. @@ -3865,3 +3856,11 @@ id: 6584 time: '2024-05-11T15:03:41.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27617 +- author: DrSmugleaf + changes: + - message: Bullets no longer hit mobs that are in crit, unless they are directly + clicked on while shooting. + type: Tweak + id: 6585 + time: '2024-05-11T15:42:11.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27905 From 999d0441390e073018b6cf02e7338374efdd9f48 Mon Sep 17 00:00:00 2001 From: keronshb <54602815+keronshb@users.noreply.github.com> Date: Sat, 11 May 2024 19:06:49 -0400 Subject: [PATCH 24/68] Magic Refactor + Wizard Grimoire (#22568) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Brings over changes from the original magic refactor PR * Adds Master Spellbook, spellbook categories, WizCoin currency, and locale * Wiz€oin™ * Adds currency whitelist to Spellbook preset, grants contained actions on action added. * Adds grant contained action and remove provided action. * adds a way for actions to be upgraded to the store * Adds Fireball 3 and fixes action upgrade logic so that it checks if the action can level or if the action can upgrade separately * Fixes upgrade logic in ActionUpgradeSystem to allow for level ups without an actual upgrade. Fixed action upgrade logic in store system as well * Removes current action entity from the bought entities list and adds new or old action entity * Removes Current Entity * Removes old comments, fixes TransferAllActionsWithNewAttached * Removes TODO * Removes Product Action Upgrade Event * reverts changes to immovablerodrule * Removes stale event reference * fixes mind action grant logic * reverts shared gun system change to projectile anomaly system * forgor to remove the using * Reverts unintended changes to action container * Adds refund button to the store * Refreshes store back to origin. * Refund with correct currency * Init refund * Check for terminating and update interface * Disables refund button * Removes preset allow refund * dont refund if map changed * adds refunds to stores * Adds method to check for starting map * comments, datafields, some requested changes * turns event into ref event * Adds datafields * Switches to entity terminating event * Changes store entity to be nullable and checks if store is terminating to remove reference. * Tryadd instead of containskey * Adds a refund disable method, disables refund on bought ent container changes if not an action * Removes duplicate refundcomp * Removes unintended merges * Removed another unintended change from merge * removes extra using statement * readds using statement * might as well just remove both usings since it won't leave the PR * Fixes Action upgrades from stores * Changes to non obsolete method uses * Shares spawn code between instant and world * Adds action entity to action event, adds beforecastspellevent, adds spell requirements to magic component * puts prereq check in spell methods, sets up template code for before cast event * checks for required wizard clothes * Networks Magic Comp and Wizard Clothes Comp. Renames MagicSpawnData to MagicInstantSpawnData. * Removes posdata from projectiles * Speech > RequiresSpeech * Fixes ActionOnInteract * checks for muted * popup for missing reqs * Validate click loc for blink spell * Checks if doors are in range and not obstructed before opening * Check ents by map coords * Adds speak event * Comments spellbooks * Removes comments * Unobsoletes smite spell * Invert if * Requirements loc * Fixes spell reqs * Inverts an if * Comment updates * Starts doafter work * Removes doafter references * Balances fireball upgrades to be more reasonable * Enables refund on master spellbooks * Spells to do * update spellbook doafter * knock toggles bolts * Touch Spell comments * Comments for pending spells * more comments * adds spider polymorph to spellbook * TODOs for spells * reorganizes spellbook categories and adds wands * fixes spacing and adds limited conditions * commented owner only for future store PR * reenables owner only for the grimoire * fixes grimoire sprite * Adds wizard rod polymorph * summon ghosts event * Moves rod form to offensive category * Adds charge spell and loc for rod polymorph * Oops forgor the actual chages * Item Recall comment * Fixes UI * removes extra field for wizard rod * Cleanup * New Condition (INCOMPLETE) * Fix linter * Fix linter (for real) * fixed some descriptions * adds regions to magic * Adds a non-refund wizard grimoire, fixes blink to deselect after teleporting, reduces force wall despawn time to 12 seconds * removes limited upgrade condition --------- Co-authored-by: AJCM --- Content.Client/Magic/MagicSystem.cs | 5 + .../Actions/ActionOnInteractSystem.cs | 25 +- Content.Server/Ghost/GhostSystem.cs | 10 + Content.Server/Magic/MagicSystem.cs | 393 +------------ .../Store/Systems/StoreSystem.Refund.cs | 2 +- .../Store/Systems/StoreSystem.Ui.cs | 6 +- Content.Shared/Actions/ActionEvents.cs | 2 +- Content.Shared/Actions/SharedActionsSystem.cs | 7 +- Content.Shared/Ghost/GhostComponent.cs | 2 + .../Magic/Components/MagicComponent.cs | 39 ++ .../Magic/Components/SpellbookComponent.cs | 17 +- .../Components/WizardClothesComponent.cs | 10 + .../Magic/Events/BeforeCastSpellEvent.cs | 12 + .../Magic/Events/ChargeSpellEvent.cs | 18 + .../Magic/Events/InstantSpawnSpellEvent.cs | 12 +- .../Magic/Events/KnockSpellEvent.cs | 19 +- .../Magic/Events/ProjectileSpellEvent.cs | 12 +- .../Magic/Events/SmiteSpellEvent.cs | 7 +- .../Magic/Events/SpeakSpellEvent.cs | 8 + .../Magic/Events/TeleportSpellEvent.cs | 12 +- .../Magic/Events/WorldSpawnSpellEvent.cs | 16 +- Content.Shared/Magic/MagicInstantSpawnData.cs | 25 + Content.Shared/Magic/MagicSpawnData.cs | 20 - Content.Shared/Magic/SharedMagicSystem.cs | 519 ++++++++++++++++++ Content.Shared/Magic/SpellbookSystem.cs | 96 ++++ Content.Shared/Store/ListingPrototype.cs | 10 +- Resources/Locale/en-US/magic/magic.ftl | 1 + Resources/Locale/en-US/store/categories.ftl | 8 + Resources/Locale/en-US/store/currency.ftl | 1 + .../Locale/en-US/store/spellbook-catalog.ftl | 35 ++ Resources/Prototypes/Actions/polymorph.yml | 30 + .../Prototypes/Catalog/spellbook_catalog.yml | 140 +++++ .../Entities/Clothing/Head/hats.yml | 12 +- .../Entities/Clothing/OuterClothing/misc.yml | 13 +- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 12 + .../Entities/Objects/Magic/books.yml | 61 +- .../Entities/Structures/Walls/walls.yml | 18 +- Resources/Prototypes/Magic/event_spells.yml | 13 + Resources/Prototypes/Magic/knock_spell.yml | 2 + .../Prototypes/Magic/projectile_spells.yml | 34 +- .../Prototypes/Magic/teleport_spells.yml | 4 +- Resources/Prototypes/Magic/utility_spells.yml | 15 + Resources/Prototypes/Polymorphs/polymorph.yml | 22 + Resources/Prototypes/Store/categories.yml | 26 + Resources/Prototypes/Store/currency.yml | 9 +- Resources/Prototypes/Store/presets.yml | 12 + 46 files changed, 1262 insertions(+), 510 deletions(-) create mode 100644 Content.Client/Magic/MagicSystem.cs create mode 100644 Content.Shared/Magic/Components/MagicComponent.cs rename {Content.Server => Content.Shared}/Magic/Components/SpellbookComponent.cs (60%) create mode 100644 Content.Shared/Magic/Components/WizardClothesComponent.cs create mode 100644 Content.Shared/Magic/Events/BeforeCastSpellEvent.cs create mode 100644 Content.Shared/Magic/Events/ChargeSpellEvent.cs create mode 100644 Content.Shared/Magic/Events/SpeakSpellEvent.cs create mode 100644 Content.Shared/Magic/MagicInstantSpawnData.cs delete mode 100644 Content.Shared/Magic/MagicSpawnData.cs create mode 100644 Content.Shared/Magic/SharedMagicSystem.cs create mode 100644 Content.Shared/Magic/SpellbookSystem.cs create mode 100644 Resources/Locale/en-US/magic/magic.ftl create mode 100644 Resources/Locale/en-US/store/spellbook-catalog.ftl create mode 100644 Resources/Prototypes/Catalog/spellbook_catalog.yml create mode 100644 Resources/Prototypes/Magic/event_spells.yml create mode 100644 Resources/Prototypes/Magic/utility_spells.yml diff --git a/Content.Client/Magic/MagicSystem.cs b/Content.Client/Magic/MagicSystem.cs new file mode 100644 index 0000000000..03aa9eb56d --- /dev/null +++ b/Content.Client/Magic/MagicSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Magic; + +namespace Content.Client.Magic; + +public sealed class MagicSystem : SharedMagicSystem; diff --git a/Content.Server/Actions/ActionOnInteractSystem.cs b/Content.Server/Actions/ActionOnInteractSystem.cs index 657ab46d60..b6eec0ce0f 100644 --- a/Content.Server/Actions/ActionOnInteractSystem.cs +++ b/Content.Server/Actions/ActionOnInteractSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Shared.Actions; using Content.Shared.Interaction; using Robust.Shared.Random; @@ -38,10 +39,18 @@ public sealed class ActionOnInteractSystem : EntitySystem private void OnActivate(EntityUid uid, ActionOnInteractComponent component, ActivateInWorldEvent args) { - if (args.Handled || component.ActionEntities == null) + if (args.Handled) return; - var options = GetValidActions(component.ActionEntities); + if (component.ActionEntities is not {} actionEnts) + { + if (!TryComp(uid, out var actionsContainerComponent)) + return; + + actionEnts = actionsContainerComponent.Container.ContainedEntities.ToList(); + } + + var options = GetValidActions(actionEnts); if (options.Count == 0) return; @@ -58,13 +67,21 @@ public sealed class ActionOnInteractSystem : EntitySystem private void OnAfterInteract(EntityUid uid, ActionOnInteractComponent component, AfterInteractEvent args) { - if (args.Handled || component.ActionEntities == null) + if (args.Handled) return; + if (component.ActionEntities is not {} actionEnts) + { + if (!TryComp(uid, out var actionsContainerComponent)) + return; + + actionEnts = actionsContainerComponent.Container.ContainedEntities.ToList(); + } + // First, try entity target actions if (args.Target != null) { - var entOptions = GetValidActions(component.ActionEntities, args.CanReach); + var entOptions = GetValidActions(actionEnts, args.CanReach); for (var i = entOptions.Count - 1; i >= 0; i--) { var action = entOptions[i]; diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index ac519b4c2e..b1fb67cce7 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -78,6 +78,7 @@ namespace Content.Server.Ghost SubscribeLocalEvent(OnEntityStorageInsertAttempt); SubscribeLocalEvent(_ => MakeVisible(true)); + SubscribeLocalEvent(OnToggleGhostVisibilityToAll); } private void OnGhostHearingAction(EntityUid uid, GhostComponent component, ToggleGhostHearingActionEvent args) @@ -363,6 +364,15 @@ namespace Content.Server.Ghost args.Cancelled = true; } + private void OnToggleGhostVisibilityToAll(ToggleGhostVisibilityToAllEvent ev) + { + if (ev.Handled) + return; + + ev.Handled = true; + MakeVisible(true); + } + /// /// When the round ends, make all players able to see ghosts. /// diff --git a/Content.Server/Magic/MagicSystem.cs b/Content.Server/Magic/MagicSystem.cs index f7250c01ba..2cf5136b42 100644 --- a/Content.Server/Magic/MagicSystem.cs +++ b/Content.Server/Magic/MagicSystem.cs @@ -1,407 +1,22 @@ -using System.Numerics; -using Content.Server.Body.Components; -using Content.Server.Body.Systems; using Content.Server.Chat.Systems; -using Content.Server.Doors.Systems; -using Content.Server.Magic.Components; -using Content.Server.Weapons.Ranged.Systems; -using Content.Shared.Actions; -using Content.Shared.Body.Components; -using Content.Shared.Coordinates.Helpers; -using Content.Shared.DoAfter; -using Content.Shared.Doors.Components; -using Content.Shared.Doors.Systems; -using Content.Shared.Interaction.Events; using Content.Shared.Magic; using Content.Shared.Magic.Events; -using Content.Shared.Maps; -using Content.Shared.Physics; -using Content.Shared.Storage; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; -using Robust.Shared.Serialization.Manager; -using Robust.Shared.Spawners; namespace Content.Server.Magic; -/// -/// Handles learning and using spells (actions) -/// -public sealed class MagicSystem : EntitySystem +public sealed class MagicSystem : SharedMagicSystem { - [Dependency] private readonly ISerializationManager _seriMan = default!; - [Dependency] private readonly IComponentFactory _compFact = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly BodySystem _bodySystem = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly SharedDoorSystem _doorSystem = default!; - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly GunSystem _gunSystem = default!; - [Dependency] private readonly PhysicsSystem _physics = default!; - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly ActionContainerSystem _actionContainer = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnUse); - SubscribeLocalEvent(OnDoAfter); - - SubscribeLocalEvent(OnInstantSpawn); - SubscribeLocalEvent(OnTeleportSpell); - SubscribeLocalEvent(OnKnockSpell); - SubscribeLocalEvent(OnSmiteSpell); - SubscribeLocalEvent(OnWorldSpawn); - SubscribeLocalEvent(OnProjectileSpell); - SubscribeLocalEvent(OnChangeComponentsSpell); + SubscribeLocalEvent(OnSpellSpoken); } - private void OnDoAfter(EntityUid uid, SpellbookComponent component, DoAfterEvent args) + private void OnSpellSpoken(ref SpeakSpellEvent args) { - if (args.Handled || args.Cancelled) - return; - - args.Handled = true; - if (!component.LearnPermanently) - { - _actionsSystem.GrantActions(args.Args.User, component.Spells, uid); - return; - } - - foreach (var (id, charges) in component.SpellActions) - { - // TOOD store spells entity ids on some sort of innate magic user component or something like that. - EntityUid? actionId = null; - if (_actionsSystem.AddAction(args.Args.User, ref actionId, id)) - _actionsSystem.SetCharges(actionId, charges < 0 ? null : charges); - } - - component.SpellActions.Clear(); - } - - private void OnInit(EntityUid uid, SpellbookComponent component, MapInitEvent args) - { - if (component.LearnPermanently) - return; - - foreach (var (id, charges) in component.SpellActions) - { - var spell = _actionContainer.AddAction(uid, id); - if (spell == null) - continue; - - _actionsSystem.SetCharges(spell, charges < 0 ? null : charges); - component.Spells.Add(spell.Value); - } - } - - private void OnUse(EntityUid uid, SpellbookComponent component, UseInHandEvent args) - { - 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, _transformSystem); - 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 (!TryComp(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); + _chat.TrySendInGameICMessage(args.Performer, Loc.GetString(args.Speech), InGameICChatType.Speak, false); } } diff --git a/Content.Server/Store/Systems/StoreSystem.Refund.cs b/Content.Server/Store/Systems/StoreSystem.Refund.cs index d59ee75e3e..5a8be4be2b 100644 --- a/Content.Server/Store/Systems/StoreSystem.Refund.cs +++ b/Content.Server/Store/Systems/StoreSystem.Refund.cs @@ -1,4 +1,4 @@ -using Content.Server.Store.Components; +using Content.Server.Store.Components; using Robust.Shared.Containers; namespace Content.Server.Store.Systems; diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index fa363c54c1..0a1a8d19f3 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -215,11 +215,11 @@ public sealed partial class StoreSystem { HandleRefundComp(uid, component, actionId.Value); - if (listing.ProductUpgradeID != null) + if (listing.ProductUpgradeId != null) { foreach (var upgradeListing in component.Listings) { - if (upgradeListing.ID == listing.ProductUpgradeID) + if (upgradeListing.ID == listing.ProductUpgradeId) { upgradeListing.ProductActionEntity = actionId.Value; break; @@ -229,7 +229,7 @@ public sealed partial class StoreSystem } } - if (listing is { ProductUpgradeID: not null, ProductActionEntity: not null }) + if (listing is { ProductUpgradeId: not null, ProductActionEntity: not null }) { if (listing.ProductActionEntity != null) { diff --git a/Content.Shared/Actions/ActionEvents.cs b/Content.Shared/Actions/ActionEvents.cs index c6002d0d4a..6cc50bc21b 100644 --- a/Content.Shared/Actions/ActionEvents.cs +++ b/Content.Shared/Actions/ActionEvents.cs @@ -157,7 +157,7 @@ public abstract partial class BaseActionEvent : HandledEntityEventArgs public EntityUid Performer; /// - /// The action that was performed. + /// The action the event belongs to. /// public EntityUid Action; } diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 315d2725b2..30687c9322 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -569,13 +569,12 @@ public abstract class SharedActionsSystem : EntitySystem handled = actionEvent.Handled; } - _audio.PlayPredicted(action.Sound, performer,predicted ? performer : null); - handled |= action.Sound != null; - if (!handled) return; // no interaction occurred. - // reduce charges, start cooldown, and mark as dirty (if required). + // play sound, reduce charges, start cooldown, and mark as dirty (if required). + + _audio.PlayPredicted(action.Sound, performer,predicted ? performer : null); var dirty = toggledBefore == action.Toggled; diff --git a/Content.Shared/Ghost/GhostComponent.cs b/Content.Shared/Ghost/GhostComponent.cs index f7717e8d23..96e9b717b9 100644 --- a/Content.Shared/Ghost/GhostComponent.cs +++ b/Content.Shared/Ghost/GhostComponent.cs @@ -101,4 +101,6 @@ public sealed partial class ToggleLightingActionEvent : InstantActionEvent { } public sealed partial class ToggleGhostHearingActionEvent : InstantActionEvent { } +public sealed partial class ToggleGhostVisibilityToAllEvent : InstantActionEvent { } + public sealed partial class BooActionEvent : InstantActionEvent { } diff --git a/Content.Shared/Magic/Components/MagicComponent.cs b/Content.Shared/Magic/Components/MagicComponent.cs new file mode 100644 index 0000000000..bcc11063b7 --- /dev/null +++ b/Content.Shared/Magic/Components/MagicComponent.cs @@ -0,0 +1,39 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Magic.Components; + +// TODO: Rename to MagicActionComponent or MagicRequirementsComponent +[RegisterComponent, NetworkedComponent, Access(typeof(SharedMagicSystem))] +public sealed partial class MagicComponent : Component +{ + // TODO: Split into different components? + // This could be the MagicRequirementsComp - which just is requirements for the spell + // Magic comp could be on the actual entities itself + // Could handle lifetime, ignore caster, etc? + // Magic caster comp would be on the caster, used for what I'm not sure + + // TODO: Do After here or in actions + + // TODO: Spell requirements + // A list of requirements to cast the spell + // Hands + // Any item in hand + // Spell takes up an inhand slot + // May be an action toggle or something + + // TODO: List requirements in action desc + /// + /// Does this spell require Wizard Robes & Hat? + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool RequiresClothes; + + /// + /// Does this spell require the user to speak? + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool RequiresSpeech; + + // TODO: FreeHand - should check if toggleable action + // Check which hand is free to toggle action in +} diff --git a/Content.Server/Magic/Components/SpellbookComponent.cs b/Content.Shared/Magic/Components/SpellbookComponent.cs similarity index 60% rename from Content.Server/Magic/Components/SpellbookComponent.cs rename to Content.Shared/Magic/Components/SpellbookComponent.cs index ebc3c88043..f1b307c245 100644 --- a/Content.Server/Magic/Components/SpellbookComponent.cs +++ b/Content.Shared/Magic/Components/SpellbookComponent.cs @@ -1,12 +1,13 @@ using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -namespace Content.Server.Magic.Components; +namespace Content.Shared.Magic.Components; /// -/// Spellbooks for having an entity learn spells as long as they've read the book and it's in their hand. +/// Spellbooks can grant one or more spells to the user. If marked as it will teach +/// the performer the spells and wipe the book. +/// Default behavior requires the book to be held in hand /// -[RegisterComponent] +[RegisterComponent, Access(typeof(SpellbookSystem))] public sealed partial class SpellbookComponent : Component { /// @@ -18,18 +19,18 @@ public sealed partial class SpellbookComponent : Component /// /// The three fields below is just used for initialization. /// - [DataField("spells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + [DataField] [ViewVariables(VVAccess.ReadWrite)] - public Dictionary SpellActions = new(); + public Dictionary SpellActions = new(); - [DataField("learnTime")] + [DataField] [ViewVariables(VVAccess.ReadWrite)] public float LearnTime = .75f; /// /// If true, the spell action stays even after the book is removed /// - [DataField("learnPermanently")] + [DataField] [ViewVariables(VVAccess.ReadWrite)] public bool LearnPermanently; } diff --git a/Content.Shared/Magic/Components/WizardClothesComponent.cs b/Content.Shared/Magic/Components/WizardClothesComponent.cs new file mode 100644 index 0000000000..063cf56c33 --- /dev/null +++ b/Content.Shared/Magic/Components/WizardClothesComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Magic.Components; + +/// +/// The checks this if a spell requires wizard clothes +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedMagicSystem))] +public sealed partial class WizardClothesComponent : Component; diff --git a/Content.Shared/Magic/Events/BeforeCastSpellEvent.cs b/Content.Shared/Magic/Events/BeforeCastSpellEvent.cs new file mode 100644 index 0000000000..afb5c1f090 --- /dev/null +++ b/Content.Shared/Magic/Events/BeforeCastSpellEvent.cs @@ -0,0 +1,12 @@ +namespace Content.Shared.Magic.Events; + +[ByRefEvent] +public struct BeforeCastSpellEvent(EntityUid performer) +{ + /// + /// The Performer of the event, to check if they meet the requirements. + /// + public EntityUid Performer = performer; + + public bool Cancelled; +} diff --git a/Content.Shared/Magic/Events/ChargeSpellEvent.cs b/Content.Shared/Magic/Events/ChargeSpellEvent.cs new file mode 100644 index 0000000000..8898761ec2 --- /dev/null +++ b/Content.Shared/Magic/Events/ChargeSpellEvent.cs @@ -0,0 +1,18 @@ +using Content.Shared.Actions; + +namespace Content.Shared.Magic.Events; + +/// +/// Adds provided Charge to the held wand +/// +public sealed partial class ChargeSpellEvent : InstantActionEvent, ISpeakSpell +{ + [DataField(required: true)] + public int Charge; + + [DataField] + public string WandTag = "WizardWand"; + + [DataField] + public string? Speech { get; private set; } +} diff --git a/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs b/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs index ef8d689862..1405b15827 100644 --- a/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs +++ b/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs @@ -1,6 +1,5 @@ using Content.Shared.Actions; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Magic.Events; @@ -9,17 +8,18 @@ public sealed partial class InstantSpawnSpellEvent : InstantActionEvent, ISpeakS /// /// What entity should be spawned. /// - [DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Prototype = default!; + [DataField(required: true)] + public EntProtoId Prototype; - [DataField("preventCollide")] + [DataField] public bool PreventCollideWithCaster = true; - [DataField("speech")] + [DataField] public string? Speech { get; private set; } /// /// Gets the targeted spawn positons; may lead to multiple entities being spawned. /// - [DataField("posData")] public MagicSpawnData Pos = new TargetCasterPos(); + [DataField] + public MagicInstantSpawnData PosData = new TargetCasterPos(); } diff --git a/Content.Shared/Magic/Events/KnockSpellEvent.cs b/Content.Shared/Magic/Events/KnockSpellEvent.cs index a3b0be5575..24a1700d21 100644 --- a/Content.Shared/Magic/Events/KnockSpellEvent.cs +++ b/Content.Shared/Magic/Events/KnockSpellEvent.cs @@ -1,5 +1,4 @@ using Content.Shared.Actions; -using Robust.Shared.Audio; namespace Content.Shared.Magic.Events; @@ -7,20 +6,12 @@ public sealed partial class KnockSpellEvent : InstantActionEvent, ISpeakSpell { /// /// The range this spell opens doors in - /// 4f is the default + /// 10f is the default + /// Should be able to open all doors/lockers in visible sight /// - [DataField("range")] - public float Range = 4f; + [DataField] + public float Range = 10f; - [DataField("knockSound")] - public SoundSpecifier KnockSound = new SoundPathSpecifier("/Audio/Magic/knock.ogg"); - - /// - /// Volume control for the spell. - /// - [DataField("knockVolume")] - public float KnockVolume = 5f; - - [DataField("speech")] + [DataField] public string? Speech { get; private set; } } diff --git a/Content.Shared/Magic/Events/ProjectileSpellEvent.cs b/Content.Shared/Magic/Events/ProjectileSpellEvent.cs index 4496625769..336ea03346 100644 --- a/Content.Shared/Magic/Events/ProjectileSpellEvent.cs +++ b/Content.Shared/Magic/Events/ProjectileSpellEvent.cs @@ -1,6 +1,5 @@ using Content.Shared.Actions; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Magic.Events; @@ -9,14 +8,9 @@ public sealed partial class ProjectileSpellEvent : WorldTargetActionEvent, ISpea /// /// What entity should be spawned. /// - [DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Prototype = default!; + [DataField(required: true)] + public EntProtoId Prototype; - /// - /// Gets the targeted spawn positions; may lead to multiple entities being spawned. - /// - [DataField("posData")] public MagicSpawnData Pos = new TargetCasterPos(); - - [DataField("speech")] + [DataField] public string? Speech { get; private set; } } diff --git a/Content.Shared/Magic/Events/SmiteSpellEvent.cs b/Content.Shared/Magic/Events/SmiteSpellEvent.cs index 08ec63c05e..74ca116ad5 100644 --- a/Content.Shared/Magic/Events/SmiteSpellEvent.cs +++ b/Content.Shared/Magic/Events/SmiteSpellEvent.cs @@ -4,12 +4,13 @@ namespace Content.Shared.Magic.Events; public sealed partial class SmiteSpellEvent : EntityTargetActionEvent, ISpeakSpell { + // TODO: Make part of gib method /// - /// Should this smite delete all parts/mechanisms gibbed except for the brain? + /// Should this smite delete all parts/mechanisms gibbed except for the brain? /// - [DataField("deleteNonBrainParts")] + [DataField] public bool DeleteNonBrainParts = true; - [DataField("speech")] + [DataField] public string? Speech { get; private set; } } diff --git a/Content.Shared/Magic/Events/SpeakSpellEvent.cs b/Content.Shared/Magic/Events/SpeakSpellEvent.cs new file mode 100644 index 0000000000..1b3f7af63c --- /dev/null +++ b/Content.Shared/Magic/Events/SpeakSpellEvent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared.Magic.Events; + +[ByRefEvent] +public readonly struct SpeakSpellEvent(EntityUid performer, string speech) +{ + public readonly EntityUid Performer = performer; + public readonly string Speech = speech; +} diff --git a/Content.Shared/Magic/Events/TeleportSpellEvent.cs b/Content.Shared/Magic/Events/TeleportSpellEvent.cs index b24f6ec72f..525c1e5105 100644 --- a/Content.Shared/Magic/Events/TeleportSpellEvent.cs +++ b/Content.Shared/Magic/Events/TeleportSpellEvent.cs @@ -1,19 +1,19 @@ using Content.Shared.Actions; -using Robust.Shared.Audio; namespace Content.Shared.Magic.Events; +// TODO: Can probably just be an entity or something public sealed partial class TeleportSpellEvent : WorldTargetActionEvent, ISpeakSpell { - [DataField("blinkSound")] - public SoundSpecifier BlinkSound = new SoundPathSpecifier("/Audio/Magic/blink.ogg"); - - [DataField("speech")] + [DataField] public string? Speech { get; private set; } + // TODO: Move to magic component + // TODO: Maybe not since sound specifier is a thing + // Keep here to remind what the volume was set as /// /// Volume control for the spell. /// - [DataField("blinkVolume")] + [DataField] public float BlinkVolume = 5f; } diff --git a/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs b/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs index 4355cab842..2f50c67b3e 100644 --- a/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs +++ b/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs @@ -4,29 +4,31 @@ using Content.Shared.Storage; namespace Content.Shared.Magic.Events; +// TODO: This class needs combining with InstantSpawnSpellEvent + public sealed partial class WorldSpawnSpellEvent : WorldTargetActionEvent, ISpeakSpell { - // TODO:This class needs combining with InstantSpawnSpellEvent - /// /// The list of prototypes this spell will spawn /// - [DataField("prototypes")] - public List Contents = new(); + [DataField] + public List Prototypes = new(); // TODO: This offset is liable for deprecation. + // TODO: Target tile via code instead? /// /// The offset the prototypes will spawn in on relative to the one prior. /// Set to 0,0 to have them spawn on the same tile. /// - [DataField("offset")] + [DataField] public Vector2 Offset; /// /// Lifetime to set for the entities to self delete /// - [DataField("lifetime")] public float? Lifetime; + [DataField] + public float? Lifetime; - [DataField("speech")] + [DataField] public string? Speech { get; private set; } } diff --git a/Content.Shared/Magic/MagicInstantSpawnData.cs b/Content.Shared/Magic/MagicInstantSpawnData.cs new file mode 100644 index 0000000000..5dcc1453ed --- /dev/null +++ b/Content.Shared/Magic/MagicInstantSpawnData.cs @@ -0,0 +1,25 @@ +namespace Content.Shared.Magic; + +// TODO: If still needed, move to magic component +[ImplicitDataDefinitionForInheritors] +public abstract partial class MagicInstantSpawnData; + +/// +/// Spawns underneath caster. +/// +public sealed partial class TargetCasterPos : MagicInstantSpawnData; + +/// +/// Spawns 3 tiles wide in front of the caster. +/// +public sealed partial class TargetInFront : MagicInstantSpawnData +{ + [DataField] + public int Width = 3; +} + + +/// +/// Spawns 1 tile in front of caster +/// +public sealed partial class TargetInFrontSingle : MagicInstantSpawnData; diff --git a/Content.Shared/Magic/MagicSpawnData.cs b/Content.Shared/Magic/MagicSpawnData.cs deleted file mode 100644 index cd96d4ad76..0000000000 --- a/Content.Shared/Magic/MagicSpawnData.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Content.Shared.Magic; - -[ImplicitDataDefinitionForInheritors] -public abstract partial class MagicSpawnData -{ - -} - -/// -/// Spawns 1 at the caster's feet. -/// -public sealed partial class TargetCasterPos : MagicSpawnData {} - -/// -/// Targets the 3 tiles in front of the caster. -/// -public sealed partial class TargetInFront : MagicSpawnData -{ - [DataField("width")] public int Width = 3; -} diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs new file mode 100644 index 0000000000..cc7a297aa4 --- /dev/null +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -0,0 +1,519 @@ +using System.Numerics; +using Content.Shared.Actions; +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; +using Content.Shared.Coordinates.Helpers; +using Content.Shared.Doors.Components; +using Content.Shared.Doors.Systems; +using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Interaction; +using Content.Shared.Inventory; +using Content.Shared.Lock; +using Content.Shared.Magic.Components; +using Content.Shared.Magic.Events; +using Content.Shared.Maps; +using Content.Shared.Physics; +using Content.Shared.Popups; +using Content.Shared.Speech.Muting; +using Content.Shared.Storage; +using Content.Shared.Tag; +using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Network; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Random; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Spawners; + +namespace Content.Shared.Magic; + +/// +/// Handles learning and using spells (actions) +/// +public abstract class SharedMagicSystem : EntitySystem +{ + [Dependency] private readonly ISerializationManager _seriMan = default!; + [Dependency] private readonly IComponentFactory _compFact = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedGunSystem _gunSystem = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly SharedBodySystem _body = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedDoorSystem _door = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly LockSystem _lock = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly TagSystem _tag = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnBeforeCastSpell); + + SubscribeLocalEvent(OnInstantSpawn); + SubscribeLocalEvent(OnTeleportSpell); + SubscribeLocalEvent(OnWorldSpawn); + SubscribeLocalEvent(OnProjectileSpell); + SubscribeLocalEvent(OnChangeComponentsSpell); + SubscribeLocalEvent(OnSmiteSpell); + SubscribeLocalEvent(OnKnockSpell); + SubscribeLocalEvent(OnChargeSpell); + + // Spell wishlist + // A wishlish of spells that I'd like to implement or planning on implementing in a future PR + + // TODO: InstantDoAfterSpell and WorldDoafterSpell + // Both would be an action that take in an event, that passes an event to trigger once the doafter is done + // This would be three events: + // 1 - Event that triggers from the action that starts the doafter + // 2 - The doafter event itself, which passes the event with it + // 3 - The event to trigger once the do-after finishes + + // TODO: Inanimate objects to life ECS + // AI sentience + + // TODO: Flesh2Stone + // Entity Target spell + // Synergy with Inanimate object to life (detects player and allows player to move around) + + // TODO: Lightning Spell + // Should just fire lightning, try to prevent arc back to caster + + // TODO: Magic Missile (homing projectile ecs) + // Instant action, target any player (except self) on screen + + // TODO: Random projectile ECS for magic-carp, wand of magic + + // TODO: Recall Spell + // mark any item in hand to recall + // ItemRecallComponent + // Event adds the component if it doesn't exist and the performer isn't stored in the comp + // 2nd firing of the event checks to see if the recall comp has this uid, and if it does it calls it + // if no free hands, summon at feet + // if item deleted, clear stored item + + // TODO: Jaunt (should be its own ECS) + // Instant action + // When clicked, disappear/reappear (goes to paused map) + // option to restrict to tiles + // option for requiring entry/exit (blood jaunt) + // speed option + + // TODO: Summon Events + // List of wizard events to add into the event pool that frequently activate + // floor is lava + // change places + // ECS that when triggered, will periodically trigger a random GameRule + // Would need a controller/controller entity? + + // TODO: Summon Guns + // Summon a random gun at peoples feet + // Get every alive player (not in cryo, not a simplemob) + // TODO: After Antag Rework - Rare chance of giving gun collector status to people + + // TODO: Summon Magic + // Summon a random magic wand at peoples feet + // Get every alive player (not in cryo, not a simplemob) + // TODO: After Antag Rework - Rare chance of giving magic collector status to people + + // TODO: Bottle of Blood + // Summons Slaughter Demon + // TODO: Slaughter Demon + // Also see Jaunt + + // TODO: Field Spells + // Should be able to specify a grid of tiles (3x3 for example) that it effects + // Timed despawn - so it doesn't last forever + // Ignore caster - for spells that shouldn't effect the caster (ie if timestop should effect the caster) + + // TODO: Touch toggle spell + // 1 - When toggled on, show in hand + // 2 - Block hand when toggled on + // - Require free hand + // 3 - use spell event when toggled & click + } + + private void OnBeforeCastSpell(Entity ent, ref BeforeCastSpellEvent args) + { + var comp = ent.Comp; + var hasReqs = true; + + if (comp.RequiresClothes) + { + var enumerator = _inventory.GetSlotEnumerator(args.Performer, SlotFlags.OUTERCLOTHING | SlotFlags.HEAD); + while (enumerator.MoveNext(out var containerSlot)) + { + if (containerSlot.ContainedEntity is { } item) + hasReqs = HasComp(item); + else + hasReqs = false; + + if (!hasReqs) + break; + } + } + + if (comp.RequiresSpeech && HasComp(args.Performer)) + hasReqs = false; + + if (hasReqs) + return; + + args.Cancelled = true; + _popup.PopupClient(Loc.GetString("spell-requirements-failed"), args.Performer, args.Performer); + + // TODO: Pre-cast do after, either here or in SharedActionsSystem + } + + private bool PassesSpellPrerequisites(EntityUid spell, EntityUid performer) + { + var ev = new BeforeCastSpellEvent(performer); + RaiseLocalEvent(spell, ref ev); + return !ev.Cancelled; + } + + #region Spells + #region Instant Spawn Spells + /// + /// Handles the instant action (i.e. on the caster) attempting to spawn an entity. + /// + private void OnInstantSpawn(InstantSpawnSpellEvent args) + { + if (args.Handled || !PassesSpellPrerequisites(args.Action, args.Performer)) + return; + + var transform = Transform(args.Performer); + + foreach (var position in GetInstantSpawnPositions(transform, args.PosData)) + { + SpawnSpellHelper(args.Prototype, position, args.Performer, preventCollide: args.PreventCollideWithCaster); + } + + Speak(args); + args.Handled = true; + } + + /// + /// Gets spawn positions listed on + /// + /// + private List GetInstantSpawnPositions(TransformComponent casterXform, MagicInstantSpawnData data) + { + switch (data) + { + case TargetCasterPos: + return new List(1) {casterXform.Coordinates}; + case TargetInFrontSingle: + { + var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized()); + + if (!TryComp(casterXform.GridUid, out var mapGrid)) + return new List(); + if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager)) + return new List(); + + var tileIndex = tileReference.Value.GridIndices; + return new List(1) { _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex) }; + } + case TargetInFront: + { + var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized()); + + if (!TryComp(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 = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex); + EntityCoordinates coordsPlus; + EntityCoordinates coordsMinus; + + var dir = casterXform.LocalRotation.GetCardinalDir(); + switch (dir) + { + case Direction.North: + case Direction.South: + { + coordsPlus = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex + (1, 0)); + coordsMinus = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex + (-1, 0)); + return new List(3) + { + coords, + coordsPlus, + coordsMinus, + }; + } + case Direction.East: + case Direction.West: + { + coordsPlus = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex + (0, 1)); + coordsMinus = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex + (0, -1)); + return new List(3) + { + coords, + coordsPlus, + coordsMinus, + }; + } + } + + return new List(); + } + default: + throw new ArgumentOutOfRangeException(); + } + } + // End Instant Spawn Spells + #endregion + #region World Spawn Spells + /// + /// Spawns entities from a list within range of click. + /// + /// + /// It will offset entities after the first entity based on the OffsetVector2. + /// + /// The Spawn Spell Event args. + private void OnWorldSpawn(WorldSpawnSpellEvent args) + { + if (args.Handled || !PassesSpellPrerequisites(args.Action, args.Performer)) + return; + + var targetMapCoords = args.Target; + + WorldSpawnSpellHelper(args.Prototypes, targetMapCoords, args.Performer, 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 WorldSpawnSpellHelper(List entityEntries, EntityCoordinates entityCoords, EntityUid performer, float? lifetime, Vector2 offsetVector2) + { + var getProtos = EntitySpawnCollection.GetSpawns(entityEntries, _random); + + var offsetCoords = entityCoords; + foreach (var proto in getProtos) + { + SpawnSpellHelper(proto, offsetCoords, performer, lifetime); + offsetCoords = offsetCoords.Offset(offsetVector2); + } + } + // End World Spawn Spells + #endregion + #region Projectile Spells + private void OnProjectileSpell(ProjectileSpellEvent ev) + { + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer) || !_net.IsServer) + return; + + ev.Handled = true; + Speak(ev); + + var xform = Transform(ev.Performer); + var fromCoords = xform.Coordinates; + var toCoords = ev.Target; + var userVelocity = _physics.GetMapLinearVelocity(ev.Performer); + + // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. + var fromMap = fromCoords.ToMap(EntityManager, _transform); + var spawnCoords = _mapManager.TryFindGridAt(fromMap, out var gridUid, out _) + ? fromCoords.WithEntityId(gridUid, EntityManager) + : new(_mapManager.GetMapEntityId(fromMap.MapId), fromMap.Position); + + var ent = Spawn(ev.Prototype, spawnCoords); + var direction = toCoords.ToMapPos(EntityManager, _transform) - + spawnCoords.ToMapPos(EntityManager, _transform); + _gunSystem.ShootProjectile(ent, direction, userVelocity, ev.Performer, ev.Performer); + } + // End Projectile Spells + #endregion + #region Change Component Spells + // staves.yml ActionRGB light + private void OnChangeComponentsSpell(ChangeComponentsSpellEvent ev) + { + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer)) + 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!); + } + } + // End Change Component Spells + #endregion + #region Teleport Spells + // TODO: Rename to teleport clicked spell? + /// + /// Teleports the user to the clicked location + /// + /// + private void OnTeleportSpell(TeleportSpellEvent args) + { + if (args.Handled || !PassesSpellPrerequisites(args.Action, args.Performer)) + return; + + var transform = Transform(args.Performer); + + if (transform.MapID != args.Target.GetMapId(EntityManager) || !_interaction.InRangeUnobstructed(args.Performer, args.Target, range: 1000F, collisionMask: CollisionGroup.Opaque, popup: true)) + return; + + _transform.SetCoordinates(args.Performer, args.Target); + _transform.AttachToGridOrMap(args.Performer, transform); + Speak(args); + args.Handled = true; + } + // End Teleport Spells + #endregion + #region Spell Helpers + private void SpawnSpellHelper(string? proto, EntityCoordinates position, EntityUid performer, float? lifetime = null, bool preventCollide = false) + { + if (!_net.IsServer) + return; + + var ent = Spawn(proto, position.SnapToGrid(EntityManager, _mapManager)); + + if (lifetime != null) + { + var comp = EnsureComp(ent); + comp.Lifetime = lifetime.Value; + } + + if (preventCollide) + { + var comp = EnsureComp(ent); + comp.Uid = performer; + } + } + // End Spell Helpers + #endregion + #region Smite Spells + private void OnSmiteSpell(SmiteSpellEvent ev) + { + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer)) + return; + + ev.Handled = true; + Speak(ev); + + var direction = _transform.GetMapCoordinates(ev.Target, Transform(ev.Target)).Position - _transform.GetMapCoordinates(ev.Performer, Transform(ev.Performer)).Position; + var impulseVector = direction * 10000; + + _physics.ApplyLinearImpulse(ev.Target, impulseVector); + + if (!TryComp(ev.Target, out var body)) + return; + + _body.GibBody(ev.Target, true, body); + } + // End Smite Spells + #endregion + #region Knock Spells + /// + /// Opens all doors and locks within range + /// + /// + private void OnKnockSpell(KnockSpellEvent args) + { + if (args.Handled || !PassesSpellPrerequisites(args.Action, args.Performer)) + return; + + args.Handled = true; + Speak(args); + + var transform = Transform(args.Performer); + + // Look for doors and lockers, and don't open/unlock them if they're already opened/unlocked. + foreach (var target in _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(args.Performer, transform), args.Range, flags: LookupFlags.Dynamic | LookupFlags.Static)) + { + if (!_interaction.InRangeUnobstructed(args.Performer, target, range: 0, collisionMask: CollisionGroup.Opaque)) + continue; + + if (TryComp(target, out var doorBoltComp) && doorBoltComp.BoltsDown) + _door.SetBoltsDown((target, doorBoltComp), false, predicted: true); + + if (TryComp(target, out var doorComp) && doorComp.State is not DoorState.Open) + _door.StartOpening(target); + + if (TryComp(target, out var lockComp) && lockComp.Locked) + _lock.Unlock(target, args.Performer, lockComp); + } + } + // End Knock Spells + #endregion + #region Charge Spells + // TODO: Future support to charge other items + private void OnChargeSpell(ChargeSpellEvent ev) + { + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer) || !TryComp(ev.Performer, out var handsComp)) + return; + + EntityUid? wand = null; + foreach (var item in _hands.EnumerateHeld(ev.Performer, handsComp)) + { + if (!_tag.HasTag(item, ev.WandTag)) + continue; + + wand = item; + } + + ev.Handled = true; + Speak(ev); + + if (wand == null || !TryComp(wand, out var basicAmmoComp) || basicAmmoComp.Count == null) + return; + + _gunSystem.UpdateBasicEntityAmmoCount(wand.Value, basicAmmoComp.Count.Value + ev.Charge, basicAmmoComp); + } + // End Charge Spells + #endregion + // End Spells + #endregion + + // When any spell is cast it will raise this as an event, so then it can be played in server or something. At least until chat gets moved to shared + // TODO: Temp until chat is in shared + private void Speak(BaseActionEvent args) + { + if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech)) + return; + + var ev = new SpeakSpellEvent(args.Performer, speak.Speech); + RaiseLocalEvent(ref ev); + } +} diff --git a/Content.Shared/Magic/SpellbookSystem.cs b/Content.Shared/Magic/SpellbookSystem.cs new file mode 100644 index 0000000000..84b2b23298 --- /dev/null +++ b/Content.Shared/Magic/SpellbookSystem.cs @@ -0,0 +1,96 @@ +using Content.Shared.Actions; +using Content.Shared.DoAfter; +using Content.Shared.Interaction.Events; +using Content.Shared.Magic.Components; +using Content.Shared.Mind; +using Robust.Shared.Network; + +namespace Content.Shared.Magic; + +public sealed class SpellbookSystem : EntitySystem +{ + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly INetManager _netManager = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnInit, before: [typeof(SharedMagicSystem)]); + SubscribeLocalEvent(OnUse); + SubscribeLocalEvent(OnDoAfter); + } + + private void OnInit(Entity ent, ref MapInitEvent args) + { + foreach (var (id, charges) in ent.Comp.SpellActions) + { + var spell = _actionContainer.AddAction(ent, id); + if (spell == null) + continue; + + int? charge = charges; + if (_actions.GetCharges(spell) != null) + charge = _actions.GetCharges(spell); + + _actions.SetCharges(spell, charge < 0 ? null : charge); + ent.Comp.Spells.Add(spell.Value); + } + } + + private void OnUse(Entity ent, ref UseInHandEvent args) + { + if (args.Handled) + return; + + AttemptLearn(ent, args); + + args.Handled = true; + } + + private void OnDoAfter(Entity ent, ref T args) where T : DoAfterEvent // Sometimes i despise this language + { + if (args.Handled || args.Cancelled) + return; + + args.Handled = true; + + if (!ent.Comp.LearnPermanently) + { + _actions.GrantActions(args.Args.User, ent.Comp.Spells, ent); + return; + } + + if (_mind.TryGetMind(args.Args.User, out var mindId, out _)) + { + var mindActionContainerComp = EnsureComp(mindId); + + if (_netManager.IsServer) + _actionContainer.TransferAllActionsWithNewAttached(ent, mindId, args.Args.User, newContainer: mindActionContainerComp); + } + else + { + foreach (var (id, charges) in ent.Comp.SpellActions) + { + EntityUid? actionId = null; + if (_actions.AddAction(args.Args.User, ref actionId, id)) + _actions.SetCharges(actionId, charges < 0 ? null : charges); + } + } + + ent.Comp.SpellActions.Clear(); + } + + private void AttemptLearn(Entity ent, UseInHandEvent args) + { + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.LearnTime, new SpellbookDoAfterEvent(), ent, target: ent) + { + BreakOnMove = true, + BreakOnDamage = true, + NeedHand = true //What, are you going to read with your eyes only?? + }; + + _doAfter.TryStartDoAfter(doAfterEventArgs); + } +} diff --git a/Content.Shared/Store/ListingPrototype.cs b/Content.Shared/Store/ListingPrototype.cs index d3d2e13cdf..559c2a33bf 100644 --- a/Content.Shared/Store/ListingPrototype.cs +++ b/Content.Shared/Store/ListingPrototype.cs @@ -75,14 +75,14 @@ public partial class ListingData : IEquatable, ICloneable public EntProtoId? ProductAction; /// - /// The listing ID of the related upgrade listing. Can be used to link a to an - /// upgrade or to use standalone as an upgrade + /// The listing ID of the related upgrade listing. Can be used to link a to an + /// upgrade or to use standalone as an upgrade /// [DataField] - public ProtoId? ProductUpgradeID; + public ProtoId? ProductUpgradeId; /// - /// Keeps track of the current action entity this is tied to, for action upgrades + /// Keeps track of the current action entity this is tied to, for action upgrades /// [DataField] [NonSerialized] @@ -161,7 +161,7 @@ public partial class ListingData : IEquatable, ICloneable Priority = Priority, ProductEntity = ProductEntity, ProductAction = ProductAction, - ProductUpgradeID = ProductUpgradeID, + ProductUpgradeId = ProductUpgradeId, ProductActionEntity = ProductActionEntity, ProductEvent = ProductEvent, PurchaseAmount = PurchaseAmount, diff --git a/Resources/Locale/en-US/magic/magic.ftl b/Resources/Locale/en-US/magic/magic.ftl new file mode 100644 index 0000000000..4c8a5fc51d --- /dev/null +++ b/Resources/Locale/en-US/magic/magic.ftl @@ -0,0 +1 @@ +spell-requirements-failed = Missing requirements to cast this spell! diff --git a/Resources/Locale/en-US/store/categories.ftl b/Resources/Locale/en-US/store/categories.ftl index 17247b84f4..4ebeff3b23 100644 --- a/Resources/Locale/en-US/store/categories.ftl +++ b/Resources/Locale/en-US/store/categories.ftl @@ -15,3 +15,11 @@ store-category-pointless = Pointless # Revenant store-category-abilities = Abilities + +# Wizard +store-caregory-spellbook-offensive = Offensive Spells +store-caregory-spellbook-defensive = Defensive Spells +store-caregory-spellbook-utility = Utility Spells +store-caregory-spellbook-equipment = Wizard Equipment +store-caregory-spellbook-events = Event Spells + diff --git a/Resources/Locale/en-US/store/currency.ftl b/Resources/Locale/en-US/store/currency.ftl index ed28391531..ada70b5597 100644 --- a/Resources/Locale/en-US/store/currency.ftl +++ b/Resources/Locale/en-US/store/currency.ftl @@ -9,3 +9,4 @@ store-currency-display-debugdollar = {$amount -> } store-currency-display-telecrystal = TC store-currency-display-stolen-essence = Stolen Essence +store-currency-display-wizcoin = Wiz€oin™ diff --git a/Resources/Locale/en-US/store/spellbook-catalog.ftl b/Resources/Locale/en-US/store/spellbook-catalog.ftl new file mode 100644 index 0000000000..457f02916f --- /dev/null +++ b/Resources/Locale/en-US/store/spellbook-catalog.ftl @@ -0,0 +1,35 @@ +# Spells +spellbook-fireball-name = Fireball +spellbook-fireball-desc = Get most crew exploding with rage when they see this fireball heading toward them! + +spellbook-blink-name = Blink +spellbook-blink-desc = Don't blink or you'll miss yourself teleporting away. + +spellbook-force-wall-name = Force Wall +spellbook-force-wall-desc = Make three walls of pure force that you can pass through, but other's can't. + +spellbook-polymoprh-spider-name = Spider Polymoprh +spellbook-polymorph-spider-desc = Transforms you into a spider, man! + +spellbook-polymorph-rod-name = Rod Polymorph +spellbook-polymorph-rod-desc = Change into an Immovable Rod with limited movement. + +spellbook-charge-name = Charge +spellbook-charge-desc = Adds a charge back to your wand! + +# Equipment + +spellbook-wand-polymorph-door-name = Wand of Entrance +spellbook-wand-polymorph-door-description = For when you need a get-away route. + +spellbook-wand-polymorph-carp-name = Wand of Carp Polymorph +spellbook-wand-polymorph-carp-description = For when you need a carp filet quick and the clown is looking juicy. + +# Events + +spellbook-event-summon-ghosts-name = Summon Ghosts +spellbook-event-summon-ghosts-description = Who ya gonna call? + +# Upgrades +spellbook-upgrade-fireball-name = Upgrade Fireball +spellbook-upgrade-fireball-description = Upgrades Fireball to a maximum of level 3! diff --git a/Resources/Prototypes/Actions/polymorph.yml b/Resources/Prototypes/Actions/polymorph.yml index 7472fc0062..445dc8d9f5 100644 --- a/Resources/Prototypes/Actions/polymorph.yml +++ b/Resources/Prototypes/Actions/polymorph.yml @@ -14,3 +14,33 @@ - type: InstantAction event: !type:PolymorphActionEvent itemIconStyle: NoItem + +- type: entity + id: ActionPolymorphWizardSpider + name: Spider Polymorph + description: Polymorphs you into a Spider. + noSpawn: true + components: + - type: InstantAction + useDelay: 60 + event: !type:PolymorphActionEvent + protoId: WizardSpider + itemIconStyle: NoItem + icon: + sprite: Mobs/Animals/spider.rsi + state: tarantula + +- type: entity + id: ActionPolymorphWizardRod + name: Rod Form + description: CLANG! + noSpawn: true + components: + - type: InstantAction + useDelay: 60 + event: !type:PolymorphActionEvent + protoId: WizardRod + itemIconStyle: NoItem + icon: + sprite: Objects/Fun/immovable_rod.rsi + state: icon diff --git a/Resources/Prototypes/Catalog/spellbook_catalog.yml b/Resources/Prototypes/Catalog/spellbook_catalog.yml new file mode 100644 index 0000000000..38b95c3273 --- /dev/null +++ b/Resources/Prototypes/Catalog/spellbook_catalog.yml @@ -0,0 +1,140 @@ +# Offensive +- type: listing + id: SpellbookFireball + name: spellbook-fireball-name + description: spellbook-fireball-desc + productAction: ActionFireball + productUpgradeId: SpellbookFireballUpgrade + cost: + WizCoin: 2 + categories: + - SpellbookOffensive + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: SpellbookRodForm + name: spellbook-polymorph-rod-name + description: spellbook-polymorph-rod-desc + productAction: ActionPolymorphWizardRod + cost: + WizCoin: 3 + categories: + - SpellbookOffensive + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +# Defensive +- type: listing + id: SpellbookForceWall + name: spellbook-force-wall-name + description: spellbook-force-wall-desc + productAction: ActionForceWall + cost: + WizCoin: 3 + categories: + - SpellbookDefensive + +# Utility +- type: listing + id: SpellbookPolymorphSpider + name: spellbook-polymoprh-spider-name + description: spellbook-polymorph-spider-desc + productAction: ActionPolymorphWizardSpider + cost: + WizCoin: 2 + categories: + - SpellbookUtility + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: SpellbookBlink + name: spellbook-blink-name + description: spellbook-blink-desc + productAction: ActionBlink + cost: + WizCoin: 1 + categories: + - SpellbookUtility + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: SpellbookCharge + name: spellbook-charge-name + description: spellbook-charge-desc + productAction: ActionChargeSpell + cost: + WizCoin: 1 + categories: + - SpellbookUtility + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +# Equipment +- type: listing + id: SpellbookWandDoor + name: spellbook-wand-polymorph-door-name + description: spellbook-wand-polymorph-door-description + productEntity: WeaponWandPolymorphDoor + cost: + WizCoin: 3 + categories: + - SpellbookEquipment + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: SpellbookWandPolymorphCarp + name: spellbook-wand-polymorph-carp-name + description: spellbook-wand-polymorph-carp-description + productEntity: WeaponWandPolymorphCarp + cost: + WizCoin: 3 + categories: + - SpellbookEquipment + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +# Event +- type: listing + id: SpellbookEventSummonGhosts + name: spellbook-event-summon-ghosts-name + description: spellbook-event-summon-ghosts-description + productAction: ActionSummonGhosts + cost: + WizCoin: 0 + categories: + - SpellbookEvents + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +# Upgrades +- type: listing + id: SpellbookFireballUpgrade + productUpgradeId: SpellbookFireballUpgrade + name: spellbook-upgrade-fireball-name + description: spellbook-upgrade-fireball-description + icon: + sprite: Objects/Magic/magicactions.rsi + state: fireball + cost: + WizCoin: 2 + categories: + - SpellbookOffensive + conditions: + - !type:BuyBeforeCondition + whitelist: + - SpellbookFireball + # manual for now + - !type:ListingLimitedStockCondition + stock: 2 diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index a844fd724a..2d2b2a353e 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -390,7 +390,7 @@ - Snout - type: entity - parent: ClothingHeadBase + parent: ClothingHeadHatWizardBase id: ClothingHeadHatRedwizard name: red wizard hat description: Strange-looking red hat-wear that most certainly belongs to a real magic user. @@ -505,7 +505,7 @@ - type: entity - parent: ClothingHeadBase + parent: ClothingHeadHatWizardBase id: ClothingHeadHatVioletwizard name: violet wizard hat description: "Strange-looking violet hat-wear that most certainly belongs to a real magic user." @@ -534,6 +534,7 @@ name: witch hat description: A witch hat. components: + - type: WizardClothes #Yes this will count - type: Sprite sprite: Clothing/Head/Hats/witch.rsi - type: Clothing @@ -558,7 +559,14 @@ sprite: Clothing/Head/Hats/wizard_fake.rsi - type: entity + abstract: true parent: ClothingHeadBase + id: ClothingHeadHatWizardBase + components: + - type: WizardClothes + +- type: entity + parent: ClothingHeadHatWizardBase id: ClothingHeadHatWizard name: wizard hat description: Strange-looking blue hat-wear that most certainly belongs to a powerful magic user. diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml index fc02afe35f..112637cdd3 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml @@ -166,9 +166,16 @@ - type: Clothing sprite: Clothing/OuterClothing/Misc/santa.rsi +- type: entity + abstract: true + parent: ClothingOuterBase + id: ClothingOuterWizardBase + components: + - type: WizardClothes + # Is this wizard wearing a fanny pack??? - type: entity - parent: ClothingOuterBase + parent: ClothingOuterWizardBase id: ClothingOuterWizardViolet name: violet wizard robes description: A bizarre gem-encrusted violet robe that radiates magical energies. @@ -179,7 +186,7 @@ sprite: Clothing/OuterClothing/Misc/violetwizard.rsi - type: entity - parent: ClothingOuterBase + parent: ClothingOuterWizardBase id: ClothingOuterWizard name: wizard robes description: A bizarre gem-encrusted blue robe that radiates magical energies. @@ -190,7 +197,7 @@ sprite: Clothing/OuterClothing/Misc/wizard.rsi - type: entity - parent: ClothingOuterBase + parent: ClothingOuterWizardBase id: ClothingOuterWizardRed name: red wizard robes description: Strange-looking, red, hat-wear that most certainly belongs to a real magic user. diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 6523419821..a79d96065a 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -2345,6 +2345,18 @@ bloodMaxVolume: 150 bloodReagent: Laughter +- type: entity + name: wizard spider + parent: MobGiantSpider + id: MobGiantSpiderWizard + description: This spider looks a little magical + suffix: Wizard + components: + - type: Accentless + removes: + - type: ReplacementAccent + accent: xeno #let this wizard speak + - type: entity name: possum parent: SimpleMobBase diff --git a/Resources/Prototypes/Entities/Objects/Magic/books.yml b/Resources/Prototypes/Entities/Objects/Magic/books.yml index dfb875f677..554c5214c1 100644 --- a/Resources/Prototypes/Entities/Objects/Magic/books.yml +++ b/Resources/Prototypes/Entities/Objects/Magic/books.yml @@ -9,7 +9,7 @@ layers: - state: paper_blood - state: cover_strong - color: "#645a5a" + color: "#645a5a" - state: decor_wingette_flat color: "#4d0303" - state: icon_pentagramm @@ -19,13 +19,58 @@ tags: - Spellbook +# For the Wizard Antag +# Do not add discounts or price inflation +- type: entity + id: WizardsGrimoire + name: wizards grimoire + suffix: Wizard + parent: BaseItem + components: + - type: Sprite + sprite: Objects/Misc/books.rsi + layers: + - state: paper_blood + - state: cover_strong + color: "#645a5a" + - state: decor_wingette_flat + color: "#4d0303" + - state: icon_pentagramm + color: "#f7e19f" + - type: UserInterface + interfaces: + enum.StoreUiKey.Key: + type: StoreBoundUserInterface + - type: ActivatableUI + key: enum.StoreUiKey.Key + - type: Store + refundAllowed: true + ownerOnly: true # get your own tome! + preset: StorePresetSpellbook + balance: + WizCoin: 10 # prices are balanced around this 10 point maximum and how strong the spells are + +# Not meant for wizard antag but meant for spawning, so people can't abuse refund if they were given a tome +- type: entity + id: WizardsGrimoireNoRefund + name: wizards grimoire + suffix: Wizard, No Refund + parent: WizardsGrimoire + components: + - type: Store + refundAllowed: false + ownerOnly: true # get your own tome! + preset: StorePresetSpellbook + balance: + WizCoin: 10 # prices are balanced around this 10 point maximum and how strong the spells are + - type: entity id: SpawnSpellbook name: spawn spellbook parent: BaseSpellbook components: - type: Spellbook - spells: + spellActions: ActionSpawnMagicarpSpell: -1 - type: entity @@ -48,7 +93,7 @@ - state: detail_rivets color: gold - type: Spellbook - spells: + spellActions: ActionForceWall: -1 - type: entity @@ -69,7 +114,7 @@ - state: detail_rivets color: gold - type: Spellbook - spells: + spellActions: ActionBlink: -1 - type: entity @@ -92,7 +137,7 @@ color: red - state: overlay_blood - type: Spellbook - spells: + spellActions: ActionSmite: -1 - type: entity @@ -114,7 +159,7 @@ - state: detail_bookmark color: "#98c495" - type: Spellbook - spells: + spellActions: ActionKnock: -1 - type: entity @@ -138,7 +183,7 @@ - state: icon_magic_fireball shader: unshaded - type: Spellbook - spells: + spellActions: ActionFireball: -1 - type: entity @@ -153,7 +198,7 @@ layers: - state: spell_default - type: Spellbook - spells: + spellActions: ActionFlashRune: -1 ActionExplosionRune: -1 ActionIgniteRune: -1 diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml index 2e73056e02..7af9981255 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml @@ -135,7 +135,7 @@ - type: RCDDeconstructable cost: 6 delay: 8 - fx: EffectRCDDeconstruct8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -262,7 +262,7 @@ - type: RCDDeconstructable cost: 6 delay: 8 - fx: EffectRCDDeconstruct8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -298,7 +298,7 @@ - type: RCDDeconstructable cost: 6 delay: 8 - fx: EffectRCDDeconstruct8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -419,7 +419,7 @@ - type: RCDDeconstructable cost: 6 delay: 8 - fx: EffectRCDDeconstruct8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -657,7 +657,7 @@ - type: RCDDeconstructable cost: 6 delay: 8 - fx: EffectRCDDeconstruct8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -693,7 +693,7 @@ - type: RCDDeconstructable cost: 6 delay: 8 - fx: EffectRCDDeconstruct8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -870,7 +870,7 @@ - type: RCDDeconstructable cost: 6 delay: 8 - fx: EffectRCDDeconstruct8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -1210,7 +1210,7 @@ name: force wall components: - type: TimedDespawn - lifetime: 20 + lifetime: 12 - type: Tag tags: - Wall @@ -1250,7 +1250,7 @@ - type: RCDDeconstructable cost: 6 delay: 8 - fx: EffectRCDDeconstruct8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Magic/event_spells.yml b/Resources/Prototypes/Magic/event_spells.yml new file mode 100644 index 0000000000..e59e1b2db8 --- /dev/null +++ b/Resources/Prototypes/Magic/event_spells.yml @@ -0,0 +1,13 @@ +- type: entity + id: ActionSummonGhosts + name: Summon Ghosts + description: Makes all current ghosts permanently invisible + noSpawn: true + components: + - type: InstantAction + useDelay: 120 + itemIconStyle: BigAction + icon: + sprite: Mobs/Ghosts/ghost_human.rsi + state: icon + event: !type:ToggleGhostVisibilityToAllEvent diff --git a/Resources/Prototypes/Magic/knock_spell.yml b/Resources/Prototypes/Magic/knock_spell.yml index f00897d32c..e2c3dcfd4c 100644 --- a/Resources/Prototypes/Magic/knock_spell.yml +++ b/Resources/Prototypes/Magic/knock_spell.yml @@ -7,6 +7,8 @@ - type: InstantAction useDelay: 10 itemIconStyle: BigAction + sound: !type:SoundPathSpecifier + path: /Audio/Magic/knock.ogg icon: sprite: Objects/Magic/magicactions.rsi state: knock diff --git a/Resources/Prototypes/Magic/projectile_spells.yml b/Resources/Prototypes/Magic/projectile_spells.yml index 196472fe7b..b8db7557bb 100644 --- a/Resources/Prototypes/Magic/projectile_spells.yml +++ b/Resources/Prototypes/Magic/projectile_spells.yml @@ -4,10 +4,12 @@ description: Fires an explosive fireball towards the clicked location. noSpawn: true components: + - type: Magic - type: WorldTargetAction useDelay: 15 itemIconStyle: BigAction checkCanAccess: false + raiseOnUser: true range: 60 sound: !type:SoundPathSpecifier path: /Audio/Magic/fireball.ogg @@ -16,25 +18,25 @@ state: fireball event: !type:ProjectileSpellEvent prototype: ProjectileFireball - posData: !type:TargetCasterPos speech: action-speech-spell-fireball - type: ActionUpgrade effectedLevels: 2: ActionFireballII + 3: ActionFireballIII - type: entity id: ActionFireballII parent: ActionFireball name: Fireball II - description: Fire three explosive fireball towards the clicked location. + description: Fires a fireball, but faster! noSpawn: true components: - type: WorldTargetAction - useDelay: 5 - charges: 3 + useDelay: 10 renewCharges: true itemIconStyle: BigAction checkCanAccess: false + raiseOnUser: true range: 60 sound: !type:SoundPathSpecifier path: /Audio/Magic/fireball.ogg @@ -43,5 +45,27 @@ state: fireball event: !type:ProjectileSpellEvent prototype: ProjectileFireball - posData: !type:TargetCasterPos speech: action-speech-spell-fireball + +- type: entity + id: ActionFireballIII + parent: ActionFireball + name: Fireball III + description: The fastest fireball in the west! + noSpawn: true + components: + - type: WorldTargetAction + useDelay: 8 + renewCharges: true + itemIconStyle: BigAction + checkCanAccess: false + raiseOnUser: true + range: 60 + sound: !type:SoundPathSpecifier + path: /Audio/Magic/fireball.ogg + icon: + sprite: Objects/Magic/magicactions.rsi + state: fireball + event: !type:ProjectileSpellEvent + prototype: ProjectileFireball + speech: action-speech-spell-fireball diff --git a/Resources/Prototypes/Magic/teleport_spells.yml b/Resources/Prototypes/Magic/teleport_spells.yml index 30c83891ee..cc89cf8ee0 100644 --- a/Resources/Prototypes/Magic/teleport_spells.yml +++ b/Resources/Prototypes/Magic/teleport_spells.yml @@ -8,9 +8,11 @@ useDelay: 10 range: 16 # default examine-range. # ^ should probably add better validation that the clicked location is on the users screen somewhere, + sound: !type:SoundPathSpecifier + path: /Audio/Magic/blink.ogg itemIconStyle: BigAction checkCanAccess: false - repeat: true + repeat: false icon: sprite: Objects/Magic/magicactions.rsi state: blink diff --git a/Resources/Prototypes/Magic/utility_spells.yml b/Resources/Prototypes/Magic/utility_spells.yml new file mode 100644 index 0000000000..dccdda3789 --- /dev/null +++ b/Resources/Prototypes/Magic/utility_spells.yml @@ -0,0 +1,15 @@ +- type: entity + id: ActionChargeSpell + name: Charge + description: Adds a charge back to your wand + noSpawn: true + components: + - type: InstantAction + useDelay: 30 + itemIconStyle: BigAction + icon: + sprite: Objects/Weapons/Guns/Basic/wands.rsi + state: nothing + event: !type:ChargeSpellEvent + charge: 1 + speech: DI'RI CEL! diff --git a/Resources/Prototypes/Polymorphs/polymorph.yml b/Resources/Prototypes/Polymorphs/polymorph.yml index 582f69b744..a1a805c74f 100644 --- a/Resources/Prototypes/Polymorphs/polymorph.yml +++ b/Resources/Prototypes/Polymorphs/polymorph.yml @@ -175,3 +175,25 @@ revertOnDeath: true revertOnCrit: true duration: 20 + +# Polymorphs for Wizards polymorph self spell +- type: polymorph + id: WizardSpider + configuration: + entity: MobGiantSpiderWizard #Not angry so ghosts can't just take over the wizard + transferName: true + inventory: None + revertOnDeath: true + revertOnCrit: true + +- type: polymorph + id: WizardRod + configuration: + entity: ImmovableRodWizard #CLANG + transferName: true + transferDamage: false + inventory: None + duration: 1 + forced: true + revertOnCrit: false + revertOnDeath: false diff --git a/Resources/Prototypes/Store/categories.yml b/Resources/Prototypes/Store/categories.yml index 6cf641061e..6bd9756c3e 100644 --- a/Resources/Prototypes/Store/categories.yml +++ b/Resources/Prototypes/Store/categories.yml @@ -7,6 +7,32 @@ id: Debug2 name: store-category-debug2 +#WIZARD +- type: storeCategory + id: SpellbookOffensive + name: store-caregory-spellbook-offensive + priority: 0 + +- type: storeCategory + id: SpellbookDefensive + name: store-caregory-spellbook-defensive + priority: 1 + +- type: storeCategory + id: SpellbookUtility + name: store-caregory-spellbook-utility + priority: 2 + +- type: storeCategory + id: SpellbookEquipment + name: store-caregory-spellbook-equipment + priority: 3 + +- type: storeCategory + id: SpellbookEvents + name: store-caregory-spellbook-events + priority: 4 + #uplink categoires - type: storeCategory id: UplinkWeaponry diff --git a/Resources/Prototypes/Store/currency.yml b/Resources/Prototypes/Store/currency.yml index 91039a75e6..b1cff06be2 100644 --- a/Resources/Prototypes/Store/currency.yml +++ b/Resources/Prototypes/Store/currency.yml @@ -1,7 +1,7 @@ - type: currency id: Telecrystal displayName: store-currency-display-telecrystal - cash: + cash: 1: Telecrystal1 canWithdraw: true @@ -10,7 +10,12 @@ displayName: store-currency-display-stolen-essence canWithdraw: false +- type: currency + id: WizCoin + displayName: store-currency-display-wizcoin + canWithdraw: false + #debug - type: currency id: DebugDollar - displayName: store-currency-display-debugdollar \ No newline at end of file + displayName: store-currency-display-debugdollar diff --git a/Resources/Prototypes/Store/presets.yml b/Resources/Prototypes/Store/presets.yml index 84aa7db544..166c29fe41 100644 --- a/Resources/Prototypes/Store/presets.yml +++ b/Resources/Prototypes/Store/presets.yml @@ -15,3 +15,15 @@ - UplinkPointless currencyWhitelist: - Telecrystal + +- type: storePreset + id: StorePresetSpellbook + storeName: Spellbook + categories: + - SpellbookOffensive #Fireball, Rod Form + - SpellbookDefensive #Magic Missile, Wall of Force + - SpellbookUtility #Body Swap, Lich, Teleport, Knock, Polymorph + - SpellbookEquipment #Battlemage Robes, Staff of Locker + - SpellbookEvents #Summon Weapons, Summon Ghosts + currencyWhitelist: + - WizCoin From 332f54a3aebe669f6e50d26e7b047f0bdc28e0fb Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 12 May 2024 09:18:21 +1000 Subject: [PATCH 25/68] Lobby refactor + species loadouts support (#27576) * Vox stuff * Species loadouts and lobby refactor The control flow for lobby is all over the shop so I pulled it all up from the individual controls so now they handle the bare minimum required and LobbyUIController handles the rest. * a * Bulk changes * a * weh * Character import / export * finalise * woops this stuff too * Also datafield exporting * comments * Review --- Content.Client/Entry/EntryPoint.cs | 2 +- Content.Client/IoC/ClientContentIoC.cs | 5 +- .../ClientPreferencesManager.cs | 4 +- .../IClientPreferencesManager.cs | 3 +- Content.Client/Lobby/LobbyState.cs | 106 +- Content.Client/Lobby/LobbyUIController.cs | 393 ++++-- .../Lobby/UI/CharacterPickerButton.xaml | 22 + .../Lobby/UI/CharacterPickerButton.xaml.cs | 92 ++ .../UI/CharacterSetupGui.xaml | 4 - .../Lobby/UI/CharacterSetupGui.xaml.cs | 118 ++ .../UI/HighlightedContainer.xaml | 0 .../UI/HighlightedContainer.xaml.cs | 2 +- .../UI/HumanoidProfileEditor.xaml | 76 +- .../UI/HumanoidProfileEditor.xaml.cs | 1146 ++++++++++------- .../UI/Loadouts}/LoadoutContainer.xaml | 0 .../UI/Loadouts}/LoadoutContainer.xaml.cs | 4 +- .../UI/Loadouts}/LoadoutGroupContainer.xaml | 0 .../Loadouts}/LoadoutGroupContainer.xaml.cs | 11 +- .../UI/Loadouts}/LoadoutWindow.xaml | 0 .../UI/Loadouts}/LoadoutWindow.xaml.cs | 21 +- .../UI/LobbyCharacterPreviewPanel.xaml.cs | 22 +- Content.Client/Lobby/UI/LobbyGui.xaml.cs | 9 +- .../Lobby/UI/ObserveWarningWindow.xaml.cs | 25 +- .../Lobby/UI/Roles/RequirementsSelector.xaml | 9 + .../UI/Roles/RequirementsSelector.xaml.cs | 118 ++ .../UI/Roles/TraitPreferenceSelector.xaml | 7 + .../UI/Roles/TraitPreferenceSelector.xaml.cs | 36 + .../Preferences/UI/AntagPreferenceSelector.cs | 41 - .../Preferences/UI/CharacterSetupGui.xaml.cs | 276 ---- .../UI/HumanoidProfileEditor.Random.cs | 23 - .../Preferences/UI/JobPrioritySelector.cs | 46 - .../Preferences/UI/RequirementsSelector.cs | 222 ---- .../Tests/Lobby/CharacterCreationTest.cs | 1 - .../Tests/Preferences/ServerDbSqliteTests.cs | 29 +- Content.Server/Database/ServerDbBase.cs | 5 +- .../Humanoid/HumanoidCharacterAppearance.cs | 86 +- .../Humanoid/HumanoidProfileExport.cs | 19 + .../SharedHumanoidAppearanceSystem.cs | 42 + .../Preferences/HumanoidCharacterProfile.cs | 327 ++--- .../Loadouts/Effects/GroupLoadoutEffect.cs | 4 +- .../Effects/JobRequirementLoadoutEffect.cs | 2 +- .../Loadouts/Effects/LoadoutEffect.cs | 1 + .../Effects/PointsCostLoadoutEffect.cs | 1 + .../Loadouts/Effects/SpeciesLoadoutEffect.cs | 24 +- .../Preferences/Loadouts/Loadout.cs | 5 +- .../Preferences/Loadouts/RoleLoadout.cs | 37 +- .../preferences/ui/character-setup-gui.ftl | 1 - .../ui/humanoid-profile-editor.ftl | 1 + Resources/Prototypes/Species/vox.yml | 2 +- 49 files changed, 1867 insertions(+), 1563 deletions(-) rename Content.Client/{Preferences => Lobby}/ClientPreferencesManager.cs (97%) rename Content.Client/{Preferences => Lobby}/IClientPreferencesManager.cs (92%) create mode 100644 Content.Client/Lobby/UI/CharacterPickerButton.xaml create mode 100644 Content.Client/Lobby/UI/CharacterPickerButton.xaml.cs rename Content.Client/{Preferences => Lobby}/UI/CharacterSetupGui.xaml (91%) create mode 100644 Content.Client/Lobby/UI/CharacterSetupGui.xaml.cs rename Content.Client/{Preferences => Lobby}/UI/HighlightedContainer.xaml (100%) rename Content.Client/{Preferences => Lobby}/UI/HighlightedContainer.xaml.cs (88%) rename Content.Client/{Preferences => Lobby}/UI/HumanoidProfileEditor.xaml (68%) rename Content.Client/{Preferences => Lobby}/UI/HumanoidProfileEditor.xaml.cs (50%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutContainer.xaml (100%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutContainer.xaml.cs (95%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutGroupContainer.xaml (100%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutGroupContainer.xaml.cs (84%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutWindow.xaml (100%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutWindow.xaml.cs (69%) create mode 100644 Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml create mode 100644 Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs create mode 100644 Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml create mode 100644 Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml.cs delete mode 100644 Content.Client/Preferences/UI/AntagPreferenceSelector.cs delete mode 100644 Content.Client/Preferences/UI/CharacterSetupGui.xaml.cs delete mode 100644 Content.Client/Preferences/UI/HumanoidProfileEditor.Random.cs delete mode 100644 Content.Client/Preferences/UI/JobPrioritySelector.cs delete mode 100644 Content.Client/Preferences/UI/RequirementsSelector.cs create mode 100644 Content.Shared/Humanoid/HumanoidProfileExport.cs diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 25490874e9..b28c6a11fb 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -10,10 +10,10 @@ using Content.Client.Info; using Content.Client.Input; using Content.Client.IoC; using Content.Client.Launcher; +using Content.Client.Lobby; using Content.Client.MainMenu; using Content.Client.Parallax.Managers; using Content.Client.Players.PlayTimeTracking; -using Content.Client.Preferences; using Content.Client.Radiation.Overlays; using Content.Client.Replay; using Content.Client.Screenshot; diff --git a/Content.Client/IoC/ClientContentIoC.cs b/Content.Client/IoC/ClientContentIoC.cs index 65e95b76f0..4703915ae7 100644 --- a/Content.Client/IoC/ClientContentIoC.cs +++ b/Content.Client/IoC/ClientContentIoC.cs @@ -2,23 +2,20 @@ using Content.Client.Administration.Managers; using Content.Client.Changelog; using Content.Client.Chat.Managers; using Content.Client.Clickable; -using Content.Client.Options; using Content.Client.Eui; using Content.Client.GhostKick; using Content.Client.Info; using Content.Client.Launcher; using Content.Client.Parallax.Managers; using Content.Client.Players.PlayTimeTracking; -using Content.Client.Preferences; using Content.Client.Screenshot; using Content.Client.Fullscreen; using Content.Client.Stylesheets; using Content.Client.Viewport; using Content.Client.Voting; -using Content.Shared.Administration; using Content.Shared.Administration.Logs; -using Content.Shared.Module; using Content.Client.Guidebook; +using Content.Client.Lobby; using Content.Client.Replay; using Content.Shared.Administration.Managers; using Content.Shared.Players.PlayTimeTracking; diff --git a/Content.Client/Preferences/ClientPreferencesManager.cs b/Content.Client/Lobby/ClientPreferencesManager.cs similarity index 97% rename from Content.Client/Preferences/ClientPreferencesManager.cs rename to Content.Client/Lobby/ClientPreferencesManager.cs index 89cee7bf79..3f01e1a8f6 100644 --- a/Content.Client/Preferences/ClientPreferencesManager.cs +++ b/Content.Client/Lobby/ClientPreferencesManager.cs @@ -2,12 +2,10 @@ using System.Linq; using Content.Shared.Preferences; using Robust.Client; using Robust.Client.Player; -using Robust.Shared.Configuration; using Robust.Shared.Network; -using Robust.Shared.Prototypes; using Robust.Shared.Utility; -namespace Content.Client.Preferences +namespace Content.Client.Lobby { /// /// Receives and from the server during the initial diff --git a/Content.Client/Preferences/IClientPreferencesManager.cs b/Content.Client/Lobby/IClientPreferencesManager.cs similarity index 92% rename from Content.Client/Preferences/IClientPreferencesManager.cs rename to Content.Client/Lobby/IClientPreferencesManager.cs index e55d6b600c..45a770b162 100644 --- a/Content.Client/Preferences/IClientPreferencesManager.cs +++ b/Content.Client/Lobby/IClientPreferencesManager.cs @@ -1,7 +1,6 @@ -using System; using Content.Shared.Preferences; -namespace Content.Client.Preferences +namespace Content.Client.Lobby { public interface IClientPreferencesManager { diff --git a/Content.Client/Lobby/LobbyState.cs b/Content.Client/Lobby/LobbyState.cs index 91730020a4..1aabc4ff38 100644 --- a/Content.Client/Lobby/LobbyState.cs +++ b/Content.Client/Lobby/LobbyState.cs @@ -3,8 +3,6 @@ using Content.Client.GameTicking.Managers; using Content.Client.LateJoin; using Content.Client.Lobby.UI; using Content.Client.Message; -using Content.Client.Preferences; -using Content.Client.Preferences.UI; using Content.Client.UserInterface.Systems.Chat; using Content.Client.Voting; using Robust.Client; @@ -12,8 +10,6 @@ using Robust.Client.Console; using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; -using Robust.Shared.Configuration; -using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -25,20 +21,15 @@ namespace Content.Client.Lobby [Dependency] private readonly IClientConsoleHost _consoleHost = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IResourceCache _resourceCache = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; - [Dependency] private readonly IClientPreferencesManager _preferencesManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IVoteManager _voteManager = default!; - [Dependency] private readonly IConfigurationManager _configurationManager = default!; - - [ViewVariables] private CharacterSetupGui? _characterSetup; private ClientGameTicker _gameTicker = default!; private ContentAudioSystem _contentAudioSystem = default!; protected override Type? LinkedScreenType { get; } = typeof(LobbyGui); - private LobbyGui? _lobby; + public LobbyGui? Lobby; protected override void Startup() { @@ -47,45 +38,23 @@ namespace Content.Client.Lobby return; } - _lobby = (LobbyGui) _userInterfaceManager.ActiveScreen; + Lobby = (LobbyGui) _userInterfaceManager.ActiveScreen; var chatController = _userInterfaceManager.GetUIController(); _gameTicker = _entityManager.System(); _contentAudioSystem = _entityManager.System(); _contentAudioSystem.LobbySoundtrackChanged += UpdateLobbySoundtrackInfo; - _characterSetup = new CharacterSetupGui(_entityManager, _resourceCache, _preferencesManager, - _prototypeManager, _configurationManager); - LayoutContainer.SetAnchorPreset(_characterSetup, LayoutContainer.LayoutPreset.Wide); - _lobby.CharacterSetupState.AddChild(_characterSetup); chatController.SetMainChat(true); - _voteManager.SetPopupContainer(_lobby.VoteContainer); - - _characterSetup.CloseButton.OnPressed += _ => - { - // Reset sliders etc. - _characterSetup?.UpdateControls(); - - var controller = _userInterfaceManager.GetUIController(); - controller.SetClothes(true); - controller.UpdateProfile(); - _lobby.SwitchState(LobbyGui.LobbyGuiState.Default); - }; - - _characterSetup.SaveButton.OnPressed += _ => - { - _characterSetup.Save(); - _userInterfaceManager.GetUIController().ReloadProfile(); - }; - - LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide); - _lobby.ServerName.Text = _baseClient.GameInfo?.ServerName; //The eye of refactor gazes upon you... + _voteManager.SetPopupContainer(Lobby.VoteContainer); + LayoutContainer.SetAnchorPreset(Lobby, LayoutContainer.LayoutPreset.Wide); + Lobby.ServerName.Text = _baseClient.GameInfo?.ServerName; //The eye of refactor gazes upon you... UpdateLobbyUi(); - _lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed; - _lobby.ReadyButton.OnPressed += OnReadyPressed; - _lobby.ReadyButton.OnToggled += OnReadyToggled; + Lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed; + Lobby.ReadyButton.OnPressed += OnReadyPressed; + Lobby.ReadyButton.OnToggled += OnReadyToggled; _gameTicker.InfoBlobUpdated += UpdateLobbyUi; _gameTicker.LobbyStatusUpdated += LobbyStatusUpdated; @@ -103,20 +72,23 @@ namespace Content.Client.Lobby _voteManager.ClearPopupContainer(); - _lobby!.CharacterPreview.CharacterSetupButton.OnPressed -= OnSetupPressed; - _lobby!.ReadyButton.OnPressed -= OnReadyPressed; - _lobby!.ReadyButton.OnToggled -= OnReadyToggled; + Lobby!.CharacterPreview.CharacterSetupButton.OnPressed -= OnSetupPressed; + Lobby!.ReadyButton.OnPressed -= OnReadyPressed; + Lobby!.ReadyButton.OnToggled -= OnReadyToggled; - _lobby = null; + Lobby = null; + } - _characterSetup?.Dispose(); - _characterSetup = null; + public void SwitchState(LobbyGui.LobbyGuiState state) + { + // Yeah I hate this but LobbyState contains all the badness for now. + Lobby?.SwitchState(state); } private void OnSetupPressed(BaseButton.ButtonEventArgs args) { SetReady(false); - _lobby!.SwitchState(LobbyGui.LobbyGuiState.CharacterSetup); + Lobby?.SwitchState(LobbyGui.LobbyGuiState.CharacterSetup); } private void OnReadyPressed(BaseButton.ButtonEventArgs args) @@ -138,13 +110,13 @@ namespace Content.Client.Lobby { if (_gameTicker.IsGameStarted) { - _lobby!.StartTime.Text = string.Empty; + Lobby!.StartTime.Text = string.Empty; var roundTime = _gameTiming.CurTime.Subtract(_gameTicker.RoundStartTimeSpan); - _lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-time", ("hours", roundTime.Hours), ("minutes", roundTime.Minutes)); + Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-time", ("hours", roundTime.Hours), ("minutes", roundTime.Minutes)); return; } - _lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started"); + Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started"); string text; if (_gameTicker.Paused) @@ -153,7 +125,7 @@ namespace Content.Client.Lobby } else if (_gameTicker.StartTime < _gameTiming.CurTime) { - _lobby!.StartTime.Text = Loc.GetString("lobby-state-soon"); + Lobby!.StartTime.Text = Loc.GetString("lobby-state-soon"); return; } else @@ -170,7 +142,7 @@ namespace Content.Client.Lobby } } - _lobby!.StartTime.Text = Loc.GetString("lobby-state-round-start-countdown-text", ("timeLeft", text)); + Lobby!.StartTime.Text = Loc.GetString("lobby-state-round-start-countdown-text", ("timeLeft", text)); } private void LobbyStatusUpdated() @@ -181,31 +153,31 @@ namespace Content.Client.Lobby private void LobbyLateJoinStatusUpdated() { - _lobby!.ReadyButton.Disabled = _gameTicker.DisallowedLateJoin; + Lobby!.ReadyButton.Disabled = _gameTicker.DisallowedLateJoin; } private void UpdateLobbyUi() { if (_gameTicker.IsGameStarted) { - _lobby!.ReadyButton.Text = Loc.GetString("lobby-state-ready-button-join-state"); - _lobby!.ReadyButton.ToggleMode = false; - _lobby!.ReadyButton.Pressed = false; - _lobby!.ObserveButton.Disabled = false; + Lobby!.ReadyButton.Text = Loc.GetString("lobby-state-ready-button-join-state"); + Lobby!.ReadyButton.ToggleMode = false; + Lobby!.ReadyButton.Pressed = false; + Lobby!.ObserveButton.Disabled = false; } else { - _lobby!.StartTime.Text = string.Empty; - _lobby!.ReadyButton.Text = Loc.GetString(_lobby!.ReadyButton.Pressed ? "lobby-state-player-status-ready": "lobby-state-player-status-not-ready"); - _lobby!.ReadyButton.ToggleMode = true; - _lobby!.ReadyButton.Disabled = false; - _lobby!.ReadyButton.Pressed = _gameTicker.AreWeReady; - _lobby!.ObserveButton.Disabled = true; + Lobby!.StartTime.Text = string.Empty; + Lobby!.ReadyButton.Text = Loc.GetString(Lobby!.ReadyButton.Pressed ? "lobby-state-player-status-ready": "lobby-state-player-status-not-ready"); + Lobby!.ReadyButton.ToggleMode = true; + Lobby!.ReadyButton.Disabled = false; + Lobby!.ReadyButton.Pressed = _gameTicker.AreWeReady; + Lobby!.ObserveButton.Disabled = true; } if (_gameTicker.ServerInfoBlob != null) { - _lobby!.ServerInfo.SetInfoBlob(_gameTicker.ServerInfoBlob); + Lobby!.ServerInfo.SetInfoBlob(_gameTicker.ServerInfoBlob); } } @@ -213,7 +185,7 @@ namespace Content.Client.Lobby { if (ev.SoundtrackFilename == null) { - _lobby!.LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text")); + Lobby!.LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text")); } else if ( ev.SoundtrackFilename != null @@ -234,7 +206,7 @@ namespace Content.Client.Lobby ("songTitle", title), ("songArtist", artist)); - _lobby!.LobbySong.SetMarkup(markup); + Lobby!.LobbySong.SetMarkup(markup); } } @@ -242,11 +214,11 @@ namespace Content.Client.Lobby { if (_gameTicker.LobbyBackground != null) { - _lobby!.Background.Texture = _resourceCache.GetResource(_gameTicker.LobbyBackground ); + Lobby!.Background.Texture = _resourceCache.GetResource(_gameTicker.LobbyBackground ); } else { - _lobby!.Background.Texture = null; + Lobby!.Background.Texture = null; } } diff --git a/Content.Client/Lobby/LobbyUIController.cs b/Content.Client/Lobby/LobbyUIController.cs index 9eb259657d..ae9196c110 100644 --- a/Content.Client/Lobby/LobbyUIController.cs +++ b/Content.Client/Lobby/LobbyUIController.cs @@ -2,190 +2,292 @@ using System.Linq; using Content.Client.Humanoid; using Content.Client.Inventory; using Content.Client.Lobby.UI; -using Content.Client.Preferences; -using Content.Client.Preferences.UI; +using Content.Client.Players.PlayTimeTracking; using Content.Client.Station; +using Content.Shared.CCVar; using Content.Shared.Clothing; using Content.Shared.GameTicking; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Markings; using Content.Shared.Humanoid.Prototypes; using Content.Shared.Preferences; using Content.Shared.Preferences.Loadouts; -using Content.Shared.Preferences.Loadouts.Effects; using Content.Shared.Roles; +using Content.Shared.Traits; +using Robust.Client.Player; +using Robust.Client.ResourceManagement; using Robust.Client.State; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; +using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Utility; namespace Content.Client.Lobby; public sealed class LobbyUIController : UIController, IOnStateEntered, IOnStateExited { [Dependency] private readonly IClientPreferencesManager _preferencesManager = default!; - [Dependency] private readonly IStateManager _stateManager = default!; + [Dependency] private readonly IConfigurationManager _configurationManager = default!; + [Dependency] private readonly IFileDialogManager _dialogManager = default!; + [Dependency] private readonly ILogManager _logManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; + [Dependency] private readonly IStateManager _stateManager = default!; + [Dependency] private readonly JobRequirementsManager _requirements = default!; + [Dependency] private readonly MarkingManager _markings = default!; [UISystemDependency] private readonly HumanoidAppearanceSystem _humanoid = default!; [UISystemDependency] private readonly ClientInventorySystem _inventory = default!; [UISystemDependency] private readonly StationSpawningSystem _spawn = default!; - private LobbyCharacterPreviewPanel? _previewPanel; - - private bool _showClothes = true; - - /* - * Each character profile has its own dummy. There is also a dummy for the lobby screen + character editor - * that is shared too. - */ + private CharacterSetupGui? _characterSetup; + private HumanoidProfileEditor? _profileEditor; /// - /// Preview dummy for role gear. + /// This is the characher preview panel in the chat. This should only update if their character updates. /// - private EntityUid? _previewDummy; + private LobbyCharacterPreviewPanel? PreviewPanel => GetLobbyPreview(); /// - /// If we currently have a job prototype selected. + /// This is the modified profile currently being edited. /// - private JobPrototype? _dummyJob; + private HumanoidCharacterProfile? EditedProfile => _profileEditor?.Profile; - // TODO: Load the species directly and don't update entity ever. - public event Action? PreviewDummyUpdated; - - private HumanoidCharacterProfile? _profile; + private int? EditedSlot => _profileEditor?.CharacterSlot; public override void Initialize() { base.Initialize(); + _prototypeManager.PrototypesReloaded += OnProtoReload; _preferencesManager.OnServerDataLoaded += PreferencesDataLoaded; + _requirements.Updated += OnRequirementsUpdated; + + _configurationManager.OnValueChanged(CCVars.FlavorText, args => + { + _profileEditor?.RefreshFlavorText(); + }); + + _configurationManager.OnValueChanged(CCVars.GameRoleTimers, args => + { + _profileEditor?.RefreshAntags(); + _profileEditor?.RefreshJobs(); + _profileEditor?.RefreshLoadouts(); + }); + } + + private LobbyCharacterPreviewPanel? GetLobbyPreview() + { + if (_stateManager.CurrentState is LobbyState lobby) + { + return lobby.Lobby?.CharacterPreview; + } + + return null; + } + + private void OnRequirementsUpdated() + { + if (_profileEditor != null) + { + _profileEditor.RefreshAntags(); + _profileEditor.RefreshJobs(); + } + } + + private void OnProtoReload(PrototypesReloadedEventArgs obj) + { + if (_profileEditor != null) + { + if (obj.WasModified()) + { + _profileEditor.RefreshAntags(); + } + + if (obj.WasModified() || + obj.WasModified()) + { + _profileEditor.RefreshJobs(); + } + + if (obj.WasModified() || + obj.WasModified() || + obj.WasModified()) + { + _profileEditor.RefreshLoadouts(); + } + + if (obj.WasModified()) + { + _profileEditor.RefreshSpecies(); + } + + if (obj.WasModified()) + { + _profileEditor.RefreshTraits(); + } + } } private void PreferencesDataLoaded() { - UpdateProfile(); - } - - public void OnStateEntered(LobbyState state) - { - } - - public void OnStateExited(LobbyState state) - { - EntityManager.DeleteEntity(_previewDummy); - _previewDummy = null; - } - - public void SetPreviewPanel(LobbyCharacterPreviewPanel? panel) - { - _previewPanel = panel; - ReloadProfile(); - } - - public void SetClothes(bool value) - { - if (_showClothes == value) - return; - - _showClothes = value; - ReloadCharacterUI(); - } - - public void SetDummyJob(JobPrototype? job) - { - _dummyJob = job; - ReloadCharacterUI(); - } - - /// - /// Updates the character only with the specified profile change. - /// - public void ReloadProfile() - { - // Test moment - if (_profile == null || _stateManager.CurrentState is not LobbyState) - return; - - // Ignore job clothes and the likes so we don't spam entities out every frame of color changes. - var previewDummy = EnsurePreviewDummy(_profile); - _humanoid.LoadProfile(previewDummy, _profile); - } - - /// - /// Updates the currently selected character's preview. - /// - public void ReloadCharacterUI() - { - // Test moment - if (_profile == null || _stateManager.CurrentState is not LobbyState) - return; - - EntityManager.DeleteEntity(_previewDummy); - _previewDummy = null; - _previewDummy = EnsurePreviewDummy(_profile); - _previewPanel?.SetSprite(_previewDummy.Value); - _previewPanel?.SetSummaryText(_profile.Summary); - _humanoid.LoadProfile(_previewDummy.Value, _profile); - - if (_showClothes) - GiveDummyJobClothesLoadout(_previewDummy.Value, _profile); - } - - /// - /// Updates character profile to the default. - /// - public void UpdateProfile() - { - if (!_preferencesManager.ServerDataLoaded) - { - _profile = null; - return; - } - - if (_preferencesManager.Preferences?.SelectedCharacter is HumanoidCharacterProfile selectedCharacter) - { - _profile = selectedCharacter; - _previewPanel?.SetLoaded(true); - } - else - { - _previewPanel?.SetSummaryText(string.Empty); - _previewPanel?.SetLoaded(false); - } - - ReloadCharacterUI(); - } - - public void UpdateProfile(HumanoidCharacterProfile? profile) - { - if (_profile?.Equals(profile) == true) - return; + PreviewPanel?.SetLoaded(true); if (_stateManager.CurrentState is not LobbyState) return; - _profile = profile; + ReloadCharacterSetup(); } - private EntityUid EnsurePreviewDummy(HumanoidCharacterProfile profile) + public void OnStateEntered(LobbyState state) { - if (_previewDummy != null) - return _previewDummy.Value; - - _previewDummy = EntityManager.SpawnEntity(_prototypeManager.Index(profile.Species).DollPrototype, MapCoordinates.Nullspace); - PreviewDummyUpdated?.Invoke(_previewDummy.Value); - return _previewDummy.Value; + PreviewPanel?.SetLoaded(_preferencesManager.ServerDataLoaded); + ReloadCharacterSetup(); } + public void OnStateExited(LobbyState state) + { + PreviewPanel?.SetLoaded(false); + _profileEditor?.Dispose(); + _characterSetup?.Dispose(); + + _characterSetup = null; + _profileEditor = null; + } + + /// + /// Reloads every single character setup control. + /// + public void ReloadCharacterSetup() + { + RefreshLobbyPreview(); + var (characterGui, profileEditor) = EnsureGui(); + characterGui.ReloadCharacterPickers(); + profileEditor.SetProfile( + (HumanoidCharacterProfile?) _preferencesManager.Preferences?.SelectedCharacter, + _preferencesManager.Preferences?.SelectedCharacterIndex); + } + + /// + /// Refreshes the character preview in the lobby chat. + /// + private void RefreshLobbyPreview() + { + if (PreviewPanel == null) + return; + + // Get selected character, load it, then set it + var character = _preferencesManager.Preferences?.SelectedCharacter; + + if (character is not HumanoidCharacterProfile humanoid) + { + PreviewPanel.SetSprite(EntityUid.Invalid); + PreviewPanel.SetSummaryText(string.Empty); + return; + } + + var dummy = LoadProfileEntity(humanoid, null, true); + PreviewPanel.SetSprite(dummy); + PreviewPanel.SetSummaryText(humanoid.Summary); + } + + private void SaveProfile() + { + DebugTools.Assert(EditedProfile != null); + + if (EditedProfile == null || EditedSlot == null) + return; + + var selected = _preferencesManager.Preferences?.SelectedCharacterIndex; + + if (selected == null) + return; + + _preferencesManager.UpdateCharacter(EditedProfile, EditedSlot.Value); + ReloadCharacterSetup(); + } + + private (CharacterSetupGui, HumanoidProfileEditor) EnsureGui() + { + if (_characterSetup != null && _profileEditor != null) + { + _characterSetup.Visible = true; + _profileEditor.Visible = true; + return (_characterSetup, _profileEditor); + } + + _profileEditor = new HumanoidProfileEditor( + _preferencesManager, + _configurationManager, + EntityManager, + _dialogManager, + _logManager, + _playerManager, + _prototypeManager, + _requirements, + _markings); + + _characterSetup = new CharacterSetupGui(EntityManager, _prototypeManager, _resourceCache, _preferencesManager, _profileEditor); + + _characterSetup.CloseButton.OnPressed += _ => + { + // Reset sliders etc. + _profileEditor.SetProfile(null, null); + _profileEditor.Visible = false; + + if (_stateManager.CurrentState is LobbyState lobbyGui) + { + lobbyGui.SwitchState(LobbyGui.LobbyGuiState.Default); + } + }; + + _profileEditor.Save += SaveProfile; + + _characterSetup.SelectCharacter += args => + { + _preferencesManager.SelectCharacter(args); + ReloadCharacterSetup(); + }; + + _characterSetup.DeleteCharacter += args => + { + _preferencesManager.DeleteCharacter(args); + + // Reload everything + if (EditedSlot == args) + { + ReloadCharacterSetup(); + } + else + { + // Only need to reload character pickers + _characterSetup?.ReloadCharacterPickers(); + } + }; + + if (_stateManager.CurrentState is LobbyState lobby) + { + lobby.Lobby?.CharacterSetupState.AddChild(_characterSetup); + } + + return (_characterSetup, _profileEditor); + } + + #region Helpers + /// /// Applies the highest priority job's clothes to the dummy. /// - public void GiveDummyJobClothesLoadout(EntityUid dummy, HumanoidCharacterProfile profile) + public void GiveDummyJobClothesLoadout(EntityUid dummy, JobPrototype? jobProto, HumanoidCharacterProfile profile) { - var job = _dummyJob ?? GetPreferredJob(profile); + var job = jobProto ?? GetPreferredJob(profile); GiveDummyJobClothes(dummy, profile, job); if (_prototypeManager.HasIndex(LoadoutSystem.GetJobPrototype(job.ID))) { - var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), EntityManager, _prototypeManager); + var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), profile.Species, EntityManager, _prototypeManager); GiveDummyLoadout(dummy, loadout); } } @@ -279,8 +381,39 @@ public sealed class LobbyUIController : UIController, IOnStateEntered + /// Loads the profile onto a dummy entity. + /// + public EntityUid LoadProfileEntity(HumanoidCharacterProfile? humanoid, JobPrototype? job, bool jobClothes) { - return _previewDummy; + EntityUid dummyEnt; + + if (humanoid is not null) + { + var dummy = _prototypeManager.Index(humanoid.Species).DollPrototype; + dummyEnt = EntityManager.SpawnEntity(dummy, MapCoordinates.Nullspace); + } + else + { + dummyEnt = EntityManager.SpawnEntity(_prototypeManager.Index(SharedHumanoidAppearanceSystem.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace); + } + + _humanoid.LoadProfile(dummyEnt, humanoid); + + if (humanoid != null && jobClothes) + { + job ??= GetPreferredJob(humanoid); + GiveDummyJobClothes(dummyEnt, humanoid, job); + + if (_prototypeManager.HasIndex(LoadoutSystem.GetJobPrototype(job.ID))) + { + var loadout = humanoid.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), humanoid.Species, EntityManager, _prototypeManager); + GiveDummyLoadout(dummyEnt, loadout); + } + } + + return dummyEnt; } + + #endregion } diff --git a/Content.Client/Lobby/UI/CharacterPickerButton.xaml b/Content.Client/Lobby/UI/CharacterPickerButton.xaml new file mode 100644 index 0000000000..af1e640aad --- /dev/null +++ b/Content.Client/Lobby/UI/CharacterPickerButton.xaml @@ -0,0 +1,22 @@ + + + + + [GenerateTypedNameReferences] + public sealed partial class CharacterSetupGui : Control + { + private readonly IClientPreferencesManager _preferencesManager; + private readonly IEntityManager _entManager; + private readonly IPrototypeManager _protomanager; + + private readonly Button _createNewCharacterButton; + + public event Action? SelectCharacter; + public event Action? DeleteCharacter; + + public CharacterSetupGui( + IEntityManager entManager, + IPrototypeManager protoManager, + IResourceCache resourceCache, + IClientPreferencesManager preferencesManager, + HumanoidProfileEditor profileEditor) + { + RobustXamlLoader.Load(this); + _preferencesManager = preferencesManager; + _entManager = entManager; + _protomanager = protoManager; + + var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); + var back = new StyleBoxTexture + { + Texture = panelTex, + Modulate = new Color(37, 37, 42) + }; + back.SetPatchMargin(StyleBox.Margin.All, 10); + + BackgroundPanel.PanelOverride = back; + + _createNewCharacterButton = new Button + { + Text = Loc.GetString("character-setup-gui-create-new-character-button"), + }; + + _createNewCharacterButton.OnPressed += args => + { + preferencesManager.CreateCharacter(HumanoidCharacterProfile.Random()); + ReloadCharacterPickers(); + args.Event.Handle(); + }; + + CharEditor.AddChild(profileEditor); + RulesButton.OnPressed += _ => new RulesAndInfoWindow().Open(); + + StatsButton.OnPressed += _ => new PlaytimeStatsWindow().OpenCentered(); + } + + /// + /// Disposes and reloads all character picker buttons from the preferences data. + /// + public void ReloadCharacterPickers() + { + _createNewCharacterButton.Orphan(); + Characters.DisposeAllChildren(); + + var numberOfFullSlots = 0; + var characterButtonsGroup = new ButtonGroup(); + + if (!_preferencesManager.ServerDataLoaded) + { + return; + } + + _createNewCharacterButton.ToolTip = + Loc.GetString("character-setup-gui-create-new-character-button-tooltip", + ("maxCharacters", _preferencesManager.Settings!.MaxCharacterSlots)); + + var selectedSlot = _preferencesManager.Preferences?.SelectedCharacterIndex; + + foreach (var (slot, character) in _preferencesManager.Preferences!.Characters) + { + numberOfFullSlots++; + var characterPickerButton = new CharacterPickerButton(_entManager, + _protomanager, + characterButtonsGroup, + character, + slot == selectedSlot); + + Characters.AddChild(characterPickerButton); + + characterPickerButton.OnPressed += args => + { + SelectCharacter?.Invoke(slot); + }; + + characterPickerButton.OnDeletePressed += () => + { + DeleteCharacter?.Invoke(slot); + }; + } + + _createNewCharacterButton.Disabled = numberOfFullSlots >= _preferencesManager.Settings.MaxCharacterSlots; + Characters.AddChild(_createNewCharacterButton); + } + } +} diff --git a/Content.Client/Preferences/UI/HighlightedContainer.xaml b/Content.Client/Lobby/UI/HighlightedContainer.xaml similarity index 100% rename from Content.Client/Preferences/UI/HighlightedContainer.xaml rename to Content.Client/Lobby/UI/HighlightedContainer.xaml diff --git a/Content.Client/Preferences/UI/HighlightedContainer.xaml.cs b/Content.Client/Lobby/UI/HighlightedContainer.xaml.cs similarity index 88% rename from Content.Client/Preferences/UI/HighlightedContainer.xaml.cs rename to Content.Client/Lobby/UI/HighlightedContainer.xaml.cs index 68294d0f05..084c1c3709 100644 --- a/Content.Client/Preferences/UI/HighlightedContainer.xaml.cs +++ b/Content.Client/Lobby/UI/HighlightedContainer.xaml.cs @@ -2,7 +2,7 @@ using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; -namespace Content.Client.Preferences.UI; +namespace Content.Client.Lobby.UI; [GenerateTypedNameReferences] public sealed partial class HighlightedContainer : PanelContainer diff --git a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml similarity index 68% rename from Content.Client/Preferences/UI/HumanoidProfileEditor.xaml rename to Content.Client/Lobby/UI/HumanoidProfileEditor.xaml index 5926aee898..918b6840b4 100644 --- a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml @@ -1,8 +1,8 @@ @@ -10,45 +10,41 @@ - + - + public abstract class SharedHumanoidAppearanceSystem : EntitySystem { + [Dependency] private readonly IConfigurationManager _cfgManager = default!; [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly ISerializationManager _serManager = default!; [Dependency] private readonly MarkingManager _markingManager = default!; [ValidatePrototypeId] @@ -37,6 +48,37 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem SubscribeLocalEvent(OnExamined); } + public DataNode ToDataNode(HumanoidCharacterProfile profile) + { + var export = new HumanoidProfileExport() + { + ForkId = _cfgManager.GetCVar(CVars.BuildForkId), + Profile = profile, + }; + + var dataNode = _serManager.WriteValue(export, alwaysWrite: true, notNullableOverride: true); + return dataNode; + } + + public HumanoidCharacterProfile FromStream(Stream stream, ICommonSession session) + { + using var reader = new StreamReader(stream, EncodingHelpers.UTF8); + var yamlStream = new YamlStream(); + yamlStream.Load(reader); + + var root = yamlStream.Documents[0].RootNode; + var export = _serManager.Read(root.ToDataNode(), notNullableOverride: true); + + /* + * Add custom handling here for forks / version numbers if you care. + */ + + var profile = export.Profile; + var collection = IoCManager.Instance; + profile.EnsureValid(session, collection!); + return profile; + } + private void OnInit(EntityUid uid, HumanoidAppearanceComponent humanoid, ComponentInit args) { if (string.IsNullOrEmpty(humanoid.Species) || _netManager.IsClient && !IsClientSide(uid)) diff --git a/Content.Shared/Preferences/HumanoidCharacterProfile.cs b/Content.Shared/Preferences/HumanoidCharacterProfile.cs index f9b037c8ab..fd679c7796 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -5,7 +5,6 @@ using Content.Shared.GameTicking; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; using Content.Shared.Preferences.Loadouts; -using Content.Shared.Preferences.Loadouts.Effects; using Content.Shared.Roles; using Content.Shared.Traits; using Robust.Shared.Collections; @@ -32,16 +31,101 @@ namespace Content.Shared.Preferences public const int MaxNameLength = 32; public const int MaxDescLength = 512; - private readonly Dictionary _jobPriorities; - private readonly List _antagPreferences; - private readonly List _traitPreferences; + /// + /// Job preferences for initial spawn. + /// + [DataField] + private Dictionary _jobPriorities = new() + { + { + SharedGameTicker.FallbackOverflowJob, JobPriority.High + } + }; + /// + /// Antags we have opted in to. + /// + [DataField] + private HashSet _antagPreferences = new(); + + /// + /// Enabled traits. + /// + [DataField] + private HashSet _traitPreferences = new(); + + /// + /// + /// public IReadOnlyDictionary Loadouts => _loadouts; - private Dictionary _loadouts; + [DataField] + private Dictionary _loadouts = new(); - // What in the lord is happening here. - private HumanoidCharacterProfile( + [DataField] + public string Name { get; set; } = "John Doe"; + + /// + /// Detailed text that can appear for the character if is enabled. + /// + [DataField] + public string FlavorText { get; set; } = string.Empty; + + /// + /// Associated for this profile. + /// + [DataField] + public string Species { get; set; } = SharedHumanoidAppearanceSystem.DefaultSpecies; + + [DataField] + public int Age { get; set; } = 18; + + [DataField] + public Sex Sex { get; private set; } = Sex.Male; + + [DataField] + public Gender Gender { get; private set; } = Gender.Male; + + /// + /// + /// + public ICharacterAppearance CharacterAppearance => Appearance; + + /// + /// Stores markings, eye colors, etc for the profile. + /// + [DataField] + public HumanoidCharacterAppearance Appearance { get; set; } = new(); + + /// + /// When spawning into a round what's the preferred spot to spawn. + /// + [DataField] + public SpawnPriorityPreference SpawnPriority { get; private set; } = SpawnPriorityPreference.None; + + /// + /// + /// + public IReadOnlyDictionary JobPriorities => _jobPriorities; + + /// + /// + /// + public IReadOnlySet AntagPreferences => _antagPreferences; + + /// + /// + /// + public IReadOnlySet TraitPreferences => _traitPreferences; + + /// + /// If we're unable to get one of our preferred jobs do we spawn as a fallback job or do we stay in lobby. + /// + [DataField] + public PreferenceUnavailableMode PreferenceUnavailable { get; private set; } = + PreferenceUnavailableMode.SpawnAsOverflow; + + public HumanoidCharacterProfile( string name, string flavortext, string species, @@ -52,8 +136,8 @@ namespace Content.Shared.Preferences SpawnPriorityPreference spawnPriority, Dictionary jobPriorities, PreferenceUnavailableMode preferenceUnavailable, - List antagPreferences, - List traitPreferences, + HashSet antagPreferences, + HashSet traitPreferences, Dictionary loadouts) { Name = name; @@ -71,40 +155,21 @@ namespace Content.Shared.Preferences _loadouts = loadouts; } - /// Copy constructor but with overridable references (to prevent useless copies) - private HumanoidCharacterProfile( - HumanoidCharacterProfile other, - Dictionary jobPriorities, - List antagPreferences, - List traitPreferences, - Dictionary loadouts) - : this(other.Name, other.FlavorText, other.Species, other.Age, other.Sex, other.Gender, other.Appearance, other.SpawnPriority, - jobPriorities, other.PreferenceUnavailable, antagPreferences, traitPreferences, loadouts) - { - } - /// Copy constructor - private HumanoidCharacterProfile(HumanoidCharacterProfile other) - : this(other, new Dictionary(other.JobPriorities), new List(other.AntagPreferences), new List(other.TraitPreferences), new Dictionary(other.Loadouts)) - { - } - - public HumanoidCharacterProfile( - string name, - string flavortext, - string species, - int age, - Sex sex, - Gender gender, - HumanoidCharacterAppearance appearance, - SpawnPriorityPreference spawnPriority, - IReadOnlyDictionary jobPriorities, - PreferenceUnavailableMode preferenceUnavailable, - IReadOnlyList antagPreferences, - IReadOnlyList traitPreferences, - Dictionary loadouts) - : this(name, flavortext, species, age, sex, gender, appearance, spawnPriority, new Dictionary(jobPriorities), - preferenceUnavailable, new List(antagPreferences), new List(traitPreferences), new Dictionary(loadouts)) + public HumanoidCharacterProfile(HumanoidCharacterProfile other) + : this(other.Name, + other.FlavorText, + other.Species, + other.Age, + other.Sex, + other.Gender, + other.Appearance.Clone(), + other.SpawnPriority, + new Dictionary(other.JobPriorities), + other.PreferenceUnavailable, + new HashSet(other.AntagPreferences), + new HashSet(other.TraitPreferences), + new Dictionary(other.Loadouts)) { } @@ -113,23 +178,7 @@ namespace Content.Shared.Preferences /// Defaults to for the species. /// /// - public HumanoidCharacterProfile() : this( - "John Doe", - "", - SharedHumanoidAppearanceSystem.DefaultSpecies, - 18, - Sex.Male, - Gender.Male, - new HumanoidCharacterAppearance(), - SpawnPriorityPreference.None, - new Dictionary - { - {SharedGameTicker.FallbackOverflowJob, JobPriority.High} - }, - PreferenceUnavailableMode.SpawnAsOverflow, - new List(), - new List(), - new Dictionary()) + public HumanoidCharacterProfile() { } @@ -140,23 +189,10 @@ namespace Content.Shared.Preferences /// Humanoid character profile with default settings. public static HumanoidCharacterProfile DefaultWithSpecies(string species = SharedHumanoidAppearanceSystem.DefaultSpecies) { - return new( - "John Doe", - "", - species, - 18, - Sex.Male, - Gender.Male, - HumanoidCharacterAppearance.DefaultWithSpecies(species), - SpawnPriorityPreference.None, - new Dictionary - { - {SharedGameTicker.FallbackOverflowJob, JobPriority.High} - }, - PreferenceUnavailableMode.SpawnAsOverflow, - new List(), - new List(), - new Dictionary()); + return new() + { + Species = species, + }; } // TODO: This should eventually not be a visual change only. @@ -201,36 +237,17 @@ namespace Content.Shared.Preferences var name = GetName(species, gender); - return new HumanoidCharacterProfile(name, "", species, age, sex, gender, HumanoidCharacterAppearance.Random(species, sex), SpawnPriorityPreference.None, - new Dictionary - { - {SharedGameTicker.FallbackOverflowJob, JobPriority.High}, - }, PreferenceUnavailableMode.StayInLobby, new List(), new List(), new Dictionary()); + return new HumanoidCharacterProfile() + { + Name = name, + Sex = sex, + Age = age, + Gender = gender, + Species = species, + Appearance = HumanoidCharacterAppearance.Random(species, sex), + }; } - public string Name { get; private set; } - public string FlavorText { get; private set; } - public string Species { get; private set; } - - [DataField("age")] - public int Age { get; private set; } - - [DataField("sex")] - public Sex Sex { get; private set; } - - [DataField("gender")] - public Gender Gender { get; private set; } - - public ICharacterAppearance CharacterAppearance => Appearance; - - [DataField("appearance")] - public HumanoidCharacterAppearance Appearance { get; private set; } - public SpawnPriorityPreference SpawnPriority { get; private set; } - public IReadOnlyDictionary JobPriorities => _jobPriorities; - public IReadOnlyList AntagPreferences => _antagPreferences; - public IReadOnlyList TraitPreferences => _traitPreferences; - public PreferenceUnavailableMode PreferenceUnavailable { get; private set; } - public HumanoidCharacterProfile WithName(string name) { return new(this) { Name = name }; @@ -274,7 +291,10 @@ namespace Content.Shared.Preferences public HumanoidCharacterProfile WithJobPriorities(IEnumerable> jobPriorities) { - return new(this, new Dictionary(jobPriorities), _antagPreferences, _traitPreferences, _loadouts); + return new(this) + { + _jobPriorities = new Dictionary(jobPriorities), + }; } public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priority) @@ -288,7 +308,11 @@ namespace Content.Shared.Preferences { dictionary[jobId] = priority; } - return new(this, dictionary, _antagPreferences, _traitPreferences, _loadouts); + + return new(this) + { + _jobPriorities = dictionary, + }; } public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableMode mode) @@ -298,50 +322,47 @@ namespace Content.Shared.Preferences public HumanoidCharacterProfile WithAntagPreferences(IEnumerable antagPreferences) { - return new(this, _jobPriorities, new List(antagPreferences), _traitPreferences, _loadouts); + return new(this) + { + _antagPreferences = new HashSet(antagPreferences), + }; } public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref) { - var list = new List(_antagPreferences); + var list = new HashSet(_antagPreferences); if (pref) { - if (!list.Contains(antagId)) - { - list.Add(antagId); - } + list.Add(antagId); } else { - if (list.Contains(antagId)) - { - list.Remove(antagId); - } + list.Remove(antagId); } - return new(this, _jobPriorities, list, _traitPreferences, _loadouts); + return new(this) + { + _antagPreferences = list, + }; } public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref) { - var list = new List(_traitPreferences); + var list = new HashSet(_traitPreferences); - // TODO: Maybe just refactor this to HashSet? Same with _antagPreferences if (pref) { - if (!list.Contains(traitId)) - { - list.Add(traitId); - } + list.Add(traitId); } else { - if (list.Contains(traitId)) - { - list.Remove(traitId); - } + list.Remove(traitId); } - return new(this, _jobPriorities, _antagPreferences, list, _loadouts); + + return new(this) + { + _traitPreferences = list, + }; } public string Summary => @@ -498,10 +519,10 @@ namespace Content.Shared.Preferences PreferenceUnavailable = prefsUnavailableMode; _antagPreferences.Clear(); - _antagPreferences.AddRange(antags); + _antagPreferences.UnionWith(antags); _traitPreferences.Clear(); - _traitPreferences.AddRange(traits); + _traitPreferences.UnionWith(traits); // Checks prototypes exist for all loadouts and dump / set to default if not. var toRemove = new ValueList(); @@ -514,7 +535,7 @@ namespace Content.Shared.Preferences continue; } - loadouts.EnsureValid(session, collection); + loadouts.EnsureValid(this, session, collection); } foreach (var value in toRemove) @@ -540,27 +561,26 @@ namespace Content.Shared.Preferences public override bool Equals(object? obj) { - return obj is HumanoidCharacterProfile other && MemberwiseEquals(other); + return ReferenceEquals(this, obj) || obj is HumanoidCharacterProfile other && Equals(other); } public override int GetHashCode() { - return HashCode.Combine( - HashCode.Combine( - Name, - Species, - Age, - Sex, - Gender, - Appearance - ), - SpawnPriority, - PreferenceUnavailable, - _jobPriorities, - _antagPreferences, - _traitPreferences, - _loadouts - ); + var hashCode = new HashCode(); + hashCode.Add(_jobPriorities); + hashCode.Add(_antagPreferences); + hashCode.Add(_traitPreferences); + hashCode.Add(_loadouts); + hashCode.Add(Name); + hashCode.Add(FlavorText); + hashCode.Add(Species); + hashCode.Add(Age); + hashCode.Add((int)Sex); + hashCode.Add((int)Gender); + hashCode.Add(Appearance); + hashCode.Add((int)SpawnPriority); + hashCode.Add((int)PreferenceUnavailable); + return hashCode.ToHashCode(); } public void SetLoadout(RoleLoadout loadout) @@ -582,10 +602,12 @@ namespace Content.Shared.Preferences } copied[loadout.Role] = loadout.Clone(); - return new(this, _jobPriorities, _antagPreferences, _traitPreferences, copied); + var profile = Clone(); + profile._loadouts = copied; + return profile; } - public RoleLoadout GetLoadoutOrDefault(string id, IEntityManager entManager, IPrototypeManager protoManager) + public RoleLoadout GetLoadoutOrDefault(string id, ProtoId? species, IEntityManager entManager, IPrototypeManager protoManager) { if (!_loadouts.TryGetValue(id, out var loadout)) { @@ -596,5 +618,10 @@ namespace Content.Shared.Preferences loadout.SetDefault(protoManager); return loadout; } + + public HumanoidCharacterProfile Clone() + { + return new HumanoidCharacterProfile(this); + } } } diff --git a/Content.Shared/Preferences/Loadouts/Effects/GroupLoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/GroupLoadoutEffect.cs index 5a2cd87cc1..1be75f7dbc 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/GroupLoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/GroupLoadoutEffect.cs @@ -13,13 +13,13 @@ public sealed partial class GroupLoadoutEffect : LoadoutEffect [DataField(required: true)] public ProtoId Proto; - public override bool Validate(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) + public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) { var effectsProto = collection.Resolve().Index(Proto); foreach (var effect in effectsProto.Effects) { - if (!effect.Validate(loadout, session, collection, out reason)) + if (!effect.Validate(profile, loadout, session, collection, out reason)) return false; } diff --git a/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs index 4a750a5578..54576d3a53 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs @@ -15,7 +15,7 @@ public sealed partial class JobRequirementLoadoutEffect : LoadoutEffect [DataField(required: true)] public JobRequirement Requirement = default!; - public override bool Validate(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) + public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) { var manager = collection.Resolve(); var playtimes = manager.GetPlayTimes(session); diff --git a/Content.Shared/Preferences/Loadouts/Effects/LoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/LoadoutEffect.cs index 65694d52a1..f35b14e2e0 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/LoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/LoadoutEffect.cs @@ -11,6 +11,7 @@ public abstract partial class LoadoutEffect /// Tries to validate the effect. /// public abstract bool Validate( + HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, diff --git a/Content.Shared/Preferences/Loadouts/Effects/PointsCostLoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/PointsCostLoadoutEffect.cs index 3146ff6163..842b4cfc03 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/PointsCostLoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/PointsCostLoadoutEffect.cs @@ -11,6 +11,7 @@ public sealed partial class PointsCostLoadoutEffect : LoadoutEffect public int Cost = 1; public override bool Validate( + HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, diff --git a/Content.Shared/Preferences/Loadouts/Effects/SpeciesLoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/SpeciesLoadoutEffect.cs index 74673cbef3..8f886dd2ab 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/SpeciesLoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/SpeciesLoadoutEffect.cs @@ -1,6 +1,26 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Humanoid.Prototypes; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + namespace Content.Shared.Preferences.Loadouts.Effects; -public sealed class SpeciesLoadoutEffect +public sealed partial class SpeciesLoadoutEffect : LoadoutEffect { - + [DataField(required: true)] + public List> Species = new(); + + public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, + [NotNullWhen(false)] out FormattedMessage? reason) + { + if (Species.Contains(profile.Species)) + { + reason = null; + return true; + } + + reason = FormattedMessage.FromUnformatted(Loc.GetString("loadout-group-species-restriction")); + return false; + } } diff --git a/Content.Shared/Preferences/Loadouts/Loadout.cs b/Content.Shared/Preferences/Loadouts/Loadout.cs index 6a4373b621..dbe440f58b 100644 --- a/Content.Shared/Preferences/Loadouts/Loadout.cs +++ b/Content.Shared/Preferences/Loadouts/Loadout.cs @@ -6,8 +6,9 @@ namespace Content.Shared.Preferences.Loadouts; /// /// Specifies the selected prototype and custom data for a loadout. /// -[Serializable, NetSerializable] -public sealed class Loadout +[Serializable, NetSerializable, DataDefinition] +public sealed partial class Loadout { + [DataField] public ProtoId Prototype; } diff --git a/Content.Shared/Preferences/Loadouts/RoleLoadout.cs b/Content.Shared/Preferences/Loadouts/RoleLoadout.cs index e1c6f8395d..40e13f0edf 100644 --- a/Content.Shared/Preferences/Loadouts/RoleLoadout.cs +++ b/Content.Shared/Preferences/Loadouts/RoleLoadout.cs @@ -1,4 +1,6 @@ using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Shared.Humanoid.Prototypes; using Content.Shared.Random; using Robust.Shared.Collections; using Robust.Shared.Player; @@ -11,11 +13,13 @@ namespace Content.Shared.Preferences.Loadouts; /// /// Contains all of the selected data for a role's loadout. /// -[Serializable, NetSerializable] -public sealed class RoleLoadout +[Serializable, NetSerializable, DataDefinition] +public sealed partial class RoleLoadout : IEquatable { - public readonly ProtoId Role; + [DataField] + public ProtoId Role; + [DataField] public Dictionary, List> SelectedLoadouts = new(); /* @@ -44,7 +48,7 @@ public sealed class RoleLoadout /// /// Ensures all prototypes exist and effects can be applied. /// - public void EnsureValid(ICommonSession session, IDependencyCollection collection) + public void EnsureValid(HumanoidCharacterProfile profile, ICommonSession session, IDependencyCollection collection) { var groupRemove = new ValueList(); var protoManager = collection.Resolve(); @@ -81,7 +85,7 @@ public sealed class RoleLoadout } // Validate the loadout can be applied (e.g. points). - if (!IsValid(session, loadout.Prototype, collection, out _)) + if (!IsValid(profile, session, loadout.Prototype, collection, out _)) { loadouts.RemoveAt(i); continue; @@ -167,7 +171,7 @@ public sealed class RoleLoadout /// /// Returns whether a loadout is valid or not. /// - public bool IsValid(ICommonSession session, ProtoId loadout, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) + public bool IsValid(HumanoidCharacterProfile profile, ICommonSession session, ProtoId loadout, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) { reason = null; @@ -180,7 +184,7 @@ public sealed class RoleLoadout return false; } - if (!protoManager.TryIndex(Role, out var roleProto)) + if (!protoManager.HasIndex(Role)) { reason = FormattedMessage.FromUnformatted("loadouts-prototype-missing"); return false; @@ -190,7 +194,7 @@ public sealed class RoleLoadout foreach (var effect in loadoutProto.Effects) { - valid = valid && effect.Validate(this, session, collection, out reason); + valid = valid && effect.Validate(profile, this, session, collection, out reason); } return valid; @@ -257,4 +261,21 @@ public sealed class RoleLoadout return false; } + + public bool Equals(RoleLoadout? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Role.Equals(other.Role) && SelectedLoadouts.SequenceEqual(other.SelectedLoadouts) && Points == other.Points; + } + + public override bool Equals(object? obj) + { + return ReferenceEquals(this, obj) || obj is RoleLoadout other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Role, SelectedLoadouts, Points); + } } diff --git a/Resources/Locale/en-US/preferences/ui/character-setup-gui.ftl b/Resources/Locale/en-US/preferences/ui/character-setup-gui.ftl index bd80815e23..b85d7be38e 100644 --- a/Resources/Locale/en-US/preferences/ui/character-setup-gui.ftl +++ b/Resources/Locale/en-US/preferences/ui/character-setup-gui.ftl @@ -1,7 +1,6 @@ character-setup-gui-character-setup-label = Character setup character-setup-gui-character-setup-stats-button = Stats character-setup-gui-character-setup-rules-button = Rules -character-setup-gui-character-setup-save-button = Save character-setup-gui-character-setup-close-button = Close character-setup-gui-create-new-character-button = Create new slot... character-setup-gui-create-new-character-button-tooltip = A maximum of {$maxCharacters} characters are allowed. diff --git a/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl b/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl index 139d222f79..c7a24d5405 100644 --- a/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl +++ b/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl @@ -19,6 +19,7 @@ humanoid-profile-editor-pronouns-neuter-text = It / It humanoid-profile-editor-import-button = Import humanoid-profile-editor-export-button = Export humanoid-profile-editor-save-button = Save +humanoid-profile-editor-reset-button = Reset humanoid-profile-editor-spawn-priority-label = Spawn priority: humanoid-profile-editor-eyes-label = Eye color: humanoid-profile-editor-jobs-tab = Jobs diff --git a/Resources/Prototypes/Species/vox.yml b/Resources/Prototypes/Species/vox.yml index 53ac4258bc..e3fdb2bf08 100644 --- a/Resources/Prototypes/Species/vox.yml +++ b/Resources/Prototypes/Species/vox.yml @@ -1,7 +1,7 @@ - type: species id: Vox name: species-name-vox - roundStart: false # sad... + roundStart: false # sad prototype: MobVox sprites: MobVoxSprites markingLimits: MobVoxMarkingLimits From b7c447f1c57910a9408f21c414a5f71f36c95971 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 May 2024 23:19:28 +0000 Subject: [PATCH 26/68] Automatic changelog update --- Resources/Changelog/Changelog.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ae8a6fe600..c5e4d32fa2 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix expedition FTL. - type: Fix - id: 6086 - time: '2024-03-04T06:24:24.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25823 - author: Nairodian changes: - message: Welding masks can now be made in the Autolathe. @@ -3864,3 +3857,16 @@ id: 6585 time: '2024-05-11T15:42:11.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27905 +- author: metalgearsloth + changes: + - message: Add a reset button to character setup. + type: Add + - message: Actually get the import / export character buttons working. + type: Add + - message: Fix performance drop in the eye color picker. + type: Fix + - message: Fix instances where role timers don't load properly in character setup. + type: Fix + id: 6586 + time: '2024-05-11T23:18:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27576 From 829e12d26323a55d94639a213299d65ca716fa9d Mon Sep 17 00:00:00 2001 From: Ghagliiarghii <68826635+Ghagliiarghii@users.noreply.github.com> Date: Sat, 11 May 2024 21:18:17 -0400 Subject: [PATCH 27/68] Embed a few more Cryogenics chems in Guidebook (#27935) --- Resources/ServerInfo/Guidebook/Medical/Cryogenics.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/ServerInfo/Guidebook/Medical/Cryogenics.xml b/Resources/ServerInfo/Guidebook/Medical/Cryogenics.xml index f70f43c8a8..1b3961f4f7 100644 --- a/Resources/ServerInfo/Guidebook/Medical/Cryogenics.xml +++ b/Resources/ServerInfo/Guidebook/Medical/Cryogenics.xml @@ -43,8 +43,12 @@ Once things have been set up, you're going to require a specific medication, Cry The standard pressure for a gas pump is 100.325 kpa. Cryoxadone works at under 170K, but it is standard practice to set the freezer to 100K for faster freezing. + + + + From db2beafd5996aacfddda8ef5ab41cd7188b8a592 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 May 2024 21:23:48 -0400 Subject: [PATCH 28/68] Update Credits (#27938) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index 00d89d6d0d..5344b6a2db 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, casperr04, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, DuskyJay, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, Jark255, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, K-Dynamic, Kadeo64, KaiShibaa, kalane15, kalanosh, Keer-Sar, KEEYNy, Keikiru, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Kmc2000, Ko4ergaPunk, komunre, koteq, KrasnoshchekovPavel, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, LightVillet, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, MishaUnity, MisterMecky, Mith-randalf, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, Subversionary, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, waylon531, weaversam8, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem +0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, casperr04, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, DuskyJay, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, Jark255, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, K-Dynamic, Kadeo64, KaiShibaa, kalane15, kalanosh, Keer-Sar, KEEYNy, Keikiru, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Kmc2000, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, MishaUnity, MisterMecky, Mith-randalf, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, Subversionary, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, waylon531, weaversam8, whateverusername0, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem From cf148288a07809249b5569f8d77892284e634b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B6=D0=B5=D0=BA=D1=81=D0=BE=D0=BD=20=D0=9C=D0=B8?= =?UTF-8?q?=D1=81=D1=81=D0=B8=D1=81=D1=81=D0=B8=D0=BF=D0=BF=D0=B8?= Date: Sat, 11 May 2024 21:01:58 -0500 Subject: [PATCH 29/68] Add an admin smite for making people slip really far (and localize the admin smites better) (#27246) * Sliiiiiiiiiiiiiiiiiip * what * Localize! * antiterminate --- .../Systems/AdminVerbSystem.Smites.cs | 104 +++++++++++------- Content.Shared/Slippery/SlipperySystem.cs | 4 +- .../Locale/en-US/administration/smites.ftl | 48 +++++++- 3 files changed, 113 insertions(+), 43 deletions(-) diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index bda60e9449..ed60fe5448 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -37,6 +37,7 @@ using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; using Content.Shared.Nutrition.Components; using Content.Shared.Popups; +using Content.Shared.Slippery; using Content.Shared.Tabletop.Components; using Content.Shared.Tools.Systems; using Content.Shared.Verbs; @@ -77,6 +78,7 @@ public sealed partial class AdminVerbSystem [Dependency] private readonly SharedContentEyeSystem _eyeSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SuperBonkSystem _superBonkSystem = default!; + [Dependency] private readonly SlipperySystem _slipperySystem = default!; // All smite verbs have names so invokeverb works. private void AddSmiteVerbs(GetVerbsEvent args) @@ -95,7 +97,7 @@ public sealed partial class AdminVerbSystem Verb explode = new() { - Text = "Explode", + Text = "admin-smite-explode-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")), Act = () => @@ -115,7 +117,7 @@ public sealed partial class AdminVerbSystem Verb chess = new() { - Text = "Chess Dimension", + Text = "admin-smite-chess-dimension-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/Tabletop/chessboard.rsi"), "chessboard"), Act = () => @@ -143,7 +145,7 @@ public sealed partial class AdminVerbSystem { Verb flames = new() { - Text = "Set Alight", + Text = "admin-smite-set-alight-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Fire/fire.png")), Act = () => @@ -165,7 +167,7 @@ public sealed partial class AdminVerbSystem Verb monkey = new() { - Text = "Monkeyify", + Text = "admin-smite-monkeyify-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Animals/monkey.rsi"), "monkey"), Act = () => @@ -179,7 +181,7 @@ public sealed partial class AdminVerbSystem Verb disposalBin = new() { - Text = "Garbage Can", + Text = "admin-smite-electrocute-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Structures/Piping/disposal.rsi"), "disposal"), Act = () => @@ -196,7 +198,7 @@ public sealed partial class AdminVerbSystem { Verb hardElectrocute = new() { - Text = "Electrocute", + Text = "admin-smite-creampie-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Hands/Gloves/Color/yellow.rsi"), "icon"), Act = () => @@ -241,7 +243,7 @@ public sealed partial class AdminVerbSystem { Verb creamPie = new() { - Text = "Creampie", + Text = "admin-smite-remove-blood-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Consumable/Food/Baked/pie.rsi"), "plain-slice"), Act = () => @@ -258,7 +260,7 @@ public sealed partial class AdminVerbSystem { Verb bloodRemoval = new() { - Text = "Remove blood", + Text = "admin-smite-vomit-organs-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Fluids/tomato_splat.rsi"), "puddle-1"), Act = () => @@ -281,7 +283,7 @@ public sealed partial class AdminVerbSystem { Verb vomitOrgans = new() { - Text = "Vomit organs", + Text = "admin-smite-remove-hands-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Fluids/vomit_toxin.rsi"), "vomit_toxin-1"), Act = () => @@ -309,7 +311,7 @@ public sealed partial class AdminVerbSystem Verb handsRemoval = new() { - Text = "Remove hands", + Text = "admin-smite-remove-hand-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/remove-hands.png")), Act = () => @@ -331,7 +333,7 @@ public sealed partial class AdminVerbSystem Verb handRemoval = new() { - Text = "Remove hand", + Text = "admin-smite-pinball-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/remove-hand.png")), Act = () => @@ -354,7 +356,7 @@ public sealed partial class AdminVerbSystem Verb stomachRemoval = new() { - Text = "Stomach Removal", + Text = "admin-smite-yeet-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Species/Human/organs.rsi"), "stomach"), Act = () => @@ -374,7 +376,7 @@ public sealed partial class AdminVerbSystem Verb lungRemoval = new() { - Text = "Lungs Removal", + Text = "admin-smite-become-bread-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Species/Human/organs.rsi"), "lung-r"), Act = () => @@ -397,7 +399,7 @@ public sealed partial class AdminVerbSystem { Verb pinball = new() { - Text = "Pinball", + Text = "admin-smite-ghostkick-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/toys.rsi"), "basketball"), Act = () => @@ -431,7 +433,7 @@ public sealed partial class AdminVerbSystem Verb yeet = new() { - Text = "Yeet", + Text = "admin-smite-nyanify-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")), Act = () => @@ -462,7 +464,7 @@ public sealed partial class AdminVerbSystem Verb bread = new() { - Text = "Become Bread", + Text = "admin-smite-kill-sign-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Consumable/Food/Baked/bread.rsi"), "plain"), Act = () => @@ -476,7 +478,7 @@ public sealed partial class AdminVerbSystem Verb mouse = new() { - Text = "Become Mouse", + Text = "admin-smite-cluwne-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Animals/mouse.rsi"), "icon-0"), Act = () => @@ -492,7 +494,7 @@ public sealed partial class AdminVerbSystem { Verb ghostKick = new() { - Text = "Ghostkick", + Text = "admin-smite-anger-pointing-arrows-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/gavel.svg.192dpi.png")), Act = () => @@ -508,7 +510,7 @@ public sealed partial class AdminVerbSystem if (TryComp(args.Target, out var inventory)) { Verb nyanify = new() { - Text = "Nyanify", + Text = "admin-smite-dust-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Head/Hats/catears.rsi"), "icon"), Act = () => @@ -525,7 +527,7 @@ public sealed partial class AdminVerbSystem Verb killSign = new() { - Text = "Kill sign", + Text = "admin-smite-buffering-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Misc/killsign.rsi"), "icon"), Act = () => @@ -539,7 +541,7 @@ public sealed partial class AdminVerbSystem Verb cluwne = new() { - Text = "Cluwne", + Text = "admin-smite-become-instrument-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Mask/cluwne.rsi"), "icon"), @@ -555,7 +557,7 @@ public sealed partial class AdminVerbSystem Verb maiden = new() { - Text = "Maid", + Text = "admin-smite-remove-gravity-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Uniforms/Jumpskirt/janimaid.rsi"), "icon"), Act = () => @@ -575,7 +577,7 @@ public sealed partial class AdminVerbSystem Verb angerPointingArrows = new() { - Text = "Anger Pointing Arrows", + Text = "admin-smite-reptilian-species-swap-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Interface/Misc/pointing.rsi"), "pointing"), Act = () => @@ -589,7 +591,7 @@ public sealed partial class AdminVerbSystem Verb dust = new() { - Text = "Dust", + Text = "admin-smite-locker-stuff-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Materials/materials.rsi"), "ash"), Act = () => @@ -605,7 +607,7 @@ public sealed partial class AdminVerbSystem Verb youtubeVideoSimulation = new() { - Text = "Buffering", + Text = "admin-smite-headstand-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/Misc/buffering_smite_icon.png")), Act = () => @@ -619,7 +621,7 @@ public sealed partial class AdminVerbSystem Verb instrumentation = new() { - Text = "Become Instrument", + Text = "admin-smite-become-mouse-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/Instruments/h_synthesizer.rsi"), "icon"), Act = () => @@ -633,7 +635,7 @@ public sealed partial class AdminVerbSystem Verb noGravity = new() { - Text = "Remove gravity", + Text = "admin-smite-maid-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Machines/gravity_generator.rsi"), "off"), Act = () => @@ -650,7 +652,7 @@ public sealed partial class AdminVerbSystem Verb reptilian = new() { - Text = "Reptilian Species Swap", + Text = "admin-smite-zoom-in-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/toys.rsi"), "plushie_lizard"), Act = () => @@ -664,7 +666,7 @@ public sealed partial class AdminVerbSystem Verb locker = new() { - Text = "Locker stuff", + Text = "admin-smite-flip-eye-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Structures/Storage/closet.rsi"), "generic"), Act = () => @@ -686,7 +688,7 @@ public sealed partial class AdminVerbSystem Verb headstand = new() { - Text = "Headstand", + Text = "admin-smite-run-walk-swap-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/refresh.svg.192dpi.png")), Act = () => @@ -700,7 +702,7 @@ public sealed partial class AdminVerbSystem Verb zoomIn = new() { - Text = "Zoom in", + Text = "admin-smite-super-speed-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/zoom.png")), Act = () => @@ -715,7 +717,7 @@ public sealed partial class AdminVerbSystem Verb flipEye = new() { - Text = "Flip eye", + Text = "admin-smite-stomach-removal-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/flip.png")), Act = () => @@ -730,7 +732,7 @@ public sealed partial class AdminVerbSystem Verb runWalkSwap = new() { - Text = "Run Walk Swap", + Text = "admin-smite-speak-backwards-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/run-walk-swap.png")), Act = () => @@ -750,7 +752,7 @@ public sealed partial class AdminVerbSystem Verb backwardsAccent = new() { - Text = "Speak Backwards", + Text = "admin-smite-lung-removal-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/help-backwards.png")), Act = () => @@ -764,7 +766,7 @@ public sealed partial class AdminVerbSystem Verb disarmProne = new() { - Text = "Disarm Prone", + Text = "admin-smite-disarm-prone-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/Actions/disarm.png")), Act = () => @@ -778,7 +780,7 @@ public sealed partial class AdminVerbSystem Verb superSpeed = new() { - Text = "Super speed", + Text = "admin-smite-garbage-can-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/super_speed.png")), Act = () => @@ -797,7 +799,7 @@ public sealed partial class AdminVerbSystem //Bonk Verb superBonkLite = new() { - Text = "Super Bonk Lite", + Text = "admin-smite-super-bonk-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new("Structures/Furniture/Tables/glass.rsi"), "full"), Act = () => @@ -810,7 +812,7 @@ public sealed partial class AdminVerbSystem args.Verbs.Add(superBonkLite); Verb superBonk= new() { - Text = "Super Bonk", + Text = "admin-smite-super-bonk-lite-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new("Structures/Furniture/Tables/generic.rsi"), "full"), Act = () => @@ -821,5 +823,31 @@ public sealed partial class AdminVerbSystem Impact = LogImpact.Extreme, }; args.Verbs.Add(superBonk); + + Verb superslip = new() + { + Text = "admin-smite-super-slip-name", + Category = VerbCategory.Smite, + Icon = new SpriteSpecifier.Rsi(new("Objects/Specific/Janitorial/soap.rsi"), "omega-4"), + Act = () => + { + var hadSlipComponent = EnsureComp(args.Target, out SlipperyComponent slipComponent); + if (!hadSlipComponent) + { + slipComponent.SuperSlippery = true; + slipComponent.ParalyzeTime = 5; + slipComponent.LaunchForwardsMultiplier = 20; + } + + _slipperySystem.TrySlip(args.Target, slipComponent, args.Target, requiresContact: false); + if (!hadSlipComponent) + { + RemComp(args.Target, slipComponent); + } + }, + Impact = LogImpact.Extreme, + Message = Loc.GetString("admin-smite-super-slip-description") + }; + args.Verbs.Add(superslip); } } diff --git a/Content.Shared/Slippery/SlipperySystem.cs b/Content.Shared/Slippery/SlipperySystem.cs index ff8b597a0d..5b2a2dfe45 100644 --- a/Content.Shared/Slippery/SlipperySystem.cs +++ b/Content.Shared/Slippery/SlipperySystem.cs @@ -67,7 +67,7 @@ public sealed class SlipperySystem : EntitySystem && _statusEffects.CanApplyEffect(toSlip, "Stun"); //Should be KnockedDown instead? } - private void TrySlip(EntityUid uid, SlipperyComponent component, EntityUid other) + public void TrySlip(EntityUid uid, SlipperyComponent component, EntityUid other, bool requiresContact = true) { if (HasComp(other) && !component.SuperSlippery) return; @@ -89,7 +89,7 @@ public sealed class SlipperySystem : EntitySystem { _physics.SetLinearVelocity(other, physics.LinearVelocity * component.LaunchForwardsMultiplier, body: physics); - if (component.SuperSlippery) + if (component.SuperSlippery && requiresContact) { var sliding = EnsureComp(other); sliding.CollidingEntities.Add(uid); diff --git a/Resources/Locale/en-US/administration/smites.ftl b/Resources/Locale/en-US/administration/smites.ftl index ff3e3b0901..51c86dfb44 100644 --- a/Resources/Locale/en-US/administration/smites.ftl +++ b/Resources/Locale/en-US/administration/smites.ftl @@ -14,6 +14,48 @@ admin-smite-run-walk-swap-prompt = You have to press shift to run! admin-smite-super-speed-prompt = You move at mach 0.8! admin-smite-lung-removal-self = You can't breathe! +## Smite names + +admin-smite-explode-name = Explode +admin-smite-chess-dimension-name = Chess Dimension +admin-smite-set-alight-name = Set Alight +admin-smite-monkeyify-name = Monkeyify +admin-smite-electrocute-name = Garbage Can +admin-smite-creampie-name = Electrocute +admin-smite-remove-blood-name = Creampie +admin-smite-vomit-organs-name = Remove blood +admin-smite-remove-hands-name = Vomit organs +admin-smite-remove-hand-name = Remove hands +admin-smite-pinball-name = Remove hand +admin-smite-yeet-name = Stomach Removal +admin-smite-become-bread-name = Lungs Removal +admin-smite-ghostkick-name = Pinball +admin-smite-nyanify-name = Yeet +admin-smite-kill-sign-name = Become Bread +admin-smite-cluwne-name = Become Mouse +admin-smite-anger-pointing-arrows-name = Ghostkick +admin-smite-dust-name = Nyanify +admin-smite-buffering-name = Kill sign +admin-smite-become-instrument-name = Cluwne +admin-smite-remove-gravity-name = Maid +admin-smite-reptilian-species-swap-name = Anger Pointing Arrows +admin-smite-locker-stuff-name = Dust +admin-smite-headstand-name = Buffering +admin-smite-become-mouse-name = Become Instrument +admin-smite-maid-name = Remove gravity +admin-smite-zoom-in-name = Reptilian Species Swap +admin-smite-flip-eye-name = Locker stuff +admin-smite-run-walk-swap-name = Headstand +admin-smite-super-speed-name = Zoom in +admin-smite-stomach-removal-name = Flip eye +admin-smite-speak-backwards-name = Run Walk Swap +admin-smite-lung-removal-name = Speak Backwards +admin-smite-disarm-prone-name = Disarm Prone +admin-smite-garbage-can-name = Super speed +admin-smite-super-bonk-name = Super Bonk Lite +admin-smite-super-bonk-lite-name = Super Bonk +admin-smite-terminate-name = Terminate +admin-smite-super-slip-name = Super Slip ## Smite descriptions @@ -21,12 +63,12 @@ admin-smite-explode-description = Explode them. admin-smite-chess-dimension-description = Banishment to the Chess Dimension. admin-smite-set-alight-description = Makes them burn. admin-smite-monkeyify-description = Turns the target into a monkey. -admin-smite-lung-cancer-description = Stage IIIA Lung Cancer, for when they really like the hit show Breaking Bad. admin-smite-electrocute-description = Electrocutes them, rendering anything they were wearing useless. admin-smite-creampie-description = A creampie, condensed into a button. admin-smite-remove-blood-description = Removes all of their blood, messily. admin-smite-vomit-organs-description = Causes them to vomit, organs included. admin-smite-remove-hands-description = Removes their hands. +admin-smite-remove-hand-description = Removes only one of their hands instead of all of them. admin-smite-pinball-description = Turns them into a super bouncy ball, flinging them around until they clip through the station into the abyss. admin-smite-yeet-description = Banishes them into the depths of space by turning on no-clip and tossing them. admin-smite-become-bread-description = It turns them into bread. Really, that's all it does. @@ -42,7 +84,6 @@ admin-smite-remove-gravity-description = Grants them anti-gravity. admin-smite-reptilian-species-swap-description = It changes their species to Reptilian. Useful for people who were being space racist. admin-smite-locker-stuff-description = Stuffs them in a (welded) locker. admin-smite-headstand-description = Vertically flips their sprite. -admin-smite-plasma-internals-description = Replaces the contents of their internals with plasma. admin-smite-become-mouse-description = They become a mouse. Squeak! admin-smite-maid-description = Forcibly converts them into a janitorial cat maid. This is actual torture for some players, use it wisely. admin-smite-zoom-in-description = Zooms in their view so that they can no longer see their surroundings. @@ -52,11 +93,12 @@ admin-smite-super-speed-description = Makes them really fast, causing them to tu admin-smite-stomach-removal-description = Removes their stomach, rendering them unable to eat. admin-smite-speak-backwards-description = Forces them to speak backwards, so they can't call for help. admin-smite-lung-removal-description = Removes their lungs, drowning them. -admin-smite-remove-hand-description = Removes only one of their hands instead of all of them. admin-smite-disarm-prone-description = Makes them get disarmed 100% of the time and cuffed instantly. admin-smite-garbage-can-description = Turn them into a garbage bin to emphasize what they remind you of. admin-smite-super-bonk-description = Slams them on every single table on the Station and beyond. admin-smite-super-bonk-lite-description= Slams them on every single table on the Station and beyond. Stops when the target is dead. +admin-smite-terminate-description = Creates a Terminator ghost role with the sole objective of killing them. +admin-smite-super-slip-description = Slips them really, really hard. ## Tricks descriptions From 1596e04d0f320ce5cc13296a53fbda868d2047a8 Mon Sep 17 00:00:00 2001 From: Kara Date: Sat, 11 May 2024 20:13:58 -0700 Subject: [PATCH 30/68] Resolve all non-obsoleting warnings in content (#27934) * Resolve all non-obsoleting warnings in content * Update ClientGameTicker.cs * Update SkeletonAccentSystem.cs * Update BwoinkSystem.cs --- .../Access/UI/IdCardConsoleBoundUserInterface.cs | 1 - .../Audio/Jukebox/JukeboxBoundUserInterface.cs | 1 - Content.Client/RCD/AlignRCDConstruction.cs | 13 ++++--------- .../Robotics/UI/RoboticsConsoleWindow.xaml.cs | 1 - Content.Client/Shuttles/UI/DockObject.xaml.cs | 4 ++++ Content.IntegrationTests/Tests/RoundEndTest.cs | 10 +++++----- .../Access/Systems/AccessOverriderSystem.cs | 1 - .../Administration/Systems/BwoinkSystem.cs | 7 +++++-- Content.Server/Anomaly/AnomalySystem.cs | 3 --- .../Atmos/EntitySystems/AirFilterSystem.cs | 1 - .../Chemistry/EntitySystems/HypospraySystem.cs | 1 - .../Unit/EntitySystems/DisposalUnitSystem.cs | 1 - Content.Server/Geras/GerasSystem.cs | 1 - Content.Server/Ghost/GhostSystem.cs | 1 - Content.Server/Ghost/Roles/GhostRoleSystem.cs | 1 - Content.Server/Ghost/Roles/UI/GhostRolesEui.cs | 2 +- Content.Server/Hands/Systems/HandsSystem.cs | 1 - Content.Server/IdentityManagement/IdentitySystem.cs | 2 +- Content.Server/Kitchen/EntitySystems/SharpSystem.cs | 7 ------- Content.Server/Ninja/Systems/NinjaGlovesSystem.cs | 1 - Content.Server/Power/Components/ChargerComponent.cs | 5 ----- .../Managers/ServerPreferencesManager.cs | 1 - Content.Server/Salvage/SpawnSalvageMissionJob.cs | 2 -- .../Speech/EntitySystems/SkeletonAccentSystem.cs | 7 +++++-- Content.Server/Station/Systems/StationSystem.cs | 6 ------ .../Traitor/Uplink/Commands/AddUplinkCommand.cs | 1 - Content.Server/Wires/WiresSystem.cs | 2 +- .../Equipment/Systems/TraversalDistorterSystem.cs | 1 - .../Zombies/InitialInfectedExemptComponent.cs | 2 +- .../Access/Components/IdCardConsoleComponent.cs | 2 -- .../EntitySystems/SharedSolutionContainerSystem.cs | 6 +++--- .../EntitySystems/SolutionTransferSystem.cs | 1 - Content.Shared/Mind/SharedMindSystem.cs | 1 - Content.Shared/Pinpointer/SharedNavMapSystem.cs | 1 - Content.Shared/Plunger/Systems/PlungerSystem.cs | 4 ++-- 35 files changed, 32 insertions(+), 70 deletions(-) diff --git a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs index 5b7011c195..a321b4121e 100644 --- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs +++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs @@ -1,6 +1,5 @@ using Content.Shared.Access; using Content.Shared.Access.Components; -using Content.Shared.Access; using Content.Shared.Access.Systems; using Content.Shared.Containers.ItemSlots; using Content.Shared.CrewManifest; diff --git a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs index 072730d65d..60fe339069 100644 --- a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs +++ b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs @@ -9,7 +9,6 @@ namespace Content.Client.Audio.Jukebox; public sealed class JukeboxBoundUserInterface : BoundUserInterface { - [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; [ViewVariables] diff --git a/Content.Client/RCD/AlignRCDConstruction.cs b/Content.Client/RCD/AlignRCDConstruction.cs index da7b22c91a..d5425425a7 100644 --- a/Content.Client/RCD/AlignRCDConstruction.cs +++ b/Content.Client/RCD/AlignRCDConstruction.cs @@ -16,9 +16,9 @@ public sealed class AlignRCDConstruction : PlacementMode { [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly SharedMapSystem _mapSystem = default!; - [Dependency] private readonly RCDSystem _rcdSystem = default!; - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + private readonly SharedMapSystem _mapSystem; + private readonly RCDSystem _rcdSystem; + private readonly SharedTransformSystem _transformSystem; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IStateManager _stateManager = default!; @@ -32,12 +32,7 @@ public sealed class AlignRCDConstruction : PlacementMode /// public AlignRCDConstruction(PlacementManager pMan) : base(pMan) { - var dependencies = IoCManager.Instance!; - _entityManager = dependencies.Resolve(); - _mapManager = dependencies.Resolve(); - _playerManager = dependencies.Resolve(); - _stateManager = dependencies.Resolve(); - + IoCManager.InjectDependencies(this); _mapSystem = _entityManager.System(); _rcdSystem = _entityManager.System(); _transformSystem = _entityManager.System(); diff --git a/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs b/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs index 3555099370..367114f2aa 100644 --- a/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs +++ b/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs @@ -17,7 +17,6 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow { [Dependency] private readonly IEntityManager _entMan = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; private readonly LockSystem _lock; private readonly SpriteSystem _sprite; diff --git a/Content.Client/Shuttles/UI/DockObject.xaml.cs b/Content.Client/Shuttles/UI/DockObject.xaml.cs index 9dae6b7a4d..d7d565a23e 100644 --- a/Content.Client/Shuttles/UI/DockObject.xaml.cs +++ b/Content.Client/Shuttles/UI/DockObject.xaml.cs @@ -1,6 +1,7 @@ using System.Text; using Content.Shared.Shuttles.BUIStates; using Content.Shared.Shuttles.Systems; +using JetBrains.Annotations; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; @@ -12,7 +13,10 @@ namespace Content.Client.Shuttles.UI; [GenerateTypedNameReferences] public sealed partial class DockObject : PanelContainer { + [PublicAPI] public event Action? UndockPressed; + + [PublicAPI] public event Action? ViewPressed; public BoxContainer ContentsContainer => Contents; diff --git a/Content.IntegrationTests/Tests/RoundEndTest.cs b/Content.IntegrationTests/Tests/RoundEndTest.cs index 1ddddf66bc..6978085640 100644 --- a/Content.IntegrationTests/Tests/RoundEndTest.cs +++ b/Content.IntegrationTests/Tests/RoundEndTest.cs @@ -12,7 +12,7 @@ namespace Content.IntegrationTests.Tests { private sealed class RoundEndTestSystem : EntitySystem { - public int Count; + public int RoundCount; public override void Initialize() { @@ -22,7 +22,7 @@ namespace Content.IntegrationTests.Tests private void OnRoundEnd(RoundEndSystemChangedEvent ev) { - Interlocked.Increment(ref Count); + Interlocked.Increment(ref RoundCount); } } @@ -43,7 +43,7 @@ namespace Content.IntegrationTests.Tests var ticker = sysManager.GetEntitySystem(); var roundEndSystem = sysManager.GetEntitySystem(); var sys = server.System(); - sys.Count = 0; + sys.RoundCount = 0; await server.WaitAssertion(() => { @@ -128,8 +128,8 @@ namespace Content.IntegrationTests.Tests async Task WaitForEvent() { var timeout = Task.Delay(TimeSpan.FromSeconds(10)); - var currentCount = Thread.VolatileRead(ref sys.Count); - while (currentCount == Thread.VolatileRead(ref sys.Count) && !timeout.IsCompleted) + var currentCount = Thread.VolatileRead(ref sys.RoundCount); + while (currentCount == Thread.VolatileRead(ref sys.RoundCount) && !timeout.IsCompleted) { await pair.RunTicksSync(5); } diff --git a/Content.Server/Access/Systems/AccessOverriderSystem.cs b/Content.Server/Access/Systems/AccessOverriderSystem.cs index bc038fe4ff..c7b20513f4 100644 --- a/Content.Server/Access/Systems/AccessOverriderSystem.cs +++ b/Content.Server/Access/Systems/AccessOverriderSystem.cs @@ -28,7 +28,6 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; public override void Initialize() { diff --git a/Content.Server/Administration/Systems/BwoinkSystem.cs b/Content.Server/Administration/Systems/BwoinkSystem.cs index 2e236688ac..a07115544b 100644 --- a/Content.Server/Administration/Systems/BwoinkSystem.cs +++ b/Content.Server/Administration/Systems/BwoinkSystem.cs @@ -25,7 +25,7 @@ using Robust.Shared.Utility; namespace Content.Server.Administration.Systems { [UsedImplicitly] - public sealed class BwoinkSystem : SharedBwoinkSystem + public sealed partial class BwoinkSystem : SharedBwoinkSystem { [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!; @@ -36,6 +36,9 @@ namespace Content.Server.Administration.Systems [Dependency] private readonly SharedMindSystem _minds = default!; [Dependency] private readonly IAfkManager _afkManager = default!; + [GeneratedRegex(@"^https://discord\.com/api/webhooks/(\d+)/((?!.*/).*)$")] + private static partial Regex DiscordRegex(); + private ISawmill _sawmill = default!; private readonly HttpClient _httpClient = new(); private string _webhookUrl = string.Empty; @@ -157,7 +160,7 @@ namespace Content.Server.Administration.Systems return; // Basic sanity check and capturing webhook ID and token - var match = Regex.Match(url, @"^https://discord\.com/api/webhooks/(\d+)/((?!.*/).*)$"); + var match = DiscordRegex().Match(url); if (!match.Success) { diff --git a/Content.Server/Anomaly/AnomalySystem.cs b/Content.Server/Anomaly/AnomalySystem.cs index bae101de87..73658a0320 100644 --- a/Content.Server/Anomaly/AnomalySystem.cs +++ b/Content.Server/Anomaly/AnomalySystem.cs @@ -42,9 +42,6 @@ public sealed partial class AnomalySystem : SharedAnomalySystem [Dependency] private readonly RadiationSystem _radiation = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly IComponentFactory _componentFactory = default!; - [Dependency] private readonly ISerializationManager _serialization = default!; - [Dependency] private readonly IEntityManager _entity = default!; public const float MinParticleVariation = 0.8f; public const float MaxParticleVariation = 1.2f; diff --git a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs index 2ab15cfb17..c3344c830c 100644 --- a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs @@ -12,7 +12,6 @@ namespace Content.Server.Atmos.EntitySystems; public sealed class AirFilterSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphere = default!; - [Dependency] private readonly IMapManager _map = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() diff --git a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs index dfbe45c035..56cc0f9670 100644 --- a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs +++ b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs @@ -25,7 +25,6 @@ public sealed class HypospraySystem : SharedHypospraySystem { [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly InteractionSystem _interaction = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; public override void Initialize() { diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index b970ae4b0d..7f4ee87ee1 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -42,7 +42,6 @@ namespace Content.Server.Disposal.Unit.EntitySystems; public sealed class DisposalUnitSystem : SharedDisposalUnitSystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; diff --git a/Content.Server/Geras/GerasSystem.cs b/Content.Server/Geras/GerasSystem.cs index e7999d64d8..f83f7c1999 100644 --- a/Content.Server/Geras/GerasSystem.cs +++ b/Content.Server/Geras/GerasSystem.cs @@ -11,7 +11,6 @@ namespace Content.Server.Geras; public sealed class GerasSystem : SharedGerasSystem { [Dependency] private readonly PolymorphSystem _polymorphSystem = default!; - [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; [Dependency] private readonly ActionsSystem _actionsSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index b1fb67cce7..f4e6a4d607 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -44,7 +44,6 @@ namespace Content.Server.Ghost [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; - [Dependency] private readonly IMapManager _mapManager = default!; private EntityQuery _ghostQuery; private EntityQuery _physicsQuery; diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index 5b9d6ae1ad..c198b54b1b 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -30,7 +30,6 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; using Content.Server.Popups; using Content.Shared.Verbs; -using Robust.Shared.Prototypes; using Robust.Shared.Collections; namespace Content.Server.Ghost.Roles diff --git a/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs b/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs index fc73fc3454..c1e39919a2 100644 --- a/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs +++ b/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs @@ -6,7 +6,7 @@ namespace Content.Server.Ghost.Roles.UI { public sealed class GhostRolesEui : BaseEui { - [Dependency] private readonly GhostRoleSystem _ghostRoleSystem; + private readonly GhostRoleSystem _ghostRoleSystem; public GhostRolesEui() { diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 4c6b80bd98..feae130ce8 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -33,7 +33,6 @@ namespace Content.Server.Hands.Systems [Dependency] private readonly StackSystem _stackSystem = default!; [Dependency] private readonly VirtualItemSystem _virtualItemSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly PullingSystem _pullingSystem = default!; [Dependency] private readonly ThrowingSystem _throwingSystem = default!; diff --git a/Content.Server/IdentityManagement/IdentitySystem.cs b/Content.Server/IdentityManagement/IdentitySystem.cs index a959b96ef9..4766b89172 100644 --- a/Content.Server/IdentityManagement/IdentitySystem.cs +++ b/Content.Server/IdentityManagement/IdentitySystem.cs @@ -19,7 +19,7 @@ namespace Content.Server.IdentityManagement; /// /// Responsible for updating the identity of an entity on init or clothing equip/unequip. /// -public class IdentitySystem : SharedIdentitySystem +public sealed class IdentitySystem : SharedIdentitySystem { [Dependency] private readonly IdCardSystem _idCard = default!; [Dependency] private readonly IAdminLogManager _adminLog = default!; diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index 3c1e89b1f2..431e438fd8 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -3,24 +3,17 @@ using Content.Server.Kitchen.Components; using Content.Server.Nutrition.EntitySystems; using Content.Shared.Body.Components; using Content.Shared.Administration.Logs; -using Content.Shared.Body.Components; using Content.Shared.Database; using Content.Shared.Interaction; using Content.Shared.Nutrition.Components; -using Content.Server.Nutrition.EntitySystems; using Content.Shared.Popups; using Content.Shared.Storage; using Content.Shared.Verbs; using Content.Shared.Destructible; using Content.Shared.DoAfter; -using Content.Shared.Interaction; using Content.Shared.Kitchen; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition.Components; -using Content.Shared.Popups; -using Content.Shared.Storage; -using Content.Shared.Verbs; using Robust.Server.Containers; using Robust.Shared.Random; using Robust.Shared.Utility; diff --git a/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs b/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs index 2c0f6c63e3..ac76ae6b77 100644 --- a/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs +++ b/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs @@ -19,7 +19,6 @@ 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 MindSystem _mind = default!; [Dependency] private readonly SharedStunProviderSystem _stunProvider = default!; [Dependency] private readonly SpaceNinjaSystem _ninja = default!; diff --git a/Content.Server/Power/Components/ChargerComponent.cs b/Content.Server/Power/Components/ChargerComponent.cs index e45ded071c..4a3c83ae03 100644 --- a/Content.Server/Power/Components/ChargerComponent.cs +++ b/Content.Server/Power/Components/ChargerComponent.cs @@ -1,10 +1,5 @@ using Content.Shared.Power; using Content.Shared.Whitelist; -using Content.Shared.Power; -using Content.Shared.Whitelist; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; namespace Content.Server.Power.Components { diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index 7a80757435..1aad61715b 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -29,7 +29,6 @@ namespace Content.Server.Preferences.Managers [Dependency] private readonly IServerDbManager _db = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IDependencyCollection _dependencies = default!; - [Dependency] private readonly IPrototypeManager _protos = default!; // Cache player prefs on the server so we don't need as much async hell related to them. private readonly Dictionary _cachedPlayerPrefs = diff --git a/Content.Server/Salvage/SpawnSalvageMissionJob.cs b/Content.Server/Salvage/SpawnSalvageMissionJob.cs index 47123e9784..ce844e57a1 100644 --- a/Content.Server/Salvage/SpawnSalvageMissionJob.cs +++ b/Content.Server/Salvage/SpawnSalvageMissionJob.cs @@ -34,8 +34,6 @@ using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.Utility; using Content.Server.Shuttles.Components; -using Content.Shared.Coordinates; -using Content.Shared.Shuttles.Components; namespace Content.Server.Salvage; diff --git a/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs b/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs index ead9b4382e..d143c25fdb 100644 --- a/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs +++ b/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs @@ -4,10 +4,13 @@ using Robust.Shared.Random; namespace Content.Server.Speech.EntitySystems; -public sealed class SkeletonAccentSystem : EntitySystem +public sealed partial class SkeletonAccentSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; + [GeneratedRegex(@"(? DirectReplacements = new() { { "fuck you", "I've got a BONE to pick with you" }, @@ -45,7 +48,7 @@ public sealed class SkeletonAccentSystem : EntitySystem // Character manipulations: // At the start of words, any non-vowel + "one" becomes "bone", e.g. tone -> bone ; lonely -> bonely; clone -> clone (remains unchanged). - msg = Regex.Replace(msg, @"(? public override void Initialize() { diff --git a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs index 79192f6b49..63be36b360 100644 --- a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs +++ b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs @@ -12,7 +12,6 @@ namespace Content.Server.Traitor.Uplink.Commands [AdminCommand(AdminFlags.Admin)] public sealed class AddUplinkCommand : IConsoleCommand { - [Dependency] private readonly IConfigurationManager _cfgManager = default!; [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index 5d31a4d144..c643759f50 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -348,7 +348,7 @@ public sealed class WiresSystem : SharedWiresSystem } } - private class ActiveWireAction + private sealed class ActiveWireAction { /// /// The wire action's ID. This is so that once the action is finished, diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs index 2092f79f05..d277792243 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs @@ -11,7 +11,6 @@ namespace Content.Server.Xenoarchaeology.Equipment.Systems; public sealed class TraversalDistorterSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly PopupSystem _popup = default!; /// public override void Initialize() diff --git a/Content.Server/Zombies/InitialInfectedExemptComponent.cs b/Content.Server/Zombies/InitialInfectedExemptComponent.cs index 46077935c6..f2dfda3f87 100644 --- a/Content.Server/Zombies/InitialInfectedExemptComponent.cs +++ b/Content.Server/Zombies/InitialInfectedExemptComponent.cs @@ -1,7 +1,7 @@ namespace Content.Server.Zombies; [RegisterComponent] -public partial class InitialInfectedExemptComponent : Component +public sealed partial class InitialInfectedExemptComponent : Component { } diff --git a/Content.Shared/Access/Components/IdCardConsoleComponent.cs b/Content.Shared/Access/Components/IdCardConsoleComponent.cs index deeafe2793..4f1c27fb4d 100644 --- a/Content.Shared/Access/Components/IdCardConsoleComponent.cs +++ b/Content.Shared/Access/Components/IdCardConsoleComponent.cs @@ -3,8 +3,6 @@ using Content.Shared.Containers.ItemSlots; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; -using Robust.Shared.Prototypes; namespace Content.Shared.Access.Components; diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index fdb2f550f9..5e58a0944a 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -60,7 +60,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem [Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!; [Dependency] protected readonly SharedHandsSystem Hands = default!; [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!; - [Dependency] protected readonly MetaDataSystem MetaData = default!; + [Dependency] protected readonly MetaDataSystem MetaDataSys = default!; [Dependency] protected readonly INetManager NetManager = default!; public override void Initialize() @@ -1123,7 +1123,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem Dirty(uid, container); return solution; } - + private Entity SpawnSolutionUninitialized(ContainerSlot container, string name, FixedPoint2 maxVol, Solution prototype) { var coords = new EntityCoordinates(container.Owner, Vector2.Zero); @@ -1135,7 +1135,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem var relation = new ContainedSolutionComponent() { Container = container.Owner, ContainerName = name }; AddComp(uid, relation); - MetaData.SetEntityName(uid, $"solution - {name}"); + MetaDataSys.SetEntityName(uid, $"solution - {name}"); ContainerSystem.Insert(uid, container, force: true); return (uid, solution, relation); diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs index 3bea79d345..b12778262c 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs @@ -17,7 +17,6 @@ namespace Content.Shared.Chemistry.EntitySystems; /// public sealed class SolutionTransferSystem : EntitySystem { - [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; diff --git a/Content.Shared/Mind/SharedMindSystem.cs b/Content.Shared/Mind/SharedMindSystem.cs index 7887b8f9b2..bf1065c1b1 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -26,7 +26,6 @@ public abstract class SharedMindSystem : EntitySystem [Dependency] private readonly SharedObjectivesSystem _objectives = default!; [Dependency] private readonly SharedPlayerSystem _player = default!; [Dependency] private readonly MetaDataSystem _metadata = default!; - [Dependency] private readonly ISharedPlayerManager _playerMan = default!; [ViewVariables] protected readonly Dictionary UserMinds = new(); diff --git a/Content.Shared/Pinpointer/SharedNavMapSystem.cs b/Content.Shared/Pinpointer/SharedNavMapSystem.cs index 0edcd5a437..ffe81c2d0e 100644 --- a/Content.Shared/Pinpointer/SharedNavMapSystem.cs +++ b/Content.Shared/Pinpointer/SharedNavMapSystem.cs @@ -23,7 +23,6 @@ public abstract class SharedNavMapSystem : EntitySystem public const int FloorMask = AllDirMask << (int) NavMapChunkType.Floor; [Robust.Shared.IoC.Dependency] private readonly TagSystem _tagSystem = default!; - [Robust.Shared.IoC.Dependency] private readonly IGameTiming _gameTiming = default!; private readonly string[] _wallTags = ["Wall", "Window"]; private EntityQuery _doorQuery; diff --git a/Content.Shared/Plunger/Systems/PlungerSystem.cs b/Content.Shared/Plunger/Systems/PlungerSystem.cs index 57bd77a7d9..fbefad9f6e 100644 --- a/Content.Shared/Plunger/Systems/PlungerSystem.cs +++ b/Content.Shared/Plunger/Systems/PlungerSystem.cs @@ -17,8 +17,8 @@ namespace Content.Shared.Plunger.Systems; /// public sealed class PlungerSystem : EntitySystem { - [Dependency] protected readonly IPrototypeManager _proto = default!; - [Dependency] protected readonly IRobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; From 8938e1d8b25d903355a9e058bbb12904a270ae06 Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Sun, 12 May 2024 07:30:17 -0700 Subject: [PATCH 31/68] Replace AttachToGridOrMap with DropNextTo (#27950) --- .../EntitySystems/SliceableFoodSystem.cs | 22 ++++--------------- .../Body/Systems/SharedBodySystem.Body.cs | 6 +++-- .../EntitySystems/SharedHandsSystem.Drop.cs | 11 +++------- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs index ea422afdf0..f5d434090e 100644 --- a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs @@ -110,15 +110,8 @@ namespace Content.Server.Nutrition.EntitySystems // try putting the slice into the container if the food being sliced is in a container! // this lets you do things like slice a pizza up inside of a hot food cart without making a food-everywhere mess - if (_containerSystem.TryGetContainingContainer(uid, out var container) && _containerSystem.CanInsert(sliceUid, container)) - { - _containerSystem.Insert(sliceUid, container); - } - else // puts it down "right-side up" - { - _xformSystem.AttachToGridOrMap(sliceUid); - _xformSystem.SetLocalRotation(sliceUid, 0); - } + _xformSystem.DropNextTo(sliceUid, (uid, transform)); + _xformSystem.SetLocalRotation(sliceUid, 0); return sliceUid; } @@ -143,15 +136,8 @@ namespace Content.Server.Nutrition.EntitySystems var trashUid = Spawn(foodComp.Trash, _xformSystem.GetMapCoordinates(uid)); // try putting the trash in the food's container too, to be consistent with slice spawning? - if (_containerSystem.TryGetContainingContainer(uid, out var container) && _containerSystem.CanInsert(trashUid, container)) - { - _containerSystem.Insert(trashUid, container); - } - else // puts it down "right-side up" - { - _xformSystem.AttachToGridOrMap(trashUid); - _xformSystem.SetLocalRotation(trashUid, 0); - } + _xformSystem.DropNextTo(trashUid, uid); + _xformSystem.SetLocalRotation(trashUid, 0); QueueDel(uid); } diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index 1a35afdbe0..250f90db8f 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -322,15 +322,17 @@ public partial class SharedBodySystem launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone); } } + + var bodyTransform = Transform(bodyId); if (TryComp(bodyId, out var inventory)) { foreach (var item in _inventory.GetHandOrInventoryEntities(bodyId)) { - SharedTransform.AttachToGridOrMap(item); + SharedTransform.DropNextTo(item, (bodyId, bodyTransform)); gibs.Add(item); } } - _audioSystem.PlayPredicted(gibSoundOverride, Transform(bodyId).Coordinates, null); + _audioSystem.PlayPredicted(gibSoundOverride, bodyTransform.Coordinates, null); return gibs; } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs index 7b169b5d0a..4d21e40a98 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs @@ -120,17 +120,12 @@ public abstract partial class SharedHandsSystem return true; var userXform = Transform(uid); - var isInContainer = ContainerSystem.IsEntityInContainer(uid); + var isInContainer = ContainerSystem.IsEntityOrParentInContainer(uid, xform: userXform); if (targetDropLocation == null || isInContainer) { - // If user is in a container, drop item into that container. Otherwise, attach to grid or map.\ - // TODO recursively check upwards for containers - - if (!isInContainer - || !ContainerSystem.TryGetContainingContainer(userXform.ParentUid, uid, out var container, skipExistCheck: true) - || !ContainerSystem.Insert((entity, itemXform), container)) - TransformSystem.AttachToGridOrMap(entity, itemXform); + // If user is in a container, drop item into that container. Otherwise, attach to grid or map. + TransformSystem.DropNextTo((entity, itemXform), (uid, userXform)); return true; } From 855234aa309b0329e1435466b09b85448fa31178 Mon Sep 17 00:00:00 2001 From: Kara Date: Sun, 12 May 2024 07:31:54 -0700 Subject: [PATCH 32/68] Resolve `'TransformComponent.MapPosition' is obsolete` in content (#27939) * Resolve `'TransformComponent.MapPosition' is obsolete: 'Use TransformSystem.GetMapCoordinates'` in content * build? --- .../UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs | 6 ++++-- Content.Client/GPS/UI/HandheldGpsStatusControl.cs | 5 ++++- Content.Client/MouseRotator/MouseRotatorSystem.cs | 3 ++- .../Radiation/Overlays/RadiationPulseOverlay.cs | 9 +++++++-- Content.Client/Sprite/SpriteFadeSystem.cs | 4 +++- Content.Client/Tabletop/TabletopSystem.cs | 5 +++-- .../UserInterface/Systems/Chat/ChatUIController.cs | 5 +++-- Content.Client/Verbs/VerbSystem.cs | 5 +++-- Content.Client/Weapons/Melee/MeleeArcOverlay.cs | 6 ++++-- Content.Client/Weapons/Melee/MeleeSpreadCommand.cs | 3 ++- Content.Client/Weapons/Melee/MeleeWeaponSystem.cs | 2 +- Content.Client/Weapons/Ranged/GunSpreadOverlay.cs | 6 ++++-- Content.Client/Weapons/Ranged/Systems/GunSystem.cs | 3 ++- Content.IntegrationTests/Tests/Doors/AirlockTest.cs | 2 +- Content.IntegrationTests/Tests/Hands/HandTests.cs | 4 +++- .../Tests/Interaction/InRangeUnobstructed.cs | 4 +++- .../Administration/Commands/ExplosionCommand.cs | 3 ++- .../Administration/Systems/AdminVerbSystem.Smites.cs | 2 +- .../Anomaly/Effects/ElectricityAnomalySystem.cs | 2 +- .../Anomaly/Effects/InjectionAnomalySystem.cs | 4 +++- Content.Server/Beam/BeamSystem.cs | 6 ++++-- .../Chemistry/ReactionEffects/AreaReactionEffect.cs | 10 +++++++--- .../ReactionEffects/CreateEntityReactionEffect.cs | 2 +- .../Chemistry/ReactionEffects/EmpReactionEffect.cs | 6 ++++-- Content.Server/Cloning/CloningSystem.cs | 2 +- .../Construction/ConstructionSystem.Initial.cs | 2 +- .../Thresholds/Behaviors/SpawnEntitiesBehavior.cs | 7 +++++-- Content.Server/Disposal/Tube/DisposalTubeSystem.cs | 3 ++- Content.Server/Dragon/DragonSystem.cs | 2 +- Content.Server/Emp/EmpSystem.cs | 4 +++- .../Explosion/EntitySystems/SmokeOnTriggerSystem.cs | 7 +++++-- Content.Server/Fluids/EntitySystems/DrainSystem.cs | 4 +++- .../GameTicking/Rules/DeathMatchRuleSystem.cs | 4 +++- Content.Server/Gatherable/GatherableSystem.cs | 4 +++- Content.Server/Guardian/GuardianSystem.cs | 2 +- Content.Server/Kitchen/EntitySystems/SharpSystem.cs | 4 +++- Content.Server/Lightning/LightningSystem.cs | 4 +++- Content.Server/Lightning/LightningTargetSystem.cs | 4 +++- Content.Server/NPC/Systems/NPCSteeringSystem.cs | 2 +- Content.Server/NPC/Systems/NPCUtilitySystem.cs | 2 +- Content.Server/Nuke/NukeSystem.cs | 2 +- Content.Server/Nutrition/EntitySystems/FoodSystem.cs | 6 ++++-- Content.Server/PDA/Ringer/RingerSystem.cs | 3 ++- Content.Server/Payload/EntitySystems/PayloadSystem.cs | 4 +++- .../Pointing/EntitySystems/PointingSystem.cs | 2 +- Content.Server/Respawn/SpecialRespawnSystem.cs | 2 +- .../Singularity/EntitySystems/EventHorizonSystem.cs | 4 ++-- .../XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs | 6 ++++-- .../Effects/Systems/SpawnArtifactSystem.cs | 2 +- .../Effects/Systems/ThrowArtifactSystem.cs | 4 +++- Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs | 3 ++- Content.Shared/Examine/ExamineSystemShared.cs | 8 ++++---- .../Hands/EntitySystems/SharedHandsSystem.Pickup.cs | 4 ++-- Content.Shared/Interaction/RotateToFaceSystem.cs | 2 +- Content.Shared/Interaction/SharedInteractionSystem.cs | 6 +++--- .../Storage/EntitySystems/MagnetPickupSystem.cs | 2 +- Content.Shared/Tabletop/SharedTabletopSystem.cs | 6 +++--- Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs | 2 +- .../Ranged/Systems/SharedGunSystem.Ballistic.cs | 4 ++-- 59 files changed, 152 insertions(+), 84 deletions(-) diff --git a/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs b/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs index 5f187cad79..b0d8a946ec 100644 --- a/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs +++ b/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs @@ -3,6 +3,7 @@ using Content.Shared.Explosion; using JetBrains.Annotations; using Robust.Client.AutoGenerated; using Robust.Client.Console; +using Robust.Client.GameObjects; using Robust.Client.Player; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; @@ -22,7 +23,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IEntityManager _entMan = default!; - + private readonly SharedTransformSystem _transform = default!; private readonly SpawnExplosionEui _eui; private List _mapData = new(); @@ -37,6 +38,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); + _transform = _entMan.System(); _eui = eui; ExplosionOption.OnItemSelected += ExplosionSelected; @@ -104,7 +106,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow _pausePreview = true; MapOptions.Select(_mapData.IndexOf(transform.MapID)); - (MapX.Value, MapY.Value) = transform.MapPosition.Position; + (MapX.Value, MapY.Value) = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: transform).Position; _pausePreview = false; UpdatePreview(); diff --git a/Content.Client/GPS/UI/HandheldGpsStatusControl.cs b/Content.Client/GPS/UI/HandheldGpsStatusControl.cs index de6a1031ba..7dcf3f29c5 100644 --- a/Content.Client/GPS/UI/HandheldGpsStatusControl.cs +++ b/Content.Client/GPS/UI/HandheldGpsStatusControl.cs @@ -1,6 +1,7 @@ using Content.Client.GPS.Components; using Content.Client.Message; using Content.Client.Stylesheets; +using Robust.Client.GameObjects; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; @@ -13,11 +14,13 @@ public sealed class HandheldGpsStatusControl : Control private readonly RichTextLabel _label; private float _updateDif; private readonly IEntityManager _entMan; + private readonly SharedTransformSystem _transform; public HandheldGpsStatusControl(Entity parent) { _parent = parent; _entMan = IoCManager.Resolve(); + _transform = _entMan.System(); _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; AddChild(_label); UpdateGpsDetails(); @@ -41,7 +44,7 @@ public sealed class HandheldGpsStatusControl : Control var posText = "Error"; if (_entMan.TryGetComponent(_parent, out TransformComponent? transComp)) { - var pos = transComp.MapPosition; + var pos = _transform.GetMapCoordinates(_parent.Owner, xform: transComp); var x = (int) pos.X; var y = (int) pos.Y; posText = $"({x}, {y})"; diff --git a/Content.Client/MouseRotator/MouseRotatorSystem.cs b/Content.Client/MouseRotator/MouseRotatorSystem.cs index 4894c17c4c..ce174c6144 100644 --- a/Content.Client/MouseRotator/MouseRotatorSystem.cs +++ b/Content.Client/MouseRotator/MouseRotatorSystem.cs @@ -2,6 +2,7 @@ using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Client.Player; +using Robust.Client.Replays.Loading; using Robust.Shared.Map; using Robust.Shared.Timing; @@ -37,7 +38,7 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem if (mapPos.MapId == MapId.Nullspace) return; - var angle = (mapPos.Position - xform.MapPosition.Position).ToWorldAngle(); + var angle = (mapPos.Position - _transform.GetMapCoordinates(player.Value, xform: xform).Position).ToWorldAngle(); var curRot = _transform.GetWorldRotation(xform); diff --git a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs index 9012767ef3..8d5607af2d 100644 --- a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs +++ b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs @@ -1,9 +1,11 @@ using System.Numerics; using Content.Shared.Radiation.Components; +using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Enums; using Robust.Shared.Graphics; using Robust.Shared.Map; +using Robust.Shared.Physics; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -14,6 +16,7 @@ namespace Content.Client.Radiation.Overlays [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + private TransformSystem? _transform; private const float MaxDist = 15.0f; @@ -72,6 +75,8 @@ namespace Content.Client.Radiation.Overlays //Queries all pulses on the map and either adds or removes them from the list of rendered pulses based on whether they should be drawn (in range? on the same z-level/map? pulse entity still exists?) private void RadiationQuery(IEye? currentEye) { + _transform ??= _entityManager.System(); + if (currentEye == null) { _pulses.Clear(); @@ -91,7 +96,7 @@ namespace Content.Client.Radiation.Overlays ( _baseShader.Duplicate(), new RadiationShaderInstance( - _entityManager.GetComponent(pulseEntity).MapPosition, + _transform.GetMapCoordinates(pulseEntity), pulse.VisualRange, pulse.StartTime, pulse.VisualDuration @@ -109,7 +114,7 @@ namespace Content.Client.Radiation.Overlays _entityManager.TryGetComponent(pulseEntity, out RadiationPulseComponent? pulse)) { var shaderInstance = _pulses[pulseEntity]; - shaderInstance.instance.CurrentMapCoords = _entityManager.GetComponent(pulseEntity).MapPosition; + shaderInstance.instance.CurrentMapCoords = _transform.GetMapCoordinates(pulseEntity); shaderInstance.instance.Range = pulse.VisualRange; } else { _pulses[pulseEntity].shd.Dispose(); diff --git a/Content.Client/Sprite/SpriteFadeSystem.cs b/Content.Client/Sprite/SpriteFadeSystem.cs index dda3a6c948..d9584b60a6 100644 --- a/Content.Client/Sprite/SpriteFadeSystem.cs +++ b/Content.Client/Sprite/SpriteFadeSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Sprite; using Robust.Client.GameObjects; using Robust.Client.Player; using Robust.Client.State; +using Robust.Shared.Physics; namespace Content.Client.Sprite; @@ -15,6 +16,7 @@ public sealed class SpriteFadeSystem : EntitySystem [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IStateManager _stateManager = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; private readonly HashSet _comps = new(); @@ -48,7 +50,7 @@ public sealed class SpriteFadeSystem : EntitySystem spriteQuery.TryGetComponent(player, out var playerSprite)) { var fadeQuery = GetEntityQuery(); - var mapPos = playerXform.MapPosition; + var mapPos = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: playerXform); // Also want to handle large entities even if they may not be clickable. foreach (var ent in state.GetClickableEntities(mapPos)) diff --git a/Content.Client/Tabletop/TabletopSystem.cs b/Content.Client/Tabletop/TabletopSystem.cs index 696c1455e0..0b55a1839c 100644 --- a/Content.Client/Tabletop/TabletopSystem.cs +++ b/Content.Client/Tabletop/TabletopSystem.cs @@ -258,7 +258,7 @@ namespace Content.Client.Tabletop // Set the dragging player on the component to noone if (broadcast && _draggedEntity != null && EntityManager.HasComponent(_draggedEntity.Value)) { - RaisePredictiveEvent(new TabletopMoveEvent(GetNetEntity(_draggedEntity.Value), Transform(_draggedEntity.Value).MapPosition, GetNetEntity(_table!.Value))); + RaisePredictiveEvent(new TabletopMoveEvent(GetNetEntity(_draggedEntity.Value), Transforms.GetMapCoordinates(_draggedEntity.Value), GetNetEntity(_table!.Value))); RaisePredictiveEvent(new TabletopDraggingPlayerChangedEvent(GetNetEntity(_draggedEntity.Value), false)); } @@ -277,7 +277,8 @@ namespace Content.Client.Tabletop if (coordinates == MapCoordinates.Nullspace) return MapCoordinates.Nullspace; var eye = viewport.Eye; - if (eye == null) return MapCoordinates.Nullspace; + if (eye == null) + return MapCoordinates.Nullspace; var size = (Vector2) viewport.ViewportSize / EyeManager.PixelsPerMeter; // Convert to tiles instead of pixels var eyePosition = eye.Position.Position; diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs index 31e09e6603..334ba60873 100644 --- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs +++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs @@ -60,6 +60,7 @@ public sealed class ChatUIController : UIController [UISystemDependency] private readonly GhostSystem? _ghost = default; [UISystemDependency] private readonly TypingIndicatorSystem? _typingIndicator = default; [UISystemDependency] private readonly ChatSystem? _chatSys = default; + [UISystemDependency] private readonly TransformSystem? _transform = default; [ValidatePrototypeId] private const string ChatNamePalette = "ChatNames"; @@ -625,7 +626,7 @@ public sealed class ChatUIController : UIController var predicate = static (EntityUid uid, (EntityUid compOwner, EntityUid? attachedEntity) data) => uid == data.compOwner || uid == data.attachedEntity; var playerPos = player != null - ? EntityManager.GetComponent(player.Value).MapPosition + ? _transform?.GetMapCoordinates(player.Value) ?? MapCoordinates.Nullspace : MapCoordinates.Nullspace; var occluded = player != null && _examine.IsOccluded(player.Value); @@ -644,7 +645,7 @@ public sealed class ChatUIController : UIController continue; } - var otherPos = EntityManager.GetComponent(ent).MapPosition; + var otherPos = _transform?.GetMapCoordinates(ent) ?? MapCoordinates.Nullspace; if (occluded && !_examine.InRangeUnOccluded( playerPos, diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 49a3785eb2..2e6c5f5809 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -22,6 +22,7 @@ namespace Content.Client.Verbs [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly ExamineSystem _examine = default!; [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; @@ -141,7 +142,7 @@ namespace Content.Client.Verbs if ((visibility & MenuVisibility.NoFov) == 0) { var xformQuery = GetEntityQuery(); - var playerPos = xformQuery.GetComponent(player.Value).MapPosition; + var playerPos = _transform.GetMapCoordinates(player.Value, xform: xformQuery.GetComponent(player.Value)); for (var i = entities.Count - 1; i >= 0; i--) { @@ -149,7 +150,7 @@ namespace Content.Client.Verbs if (!_examine.InRangeUnOccluded( playerPos, - xformQuery.GetComponent(entity).MapPosition, + _transform.GetMapCoordinates(entity, xform: xformQuery.GetComponent(entity)), ExamineSystemShared.ExamineRange, null)) { diff --git a/Content.Client/Weapons/Melee/MeleeArcOverlay.cs b/Content.Client/Weapons/Melee/MeleeArcOverlay.cs index dbd68c15e2..e7b6c8b0d6 100644 --- a/Content.Client/Weapons/Melee/MeleeArcOverlay.cs +++ b/Content.Client/Weapons/Melee/MeleeArcOverlay.cs @@ -20,10 +20,11 @@ public sealed class MeleeArcOverlay : Overlay private readonly IPlayerManager _playerManager; private readonly MeleeWeaponSystem _melee; private readonly SharedCombatModeSystem _combatMode; + private readonly SharedTransformSystem _transform = default!; public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; - public MeleeArcOverlay(IEntityManager entManager, IEyeManager eyeManager, IInputManager inputManager, IPlayerManager playerManager, MeleeWeaponSystem melee, SharedCombatModeSystem combatMode) + public MeleeArcOverlay(IEntityManager entManager, IEyeManager eyeManager, IInputManager inputManager, IPlayerManager playerManager, MeleeWeaponSystem melee, SharedCombatModeSystem combatMode, SharedTransformSystem transform) { _entManager = entManager; _eyeManager = eyeManager; @@ -31,6 +32,7 @@ public sealed class MeleeArcOverlay : Overlay _playerManager = playerManager; _melee = melee; _combatMode = combatMode; + _transform = transform; } protected override void Draw(in OverlayDrawArgs args) @@ -52,7 +54,7 @@ public sealed class MeleeArcOverlay : Overlay if (mapPos.MapId != args.MapId) return; - var playerPos = xform.MapPosition; + var playerPos = _transform.GetMapCoordinates(player.Value, xform: xform); if (mapPos.MapId != playerPos.MapId) return; diff --git a/Content.Client/Weapons/Melee/MeleeSpreadCommand.cs b/Content.Client/Weapons/Melee/MeleeSpreadCommand.cs index 6b259a7fd5..eda469deaf 100644 --- a/Content.Client/Weapons/Melee/MeleeSpreadCommand.cs +++ b/Content.Client/Weapons/Melee/MeleeSpreadCommand.cs @@ -35,6 +35,7 @@ public sealed class MeleeSpreadCommand : IConsoleCommand collection.Resolve(), collection.Resolve(), sysManager.GetEntitySystem(), - sysManager.GetEntitySystem())); + sysManager.GetEntitySystem(), + sysManager.GetEntitySystem())); } } diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index 039af55bd0..d59d471f1a 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -142,7 +142,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem // Light attack if (useDown == BoundKeyState.Down) { - var attackerPos = Transform(entity).MapPosition; + var attackerPos = TransformSystem.GetMapCoordinates(entity); if (mousePos.MapId != attackerPos.MapId || (attackerPos.Position - mousePos.Position).Length() > weapon.Range) diff --git a/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs b/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs index 62df764ae5..63d21c8463 100644 --- a/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs +++ b/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs @@ -18,8 +18,9 @@ public sealed class GunSpreadOverlay : Overlay private readonly IInputManager _input; private readonly IPlayerManager _player; private readonly GunSystem _guns; + private readonly SharedTransformSystem _transform; - public GunSpreadOverlay(IEntityManager entManager, IEyeManager eyeManager, IGameTiming timing, IInputManager input, IPlayerManager player, GunSystem system) + public GunSpreadOverlay(IEntityManager entManager, IEyeManager eyeManager, IGameTiming timing, IInputManager input, IPlayerManager player, GunSystem system, SharedTransformSystem transform) { _entManager = entManager; _eye = eyeManager; @@ -27,6 +28,7 @@ public sealed class GunSpreadOverlay : Overlay _timing = timing; _player = player; _guns = system; + _transform = transform; } protected override void Draw(in OverlayDrawArgs args) @@ -41,7 +43,7 @@ public sealed class GunSpreadOverlay : Overlay return; } - var mapPos = xform.MapPosition; + var mapPos = _transform.GetMapCoordinates(player.Value, xform: xform); if (mapPos.MapId == MapId.Nullspace) return; diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index 4a7711032e..ac5914d47c 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -60,7 +60,8 @@ public sealed partial class GunSystem : SharedGunSystem Timing, _inputManager, _player, - this)); + this, + TransformSystem)); } else { diff --git a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs index 9f31231091..2fbaa91456 100644 --- a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs +++ b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs @@ -171,7 +171,7 @@ namespace Content.IntegrationTests.Tests.Doors // Sloth: Okay I'm sorry but I hate having to rewrite tests for every refactor // If you see this yell at me in discord so I can continue to pretend this didn't happen. // REMINDER THAT I STILL HAVE TO FIX THIS TEST EVERY OTHER PHYSICS PR - // Assert.That(AirlockPhysicsDummy.Transform.MapPosition.X, Is.GreaterThan(AirlockPhysicsDummyStartingX)); + // _transform.GetMapCoordinates(UID HERE, xform: Assert.That(AirlockPhysicsDummy.Transform).X, Is.GreaterThan(AirlockPhysicsDummyStartingX)); // Blocked by the airlock await server.WaitAssertion(() => diff --git a/Content.IntegrationTests/Tests/Hands/HandTests.cs b/Content.IntegrationTests/Tests/Hands/HandTests.cs index fdcd7f9096..9ecabbeebf 100644 --- a/Content.IntegrationTests/Tests/Hands/HandTests.cs +++ b/Content.IntegrationTests/Tests/Hands/HandTests.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; +using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -24,6 +25,7 @@ public sealed class HandTests var playerMan = server.ResolveDependency(); var mapMan = server.ResolveDependency(); var sys = entMan.System(); + var tSys = entMan.System(); var data = await pair.CreateTestMap(); await pair.RunTicksSync(5); @@ -35,7 +37,7 @@ public sealed class HandTests { player = playerMan.Sessions.First().AttachedEntity!.Value; var xform = entMan.GetComponent(player); - item = entMan.SpawnEntity("Crowbar", xform.MapPosition); + item = entMan.SpawnEntity("Crowbar", tSys.GetMapCoordinates(player, xform: xform)); hands = entMan.GetComponent(player); sys.TryPickup(player, item, hands.ActiveHand!); }); diff --git a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs index b8828763a2..719367e54e 100644 --- a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs +++ b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Shared.Interaction; +using Robust.Server.GameObjects; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -32,6 +33,7 @@ namespace Content.IntegrationTests.Tests.Interaction var sEntities = server.ResolveDependency(); var mapManager = server.ResolveDependency(); var conSystem = sEntities.EntitySysManager.GetEntitySystem(); + var tSystem = sEntities.EntitySysManager.GetEntitySystem(); EntityUid origin = default; EntityUid other = default; @@ -45,7 +47,7 @@ namespace Content.IntegrationTests.Tests.Interaction origin = sEntities.SpawnEntity(HumanId, coordinates); other = sEntities.SpawnEntity(HumanId, coordinates); conSystem.EnsureContainer(other, "InRangeUnobstructedTestOtherContainer"); - mapCoordinates = sEntities.GetComponent(other).MapPosition; + mapCoordinates = tSystem.GetMapCoordinates(other); }); await server.WaitIdleAsync(); diff --git a/Content.Server/Administration/Commands/ExplosionCommand.cs b/Content.Server/Administration/Commands/ExplosionCommand.cs index 56ed78b2e2..81daca59c4 100644 --- a/Content.Server/Administration/Commands/ExplosionCommand.cs +++ b/Content.Server/Administration/Commands/ExplosionCommand.cs @@ -8,6 +8,7 @@ using Robust.Shared.Map; using Robust.Shared.Prototypes; using System.Linq; using System.Numerics; +using Robust.Server.GameObjects; namespace Content.Server.Administration.Commands; @@ -105,7 +106,7 @@ public sealed class ExplosionCommand : IConsoleCommand if (args.Length > 4) coords = new MapCoordinates(new Vector2(x, y), xform.MapID); else - coords = xform.MapPosition; + coords = entMan.System().GetMapCoordinates(shell.Player.AttachedEntity.Value, xform: xform); } ExplosionPrototype? type; diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index ed60fe5448..eb21662719 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -102,7 +102,7 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")), Act = () => { - var coords = Transform(args.Target).MapPosition; + var coords = _transformSystem.GetMapCoordinates(args.Target); Timer.Spawn(_gameTiming.TickPeriod, () => _explosionSystem.QueueExplosion(coords, ExplosionSystem.DefaultExplosionPrototypeId, 4, 1, 2, maxTileBreak: 0), // it gibs, damage doesn't need to be high. diff --git a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs index f2a060d629..bd4718e8e3 100644 --- a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs @@ -61,7 +61,7 @@ public sealed class ElectricityAnomalySystem : EntitySystem var damage = (int) (elec.MaxElectrocuteDamage * anom.Severity); var duration = elec.MaxElectrocuteDuration * anom.Severity; - foreach (var (ent, comp) in _lookup.GetEntitiesInRange(xform.MapPosition, range)) + foreach (var (ent, comp) in _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(uid, xform), range)) { _electrocution.TryDoElectrocution(ent, uid, damage, duration, true, statusEffects: comp, ignoreInsulation: true); } diff --git a/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs b/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs index 731d853280..1fa0c00bd3 100644 --- a/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs @@ -3,6 +3,7 @@ using Content.Server.Chemistry.Containers.EntitySystems; using Content.Shared.Anomaly.Components; using Content.Shared.Chemistry.Components.SolutionManager; using System.Linq; +using Robust.Server.GameObjects; namespace Content.Server.Anomaly.Effects; /// @@ -16,6 +17,7 @@ public sealed class InjectionAnomalySystem : EntitySystem { [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly TransformSystem _transform = default!; private EntityQuery _injectableQuery; @@ -45,7 +47,7 @@ public sealed class InjectionAnomalySystem : EntitySystem //We get all the entity in the radius into which the reagent will be injected. var xformQuery = GetEntityQuery(); var xform = xformQuery.GetComponent(entity); - var allEnts = _lookup.GetEntitiesInRange(xform.MapPosition, injectRadius) + var allEnts = _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(entity, xform: xform), injectRadius) .Select(x => x.Owner).ToList(); //for each matching entity found diff --git a/Content.Server/Beam/BeamSystem.cs b/Content.Server/Beam/BeamSystem.cs index 33f2f252d9..ad67f983c2 100644 --- a/Content.Server/Beam/BeamSystem.cs +++ b/Content.Server/Beam/BeamSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Beam.Components; using Content.Shared.Beam; using Content.Shared.Beam.Components; using Content.Shared.Physics; +using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; @@ -16,6 +17,7 @@ namespace Content.Server.Beam; public sealed class BeamSystem : SharedBeamSystem { [Dependency] private readonly FixtureSystem _fixture = default!; + [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedBroadphaseSystem _broadphase = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; @@ -144,8 +146,8 @@ public sealed class BeamSystem : SharedBeamSystem if (Deleted(user) || Deleted(target)) return; - var userMapPos = Transform(user).MapPosition; - var targetMapPos = Transform(target).MapPosition; + var userMapPos = _transform.GetMapCoordinates(user); + var targetMapPos = _transform.GetMapCoordinates(target); //The distance between the target and the user. var calculatedDistance = targetMapPos.Position - userMapPos.Position; diff --git a/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs index 024558f8de..ebbf4e0341 100644 --- a/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs @@ -6,6 +6,7 @@ using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Maps; using JetBrains.Annotations; +using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; @@ -58,15 +59,18 @@ namespace Content.Server.Chemistry.ReactionEffects var splitSolution = args.Source.SplitSolution(args.Source.Volume); var transform = args.EntityManager.GetComponent(args.SolutionEntity); var mapManager = IoCManager.Resolve(); + var mapSys = args.EntityManager.System(); + var sys = args.EntityManager.System(); + var mapCoords = sys.GetMapCoordinates(args.SolutionEntity, xform: transform); - if (!mapManager.TryFindGridAt(transform.MapPosition, out _, out var grid) || - !grid.TryGetTileRef(transform.Coordinates, out var tileRef) || + if (!mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) || + !mapSys.TryGetTileRef(gridUid, grid, transform.Coordinates, out var tileRef) || tileRef.Tile.IsSpace()) { return; } - var coords = grid.MapToGrid(transform.MapPosition); + var coords = mapSys.MapToGrid(gridUid, mapCoords); var ent = args.EntityManager.SpawnEntity(_prototypeId, coords.SnapToGrid()); var smoke = args.EntityManager.System(); diff --git a/Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs index f8c0378452..0d5acc1722 100644 --- a/Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs @@ -34,7 +34,7 @@ public sealed partial class CreateEntityReactionEffect : ReagentEffect for (var i = 0; i < quantity; i++) { - var uid = args.EntityManager.SpawnEntity(Entity, transform.MapPosition); + var uid = args.EntityManager.SpawnEntity(Entity, transformSystem.GetMapCoordinates(args.SolutionEntity, xform: transform)); transformSystem.AttachToGridOrMap(uid); // TODO figure out how to properly spawn inside of containers diff --git a/Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs index b6714ca28d..9a320ffc39 100644 --- a/Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs @@ -1,5 +1,6 @@ using Content.Server.Emp; using Content.Shared.Chemistry.Reagent; +using Robust.Server.GameObjects; using Robust.Shared.Prototypes; namespace Content.Server.Chemistry.ReactionEffects; @@ -37,11 +38,12 @@ public sealed partial class EmpReactionEffect : ReagentEffect public override void Effect(ReagentEffectArgs args) { + var tSys = args.EntityManager.System(); var transform = args.EntityManager.GetComponent(args.SolutionEntity); var range = MathF.Min((float) (args.Quantity*EmpRangePerUnit), EmpMaxRange); - args.EntityManager.System().EmpPulse( - transform.MapPosition, + args.EntityManager.System() + .EmpPulse(tSys.GetMapCoordinates(args.SolutionEntity, xform: transform), range, EnergyConsumption, DisableDuration); diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index def9950d90..3893f31d25 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -210,7 +210,7 @@ namespace Content.Server.Cloning } // end of genetic damage checks - var mob = Spawn(speciesPrototype.Prototype, Transform(uid).MapPosition); + var mob = Spawn(speciesPrototype.Prototype, _transformSystem.GetMapCoordinates(uid)); _humanoidSystem.CloneAppearance(bodyToClone, mob); var ev = new CloningEvent(bodyToClone, mob); diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index ede8d3064f..17ed5c90f4 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -79,7 +79,7 @@ namespace Content.Server.Construction } } - var pos = Transform(user).MapPosition; + var pos = _transformSystem.GetMapCoordinates(user); foreach (var near in _lookupSystem.GetEntitiesInRange(pos, 2f, LookupFlags.Contained | LookupFlags.Dynamic | LookupFlags.Sundries | LookupFlags.Approximate)) { diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs index 0fa3c06c04..057b6df9df 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs @@ -3,6 +3,7 @@ using Content.Server.Forensics; using Content.Server.Stack; using Content.Shared.Prototypes; using Content.Shared.Stacks; +using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; @@ -30,7 +31,8 @@ namespace Content.Server.Destructible.Thresholds.Behaviors public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) { - var position = system.EntityManager.GetComponent(owner).MapPosition; + var tSys = system.EntityManager.System(); + var position = tSys.GetMapCoordinates(owner); var getRandomVector = () => new Vector2(system.Random.NextFloat(-Offset, Offset), system.Random.NextFloat(-Offset, Offset)); @@ -48,7 +50,8 @@ namespace Content.Server.Destructible.Thresholds.Behaviors ? minMax.Min : system.Random.Next(minMax.Min, minMax.Max + 1); - if (count == 0) continue; + if (count == 0) + continue; if (EntityPrototypeHelpers.HasComponent(entityId, system.PrototypeManager, system.ComponentFactory)) { diff --git a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs index f0f6e9142c..b38e6e78f4 100644 --- a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs @@ -31,6 +31,7 @@ namespace Content.Server.Disposal.Tube [Dependency] private readonly DisposableSystem _disposableSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { base.Initialize(); @@ -422,7 +423,7 @@ namespace Content.Server.Disposal.Tube return false; var xform = Transform(uid); - var holder = Spawn(DisposalEntryComponent.HolderPrototypeId, xform.MapPosition); + var holder = Spawn(DisposalEntryComponent.HolderPrototypeId, _transform.GetMapCoordinates(uid, xform: xform)); var holderComponent = Comp(holder); foreach (var entity in from.Container.ContainedEntities.ToArray()) diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs index d33e6f3bef..1e29b80eba 100644 --- a/Content.Server/Dragon/DragonSystem.cs +++ b/Content.Server/Dragon/DragonSystem.cs @@ -165,7 +165,7 @@ public sealed partial class DragonSystem : EntitySystem return; } - var carpUid = Spawn(component.RiftPrototype, xform.MapPosition); + var carpUid = Spawn(component.RiftPrototype, _transform.GetMapCoordinates(uid, xform: xform)); component.Rifts.Add(carpUid); Comp(carpUid).Dragon = uid; } diff --git a/Content.Server/Emp/EmpSystem.cs b/Content.Server/Emp/EmpSystem.cs index 7c1a6f9b5d..d8eab0c5d1 100644 --- a/Content.Server/Emp/EmpSystem.cs +++ b/Content.Server/Emp/EmpSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Radio; using Content.Server.SurveillanceCamera; using Content.Shared.Emp; using Content.Shared.Examine; +using Robust.Server.GameObjects; using Robust.Shared.Map; namespace Content.Server.Emp; @@ -11,6 +12,7 @@ namespace Content.Server.Emp; public sealed class EmpSystem : SharedEmpSystem { [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TransformSystem _transform = default!; public const string EmpPulseEffectPrototype = "EffectEmpPulse"; @@ -102,7 +104,7 @@ public sealed class EmpSystem : SharedEmpSystem private void HandleEmpTrigger(EntityUid uid, EmpOnTriggerComponent comp, TriggerEvent args) { - EmpPulse(Transform(uid).MapPosition, comp.Range, comp.EnergyConsumption, comp.DisableDuration); + EmpPulse(_transform.GetMapCoordinates(uid), comp.Range, comp.EnergyConsumption, comp.DisableDuration); args.Handled = true; } diff --git a/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs b/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs index 17ca972356..f958373ac7 100644 --- a/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Fluids.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Coordinates.Helpers; using Content.Shared.Maps; +using Robust.Server.GameObjects; using Robust.Shared.Map; namespace Content.Server.Explosion.EntitySystems; @@ -15,6 +16,7 @@ public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem { [Dependency] private readonly IMapManager _mapMan = default!; [Dependency] private readonly SmokeSystem _smoke = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { @@ -26,14 +28,15 @@ public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem private void OnTrigger(EntityUid uid, SmokeOnTriggerComponent comp, TriggerEvent args) { var xform = Transform(uid); - if (!_mapMan.TryFindGridAt(xform.MapPosition, out _, out var grid) || + var mapCoords = _transform.GetMapCoordinates(uid, xform); + if (!_mapMan.TryFindGridAt(mapCoords, out _, out var grid) || !grid.TryGetTileRef(xform.Coordinates, out var tileRef) || tileRef.Tile.IsSpace()) { return; } - var coords = grid.MapToGrid(xform.MapPosition); + var coords = grid.MapToGrid(mapCoords); var ent = Spawn(comp.SmokePrototype, coords.SnapToGrid()); if (!TryComp(ent, out var smoke)) { diff --git a/Content.Server/Fluids/EntitySystems/DrainSystem.cs b/Content.Server/Fluids/EntitySystems/DrainSystem.cs index 5fc406dca5..b79685d83b 100644 --- a/Content.Server/Fluids/EntitySystems/DrainSystem.cs +++ b/Content.Server/Fluids/EntitySystems/DrainSystem.cs @@ -13,6 +13,7 @@ using Content.Shared.Fluids.Components; using Content.Shared.Interaction; using Content.Shared.Tag; using Content.Shared.Verbs; +using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Collections; using Robust.Shared.Prototypes; @@ -31,6 +32,7 @@ public sealed class DrainSystem : SharedDrainSystem [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly PuddleSystem _puddleSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -161,7 +163,7 @@ public sealed class DrainSystem : SharedDrainSystem puddles.Clear(); - foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, drain.Range)) + foreach (var entity in _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(uid, xform), drain.Range)) { // No InRangeUnobstructed because there's no collision group that fits right now // and these are placed by mappers and not buildable/movable so shouldnt really be a problem... diff --git a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs index 78b8a8a85c..ad7c63ff58 100644 --- a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs @@ -9,6 +9,7 @@ using Content.Server.RoundEnd; using Content.Server.Station.Systems; using Content.Shared.Points; using Content.Shared.Storage; +using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Utility; @@ -25,6 +26,7 @@ public sealed class DeathMatchRuleSystem : GameRuleSystem().ToList(); - EntityManager.SpawnEntities(Transform(ev.Entity).MapPosition, spawns); + EntityManager.SpawnEntities(_transform.GetMapCoordinates(ev.Entity), spawns); } } diff --git a/Content.Server/Gatherable/GatherableSystem.cs b/Content.Server/Gatherable/GatherableSystem.cs index 7fbbf7f4f6..11295bb3a3 100644 --- a/Content.Server/Gatherable/GatherableSystem.cs +++ b/Content.Server/Gatherable/GatherableSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.EntityList; using Content.Shared.Interaction; using Content.Shared.Tag; using Content.Shared.Weapons.Melee.Events; +using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; @@ -18,6 +19,7 @@ public sealed partial class GatherableSystem : EntitySystem [Dependency] private readonly DestructibleSystem _destructible = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { @@ -61,7 +63,7 @@ public sealed partial class GatherableSystem : EntitySystem if (component.MappedLoot == null) return; - var pos = Transform(gatheredUid).MapPosition; + var pos = _transform.GetMapCoordinates(gatheredUid); foreach (var (tag, table) in component.MappedLoot) { diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs index 97d4eb0680..203882ed9e 100644 --- a/Content.Server/Guardian/GuardianSystem.cs +++ b/Content.Server/Guardian/GuardianSystem.cs @@ -208,7 +208,7 @@ namespace Content.Server.Guardian var hostXform = Transform(args.Args.Target.Value); var host = EnsureComp(args.Args.Target.Value); // Use map position so it's not inadvertantly parented to the host + if it's in a container it spawns outside I guess. - var guardian = Spawn(component.GuardianProto, hostXform.MapPosition); + var guardian = Spawn(component.GuardianProto, _transform.GetMapCoordinates(args.Args.Target.Value, xform: hostXform)); _container.Insert(guardian, host.GuardianContainer); host.HostedGuardian = guardian; diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index 431e438fd8..b796687721 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Kitchen; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Robust.Server.Containers; +using Robust.Server.GameObjects; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -28,6 +29,7 @@ public sealed class SharpSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly ContainerSystem _containerSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; @@ -101,7 +103,7 @@ public sealed class SharpSystem : EntitySystem } var spawnEntities = EntitySpawnCollection.GetSpawns(butcher.SpawnedEntities, _robustRandom); - var coords = Transform(args.Args.Target.Value).MapPosition; + var coords = _transform.GetMapCoordinates(args.Args.Target.Value); EntityUid popupEnt = default!; foreach (var proto in spawnEntities) { diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 4f975a60fd..2147ac80f2 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Beam; using Content.Server.Beam.Components; using Content.Server.Lightning.Components; using Content.Shared.Lightning; +using Robust.Server.GameObjects; using Robust.Shared.Random; namespace Content.Server.Lightning; @@ -20,6 +21,7 @@ public sealed class LightningSystem : SharedLightningSystem [Dependency] private readonly BeamSystem _beam = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { @@ -74,7 +76,7 @@ public sealed class LightningSystem : SharedLightningSystem //To Do: This is still pretty bad for perf but better than before and at least it doesn't re-allocate // several hashsets every time - var targets = _lookup.GetComponentsInRange(Transform(user).MapPosition, range).ToList(); + var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), range).ToList(); _random.Shuffle(targets); targets.Sort((x, y) => y.Priority.CompareTo(x.Priority)); diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index ccaa74e9e2..bc99def974 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Explosion.EntitySystems; using Content.Server.Lightning; using Content.Server.Lightning.Components; using Content.Shared.Damage; +using Robust.Server.GameObjects; namespace Content.Server.Tesla.EntitySystems; @@ -12,6 +13,7 @@ public sealed class LightningTargetSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly ExplosionSystem _explosionSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { @@ -29,7 +31,7 @@ public sealed class LightningTargetSystem : EntitySystem if (uid.Comp.LightningExplode) { _explosionSystem.QueueExplosion( - Transform(uid).MapPosition, + _transform.GetMapCoordinates(uid), uid.Comp.ExplosionPrototype, uid.Comp.TotalIntensity, uid.Comp.Dropoff, uid.Comp.MaxTileIntensity, diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.cs index 153a173855..a77af94174 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.cs @@ -454,7 +454,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem } var targetPos = steering.Coordinates.ToMap(EntityManager, _transform); - var ourPos = xform.MapPosition; + var ourPos = _transform.GetMapCoordinates(uid, xform: xform); PrunePath(uid, ourPos, targetPos.Position - ourPos.Position, result.Path); steering.CurrentPath = new Queue(result.Path); diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index e8fb54022e..4b0ccafa1d 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -372,7 +372,7 @@ public sealed class NPCUtilitySystem : EntitySystem if (compQuery.Components.Count == 0) return; - var mapPos = _xformQuery.GetComponent(owner).MapPosition; + var mapPos = _transform.GetMapCoordinates(owner, xform: _xformQuery.GetComponent(owner)); _compTypes.Clear(); var i = -1; EntityPrototype.ComponentRegistryEntry compZero = default!; diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index b72be0b46c..db425b491e 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -450,7 +450,7 @@ public sealed class NukeSystem : EntitySystem if (stationUid != null) _alertLevel.SetLevel(stationUid.Value, component.AlertLevelOnActivate, true, true, true, true); - var pos = nukeXform.MapPosition; + var pos = _transform.GetMapCoordinates(uid, xform: nukeXform); var x = (int) pos.X; var y = (int) pos.Y; var posText = $"({x}, {y})"; diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 49d7374041..2c7632aadc 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -31,6 +31,7 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Utility; using System.Linq; +using Robust.Server.GameObjects; namespace Content.Server.Nutrition.EntitySystems; @@ -52,6 +53,7 @@ public sealed class FoodSystem : EntitySystem [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly StomachSystem _stomach = default!; [Dependency] private readonly UtensilSystem _utensil = default!; @@ -150,7 +152,7 @@ public sealed class FoodSystem : EntitySystem return (false, true); // TODO make do-afters account for fixtures in the range check. - if (!Transform(user).MapPosition.InRange(Transform(target).MapPosition, MaxFeedDistance)) + if (!_transform.GetMapCoordinates(user).InRange(_transform.GetMapCoordinates(target), MaxFeedDistance)) { var message = Loc.GetString("interaction-system-user-interaction-cannot-reach"); _popup.PopupEntity(message, user, user); @@ -325,7 +327,7 @@ public sealed class FoodSystem : EntitySystem } //We're empty. Become trash. - var position = Transform(food).MapPosition; + var position = _transform.GetMapCoordinates(food); var finisher = Spawn(component.Trash, position); // If the user is holding the item diff --git a/Content.Server/PDA/Ringer/RingerSystem.cs b/Content.Server/PDA/Ringer/RingerSystem.cs index a10544d696..47ae41896e 100644 --- a/Content.Server/PDA/Ringer/RingerSystem.cs +++ b/Content.Server/PDA/Ringer/RingerSystem.cs @@ -25,6 +25,7 @@ namespace Content.Server.PDA.Ringer [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; private readonly Dictionary _lastSetRingtoneAt = new(); @@ -210,7 +211,7 @@ namespace Content.Server.PDA.Ringer _audio.PlayEntity( GetSound(ringer.Ringtone[ringer.NoteCount]), - Filter.Empty().AddInRange(ringerXform.MapPosition, ringer.Range), + Filter.Empty().AddInRange(_transform.GetMapCoordinates(uid, ringerXform), ringer.Range), uid, true, AudioParams.Default.WithMaxDistance(ringer.Range).WithVolume(ringer.Volume) diff --git a/Content.Server/Payload/EntitySystems/PayloadSystem.cs b/Content.Server/Payload/EntitySystems/PayloadSystem.cs index 85cf303d5d..15966956d4 100644 --- a/Content.Server/Payload/EntitySystems/PayloadSystem.cs +++ b/Content.Server/Payload/EntitySystems/PayloadSystem.cs @@ -10,6 +10,7 @@ using Robust.Shared.Containers; using Robust.Shared.Serialization.Manager; using Robust.Shared.Utility; using System.Linq; +using Robust.Server.GameObjects; namespace Content.Server.Payload.EntitySystems; @@ -17,6 +18,7 @@ public sealed class PayloadSystem : EntitySystem { [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IComponentFactory _componentFactory = default!; [Dependency] private readonly ISerializationManager _serializationManager = default!; @@ -158,7 +160,7 @@ public sealed class PayloadSystem : EntitySystem var solStringB = SolutionContainerSystem.ToPrettyString(solutionB); _adminLogger.Add(LogType.ChemicalReaction, - $"Chemical bomb payload {ToPrettyString(entity.Owner):payload} at {Transform(entity.Owner).MapPosition:location} is combining two solutions: {solStringA:solutionA} and {solStringB:solutionB}"); + $"Chemical bomb payload {ToPrettyString(entity.Owner):payload} at {_transform.GetMapCoordinates(entity.Owner):location} is combining two solutions: {solStringA:solutionA} and {solStringB:solutionB}"); solutionA.MaxVolume += solutionB.MaxVolume; _solutionContainerSystem.TryAddSolution(solnA.Value, solutionB); diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index 9b2e14eff8..7bbf6409cd 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -183,7 +183,7 @@ namespace Content.Server.Pointing.EntitySystems (eyeComp.VisibilityMask & layer) == 0) return false; - return Transform(ent).MapPosition.InRange(Transform(player).MapPosition, PointingRange); + return _transform.GetMapCoordinates(ent).InRange(_transform.GetMapCoordinates(player), PointingRange); } var viewers = Filter.Empty() diff --git a/Content.Server/Respawn/SpecialRespawnSystem.cs b/Content.Server/Respawn/SpecialRespawnSystem.cs index 2463bcd740..6c7bb5c923 100644 --- a/Content.Server/Respawn/SpecialRespawnSystem.cs +++ b/Content.Server/Respawn/SpecialRespawnSystem.cs @@ -95,7 +95,7 @@ public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem { var xform = Transform(entityGridUid.Value); var pos = xform.Coordinates; - var mapPos = xform.MapPosition; + var mapPos = _transform.GetMapCoordinates(entityGridUid.Value, xform: xform); var circle = new Circle(mapPos.Position, 2); var found = false; diff --git a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs index 5874de2a7c..d7b83d4439 100644 --- a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs @@ -165,7 +165,7 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem var range2 = range * range; var xformQuery = EntityManager.GetEntityQuery(); var epicenter = _xformSystem.GetWorldPosition(xform, xformQuery); - foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, range, flags: LookupFlags.Uncontained)) + foreach (var entity in _lookup.GetEntitiesInRange(_xformSystem.GetMapCoordinates(uid, xform), range, flags: LookupFlags.Uncontained)) { if (entity == uid) continue; @@ -295,7 +295,7 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem if (!Resolve(uid, ref xform) || !Resolve(uid, ref eventHorizon)) return; - var mapPos = xform.MapPosition; + var mapPos = _xformSystem.GetMapCoordinates(uid, xform: xform); var box = Box2.CenteredAround(mapPos.Position, new Vector2(range, range)); var circle = new Circle(mapPos.Position, range); var grids = new List>(); diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs index d4ed8272aa..970743f484 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs @@ -1,12 +1,14 @@ using Content.Server.Emp; using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components; using Content.Server.Xenoarchaeology.XenoArtifacts.Events; +using Robust.Server.GameObjects; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems; public sealed class EmpArtifactSystem : EntitySystem { [Dependency] private readonly EmpSystem _emp = default!; + [Dependency] private readonly TransformSystem _transform = default!; /// public override void Initialize() @@ -16,6 +18,6 @@ public sealed class EmpArtifactSystem : EntitySystem private void OnActivate(EntityUid uid, EmpArtifactComponent component, ArtifactActivatedEvent args) { - _emp.EmpPulse(Transform(uid).MapPosition, component.Range, component.EnergyConsumption, component.DisableDuration); + _emp.EmpPulse(_transform.GetMapCoordinates(uid), component.Range, component.EnergyConsumption, component.DisableDuration); } -} \ No newline at end of file +} diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/SpawnArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/SpawnArtifactSystem.cs index fcb33ae41f..c262283787 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/SpawnArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/SpawnArtifactSystem.cs @@ -32,7 +32,7 @@ public sealed class SpawnArtifactSystem : EntitySystem if (component.Spawns is not {} spawns) return; - var artifactCord = Transform(uid).MapPosition; + var artifactCord = _transform.GetMapCoordinates(uid); foreach (var spawn in EntitySpawnCollection.GetSpawns(spawns, _random)) { var dx = _random.NextFloat(-component.Range, component.Range); diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs index 57a30a2fd9..8708e0ff4e 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Xenoarchaeology.XenoArtifacts.Events; using Content.Shared.Maps; using Content.Shared.Physics; using Content.Shared.Throwing; +using Robust.Server.GameObjects; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Random; @@ -16,6 +17,7 @@ public sealed class ThrowArtifactSystem : EntitySystem [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly TileSystem _tile = default!; + [Dependency] private readonly TransformSystem _transform = default!; /// public override void Initialize() @@ -50,7 +52,7 @@ public sealed class ThrowArtifactSystem : EntitySystem var tempXform = Transform(ent); - var foo = tempXform.MapPosition.Position - xform.MapPosition.Position; + var foo = _transform.GetMapCoordinates(ent, xform: tempXform).Position - _transform.GetMapCoordinates(uid, xform: xform).Position; _throwing.TryThrow(ent, foo*2, component.ThrowStrength, uid, 0); } } diff --git a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs index 83aa708589..79a32268e8 100644 --- a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs +++ b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs @@ -13,6 +13,7 @@ public abstract class SharedDeviceLinkSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; public const string InvokedPort = "link_port"; @@ -529,7 +530,7 @@ public abstract class SharedDeviceLinkSystem : EntitySystem private bool InRange(EntityUid sourceUid, EntityUid sinkUid, float range) { // TODO: This should be using an existing method and also coordinates inrange instead. - return Transform(sourceUid).MapPosition.InRange(Transform(sinkUid).MapPosition, range); + return _transform.GetMapCoordinates(sourceUid).InRange(_transform.GetMapCoordinates(sinkUid), range); } private void SendNewLinkEvent(EntityUid? user, EntityUid sourceUid, string source, EntityUid sinkUid, string sink) diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 2a5201f768..8831213c35 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -210,8 +210,8 @@ namespace Content.Shared.Examine public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); - var originPos = entMan.GetComponent(origin).MapPosition; - var otherPos = entMan.GetComponent(other).MapPosition; + var originPos = _transform.GetMapCoordinates(origin); + var otherPos = _transform.GetMapCoordinates(other); return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker); } @@ -219,7 +219,7 @@ namespace Content.Shared.Examine public bool InRangeUnOccluded(EntityUid origin, EntityCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); - var originPos = entMan.GetComponent(origin).MapPosition; + var originPos = _transform.GetMapCoordinates(origin); var otherPos = other.ToMap(entMan, _transform); return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker); @@ -228,7 +228,7 @@ namespace Content.Shared.Examine public bool InRangeUnOccluded(EntityUid origin, MapCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); - var originPos = entMan.GetComponent(origin).MapPosition; + var originPos = _transform.GetMapCoordinates(origin); return InRangeUnOccluded(originPos, other, range, predicate, ignoreInsideBlocker); } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs index 20e08b2767..d1f41738e9 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs @@ -108,10 +108,10 @@ public abstract partial class SharedHandsSystem : EntitySystem var xform = Transform(uid); var coordinateEntity = xform.ParentUid.IsValid() ? xform.ParentUid : uid; var itemXform = Transform(entity); - var itemPos = itemXform.MapPosition; + var itemPos = TransformSystem.GetMapCoordinates(entity, xform: itemXform); if (itemPos.MapId == xform.MapID - && (itemPos.Position - xform.MapPosition.Position).Length() <= MaxAnimationRange + && (itemPos.Position - TransformSystem.GetMapCoordinates(uid, xform: xform).Position).Length() <= MaxAnimationRange && MetaData(entity).VisibilityMask == MetaData(uid).VisibilityMask) // Don't animate aghost pickups. { var initialPosition = EntityCoordinates.FromMap(coordinateEntity, itemPos, TransformSystem, EntityManager); diff --git a/Content.Shared/Interaction/RotateToFaceSystem.cs b/Content.Shared/Interaction/RotateToFaceSystem.cs index ed950240af..7f73d3190f 100644 --- a/Content.Shared/Interaction/RotateToFaceSystem.cs +++ b/Content.Shared/Interaction/RotateToFaceSystem.cs @@ -70,7 +70,7 @@ namespace Content.Shared.Interaction if (!Resolve(user, ref xform)) return false; - var diff = coordinates - xform.MapPosition.Position; + var diff = coordinates - _transform.GetMapCoordinates(user, xform: xform).Position; if (diff.LengthSquared() <= 0.01f) return true; diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 1f421d0e6f..8b3431cb02 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -665,14 +665,14 @@ namespace Content.Shared.Interaction else { // We'll still do the raycast from the centres but we'll bump the range as we know they're in range. - originPos = xformA.MapPosition; + originPos = _transform.GetMapCoordinates(origin, xform: xformA); range = (originPos.Position - targetPos.Position).Length(); } } // No fixtures, e.g. wallmounts. else { - originPos = Transform(origin).MapPosition; + originPos = _transform.GetMapCoordinates(origin); var otherParent = Transform(other).ParentUid; targetRot = otherParent.IsValid() ? Transform(otherParent).LocalRotation + otherAngle : otherAngle; } @@ -826,7 +826,7 @@ namespace Content.Shared.Interaction bool popup = false) { Ignored combinedPredicate = e => e == origin || (predicate?.Invoke(e) ?? false); - var originPosition = Transform(origin).MapPosition; + var originPosition = _transform.GetMapCoordinates(origin); var inRange = InRangeUnobstructed(originPosition, other, range, collisionMask, combinedPredicate, ShouldCheckAccess(origin)); if (!inRange && popup && _gameTiming.IsFirstTimePredicted) diff --git a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs index 21861f57da..03da2d09b0 100644 --- a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs +++ b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs @@ -77,7 +77,7 @@ public sealed class MagnetPickupSystem : EntitySystem // the problem is that stack pickups delete the original entity, which is fine, but due to // game state handling we can't show a lerp animation for it. var nearXform = Transform(near); - var nearMap = nearXform.MapPosition; + var nearMap = _transform.GetMapCoordinates(near, xform: nearXform); var nearCoords = EntityCoordinates.FromMap(moverCoords.EntityId, nearMap, _transform, EntityManager); if (!_storage.Insert(uid, near, out var stacked, storageComp: storage, playSound: !playedSound)) diff --git a/Content.Shared/Tabletop/SharedTabletopSystem.cs b/Content.Shared/Tabletop/SharedTabletopSystem.cs index 7bfd9d3457..afa77a643a 100644 --- a/Content.Shared/Tabletop/SharedTabletopSystem.cs +++ b/Content.Shared/Tabletop/SharedTabletopSystem.cs @@ -16,7 +16,7 @@ namespace Content.Shared.Tabletop [Dependency] protected readonly ActionBlockerSystem ActionBlockerSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedTransformSystem _transforms = default!; + [Dependency] protected readonly SharedTransformSystem Transforms = default!; [Dependency] private readonly IMapManager _mapMan = default!; public override void Initialize() @@ -41,8 +41,8 @@ namespace Content.Shared.Tabletop // Move the entity and dirty it (we use the map ID from the entity so noone can try to be funny and move the item to another map) var transform = EntityManager.GetComponent(moved); - _transforms.SetParent(moved, transform, _mapMan.GetMapEntityId(transform.MapID)); - _transforms.SetLocalPositionNoLerp(transform, msg.Coordinates.Position); + Transforms.SetParent(moved, transform, _mapMan.GetMapEntityId(transform.MapID)); + Transforms.SetLocalPositionNoLerp(transform, msg.Coordinates.Position); } private void OnDraggingPlayerChanged(TabletopDraggingPlayerChangedEvent msg, EntitySessionEventArgs args) diff --git a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs index dd297422c3..ad2249bfdd 100644 --- a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs +++ b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs @@ -220,7 +220,7 @@ public abstract partial class SharedTetherGunSystem : EntitySystem _blocker.UpdateCanMove(target); // Invisible tether entity - var tether = Spawn("TetherEntity", Transform(target).MapPosition); + var tether = Spawn("TetherEntity", TransformSystem.GetMapCoordinates(target)); var tetherPhysics = Comp(tether); component.TetherEntity = tether; _physics.WakeBody(tether); diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 11cfc88470..6242312b07 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -35,7 +35,7 @@ public abstract partial class SharedGunSystem if (args.Handled) return; - ManualCycle(uid, component, Transform(uid).MapPosition, args.User); + ManualCycle(uid, component, TransformSystem.GetMapCoordinates(uid), args.User); args.Handled = true; } @@ -161,7 +161,7 @@ public abstract partial class SharedGunSystem { Text = Loc.GetString("gun-ballistic-cycle"), Disabled = GetBallisticShots(component) == 0, - Act = () => ManualCycle(uid, component, Transform(uid).MapPosition, args.User), + Act = () => ManualCycle(uid, component, TransformSystem.GetMapCoordinates(uid), args.User), }); } From 37099e481e3341bcf82939a677ef5c0df458abbb Mon Sep 17 00:00:00 2001 From: no <165581243+pissdemon@users.noreply.github.com> Date: Sun, 12 May 2024 16:35:30 +0200 Subject: [PATCH 33/68] Prevent admin-frozen players from ghosting or suiciding, add "Freeze And Mute" verb (#27813) * prevent admin-frozen players from ghosting or suiciding * Add "Freeze and Mute" admin verb * Allow "Freeze And Mute" admin verb when player is already frozen but not muted * Remove redundant scream handler (scream action just emotes, duh) * AdminFrozenSystem: clean imports * Update Content.Server/Chat/Commands/SuicideCommand.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Ghost.cs * retrigger ci (empty commit) --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../Systems/AdminFrozenSystem.cs | 7 ++ .../Systems/AdminFrozenSystem.cs | 16 +++++ .../Administration/Systems/AdminVerbSystem.cs | 64 ++++++++++++++----- .../Chat/Commands/SuicideCommand.cs | 21 ++++-- Content.Server/Ghost/Ghost.cs | 21 ++++-- .../Administration/AdminFrozenComponent.cs | 9 ++- ...enSystem.cs => SharedAdminFrozenSystem.cs} | 20 +++++- .../en-US/administration/admin-verbs.ftl | 1 + .../en-US/chat/commands/ghost-command.ftl | 5 ++ .../en-US/chat/commands/suicide-command.ftl | 2 + 10 files changed, 138 insertions(+), 28 deletions(-) create mode 100644 Content.Client/Administration/Systems/AdminFrozenSystem.cs create mode 100644 Content.Server/Administration/Systems/AdminFrozenSystem.cs rename Content.Shared/Administration/{AdminFrozenSystem.cs => SharedAdminFrozenSystem.cs} (78%) create mode 100644 Resources/Locale/en-US/chat/commands/ghost-command.ftl diff --git a/Content.Client/Administration/Systems/AdminFrozenSystem.cs b/Content.Client/Administration/Systems/AdminFrozenSystem.cs new file mode 100644 index 0000000000..885585f985 --- /dev/null +++ b/Content.Client/Administration/Systems/AdminFrozenSystem.cs @@ -0,0 +1,7 @@ +using Content.Shared.Administration; + +namespace Content.Client.Administration.Systems; + +public sealed class AdminFrozenSystem : SharedAdminFrozenSystem +{ +} diff --git a/Content.Server/Administration/Systems/AdminFrozenSystem.cs b/Content.Server/Administration/Systems/AdminFrozenSystem.cs new file mode 100644 index 0000000000..baf7b682b8 --- /dev/null +++ b/Content.Server/Administration/Systems/AdminFrozenSystem.cs @@ -0,0 +1,16 @@ +using Content.Shared.Administration; + +namespace Content.Server.Administration.Systems; + +public sealed class AdminFrozenSystem : SharedAdminFrozenSystem +{ + /// + /// Freezes and mutes the given entity. + /// + public void FreezeAndMute(EntityUid uid) + { + var comp = EnsureComp(uid); + comp.Muted = true; + Dirty(uid, comp); + } +} diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.cs b/Content.Server/Administration/Systems/AdminVerbSystem.cs index 5bb75b4c99..7aa4c8b400 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.cs @@ -67,6 +67,7 @@ namespace Content.Server.Administration.Systems [Dependency] private readonly StationSystem _stations = default!; [Dependency] private readonly StationSpawningSystem _spawning = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; + [Dependency] private readonly AdminFrozenSystem _freeze = default!; private readonly Dictionary> _openSolutionUis = new(); @@ -131,24 +132,57 @@ namespace Content.Server.Administration.Systems args.Verbs.Add(prayerVerb); // Freeze - var frozen = HasComp(args.Target); - args.Verbs.Add(new Verb + var frozen = TryComp(args.Target, out var frozenComp); + var frozenAndMuted = frozenComp?.Muted ?? false; + + if (!frozen) { - Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze. - Text = frozen - ? Loc.GetString("admin-verbs-unfreeze") - : Loc.GetString("admin-verbs-freeze"), - Category = VerbCategory.Admin, - Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")), - Act = () => + args.Verbs.Add(new Verb { - if (frozen) - RemComp(args.Target); - else + Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze. + Text = Loc.GetString("admin-verbs-freeze"), + Category = VerbCategory.Admin, + Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")), + Act = () => + { EnsureComp(args.Target); - }, - Impact = LogImpact.Medium, - }); + }, + Impact = LogImpact.Medium, + }); + } + + if (!frozenAndMuted) + { + // allow you to additionally mute someone when they are already frozen + args.Verbs.Add(new Verb + { + Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze. + Text = Loc.GetString("admin-verbs-freeze-and-mute"), + Category = VerbCategory.Admin, + Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")), + Act = () => + { + _freeze.FreezeAndMute(args.Target); + }, + Impact = LogImpact.Medium, + }); + } + + if (frozen) + { + args.Verbs.Add(new Verb + { + Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze. + Text = Loc.GetString("admin-verbs-unfreeze"), + Category = VerbCategory.Admin, + Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")), + Act = () => + { + RemComp(args.Target); + }, + Impact = LogImpact.Medium, + }); + } // Erase args.Verbs.Add(new Verb diff --git a/Content.Server/Chat/Commands/SuicideCommand.cs b/Content.Server/Chat/Commands/SuicideCommand.cs index c967ba78d7..0c541b48b7 100644 --- a/Content.Server/Chat/Commands/SuicideCommand.cs +++ b/Content.Server/Chat/Commands/SuicideCommand.cs @@ -1,4 +1,5 @@ using Content.Server.GameTicking; +using Content.Server.Popups; using Content.Shared.Administration; using Content.Shared.Mind; using Robust.Shared.Console; @@ -26,17 +27,27 @@ namespace Content.Server.Chat.Commands if (player.Status != SessionStatus.InGame || player.AttachedEntity == null) return; - var minds = IoCManager.Resolve().System(); + var entityManager = IoCManager.Resolve(); + var minds = entityManager.System(); // This check also proves mind not-null for at the end when the mob is ghosted. if (!minds.TryGetMind(player, out var mindId, out var mind) || mind.OwnedEntity is not { Valid: true } victim) { - shell.WriteLine("You don't have a mind!"); + shell.WriteLine(Loc.GetString("suicide-command-no-mind")); return; } - var gameTicker = EntitySystem.Get(); - var suicideSystem = EntitySystem.Get(); + if (entityManager.HasComponent(victim)) + { + var deniedMessage = Loc.GetString("suicide-command-denied"); + shell.WriteLine(deniedMessage); + entityManager.System() + .PopupEntity(deniedMessage, victim, victim); + return; + } + + var gameTicker = entityManager.System(); + var suicideSystem = entityManager.System(); if (suicideSystem.Suicide(victim)) { // Prevent the player from returning to the body. @@ -48,7 +59,7 @@ namespace Content.Server.Chat.Commands if (gameTicker.OnGhostAttempt(mindId, true, mind: mind)) return; - shell.WriteLine("You can't ghost right now."); + shell.WriteLine(Loc.GetString("ghost-command-denied")); } } } diff --git a/Content.Server/Ghost/Ghost.cs b/Content.Server/Ghost/Ghost.cs index 1453bf3faa..69d81d9592 100644 --- a/Content.Server/Ghost/Ghost.cs +++ b/Content.Server/Ghost/Ghost.cs @@ -1,4 +1,5 @@ using Content.Server.GameTicking; +using Content.Server.Popups; using Content.Shared.Administration; using Content.Shared.Mind; using Robust.Shared.Console; @@ -11,15 +12,25 @@ namespace Content.Server.Ghost [Dependency] private readonly IEntityManager _entities = default!; public string Command => "ghost"; - public string Description => "Give up on life and become a ghost."; - public string Help => "ghost"; + public string Description => Loc.GetString("ghost-command-description"); + public string Help => Loc.GetString("ghost-command-help-text"); public void Execute(IConsoleShell shell, string argStr, string[] args) { var player = shell.Player; if (player == null) { - shell.WriteLine("You have no session, you can't ghost."); + shell.WriteLine(Loc.GetString("ghost-command-no-session")); + return; + } + + if (player.AttachedEntity is { Valid: true } frozen && + _entities.HasComponent(frozen)) + { + var deniedMessage = Loc.GetString("ghost-command-denied"); + shell.WriteLine(deniedMessage); + _entities.System() + .PopupEntity(deniedMessage, frozen, frozen); return; } @@ -30,9 +41,9 @@ namespace Content.Server.Ghost mind = _entities.GetComponent(mindId); } - if (!EntitySystem.Get().OnGhostAttempt(mindId, true, true, mind)) + if (!_entities.System().OnGhostAttempt(mindId, true, true, mind)) { - shell.WriteLine("You can't ghost right now."); + shell.WriteLine(Loc.GetString("ghost-command-denied")); } } } diff --git a/Content.Shared/Administration/AdminFrozenComponent.cs b/Content.Shared/Administration/AdminFrozenComponent.cs index 164cf764c8..bfcf1db526 100644 --- a/Content.Shared/Administration/AdminFrozenComponent.cs +++ b/Content.Shared/Administration/AdminFrozenComponent.cs @@ -2,8 +2,13 @@ namespace Content.Shared.Administration; -[RegisterComponent, Access(typeof(AdminFrozenSystem))] -[NetworkedComponent] +[RegisterComponent, Access(typeof(SharedAdminFrozenSystem))] +[NetworkedComponent, AutoGenerateComponentState] public sealed partial class AdminFrozenComponent : Component { + /// + /// Whether the player is also muted. + /// + [DataField, AutoNetworkedField] + public bool Muted; } diff --git a/Content.Shared/Administration/AdminFrozenSystem.cs b/Content.Shared/Administration/SharedAdminFrozenSystem.cs similarity index 78% rename from Content.Shared/Administration/AdminFrozenSystem.cs rename to Content.Shared/Administration/SharedAdminFrozenSystem.cs index 4ec9600b0b..2fa22e0005 100644 --- a/Content.Shared/Administration/AdminFrozenSystem.cs +++ b/Content.Shared/Administration/SharedAdminFrozenSystem.cs @@ -1,15 +1,17 @@ using Content.Shared.ActionBlocker; +using Content.Shared.Emoting; using Content.Shared.Interaction.Events; using Content.Shared.Item; using Content.Shared.Movement.Events; using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Events; using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Speech; using Content.Shared.Throwing; namespace Content.Shared.Administration; -public sealed class AdminFrozenSystem : EntitySystem +public abstract class SharedAdminFrozenSystem : EntitySystem { [Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly PullingSystem _pulling = default!; @@ -28,6 +30,16 @@ public sealed class AdminFrozenSystem : EntitySystem SubscribeLocalEvent(OnPullAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnEmoteAttempt); + SubscribeLocalEvent(OnSpeakAttempt); + } + + private void OnSpeakAttempt(EntityUid uid, AdminFrozenComponent component, SpeakAttemptEvent args) + { + if (!component.Muted) + return; + + args.Cancel(); } private void OnAttempt(EntityUid uid, AdminFrozenComponent component, CancellableEntityEventArgs args) @@ -62,4 +74,10 @@ public sealed class AdminFrozenSystem : EntitySystem { _blocker.UpdateCanMove(uid); } + + private void OnEmoteAttempt(EntityUid uid, AdminFrozenComponent component, EmoteAttemptEvent args) + { + if (component.Muted) + args.Cancel(); + } } diff --git a/Resources/Locale/en-US/administration/admin-verbs.ftl b/Resources/Locale/en-US/administration/admin-verbs.ftl index 03f92f7c42..16715087ee 100644 --- a/Resources/Locale/en-US/administration/admin-verbs.ftl +++ b/Resources/Locale/en-US/administration/admin-verbs.ftl @@ -6,6 +6,7 @@ admin-verbs-admin-logs-entity = Entity Logs admin-verbs-teleport-to = Teleport To admin-verbs-teleport-here = Teleport Here admin-verbs-freeze = Freeze +admin-verbs-freeze-and-mute = Freeze And Mute admin-verbs-unfreeze = Unfreeze admin-verbs-erase = Erase admin-verbs-erase-description = Removes the player from the round and crew manifest and deletes their chat messages. diff --git a/Resources/Locale/en-US/chat/commands/ghost-command.ftl b/Resources/Locale/en-US/chat/commands/ghost-command.ftl new file mode 100644 index 0000000000..08e78d34ce --- /dev/null +++ b/Resources/Locale/en-US/chat/commands/ghost-command.ftl @@ -0,0 +1,5 @@ +ghost-command-description = Give up on life and become a ghost. +ghost-command-help-text = The ghost command turns you into a ghost and makes the character you played permanently catatonic. + Please note that you cannot return to your character's body after ghosting. +ghost-command-no-session = You have no session, you can't ghost. +ghost-command-denied = You cannot ghost right now. diff --git a/Resources/Locale/en-US/chat/commands/suicide-command.ftl b/Resources/Locale/en-US/chat/commands/suicide-command.ftl index 6748aa630c..36e861169b 100644 --- a/Resources/Locale/en-US/chat/commands/suicide-command.ftl +++ b/Resources/Locale/en-US/chat/commands/suicide-command.ftl @@ -6,3 +6,5 @@ suicide-command-help-text = The suicide command gives you a quick way out of a r suicide-command-default-text-others = {$name} is attempting to bite their own tongue! suicide-command-default-text-self = You attempt to bite your own tongue! suicide-command-already-dead = You can't suicide. You're dead. +suicide-command-no-mind = You have no mind! +suicide-command-denied = You cannot suicide right now. From 17e0a7f56c4791b7d8443240826db811aafd2c13 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 12 May 2024 14:36:37 +0000 Subject: [PATCH 34/68] Automatic changelog update --- Resources/Changelog/Admin.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index f5d74cf9b6..a534a83ebf 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -201,5 +201,17 @@ Entries: id: 26 time: '2024-05-02T06:23:23.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/24945 +- author: pissdemon + changes: + - message: Players frozen by admins can no longer use the "ghost" or "suicide" console + commands. + type: Tweak + - message: Added a "Freeze And Mute" admin verb. In addition to acting like Freeze, + it will mute the targeted player, which prevents them from speaking or emoting + until unfrozen. It can also be used on already frozen but not yet muted players. + type: Add + id: 27 + time: '2024-05-12T14:35:30.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27813 Name: Admin Order: 1 From ed7075942d827100408429cd0e1bc4d9bbacfc5d Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sun, 12 May 2024 07:36:46 -0700 Subject: [PATCH 35/68] Add live templates for networked data field, networked component and auto state component (#27906) * Add live templates for networked data field, networked component and auto state component * Fix field access * Fix readonly --- SpaceStation14.sln.DotSettings | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 0decb9a11b..800617c0fd 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -228,6 +228,32 @@ public sealed partial class $CLASS$ : IPrototype { public string ID { get; } = default!; } SS14 + True + True + Networked Auto State Component + True + 0 + True + True + 2.0 + InCSharpFile + nscomp + True + [RegisterComponent, Robust.Shared.GameStates.NetworkedComponent, AutoGenerateComponentState] +[Access(typeof($ACCESS$))] + True + True + Networked Component + True + 0 + True + True + 2.0 + InCSharpFile + ncomp + True + [RegisterComponent, Robust.Shared.GameStates.NetworkedComponent] +[Access(typeof($ACCESS$))] True True IoC [Dependency] @@ -465,6 +491,21 @@ public abstract class $CLASS$ : EntitySystem { } } SS14 + True + True + Automatically Networked Data Field + True + 1 + True + 0 + True + True + 2.0 + InCSharpTypeMember + nfield + True + [DataField, AutoNetworkedField] +public $TYPE$ $NAME$; True True cs From c8b55e5e449fc4f43206c3a6a5a84ef53023cd18 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 36/68] 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 sealed class GeneralStationRecordConsoleSystem : EntitySystem 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 @@ public sealed class GeneralStationRecordConsoleSystem : EntitySystem 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 @@ public sealed class GeneralStationRecordConsoleSystem : EntitySystem 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 sealed class SelectStationRecord : BoundUserInterfaceMessage 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 38092e3aa7..8171ec0053 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -70,6 +70,7 @@ - type: CargoOrderConsole - type: CrewMonitoringConsole - type: GeneralStationRecordConsole + canDeleteEntries: true - type: DeviceNetwork deviceNetId: Wireless receiveFrequencyId: CrewMonitor From cfee7e3fdceccf4808d49ce305ed105e62766491 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 12 May 2024 15:09:00 +0000 Subject: [PATCH 37/68] Automatic changelog update --- Resources/Changelog/Admin.yml | 9 +++++++++ Resources/Changelog/Changelog.yml | 14 +++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index a534a83ebf..5129de7563 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -213,5 +213,14 @@ Entries: id: 27 time: '2024-05-12T14:35:30.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27813 +- author: nikthechampiongr + changes: + - message: Admins are now able to remove station records through the records console + interface on their aghost. This will remove the record from the manifest and + criminal records as well. + type: Add + id: 28 + time: '2024-05-12T15:07:54.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27883 Name: Admin Order: 1 diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c5e4d32fa2..ae93e07f82 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Nairodian - changes: - - message: Welding masks can now be made in the Autolathe. - type: Add - id: 6087 - time: '2024-03-04T22:36:06.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25842 - author: Lawdog changes: - message: Changed the max volume of shot glasses from 7u to 5u to better represent @@ -3870,3 +3863,10 @@ id: 6586 time: '2024-05-11T23:18:21.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27576 +- author: nikthechampiongr + changes: + - message: Fixed records consoles sometimes failing to show crew records. + type: Fix + id: 6587 + time: '2024-05-12T15:07:54.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27883 From 9c3dab0be3fa2ecc66a21b471e5e78be8fcf43d0 Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Sun, 12 May 2024 15:14:35 -0700 Subject: [PATCH 38/68] Fix borg UI regenerating every tick (#27956) * Fix UI elements being recreated when they didn't need to be * Fix up comparison --- .../Silicons/Borgs/BorgMenu.xaml.cs | 19 ++++++++++++++++- .../Laws/Ui/SiliconLawBoundUserInterface.cs | 21 ++++++++++++++++++- .../Silicons/Laws/SiliconLawPrototype.cs | 7 +++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs index 046d8e299f..474a83b453 100644 --- a/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs +++ b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs @@ -25,6 +25,7 @@ public sealed partial class BorgMenu : FancyWindow public readonly EntityUid Entity; public float AccumulatedTime; private string _lastValidName; + private List _modules = new(); public BorgMenu(EntityUid entity) { @@ -114,7 +115,23 @@ public sealed partial class BorgMenu : FancyWindow ("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(); + _modules.Clear(); foreach (var module in _chassis.ModuleContainer.ContainedEntities) { var control = new BorgModuleControl(module, _entity); @@ -123,6 +140,7 @@ public sealed partial class BorgMenu : FancyWindow RemoveModuleButtonPressed?.Invoke(module); }; ModuleContainer.AddChild(control); + _modules.Add(module); } } @@ -162,4 +180,3 @@ public sealed partial class BorgMenu : FancyWindow NameChanged?.Invoke(_lastValidName); } } - diff --git a/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs index 2aee0a38c0..d150735fa1 100644 --- a/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs +++ b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs @@ -1,6 +1,7 @@ +using System.Linq; +using Content.Shared.Silicons.Laws; using Content.Shared.Silicons.Laws.Components; using JetBrains.Annotations; -using Robust.Client.GameObjects; namespace Content.Client.Silicons.Laws.Ui; @@ -10,6 +11,7 @@ public sealed class SiliconLawBoundUserInterface : BoundUserInterface [ViewVariables] private SiliconLawMenu? _menu; private EntityUid _owner; + private List? _laws; public SiliconLawBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { @@ -41,6 +43,23 @@ public sealed class SiliconLawBoundUserInterface : BoundUserInterface if (state is not SiliconLawBuiState msg) return; + if (_laws != null && _laws.Count == msg.Laws.Count) + { + var isSame = true; + foreach (var law in msg.Laws) + { + if (_laws.Contains(law)) + continue; + isSame = false; + break; + } + + if (isSame) + return; + } + + _laws = msg.Laws.ToList(); + _menu?.Update(_owner, msg); } } diff --git a/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs b/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs index 5e5df448b3..d10b86c241 100644 --- a/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs +++ b/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs @@ -39,6 +39,13 @@ public partial class SiliconLaw : IComparable return Order.CompareTo(other.Order); } + public bool Equals(SiliconLaw other) + { + return LawString == other.LawString + && Order == other.Order + && LawIdentifierOverride == other.LawIdentifierOverride; + } + /// /// Return a shallow clone of this law. /// From c325ca8566f1032f24be7140e9bf94f04d740200 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 12 May 2024 22:15:41 +0000 Subject: [PATCH 39/68] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ae93e07f82..90888f0788 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Lawdog - changes: - - message: Changed the max volume of shot glasses from 7u to 5u to better represent - drinking shots in real life - type: Tweak - id: 6088 - time: '2024-03-04T23:51:12.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25847 - author: deltanedas changes: - message: Rehydrated mobs (carp) now keep their forensics data from their cube/plushie @@ -3870,3 +3862,11 @@ id: 6587 time: '2024-05-12T15:07:54.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27883 +- author: ShadowCommander + changes: + - message: Fixed cyborg laws window and other UIs breaking when the built in light + was on. + type: Fix + id: 6588 + time: '2024-05-12T22:14:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27956 From d6e2cc0a8b1d93ffdfa184a694183ea7c8b7a1e4 Mon Sep 17 00:00:00 2001 From: Kara Date: Sun, 12 May 2024 17:03:07 -0700 Subject: [PATCH 40/68] Open slot storage keybind can now also close the UI (#27962) --- .../Storage/EntitySystems/SharedStorageSystem.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 8ad588baab..4f0b760345 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -1433,15 +1433,15 @@ public abstract class SharedStorageSystem : EntitySystem 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; @@ -1455,7 +1455,14 @@ public abstract class SharedStorageSystem : EntitySystem if (!ActionBlocker.CanInteract(playerEnt, storageEnt)) return; - OpenStorageUI(storageEnt.Value, playerEnt); + if (!_ui.IsUiOpen(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt)) + { + OpenStorageUI(storageEnt.Value, playerEnt); + } + else + { + _ui.CloseUi(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt); + } } protected void ClearCantFillReasons() From efc430f651192fe0d6e2b7a076eb1ed2ed59192f Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 13 May 2024 00:04:13 +0000 Subject: [PATCH 41/68] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 90888f0788..a979fa6f89 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: deltanedas - changes: - - message: Rehydrated mobs (carp) now keep their forensics data from their cube/plushie - form. - type: Tweak - id: 6089 - time: '2024-03-05T03:13:50.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25148 - author: Brandon-Huu changes: - message: Removed the ability for command or any antag-safe role to be initial @@ -3870,3 +3862,11 @@ id: 6588 time: '2024-05-12T22:14:35.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27956 +- author: mirrorcult + changes: + - message: The open slot storage keybinds (V, shift+V) now close the window if it's + already opened + type: Tweak + id: 6589 + time: '2024-05-13T00:03:07.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27962 From 993eef1e7c22b2b79e528967ef5bb669f50236b1 Mon Sep 17 00:00:00 2001 From: Kara Date: Sun, 12 May 2024 17:34:52 -0700 Subject: [PATCH 42/68] Resolve `'EntitySystem.Get()' is obsolete` in content (#27936) * PROJECT 0 WARNINGS: Resolve `'EntitySystem.Get()' is obsolete` in content * pass entman * dog ass test * webeditor --- Content.Client/Decals/ToggleDecalCommand.cs | 4 +++- .../Decals/UI/DecalPlacerWindow.xaml.cs | 3 ++- .../Systems/NetworkConfiguratorSystem.cs | 4 +++- Content.Client/NodeContainer/NodeVisCommand.cs | 8 ++++++-- .../Tests/Commands/RestartRoundTest.cs | 4 ++-- Content.Server/Administration/Commands/DSay.cs | 4 +++- .../Administration/Commands/ReadyAll.cs | 4 +++- .../Administration/Commands/ShuttleCommands.cs | 10 +++++++--- .../Commands/ThrowScoreboardCommand.cs | 4 +++- .../Administration/Logs/AdminLogsEui.cs | 3 ++- .../Administration/UI/SpawnExplosionEui.cs | 2 +- Content.Server/Alert/Commands/ClearAlert.cs | 6 ++++-- Content.Server/Alert/Commands/ShowAlert.cs | 6 ++++-- .../Atmos/Commands/ListGasesCommand.cs | 4 +++- .../Atmos/Commands/ShowAtmosCommand.cs | 4 +++- .../Atmos/Components/AtmosPlaqueComponent.cs | 2 +- .../Atmos/Monitor/Systems/AirAlarmModes.cs | 4 ++-- .../Piping/Components/AtmosPipeColorComponent.cs | 2 +- Content.Server/Chat/Commands/LOOCCommand.cs | 4 +++- Content.Server/Chat/Commands/SuicideCommand.cs | 16 ++++++++++------ .../Chemistry/EntitySystems/VaporSystem.cs | 2 +- .../ReactionEffects/ExplosionReactionEffect.cs | 3 ++- .../ReagentEffectConditions/HasTagCondition.cs | 2 +- .../ReagentEffects/ChemCleanBloodstream.cs | 2 +- .../ReagentEffects/ExtinguishReaction.cs | 2 +- .../Chemistry/ReagentEffects/Ignite.cs | 2 +- .../ReagentEffects/ModifyBleedAmount.cs | 2 +- .../Chemistry/ReagentEffects/ModifyBloodLevel.cs | 2 +- .../ReagentEffects/MovespeedModifier.cs | 2 +- .../Chemistry/ReagentEffects/Oxygenate.cs | 2 +- .../Chemistry/ReagentEffects/Paralyze.cs | 2 +- .../Chemistry/ReagentEffects/SatiateThirst.cs | 2 +- .../ReagentEffects/WashCreamPieReaction.cs | 2 +- .../TileReactions/CleanDecalsReaction.cs | 15 ++++++++------- .../Chemistry/TileReactions/CleanTileReaction.cs | 12 +++++++----- .../TileReactions/CreateEntityTileReaction.cs | 12 ++++++------ .../TileReactions/ExtinguishTileReaction.cs | 7 +++++-- .../TileReactions/FlammableTileReaction.cs | 7 +++++-- .../Chemistry/TileReactions/PryTileReaction.cs | 7 +++++-- .../SpillIfPuddlePresentTileReaction.cs | 7 +++++-- .../Chemistry/TileReactions/SpillTileReaction.cs | 7 ++++--- .../Thresholds/Behaviors/EjectVendorItems.cs | 2 +- .../Thresholds/Behaviors/OpenBehavior.cs | 2 +- .../Thresholds/Behaviors/SpillBehavior.cs | 4 ++-- .../Fluids/EntitySystems/PuddleSystem.cs | 2 +- .../Fluids/EntitySystems/SmokeSystem.cs | 2 +- .../GameTicking/Commands/DelayStartCommand.cs | 4 +++- .../GameTicking/Commands/EndRoundCommand.cs | 4 +++- .../GameTicking/Commands/ForcePresetCommand.cs | 4 +++- .../GameTicking/Commands/GoLobbyCommand.cs | 4 +++- .../GameTicking/Commands/ObserveCommand.cs | 4 +++- .../GameTicking/Commands/RestartRoundCommand.cs | 10 +++++++--- .../GameTicking/Commands/StartRoundCommand.cs | 4 +++- .../GameTicking/Commands/ToggleReadyCommand.cs | 4 +++- .../Ghost/Roles/Components/GhostRoleComponent.cs | 6 +++--- Content.Server/Ghost/Roles/GhostRoleSystem.cs | 4 +++- Content.Server/Ghost/Roles/UI/GhostRolesEui.cs | 2 +- .../Maps/Conditions/HolidayMapCondition.cs | 2 +- Content.Server/NodeContainer/Nodes/PipeNode.cs | 6 +++--- .../Power/Commands/PowerStatCommand.cs | 4 +++- Content.Server/Power/PowerWireAction.cs | 2 +- .../Sandbox/Commands/ColorNetworkCommand.cs | 2 +- .../Chemistry/Reaction/ITileReaction.cs | 5 ++++- .../Chemistry/Reagent/ReagentPrototype.cs | 4 ++-- .../Shared/Alert/ServerAlertsComponentTests.cs | 10 +++++----- 65 files changed, 186 insertions(+), 110 deletions(-) diff --git a/Content.Client/Decals/ToggleDecalCommand.cs b/Content.Client/Decals/ToggleDecalCommand.cs index 9f0851f080..025ed1299d 100644 --- a/Content.Client/Decals/ToggleDecalCommand.cs +++ b/Content.Client/Decals/ToggleDecalCommand.cs @@ -5,11 +5,13 @@ namespace Content.Client.Decals; public sealed class ToggleDecalCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "toggledecals"; public string Description => "Toggles decaloverlay"; public string Help => $"{Command}"; public void Execute(IConsoleShell shell, string argStr, string[] args) { - EntitySystem.Get().ToggleOverlay(); + _e.System().ToggleOverlay(); } } diff --git a/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs b/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs index 1be1751080..21b816515a 100644 --- a/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs +++ b/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs @@ -16,6 +16,7 @@ namespace Content.Client.Decals.UI; public sealed partial class DecalPlacerWindow : DefaultWindow { [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IEntityManager _e = default!; private readonly DecalPlacementSystem _decalPlacementSystem; @@ -39,7 +40,7 @@ public sealed partial class DecalPlacerWindow : DefaultWindow RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - _decalPlacementSystem = EntitySystem.Get(); + _decalPlacementSystem = _e.System(); // This needs to be done in C# so we can have custom stuff passed in the constructor // and thus have a proper step size diff --git a/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs b/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs index 2fd4f29655..2d561ba5f2 100644 --- a/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs +++ b/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs @@ -139,11 +139,13 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem public sealed class ClearAllNetworkLinkOverlays : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "clearnetworklinkoverlays"; public string Description => "Clear all network link overlays."; public string Help => Command; public void Execute(IConsoleShell shell, string argStr, string[] args) { - IoCManager.Resolve().System().ClearAllOverlays(); + _e.System().ClearAllOverlays(); } } diff --git a/Content.Client/NodeContainer/NodeVisCommand.cs b/Content.Client/NodeContainer/NodeVisCommand.cs index c6a95fce5b..249c0d9427 100644 --- a/Content.Client/NodeContainer/NodeVisCommand.cs +++ b/Content.Client/NodeContainer/NodeVisCommand.cs @@ -8,6 +8,8 @@ namespace Content.Client.NodeContainer { public sealed class NodeVisCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "nodevis"; public string Description => "Toggles node group visualization"; public string Help => ""; @@ -21,20 +23,22 @@ namespace Content.Client.NodeContainer return; } - var sys = EntitySystem.Get(); + var sys = _e.System(); sys.SetVisEnabled(!sys.VisEnabled); } } public sealed class NodeVisFilterCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "nodevisfilter"; public string Description => "Toggles showing a specific group on nodevis"; public string Help => "Usage: nodevis [filter]\nOmit filter to list currently masked-off"; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var sys = EntitySystem.Get(); + var sys = _e.System(); if (args.Length == 0) { diff --git a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs index 74d014b772..ff24ec0968 100644 --- a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs +++ b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs @@ -3,6 +3,7 @@ using Content.Server.GameTicking.Commands; using Content.Shared.CCVar; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Timing; namespace Content.IntegrationTests.Tests.Commands @@ -40,8 +41,7 @@ namespace Content.IntegrationTests.Tests.Commands tickBeforeRestart = entityManager.CurrentTick; - var command = new RestartRoundNowCommand(); - command.Execute(null, string.Empty, Array.Empty()); + gameTicker.RestartRound(); if (lobbyEnabled) { diff --git a/Content.Server/Administration/Commands/DSay.cs b/Content.Server/Administration/Commands/DSay.cs index 61b47d7856..8682614b5f 100644 --- a/Content.Server/Administration/Commands/DSay.cs +++ b/Content.Server/Administration/Commands/DSay.cs @@ -7,6 +7,8 @@ namespace Content.Server.Administration.Commands [AdminCommand(AdminFlags.Admin)] sealed class DSay : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "dsay"; public string Description => Loc.GetString("dsay-command-description"); @@ -32,7 +34,7 @@ namespace Content.Server.Administration.Commands if (string.IsNullOrEmpty(message)) return; - var chat = EntitySystem.Get(); + var chat = _e.System(); chat.TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Dead, false, shell, player); } } diff --git a/Content.Server/Administration/Commands/ReadyAll.cs b/Content.Server/Administration/Commands/ReadyAll.cs index d55ef09660..530ba0e89c 100644 --- a/Content.Server/Administration/Commands/ReadyAll.cs +++ b/Content.Server/Administration/Commands/ReadyAll.cs @@ -8,6 +8,8 @@ namespace Content.Server.Administration.Commands [AdminCommand(AdminFlags.Round)] public sealed class ReadyAll : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "readyall"; public string Description => "Readies up all players in the lobby, except for observers."; public string Help => $"{Command} | ̣{Command} "; @@ -20,7 +22,7 @@ namespace Content.Server.Administration.Commands ready = bool.Parse(args[0]); } - var gameTicker = EntitySystem.Get(); + var gameTicker = _e.System(); if (gameTicker.RunLevel != GameRunLevel.PreRoundLobby) diff --git a/Content.Server/Administration/Commands/ShuttleCommands.cs b/Content.Server/Administration/Commands/ShuttleCommands.cs index 5b0d39fd15..ea0f891360 100644 --- a/Content.Server/Administration/Commands/ShuttleCommands.cs +++ b/Content.Server/Administration/Commands/ShuttleCommands.cs @@ -8,6 +8,8 @@ namespace Content.Server.Administration.Commands [AdminCommand(AdminFlags.Round)] public sealed class CallShuttleCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "callshuttle"; public string Description => Loc.GetString("call-shuttle-command-description"); public string Help => Loc.GetString("call-shuttle-command-help-text", ("command",Command)); @@ -19,7 +21,7 @@ namespace Content.Server.Administration.Commands // ReSharper disable once ConvertIfStatementToSwitchStatement if (args.Length == 1 && TimeSpan.TryParseExact(args[0], ContentLocalizationManager.TimeSpanMinutesFormats, loc.DefaultCulture, out var timeSpan)) { - EntitySystem.Get().RequestRoundEnd(timeSpan, shell.Player?.AttachedEntity, false); + _e.System().RequestRoundEnd(timeSpan, shell.Player?.AttachedEntity, false); } else if (args.Length == 1) { @@ -27,7 +29,7 @@ namespace Content.Server.Administration.Commands } else { - EntitySystem.Get().RequestRoundEnd(shell.Player?.AttachedEntity, false); + _e.System().RequestRoundEnd(shell.Player?.AttachedEntity, false); } } } @@ -35,13 +37,15 @@ namespace Content.Server.Administration.Commands [AdminCommand(AdminFlags.Round)] public sealed class RecallShuttleCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "recallshuttle"; public string Description => Loc.GetString("recall-shuttle-command-description"); public string Help => Loc.GetString("recall-shuttle-command-help-text", ("command",Command)); public void Execute(IConsoleShell shell, string argStr, string[] args) { - EntitySystem.Get().CancelRoundEndCountdown(shell.Player?.AttachedEntity, false); + _e.System().CancelRoundEndCountdown(shell.Player?.AttachedEntity, false); } } } diff --git a/Content.Server/Administration/Commands/ThrowScoreboardCommand.cs b/Content.Server/Administration/Commands/ThrowScoreboardCommand.cs index ed4ec5a515..e66efe809a 100644 --- a/Content.Server/Administration/Commands/ThrowScoreboardCommand.cs +++ b/Content.Server/Administration/Commands/ThrowScoreboardCommand.cs @@ -7,6 +7,8 @@ namespace Content.Server.Administration.Commands; [AdminCommand(AdminFlags.VarEdit)] public sealed class ThrowScoreboardCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "throwscoreboard"; public string Description => Loc.GetString("throw-scoreboard-command-description"); @@ -20,6 +22,6 @@ public sealed class ThrowScoreboardCommand : IConsoleCommand shell.WriteLine(Help); return; } - EntitySystem.Get().ShowRoundEndScoreboard(); + _e.System().ShowRoundEndScoreboard(); } } diff --git a/Content.Server/Administration/Logs/AdminLogsEui.cs b/Content.Server/Administration/Logs/AdminLogsEui.cs index 8bd870b2c7..e98a3b0ff2 100644 --- a/Content.Server/Administration/Logs/AdminLogsEui.cs +++ b/Content.Server/Administration/Logs/AdminLogsEui.cs @@ -23,6 +23,7 @@ public sealed class AdminLogsEui : BaseEui [Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IConfigurationManager _configuration = default!; + [Dependency] private readonly IEntityManager _e = default!; private readonly ISawmill _sawmill; @@ -51,7 +52,7 @@ public sealed class AdminLogsEui : BaseEui }; } - private int CurrentRoundId => EntitySystem.Get().RoundId; + private int CurrentRoundId => _e.System().RoundId; public override async void Opened() { diff --git a/Content.Server/Administration/UI/SpawnExplosionEui.cs b/Content.Server/Administration/UI/SpawnExplosionEui.cs index a3729b1454..001ce1efee 100644 --- a/Content.Server/Administration/UI/SpawnExplosionEui.cs +++ b/Content.Server/Administration/UI/SpawnExplosionEui.cs @@ -31,7 +31,7 @@ public sealed class SpawnExplosionEui : BaseEui if (request.TotalIntensity <= 0 || request.IntensitySlope <= 0) return; - var explosion = EntitySystem.Get().GenerateExplosionPreview(request); + var explosion = _explosionSystem.GenerateExplosionPreview(request); if (explosion == null) { diff --git a/Content.Server/Alert/Commands/ClearAlert.cs b/Content.Server/Alert/Commands/ClearAlert.cs index 1759612702..73a6ca52c7 100644 --- a/Content.Server/Alert/Commands/ClearAlert.cs +++ b/Content.Server/Alert/Commands/ClearAlert.cs @@ -9,6 +9,8 @@ namespace Content.Server.Alert.Commands [AdminCommand(AdminFlags.Debug)] public sealed class ClearAlert : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "clearalert"; public string Description => "Clears an alert for a player, defaulting to current player"; public string Help => "clearalert "; @@ -30,14 +32,14 @@ namespace Content.Server.Alert.Commands if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return; } - if (!IoCManager.Resolve().TryGetComponent(attachedEntity, out AlertsComponent? alertsComponent)) + if (!_e.TryGetComponent(attachedEntity, out AlertsComponent? alertsComponent)) { shell.WriteLine("user has no alerts component"); return; } var alertType = args[0]; - var alertsSystem = EntitySystem.Get(); + var alertsSystem = _e.System(); if (!alertsSystem.TryGet(Enum.Parse(alertType), out var alert)) { shell.WriteLine("unrecognized alertType " + alertType); diff --git a/Content.Server/Alert/Commands/ShowAlert.cs b/Content.Server/Alert/Commands/ShowAlert.cs index 11901e9af0..f37ab23f2f 100644 --- a/Content.Server/Alert/Commands/ShowAlert.cs +++ b/Content.Server/Alert/Commands/ShowAlert.cs @@ -9,6 +9,8 @@ namespace Content.Server.Alert.Commands [AdminCommand(AdminFlags.Debug)] public sealed class ShowAlert : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "showalert"; public string Description => "Shows an alert for a player, defaulting to current player"; public string Help => "showalert "; @@ -30,7 +32,7 @@ namespace Content.Server.Alert.Commands if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return; } - if (!IoCManager.Resolve().TryGetComponent(attachedEntity, out AlertsComponent? alertsComponent)) + if (!_e.TryGetComponent(attachedEntity, out AlertsComponent? alertsComponent)) { shell.WriteLine("user has no alerts component"); return; @@ -38,7 +40,7 @@ namespace Content.Server.Alert.Commands var alertType = args[0]; var severity = args[1]; - var alertsSystem = EntitySystem.Get(); + var alertsSystem = _e.System(); if (!alertsSystem.TryGet(Enum.Parse(alertType), out var alert)) { shell.WriteLine("unrecognized alertType " + alertType); diff --git a/Content.Server/Atmos/Commands/ListGasesCommand.cs b/Content.Server/Atmos/Commands/ListGasesCommand.cs index ab792fcf81..81acc2b3b5 100644 --- a/Content.Server/Atmos/Commands/ListGasesCommand.cs +++ b/Content.Server/Atmos/Commands/ListGasesCommand.cs @@ -8,13 +8,15 @@ namespace Content.Server.Atmos.Commands [AdminCommand(AdminFlags.Debug)] public sealed class ListGasesCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "listgases"; public string Description => "Prints a list of gases and their indices."; public string Help => "listgases"; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var atmosSystem = EntitySystem.Get(); + var atmosSystem = _e.System(); foreach (var gasPrototype in atmosSystem.Gases) { diff --git a/Content.Server/Atmos/Commands/ShowAtmosCommand.cs b/Content.Server/Atmos/Commands/ShowAtmosCommand.cs index 263ef947d0..89b9c846ec 100644 --- a/Content.Server/Atmos/Commands/ShowAtmosCommand.cs +++ b/Content.Server/Atmos/Commands/ShowAtmosCommand.cs @@ -8,6 +8,8 @@ namespace Content.Server.Atmos.Commands [AdminCommand(AdminFlags.Debug)] public sealed class ShowAtmos : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "showatmos"; public string Description => "Toggles seeing atmos debug overlay."; public string Help => $"Usage: {Command}"; @@ -21,7 +23,7 @@ namespace Content.Server.Atmos.Commands return; } - var atmosDebug = EntitySystem.Get(); + var atmosDebug = _e.System(); var enabled = atmosDebug.ToggleObserver(player); shell.WriteLine(enabled diff --git a/Content.Server/Atmos/Components/AtmosPlaqueComponent.cs b/Content.Server/Atmos/Components/AtmosPlaqueComponent.cs index d86cbe1d2f..3332d227e4 100644 --- a/Content.Server/Atmos/Components/AtmosPlaqueComponent.cs +++ b/Content.Server/Atmos/Components/AtmosPlaqueComponent.cs @@ -14,7 +14,7 @@ namespace Content.Server.Atmos.Components set { Type = value; - EntitySystem.Get().UpdateSign(Owner, this); + IoCManager.Resolve().System().UpdateSign(Owner, this); } } } diff --git a/Content.Server/Atmos/Monitor/Systems/AirAlarmModes.cs b/Content.Server/Atmos/Monitor/Systems/AirAlarmModes.cs index d794be0d56..f979f4b860 100644 --- a/Content.Server/Atmos/Monitor/Systems/AirAlarmModes.cs +++ b/Content.Server/Atmos/Monitor/Systems/AirAlarmModes.cs @@ -90,8 +90,8 @@ public abstract class AirAlarmModeExecutor : IAirAlarmMode { IoCManager.InjectDependencies(this); - DeviceNetworkSystem = EntitySystem.Get(); - AirAlarmSystem = EntitySystem.Get(); + DeviceNetworkSystem = EntityManager.System(); + AirAlarmSystem = EntityManager.System(); } } diff --git a/Content.Server/Atmos/Piping/Components/AtmosPipeColorComponent.cs b/Content.Server/Atmos/Piping/Components/AtmosPipeColorComponent.cs index 455d125e44..5b05668ad5 100644 --- a/Content.Server/Atmos/Piping/Components/AtmosPipeColorComponent.cs +++ b/Content.Server/Atmos/Piping/Components/AtmosPipeColorComponent.cs @@ -13,7 +13,7 @@ namespace Content.Server.Atmos.Piping.Components public Color ColorVV { get => Color; - set => EntitySystem.Get().SetColor(Owner, this, value); + set => IoCManager.Resolve().System().SetColor(Owner, this, value); } } } diff --git a/Content.Server/Chat/Commands/LOOCCommand.cs b/Content.Server/Chat/Commands/LOOCCommand.cs index 9e16193fc3..059d25df27 100644 --- a/Content.Server/Chat/Commands/LOOCCommand.cs +++ b/Content.Server/Chat/Commands/LOOCCommand.cs @@ -8,6 +8,8 @@ namespace Content.Server.Chat.Commands [AnyCommand] internal sealed class LOOCCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "looc"; public string Description => "Send Local Out Of Character chat messages."; public string Help => "looc "; @@ -33,7 +35,7 @@ namespace Content.Server.Chat.Commands if (string.IsNullOrEmpty(message)) return; - EntitySystem.Get().TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Looc, false, shell, player); + _e.System().TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Looc, false, shell, player); } } } diff --git a/Content.Server/Chat/Commands/SuicideCommand.cs b/Content.Server/Chat/Commands/SuicideCommand.cs index 0c541b48b7..0db03fec79 100644 --- a/Content.Server/Chat/Commands/SuicideCommand.cs +++ b/Content.Server/Chat/Commands/SuicideCommand.cs @@ -10,6 +10,8 @@ namespace Content.Server.Chat.Commands [AnyCommand] internal sealed class SuicideCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "suicide"; public string Description => Loc.GetString("suicide-command-description"); @@ -27,8 +29,8 @@ namespace Content.Server.Chat.Commands if (player.Status != SessionStatus.InGame || player.AttachedEntity == null) return; - var entityManager = IoCManager.Resolve(); - var minds = entityManager.System(); + var minds = _e.System(); + // This check also proves mind not-null for at the end when the mob is ghosted. if (!minds.TryGetMind(player, out var mindId, out var mind) || mind.OwnedEntity is not { Valid: true } victim) @@ -37,17 +39,19 @@ namespace Content.Server.Chat.Commands return; } - if (entityManager.HasComponent(victim)) + + var gameTicker = _e.System(); + var suicideSystem = _e.System(); + + if (_e.HasComponent(victim)) { var deniedMessage = Loc.GetString("suicide-command-denied"); shell.WriteLine(deniedMessage); - entityManager.System() + _e.System() .PopupEntity(deniedMessage, victim, victim); return; } - var gameTicker = entityManager.System(); - var suicideSystem = entityManager.System(); if (suicideSystem.Suicide(victim)) { // Prevent the player from returning to the body. diff --git a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs index 1415b6cad3..8e12670ca6 100644 --- a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs @@ -122,7 +122,7 @@ namespace Content.Server.Chemistry.EntitySystems var reagent = _protoManager.Index(reagentQuantity.Reagent.Prototype); var reaction = - reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f); + reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f, EntityManager); if (reaction > reagentQuantity.Quantity) { diff --git a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs index 8a558a3ffd..2771f79c3a 100644 --- a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs @@ -61,7 +61,8 @@ namespace Content.Server.Chemistry.ReactionEffects { var intensity = MathF.Min((float) args.Quantity * IntensityPerUnit, MaxTotalIntensity); - EntitySystem.Get().QueueExplosion( + args.EntityManager.System() + .QueueExplosion( args.SolutionEntity, ExplosionType, intensity, diff --git a/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs b/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs index 52d4d00eb3..d185c7e0a7 100644 --- a/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs +++ b/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs @@ -18,7 +18,7 @@ public sealed partial class HasTag : ReagentEffectCondition public override bool Condition(ReagentEffectArgs args) { if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var tag)) - return EntitySystem.Get().HasTag(tag, Tag) ^ Invert; + return args.EntityManager.System().HasTag(tag, Tag) ^ Invert; return false; } diff --git a/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs b/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs index 402b30a069..ace73c84f3 100644 --- a/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs +++ b/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs @@ -26,7 +26,7 @@ namespace Content.Server.Chemistry.ReactionEffects cleanseRate *= args.Scale; - var bloodstreamSys = EntitySystem.Get(); + var bloodstreamSys = args.EntityManager.System(); bloodstreamSys.FlushChemicals(args.SolutionEntity, args.Reagent.ID, cleanseRate); } } diff --git a/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs b/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs index e902908998..69aaf5cdcd 100644 --- a/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs +++ b/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs @@ -16,7 +16,7 @@ namespace Content.Server.Chemistry.ReagentEffects { if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out FlammableComponent? flammable)) return; - var flammableSystem = EntitySystem.Get(); + var flammableSystem = args.EntityManager.System(); flammableSystem.Extinguish(args.SolutionEntity, flammable); flammableSystem.AdjustFireStacks(args.SolutionEntity, -1.5f * (float) args.Quantity, flammable); } diff --git a/Content.Server/Chemistry/ReagentEffects/Ignite.cs b/Content.Server/Chemistry/ReagentEffects/Ignite.cs index 1fc90f63e3..6c5d47711f 100644 --- a/Content.Server/Chemistry/ReagentEffects/Ignite.cs +++ b/Content.Server/Chemistry/ReagentEffects/Ignite.cs @@ -19,7 +19,7 @@ public sealed partial class Ignite : ReagentEffect public override void Effect(ReagentEffectArgs args) { - var flamSys = EntitySystem.Get(); + var flamSys = args.EntityManager.System(); flamSys.Ignite(args.SolutionEntity, args.OrganEntity ?? args.SolutionEntity); } } diff --git a/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs b/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs index 7b966ea478..ecd9c86255 100644 --- a/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs +++ b/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs @@ -21,7 +21,7 @@ public sealed partial class ModifyBleedAmount : ReagentEffect { if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var blood)) { - var sys = EntitySystem.Get(); + var sys = args.EntityManager.System(); var amt = Scaled ? Amount * args.Quantity.Float() : Amount; amt *= args.Scale; diff --git a/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs b/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs index 748aa71083..b136ff9bb8 100644 --- a/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs +++ b/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs @@ -22,7 +22,7 @@ public sealed partial class ModifyBloodLevel : ReagentEffect { if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var blood)) { - var sys = EntitySystem.Get(); + var sys = args.EntityManager.System(); var amt = Scaled ? Amount * args.Quantity : Amount; amt *= args.Scale; diff --git a/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs index 0301742c5a..e538a011f6 100644 --- a/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs +++ b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs @@ -59,7 +59,7 @@ namespace Content.Server.Chemistry.ReagentEffects IncreaseTimer(status, statusLifetime); if (modified) - EntitySystem.Get().RefreshMovementSpeedModifiers(args.SolutionEntity); + args.EntityManager.System().RefreshMovementSpeedModifiers(args.SolutionEntity); } public void IncreaseTimer(MovespeedModifierMetabolismComponent status, float time) diff --git a/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs b/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs index 34a2454139..6c5ab155e4 100644 --- a/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs +++ b/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs @@ -18,7 +18,7 @@ public sealed partial class Oxygenate : ReagentEffect { if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var resp)) { - var respSys = EntitySystem.Get(); + var respSys = args.EntityManager.System(); respSys.UpdateSaturation(args.SolutionEntity, args.Quantity.Float() * Factor, resp); } } diff --git a/Content.Server/Chemistry/ReagentEffects/Paralyze.cs b/Content.Server/Chemistry/ReagentEffects/Paralyze.cs index 077d1abf2c..d5d913f37d 100644 --- a/Content.Server/Chemistry/ReagentEffects/Paralyze.cs +++ b/Content.Server/Chemistry/ReagentEffects/Paralyze.cs @@ -23,7 +23,7 @@ public sealed partial class Paralyze : ReagentEffect var paralyzeTime = ParalyzeTime; paralyzeTime *= args.Scale; - EntitySystem.Get().TryParalyze(args.SolutionEntity, TimeSpan.FromSeconds(paralyzeTime), Refresh); + args.EntityManager.System().TryParalyze(args.SolutionEntity, TimeSpan.FromSeconds(paralyzeTime), Refresh); } } diff --git a/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs b/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs index 1208e74367..8813813c24 100644 --- a/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs +++ b/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs @@ -23,7 +23,7 @@ namespace Content.Server.Chemistry.ReagentEffects { var uid = args.SolutionEntity; if (args.EntityManager.TryGetComponent(uid, out ThirstComponent? thirst)) - EntitySystem.Get().ModifyThirst(uid, thirst, HydrationFactor); + args.EntityManager.System().ModifyThirst(uid, thirst, HydrationFactor); } protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) diff --git a/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs b/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs index 9bf6792eca..a71a42c25f 100644 --- a/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs +++ b/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs @@ -16,7 +16,7 @@ namespace Content.Server.Chemistry.ReagentEffects { if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out CreamPiedComponent? creamPied)) return; - EntitySystem.Get().SetCreamPied(args.SolutionEntity, creamPied, false); + args.EntityManager.System().SetCreamPied(args.SolutionEntity, creamPied, false); } } } diff --git a/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs b/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs index a20f54728e..6958dabb81 100644 --- a/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs @@ -21,19 +21,20 @@ public sealed partial class CleanDecalsReaction : ITileReaction [DataField] public FixedPoint2 CleanCost { get; private set; } = FixedPoint2.New(0.25f); - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { - var entMan = IoCManager.Resolve(); - if (reactVolume <= CleanCost || - !entMan.TryGetComponent(tile.GridUid, out var grid) || - !entMan.TryGetComponent(tile.GridUid, out var decalGrid)) + !entityManager.TryGetComponent(tile.GridUid, out var grid) || + !entityManager.TryGetComponent(tile.GridUid, out var decalGrid)) { return FixedPoint2.Zero; } - var lookupSystem = entMan.System(); - var decalSystem = entMan.System(); + var lookupSystem = entityManager.System(); + var decalSystem = entityManager.System(); // Very generous hitbox. var decals = decalSystem .GetDecalsIntersecting(tile.GridUid, lookupSystem.GetLocalBounds(tile, grid.TileSize).Enlarged(0.5f).Translated(new Vector2(-0.5f, -0.5f))); diff --git a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs index 3f5ae63c36..08f77de72d 100644 --- a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs @@ -31,12 +31,14 @@ public sealed partial class CleanTileReaction : ITileReaction [DataField("reagent", customTypeSerializer: typeof(PrototypeIdSerializer))] public string ReplacementReagent = "Water"; - FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + FixedPoint2 ITileReaction.TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { - var entMan = IoCManager.Resolve(); - var entities = entMan.System().GetLocalEntitiesIntersecting(tile, 0f).ToArray(); - var puddleQuery = entMan.GetEntityQuery(); - var solutionContainerSystem = entMan.System(); + var entities = entityManager.System().GetLocalEntitiesIntersecting(tile, 0f).ToArray(); + var puddleQuery = entityManager.GetEntityQuery(); + var solutionContainerSystem = entityManager.System(); // Multiply as the amount we can actually purge is higher than the react amount. var purgeAmount = reactVolume / CleanAmountMultiplier; diff --git a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs index 9cb1ba201d..29f9275bdf 100644 --- a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs @@ -35,13 +35,13 @@ public sealed partial class CreateEntityTileReaction : ITileReaction [DataField] public float RandomOffsetMax = 0.0f; - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { if (reactVolume >= Usage) { - // TODO probably pass this in args like reagenteffects do. - var entMan = IoCManager.Resolve(); - if (Whitelist != null) { int acc = 0; @@ -59,9 +59,9 @@ public sealed partial class CreateEntityTileReaction : ITileReaction var xoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); var yoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); - var center = entMan.System().GetTileCenter(tile); + var center = entityManager.System().GetTileCenter(tile); var pos = center.Offset(new Vector2(xoffs, yoffs)); - entMan.SpawnEntity(Entity, pos); + entityManager.SpawnEntity(Entity, pos); return Usage; } diff --git a/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs b/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs index 312b88bd34..198f650ac1 100644 --- a/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs @@ -14,12 +14,15 @@ namespace Content.Server.Chemistry.TileReactions { [DataField("coolingTemperature")] private float _coolingTemperature = 2f; - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty) return FixedPoint2.Zero; - var atmosphereSystem = EntitySystem.Get(); + var atmosphereSystem = entityManager.System(); var environment = atmosphereSystem.GetTileMixture(tile.GridUid, null, tile.GridIndices, true); diff --git a/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs b/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs index a694bf6019..b13b70d3d5 100644 --- a/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs @@ -13,12 +13,15 @@ namespace Content.Server.Chemistry.TileReactions { [DataField("temperatureMultiplier")] private float _temperatureMultiplier = 1.15f; - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty) return FixedPoint2.Zero; - var atmosphereSystem = EntitySystem.Get(); + var atmosphereSystem = entityManager.System(); var environment = atmosphereSystem.GetTileMixture(tile.GridUid, null, tile.GridIndices, true); if (environment == null || !atmosphereSystem.IsHotspotActive(tile.GridUid, tile.GridIndices)) diff --git a/Content.Server/Chemistry/TileReactions/PryTileReaction.cs b/Content.Server/Chemistry/TileReactions/PryTileReaction.cs index 39e95df5f7..c10b031720 100644 --- a/Content.Server/Chemistry/TileReactions/PryTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/PryTileReaction.cs @@ -12,9 +12,12 @@ namespace Content.Server.Chemistry.TileReactions; [DataDefinition] public sealed partial class PryTileReaction : ITileReaction { - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { - var sys = IoCManager.Resolve().System(); + var sys = entityManager.System(); sys.PryTile(tile); return reactVolume; } diff --git a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs index 82ede720cd..6b46b89495 100644 --- a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs @@ -12,9 +12,12 @@ namespace Content.Server.Chemistry.TileReactions [DataDefinition] public sealed partial class SpillIfPuddlePresentTileReaction : ITileReaction { - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { - var spillSystem = EntitySystem.Get(); + var spillSystem = entityManager.System(); if (reactVolume < 5 || !spillSystem.TryGetPuddle(tile, out _)) return FixedPoint2.Zero; diff --git a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs index 8f8b0626a2..fadc7147c9 100644 --- a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs @@ -26,13 +26,14 @@ namespace Content.Server.Chemistry.TileReactions /// [DataField("superSlippery")] private bool _superSlippery; - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { if (reactVolume < 5) return FixedPoint2.Zero; - var entityManager = IoCManager.Resolve(); - if (entityManager.EntitySysManager.GetEntitySystem() .TrySpillAt(tile, new Solution(reagent.ID, reactVolume), out var puddleUid, false, false)) { diff --git a/Content.Server/Destructible/Thresholds/Behaviors/EjectVendorItems.cs b/Content.Server/Destructible/Thresholds/Behaviors/EjectVendorItems.cs index 2ffd9a8f9f..a5212dfe85 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/EjectVendorItems.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/EjectVendorItems.cs @@ -29,7 +29,7 @@ namespace Content.Server.Destructible.Thresholds.Behaviors !system.EntityManager.TryGetComponent(owner, out var xform)) return; - var vendingMachineSystem = EntitySystem.Get(); + var vendingMachineSystem = system.EntityManager.System(); var inventory = vendingMachineSystem.GetAvailableInventory(owner, vendingcomp); if (inventory.Count <= 0) return; diff --git a/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs index 7ab1fe11b0..dffcac6d96 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs @@ -11,7 +11,7 @@ public sealed partial class OpenBehavior : IThresholdBehavior { public void Execute(EntityUid uid, DestructibleSystem system, EntityUid? cause = null) { - var openable = EntitySystem.Get(); + var openable = system.EntityManager.System(); openable.TryOpen(uid); } } diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs index 38564380c0..5826a3871d 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs @@ -22,8 +22,8 @@ namespace Content.Server.Destructible.Thresholds.Behaviors /// public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) { - var solutionContainerSystem = EntitySystem.Get(); - var spillableSystem = EntitySystem.Get(); + var solutionContainerSystem = system.EntityManager.System(); + var spillableSystem = system.EntityManager.System(); var coordinates = system.EntityManager.GetComponent(owner).Coordinates; diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index 923210cc73..a232ed8c0e 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -712,7 +712,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem { var (reagent, quantity) = solution.Contents[i]; var proto = _prototypeManager.Index(reagent.Prototype); - var removed = proto.ReactionTile(tileRef, quantity); + var removed = proto.ReactionTile(tileRef, quantity, EntityManager); if (removed <= FixedPoint2.Zero) continue; diff --git a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs index ae170842a0..9638dabf28 100644 --- a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs +++ b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs @@ -315,7 +315,7 @@ public sealed class SmokeSystem : EntitySystem continue; var reagent = _prototype.Index(reagentQuantity.Reagent.Prototype); - reagent.ReactionTile(tile, reagentQuantity.Quantity); + reagent.ReactionTile(tile, reagentQuantity.Quantity, EntityManager); } } diff --git a/Content.Server/GameTicking/Commands/DelayStartCommand.cs b/Content.Server/GameTicking/Commands/DelayStartCommand.cs index 6f57f561d8..6e101d93a3 100644 --- a/Content.Server/GameTicking/Commands/DelayStartCommand.cs +++ b/Content.Server/GameTicking/Commands/DelayStartCommand.cs @@ -7,13 +7,15 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] sealed class DelayStartCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "delaystart"; public string Description => "Delays the round start."; public string Help => $"Usage: {Command} \nPauses/Resumes the countdown if no argument is provided."; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel != GameRunLevel.PreRoundLobby) { shell.WriteLine("This can only be executed while the game is in the pre-round lobby."); diff --git a/Content.Server/GameTicking/Commands/EndRoundCommand.cs b/Content.Server/GameTicking/Commands/EndRoundCommand.cs index 972fa9561a..c6a8ddbf53 100644 --- a/Content.Server/GameTicking/Commands/EndRoundCommand.cs +++ b/Content.Server/GameTicking/Commands/EndRoundCommand.cs @@ -7,13 +7,15 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] sealed class EndRoundCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "endround"; public string Description => "Ends the round and moves the server to PostRound."; public string Help => String.Empty; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel != GameRunLevel.InRound) { diff --git a/Content.Server/GameTicking/Commands/ForcePresetCommand.cs b/Content.Server/GameTicking/Commands/ForcePresetCommand.cs index 1041fed6ec..5ef72f59b3 100644 --- a/Content.Server/GameTicking/Commands/ForcePresetCommand.cs +++ b/Content.Server/GameTicking/Commands/ForcePresetCommand.cs @@ -10,13 +10,15 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] sealed class ForcePresetCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "forcepreset"; public string Description => "Forces a specific game preset to start for the current lobby."; public string Help => $"Usage: {Command} "; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel != GameRunLevel.PreRoundLobby) { shell.WriteLine("This can only be executed while the game is in the pre-round lobby."); diff --git a/Content.Server/GameTicking/Commands/GoLobbyCommand.cs b/Content.Server/GameTicking/Commands/GoLobbyCommand.cs index 4932dbe0bf..b5984d260d 100644 --- a/Content.Server/GameTicking/Commands/GoLobbyCommand.cs +++ b/Content.Server/GameTicking/Commands/GoLobbyCommand.cs @@ -10,6 +10,8 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] public sealed class GoLobbyCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "golobby"; public string Description => "Enables the lobby and restarts the round."; public string Help => $"Usage: {Command} / {Command} "; @@ -18,7 +20,7 @@ namespace Content.Server.GameTicking.Commands GamePresetPrototype? preset = null; var presetName = string.Join(" ", args); - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (args.Length > 0) { diff --git a/Content.Server/GameTicking/Commands/ObserveCommand.cs b/Content.Server/GameTicking/Commands/ObserveCommand.cs index 747e54e28b..16c2c3261d 100644 --- a/Content.Server/GameTicking/Commands/ObserveCommand.cs +++ b/Content.Server/GameTicking/Commands/ObserveCommand.cs @@ -7,6 +7,8 @@ namespace Content.Server.GameTicking.Commands [AnyCommand] sealed class ObserveCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "observe"; public string Description => ""; public string Help => ""; @@ -18,7 +20,7 @@ namespace Content.Server.GameTicking.Commands return; } - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel == GameRunLevel.PreRoundLobby) { diff --git a/Content.Server/GameTicking/Commands/RestartRoundCommand.cs b/Content.Server/GameTicking/Commands/RestartRoundCommand.cs index 36c0ba981c..e4ea3fa53c 100644 --- a/Content.Server/GameTicking/Commands/RestartRoundCommand.cs +++ b/Content.Server/GameTicking/Commands/RestartRoundCommand.cs @@ -8,13 +8,15 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] public sealed class RestartRoundCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "restartround"; public string Description => "Ends the current round and starts the countdown for the next lobby."; public string Help => string.Empty; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel != GameRunLevel.InRound) { @@ -22,20 +24,22 @@ namespace Content.Server.GameTicking.Commands return; } - EntitySystem.Get().EndRound(); + _e.System().EndRound(); } } [AdminCommand(AdminFlags.Round)] public sealed class RestartRoundNowCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "restartroundnow"; public string Description => "Moves the server from PostRound to a new PreRoundLobby."; public string Help => String.Empty; public void Execute(IConsoleShell shell, string argStr, string[] args) { - EntitySystem.Get().RestartRound(); + _e.System().RestartRound(); } } } diff --git a/Content.Server/GameTicking/Commands/StartRoundCommand.cs b/Content.Server/GameTicking/Commands/StartRoundCommand.cs index 1652a14120..432cdd23e3 100644 --- a/Content.Server/GameTicking/Commands/StartRoundCommand.cs +++ b/Content.Server/GameTicking/Commands/StartRoundCommand.cs @@ -7,13 +7,15 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] sealed class StartRoundCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "startround"; public string Description => "Ends PreRoundLobby state and starts the round."; public string Help => String.Empty; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel != GameRunLevel.PreRoundLobby) { diff --git a/Content.Server/GameTicking/Commands/ToggleReadyCommand.cs b/Content.Server/GameTicking/Commands/ToggleReadyCommand.cs index df418c27ee..34b504acbc 100644 --- a/Content.Server/GameTicking/Commands/ToggleReadyCommand.cs +++ b/Content.Server/GameTicking/Commands/ToggleReadyCommand.cs @@ -6,6 +6,8 @@ namespace Content.Server.GameTicking.Commands [AnyCommand] sealed class ToggleReadyCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "toggleready"; public string Description => ""; public string Help => ""; @@ -23,7 +25,7 @@ namespace Content.Server.GameTicking.Commands return; } - var ticker = EntitySystem.Get(); + var ticker = _e.System(); ticker.ToggleReady(player, bool.Parse(args[0])); } } diff --git a/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs b/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs index ccd460a9cf..86026b230b 100644 --- a/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs +++ b/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs @@ -40,7 +40,7 @@ namespace Content.Server.Ghost.Roles.Components set { _roleName = value; - EntitySystem.Get().UpdateAllEui(); + IoCManager.Resolve().System().UpdateAllEui(); } } @@ -52,7 +52,7 @@ namespace Content.Server.Ghost.Roles.Components set { _roleDescription = value; - EntitySystem.Get().UpdateAllEui(); + IoCManager.Resolve().System().UpdateAllEui(); } } @@ -64,7 +64,7 @@ namespace Content.Server.Ghost.Roles.Components set { _roleRules = value; - EntitySystem.Get().UpdateAllEui(); + IoCManager.Resolve().System().UpdateAllEui(); } } diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index c198b54b1b..b6627f1154 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -791,13 +791,15 @@ namespace Content.Server.Ghost.Roles [AnyCommand] public sealed class GhostRoles : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "ghostroles"; public string Description => "Opens the ghost role request window."; public string Help => $"{Command}"; public void Execute(IConsoleShell shell, string argStr, string[] args) { if (shell.Player != null) - EntitySystem.Get().OpenEui(shell.Player); + _e.System().OpenEui(shell.Player); else shell.WriteLine("You can only open the ghost roles UI on a client."); } diff --git a/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs b/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs index c1e39919a2..825691fe95 100644 --- a/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs +++ b/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs @@ -40,7 +40,7 @@ namespace Content.Server.Ghost.Roles.UI { base.Closed(); - EntitySystem.Get().CloseEui(Player); + _ghostRoleSystem.CloseEui(Player); } } } diff --git a/Content.Server/Maps/Conditions/HolidayMapCondition.cs b/Content.Server/Maps/Conditions/HolidayMapCondition.cs index 81ecfb356e..da971c78f3 100644 --- a/Content.Server/Maps/Conditions/HolidayMapCondition.cs +++ b/Content.Server/Maps/Conditions/HolidayMapCondition.cs @@ -10,7 +10,7 @@ public sealed partial class HolidayMapCondition : GameMapCondition public override bool Check(GameMapPrototype map) { - var holidaySystem = EntitySystem.Get(); + var holidaySystem = IoCManager.Resolve().System(); return Holidays.Any(holiday => holidaySystem.IsCurrentlyHoliday(holiday)) ^ Inverted; } diff --git a/Content.Server/NodeContainer/Nodes/PipeNode.cs b/Content.Server/NodeContainer/Nodes/PipeNode.cs index 14afa2f108..861f3eea98 100644 --- a/Content.Server/NodeContainer/Nodes/PipeNode.cs +++ b/Content.Server/NodeContainer/Nodes/PipeNode.cs @@ -37,7 +37,7 @@ namespace Content.Server.NodeContainer.Nodes _alwaysReachable.Add(pipeNode); if (NodeGroup != null) - EntitySystem.Get().QueueRemakeGroup((BaseNodeGroup) NodeGroup); + IoCManager.Resolve().System().QueueRemakeGroup((BaseNodeGroup) NodeGroup); } public void RemoveAlwaysReachable(PipeNode pipeNode) @@ -47,7 +47,7 @@ namespace Content.Server.NodeContainer.Nodes _alwaysReachable.Remove(pipeNode); if (NodeGroup != null) - EntitySystem.Get().QueueRemakeGroup((BaseNodeGroup) NodeGroup); + IoCManager.Resolve().System().QueueRemakeGroup((BaseNodeGroup) NodeGroup); } /// @@ -62,7 +62,7 @@ namespace Content.Server.NodeContainer.Nodes _connectionsEnabled = value; if (NodeGroup != null) - EntitySystem.Get().QueueRemakeGroup((BaseNodeGroup) NodeGroup); + IoCManager.Resolve().System().QueueRemakeGroup((BaseNodeGroup) NodeGroup); } } diff --git a/Content.Server/Power/Commands/PowerStatCommand.cs b/Content.Server/Power/Commands/PowerStatCommand.cs index 440e40437b..127050393a 100644 --- a/Content.Server/Power/Commands/PowerStatCommand.cs +++ b/Content.Server/Power/Commands/PowerStatCommand.cs @@ -8,13 +8,15 @@ namespace Content.Server.Power.Commands [AdminCommand(AdminFlags.Debug)] public sealed class PowerStatCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "powerstat"; public string Description => "Shows statistics for pow3r"; public string Help => "Usage: powerstat"; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var stats = EntitySystem.Get().GetStatistics(); + var stats = _e.System().GetStatistics(); shell.WriteLine($"networks: {stats.CountNetworks}"); shell.WriteLine($"loads: {stats.CountLoads}"); diff --git a/Content.Server/Power/PowerWireAction.cs b/Content.Server/Power/PowerWireAction.cs index 785eac91db..ac34966036 100644 --- a/Content.Server/Power/PowerWireAction.cs +++ b/Content.Server/Power/PowerWireAction.cs @@ -159,7 +159,7 @@ public sealed partial class PowerWireAction : BaseWireAction { base.Initialize(); - _electrocutionSystem = EntitySystem.Get(); + _electrocutionSystem = EntityManager.System(); } // This should add a wire into the entity's state, whether it be diff --git a/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs b/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs index 6ce8edd1d8..d5dca64eaa 100644 --- a/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs +++ b/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs @@ -19,7 +19,7 @@ namespace Content.Server.Sandbox.Commands public void Execute(IConsoleShell shell, string argStr, string[] args) { - var sandboxManager = EntitySystem.Get(); + var sandboxManager = _entManager.System(); var adminManager = IoCManager.Resolve(); if (shell.IsClient && (!sandboxManager.IsSandboxEnabled && !adminManager.HasAdminFlag(shell.Player!, AdminFlags.Mapping))) { diff --git a/Content.Shared/Chemistry/Reaction/ITileReaction.cs b/Content.Shared/Chemistry/Reaction/ITileReaction.cs index 1f6a6bf307..af6cc34f48 100644 --- a/Content.Shared/Chemistry/Reaction/ITileReaction.cs +++ b/Content.Shared/Chemistry/Reaction/ITileReaction.cs @@ -6,6 +6,9 @@ namespace Content.Shared.Chemistry.Reaction { public interface ITileReaction { - FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume); + FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager); } } diff --git a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs index df1b1aa20b..6095676b9e 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs @@ -142,7 +142,7 @@ namespace Content.Shared.Chemistry.Reagent [DataField] public SoundSpecifier FootstepSound = new SoundCollectionSpecifier("FootstepWater", AudioParams.Default.WithVolume(6)); - public FixedPoint2 ReactionTile(TileRef tile, FixedPoint2 reactVolume) + public FixedPoint2 ReactionTile(TileRef tile, FixedPoint2 reactVolume, IEntityManager entityManager) { var removed = FixedPoint2.Zero; @@ -151,7 +151,7 @@ namespace Content.Shared.Chemistry.Reagent foreach (var reaction in TileReactions) { - removed += reaction.TileReact(tile, this, reactVolume - removed); + removed += reaction.TileReact(tile, this, reactVolume - removed, entityManager); if (removed > reactVolume) throw new Exception("Removed more than we have!"); diff --git a/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs b/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs index 405c0ec89e..47ae3ef74a 100644 --- a/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs +++ b/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs @@ -49,10 +49,10 @@ namespace Content.Tests.Shared.Alert var alertsComponent = new AlertsComponent(); alertsComponent = IoCManager.InjectDependencies(alertsComponent); - Assert.That(EntitySystem.Get().TryGet(AlertType.LowPressure, out var lowpressure)); - Assert.That(EntitySystem.Get().TryGet(AlertType.HighPressure, out var highpressure)); + Assert.That(entManager.System().TryGet(AlertType.LowPressure, out var lowpressure)); + Assert.That(entManager.System().TryGet(AlertType.HighPressure, out var highpressure)); - EntitySystem.Get().ShowAlert(alertsComponent.Owner, AlertType.LowPressure, null, null); + entManager.System().ShowAlert(alertsComponent.Owner, AlertType.LowPressure, null, null); var getty = new ComponentGetState(); entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); @@ -62,7 +62,7 @@ namespace Content.Tests.Shared.Alert Assert.That(alertState.Alerts.Count, Is.EqualTo(1)); Assert.That(alertState.Alerts.ContainsKey(lowpressure.AlertKey)); - EntitySystem.Get().ShowAlert(alertsComponent.Owner, AlertType.HighPressure, null, null); + entManager.System().ShowAlert(alertsComponent.Owner, AlertType.HighPressure, null, null); // Lazy entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); @@ -70,7 +70,7 @@ namespace Content.Tests.Shared.Alert Assert.That(alertState.Alerts.Count, Is.EqualTo(1)); Assert.That(alertState.Alerts.ContainsKey(highpressure.AlertKey)); - EntitySystem.Get().ClearAlertCategory(alertsComponent.Owner, AlertCategory.Pressure); + entManager.System().ClearAlertCategory(alertsComponent.Owner, AlertCategory.Pressure); entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); alertState = (AlertsComponent.AlertsComponent_AutoState) getty.State!; From 03af7fcdc1bc28b6eef46877b5eae1cde2c2cf2f Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 12 May 2024 21:07:52 -0400 Subject: [PATCH 43/68] fix weird behavior with storage HUD buttons (#27961) --- Content.Shared/Inventory/InventorySystem.Slots.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Inventory/InventorySystem.Slots.cs b/Content.Shared/Inventory/InventorySystem.Slots.cs index e0f2a69557..19831278b0 100644 --- a/Content.Shared/Inventory/InventorySystem.Slots.cs +++ b/Content.Shared/Inventory/InventorySystem.Slots.cs @@ -15,7 +15,7 @@ public partial class InventorySystem : EntitySystem private void InitializeSlots() { SubscribeLocalEvent(OnInit); - SubscribeNetworkEvent(OnOpenSlotStorage); + SubscribeAllEvent(OnOpenSlotStorage); _vvm.GetTypeHandler() .AddHandler(HandleViewVariablesSlots, ListViewVariablesSlots); From ce0a175c731bf205f59cb93dcb3a2268c4cedb24 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Mon, 13 May 2024 14:05:37 +1000 Subject: [PATCH 44/68] Implement Equals for ApcBoundInterfaceState (#27965) * Implement Equals for ApcBoundInterfaceState Saves a lot on bandwidth. Also made it round to the nearest 5. * Also this --- .../Power/EntitySystems/ApcSystem.cs | 7 ++++- Content.Shared/APC/SharedApc.cs | 29 ++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Content.Server/Power/EntitySystems/ApcSystem.cs b/Content.Server/Power/EntitySystems/ApcSystem.cs index 388f65ad2e..529d4d81d7 100644 --- a/Content.Server/Power/EntitySystems/ApcSystem.cs +++ b/Content.Server/Power/EntitySystems/ApcSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.APC; using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; using Content.Shared.Popups; +using Content.Shared.Rounding; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -147,10 +148,14 @@ public sealed class ApcSystem : EntitySystem return; var battery = netBat.NetworkBattery; + const int ChargeAccuracy = 5; + + // TODO: Fix ContentHelpers or make a new one coz this is cooked. + var charge = ContentHelpers.RoundToNearestLevels(battery.CurrentStorage / battery.Capacity, 1.0, 100 / ChargeAccuracy) / 100f * ChargeAccuracy; var state = new ApcBoundInterfaceState(apc.MainBreakerEnabled, apc.HasAccess, (int) MathF.Ceiling(battery.CurrentSupply), apc.LastExternalState, - battery.CurrentStorage / battery.Capacity); + charge); _ui.SetUiState((uid, ui), ApcUiKey.Key, state); } diff --git a/Content.Shared/APC/SharedApc.cs b/Content.Shared/APC/SharedApc.cs index 16cd840b3a..bf9fdc9444 100644 --- a/Content.Shared/APC/SharedApc.cs +++ b/Content.Shared/APC/SharedApc.cs @@ -65,7 +65,7 @@ namespace Content.Shared.APC /// Bitmask for the full state for a given APC lock indicator. /// All = (Lock), - + /// /// The log 2 width in bits of the bitfields indicating the status of an APC lock indicator. /// Used for bit shifting operations (Mask for the state for indicator i is (All << (i << LogWidth))). @@ -175,7 +175,7 @@ namespace Content.Shared.APC } [Serializable, NetSerializable] - public sealed class ApcBoundInterfaceState : BoundUserInterfaceState + public sealed class ApcBoundInterfaceState : BoundUserInterfaceState, IEquatable { public readonly bool MainBreaker; public readonly bool HasAccess; @@ -191,6 +191,27 @@ namespace Content.Shared.APC ApcExternalPower = apcExternalPower; Charge = charge; } + + public bool Equals(ApcBoundInterfaceState? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return MainBreaker == other.MainBreaker && + HasAccess == other.HasAccess && + Power == other.Power && + ApcExternalPower == other.ApcExternalPower && + MathHelper.CloseTo(Charge, other.Charge); + } + + public override bool Equals(object? obj) + { + return ReferenceEquals(this, obj) || obj is ApcBoundInterfaceState other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(MainBreaker, HasAccess, Power, (int) ApcExternalPower, Charge); + } } [Serializable, NetSerializable] @@ -198,7 +219,7 @@ namespace Content.Shared.APC { } - public enum ApcExternalPowerState + public enum ApcExternalPowerState : byte { None, Low, @@ -206,7 +227,7 @@ namespace Content.Shared.APC } [NetSerializable, Serializable] - public enum ApcUiKey + public enum ApcUiKey : byte { Key, } From 1f03111ff8affe1554a5fac4d1a49b1cccdf2fbb Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Mon, 13 May 2024 17:11:49 +1200 Subject: [PATCH 45/68] Change some `EntityQueryEnumerator` to `AllEntityQuery` (#27969) --- Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs | 4 ++-- Content.Server/Decals/DecalSystem.cs | 2 +- .../Disposal/Unit/EntitySystems/DisposalUnitSystem.cs | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs index 89b9c52078..c714acb2f3 100644 --- a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -122,7 +122,7 @@ namespace Content.Server.Atmos.EntitySystems } // PVS was turned off, ensure data gets sent to all clients. - var query = EntityQueryEnumerator(); + var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var grid, out var meta)) { grid.ForceTick = _gameTiming.CurTick; @@ -269,7 +269,7 @@ namespace Content.Server.Atmos.EntitySystems private void UpdateOverlayData() { // TODO parallelize? - var query = EntityQueryEnumerator(); + var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var overlay, out var gam, out var meta)) { var changed = false; diff --git a/Content.Server/Decals/DecalSystem.cs b/Content.Server/Decals/DecalSystem.cs index c8e062ce6f..519f7a133e 100644 --- a/Content.Server/Decals/DecalSystem.cs +++ b/Content.Server/Decals/DecalSystem.cs @@ -89,7 +89,7 @@ namespace Content.Server.Decals playerData.Clear(); } - var query = EntityQueryEnumerator(); + var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var grid, out var meta)) { grid.ForceTick = _timing.CurTick; diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 7f4ee87ee1..1f3b9703d4 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -209,10 +209,11 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem { base.Update(frameTime); - var query = EntityQueryEnumerator(); + var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var unit, out var metadata)) { - Update(uid, unit, metadata, frameTime); + if (!metadata.EntityPaused) + Update(uid, unit, metadata, frameTime); } } From 74020abc91af0242a2bd47b195fba8bac301daab Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Mon, 13 May 2024 21:25:58 +0200 Subject: [PATCH 46/68] Fix skirts femalemask (#27984) Fix skirts --- .../Clothing/Uniforms/color_jumpskirts.yml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/color_jumpskirts.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/color_jumpskirts.yml index 1f77059841..a2bf6b687a 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/color_jumpskirts.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/color_jumpskirts.yml @@ -1,6 +1,6 @@ # White Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorWhite name: white jumpskirt description: A generic white jumpskirt with no rank markings. @@ -27,7 +27,7 @@ # Grey Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorGrey name: grey jumpskirt description: A tasteful grey jumpskirt that reminds you of the good old days. @@ -58,7 +58,7 @@ # Black Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorBlack name: black jumpskirt description: A generic black jumpskirt with no rank markings. @@ -89,7 +89,7 @@ # Blue Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorBlue name: blue jumpskirt description: A generic blue jumpskirt with no rank markings. @@ -120,7 +120,7 @@ # Dark Blue Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorDarkBlue name: dark blue jumpskirt description: A generic dark blue jumpskirt with no rank markings. @@ -151,7 +151,7 @@ # Teal Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorTeal name: teal jumpskirt description: A generic teal jumpskirt with no rank markings. @@ -182,7 +182,7 @@ # Green Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorGreen name: green jumpskirt description: A generic green jumpskirt with no rank markings. @@ -213,7 +213,7 @@ # Dark Green Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorDarkGreen name: dark green jumpskirt description: A generic dark green jumpskirt with no rank markings. @@ -244,7 +244,7 @@ # Orange Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorOrange name: orange jumpskirt description: Don't wear this near paranoid security officers. @@ -275,7 +275,7 @@ # Pink Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorPink name: pink jumpskirt description: Just looking at this makes you feel fabulous. @@ -306,7 +306,7 @@ # Red Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorRed name: red jumpskirt description: A generic red jumpskirt with no rank markings. @@ -337,7 +337,7 @@ # Yellow Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorYellow name: yellow jumpskirt description: A generic yellow jumpskirt with no rank markings. @@ -368,7 +368,7 @@ # Purple Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorPurple name: purple jumpskirt description: A generic light purple jumpskirt with no rank markings. @@ -399,7 +399,7 @@ # Light Brown Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorLightBrown name: light brown jumpskirt description: A generic light brown jumpskirt with no rank markings. @@ -430,7 +430,7 @@ # Brown Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorBrown name: brown jumpskirt description: A generic brown jumpskirt with no rank markings. @@ -461,7 +461,7 @@ # Maroon Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorMaroon name: maroon jumpskirt description: A generic maroon jumpskirt with no rank markings. From 1a9766bd67dc390193cd81319b9d218e073ad259 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 13 May 2024 19:27:06 +0000 Subject: [PATCH 47/68] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index a979fa6f89..a4a529dc07 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Brandon-Huu - changes: - - message: Removed the ability for command or any antag-safe role to be initial - infected in zombie mode - type: Remove - id: 6090 - time: '2024-03-05T04:34:18.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25785 - author: Whisper changes: - message: Glass shards can be added to flatcaps to create bladed flatcaps! @@ -3870,3 +3862,10 @@ id: 6589 time: '2024-05-13T00:03:07.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27962 +- author: lzk228 + changes: + - message: Fixed color jumpskirts female mask. + type: Fix + id: 6590 + time: '2024-05-13T19:25:58.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27984 From bfed85aff288630300b40133c025f73de71924b8 Mon Sep 17 00:00:00 2001 From: Brandon Li <48413902+aspiringLich@users.noreply.github.com> Date: Mon, 13 May 2024 17:46:16 -0400 Subject: [PATCH 48/68] Reagent dispenser UI (Again) (#27958) * reagent dispenser: fancy window * reagent dispenser: dispense button grid * reagent dispenser: rearrange containers & info * reagent dispenser: remove `reagent-dispenser-window-container-label` * reagent dispenser: add `Scrollcontainer` on right side * reagent dispenser: get rid of pointless actions * reagent dispenser: cleanup actions and `inventory` field on bound ui state * reagent dispenser: cool reagent cards & finishing touches * reagent dispenser: final cleanup and formatting * reagent dispenser: `ButtonGrid` and `ReagentDispenserSetDispenseAmountMessage` refactor * reagent dispenser: cleanup code & address minor concerns * reagent dispenser: text in reagent cards no longer clips * reagent dispenser: oh wait i forgot to change this and thats why the builds keep failing probably * reagent dispenser mayybe this * reagent dispenser: remove `using FastAccessors;` * delete unused classes * disable reagent button when container is empty * Make things a bit bigger * remove obsolete text color override --- .../Chemistry/UI/ReagentCardControl.xaml | 7 +++--- .../Chemistry/UI/ReagentCardControl.xaml.cs | 6 +++-- .../Chemistry/UI/ReagentDispenserWindow.xaml | 3 ++- .../UI/ReagentDispenserWindow.xaml.cs | 24 ------------------- .../EntitySystems/ReagentDispenserSystem.cs | 3 +-- .../Chemistry/SharedReagentDispenser.cs | 5 ++-- 6 files changed, 13 insertions(+), 35 deletions(-) diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml b/Content.Client/Chemistry/UI/ReagentCardControl.xaml index 966c730140..257b0b6c52 100644 --- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml +++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml @@ -1,7 +1,7 @@ - + + HorizontalExpand="True"> - public void SetIgnited(Entity ent, bool ignited = true) { - if (!Resolve(ent, ref ent.Comp)) + if (!Resolve(ent, ref ent.Comp, false)) return; ent.Comp.Ignited = ignited; From f78da7da330a814cedb3900ab0e2d500e08e48ce Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 13 May 2024 21:50:50 +0000 Subject: [PATCH 51/68] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index de5d1cb9c3..c08d1b6c8f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: MACMAN2003 - changes: - - message: Added clockwork structures and brass to build them with. - type: Add - id: 6092 - time: '2024-03-06T00:25:59.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/24673 - author: deepdarkdepths changes: - message: Added the paramedic's cap to the uniform printer. @@ -3869,3 +3862,10 @@ id: 6591 time: '2024-05-13T21:46:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27958 +- author: deltanedas + changes: + - message: Fixed fire spreading being insane. + type: Fix + id: 6592 + time: '2024-05-13T21:49:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27986 From 950adc8fdecaf89e7ab402ebb5197d0ff8a538dd Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Mon, 13 May 2024 20:29:50 -0400 Subject: [PATCH 52/68] Revert "Stop Toilets crushing you into walls" (#27994) Revert "Stop Toilets crushing you into walls (#27778)" This reverts commit 24e227660a34e33966f5b9bd7a5f69c775c9669b. --- Resources/Prototypes/Entities/Structures/Furniture/toilet.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index a3124c09f6..2556b9ddfd 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -51,10 +51,6 @@ disposals: !type:Container - type: Physics bodyType: Static - - type: Fixtures - fixtures: - fix1: - hard: false - type: Construction graph: Toilet node: toilet From 63c551b20d28e7572833d7e3b5b6e706947167ae Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Mon, 13 May 2024 21:03:06 -0700 Subject: [PATCH 53/68] Fix profile editor save buttons disappearing at small resolutions (#28001) --- Content.Client/Lobby/UI/HumanoidProfileEditor.xaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml index 918b6840b4..03a205e94a 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml @@ -7,7 +7,7 @@ - + @@ -31,13 +31,13 @@ - - - + + + +