From ebddef7675269ea4c354673a888a2c8f6e418548 Mon Sep 17 00:00:00 2001 From: Jake Huxell Date: Tue, 26 Mar 2024 19:13:58 -0400 Subject: [PATCH 01/56] Objects such as lighters/welders are now able to be dropped in disposal units. (#26463) Don't always mark after interact event as handled for welder tools. Done with a view towards allowing disposal interaction post tool system handling. Co-authored-by: MQuatermain --- Content.Server/Tools/ToolSystem.Welder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/Tools/ToolSystem.Welder.cs b/Content.Server/Tools/ToolSystem.Welder.cs index 98e29c763a..1eabd6c6d2 100644 --- a/Content.Server/Tools/ToolSystem.Welder.cs +++ b/Content.Server/Tools/ToolSystem.Welder.cs @@ -152,9 +152,9 @@ namespace Content.Server.Tools { _popup.PopupEntity(Loc.GetString("welder-component-no-fuel-in-tank", ("owner", args.Target)), entity, args.User); } - } - args.Handled = true; + args.Handled = true; + } } private void OnWelderToolUseAttempt(Entity entity, ref DoAfterAttemptEvent args) From f90b97a02442601141fe6578bc5f2760fd09f2af Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:41:42 +1100 Subject: [PATCH 02/56] Update submodule to 216.0.0 (#26467) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 536fca4115..8607ba1f16 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 536fca4115ec005e9cbaa45bcc79e31c5eb08f30 +Subproject commit 8607ba1f16ce676a849b59a41efd389a6e467f5c From fbea150b53136d33b547abfec377f00499c45947 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:24:27 +1100 Subject: [PATCH 03/56] Revert "Give cap door remote his access" (#26465) Revert "Give cap door remote his access (#26378)" This reverts commit ea818ea1b6d910fb1b86ee06648fc0da5a100a27. --- Resources/Prototypes/Entities/Objects/Devices/door_remote.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml b/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml index e63c7f2e03..25ac56a6da 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml @@ -9,7 +9,7 @@ sprite: Objects/Devices/door_remote.rsi - type: Item storedRotation: -90 - - type: Access #No access, useless + - type: Access - type: DoorRemote - type: StealTarget stealGroup: DoorRemote @@ -28,7 +28,7 @@ color: "#9f9f00" - type: Access groups: - - AllAccess #Cap must be able to control the station + - Command - type: entity parent: DoorRemoteDefault From cb8f1459759396552b1421aaf28c77fc8ac95003 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:31:26 +1300 Subject: [PATCH 04/56] Fix GhostRoleComponent performing randomization on ComponentInit (#26466) * Fix ghostrole ComponentInit * A * a --- .../Roles/Components/GhostRoleComponent.cs | 2 +- Content.Server/Ghost/Roles/GhostRoleSystem.cs | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs b/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs index 93e7e9efaa..abb26a8c8b 100644 --- a/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs +++ b/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs @@ -11,7 +11,7 @@ namespace Content.Server.Ghost.Roles.Components [DataField("description")] private string _roleDescription = "Unknown"; - [DataField("rules")] private string _roleRules = ""; + [DataField("rules")] private string _roleRules = "ghost-role-component-default-rules"; [DataField("requirements")] public HashSet? Requirements; diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index cd04e3fb37..0649e68a31 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -56,7 +56,8 @@ namespace Content.Server.Ghost.Roles SubscribeLocalEvent(OnMindAdded); SubscribeLocalEvent(OnMindRemoved); SubscribeLocalEvent(OnMobStateChanged); - SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnPaused); SubscribeLocalEvent(OnUnpaused); @@ -317,17 +318,14 @@ namespace Content.Server.Ghost.Roles UpdateAllEui(); } - private void OnInit(Entity ent, ref ComponentInit args) + private void OnMapInit(Entity ent, ref MapInitEvent args) { - var role = ent.Comp; - if (role.Probability < 1f && !_random.Prob(role.Probability)) - { - RemComp(ent); - return; - } + if (ent.Comp.Probability < 1f && !_random.Prob(ent.Comp.Probability)) + RemCompDeferred(ent); + } - if (role.RoleRules == "") - role.RoleRules = Loc.GetString("ghost-role-component-default-rules"); + private void OnStartup(Entity ent, ref ComponentStartup args) + { RegisterGhostRole(ent); } From 9594afa400ff9dbcbc8f9589e91da53f48206f42 Mon Sep 17 00:00:00 2001 From: Nairod <110078045+Nairodian@users.noreply.github.com> Date: Wed, 27 Mar 2024 10:11:13 -0500 Subject: [PATCH 05/56] Remove Rubber Rounds, Give Security Disablers (#26470) Remove Rubbers, give sec Disablers --- .../Prototypes/Catalog/Fills/Items/belt.yml | 1 - .../Catalog/Fills/Lockers/security.yml | 3 +- .../Entities/Structures/Machines/lathe.yml | 18 +---- .../Prototypes/Recipes/Lathes/security.yml | 72 ------------------- Resources/Prototypes/Research/arsenal.yml | 20 ------ .../Roles/Jobs/Security/head_of_security.yml | 2 +- .../Roles/Jobs/Security/security_cadet.yml | 2 +- .../Roles/Jobs/Security/security_officer.yml | 2 +- .../Prototypes/Roles/Jobs/Security/warden.yml | 2 +- 9 files changed, 6 insertions(+), 116 deletions(-) diff --git a/Resources/Prototypes/Catalog/Fills/Items/belt.yml b/Resources/Prototypes/Catalog/Fills/Items/belt.yml index 27dd4c7e9a..fdbe647e62 100644 --- a/Resources/Prototypes/Catalog/Fills/Items/belt.yml +++ b/Resources/Prototypes/Catalog/Fills/Items/belt.yml @@ -174,7 +174,6 @@ contents: - id: WeaponRevolverInspector - id: SpeedLoaderMagnum - - id: SpeedLoaderMagnumRubber - type: entity id: ClothingBeltChefFilled diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/security.yml b/Resources/Prototypes/Catalog/Fills/Lockers/security.yml index dff4aa62d2..4bb300b158 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/security.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/security.yml @@ -7,7 +7,6 @@ contents: - id: FlashlightSeclite - id: WeaponDisabler - prob: 0.3 - id: ClothingBeltSecurityFilled - id: Flash - id: ClothingEyesGlassesSunglasses @@ -31,7 +30,6 @@ contents: - id: FlashlightSeclite - id: WeaponDisabler - prob: 0.3 - id: ClothingBeltSecurityFilled - id: Flash - id: ClothingEyesGlassesSunglasses @@ -54,6 +52,7 @@ contents: - id: FlashlightSeclite prob: 0.8 + - id: WeaponDisabler - id: ClothingUniformJumpsuitSecGrey prob: 0.3 - id: ClothingHeadHelmetBasic diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 56200324ec..28bc62894e 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -177,15 +177,7 @@ - MagazineBoxLightRifle - GrenadeBlast emagDynamicRecipes: - - CartridgePistolRubber - - CartridgeMagnumRubber - ShellShotgunBeanbag - - CartridgeRifleRubber - - CartridgeLightRifleRubber - - MagazineBoxPistolRubber - - MagazineBoxMagnumRubber - - MagazineBoxRifleRubber - - MagazineBoxLightRifleRubber - ShellShotgunIncendiary - CartridgePistolIncendiary - CartridgeMagnumIncendiary @@ -657,6 +649,7 @@ - ForensicPad - RiotShield - ShellShotgun + - ShellShotgunBeanbag - ShellShotgunSlug - ShellShotgunFlare - ShellTranquilizer @@ -689,10 +682,6 @@ - CartridgeMagnumUranium - CartridgePistolUranium - CartridgeRifleUranium - - CartridgeLightRifleRubber - - CartridgeMagnumRubber - - CartridgePistolRubber - - CartridgeRifleRubber - ClothingEyesGlassesSecurity - ExplosivePayload - FlashPayload @@ -705,14 +694,9 @@ - MagazineBoxMagnumUranium - MagazineBoxPistolUranium - MagazineBoxRifleUranium - - MagazineBoxLightRifleRubber - - MagazineBoxMagnumRubber - - MagazineBoxPistolRubber - - MagazineBoxRifleRubber - MagazineGrenadeEmpty - GrenadeEMP - GrenadeFlash - - ShellShotgunBeanbag - ShellShotgunIncendiary - ShellShotgunUranium - Signaller diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index ffa74d460f..432594d4b5 100644 --- a/Resources/Prototypes/Recipes/Lathes/security.yml +++ b/Resources/Prototypes/Recipes/Lathes/security.yml @@ -146,24 +146,6 @@ Plastic: 15 Steel: 10 -- type: latheRecipe - id: CartridgePistolRubber - result: CartridgePistolRubber - category: Ammo - completetime: 2 - materials: - Plastic: 5 - Steel: 5 - -- type: latheRecipe - id: CartridgeMagnumRubber - result: CartridgeMagnumRubber - category: Ammo - completetime: 2 - materials: - Plastic: 5 - Steel: 5 - - type: latheRecipe id: CartridgeRifle result: CartridgeRifle @@ -172,24 +154,6 @@ materials: Steel: 15 -- type: latheRecipe - id: CartridgeLightRifleRubber - result: CartridgeLightRifleRubber - category: Ammo - completetime: 2 - materials: - Plastic: 10 - Steel: 5 - -- type: latheRecipe - id: CartridgeRifleRubber - result: CartridgeRifleRubber - category: Ammo - completetime: 2 - materials: - Plastic: 10 - Steel: 5 - - type: latheRecipe id: CartridgePistol result: CartridgePistol @@ -306,15 +270,6 @@ materials: Steel: 650 -- type: latheRecipe - id: MagazineBoxPistolRubber - result: MagazineBoxPistolRubber - category: Ammo - completetime: 5 - materials: - Steel: 350 - Plastic: 300 - - type: latheRecipe id: MagazineBoxMagnum result: MagazineBoxMagnum @@ -323,15 +278,6 @@ materials: Steel: 1250 -- type: latheRecipe - id: MagazineBoxMagnumRubber - result: MagazineBoxMagnumRubber - category: Ammo - completetime: 5 - materials: - Steel: 350 - Plastic: 300 - - type: latheRecipe id: MagazineRifle result: MagazineRifle @@ -356,15 +302,6 @@ materials: Steel: 950 -- type: latheRecipe - id: MagazineBoxRifleRubber - result: MagazineBoxRifleRubber - category: Ammo - completetime: 5 - materials: - Steel: 350 - Plastic: 600 - - type: latheRecipe id: MagazineBoxLightRifle result: MagazineBoxLightRifle @@ -373,15 +310,6 @@ materials: Steel: 1800 -- type: latheRecipe - id: MagazineBoxLightRifleRubber - result: MagazineBoxLightRifleRubber - category: Ammo - completetime: 5 - materials: - Steel: 350 - Plastic: 600 - - type: latheRecipe id: SpeedLoaderMagnum result: SpeedLoaderMagnum diff --git a/Resources/Prototypes/Research/arsenal.yml b/Resources/Prototypes/Research/arsenal.yml index 38ed0ceec8..b17fb0ce00 100644 --- a/Resources/Prototypes/Research/arsenal.yml +++ b/Resources/Prototypes/Research/arsenal.yml @@ -46,26 +46,6 @@ recipeUnlocks: - WeaponLaserCarbine -- type: technology - id: NonlethalAmmunition - name: research-technology-nonlethal-ammunition - icon: - sprite: Objects/Weapons/Guns/Ammunition/Casings/shotgun_shell.rsi - state: beanbag - discipline: Arsenal - tier: 1 - cost: 5000 - recipeUnlocks: - - ShellShotgunBeanbag - - CartridgePistolRubber - - CartridgeMagnumRubber - - CartridgeLightRifleRubber - - CartridgeRifleRubber - - MagazineBoxPistolRubber - - MagazineBoxMagnumRubber - - MagazineBoxLightRifleRubber - - MagazineBoxRifleRubber - - type: technology id: UraniumMunitions name: research-technology-uranium-munitions diff --git a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml index 7babe2f816..5223357379 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml @@ -52,7 +52,7 @@ gloves: ClothingHandsGlovesCombat ears: ClothingHeadsetAltSecurity belt: ClothingBeltSecurityFilled - pocket1: WeaponPistolMk58Nonlethal + pocket1: WeaponPistolMk58 innerClothingSkirt: ClothingUniformJumpskirtHoS satchel: ClothingBackpackSatchelHOSFilled duffelbag: ClothingBackpackDuffelHOSFilled diff --git a/Resources/Prototypes/Roles/Jobs/Security/security_cadet.yml b/Resources/Prototypes/Roles/Jobs/Security/security_cadet.yml index 26c578c1a7..a1b0659384 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/security_cadet.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/security_cadet.yml @@ -35,7 +35,7 @@ id: SecurityCadetPDA ears: ClothingHeadsetSecurity belt: ClothingBeltSecurityFilled - pocket1: WeaponPistolMk58Nonlethal + pocket1: WeaponPistolMk58 pocket2: BookSecurity innerClothingSkirt: ClothingUniformJumpskirtColorRed satchel: ClothingBackpackSatchelSecurityFilled diff --git a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml index ec3e78c135..686d140447 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml @@ -34,7 +34,7 @@ id: SecurityPDA ears: ClothingHeadsetSecurity belt: ClothingBeltSecurityFilled - pocket1: WeaponPistolMk58Nonlethal + pocket1: WeaponPistolMk58 innerClothingSkirt: ClothingUniformJumpskirtSec satchel: ClothingBackpackSatchelSecurityFilled duffelbag: ClothingBackpackDuffelSecurityFilled diff --git a/Resources/Prototypes/Roles/Jobs/Security/warden.yml b/Resources/Prototypes/Roles/Jobs/Security/warden.yml index 074e9fae33..7d509651b6 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/warden.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/warden.yml @@ -36,7 +36,7 @@ id: WardenPDA ears: ClothingHeadsetSecurity belt: ClothingBeltSecurityFilled - pocket1: WeaponPistolMk58Nonlethal + pocket1: WeaponPistolMk58 innerClothingSkirt: ClothingUniformJumpskirtWarden satchel: ClothingBackpackSatchelSecurityFilled duffelbag: ClothingBackpackDuffelSecurityFilled From bd2772792fd51970a3a139e7e1c00425ad2e4a66 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 27 Mar 2024 15:12:21 +0000 Subject: [PATCH 06/56] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 1bae524d59..c3455ca236 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Ubaser - changes: - - message: The paramedic's void suit now has a helmet toggle. - type: Tweak - id: 5736 - time: '2024-01-18T15:26:59.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24229 - author: Ubaser changes: - message: Carrots now properly contain Oculine, and you can Juice them to obtain @@ -3792,3 +3785,12 @@ id: 6235 time: '2024-03-26T19:15:08.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26434 +- author: Nairodian + changes: + - message: Added disablers to every security officer locker. + type: Add + - message: Any way to obtain rubber bullets has been removed. + type: Remove + id: 6236 + time: '2024-03-27T15:11:13.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26470 From 3e786f548d2b429d052fb5c0d54b81c5234c9372 Mon Sep 17 00:00:00 2001 From: Ghagliiarghii <68826635+Ghagliiarghii@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:50:35 -0400 Subject: [PATCH 07/56] Removed Box of Hugs and Donk Pockets from Bounties (#26481) As far as I know, the Box of Hugs only spawns on the clown and is used in the construction of a honkbot such that if the clown decided earlier to craft a honkbot or if the low price of some 3000 spesos is insufficient this bounty is impossible. Similarly, Donk Pockets are not a renewable resource and it has happened that a station has eaten too many for the bounty to be completed only to get it in rotation and esssentially lose a bounty slot forever. I would like it if each bounty was at least theoretically possible on any station it's likely to occur in, and there are too many where there aren't enough of these obtainable to complete the bounty, which is no fun. --- .../Prototypes/Catalog/Bounties/bounties.yml | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/Resources/Prototypes/Catalog/Bounties/bounties.yml b/Resources/Prototypes/Catalog/Bounties/bounties.yml index c8c35aabfe..3c1bd02fcf 100644 --- a/Resources/Prototypes/Catalog/Bounties/bounties.yml +++ b/Resources/Prototypes/Catalog/Bounties/bounties.yml @@ -21,17 +21,6 @@ tags: - BaseballBat -- type: cargoBounty - id: BountyBoxHug - reward: 3000 - description: bounty-description-box-hugs - entries: - - name: bounty-item-box-hugs - amount: 1 - whitelist: - tags: - - BoxHug - - type: cargoBounty id: BountyBrain reward: 12500 @@ -139,18 +128,6 @@ tags: - CubanCarp -- type: cargoBounty - id: BountyDonkPocket - reward: 6000 - description: bounty-description-donk-pocket - idPrefix: CC - entries: - - name: bounty-item-donk-pocket - amount: 12 - whitelist: - tags: - - DonkPocket - - type: cargoBounty id: BountyDonut reward: 6000 From ba7d2dcdf7a934443fea34532f6934cb2091d1ca Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 27 Mar 2024 21:51:41 +0000 Subject: [PATCH 08/56] 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 c3455ca236..a2e546037e 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Ubaser - changes: - - message: Carrots now properly contain Oculine, and you can Juice them to obtain - the raw chemical. - type: Fix - id: 5737 - time: '2024-01-18T15:28:01.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24228 - author: koteq changes: - message: Jugs with the same labels are now correctly grouped and sorted in the @@ -3794,3 +3786,12 @@ id: 6236 time: '2024-03-27T15:11:13.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26470 +- author: Ghagliiarghii + changes: + - message: Removed Donk Pocket Bounty from Cargo + type: Remove + - message: Removed Box of Hugs Bounty from Cargo + type: Remove + id: 6237 + time: '2024-03-27T21:50:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26481 From 2e9deaee1ed14bf03eff47e4e5b156f3ddc1bcb2 Mon Sep 17 00:00:00 2001 From: Verm <32827189+Vermidia@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:26:26 -0500 Subject: [PATCH 09/56] Artifact node IDs are now only 3 digits long (#26482) * 2-digit nodes * 3-digits instead * Fix exclusive bounds --- .../Xenoarchaeology/XenoArtifacts/ArtifactSystem.Nodes.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Nodes.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Nodes.cs index 70ae7dcf0f..647f31a895 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Nodes.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Nodes.cs @@ -44,10 +44,10 @@ public sealed partial class ArtifactSystem private int GetValidNodeId() { - var id = _random.Next(10000, 100000); + var id = _random.Next(100, 1000); while (_usedNodeIds.Contains(id)) { - id = _random.Next(10000, 100000); + id = _random.Next(100, 1000); } _usedNodeIds.Add(id); From 527c2c42ed00557158b8faa599e394f69b1d86e3 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 27 Mar 2024 23:27:32 +0000 Subject: [PATCH 10/56] 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 a2e546037e..7ff57b0df1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: koteq - changes: - - message: Jugs with the same labels are now correctly grouped and sorted in the - context menu. - type: Tweak - id: 5738 - time: '2024-01-18T22:18:53.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24243 - author: EmoGarbage404 changes: - message: Duplicate players will no longer spawn in cryogenic sleep units. @@ -3795,3 +3787,10 @@ id: 6237 time: '2024-03-27T21:50:35.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26481 +- author: Vermidia + changes: + - message: Artifact node IDs are now only 3-digits long + type: Tweak + id: 6238 + time: '2024-03-27T23:26:26.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26482 From 37b8d78dac047122fe77038bafb8e682132db3cd Mon Sep 17 00:00:00 2001 From: 0x6273 <0x40@keemail.me> Date: Thu, 28 Mar 2024 01:48:37 +0100 Subject: [PATCH 11/56] Body code cleanup (#24946) * Fix test * Kill float accumulators * Use entity proxy methods * DataField auto name generation where possible * Kill comp properties * Clean up server comps * Make events record structs * Clean up shared body code * Clean up server body code * Rename organ events to be same names as in med refactor --- .../Tests/Body/LungTest.cs | 37 +- Content.Server/Bed/BedSystem.cs | 10 +- .../Body/Commands/AddHandCommand.cs | 96 +++--- .../Body/Commands/AttachBodyPartCommand.cs | 4 +- .../Body/Components/BeingGibbedEvent.cs | 14 +- .../Body/Components/BloodstreamComponent.cs | 34 +- .../Body/Components/InternalsComponent.cs | 14 +- .../Body/Components/LungComponent.cs | 4 +- .../Body/Components/MetabolizerComponent.cs | 26 +- .../Body/Components/RespiratorComponent.cs | 37 +- .../Body/Components/StomachComponent.cs | 23 +- .../Components/ThermalRegulatorComponent.cs | 43 ++- .../Body/Systems/BloodstreamSystem.cs | 147 ++++---- Content.Server/Body/Systems/BodySystem.cs | 78 +++-- Content.Server/Body/Systems/BrainSystem.cs | 6 +- .../Body/Systems/InternalsSystem.cs | 117 ++++--- Content.Server/Body/Systems/LungSystem.cs | 21 +- .../Body/Systems/MetabolizerSystem.cs | 112 +++--- .../Body/Systems/RespiratorSystem.cs | 108 +++--- Content.Server/Body/Systems/StomachSystem.cs | 63 ++-- .../Body/Systems/ThermalRegulatorSystem.cs | 68 ++-- Content.Server/Hands/Systems/HandsSystem.cs | 8 +- Content.Server/Species/Systems/NymphSystem.cs | 4 +- .../ActionBlocker/ActionBlockerSystem.cs | 4 +- .../Body/Events/MechanismBodyEvents.cs | 79 ++--- .../Body/Events/ShiverAttemptEvent.cs | 16 +- .../Body/Events/SweatAttemptEvent.cs | 16 +- Content.Shared/Body/Organ/OrganComponent.cs | 4 +- Content.Shared/Body/Part/BodyPartEvents.cs | 6 +- .../Body/Systems/SharedBodySystem.Body.cs | 119 ++++--- .../Body/Systems/SharedBodySystem.Organs.cs | 96 +++--- .../Body/Systems/SharedBodySystem.Parts.cs | 322 +++++++++--------- 32 files changed, 916 insertions(+), 820 deletions(-) diff --git a/Content.IntegrationTests/Tests/Body/LungTest.cs b/Content.IntegrationTests/Tests/Body/LungTest.cs index f2e19849b0..dce3741c98 100644 --- a/Content.IntegrationTests/Tests/Body/LungTest.cs +++ b/Content.IntegrationTests/Tests/Body/LungTest.cs @@ -9,7 +9,6 @@ using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Maths; using System.Linq; using System.Numerics; @@ -61,12 +60,11 @@ namespace Content.IntegrationTests.Tests.Body var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var mapLoader = entityManager.System(); - RespiratorSystem respSys = default; - MetabolizerSystem metaSys = default; MapId mapId; EntityUid? grid = null; BodyComponent body = default; + RespiratorComponent resp = default; EntityUid human = default; GridAtmosphereComponent relevantAtmos = default; var startingMoles = 0.0f; @@ -99,17 +97,15 @@ namespace Content.IntegrationTests.Tests.Body await server.WaitAssertion(() => { - var coords = new Vector2(0.5f, -1f); - var coordinates = new EntityCoordinates(grid.Value, coords); + var center = new Vector2(0.5f, 0.5f); + var coordinates = new EntityCoordinates(grid.Value, center); human = entityManager.SpawnEntity("HumanLungDummy", coordinates); - respSys = entityManager.System(); - metaSys = entityManager.System(); relevantAtmos = entityManager.GetComponent(grid.Value); - startingMoles = GetMapMoles(); + startingMoles = 100f; // Hardcoded because GetMapMoles returns 900 here for some reason. #pragma warning disable NUnit2045 Assert.That(entityManager.TryGetComponent(human, out body), Is.True); - Assert.That(entityManager.HasComponent(human), Is.True); + Assert.That(entityManager.TryGetComponent(human, out resp), Is.True); #pragma warning restore NUnit2045 }); @@ -118,18 +114,19 @@ namespace Content.IntegrationTests.Tests.Body var inhaleCycles = 100; for (var i = 0; i < inhaleCycles; i++) { - await server.WaitAssertion(() => - { - // inhale - respSys.Update(2.0f); - Assert.That(GetMapMoles(), Is.LessThan(startingMoles)); + // Breathe in + await PoolManager.WaitUntil(server, () => resp.Status == RespiratorStatus.Exhaling); + Assert.That( + GetMapMoles(), Is.LessThan(startingMoles), + "Did not inhale in any gas" + ); - // metabolize + exhale - metaSys.Update(1.0f); - metaSys.Update(1.0f); - respSys.Update(2.0f); - Assert.That(GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0002)); - }); + // Breathe out + await PoolManager.WaitUntil(server, () => resp.Status == RespiratorStatus.Inhaling); + Assert.That( + GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0002), + "Did not exhale as much gas as was inhaled" + ); } await pair.CleanReturnAsync(); diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs index 131bd4b182..49021c142f 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -93,9 +93,8 @@ namespace Content.Server.Bed if (!this.IsPowered(uid, EntityManager)) return; - var metabolicEvent = new ApplyMetabolicMultiplierEvent - {Uid = args.BuckledEntity, Multiplier = component.Multiplier, Apply = args.Buckling}; - RaiseLocalEvent(args.BuckledEntity, metabolicEvent); + var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.BuckledEntity, component.Multiplier, args.Buckling); + RaiseLocalEvent(args.BuckledEntity, ref metabolicEvent); } private void OnPowerChanged(EntityUid uid, StasisBedComponent component, ref PowerChangedEvent args) @@ -121,9 +120,8 @@ namespace Content.Server.Bed foreach (var buckledEntity in strap.BuckledEntities) { - var metabolicEvent = new ApplyMetabolicMultiplierEvent - {Uid = buckledEntity, Multiplier = component.Multiplier, Apply = shouldApply}; - RaiseLocalEvent(buckledEntity, metabolicEvent); + var metabolicEvent = new ApplyMetabolicMultiplierEvent(buckledEntity, component.Multiplier, shouldApply); + RaiseLocalEvent(buckledEntity, ref metabolicEvent); } } } diff --git a/Content.Server/Body/Commands/AddHandCommand.cs b/Content.Server/Body/Commands/AddHandCommand.cs index 655d0c88f9..3e006c539c 100644 --- a/Content.Server/Body/Commands/AddHandCommand.cs +++ b/Content.Server/Body/Commands/AddHandCommand.cs @@ -34,7 +34,6 @@ namespace Content.Server.Body.Commands switch (args.Length) { case 0: - { if (player == null) { shell.WriteLine("Only a player can run this command without arguments."); @@ -50,71 +49,68 @@ namespace Content.Server.Body.Commands entity = player.AttachedEntity.Value; hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent(entity).Coordinates); break; - } case 1: - { - if (NetEntity.TryParse(args[0], out var uidNet) && _entManager.TryGetEntity(uidNet, out var uid)) { + if (NetEntity.TryParse(args[0], out var uidNet) && _entManager.TryGetEntity(uidNet, out var uid)) + { + if (!_entManager.EntityExists(uid)) + { + shell.WriteLine($"No entity found with uid {uid}"); + return; + } + + entity = uid.Value; + hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent(entity).Coordinates); + } + else + { + if (player == null) + { + shell.WriteLine("You must specify an entity to add a hand to when using this command from the server terminal."); + return; + } + + if (player.AttachedEntity == null) + { + shell.WriteLine("You don't have an entity to add a hand to."); + return; + } + + entity = player.AttachedEntity.Value; + hand = _entManager.SpawnEntity(args[0], _entManager.GetComponent(entity).Coordinates); + } + + break; + } + case 2: + { + if (!NetEntity.TryParse(args[0], out var netEnt) || !_entManager.TryGetEntity(netEnt, out var uid)) + { + shell.WriteLine($"{args[0]} is not a valid entity uid."); + return; + } + if (!_entManager.EntityExists(uid)) { - shell.WriteLine($"No entity found with uid {uid}"); + shell.WriteLine($"No entity exists with uid {uid}."); return; } entity = uid.Value; - hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent(entity).Coordinates); - } - else - { - if (player == null) + + if (!_protoManager.HasIndex(args[1])) { - shell.WriteLine("You must specify an entity to add a hand to when using this command from the server terminal."); + shell.WriteLine($"No hand entity exists with id {args[1]}."); return; } - if (player.AttachedEntity == null) - { - shell.WriteLine("You don't have an entity to add a hand to."); - return; - } + hand = _entManager.SpawnEntity(args[1], _entManager.GetComponent(entity).Coordinates); - entity = player.AttachedEntity.Value; - hand = _entManager.SpawnEntity(args[0], _entManager.GetComponent(entity).Coordinates); + break; } - - break; - } - case 2: - { - if (!NetEntity.TryParse(args[0], out var netEnt) || !_entManager.TryGetEntity(netEnt, out var uid)) - { - shell.WriteLine($"{args[0]} is not a valid entity uid."); - return; - } - - if (!_entManager.EntityExists(uid)) - { - shell.WriteLine($"No entity exists with uid {uid}."); - return; - } - - entity = uid.Value; - - if (!_protoManager.HasIndex(args[1])) - { - shell.WriteLine($"No hand entity exists with id {args[1]}."); - return; - } - - hand = _entManager.SpawnEntity(args[1], _entManager.GetComponent(entity).Coordinates); - - break; - } default: - { shell.WriteLine(Help); return; - } } if (!_entManager.TryGetComponent(entity, out BodyComponent? body) || body.RootContainer.ContainedEntity == null) @@ -139,7 +135,7 @@ namespace Content.Server.Body.Commands var slotId = part.GetHashCode().ToString(); - if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, hand, BodyPartType.Hand,attachAt.Component, part)) + if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, hand, BodyPartType.Hand, attachAt.Component, part)) { shell.WriteError($"Couldn't create a slot with id {slotId} on entity {_entManager.ToPrettyString(entity)}"); return; diff --git a/Content.Server/Body/Commands/AttachBodyPartCommand.cs b/Content.Server/Body/Commands/AttachBodyPartCommand.cs index 24604b88b7..82f7161937 100644 --- a/Content.Server/Body/Commands/AttachBodyPartCommand.cs +++ b/Content.Server/Body/Commands/AttachBodyPartCommand.cs @@ -103,11 +103,11 @@ namespace Content.Server.Body.Commands // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (body.RootContainer.ContainedEntity != null) { - bodySystem.AttachPartToRoot(bodyId,partUid.Value, body ,part); + bodySystem.AttachPartToRoot(bodyId, partUid.Value, body, part); } else { - var (rootPartId,rootPart) = bodySystem.GetRootPartOrNull(bodyId, body)!.Value; + var (rootPartId, rootPart) = bodySystem.GetRootPartOrNull(bodyId, body)!.Value; if (!bodySystem.TryCreatePartSlotAndAttach(rootPartId, slotId, partUid.Value, part.PartType, rootPart, part)) { shell.WriteError($"Could not create slot {slotId} on entity {_entManager.ToPrettyString(bodyId)}"); diff --git a/Content.Server/Body/Components/BeingGibbedEvent.cs b/Content.Server/Body/Components/BeingGibbedEvent.cs index 66b52af47b..a010855f78 100644 --- a/Content.Server/Body/Components/BeingGibbedEvent.cs +++ b/Content.Server/Body/Components/BeingGibbedEvent.cs @@ -1,11 +1,7 @@ namespace Content.Server.Body.Components; -public sealed class BeingGibbedEvent : EntityEventArgs -{ - public readonly HashSet GibbedParts; - - public BeingGibbedEvent(HashSet gibbedParts) - { - GibbedParts = gibbedParts; - } -} +/// +/// Raised when a body gets gibbed, before it is deleted. +/// +[ByRefEvent] +public readonly record struct BeingGibbedEvent(HashSet GibbedParts); diff --git a/Content.Server/Body/Components/BloodstreamComponent.cs b/Content.Server/Body/Components/BloodstreamComponent.cs index 7041df4448..d448c4aab2 100644 --- a/Content.Server/Body/Components/BloodstreamComponent.cs +++ b/Content.Server/Body/Components/BloodstreamComponent.cs @@ -1,11 +1,13 @@ using Content.Server.Body.Systems; using Content.Server.Chemistry.EntitySystems; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Robust.Shared.Audio; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components { @@ -16,7 +18,17 @@ namespace Content.Server.Body.Components public static string DefaultBloodSolutionName = "bloodstream"; public static string DefaultBloodTemporarySolutionName = "bloodstreamTemporary"; - public float AccumulatedFrametime = 0.0f; + /// + /// The next time that blood level will be updated and bloodloss damage dealt. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdate; + + /// + /// The interval at which this component updates. + /// + [DataField] + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(3); /// /// How much is this entity currently bleeding? @@ -32,7 +44,7 @@ namespace Content.Server.Body.Components public float BleedAmount; /// - /// How much should bleeding should be reduced every update interval? + /// How much should bleeding be reduced every update interval? /// [DataField] public float BleedReductionAmount = 0.33f; @@ -63,18 +75,12 @@ namespace Content.Server.Body.Components [DataField(required: true)] public DamageSpecifier BloodlossHealDamage = new(); - /// - /// How frequently should this bloodstream update, in seconds? - /// - [DataField] - public float UpdateInterval = 3.0f; - // TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth. /// /// How much reagent of blood should be restored each update interval? /// [DataField] - public float BloodRefreshAmount = 1.0f; + public FixedPoint2 BloodRefreshAmount = 1.0f; /// /// How much blood needs to be in the temporary solution in order to create a puddle? @@ -89,8 +95,8 @@ namespace Content.Server.Body.Components /// /// For example, piercing damage is increased while poison damage is nullified entirely. /// - [DataField(customTypeSerializer:typeof(PrototypeIdSerializer))] - public string DamageBleedModifiers = "BloodlossHuman"; + [DataField] + public ProtoId DamageBleedModifiers = "BloodlossHuman"; /// /// The sound to be played when a weapon instantly deals blood loss damage. @@ -126,7 +132,7 @@ namespace Content.Server.Body.Components /// Slime-people might use slime as their blood or something like that. /// [DataField] - public string BloodReagent = "Blood"; + public ProtoId BloodReagent = "Blood"; /// Name/Key that is indexed by. [DataField] @@ -164,6 +170,6 @@ namespace Content.Server.Body.Components /// Variable that stores the amount of status time added by having a low blood level. /// [ViewVariables(VVAccess.ReadWrite)] - public float StatusTime; + public TimeSpan StatusTime; } } diff --git a/Content.Server/Body/Components/InternalsComponent.cs b/Content.Server/Body/Components/InternalsComponent.cs index 4eda008b0f..18caab8dcf 100644 --- a/Content.Server/Body/Components/InternalsComponent.cs +++ b/Content.Server/Body/Components/InternalsComponent.cs @@ -1,4 +1,3 @@ -using System.Threading; namespace Content.Server.Body.Components { /// @@ -7,14 +6,17 @@ namespace Content.Server.Body.Components [RegisterComponent] public sealed partial class InternalsComponent : Component { - [ViewVariables] public EntityUid? GasTankEntity { get; set; } - [ViewVariables] public EntityUid? BreathToolEntity { get; set; } + [ViewVariables] + public EntityUid? GasTankEntity; + + [ViewVariables] + public EntityUid? BreathToolEntity; /// - /// Toggle Internals delay (seconds) when the target is not you. + /// Toggle Internals delay when the target is not you. /// [ViewVariables(VVAccess.ReadWrite)] - [DataField("delay")] - public float Delay = 3; + [DataField] + public TimeSpan Delay = TimeSpan.FromSeconds(3); } } diff --git a/Content.Server/Body/Components/LungComponent.cs b/Content.Server/Body/Components/LungComponent.cs index 0656ef8fad..46600b3020 100644 --- a/Content.Server/Body/Components/LungComponent.cs +++ b/Content.Server/Body/Components/LungComponent.cs @@ -1,4 +1,4 @@ -using Content.Server.Atmos; +using Content.Server.Atmos; using Content.Server.Body.Systems; using Content.Shared.Alert; using Content.Shared.Atmos; @@ -11,7 +11,7 @@ public sealed partial class LungComponent : Component { [DataField] [Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends - public GasMixture Air { get; set; } = new() + public GasMixture Air = new() { Volume = 6, Temperature = Atmospherics.NormalBodyTemperature diff --git a/Content.Server/Body/Components/MetabolizerComponent.cs b/Content.Server/Body/Components/MetabolizerComponent.cs index a8c82f3d36..90c99df7db 100644 --- a/Content.Server/Body/Components/MetabolizerComponent.cs +++ b/Content.Server/Body/Components/MetabolizerComponent.cs @@ -1,8 +1,8 @@ -using Content.Server.Body.Systems; +using Content.Server.Body.Systems; using Content.Shared.Body.Prototypes; using Content.Shared.FixedPoint; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components { @@ -12,20 +12,24 @@ namespace Content.Server.Body.Components [RegisterComponent, Access(typeof(MetabolizerSystem))] public sealed partial class MetabolizerComponent : Component { - public float AccumulatedFrametime = 0.0f; + /// + /// The next time that reagents will be metabolized. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdate; /// - /// How often to metabolize reagents, in seconds. + /// How often to metabolize reagents. /// /// [DataField] - public float UpdateFrequency = 1.0f; + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1); /// /// From which solution will this metabolizer attempt to metabolize chemicals /// [DataField("solution")] - public string SolutionName { get; set; } = BloodstreamComponent.DefaultChemicalsSolutionName; + public string SolutionName = BloodstreamComponent.DefaultChemicalsSolutionName; /// /// Does this component use a solution on it's parent entity (the body) or itself @@ -39,9 +43,9 @@ namespace Content.Server.Body.Components /// /// List of metabolizer types that this organ is. ex. Human, Slime, Felinid, w/e. /// - [DataField(customTypeSerializer:typeof(PrototypeIdHashSetSerializer))] + [DataField] [Access(typeof(MetabolizerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends - public HashSet? MetabolizerTypes = null; + public HashSet>? MetabolizerTypes = null; /// /// Should this metabolizer remove chemicals that have no metabolisms defined? @@ -72,8 +76,8 @@ namespace Content.Server.Body.Components [DataDefinition] public sealed partial class MetabolismGroupEntry { - [DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] - public string Id = default!; + [DataField(required: true)] + public ProtoId Id = default!; [DataField("rateModifier")] public FixedPoint2 MetabolismRateModifier = 1.0; diff --git a/Content.Server/Body/Components/RespiratorComponent.cs b/Content.Server/Body/Components/RespiratorComponent.cs index 9f080a3dd9..4045e21e26 100644 --- a/Content.Server/Body/Components/RespiratorComponent.cs +++ b/Content.Server/Body/Components/RespiratorComponent.cs @@ -1,5 +1,6 @@ using Content.Server.Body.Systems; using Content.Shared.Damage; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components { @@ -7,36 +8,49 @@ namespace Content.Server.Body.Components public sealed partial class RespiratorComponent : Component { /// - /// Saturation level. Reduced by CycleDelay each tick. + /// The next time that this body will inhale or exhale. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdate; + + /// + /// The interval between updates. Each update is either inhale or exhale, + /// so a full cycle takes twice as long. + /// + [DataField] + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(2); + + /// + /// Saturation level. Reduced by UpdateInterval each tick. /// Can be thought of as 'how many seconds you have until you start suffocating' in this configuration. /// - [DataField("saturation")] + [DataField] public float Saturation = 5.0f; /// /// At what level of saturation will you begin to suffocate? /// - [DataField("suffocationThreshold")] + [DataField] public float SuffocationThreshold; - [DataField("maxSaturation")] + [DataField] public float MaxSaturation = 5.0f; - [DataField("minSaturation")] + [DataField] public float MinSaturation = -2.0f; // TODO HYPEROXIA? - [DataField("damage", required: true)] + [DataField(required: true)] [ViewVariables(VVAccess.ReadWrite)] public DamageSpecifier Damage = default!; - [DataField("damageRecovery", required: true)] + [DataField(required: true)] [ViewVariables(VVAccess.ReadWrite)] public DamageSpecifier DamageRecovery = default!; - [DataField("gaspPopupCooldown")] - public TimeSpan GaspPopupCooldown { get; private set; } = TimeSpan.FromSeconds(8); + [DataField] + public TimeSpan GaspPopupCooldown = TimeSpan.FromSeconds(8); [ViewVariables] public TimeSpan LastGaspPopupTime; @@ -55,11 +69,6 @@ namespace Content.Server.Body.Components [ViewVariables] public RespiratorStatus Status = RespiratorStatus.Inhaling; - - [DataField("cycleDelay")] - public float CycleDelay = 2.0f; - - public float AccumulatedFrametime; } } diff --git a/Content.Server/Body/Components/StomachComponent.cs b/Content.Server/Body/Components/StomachComponent.cs index fe93468f74..d541ca4d7c 100644 --- a/Content.Server/Body/Components/StomachComponent.cs +++ b/Content.Server/Body/Components/StomachComponent.cs @@ -1,21 +1,26 @@ -using Content.Server.Body.Systems; +using Content.Server.Body.Systems; using Content.Server.Nutrition.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.Whitelist; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components { [RegisterComponent, Access(typeof(StomachSystem), typeof(FoodSystem))] public sealed partial class StomachComponent : Component { - public float AccumulatedFrameTime; + /// + /// The next time that the stomach will try to digest its contents. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdate; /// - /// How fast should this component update, in seconds? + /// The interval at which this stomach digests its contents. /// [DataField] - public float UpdateInterval = 1.0f; + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1); /// /// The solution inside of this stomach this transfers reagents to the body. @@ -30,11 +35,11 @@ namespace Content.Server.Body.Components public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName; /// - /// Time in seconds between reagents being ingested and them being + /// Time between reagents being ingested and them being /// transferred to /// [DataField] - public float DigestionDelay = 20; + public TimeSpan DigestionDelay = TimeSpan.FromSeconds(20); /// /// A whitelist for what special-digestible-required foods this stomach is capable of eating. @@ -54,15 +59,15 @@ namespace Content.Server.Body.Components public sealed class ReagentDelta { public readonly ReagentQuantity ReagentQuantity; - public float Lifetime { get; private set; } + public TimeSpan Lifetime { get; private set; } public ReagentDelta(ReagentQuantity reagentQuantity) { ReagentQuantity = reagentQuantity; - Lifetime = 0.0f; + Lifetime = TimeSpan.Zero; } - public void Increment(float delta) => Lifetime += delta; + public void Increment(TimeSpan delta) => Lifetime += delta; } } } diff --git a/Content.Server/Body/Components/ThermalRegulatorComponent.cs b/Content.Server/Body/Components/ThermalRegulatorComponent.cs index 4acdccf1ba..19b76189e0 100644 --- a/Content.Server/Body/Components/ThermalRegulatorComponent.cs +++ b/Content.Server/Body/Components/ThermalRegulatorComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Body.Systems; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components; @@ -6,48 +7,58 @@ namespace Content.Server.Body.Components; [Access(typeof(ThermalRegulatorSystem))] public sealed partial class ThermalRegulatorComponent : Component { + /// + /// The next time that the body will regulate its heat. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdate; + + /// + /// The interval at which thermal regulation is processed. + /// + [DataField] + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1); + /// /// Heat generated due to metabolism. It's generated via metabolism /// - [DataField("metabolismHeat")] - public float MetabolismHeat { get; private set; } + [DataField] + public float MetabolismHeat; /// /// Heat output via radiation. /// - [DataField("radiatedHeat")] - public float RadiatedHeat { get; private set; } + [DataField] + public float RadiatedHeat; /// /// Maximum heat regulated via sweat /// - [DataField("sweatHeatRegulation")] - public float SweatHeatRegulation { get; private set; } + [DataField] + public float SweatHeatRegulation; /// /// Maximum heat regulated via shivering /// - [DataField("shiveringHeatRegulation")] - public float ShiveringHeatRegulation { get; private set; } + [DataField] + public float ShiveringHeatRegulation; /// /// Amount of heat regulation that represents thermal regulation processes not /// explicitly coded. /// - [DataField("implicitHeatRegulation")] - public float ImplicitHeatRegulation { get; private set; } + [DataField] + public float ImplicitHeatRegulation; /// /// Normal body temperature /// - [DataField("normalBodyTemperature")] - public float NormalBodyTemperature { get; private set; } + [DataField] + public float NormalBodyTemperature; /// /// Deviation from normal temperature for body to start thermal regulation /// - [DataField("thermalRegulationTemperatureThreshold")] - public float ThermalRegulationTemperatureThreshold { get; private set; } - - public float AccumulatedFrametime; + [DataField] + public float ThermalRegulationTemperatureThreshold; } diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index f6fdcfedff..9e29fdf756 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -13,7 +13,6 @@ using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Drunk; using Content.Shared.FixedPoint; -using Content.Shared.IdentityManagement; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; using Content.Shared.Rejuvenate; @@ -21,11 +20,13 @@ using Content.Shared.Speech.EntitySystems; using Robust.Server.Audio; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Server.Body.Systems; public sealed class BloodstreamSystem : EntitySystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly AudioSystem _audio = default!; @@ -44,6 +45,8 @@ public sealed class BloodstreamSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnUnpaused); SubscribeLocalEvent(OnDamageChanged); SubscribeLocalEvent(OnHealthBeingExamined); SubscribeLocalEvent(OnBeingGibbed); @@ -53,6 +56,16 @@ public sealed class BloodstreamSystem : EntitySystem SubscribeLocalEvent(OnRejuvenate); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; + } + + private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.NextUpdate += args.PausedTime; + } + private void OnReactionAttempt(Entity entity, ref ReactionAttemptEvent args) { if (args.Cancelled) @@ -83,7 +96,9 @@ public sealed class BloodstreamSystem : EntitySystem if (args.Name != entity.Comp.BloodSolutionName && args.Name != entity.Comp.ChemicalSolutionName && args.Name != entity.Comp.BloodTemporarySolutionName) + { return; + } OnReactionAttempt(entity, ref args.Event); } @@ -95,12 +110,10 @@ public sealed class BloodstreamSystem : EntitySystem var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var bloodstream)) { - bloodstream.AccumulatedFrametime += frameTime; - - if (bloodstream.AccumulatedFrametime < bloodstream.UpdateInterval) + if (_gameTiming.CurTime < bloodstream.NextUpdate) continue; - bloodstream.AccumulatedFrametime -= bloodstream.UpdateInterval; + bloodstream.NextUpdate += bloodstream.UpdateInterval; if (!_solutionContainerSystem.ResolveSolution(uid, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution)) continue; @@ -128,13 +141,17 @@ public sealed class BloodstreamSystem : EntitySystem // bloodloss damage is based on the base value, and modified by how low your blood level is. var amt = bloodstream.BloodlossDamage / (0.1f + bloodPercentage); - _damageableSystem.TryChangeDamage(uid, amt, false, false); + _damageableSystem.TryChangeDamage(uid, amt, + ignoreResistances: false, interruptsDoAfters: false); // Apply dizziness as a symptom of bloodloss. // The effect is applied in a way that it will never be cleared without being healthy. // Multiplying by 2 is arbitrary but works for this case, it just prevents the time from running out - _drunkSystem.TryApplyDrunkenness(uid, bloodstream.UpdateInterval*2, false); - _stutteringSystem.DoStutter(uid, TimeSpan.FromSeconds(bloodstream.UpdateInterval*2), false); + _drunkSystem.TryApplyDrunkenness( + uid, + (float) bloodstream.UpdateInterval.TotalSeconds * 2, + applySlur: false); + _stutteringSystem.DoStutter(uid, bloodstream.UpdateInterval * 2, refresh: false); // storing the drunk and stutter time so we can remove it independently from other effects additions bloodstream.StatusTime += bloodstream.UpdateInterval * 2; @@ -142,13 +159,16 @@ public sealed class BloodstreamSystem : EntitySystem else if (!_mobStateSystem.IsDead(uid)) { // If they're healthy, we'll try and heal some bloodloss instead. - _damageableSystem.TryChangeDamage(uid, bloodstream.BloodlossHealDamage * bloodPercentage, true, false); + _damageableSystem.TryChangeDamage( + uid, + bloodstream.BloodlossHealDamage * bloodPercentage, + ignoreResistances: true, interruptsDoAfters: false); // Remove the drunk effect when healthy. Should only remove the amount of drunk and stutter added by low blood level - _drunkSystem.TryRemoveDrunkenessTime(uid, bloodstream.StatusTime); - _stutteringSystem.DoRemoveStutterTime(uid, bloodstream.StatusTime); + _drunkSystem.TryRemoveDrunkenessTime(uid, bloodstream.StatusTime.TotalSeconds); + _stutteringSystem.DoRemoveStutterTime(uid, bloodstream.StatusTime.TotalSeconds); // Reset the drunk and stutter time to zero - bloodstream.StatusTime = 0; + bloodstream.StatusTime = TimeSpan.Zero; } } } @@ -167,17 +187,15 @@ public sealed class BloodstreamSystem : EntitySystem bloodSolution.AddReagent(entity.Comp.BloodReagent, entity.Comp.BloodMaxVolume - bloodSolution.Volume); } - private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, DamageChangedEvent args) + private void OnDamageChanged(Entity ent, ref DamageChangedEvent args) { - if (args.DamageDelta is null) - return; - - // definitely don't make them bleed if they got healed - if (!args.DamageIncreased) + if (args.DamageDelta is null || !args.DamageIncreased) + { return; + } // TODO probably cache this or something. humans get hurt a lot - if (!_prototypeManager.TryIndex(component.DamageBleedModifiers, out var modifiers)) + if (!_prototypeManager.TryIndex(ent.Comp.DamageBleedModifiers, out var modifiers)) return; var bloodloss = DamageSpecifier.ApplyModifierSet(args.DamageDelta, modifiers); @@ -186,10 +204,10 @@ public sealed class BloodstreamSystem : EntitySystem return; // Does the calculation of how much bleed rate should be added/removed, then applies it - var oldBleedAmount = component.BleedAmount; + var oldBleedAmount = ent.Comp.BleedAmount; var total = bloodloss.GetTotal(); var totalFloat = total.Float(); - TryModifyBleedAmount(uid, totalFloat, component); + TryModifyBleedAmount(ent, totalFloat, ent); /// /// Critical hit. Causes target to lose blood, using the bleed rate modifier of the weapon, currently divided by 5 @@ -199,8 +217,8 @@ public sealed class BloodstreamSystem : EntitySystem var prob = Math.Clamp(totalFloat / 25, 0, 1); if (totalFloat > 0 && _robustRandom.Prob(prob)) { - TryModifyBloodLevel(uid, (-total) / 5, component); - _audio.PlayPvs(component.InstantBloodSound, uid); + TryModifyBloodLevel(ent, (-total) / 5, ent); + _audio.PlayPvs(ent.Comp.InstantBloodSound, ent); } // Heat damage will cauterize, causing the bleed rate to be reduced. @@ -210,53 +228,52 @@ public sealed class BloodstreamSystem : EntitySystem // because it's burn damage that cauterized their wounds. // We'll play a special sound and popup for feedback. - _audio.PlayPvs(component.BloodHealedSound, uid); - _popupSystem.PopupEntity(Loc.GetString("bloodstream-component-wounds-cauterized"), uid, - uid, PopupType.Medium); + _audio.PlayPvs(ent.Comp.BloodHealedSound, ent); + _popupSystem.PopupEntity(Loc.GetString("bloodstream-component-wounds-cauterized"), ent, + ent, PopupType.Medium); } } /// /// Shows text on health examine, based on bleed rate and blood level. /// - private void OnHealthBeingExamined(EntityUid uid, BloodstreamComponent component, HealthBeingExaminedEvent args) + private void OnHealthBeingExamined(Entity ent, ref HealthBeingExaminedEvent args) { // Shows profusely bleeding at half the max bleed rate. - if (component.BleedAmount > component.MaxBleedAmount / 2) + if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount / 2) { args.Message.PushNewline(); - args.Message.AddMarkup(Loc.GetString("bloodstream-component-profusely-bleeding", ("target", Identity.Entity(uid, EntityManager)))); + args.Message.AddMarkup(Loc.GetString("bloodstream-component-profusely-bleeding", ("target", ent.Owner))); } // Shows bleeding message when bleeding, but less than profusely. - else if (component.BleedAmount > 0) + else if (ent.Comp.BleedAmount > 0) { args.Message.PushNewline(); - args.Message.AddMarkup(Loc.GetString("bloodstream-component-bleeding", ("target", Identity.Entity(uid, EntityManager)))); + args.Message.AddMarkup(Loc.GetString("bloodstream-component-bleeding", ("target", ent.Owner))); } // If the mob's blood level is below the damage threshhold, the pale message is added. - if (GetBloodLevelPercentage(uid, component) < component.BloodlossThreshold) + if (GetBloodLevelPercentage(ent, ent) < ent.Comp.BloodlossThreshold) { args.Message.PushNewline(); - args.Message.AddMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", Identity.Entity(uid, EntityManager)))); + args.Message.AddMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", ent.Owner))); } } - private void OnBeingGibbed(EntityUid uid, BloodstreamComponent component, BeingGibbedEvent args) + private void OnBeingGibbed(Entity ent, ref BeingGibbedEvent args) { - SpillAllSolutions(uid, component); + SpillAllSolutions(ent, ent); } - private void OnApplyMetabolicMultiplier(EntityUid uid, BloodstreamComponent component, ApplyMetabolicMultiplierEvent args) + private void OnApplyMetabolicMultiplier( + Entity ent, + ref ApplyMetabolicMultiplierEvent args) { if (args.Apply) { - component.UpdateInterval *= args.Multiplier; + ent.Comp.UpdateInterval *= args.Multiplier; return; } - component.UpdateInterval /= args.Multiplier; - // Reset the accumulator properly - if (component.AccumulatedFrametime >= component.UpdateInterval) - component.AccumulatedFrametime = component.UpdateInterval; + ent.Comp.UpdateInterval /= args.Multiplier; } private void OnRejuvenate(Entity entity, ref RejuvenateEvent args) @@ -275,21 +292,15 @@ public sealed class BloodstreamSystem : EntitySystem /// public bool TryAddToChemicals(EntityUid uid, Solution solution, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component, false)) - return false; - - if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution)) - return false; - - return _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution); + return Resolve(uid, ref component, logMissing: false) + && _solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution) + && _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution); } public bool FlushChemicals(EntityUid uid, string excludedReagentID, FixedPoint2 quantity, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component, false)) - return false; - - if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution)) + if (!Resolve(uid, ref component, logMissing: false) + || !_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution)) return false; for (var i = chemSolution.Contents.Count - 1; i >= 0; i--) @@ -306,11 +317,11 @@ public sealed class BloodstreamSystem : EntitySystem public float GetBloodLevelPercentage(EntityUid uid, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component)) - return 0.0f; - - if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution)) + if (!Resolve(uid, ref component) + || !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution)) + { return 0.0f; + } return bloodSolution.FillFraction; } @@ -328,11 +339,11 @@ public sealed class BloodstreamSystem : EntitySystem /// public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component, false)) - return false; - - if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution)) + if (!Resolve(uid, ref component, logMissing: false) + || !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution)) + { return false; + } if (amount >= 0) return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, out _); @@ -356,9 +367,9 @@ public sealed class BloodstreamSystem : EntitySystem tempSolution.AddSolution(temp, _prototypeManager); } - if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, false)) + if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false)) { - _forensicsSystem.TransferDna(puddleUid, uid, false); + _forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false); } tempSolution.RemoveAllSolution(); @@ -374,7 +385,7 @@ public sealed class BloodstreamSystem : EntitySystem /// public bool TryModifyBleedAmount(EntityUid uid, float amount, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component, false)) + if (!Resolve(uid, ref component, logMissing: false)) return false; component.BleedAmount += amount; @@ -424,7 +435,7 @@ public sealed class BloodstreamSystem : EntitySystem if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid)) { - _forensicsSystem.TransferDna(puddleUid, uid, false); + _forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false); } } @@ -433,11 +444,11 @@ public sealed class BloodstreamSystem : EntitySystem /// public void ChangeBloodReagent(EntityUid uid, string reagent, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component, false)) - return; - - if (reagent == component.BloodReagent) + if (!Resolve(uid, ref component, logMissing: false) + || reagent == component.BloodReagent) + { return; + } if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution)) { diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index 18119909ab..92074d06ff 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -1,23 +1,19 @@ using Content.Server.Body.Components; using Content.Server.GameTicking; using Content.Server.Humanoid; -using Content.Server.Kitchen.Components; using Content.Shared.Body.Components; using Content.Shared.Body.Part; using Content.Shared.Body.Systems; using Content.Shared.Humanoid; -using Content.Shared.Kitchen.Components; using Content.Shared.Mind; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; +using Content.Shared.Movement.Systems; using Robust.Shared.Audio; -using Robust.Shared.Player; +using Robust.Shared.Audio.Systems; using Robust.Shared.Random; using Robust.Shared.Timing; using System.Numerics; -using Content.Shared.Gibbing.Components; -using Content.Shared.Movement.Systems; -using Robust.Shared.Audio.Systems; namespace Content.Server.Body.Systems; @@ -28,7 +24,6 @@ public sealed class BodySystem : SharedBodySystem [Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly IRobustRandom _random = default!; @@ -40,7 +35,7 @@ public sealed class BodySystem : SharedBodySystem SubscribeLocalEvent(OnApplyMetabolicMultiplier); } - private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args) + private void OnRelayMoveInput(Entity ent, ref MoveInputEvent args) { // If they haven't actually moved then ignore it. if ((args.Component.HeldMoveButtons & @@ -49,68 +44,67 @@ public sealed class BodySystem : SharedBodySystem return; } - if (_mobState.IsDead(uid) && _mindSystem.TryGetMind(uid, out var mindId, out var mind)) + if (_mobState.IsDead(ent) && _mindSystem.TryGetMind(ent, out var mindId, out var mind)) { mind.TimeOfDeath ??= _gameTiming.RealTime; - _ticker.OnGhostAttempt(mindId, true, mind: mind); + _ticker.OnGhostAttempt(mindId, canReturnGlobal: true, mind: mind); } } - private void OnApplyMetabolicMultiplier(EntityUid uid, BodyComponent component, - ApplyMetabolicMultiplierEvent args) + private void OnApplyMetabolicMultiplier( + Entity ent, + ref ApplyMetabolicMultiplierEvent args) { - foreach (var organ in GetBodyOrgans(uid, component)) + foreach (var organ in GetBodyOrgans(ent, ent)) { - RaiseLocalEvent(organ.Id, args); + RaiseLocalEvent(organ.Id, ref args); } } protected override void AddPart( - EntityUid bodyUid, - EntityUid partUid, - string slotId, - BodyPartComponent component, - BodyComponent? bodyComp = null) + Entity bodyEnt, + Entity partEnt, + string slotId) { // TODO: Predict this probably. - base.AddPart(bodyUid, partUid, slotId, component, bodyComp); + base.AddPart(bodyEnt, partEnt, slotId); - if (TryComp(bodyUid, out var humanoid)) + if (TryComp(bodyEnt, out var humanoid)) { - var layer = component.ToHumanoidLayers(); + var layer = partEnt.Comp.ToHumanoidLayers(); if (layer != null) { var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value); - _humanoidSystem.SetLayersVisibility(bodyUid, layers, true, true, humanoid); + _humanoidSystem.SetLayersVisibility( + bodyEnt, layers, visible: true, permanent: true, humanoid); } } } protected override void RemovePart( - EntityUid bodyUid, - EntityUid partUid, - string slotId, - BodyPartComponent component, - BodyComponent? bodyComp = null) + Entity bodyEnt, + Entity partEnt, + string slotId) { - base.RemovePart(bodyUid, partUid, slotId, component, bodyComp); + base.RemovePart(bodyEnt, partEnt, slotId); - if (!TryComp(bodyUid, out var humanoid)) + if (!TryComp(bodyEnt, out var humanoid)) return; - var layer = component.ToHumanoidLayers(); + var layer = partEnt.Comp.ToHumanoidLayers(); - if (layer == null) + if (layer is null) return; var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value); - _humanoidSystem.SetLayersVisibility(bodyUid, layers, false, true, humanoid); + _humanoidSystem.SetLayersVisibility( + bodyEnt, layers, visible: false, permanent: true, humanoid); } public override HashSet GibBody( EntityUid bodyId, bool gibOrgans = false, - BodyComponent? body = null , + BodyComponent? body = null, bool launchGibs = true, Vector2? splatDirection = null, float splatModifier = 1, @@ -118,19 +112,23 @@ public sealed class BodySystem : SharedBodySystem SoundSpecifier? gibSoundOverride = null ) { - if (!Resolve(bodyId, ref body, false)) - return new HashSet(); - - if (TerminatingOrDeleted(bodyId) || EntityManager.IsQueuedForDeletion(bodyId)) + if (!Resolve(bodyId, ref body, logMissing: false) + || TerminatingOrDeleted(bodyId) + || EntityManager.IsQueuedForDeletion(bodyId)) + { return new HashSet(); + } var xform = Transform(bodyId); - if (xform.MapUid == null) + if (xform.MapUid is null) return new HashSet(); var gibs = base.GibBody(bodyId, gibOrgans, body, launchGibs: launchGibs, splatDirection: splatDirection, splatModifier: splatModifier, splatCone:splatCone); - RaiseLocalEvent(bodyId, new BeingGibbedEvent(gibs)); + + var ev = new BeingGibbedEvent(gibs); + RaiseLocalEvent(bodyId, ref ev); + QueueDel(bodyId); return gibs; diff --git a/Content.Server/Body/Systems/BrainSystem.cs b/Content.Server/Body/Systems/BrainSystem.cs index abb5497120..86d2cb61ff 100644 --- a/Content.Server/Body/Systems/BrainSystem.cs +++ b/Content.Server/Body/Systems/BrainSystem.cs @@ -16,8 +16,8 @@ namespace Content.Server.Body.Systems { base.Initialize(); - SubscribeLocalEvent((uid, _, args) => HandleMind(args.Body, uid)); - SubscribeLocalEvent((uid, _, args) => HandleMind(uid, args.OldBody)); + SubscribeLocalEvent((uid, _, args) => HandleMind(args.Body, uid)); + SubscribeLocalEvent((uid, _, args) => HandleMind(uid, args.OldBody)); SubscribeLocalEvent(OnPointAttempt); } @@ -39,7 +39,7 @@ namespace Content.Server.Body.Systems _mindSystem.TransferTo(mindId, newEntity, mind: mind); } - private void OnPointAttempt(EntityUid uid, BrainComponent component, PointAttemptEvent args) + private void OnPointAttempt(Entity ent, ref PointAttemptEvent args) { args.Cancel(); } diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index 004dae99b9..999aa40077 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -18,12 +18,10 @@ namespace Content.Server.Body.Systems; public sealed class InternalsSystem : EntitySystem { - [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly AtmosphereSystem _atmos = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly GasTankSystem _gasTank = default!; - [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; @@ -40,28 +38,36 @@ public sealed class InternalsSystem : EntitySystem SubscribeLocalEvent(OnDoAfter); } - private void OnGetInteractionVerbs(EntityUid uid, InternalsComponent component, GetVerbsEvent args) + private void OnGetInteractionVerbs( + Entity ent, + ref GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract || args.Hands == null) + if (!args.CanAccess || !args.CanInteract || args.Hands is null) return; + var user = args.User; + InteractionVerb verb = new() { Act = () => { - ToggleInternals(uid, args.User, false, component); + ToggleInternals(ent, user, force: false, ent); }, Message = Loc.GetString("action-description-internals-toggle"), - Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/dot.svg.192dpi.png")), + Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/dot.svg.192dpi.png")), Text = Loc.GetString("action-name-internals-toggle"), }; args.Verbs.Add(verb); } - public void ToggleInternals(EntityUid uid, EntityUid user, bool force, InternalsComponent? internals = null) + public void ToggleInternals( + EntityUid uid, + EntityUid user, + bool force, + InternalsComponent? internals = null) { - if (!Resolve(uid, ref internals, false)) + if (!Resolve(uid, ref internals, logMissing: false)) return; // Toggle off if they're on @@ -73,12 +79,12 @@ public sealed class InternalsSystem : EntitySystem return; } - StartToggleInternalsDoAfter(user, uid, internals); + StartToggleInternalsDoAfter(user, (uid, internals)); return; } // If they're not on then check if we have a mask to use - if (internals.BreathToolEntity == null) + if (internals.BreathToolEntity is null) { _popupSystem.PopupEntity(Loc.GetString("internals-no-breath-tool"), uid, user); return; @@ -86,7 +92,7 @@ public sealed class InternalsSystem : EntitySystem var tank = FindBestGasTank(uid); - if (tank == null) + if (tank is null) { _popupSystem.PopupEntity(Loc.GetString("internals-no-tank"), uid, user); return; @@ -94,20 +100,20 @@ public sealed class InternalsSystem : EntitySystem if (!force) { - StartToggleInternalsDoAfter(user, uid, internals); + StartToggleInternalsDoAfter(user, (uid, internals)); return; } _gasTank.ConnectToInternals(tank.Value); } - private void StartToggleInternalsDoAfter(EntityUid user, EntityUid target, InternalsComponent internals) + private void StartToggleInternalsDoAfter(EntityUid user, Entity targetEnt) { // Is the target not you? If yes, use a do-after to give them time to respond. - var isUser = user == target; - var delay = !isUser ? internals.Delay : 0f; + var isUser = user == targetEnt.Owner; + var delay = !isUser ? targetEnt.Comp.Delay : TimeSpan.Zero; - _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, delay, new InternalsDoAfterEvent(), target, target: target) + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, delay, new InternalsDoAfterEvent(), targetEnt, target: targetEnt) { BreakOnDamage = true, BreakOnMove = true, @@ -115,66 +121,64 @@ public sealed class InternalsSystem : EntitySystem }); } - private void OnDoAfter(EntityUid uid, InternalsComponent component, InternalsDoAfterEvent args) + private void OnDoAfter(Entity ent, ref InternalsDoAfterEvent args) { if (args.Cancelled || args.Handled) return; - ToggleInternals(uid, args.User, true, component); + ToggleInternals(ent, args.User, force: true, ent); args.Handled = true; } - private void OnInternalsStartup(EntityUid uid, InternalsComponent component, ComponentStartup args) + private void OnInternalsStartup(Entity ent, ref ComponentStartup args) { - _alerts.ShowAlert(uid, AlertType.Internals, GetSeverity(component)); + _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); } - private void OnInternalsShutdown(EntityUid uid, InternalsComponent component, ComponentShutdown args) + private void OnInternalsShutdown(Entity ent, ref ComponentShutdown args) { - _alerts.ClearAlert(uid, AlertType.Internals); + _alerts.ClearAlert(ent, AlertType.Internals); } - private void OnInhaleLocation(EntityUid uid, InternalsComponent component, InhaleLocationEvent args) + private void OnInhaleLocation(Entity ent, ref InhaleLocationEvent args) { - if (AreInternalsWorking(component)) + if (AreInternalsWorking(ent)) { - var gasTank = Comp(component.GasTankEntity!.Value); - args.Gas = _gasTank.RemoveAirVolume((component.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume); + var gasTank = Comp(ent.Comp.GasTankEntity!.Value); + args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume); // TODO: Should listen to gas tank updates instead I guess? - _alerts.ShowAlert(uid, AlertType.Internals, GetSeverity(component)); + _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); } } public void DisconnectBreathTool(Entity ent) { - var (owner, component) = ent; - var old = component.BreathToolEntity; - component.BreathToolEntity = null; + var old = ent.Comp.BreathToolEntity; + ent.Comp.BreathToolEntity = null; - if (TryComp(old, out BreathToolComponent? breathTool) ) + if (TryComp(old, out BreathToolComponent? breathTool)) { _atmos.DisconnectInternals(breathTool); DisconnectTank(ent); } - _alerts.ShowAlert(owner, AlertType.Internals, GetSeverity(component)); + _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); } public void ConnectBreathTool(Entity ent, EntityUid toolEntity) { - var (owner, component) = ent; - if (TryComp(component.BreathToolEntity, out BreathToolComponent? tool)) + if (TryComp(ent.Comp.BreathToolEntity, out BreathToolComponent? tool)) { _atmos.DisconnectInternals(tool); } - component.BreathToolEntity = toolEntity; - _alerts.ShowAlert(owner, AlertType.Internals, GetSeverity(component)); + ent.Comp.BreathToolEntity = toolEntity; + _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); } public void DisconnectTank(InternalsComponent? component) { - if (component == null) + if (component is null) return; if (TryComp(component.GasTankEntity, out GasTankComponent? tank)) @@ -186,46 +190,47 @@ public sealed class InternalsSystem : EntitySystem public bool TryConnectTank(Entity ent, EntityUid tankEntity) { - var component = ent.Comp; - if (component.BreathToolEntity == null) + if (ent.Comp.BreathToolEntity is null) return false; - if (TryComp(component.GasTankEntity, out GasTankComponent? tank)) - _gasTank.DisconnectFromInternals((component.GasTankEntity.Value, tank)); + if (TryComp(ent.Comp.GasTankEntity, out GasTankComponent? tank)) + _gasTank.DisconnectFromInternals((ent.Comp.GasTankEntity.Value, tank)); - component.GasTankEntity = tankEntity; - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(component)); + ent.Comp.GasTankEntity = tankEntity; + _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); return true; } public bool AreInternalsWorking(EntityUid uid, InternalsComponent? component = null) { - if (!Resolve(uid, ref component, false)) - return false; - - return AreInternalsWorking(component); + return Resolve(uid, ref component, logMissing: false) + && AreInternalsWorking(component); } public bool AreInternalsWorking(InternalsComponent component) { - return TryComp(component.BreathToolEntity, out BreathToolComponent? breathTool) && - breathTool.IsFunctional && - TryComp(component.GasTankEntity, out GasTankComponent? _); + return TryComp(component.BreathToolEntity, out BreathToolComponent? breathTool) + && breathTool.IsFunctional + && HasComp(component.GasTankEntity); } private short GetSeverity(InternalsComponent component) { - if (component.BreathToolEntity == null || !AreInternalsWorking(component)) + if (component.BreathToolEntity is null || !AreInternalsWorking(component)) return 2; // If pressure in the tank is below low pressure threshhold, flash warning on internals UI - if (TryComp(component.GasTankEntity, out var gasTank) && gasTank.IsLowPressure) + if (TryComp(component.GasTankEntity, out var gasTank) + && gasTank.IsLowPressure) + { return 0; + } return 1; } - public Entity? FindBestGasTank(Entity user) + public Entity? FindBestGasTank( + Entity user) { // Prioritise // 1. back equipped tanks @@ -233,17 +238,17 @@ public sealed class InternalsSystem : EntitySystem // 3. in-hand tanks // 4. pocket/belt tanks - if (!Resolve(user.Owner, ref user.Comp1, ref user.Comp2, ref user.Comp3)) + if (!Resolve(user, ref user.Comp1, ref user.Comp2, ref user.Comp3)) return null; - if (_inventory.TryGetSlotEntity(user.Owner, "back", out var backEntity, user.Comp2, user.Comp3) && + if (_inventory.TryGetSlotEntity(user, "back", out var backEntity, user.Comp2, user.Comp3) && TryComp(backEntity, out var backGasTank) && _gasTank.CanConnectToInternals(backGasTank)) { return (backEntity.Value, backGasTank); } - if (_inventory.TryGetSlotEntity(user.Owner, "suitstorage", out var entity, user.Comp2, user.Comp3) && + if (_inventory.TryGetSlotEntity(user, "suitstorage", out var entity, user.Comp2, user.Comp3) && TryComp(entity, out var gasTank) && _gasTank.CanConnectToInternals(gasTank)) { diff --git a/Content.Server/Body/Systems/LungSystem.cs b/Content.Server/Body/Systems/LungSystem.cs index 4b60f8814b..e83d3c32a2 100644 --- a/Content.Server/Body/Systems/LungSystem.cs +++ b/Content.Server/Body/Systems/LungSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Atmos.Components; +using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Components; using Content.Server.Chemistry.Containers.EntitySystems; @@ -26,21 +26,24 @@ public sealed class LungSystem : EntitySystem SubscribeLocalEvent(OnMaskToggled); } - private void OnGotUnequipped(EntityUid uid, BreathToolComponent component, GotUnequippedEvent args) + private void OnGotUnequipped(Entity ent, ref GotUnequippedEvent args) { - _atmosphereSystem.DisconnectInternals(component); + _atmosphereSystem.DisconnectInternals(ent); } - private void OnGotEquipped(EntityUid uid, BreathToolComponent component, GotEquippedEvent args) + private void OnGotEquipped(Entity ent, ref GotEquippedEvent args) { + if ((args.SlotFlags & ent.Comp.AllowedSlots) == 0) + { + return; + } - if ((args.SlotFlags & component.AllowedSlots) == 0) return; - component.IsFunctional = true; + ent.Comp.IsFunctional = true; if (TryComp(args.Equipee, out InternalsComponent? internals)) { - component.ConnectedInternalsEntity = args.Equipee; - _internals.ConnectBreathTool((args.Equipee, internals), uid); + ent.Comp.ConnectedInternalsEntity = args.Equipee; + _internals.ConnectBreathTool((args.Equipee, internals), ent); } } @@ -81,7 +84,7 @@ public sealed class LungSystem : EntitySystem if (moles <= 0) continue; var reagent = _atmosphereSystem.GasReagents[i]; - if (reagent == null) continue; + if (reagent is null) continue; var amount = moles * Atmospherics.BreathMolesToReagentMultiplier; solution.AddReagent(reagent, amount); diff --git a/Content.Server/Body/Systems/MetabolizerSystem.cs b/Content.Server/Body/Systems/MetabolizerSystem.cs index e5f604f70d..45cba5a195 100644 --- a/Content.Server/Body/Systems/MetabolizerSystem.cs +++ b/Content.Server/Body/Systems/MetabolizerSystem.cs @@ -12,11 +12,13 @@ using Content.Shared.Mobs.Systems; using Robust.Shared.Collections; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Server.Body.Systems { public sealed class MetabolizerSystem : EntitySystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; @@ -34,9 +36,21 @@ namespace Content.Server.Body.Systems _solutionQuery = GetEntityQuery(); SubscribeLocalEvent(OnMetabolizerInit); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnUnpaused); SubscribeLocalEvent(OnApplyMetabolicMultiplier); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; + } + + private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.NextUpdate += args.PausedTime; + } + private void OnMetabolizerInit(Entity entity, ref ComponentInit args) { if (!entity.Comp.SolutionOnBody) @@ -49,19 +63,17 @@ namespace Content.Server.Body.Systems } } - private void OnApplyMetabolicMultiplier(EntityUid uid, MetabolizerComponent component, - ApplyMetabolicMultiplierEvent args) + private void OnApplyMetabolicMultiplier( + Entity ent, + ref ApplyMetabolicMultiplierEvent args) { if (args.Apply) { - component.UpdateFrequency *= args.Multiplier; + ent.Comp.UpdateInterval *= args.Multiplier; return; } - component.UpdateFrequency /= args.Multiplier; - // Reset the accumulator properly - if (component.AccumulatedFrametime >= component.UpdateFrequency) - component.AccumulatedFrametime = component.UpdateFrequency; + ent.Comp.UpdateInterval /= args.Multiplier; } public override void Update(float frameTime) @@ -78,50 +90,52 @@ namespace Content.Server.Body.Systems foreach (var (uid, metab) in metabolizers) { - metab.AccumulatedFrametime += frameTime; - // Only update as frequently as it should - if (metab.AccumulatedFrametime < metab.UpdateFrequency) + if (_gameTiming.CurTime < metab.NextUpdate) continue; - metab.AccumulatedFrametime -= metab.UpdateFrequency; - TryMetabolize(uid, metab); + metab.NextUpdate += metab.UpdateInterval; + TryMetabolize((uid, metab)); } } - private void TryMetabolize(EntityUid uid, MetabolizerComponent meta, OrganComponent? organ = null) + private void TryMetabolize(Entity ent) { - _organQuery.Resolve(uid, ref organ, false); + _organQuery.Resolve(ent, ref ent.Comp2, logMissing: false); // First step is get the solution we actually care about + var solutionName = ent.Comp1.SolutionName; Solution? solution = null; Entity? soln = default!; EntityUid? solutionEntityUid = null; - SolutionContainerManagerComponent? manager = null; - - if (meta.SolutionOnBody) + if (ent.Comp1.SolutionOnBody) { - if (organ?.Body is { } body) + if (ent.Comp2?.Body is { } body) { - if (!_solutionQuery.Resolve(body, ref manager, false)) + if (!_solutionQuery.Resolve(body, ref ent.Comp3, logMissing: false)) return; - _solutionContainerSystem.TryGetSolution((body, manager), meta.SolutionName, out soln, out solution); + _solutionContainerSystem.TryGetSolution((body, ent.Comp3), solutionName, out soln, out solution); solutionEntityUid = body; } } else { - if (!_solutionQuery.Resolve(uid, ref manager, false)) + if (!_solutionQuery.Resolve(ent, ref ent.Comp3, logMissing: false)) return; - _solutionContainerSystem.TryGetSolution((uid, manager), meta.SolutionName, out soln, out solution); - solutionEntityUid = uid; + _solutionContainerSystem.TryGetSolution((ent, ent), solutionName, out soln, out solution); + solutionEntityUid = ent; } - if (solutionEntityUid == null || soln is null || solution is null || solution.Contents.Count == 0) + if (solutionEntityUid is null + || soln is null + || solution is null + || solution.Contents.Count == 0) + { return; + } // randomize the reagent list so we don't have any weird quirks // like alphabetical order or insertion order mattering for processing @@ -135,9 +149,9 @@ namespace Content.Server.Body.Systems continue; var mostToRemove = FixedPoint2.Zero; - if (proto.Metabolisms == null) + if (proto.Metabolisms is null) { - if (meta.RemoveEmpty) + if (ent.Comp1.RemoveEmpty) { solution.RemoveReagent(reagent, FixedPoint2.New(1)); } @@ -146,15 +160,15 @@ namespace Content.Server.Body.Systems } // we're done here entirely if this is true - if (reagents >= meta.MaxReagentsProcessable) + if (reagents >= ent.Comp1.MaxReagentsProcessable) return; // loop over all our groups and see which ones apply - if (meta.MetabolismGroups == null) + if (ent.Comp1.MetabolismGroups is null) continue; - foreach (var group in meta.MetabolismGroups) + foreach (var group in ent.Comp1.MetabolismGroups) { if (!proto.Metabolisms.TryGetValue(group.Id, out var entry)) continue; @@ -169,14 +183,14 @@ namespace Content.Server.Body.Systems // if it's possible for them to be dead, and they are, // then we shouldn't process any effects, but should probably // still remove reagents - if (EntityManager.TryGetComponent(solutionEntityUid.Value, out var state)) + if (TryComp(solutionEntityUid.Value, out var state)) { if (!proto.WorksOnTheDead && _mobStateSystem.IsDead(solutionEntityUid.Value, state)) continue; } - var actualEntity = organ?.Body ?? solutionEntityUid.Value; - var args = new ReagentEffectArgs(actualEntity, uid, solution, proto, mostToRemove, + var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value; + var args = new ReagentEffectArgs(actualEntity, ent, solution, proto, mostToRemove, EntityManager, null, scale); // do all effects, if conditions apply @@ -187,8 +201,14 @@ namespace Content.Server.Body.Systems if (effect.ShouldLog) { - _adminLogger.Add(LogType.ReagentEffect, effect.LogImpact, - $"Metabolism effect {effect.GetType().Name:effect} of reagent {proto.LocalizedName:reagent} applied on entity {actualEntity:entity} at {Transform(actualEntity).Coordinates:coordinates}"); + _adminLogger.Add( + LogType.ReagentEffect, + effect.LogImpact, + $"Metabolism effect {effect.GetType().Name:effect}" + + $" of reagent {proto.LocalizedName:reagent}" + + $" applied on entity {actualEntity:entity}" + + $" at {Transform(actualEntity).Coordinates:coordinates}" + ); } effect.Effect(args); @@ -209,15 +229,25 @@ namespace Content.Server.Body.Systems } } - public sealed class ApplyMetabolicMultiplierEvent : EntityEventArgs + [ByRefEvent] + public readonly record struct ApplyMetabolicMultiplierEvent( + EntityUid Uid, + float Multiplier, + bool Apply) { - // The entity whose metabolism is being modified - public EntityUid Uid; + /// + /// The entity whose metabolism is being modified. + /// + public readonly EntityUid Uid = Uid; - // What the metabolism's update rate will be multiplied by - public float Multiplier; + /// + /// What the metabolism's update rate will be multiplied by. + /// + public readonly float Multiplier = Multiplier; - // Apply this multiplier or ignore / reset it? - public bool Apply; + /// + /// If true, apply the multiplier. If false, revert it. + /// + public readonly bool Apply = Apply; } } diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index 0fd61a9cb7..c7266e2c46 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -35,9 +35,21 @@ public sealed class RespiratorSystem : EntitySystem // We want to process lung reagents before we inhale new reagents. UpdatesAfter.Add(typeof(MetabolizerSystem)); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnUnpaused); SubscribeLocalEvent(OnApplyMetabolicMultiplier); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; + } + + private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.NextUpdate += args.PausedTime; + } + public override void Update(float frameTime) { base.Update(frameTime); @@ -45,17 +57,15 @@ public sealed class RespiratorSystem : EntitySystem var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var respirator, out var body)) { + if (_gameTiming.CurTime < respirator.NextUpdate) + continue; + + respirator.NextUpdate += respirator.UpdateInterval; + if (_mobState.IsDead(uid)) - { continue; - } - respirator.AccumulatedFrametime += frameTime; - - if (respirator.AccumulatedFrametime < respirator.CycleDelay) - continue; - respirator.AccumulatedFrametime -= respirator.CycleDelay; - UpdateSaturation(uid, -respirator.CycleDelay, respirator); + UpdateSaturation(uid, -(float) respirator.UpdateInterval.TotalSeconds, respirator); if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit. { @@ -80,30 +90,30 @@ public sealed class RespiratorSystem : EntitySystem _popupSystem.PopupEntity(Loc.GetString("lung-behavior-gasp"), uid); } - TakeSuffocationDamage(uid, respirator); + TakeSuffocationDamage((uid, respirator)); respirator.SuffocationCycles += 1; continue; } - StopSuffocation(uid, respirator); + StopSuffocation((uid, respirator)); respirator.SuffocationCycles = 0; } } public void Inhale(EntityUid uid, BodyComponent? body = null) { - if (!Resolve(uid, ref body, false)) + if (!Resolve(uid, ref body, logMissing: false)) return; var organs = _bodySystem.GetBodyOrganComponents(uid, body); // Inhale gas var ev = new InhaleLocationEvent(); - RaiseLocalEvent(uid, ev); + RaiseLocalEvent(uid, ref ev, broadcast: false); - ev.Gas ??= _atmosSys.GetContainingMixture(uid, false, true); + ev.Gas ??= _atmosSys.GetContainingMixture(uid, excite: true); - if (ev.Gas == null) + if (ev.Gas is null) { return; } @@ -122,7 +132,7 @@ public sealed class RespiratorSystem : EntitySystem public void Exhale(EntityUid uid, BodyComponent? body = null) { - if (!Resolve(uid, ref body, false)) + if (!Resolve(uid, ref body, logMissing: false)) return; var organs = _bodySystem.GetBodyOrganComponents(uid, body); @@ -130,11 +140,11 @@ public sealed class RespiratorSystem : EntitySystem // exhale gas var ev = new ExhaleLocationEvent(); - RaiseLocalEvent(uid, ev, false); + RaiseLocalEvent(uid, ref ev, broadcast: false); - if (ev.Gas == null) + if (ev.Gas is null) { - ev.Gas = _atmosSys.GetContainingMixture(uid, false, true); + ev.Gas = _atmosSys.GetContainingMixture(uid, excite: true); // Walls and grids without atmos comp return null. I guess it makes sense to not be able to exhale in walls, // but this also means you cannot exhale on some grids. @@ -154,37 +164,37 @@ public sealed class RespiratorSystem : EntitySystem _atmosSys.Merge(ev.Gas, outGas); } - private void TakeSuffocationDamage(EntityUid uid, RespiratorComponent respirator) + private void TakeSuffocationDamage(Entity ent) { - if (respirator.SuffocationCycles == 2) - _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} started suffocating"); + if (ent.Comp.SuffocationCycles == 2) + _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} started suffocating"); - if (respirator.SuffocationCycles >= respirator.SuffocationCycleThreshold) + if (ent.Comp.SuffocationCycles >= ent.Comp.SuffocationCycleThreshold) { // TODO: This is not going work with multiple different lungs, if that ever becomes a possibility - var organs = _bodySystem.GetBodyOrganComponents(uid); + var organs = _bodySystem.GetBodyOrganComponents(ent); foreach (var (comp, _) in organs) { - _alertsSystem.ShowAlert(uid, comp.Alert); + _alertsSystem.ShowAlert(ent, comp.Alert); } } - _damageableSys.TryChangeDamage(uid, respirator.Damage, false, false); + _damageableSys.TryChangeDamage(ent, ent.Comp.Damage, interruptsDoAfters: false); } - private void StopSuffocation(EntityUid uid, RespiratorComponent respirator) + private void StopSuffocation(Entity ent) { - if (respirator.SuffocationCycles >= 2) - _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} stopped suffocating"); + if (ent.Comp.SuffocationCycles >= 2) + _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} stopped suffocating"); // TODO: This is not going work with multiple different lungs, if that ever becomes a possibility - var organs = _bodySystem.GetBodyOrganComponents(uid); + var organs = _bodySystem.GetBodyOrganComponents(ent); foreach (var (comp, _) in organs) { - _alertsSystem.ClearAlert(uid, comp.Alert); + _alertsSystem.ClearAlert(ent, comp.Alert); } - _damageableSys.TryChangeDamage(uid, respirator.DamageRecovery); + _damageableSys.TryChangeDamage(ent, ent.Comp.DamageRecovery); } public void UpdateSaturation(EntityUid uid, float amount, @@ -198,35 +208,29 @@ public sealed class RespiratorSystem : EntitySystem Math.Clamp(respirator.Saturation, respirator.MinSaturation, respirator.MaxSaturation); } - private void OnApplyMetabolicMultiplier(EntityUid uid, RespiratorComponent component, - ApplyMetabolicMultiplierEvent args) + private void OnApplyMetabolicMultiplier( + Entity ent, + ref ApplyMetabolicMultiplierEvent args) { if (args.Apply) { - component.CycleDelay *= args.Multiplier; - component.Saturation *= args.Multiplier; - component.MaxSaturation *= args.Multiplier; - component.MinSaturation *= args.Multiplier; + ent.Comp.UpdateInterval *= args.Multiplier; + ent.Comp.Saturation *= args.Multiplier; + ent.Comp.MaxSaturation *= args.Multiplier; + ent.Comp.MinSaturation *= args.Multiplier; return; } // This way we don't have to worry about it breaking if the stasis bed component is destroyed - component.CycleDelay /= args.Multiplier; - component.Saturation /= args.Multiplier; - component.MaxSaturation /= args.Multiplier; - component.MinSaturation /= args.Multiplier; - // Reset the accumulator properly - if (component.AccumulatedFrametime >= component.CycleDelay) - component.AccumulatedFrametime = component.CycleDelay; + ent.Comp.UpdateInterval /= args.Multiplier; + ent.Comp.Saturation /= args.Multiplier; + ent.Comp.MaxSaturation /= args.Multiplier; + ent.Comp.MinSaturation /= args.Multiplier; } } -public sealed class InhaleLocationEvent : EntityEventArgs -{ - public GasMixture? Gas; -} +[ByRefEvent] +public record struct InhaleLocationEvent(GasMixture? Gas); -public sealed class ExhaleLocationEvent : EntityEventArgs -{ - public GasMixture? Gas; -} +[ByRefEvent] +public record struct ExhaleLocationEvent(GasMixture? Gas); diff --git a/Content.Server/Body/Systems/StomachSystem.cs b/Content.Server/Body/Systems/StomachSystem.cs index 4c11244c37..a4c2e8292d 100644 --- a/Content.Server/Body/Systems/StomachSystem.cs +++ b/Content.Server/Body/Systems/StomachSystem.cs @@ -3,32 +3,44 @@ using Content.Server.Chemistry.Containers.EntitySystems; using Content.Shared.Body.Organ; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; +using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Body.Systems { public sealed class StomachSystem : EntitySystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; public const string DefaultSolutionName = "stomach"; public override void Initialize() { + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnUnpaused); SubscribeLocalEvent(OnApplyMetabolicMultiplier); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; + } + + private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.NextUpdate += args.PausedTime; + } + public override void Update(float frameTime) { var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var stomach, out var organ, out var sol)) { - stomach.AccumulatedFrameTime += frameTime; - - if (stomach.AccumulatedFrameTime < stomach.UpdateInterval) + if (_gameTiming.CurTime < stomach.NextUpdate) continue; - stomach.AccumulatedFrameTime -= stomach.UpdateInterval; + stomach.NextUpdate += stomach.UpdateInterval; // Get our solutions if (!_solutionContainerSystem.ResolveSolution((uid, sol), DefaultSolutionName, ref stomach.Solution, out var stomachSolution)) @@ -70,49 +82,44 @@ namespace Content.Server.Body.Systems } } - private void OnApplyMetabolicMultiplier(EntityUid uid, StomachComponent component, - ApplyMetabolicMultiplierEvent args) + private void OnApplyMetabolicMultiplier( + Entity ent, + ref ApplyMetabolicMultiplierEvent args) { if (args.Apply) { - component.UpdateInterval *= args.Multiplier; + ent.Comp.UpdateInterval *= args.Multiplier; return; } // This way we don't have to worry about it breaking if the stasis bed component is destroyed - component.UpdateInterval /= args.Multiplier; - // Reset the accumulator properly - if (component.AccumulatedFrameTime >= component.UpdateInterval) - component.AccumulatedFrameTime = component.UpdateInterval; + ent.Comp.UpdateInterval /= args.Multiplier; } - public bool CanTransferSolution(EntityUid uid, Solution solution, + public bool CanTransferSolution( + EntityUid uid, + Solution solution, StomachComponent? stomach = null, SolutionContainerManagerComponent? solutions = null) { - if (!Resolve(uid, ref stomach, ref solutions, false)) - return false; - - if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution)) - return false; - - // TODO: For now no partial transfers. Potentially change by design - if (!stomachSolution.CanAddSolution(solution)) - return false; - - return true; + return Resolve(uid, ref stomach, ref solutions, logMissing: false) + && _solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution) + // TODO: For now no partial transfers. Potentially change by design + && stomachSolution.CanAddSolution(solution); } - public bool TryTransferSolution(EntityUid uid, Solution solution, + public bool TryTransferSolution( + EntityUid uid, + Solution solution, StomachComponent? stomach = null, SolutionContainerManagerComponent? solutions = null) { - if (!Resolve(uid, ref stomach, ref solutions, false)) - return false; - - if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution) + if (!Resolve(uid, ref stomach, ref solutions, logMissing: false) + || !_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution) || !CanTransferSolution(uid, solution, stomach, solutions)) + { return false; + } _solutionContainerSystem.TryAddSolution(stomach.Solution.Value, solution); // Add each reagent to ReagentDeltas. Used to track how long each reagent has been in the stomach diff --git a/Content.Server/Body/Systems/ThermalRegulatorSystem.cs b/Content.Server/Body/Systems/ThermalRegulatorSystem.cs index a9556be773..a8bf4184ac 100644 --- a/Content.Server/Body/Systems/ThermalRegulatorSystem.cs +++ b/Content.Server/Body/Systems/ThermalRegulatorSystem.cs @@ -1,73 +1,95 @@ -using Content.Server.Body.Components; +using Content.Server.Body.Components; using Content.Server.Temperature.Components; using Content.Server.Temperature.Systems; using Content.Shared.ActionBlocker; +using Robust.Shared.Timing; namespace Content.Server.Body.Systems; public sealed class ThermalRegulatorSystem : EntitySystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly TemperatureSystem _tempSys = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSys = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnUnpaused); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; + } + + private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.NextUpdate += args.PausedTime; + } + public override void Update(float frameTime) { var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var regulator)) { - regulator.AccumulatedFrametime += frameTime; - if (regulator.AccumulatedFrametime < 1) + if (_gameTiming.CurTime < regulator.NextUpdate) continue; - regulator.AccumulatedFrametime -= 1; - ProcessThermalRegulation(uid, regulator); + regulator.NextUpdate += regulator.UpdateInterval; + ProcessThermalRegulation((uid, regulator)); } } /// /// Processes thermal regulation for a mob /// - private void ProcessThermalRegulation(EntityUid uid, ThermalRegulatorComponent comp) + private void ProcessThermalRegulation(Entity ent) { - if (!EntityManager.TryGetComponent(uid, out TemperatureComponent? temperatureComponent)) return; + if (!Resolve(ent, ref ent.Comp2, logMissing: false)) + return; - var totalMetabolismTempChange = comp.MetabolismHeat - comp.RadiatedHeat; + var totalMetabolismTempChange = ent.Comp1.MetabolismHeat - ent.Comp1.RadiatedHeat; // implicit heat regulation - var tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - comp.NormalBodyTemperature); - var heatCapacity = _tempSys.GetHeatCapacity(uid, temperatureComponent); + var tempDiff = Math.Abs(ent.Comp2.CurrentTemperature - ent.Comp1.NormalBodyTemperature); + var heatCapacity = _tempSys.GetHeatCapacity(ent, ent); var targetHeat = tempDiff * heatCapacity; - if (temperatureComponent.CurrentTemperature > comp.NormalBodyTemperature) + if (ent.Comp2.CurrentTemperature > ent.Comp1.NormalBodyTemperature) { - totalMetabolismTempChange -= Math.Min(targetHeat, comp.ImplicitHeatRegulation); + totalMetabolismTempChange -= Math.Min(targetHeat, ent.Comp1.ImplicitHeatRegulation); } else { - totalMetabolismTempChange += Math.Min(targetHeat, comp.ImplicitHeatRegulation); + totalMetabolismTempChange += Math.Min(targetHeat, ent.Comp1.ImplicitHeatRegulation); } - _tempSys.ChangeHeat(uid, totalMetabolismTempChange, true, temperatureComponent); + _tempSys.ChangeHeat(ent, totalMetabolismTempChange, ignoreHeatResistance: true, ent); // recalc difference and target heat - tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - comp.NormalBodyTemperature); + tempDiff = Math.Abs(ent.Comp2.CurrentTemperature - ent.Comp1.NormalBodyTemperature); targetHeat = tempDiff * heatCapacity; // if body temperature is not within comfortable, thermal regulation // processes starts - if (tempDiff > comp.ThermalRegulationTemperatureThreshold) + if (tempDiff > ent.Comp1.ThermalRegulationTemperatureThreshold) return; - if (temperatureComponent.CurrentTemperature > comp.NormalBodyTemperature) + if (ent.Comp2.CurrentTemperature > ent.Comp1.NormalBodyTemperature) { - if (!_actionBlockerSys.CanSweat(uid)) return; - _tempSys.ChangeHeat(uid, -Math.Min(targetHeat, comp.SweatHeatRegulation), true, - temperatureComponent); + if (!_actionBlockerSys.CanSweat(ent)) + return; + + _tempSys.ChangeHeat(ent, -Math.Min(targetHeat, ent.Comp1.SweatHeatRegulation), ignoreHeatResistance: true, ent); } else { - if (!_actionBlockerSys.CanShiver(uid)) return; - _tempSys.ChangeHeat(uid, Math.Min(targetHeat, comp.ShiveringHeatRegulation), true, - temperatureComponent); + if (!_actionBlockerSys.CanShiver(ent)) + return; + + _tempSys.ChangeHeat(ent, Math.Min(targetHeat, ent.Comp1.ShiveringHeatRegulation), ignoreHeatResistance: true, ent); } } } diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index c783701003..06946cd052 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -101,17 +101,17 @@ namespace Content.Server.Hands.Systems private void HandleBodyPartAdded(EntityUid uid, HandsComponent component, ref BodyPartAddedEvent args) { - if (args.Part.PartType != BodyPartType.Hand) + if (args.Part.Comp.PartType != BodyPartType.Hand) return; // If this annoys you, which it should. // Ping Smugleaf. - var location = args.Part.Symmetry switch + var location = args.Part.Comp.Symmetry switch { BodyPartSymmetry.None => HandLocation.Middle, BodyPartSymmetry.Left => HandLocation.Left, BodyPartSymmetry.Right => HandLocation.Right, - _ => throw new ArgumentOutOfRangeException(nameof(args.Part.Symmetry)) + _ => throw new ArgumentOutOfRangeException(nameof(args.Part.Comp.Symmetry)) }; AddHand(uid, args.Slot, location); @@ -119,7 +119,7 @@ namespace Content.Server.Hands.Systems private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args) { - if (args.Part.PartType != BodyPartType.Hand) + if (args.Part.Comp.PartType != BodyPartType.Hand) return; RemoveHand(uid, args.Slot); diff --git a/Content.Server/Species/Systems/NymphSystem.cs b/Content.Server/Species/Systems/NymphSystem.cs index b7751afbf1..d491b957bf 100644 --- a/Content.Server/Species/Systems/NymphSystem.cs +++ b/Content.Server/Species/Systems/NymphSystem.cs @@ -19,10 +19,10 @@ public sealed partial class NymphSystem : EntitySystem { base.Initialize(); - SubscribeLocalEvent(OnRemovedFromPart); + SubscribeLocalEvent(OnRemovedFromPart); } - private void OnRemovedFromPart(EntityUid uid, NymphComponent comp, RemovedFromPartInBodyEvent args) + private void OnRemovedFromPart(EntityUid uid, NymphComponent comp, ref OrganRemovedFromBodyEvent args) { if (!_timing.IsFirstTimePredicted) return; diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index a914a8f267..f5ed2df227 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -202,7 +202,7 @@ namespace Content.Shared.ActionBlocker public bool CanShiver(EntityUid uid) { var ev = new ShiverAttemptEvent(uid); - RaiseLocalEvent(uid, ev); + RaiseLocalEvent(uid, ref ev); return !ev.Cancelled; } @@ -210,7 +210,7 @@ namespace Content.Shared.ActionBlocker public bool CanSweat(EntityUid uid) { var ev = new SweatAttemptEvent(uid); - RaiseLocalEvent(uid, ev); + RaiseLocalEvent(uid, ref ev); return !ev.Cancelled; } diff --git a/Content.Shared/Body/Events/MechanismBodyEvents.cs b/Content.Shared/Body/Events/MechanismBodyEvents.cs index b52a333613..968b172aef 100644 --- a/Content.Shared/Body/Events/MechanismBodyEvents.cs +++ b/Content.Shared/Body/Events/MechanismBodyEvents.cs @@ -1,61 +1,28 @@ -namespace Content.Shared.Body.Events -{ - // All of these events are raised on a mechanism entity when added/removed to a body in different - // ways. +namespace Content.Shared.Body.Events; - /// - /// Raised on a mechanism when it is added to a body part. - /// - public sealed class AddedToPartEvent : EntityEventArgs - { - public EntityUid Part; +// All of these events are raised on a mechanism entity when added/removed to a body in different +// ways. - public AddedToPartEvent(EntityUid part) - { - Part = part; - } - } +/// +/// Raised on a mechanism when it is added to a body part. +/// +[ByRefEvent] +public readonly record struct OrganAddedEvent(EntityUid Part); - /// - /// Raised on a mechanism when it is added to a body part within a body. - /// - public sealed class AddedToPartInBodyEvent : EntityEventArgs - { - public EntityUid Body; - public EntityUid Part; +/// +/// Raised on a mechanism when it is added to a body part within a body. +/// +[ByRefEvent] +public readonly record struct OrganAddedToBodyEvent(EntityUid Body, EntityUid Part); - public AddedToPartInBodyEvent(EntityUid body, EntityUid part) - { - Body = body; - Part = part; - } - } +/// +/// Raised on a mechanism when it is removed from a body part. +/// +[ByRefEvent] +public readonly record struct OrganRemovedEvent(EntityUid OldPart); - /// - /// Raised on a mechanism when it is removed from a body part. - /// - public sealed class RemovedFromPartEvent : EntityEventArgs - { - public EntityUid OldPart; - - public RemovedFromPartEvent(EntityUid oldPart) - { - OldPart = oldPart; - } - } - - /// - /// Raised on a mechanism when it is removed from a body part within a body. - /// - public sealed class RemovedFromPartInBodyEvent : EntityEventArgs - { - public EntityUid OldBody; - public EntityUid OldPart; - - public RemovedFromPartInBodyEvent(EntityUid oldBody, EntityUid oldPart) - { - OldBody = oldBody; - OldPart = oldPart; - } - } -} +/// +/// Raised on a mechanism when it is removed from a body part within a body. +/// +[ByRefEvent] +public readonly record struct OrganRemovedFromBodyEvent(EntityUid OldBody, EntityUid OldPart); diff --git a/Content.Shared/Body/Events/ShiverAttemptEvent.cs b/Content.Shared/Body/Events/ShiverAttemptEvent.cs index 8c2761f545..e9400bc48d 100644 --- a/Content.Shared/Body/Events/ShiverAttemptEvent.cs +++ b/Content.Shared/Body/Events/ShiverAttemptEvent.cs @@ -1,12 +1,8 @@ -namespace Content.Shared.Body.Events -{ - public sealed class ShiverAttemptEvent : CancellableEntityEventArgs - { - public ShiverAttemptEvent(EntityUid uid) - { - Uid = uid; - } +namespace Content.Shared.Body.Events; - public EntityUid Uid { get; } - } +[ByRefEvent] +public record struct ShiverAttemptEvent(EntityUid Uid) +{ + public readonly EntityUid Uid = Uid; + public bool Cancelled = false; } diff --git a/Content.Shared/Body/Events/SweatAttemptEvent.cs b/Content.Shared/Body/Events/SweatAttemptEvent.cs index 7f4b3fab15..7506538c43 100644 --- a/Content.Shared/Body/Events/SweatAttemptEvent.cs +++ b/Content.Shared/Body/Events/SweatAttemptEvent.cs @@ -1,12 +1,8 @@ -namespace Content.Shared.Body.Events -{ - public sealed class SweatAttemptEvent : CancellableEntityEventArgs - { - public SweatAttemptEvent(EntityUid uid) - { - Uid = uid; - } +namespace Content.Shared.Body.Events; - public EntityUid Uid { get; } - } +[ByRefEvent] +public record struct SweatAttemptEvent(EntityUid Uid) +{ + public readonly EntityUid Uid = Uid; + public bool Cancelled = false; } diff --git a/Content.Shared/Body/Organ/OrganComponent.cs b/Content.Shared/Body/Organ/OrganComponent.cs index 9e1de6b355..3048927b5f 100644 --- a/Content.Shared/Body/Organ/OrganComponent.cs +++ b/Content.Shared/Body/Organ/OrganComponent.cs @@ -1,4 +1,4 @@ -using Content.Shared.Body.Systems; +using Content.Shared.Body.Systems; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -11,6 +11,6 @@ public sealed partial class OrganComponent : Component /// /// Relevant body this organ is attached to. /// - [DataField("body"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? Body; } diff --git a/Content.Shared/Body/Part/BodyPartEvents.cs b/Content.Shared/Body/Part/BodyPartEvents.cs index 4dbc543fc8..0d8d2c8a26 100644 --- a/Content.Shared/Body/Part/BodyPartEvents.cs +++ b/Content.Shared/Body/Part/BodyPartEvents.cs @@ -1,7 +1,7 @@ -namespace Content.Shared.Body.Part; +namespace Content.Shared.Body.Part; [ByRefEvent] -public readonly record struct BodyPartAddedEvent(string Slot, BodyPartComponent Part); +public readonly record struct BodyPartAddedEvent(string Slot, Entity Part); [ByRefEvent] -public readonly record struct BodyPartRemovedEvent(string Slot, BodyPartComponent Part); +public readonly record struct BodyPartRemovedEvent(string Slot, Entity Part); diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index bc7cf63124..1a35afdbe0 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -9,7 +9,6 @@ using Content.Shared.Gibbing.Components; using Content.Shared.Gibbing.Events; using Content.Shared.Gibbing.Systems; using Content.Shared.Inventory; -using Content.Shared.Inventory.Events; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -30,8 +29,10 @@ public partial class SharedBodySystem [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly GibbingSystem _gibbingSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + private const float GibletLaunchImpulse = 8; private const float GibletLaunchImpulseVariance = 3; + private void InitializeBody() { // Body here to handle root body parts. @@ -43,7 +44,7 @@ public partial class SharedBodySystem SubscribeLocalEvent(OnBodyCanDrag); } - private void OnBodyInserted(EntityUid uid, BodyComponent component, EntInsertedIntoContainerMessage args) + private void OnBodyInserted(Entity ent, ref EntInsertedIntoContainerMessage args) { // Root body part? var slotId = args.Container.ID; @@ -51,21 +52,21 @@ public partial class SharedBodySystem if (slotId != BodyRootContainerId) return; - var entity = args.Entity; + var insertedUid = args.Entity; - if (TryComp(entity, out BodyPartComponent? childPart)) + if (TryComp(insertedUid, out BodyPartComponent? part)) { - AddPart(uid, entity, slotId, childPart); - RecursiveBodyUpdate(entity, uid, childPart); + AddPart((ent, ent), (insertedUid, part), slotId); + RecursiveBodyUpdate((insertedUid, part), ent); } - if (TryComp(entity, out OrganComponent? organ)) + if (TryComp(insertedUid, out OrganComponent? organ)) { - AddOrgan(entity, uid, uid, organ); + AddOrgan((insertedUid, organ), ent, ent); } } - private void OnBodyRemoved(EntityUid uid, BodyComponent component, EntRemovedFromContainerMessage args) + private void OnBodyRemoved(Entity ent, ref EntRemovedFromContainerMessage args) { // Root body part? var slotId = args.Container.ID; @@ -73,55 +74,55 @@ public partial class SharedBodySystem if (slotId != BodyRootContainerId) return; - var entity = args.Entity; - DebugTools.Assert(!TryComp(entity, out BodyPartComponent? b) || b.Body == uid); - DebugTools.Assert(!TryComp(entity, out OrganComponent? o) || o.Body == uid); + var removedUid = args.Entity; + DebugTools.Assert(!TryComp(removedUid, out BodyPartComponent? b) || b.Body == ent); + DebugTools.Assert(!TryComp(removedUid, out OrganComponent? o) || o.Body == ent); - if (TryComp(entity, out BodyPartComponent? childPart)) + if (TryComp(removedUid, out BodyPartComponent? part)) { - RemovePart(uid, entity, slotId, childPart); - RecursiveBodyUpdate(entity, null, childPart); + RemovePart((ent, ent), (removedUid, part), slotId); + RecursiveBodyUpdate((removedUid, part), null); } - if (TryComp(entity, out OrganComponent? organ)) - RemoveOrgan(entity, uid, organ); + if (TryComp(removedUid, out OrganComponent? organ)) + RemoveOrgan((removedUid, organ), ent); } - private void OnBodyInit(EntityUid bodyId, BodyComponent body, ComponentInit args) + private void OnBodyInit(Entity ent, ref ComponentInit args) { // Setup the initial container. - body.RootContainer = Containers.EnsureContainer(bodyId, BodyRootContainerId); + ent.Comp.RootContainer = Containers.EnsureContainer(ent, BodyRootContainerId); } - private void OnBodyMapInit(EntityUid bodyId, BodyComponent body, MapInitEvent args) + private void OnBodyMapInit(Entity ent, ref MapInitEvent args) { - if (body.Prototype == null) + if (ent.Comp.Prototype is null) return; // One-time setup // Obviously can't run in Init to avoid double-spawns on save / load. - var prototype = Prototypes.Index(body.Prototype.Value); - MapInitBody(bodyId, prototype); + var prototype = Prototypes.Index(ent.Comp.Prototype.Value); + MapInitBody(ent, prototype); } private void MapInitBody(EntityUid bodyEntity, BodyPrototype prototype) { var protoRoot = prototype.Slots[prototype.Root]; - if (protoRoot.Part == null) + if (protoRoot.Part is null) return; // This should already handle adding the entity to the root. - var rootPartEntity = SpawnInContainerOrDrop(protoRoot.Part, bodyEntity, BodyRootContainerId); - var rootPart = Comp(rootPartEntity); + var rootPartUid = SpawnInContainerOrDrop(protoRoot.Part, bodyEntity, BodyRootContainerId); + var rootPart = Comp(rootPartUid); rootPart.Body = bodyEntity; - Dirty(rootPartEntity, rootPart); + Dirty(rootPartUid, rootPart); // Setup the rest of the body entities. - SetupOrgans(rootPartEntity, rootPart, protoRoot.Organs); - MapInitParts(rootPartEntity, prototype); + SetupOrgans((rootPartUid, rootPart), protoRoot.Organs); + MapInitParts(rootPartUid, prototype); } - private void OnBodyCanDrag(EntityUid uid, BodyComponent component, ref CanDragEvent args) + private void OnBodyCanDrag(Entity ent, ref CanDragEvent args) { args.Handled = true; } @@ -169,7 +170,7 @@ public partial class SharedBodySystem var partSlot = CreatePartSlot(parentEntity, connection, childPartComponent.PartType, parentPartComponent); var cont = Containers.GetContainer(parentEntity, GetPartSlotContainerId(connection)); - if (partSlot == null || !Containers.Insert(childPart, cont)) + if (partSlot is null || !Containers.Insert(childPart, cont)) { Log.Error($"Could not create slot for connection {connection} in body {prototype.ID}"); QueueDel(childPart); @@ -177,7 +178,7 @@ public partial class SharedBodySystem } // Add organs - SetupOrgans(childPart, childPartComponent, connectionSlot.Organs); + SetupOrgans((childPart, childPartComponent), connectionSlot.Organs); // Enqueue it so we can also get its neighbors. frontier.Enqueue(connection); @@ -185,16 +186,16 @@ public partial class SharedBodySystem } } - private void SetupOrgans(EntityUid partId, BodyPartComponent partComponent, Dictionary organs) + private void SetupOrgans(Entity ent, Dictionary organs) { foreach (var (organSlotId, organProto) in organs) { - var slot = CreateOrganSlot(organSlotId, partId, partComponent); - SpawnInContainerOrDrop(organProto, partId, GetOrganContainerId(organSlotId)); + var slot = CreateOrganSlot((ent, ent), organSlotId); + SpawnInContainerOrDrop(organProto, ent, GetOrganContainerId(organSlotId)); - if (slot == null) + if (slot is null) { - Log.Error($"Could not create organ for slot {organSlotId} in {ToPrettyString(partId)}"); + Log.Error($"Could not create organ for slot {organSlotId} in {ToPrettyString(ent)}"); } } } @@ -202,12 +203,14 @@ public partial class SharedBodySystem /// /// Gets all body containers on this entity including the root one. /// - public IEnumerable GetBodyContainers(EntityUid id, BodyComponent? body = null, + public IEnumerable GetBodyContainers( + EntityUid id, + BodyComponent? body = null, BodyPartComponent? rootPart = null) { - if (!Resolve(id, ref body, false) || - body.RootContainer.ContainedEntity == null || - !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart)) + if (!Resolve(id, ref body, logMissing: false) + || body.RootContainer.ContainedEntity is null + || !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart)) { yield break; } @@ -223,13 +226,15 @@ public partial class SharedBodySystem /// /// Gets all child body parts of this entity, including the root entity. /// - public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildren(EntityUid? id, BodyComponent? body = null, + public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildren( + EntityUid? id, + BodyComponent? body = null, BodyPartComponent? rootPart = null) { - if (id == null || - !Resolve(id.Value, ref body, false) || - body.RootContainer.ContainedEntity == null || - !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart)) + if (id is null + || !Resolve(id.Value, ref body, logMissing: false) + || body.RootContainer.ContainedEntity is null + || !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart)) { yield break; } @@ -240,9 +245,11 @@ public partial class SharedBodySystem } } - public IEnumerable<(EntityUid Id, OrganComponent Component)> GetBodyOrgans(EntityUid? bodyId, BodyComponent? body = null) + public IEnumerable<(EntityUid Id, OrganComponent Component)> GetBodyOrgans( + EntityUid? bodyId, + BodyComponent? body = null) { - if (bodyId == null || !Resolve(bodyId.Value, ref body, false)) + if (bodyId is null || !Resolve(bodyId.Value, ref body, logMissing: false)) yield break; foreach (var part in GetBodyChildren(bodyId, body)) @@ -260,10 +267,15 @@ public partial class SharedBodySystem /// /// /// - public IEnumerable GetBodyAllSlots(EntityUid bodyId, BodyComponent? body = null) + public IEnumerable GetBodyAllSlots( + EntityUid bodyId, + BodyComponent? body = null) { - if (!Resolve(bodyId, ref body, false) || body.RootContainer.ContainedEntity == null) + if (!Resolve(bodyId, ref body, logMissing: false) + || body.RootContainer.ContainedEntity is null) + { yield break; + } foreach (var slot in GetAllBodyPartSlots(body.RootContainer.ContainedEntity.Value)) { @@ -279,12 +291,11 @@ public partial class SharedBodySystem Vector2? splatDirection = null, float splatModifier = 1, Angle splatCone = default, - SoundSpecifier? gibSoundOverride = null - ) + SoundSpecifier? gibSoundOverride = null) { var gibs = new HashSet(); - if (!Resolve(bodyId, ref body, false)) + if (!Resolve(bodyId, ref body, logMissing: false)) return gibs; var root = GetRootPartOrNull(bodyId, body); @@ -311,7 +322,7 @@ public partial class SharedBodySystem launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone); } } - if(TryComp(bodyId, out var inventory)) + if (TryComp(bodyId, out var inventory)) { foreach (var item in _inventory.GetHandOrInventoryEntities(bodyId)) { diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs b/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs index fa11390705..efabebfc85 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Content.Shared.Body.Components; using Content.Shared.Body.Events; using Content.Shared.Body.Organ; @@ -9,41 +9,50 @@ namespace Content.Shared.Body.Systems; public partial class SharedBodySystem { - private void AddOrgan(EntityUid uid, EntityUid bodyUid, EntityUid parentPartUid, OrganComponent component) + private void AddOrgan( + Entity organEnt, + EntityUid bodyUid, + EntityUid parentPartUid) { - component.Body = bodyUid; - RaiseLocalEvent(uid, new AddedToPartEvent(parentPartUid)); + organEnt.Comp.Body = bodyUid; + var addedEv = new OrganAddedEvent(parentPartUid); + RaiseLocalEvent(organEnt, ref addedEv); - if (component.Body != null) - RaiseLocalEvent(uid, new AddedToPartInBodyEvent(component.Body.Value, parentPartUid)); - - Dirty(uid, component); - } - - private void RemoveOrgan(EntityUid uid, EntityUid parentPartUid, OrganComponent component) - { - RaiseLocalEvent(uid, new RemovedFromPartEvent(parentPartUid)); - - if (component.Body != null) + if (organEnt.Comp.Body is not null) { - RaiseLocalEvent(uid, new RemovedFromPartInBodyEvent(component.Body.Value, parentPartUid)); + var addedInBodyEv = new OrganAddedToBodyEvent(bodyUid, parentPartUid); + RaiseLocalEvent(organEnt, ref addedInBodyEv); } - component.Body = null; - Dirty(uid, component); + Dirty(organEnt, organEnt.Comp); + } + + private void RemoveOrgan(Entity organEnt, EntityUid parentPartUid) + { + var removedEv = new OrganRemovedEvent(parentPartUid); + RaiseLocalEvent(organEnt, ref removedEv); + + if (organEnt.Comp.Body is { Valid: true } bodyUid) + { + var removedInBodyEv = new OrganRemovedFromBodyEvent(bodyUid, parentPartUid); + RaiseLocalEvent(organEnt, ref removedInBodyEv); + } + + organEnt.Comp.Body = null; + Dirty(organEnt, organEnt.Comp); } /// /// Creates the specified organ slot on the parent entity. /// - private OrganSlot? CreateOrganSlot(string slotId, EntityUid parent, BodyPartComponent? part = null) + private OrganSlot? CreateOrganSlot(Entity parentEnt, string slotId) { - if (!Resolve(parent, ref part, false)) + if (!Resolve(parentEnt, ref parentEnt.Comp, logMissing: false)) return null; - Containers.EnsureContainer(parent, GetOrganContainerId(slotId)); + Containers.EnsureContainer(parentEnt, GetOrganContainerId(slotId)); var slot = new OrganSlot(slotId); - part.Organs.Add(slotId, slot); + parentEnt.Comp.Organs.Add(slotId, slot); return slot; } @@ -58,20 +67,23 @@ public partial class SharedBodySystem { slot = null; - if (parent == null || !Resolve(parent.Value, ref part, false)) + if (parent is null || !Resolve(parent.Value, ref part, logMissing: false)) { return false; } Containers.EnsureContainer(parent.Value, GetOrganContainerId(slotId)); slot = new OrganSlot(slotId); - return part.Organs.TryAdd(slotId,slot.Value); + return part.Organs.TryAdd(slotId, slot.Value); } /// /// Returns whether the slotId exists on the partId. /// - public bool CanInsertOrgan(EntityUid partId, string slotId, BodyPartComponent? part = null) + public bool CanInsertOrgan( + EntityUid partId, + string slotId, + BodyPartComponent? part = null) { return Resolve(partId, ref part) && part.Organs.ContainsKey(slotId); } @@ -79,26 +91,32 @@ public partial class SharedBodySystem /// /// Returns whether the specified organ slot exists on the partId. /// - public bool CanInsertOrgan(EntityUid partId, OrganSlot slot, BodyPartComponent? part = null) + public bool CanInsertOrgan( + EntityUid partId, + OrganSlot slot, + BodyPartComponent? part = null) { return CanInsertOrgan(partId, slot.Id, part); } - public bool InsertOrgan(EntityUid partId, EntityUid organId, string slotId, BodyPartComponent? part = null, OrganComponent? organ = null) + public bool InsertOrgan( + EntityUid partId, + EntityUid organId, + string slotId, + BodyPartComponent? part = null, + OrganComponent? organ = null) { - if (!Resolve(organId, ref organ, false) || - !Resolve(partId, ref part, false) || - !CanInsertOrgan(partId, slotId, part)) + if (!Resolve(organId, ref organ, logMissing: false) + || !Resolve(partId, ref part, logMissing: false) + || !CanInsertOrgan(partId, slotId, part)) { return false; } var containerId = GetOrganContainerId(slotId); - if (!Containers.TryGetContainer(partId, containerId, out var container)) - return false; - - return Containers.Insert(organId, container); + return Containers.TryGetContainer(partId, containerId, out var container) + && Containers.Insert(organId, container); } /// @@ -111,10 +129,8 @@ public partial class SharedBodySystem var parent = container.Owner; - if (!HasComp(parent)) - return false; - - return Containers.Remove(organId, container); + return HasComp(parent) + && Containers.Remove(organId, container); } /// @@ -126,8 +142,8 @@ public partial class SharedBodySystem BodyPartComponent? part = null, OrganComponent? organ = null) { - if (!Resolve(partId, ref part, false) || - !Resolve(organId, ref organ, false)) + if (!Resolve(partId, ref part, logMissing: false) + || !Resolve(organId, ref organ, logMissing: false)) { return false; } diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index e07aac0622..ee79faa0b8 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Body.Components; using Content.Shared.Body.Events; @@ -23,52 +23,52 @@ public partial class SharedBodySystem SubscribeLocalEvent(OnBodyPartRemoved); } - private void OnBodyPartInserted(EntityUid uid, BodyPartComponent component, EntInsertedIntoContainerMessage args) + private void OnBodyPartInserted(Entity ent, ref EntInsertedIntoContainerMessage args) { // Body part inserted into another body part. - var entity = args.Entity; + var insertedUid = args.Entity; var slotId = args.Container.ID; - if (component.Body == null) + if (ent.Comp.Body is null) return; - if (TryComp(entity, out BodyPartComponent? childPart)) + if (TryComp(insertedUid, out BodyPartComponent? part)) { - AddPart(component.Body.Value, entity, slotId, childPart); - RecursiveBodyUpdate(entity, component.Body.Value, childPart); + AddPart(ent.Comp.Body.Value, (insertedUid, part), slotId); + RecursiveBodyUpdate((insertedUid, part), ent.Comp.Body.Value); } - if (TryComp(entity, out OrganComponent? organ)) - AddOrgan(entity, component.Body.Value, uid, organ); + if (TryComp(insertedUid, out OrganComponent? organ)) + AddOrgan((insertedUid, organ), ent.Comp.Body.Value, ent); } - private void OnBodyPartRemoved(EntityUid uid, BodyPartComponent component, EntRemovedFromContainerMessage args) + private void OnBodyPartRemoved(Entity ent, ref EntRemovedFromContainerMessage args) { // Body part removed from another body part. - var entity = args.Entity; + var removedUid = args.Entity; var slotId = args.Container.ID; - DebugTools.Assert(!TryComp(entity, out BodyPartComponent? b) || b.Body == component.Body); - DebugTools.Assert(!TryComp(entity, out OrganComponent? o) || o.Body == component.Body); + DebugTools.Assert(!TryComp(removedUid, out BodyPartComponent? b) || b.Body == ent.Comp.Body); + DebugTools.Assert(!TryComp(removedUid, out OrganComponent? o) || o.Body == ent.Comp.Body); - if (TryComp(entity, out BodyPartComponent? childPart) && childPart.Body != null) + if (TryComp(removedUid, out BodyPartComponent? part) && part.Body is not null) { - RemovePart(childPart.Body.Value, entity, slotId, childPart); - RecursiveBodyUpdate(entity, null, childPart); + RemovePart(part.Body.Value, (removedUid, part), slotId); + RecursiveBodyUpdate((removedUid, part), null); } - if (TryComp(entity, out OrganComponent? organ)) - RemoveOrgan(entity, uid, organ); + if (TryComp(removedUid, out OrganComponent? organ)) + RemoveOrgan((removedUid, organ), ent); } - private void RecursiveBodyUpdate(EntityUid uid, EntityUid? bodyUid, BodyPartComponent component) + private void RecursiveBodyUpdate(Entity ent, EntityUid? bodyUid) { - component.Body = bodyUid; - Dirty(uid, component); + ent.Comp.Body = bodyUid; + Dirty(ent, ent.Comp); - foreach (var slotId in component.Organs.Keys) + foreach (var slotId in ent.Comp.Organs.Keys) { - if (!Containers.TryGetContainer(uid, GetOrganContainerId(slotId), out var container)) + if (!Containers.TryGetContainer(ent, GetOrganContainerId(slotId), out var container)) continue; foreach (var organ in container.ContainedEntities) @@ -78,105 +78,108 @@ public partial class SharedBodySystem Dirty(organ, organComp); - if (organComp.Body != null) - RaiseLocalEvent(organ, new RemovedFromPartInBodyEvent(organComp.Body.Value, uid)); + if (organComp.Body is { Valid: true } oldBodyUid) + { + var removedEv = new OrganRemovedFromBodyEvent(oldBodyUid, ent); + RaiseLocalEvent(organ, ref removedEv); + } organComp.Body = bodyUid; - if (bodyUid != null) - RaiseLocalEvent(organ, new AddedToPartInBodyEvent(bodyUid.Value, uid)); + if (bodyUid is not null) + { + var addedEv = new OrganAddedToBodyEvent(bodyUid.Value, ent); + RaiseLocalEvent(organ, ref addedEv); + } } } - foreach (var slotId in component.Children.Keys) + foreach (var slotId in ent.Comp.Children.Keys) { - if (!Containers.TryGetContainer(uid, GetPartSlotContainerId(slotId), out var container)) + if (!Containers.TryGetContainer(ent, GetPartSlotContainerId(slotId), out var container)) continue; - foreach (var containedEnt in container.ContainedEntities) + foreach (var containedUid in container.ContainedEntities) { - if (TryComp(containedEnt, out BodyPartComponent? childPart)) - RecursiveBodyUpdate(containedEnt, bodyUid, childPart); + if (TryComp(containedUid, out BodyPartComponent? childPart)) + RecursiveBodyUpdate((containedUid, childPart), bodyUid); } } } protected virtual void AddPart( - EntityUid bodyUid, - EntityUid partUid, - string slotId, - BodyPartComponent component, - BodyComponent? bodyComp = null) + Entity bodyEnt, + Entity partEnt, + string slotId) { - DebugTools.AssertOwner(partUid, component); - Dirty(partUid, component); - component.Body = bodyUid; + Dirty(partEnt, partEnt.Comp); + partEnt.Comp.Body = bodyEnt; - var ev = new BodyPartAddedEvent(slotId, component); - RaiseLocalEvent(bodyUid, ref ev); + var ev = new BodyPartAddedEvent(slotId, partEnt); + RaiseLocalEvent(bodyEnt, ref ev); - AddLeg(partUid, bodyUid, component, bodyComp); + AddLeg(partEnt, bodyEnt); } protected virtual void RemovePart( - EntityUid bodyUid, - EntityUid partUid, - string slotId, - BodyPartComponent component, - BodyComponent? bodyComp = null) + Entity bodyEnt, + Entity partEnt, + string slotId) { - DebugTools.AssertOwner(partUid, component); - Resolve(bodyUid, ref bodyComp, false); - Dirty(partUid, component); - component.Body = null; + Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false); + Dirty(partEnt, partEnt.Comp); + partEnt.Comp.Body = null; - var ev = new BodyPartRemovedEvent(slotId, component); - RaiseLocalEvent(bodyUid, ref ev); + var ev = new BodyPartRemovedEvent(slotId, partEnt); + RaiseLocalEvent(bodyEnt, ref ev); - RemoveLeg(partUid, bodyUid, component); - PartRemoveDamage(bodyUid, component, bodyComp); + RemoveLeg(partEnt, bodyEnt); + PartRemoveDamage(bodyEnt, partEnt); } - private void AddLeg(EntityUid uid, EntityUid bodyUid, BodyPartComponent component, BodyComponent? bodyComp = null) + private void AddLeg(Entity legEnt, Entity bodyEnt) { - if (!Resolve(bodyUid, ref bodyComp, false)) + if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false)) return; - if (component.PartType == BodyPartType.Leg) + if (legEnt.Comp.PartType == BodyPartType.Leg) { - bodyComp.LegEntities.Add(uid); - UpdateMovementSpeed(bodyUid); - Dirty(bodyUid, bodyComp); + bodyEnt.Comp.LegEntities.Add(legEnt); + UpdateMovementSpeed(bodyEnt); + Dirty(bodyEnt, bodyEnt.Comp); } } - private void RemoveLeg(EntityUid uid, EntityUid bodyUid, BodyPartComponent component, BodyComponent? bodyComp = null) + private void RemoveLeg(Entity legEnt, Entity bodyEnt) { - if (!Resolve(bodyUid, ref bodyComp, false)) + if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false)) return; - if (component.PartType == BodyPartType.Leg) + if (legEnt.Comp.PartType == BodyPartType.Leg) { - bodyComp.LegEntities.Remove(uid); - UpdateMovementSpeed(bodyUid); - Dirty(bodyUid, bodyComp); + bodyEnt.Comp.LegEntities.Remove(legEnt); + UpdateMovementSpeed(bodyEnt); + Dirty(bodyEnt, bodyEnt.Comp); - if (!bodyComp.LegEntities.Any()) + if (!bodyEnt.Comp.LegEntities.Any()) { - Standing.Down(bodyUid); + Standing.Down(bodyEnt); } } } - private void PartRemoveDamage(EntityUid parent, BodyPartComponent component, BodyComponent? bodyComp = null) + private void PartRemoveDamage(Entity bodyEnt, Entity partEnt) { - if (!Resolve(parent, ref bodyComp, false)) + if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false)) return; - if (!_timing.ApplyingState && component.IsVital && !GetBodyChildrenOfType(parent, component.PartType, bodyComp).Any()) + if (!_timing.ApplyingState + && partEnt.Comp.IsVital + && !GetBodyChildrenOfType(bodyEnt, partEnt.Comp.PartType, bodyEnt.Comp).Any() + ) { // TODO BODY SYSTEM KILL : remove this when wounding and required parts are implemented properly var damage = new DamageSpecifier(Prototypes.Index("Bloodloss"), 300); - Damageable.TryChangeDamage(parent, damage); + Damageable.TryChangeDamage(bodyEnt, damage); } } @@ -212,7 +215,8 @@ public partial class SharedBodySystem var parent = container.Owner; - if (!TryComp(parent, out var parentBody) || !parentBody.Children.ContainsKey(slotId)) + if (!TryComp(parent, out var parentBody) + || !parentBody.Children.ContainsKey(slotId)) return null; return (parent, slotId); @@ -252,7 +256,7 @@ public partial class SharedBodySystem BodyPartType partType, BodyPartComponent? part = null) { - if (!Resolve(partUid, ref part, false)) + if (!Resolve(partUid, ref part, logMissing: false)) return null; Containers.EnsureContainer(partUid, GetPartSlotContainerId(slotId)); @@ -275,8 +279,8 @@ public partial class SharedBodySystem { slot = null; - if (partId == null || - !Resolve(partId.Value, ref part, false)) + if (partId is null + || !Resolve(partId.Value, ref part, logMissing: false)) { return false; } @@ -310,24 +314,31 @@ public partial class SharedBodySystem /// /// Returns true if the partId is the root body container for the specified bodyId. /// - public bool IsPartRoot(EntityUid bodyId, EntityUid partId, BodyComponent? body = null, BodyPartComponent? part = null) + public bool IsPartRoot( + EntityUid bodyId, + EntityUid partId, + BodyComponent? body = null, + BodyPartComponent? part = null) { - if (!Resolve(partId, ref part)|| !Resolve(bodyId, ref body)) - return false; - - return Containers.TryGetContainingContainer(bodyId, partId, out var container) && container.ID == BodyRootContainerId; + return Resolve(partId, ref part) + && Resolve(bodyId, ref body) + && Containers.TryGetContainingContainer(bodyId, partId, out var container) + && container.ID == BodyRootContainerId; } /// /// Returns true if we can attach the partId to the bodyId as the root entity. /// - public bool CanAttachToRoot(EntityUid bodyId, EntityUid partId, BodyComponent? body = null, + public bool CanAttachToRoot( + EntityUid bodyId, + EntityUid partId, + BodyComponent? body = null, BodyPartComponent? part = null) { - return Resolve(bodyId, ref body) && - Resolve(partId, ref part) && - body.RootContainer.ContainedEntity == null && - bodyId != part.Body; + return Resolve(bodyId, ref body) + && Resolve(partId, ref part) + && body.RootContainer.ContainedEntity is null + && bodyId != part.Body; } /// @@ -335,8 +346,11 @@ public partial class SharedBodySystem /// public (EntityUid Entity, BodyPartComponent BodyPart)? GetRootPartOrNull(EntityUid bodyId, BodyComponent? body = null) { - if (!Resolve(bodyId, ref body) || body.RootContainer.ContainedEntity == null) + if (!Resolve(bodyId, ref body) + || body.RootContainer.ContainedEntity is null) + { return null; + } return (body.RootContainer.ContainedEntity.Value, Comp(body.RootContainer.ContainedEntity.Value)); @@ -352,13 +366,9 @@ public partial class SharedBodySystem BodyPartComponent? parentPart = null, BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false) || - !Resolve(parentId, ref parentPart, false)) - { - return false; - } - - return CanAttachPart(parentId, slot.Id, partId, parentPart, part); + return Resolve(partId, ref part, logMissing: false) + && Resolve(parentId, ref parentPart, logMissing: false) + && CanAttachPart(parentId, slot.Id, partId, parentPart, part); } /// @@ -371,16 +381,12 @@ public partial class SharedBodySystem BodyPartComponent? parentPart = null, BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false) || - !Resolve(parentId, ref parentPart, false) || - !parentPart.Children.TryGetValue(slotId, out var parentSlotData)) - { - return false; - } - - return part.PartType == parentSlotData.Type && - Containers.TryGetContainer(parentId, GetPartSlotContainerId(slotId), out var container) && - Containers.CanInsert(partId, container); + return Resolve(partId, ref part, logMissing: false) + && Resolve(parentId, ref parentPart, logMissing: false) + && parentPart.Children.TryGetValue(slotId, out var parentSlotData) + && part.PartType == parentSlotData.Type + && Containers.TryGetContainer(parentId, GetPartSlotContainerId(slotId), out var container) + && Containers.CanInsert(partId, container); } public bool AttachPartToRoot( @@ -389,14 +395,10 @@ public partial class SharedBodySystem BodyComponent? body = null, BodyPartComponent? part = null) { - if (!Resolve(bodyId, ref body) || - !Resolve(partId, ref part) || - !CanAttachToRoot(bodyId, partId, body, part)) - { - return false; - } - - return Containers.Insert(partId, body.RootContainer); + return Resolve(bodyId, ref body) + && Resolve(partId, ref part) + && CanAttachToRoot(bodyId, partId, body, part) + && Containers.Insert(partId, body.RootContainer); } #endregion @@ -406,20 +408,16 @@ public partial class SharedBodySystem /// /// Attaches a body part to the specified body part parent. /// - public bool AttachPart( - EntityUid parentPartId, - string slotId, - EntityUid partId, - BodyPartComponent? parentPart = null, - BodyPartComponent? part = null) + public bool AttachPart( + EntityUid parentPartId, + string slotId, + EntityUid partId, + BodyPartComponent? parentPart = null, + BodyPartComponent? part = null) { - if (!Resolve(parentPartId, ref parentPart, false) || - !parentPart.Children.TryGetValue(slotId, out var slot)) - { - return false; - } - - return AttachPart(parentPartId, slot, partId, parentPart, part); + return Resolve(parentPartId, ref parentPart, logMissing: false) + && parentPart.Children.TryGetValue(slotId, out var slot) + && AttachPart(parentPartId, slot, partId, parentPart, part); } /// @@ -432,10 +430,10 @@ public partial class SharedBodySystem BodyPartComponent? parentPart = null, BodyPartComponent? part = null) { - if (!Resolve(parentPartId, ref parentPart, false) || - !Resolve(partId, ref part, false) || - !CanAttachPart(parentPartId, slot.Id, partId, parentPart, part) || - !parentPart.Children.ContainsKey(slot.Id)) + if (!Resolve(parentPartId, ref parentPart, logMissing: false) + || !Resolve(partId, ref part, logMissing: false) + || !CanAttachPart(parentPartId, slot.Id, partId, parentPart, part) + || !parentPart.Children.ContainsKey(slot.Id)) { return false; } @@ -453,13 +451,16 @@ public partial class SharedBodySystem #region Misc - public void UpdateMovementSpeed(EntityUid bodyId, BodyComponent? body = null, MovementSpeedModifierComponent? movement = null) + public void UpdateMovementSpeed( + EntityUid bodyId, + BodyComponent? body = null, + MovementSpeedModifierComponent? movement = null) { - if (!Resolve(bodyId, ref body, ref movement, false)) - return; - - if (body.RequiredLegs <= 0) + if (!Resolve(bodyId, ref body, ref movement, logMissing: false) + || body.RequiredLegs <= 0) + { return; + } var walkSpeed = 0f; var sprintSpeed = 0f; @@ -488,7 +489,7 @@ public partial class SharedBodySystem /// public IEnumerable<(EntityUid Id, OrganComponent Component)> GetPartOrgans(EntityUid partId, BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) yield break; foreach (var slotId in part.Organs.Keys) @@ -513,7 +514,7 @@ public partial class SharedBodySystem /// public IEnumerable GetPartContainers(EntityUid id, BodyPartComponent? part = null) { - if (!Resolve(id, ref part, false) || + if (!Resolve(id, ref part, logMissing: false) || part.Children.Count == 0) { yield break; @@ -541,9 +542,11 @@ public partial class SharedBodySystem /// /// Returns all body part components for this entity including itself. /// - public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyPartChildren(EntityUid partId, BodyPartComponent? part = null) + public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyPartChildren( + EntityUid partId, + BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) yield break; yield return (partId, part); @@ -571,9 +574,11 @@ public partial class SharedBodySystem /// /// Returns all body part slots for this entity. /// - public IEnumerable GetAllBodyPartSlots(EntityUid partId, BodyPartComponent? part = null) + public IEnumerable GetAllBodyPartSlots( + EntityUid partId, + BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) yield break; foreach (var (slotId, slot) in part.Children) @@ -601,7 +606,10 @@ public partial class SharedBodySystem /// /// Returns true if the bodyId has any parts of this type. /// - public bool BodyHasPartType(EntityUid bodyId, BodyPartType type, BodyComponent? body = null) + public bool BodyHasPartType( + EntityUid bodyId, + BodyPartType type, + BodyComponent? body = null) { return GetBodyChildrenOfType(bodyId, type, body).Any(); } @@ -615,8 +623,8 @@ public partial class SharedBodySystem BodyPartComponent? parent, BodyPartComponent? child) { - if (!Resolve(parentId, ref parent, false) || - !Resolve(childId, ref child, false)) + if (!Resolve(parentId, ref parent, logMissing: false) + || !Resolve(childId, ref child, logMissing: false)) { return false; } @@ -638,15 +646,11 @@ public partial class SharedBodySystem BodyComponent? body = null, BodyPartComponent? part = null) { - if (!Resolve(bodyId, ref body, false) || - body.RootContainer.ContainedEntity == null || - !Resolve(partId, ref part, false) || - !TryComp(body.RootContainer.ContainedEntity, out BodyPartComponent? rootPart)) - { - return false; - } - - return PartHasChild(body.RootContainer.ContainedEntity.Value, partId, rootPart, part); + return Resolve(bodyId, ref body, logMissing: false) + && body.RootContainer.ContainedEntity is not null + && Resolve(partId, ref part, logMissing: false) + && TryComp(body.RootContainer.ContainedEntity, out BodyPartComponent? rootPart) + && PartHasChild(body.RootContainer.ContainedEntity.Value, partId, rootPart, part); } public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildrenOfType( @@ -721,9 +725,11 @@ public partial class SharedBodySystem /// /// Gets the parent body part and all immediate child body parts for the partId. /// - public IEnumerable GetBodyPartAdjacentParts(EntityUid partId, BodyPartComponent? part = null) + public IEnumerable GetBodyPartAdjacentParts( + EntityUid partId, + BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) yield break; if (TryGetParentBodyPart(partId, out var parentUid, out _)) @@ -745,7 +751,7 @@ public partial class SharedBodySystem BodyPartComponent? part = null) where T : IComponent { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) yield break; var query = GetEntityQuery(); @@ -762,7 +768,7 @@ public partial class SharedBodySystem BodyPartComponent? part = null) where T : IComponent { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) { comps = null; return false; From 92a444311f57dde668cfc583e621e1db87555aea Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Thu, 28 Mar 2024 14:16:13 +1300 Subject: [PATCH 12/56] Add stacktrace to action error logs (#26486) Add trace to action error logs --- Content.Shared/Actions/SharedActionsSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index e909f0a8a3..9f3fb96410 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -124,7 +124,7 @@ public abstract class SharedActionsSystem : EntitySystem return true; if (logError) - Log.Error($"Failed to get action from action entity: {ToPrettyString(uid.Value)}"); + Log.Error($"Failed to get action from action entity: {ToPrettyString(uid.Value)}. Trace: {Environment.StackTrace}"); return false; } @@ -804,7 +804,7 @@ public abstract class SharedActionsSystem : EntitySystem || !comp.Actions.Contains(actionId.Value)); if (!GameTiming.ApplyingState) - Log.Error($"Attempted to remove an action {ToPrettyString(actionId)} from an entity that it was never attached to: {ToPrettyString(performer)}"); + Log.Error($"Attempted to remove an action {ToPrettyString(actionId)} from an entity that it was never attached to: {ToPrettyString(performer)}. Trace: {Environment.StackTrace}"); return; } From bf7c1d647e7633ee2993a7da22318b7cf280d8ba Mon Sep 17 00:00:00 2001 From: Jake Huxell Date: Wed, 27 Mar 2024 21:43:55 -0400 Subject: [PATCH 13/56] Late Join Menu Properly Retains Position On New Player Joins (#26483) * When another player late joins it will correctly update the UI locally * Resolve passengers not displaying the correct message in late join * Improve final boolean comparison of button disabled state to be a bit neater --- Content.Client/LateJoin/LateJoinGui.cs | 82 ++++++++++++++++++++------ 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/Content.Client/LateJoin/LateJoinGui.cs b/Content.Client/LateJoin/LateJoinGui.cs index b99d30015e..d6a028d6c4 100644 --- a/Content.Client/LateJoin/LateJoinGui.cs +++ b/Content.Client/LateJoin/LateJoinGui.cs @@ -33,7 +33,7 @@ namespace Content.Client.LateJoin private readonly SpriteSystem _sprites; private readonly CrewManifestSystem _crewManifest; - private readonly Dictionary> _jobButtons = new(); + private readonly Dictionary>> _jobButtons = new(); private readonly Dictionary> _jobCategories = new(); private readonly List _jobLists = new(); @@ -139,7 +139,7 @@ namespace Content.Client.LateJoin var jobListScroll = new ScrollContainer() { VerticalExpand = true, - Children = {jobList}, + Children = { jobList }, Visible = false, }; @@ -163,11 +163,12 @@ namespace Content.Client.LateJoin var departments = _prototypeManager.EnumeratePrototypes().ToArray(); Array.Sort(departments, DepartmentUIComparer.Instance); + _jobButtons[id] = new Dictionary>(); + foreach (var department in departments) { var departmentName = Loc.GetString($"department-{department.ID}"); _jobCategories[id] = new Dictionary(); - _jobButtons[id] = new Dictionary(); var stationAvailable = _gameTicker.JobsAvailable[id]; var jobsAvailable = new List(); @@ -223,7 +224,13 @@ namespace Content.Client.LateJoin foreach (var prototype in jobsAvailable) { var value = stationAvailable[prototype.ID]; - var jobButton = new JobButton(prototype.ID, value); + + var jobLabel = new Label + { + Margin = new Thickness(5f, 0, 0, 0) + }; + + var jobButton = new JobButton(jobLabel, prototype.ID, prototype.LocalizedName, value); var jobSelector = new BoxContainer { @@ -241,14 +248,6 @@ namespace Content.Client.LateJoin icon.Texture = _sprites.Frame0(jobIcon.Icon); jobSelector.AddChild(icon); - var jobLabel = new Label - { - Margin = new Thickness(5f, 0, 0, 0), - Text = value != null ? - Loc.GetString("late-join-gui-job-slot-capped", ("jobName", prototype.LocalizedName), ("amount", value)) : - Loc.GetString("late-join-gui-job-slot-uncapped", ("jobName", prototype.LocalizedName)), - }; - jobSelector.AddChild(jobLabel); jobButton.AddChild(jobSelector); category.AddChild(jobButton); @@ -280,15 +279,43 @@ namespace Content.Client.LateJoin jobButton.Disabled = true; } - _jobButtons[id][prototype.ID] = jobButton; + if (!_jobButtons[id].ContainsKey(prototype.ID)) + { + _jobButtons[id][prototype.ID] = new List(); + } + + _jobButtons[id][prototype.ID].Add(jobButton); } } } } - private void JobsAvailableUpdated(IReadOnlyDictionary> _) + private void JobsAvailableUpdated(IReadOnlyDictionary> updatedJobs) { - RebuildUI(); + foreach (var stationEntries in updatedJobs) + { + if (_jobButtons.ContainsKey(stationEntries.Key)) + { + var jobsAvailable = stationEntries.Value; + + var existingJobEntries = _jobButtons[stationEntries.Key]; + foreach (var existingJobEntry in existingJobEntries) + { + if (jobsAvailable.ContainsKey(existingJobEntry.Key)) + { + var updatedJobValue = jobsAvailable[existingJobEntry.Key]; + foreach (var matchingJobButton in existingJobEntry.Value) + { + if (matchingJobButton.Amount != updatedJobValue) + { + matchingJobButton.RefreshLabel(updatedJobValue); + matchingJobButton.Disabled = matchingJobButton.Amount == 0; + } + } + } + } + } + } } protected override void Dispose(bool disposing) @@ -307,14 +334,33 @@ namespace Content.Client.LateJoin sealed class JobButton : ContainerButton { + public Label JobLabel { get; } public string JobId { get; } - public uint? Amount { get; } + public string JobLocalisedName { get; } + public uint? Amount { get; private set; } + private bool _initialised = false; - public JobButton(string jobId, uint? amount) + public JobButton(Label jobLabel, string jobId, string jobLocalisedName, uint? amount) { + JobLabel = jobLabel; JobId = jobId; - Amount = amount; + JobLocalisedName = jobLocalisedName; + RefreshLabel(amount); AddStyleClass(StyleClassButton); + _initialised = true; + } + + public void RefreshLabel(uint? amount) + { + if (Amount == amount && _initialised) + { + return; + } + Amount = amount; + + JobLabel.Text = Amount != null ? + Loc.GetString("late-join-gui-job-slot-capped", ("jobName", JobLocalisedName), ("amount", Amount)) : + Loc.GetString("late-join-gui-job-slot-uncapped", ("jobName", JobLocalisedName)); } } } From 29fb3ca4a9f9efdf78ecae94f18317930d2f1570 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 28 Mar 2024 01:45:01 +0000 Subject: [PATCH 14/56] 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 7ff57b0df1..4a430e3d74 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: EmoGarbage404 - changes: - - message: Duplicate players will no longer spawn in cryogenic sleep units. - type: Fix - id: 5739 - time: '2024-01-19T00:27:21.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24251 - author: lapatison changes: - message: Guidebooks can now be found in random bookshelves. @@ -3794,3 +3787,11 @@ id: 6238 time: '2024-03-27T23:26:26.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26482 +- author: Jake Huxell + changes: + - message: Fixed late join menu scrolling to the top whenever a new player joins + the round. + type: Fix + id: 6239 + time: '2024-03-28T01:43:55.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26483 From 77e029d4ca787485152dec5a4aaafd4589f9ad43 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:22:19 +1300 Subject: [PATCH 15/56] Remove atmos method events (#26402) * Remove HasAtmosphereMethodEvent * Remove GetTileMixturesMethodEvent * Remove GetTileMixtureMethodEvent * Remove GetAdjacentTilesMethodEvent * Add TileMixtureEnumerator * Remove GetAdjacentTileMixturesMethodEvent * Remove IsTileSpaceMethodEvent * Remove HotspotExposeMethodEvent * Remove pipe net method events * Remove device method events * Use Entity * Misc fixes * A * Theres probably a few more of these * Fix other resolve errors --- .../Anomaly/AnomalySystem.Generator.cs | 2 +- .../Effects/GasProducerAnomalySystem.cs | 2 +- .../EntitySystems/AtmosphereSystem.API.cs | 240 ++++++++---------- .../AtmosphereSystem.Commands.cs | 2 +- .../AtmosphereSystem.GridAtmosphere.cs | 178 ------------- .../EntitySystems/AtmosphereSystem.Map.cs | 34 --- .../Atmos/EntitySystems/AtmosphereSystem.cs | 2 + .../Piping/Components/AtmosDeviceComponent.cs | 2 +- .../Piping/EntitySystems/AtmosDeviceSystem.cs | 14 +- .../EntitySystems/GasVentScrubberSystem.cs | 3 +- .../Atmos/Portable/PortableScrubberSystem.cs | 3 +- .../Atmos/Reactions/PlasmaFireReaction.cs | 2 +- .../Atmos/Reactions/TritiumFireReaction.cs | 2 +- Content.Server/Atmos/TileMixtureEnumerator.cs | 29 +++ .../Rules/GameRuleSystem.Utility.cs | 3 +- Content.Server/Lathe/LatheSystem.cs | 6 +- .../Systems/TemperatureArtifactSystem.cs | 4 +- 17 files changed, 170 insertions(+), 358 deletions(-) create mode 100644 Content.Server/Atmos/TileMixtureEnumerator.cs diff --git a/Content.Server/Anomaly/AnomalySystem.Generator.cs b/Content.Server/Anomaly/AnomalySystem.Generator.cs index e03c566593..7aa1a8935f 100644 --- a/Content.Server/Anomaly/AnomalySystem.Generator.cs +++ b/Content.Server/Anomaly/AnomalySystem.Generator.cs @@ -103,7 +103,7 @@ public sealed partial class AnomalySystem var tile = new Vector2i(randomX, randomY); // no air-blocked areas. - if (_atmosphere.IsTileSpace(grid, xform.MapUid, tile, mapGridComp: gridComp) || + if (_atmosphere.IsTileSpace(grid, xform.MapUid, tile) || _atmosphere.IsTileAirBlocked(grid, tile, mapGridComp: gridComp)) { continue; diff --git a/Content.Server/Anomaly/Effects/GasProducerAnomalySystem.cs b/Content.Server/Anomaly/Effects/GasProducerAnomalySystem.cs index 2408ad0b3d..7fee8fdb98 100644 --- a/Content.Server/Anomaly/Effects/GasProducerAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/GasProducerAnomalySystem.cs @@ -62,7 +62,7 @@ public sealed class GasProducerAnomalySystem : EntitySystem if (tilerefs.Length == 0) return; - var mixture = _atmosphere.GetTileMixture((uid, xform), grid, true); + var mixture = _atmosphere.GetTileMixture((uid, xform), true); if (mixture != null) { mixture.AdjustMoles(gas, mols); diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs index cece99cacf..fb94fe414b 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs @@ -4,7 +4,6 @@ using Content.Server.Atmos.Piping.Components; using Content.Server.Atmos.Reactions; using Content.Server.NodeContainer.NodeGroups; using Content.Shared.Atmos; -using Robust.Server.GameObjects; using Robust.Shared.Map.Components; using Robust.Shared.Utility; @@ -49,13 +48,7 @@ public partial class AtmosphereSystem return GetTileMixture(gridUid, mapUid, position, excite); } - public bool HasAtmosphere(EntityUid gridUid) - { - var ev = new HasAtmosphereMethodEvent(gridUid); - RaiseLocalEvent(gridUid, ref ev); - - return ev.Result; - } + public bool HasAtmosphere(EntityUid gridUid) => _atmosQuery.HasComponent(gridUid); public bool SetSimulatedGrid(EntityUid gridUid, bool simulated) { @@ -91,43 +84,60 @@ public partial class AtmosphereSystem entity.Comp.InvalidatedCoords.Add(tile); } - public GasMixture?[]? GetTileMixtures(EntityUid? gridUid, EntityUid? mapUid, List tiles, bool excite = false) + public GasMixture?[]? GetTileMixtures(Entity? grid, Entity? map, List tiles, bool excite = false) { - var ev = new GetTileMixturesMethodEvent(gridUid, mapUid, tiles, excite); + GasMixture?[]? mixtures = null; + var handled = false; // If we've been passed a grid, try to let it handle it. - if (gridUid.HasValue) + if (grid is {} gridEnt && Resolve(gridEnt, ref gridEnt.Comp)) { - DebugTools.Assert(_mapManager.IsGrid(gridUid.Value)); - RaiseLocalEvent(gridUid.Value, ref ev, false); + handled = true; + mixtures = new GasMixture?[tiles.Count]; + + for (var i = 0; i < tiles.Count; i++) + { + var tile = tiles[i]; + if (!gridEnt.Comp.Tiles.TryGetValue(tile, out var atmosTile)) + { + // need to get map atmosphere + handled = false; + continue; + } + + mixtures[i] = atmosTile.Air; + + if (excite) + gridEnt.Comp.InvalidatedCoords.Add(tile); + } } - if (ev.Handled) - return ev.Mixtures; + if (handled) + return mixtures; // We either don't have a grid, or the event wasn't handled. // Let the map handle it instead, and also broadcast the event. - if (mapUid.HasValue) + if (map is {} mapEnt && _mapAtmosQuery.Resolve(mapEnt, ref mapEnt.Comp)) { - DebugTools.Assert(_mapManager.IsMap(mapUid.Value)); - RaiseLocalEvent(mapUid.Value, ref ev, true); - } - else - RaiseLocalEvent(ref ev); + mixtures ??= new GasMixture?[tiles.Count]; + for (var i = 0; i < tiles.Count; i++) + { + mixtures[i] ??= mapEnt.Comp.Mixture; + } - if (ev.Handled) - return ev.Mixtures; + return mixtures; + } // Default to a space mixture... This is a space game, after all! - ev.Mixtures ??= new GasMixture?[tiles.Count]; + mixtures ??= new GasMixture?[tiles.Count]; for (var i = 0; i < tiles.Count; i++) { - ev.Mixtures[i] ??= GasMixture.SpaceGas; + mixtures[i] ??= GasMixture.SpaceGas; } - return ev.Mixtures; + return mixtures; } - public GasMixture? GetTileMixture (Entity entity, MapGridComponent? grid = null, bool excite = false) + public GasMixture? GetTileMixture (Entity entity, bool excite = false) { if (!Resolve(entity.Owner, ref entity.Comp)) return null; @@ -136,32 +146,24 @@ public partial class AtmosphereSystem return GetTileMixture(entity.Comp.GridUid, entity.Comp.MapUid, indices, excite); } - public GasMixture? GetTileMixture(EntityUid? gridUid, EntityUid? mapUid, Vector2i gridTile, bool excite = false) + public GasMixture? GetTileMixture(Entity? grid, Entity? map, Vector2i gridTile, bool excite = false) { - var ev = new GetTileMixtureMethodEvent(gridUid, mapUid, gridTile, excite); - // If we've been passed a grid, try to let it handle it. - if(gridUid.HasValue) + if (grid is {} gridEnt + && Resolve(gridEnt, ref gridEnt.Comp, false) + && gridEnt.Comp.Tiles.TryGetValue(gridTile, out var tile)) { - DebugTools.Assert(_mapManager.IsGrid(gridUid.Value)); - RaiseLocalEvent(gridUid.Value, ref ev, false); + if (excite) + gridEnt.Comp.InvalidatedCoords.Add(gridTile); + + return tile.Air; } - if (ev.Handled) - return ev.Mixture; - - // We either don't have a grid, or the event wasn't handled. - // Let the map handle it instead, and also broadcast the event. - if(mapUid.HasValue) - { - DebugTools.Assert(_mapManager.IsMap(mapUid.Value)); - RaiseLocalEvent(mapUid.Value, ref ev, true); - } - else - RaiseLocalEvent(ref ev); + if (map is {} mapEnt && _mapAtmosQuery.Resolve(mapEnt, ref mapEnt.Comp, false)) + return mapEnt.Comp.Mixture; // Default to a space mixture... This is a space game, after all! - return ev.Mixture ?? GasMixture.SpaceGas; + return GasMixture.SpaceGas; } public ReactionResult ReactTile(EntityUid gridId, Vector2i tile) @@ -176,66 +178,67 @@ public partial class AtmosphereSystem public bool IsTileAirBlocked(EntityUid gridUid, Vector2i tile, AtmosDirection directions = AtmosDirection.All, MapGridComponent? mapGridComp = null) { - if (!Resolve(gridUid, ref mapGridComp)) + if (!Resolve(gridUid, ref mapGridComp, false)) return false; var data = GetAirtightData(gridUid, mapGridComp, tile); return data.BlockedDirections.IsFlagSet(directions); } - public bool IsTileSpace(EntityUid? gridUid, EntityUid? mapUid, Vector2i tile, MapGridComponent? mapGridComp = null) + public bool IsTileSpace(Entity? grid, Entity? map, Vector2i tile) { - var ev = new IsTileSpaceMethodEvent(gridUid, mapUid, tile, mapGridComp); + if (grid is {} gridEnt && _atmosQuery.Resolve(gridEnt, ref gridEnt.Comp, false) + && gridEnt.Comp.Tiles.TryGetValue(tile, out var tileAtmos)) + { + return tileAtmos.Space; + } - // Try to let the grid (if any) handle it... - if (gridUid.HasValue) - RaiseLocalEvent(gridUid.Value, ref ev, false); - - // If we didn't have a grid or the event wasn't handled - // we let the map know, and also broadcast the event while at it! - if (mapUid.HasValue && !ev.Handled) - RaiseLocalEvent(mapUid.Value, ref ev, true); - - // We didn't have a map, and the event isn't handled, therefore broadcast the event. - else if (!mapUid.HasValue && !ev.Handled) - RaiseLocalEvent(ref ev); + if (map is {} mapEnt && _mapAtmosQuery.Resolve(mapEnt, ref mapEnt.Comp, false)) + return mapEnt.Comp.Space; // If nothing handled the event, it'll default to true. // Oh well, this is a space game after all, deal with it! - return ev.Result; + return true; } - public bool IsTileMixtureProbablySafe(EntityUid? gridUid, EntityUid mapUid, Vector2i tile) + public bool IsTileMixtureProbablySafe(Entity? grid, Entity map, Vector2i tile) { - return IsMixtureProbablySafe(GetTileMixture(gridUid, mapUid, tile)); + return IsMixtureProbablySafe(GetTileMixture(grid, map, tile)); } - public float GetTileHeatCapacity(EntityUid? gridUid, EntityUid mapUid, Vector2i tile) + public float GetTileHeatCapacity(Entity? grid, Entity map, Vector2i tile) { - return GetHeatCapacity(GetTileMixture(gridUid, mapUid, tile) ?? GasMixture.SpaceGas); + return GetHeatCapacity(GetTileMixture(grid, map, tile) ?? GasMixture.SpaceGas); } - public IEnumerable GetAdjacentTiles(EntityUid gridUid, Vector2i tile) + public TileMixtureEnumerator GetAdjacentTileMixtures(Entity grid, Vector2i tile, bool includeBlocked = false, bool excite = false) { - var ev = new GetAdjacentTilesMethodEvent(gridUid, tile); - RaiseLocalEvent(gridUid, ref ev); + if (!_atmosQuery.Resolve(grid, ref grid.Comp, false)) + return TileMixtureEnumerator.Empty; - return ev.Result ?? Enumerable.Empty(); + return !grid.Comp.Tiles.TryGetValue(tile, out var atmosTile) + ? TileMixtureEnumerator.Empty + : new(atmosTile.AdjacentTiles); } - public IEnumerable GetAdjacentTileMixtures(EntityUid gridUid, Vector2i tile, bool includeBlocked = false, bool excite = false) - { - var ev = new GetAdjacentTileMixturesMethodEvent(gridUid, tile, includeBlocked, excite); - RaiseLocalEvent(gridUid, ref ev); - - return ev.Result ?? Enumerable.Empty(); - } - - public void HotspotExpose(EntityUid gridUid, Vector2i tile, float exposedTemperature, float exposedVolume, + public void HotspotExpose(Entity grid, Vector2i tile, float exposedTemperature, float exposedVolume, EntityUid? sparkSourceUid = null, bool soh = false) { - var ev = new HotspotExposeMethodEvent(gridUid, sparkSourceUid, tile, exposedTemperature, exposedVolume, soh); - RaiseLocalEvent(gridUid, ref ev); + if (!_atmosQuery.Resolve(grid, ref grid.Comp, false)) + return; + + if (grid.Comp.Tiles.TryGetValue(tile, out var atmosTile)) + HotspotExpose(grid.Comp, atmosTile, exposedTemperature, exposedVolume, soh, sparkSourceUid); + } + + public void HotspotExpose(TileAtmosphere tile, float exposedTemperature, float exposedVolume, + EntityUid? sparkSourceUid = null, bool soh = false) + { + if (!_atmosQuery.TryGetComponent(tile.GridIndex, out var atmos)) + return; + + DebugTools.Assert(atmos.Tiles.TryGetValue(tile.GridIndices, out var tmp) && tmp == tile); + HotspotExpose(atmos, tile, exposedTemperature, exposedVolume, soh, sparkSourceUid); } public void HotspotExtinguish(EntityUid gridUid, Vector2i tile) @@ -253,39 +256,45 @@ public partial class AtmosphereSystem return ev.Result; } - public void AddPipeNet(EntityUid gridUid, PipeNet pipeNet) + public bool AddPipeNet(Entity grid, PipeNet pipeNet) { - var ev = new AddPipeNetMethodEvent(gridUid, pipeNet); - RaiseLocalEvent(gridUid, ref ev); + return _atmosQuery.Resolve(grid, ref grid.Comp, false) && grid.Comp.PipeNets.Add(pipeNet); } - public void RemovePipeNet(EntityUid gridUid, PipeNet pipeNet) + public bool RemovePipeNet(Entity grid, PipeNet pipeNet) { - var ev = new RemovePipeNetMethodEvent(gridUid, pipeNet); - RaiseLocalEvent(gridUid, ref ev); + return _atmosQuery.Resolve(grid, ref grid.Comp, false) && grid.Comp.PipeNets.Remove(pipeNet); } - public bool AddAtmosDevice(EntityUid gridUid, AtmosDeviceComponent device) + public bool AddAtmosDevice(Entity grid, Entity device) { - // TODO: check device is on grid + DebugTools.Assert(device.Comp.JoinedGrid == null); + DebugTools.Assert(Transform(device).GridUid == grid); - var ev = new AddAtmosDeviceMethodEvent(gridUid, device); - RaiseLocalEvent(gridUid, ref ev); - return ev.Result; + if (!_atmosQuery.Resolve(grid, ref grid.Comp, false)) + return false; + + if (!grid.Comp.AtmosDevices.Add(device)) + return false; + + device.Comp.JoinedGrid = grid; + return true; } - public bool RemoveAtmosDevice(EntityUid gridUid, AtmosDeviceComponent device) + public bool RemoveAtmosDevice(Entity grid, Entity device) { - // TODO: check device is on grid + DebugTools.Assert(device.Comp.JoinedGrid == grid); - var ev = new RemoveAtmosDeviceMethodEvent(gridUid, device); - RaiseLocalEvent(gridUid, ref ev); - return ev.Result; + if (!_atmosQuery.Resolve(grid, ref grid.Comp, false)) + return false; + + if (!grid.Comp.AtmosDevices.Remove(device)) + return false; + + device.Comp.JoinedGrid = null; + return true; } - [ByRefEvent] private record struct HasAtmosphereMethodEvent - (EntityUid Grid, bool Result = false, bool Handled = false); - [ByRefEvent] private record struct SetSimulatedGridMethodEvent (EntityUid Grid, bool Simulated, bool Handled = false); @@ -295,43 +304,12 @@ public partial class AtmosphereSystem [ByRefEvent] private record struct GetAllMixturesMethodEvent (EntityUid Grid, bool Excite = false, IEnumerable? Mixtures = null, bool Handled = false); - [ByRefEvent] private record struct GetTileMixturesMethodEvent - (EntityUid? GridUid, EntityUid? MapUid, List Tiles, bool Excite = false, GasMixture?[]? Mixtures = null, bool Handled = false); - - [ByRefEvent] private record struct GetTileMixtureMethodEvent - (EntityUid? GridUid, EntityUid? MapUid, Vector2i Tile, bool Excite = false, GasMixture? Mixture = null, bool Handled = false); - [ByRefEvent] private record struct ReactTileMethodEvent (EntityUid GridId, Vector2i Tile, ReactionResult Result = default, bool Handled = false); - [ByRefEvent] private record struct IsTileSpaceMethodEvent - (EntityUid? Grid, EntityUid? Map, Vector2i Tile, MapGridComponent? MapGridComponent = null, bool Result = true, bool Handled = false); - - [ByRefEvent] private record struct GetAdjacentTilesMethodEvent - (EntityUid Grid, Vector2i Tile, IEnumerable? Result = null, bool Handled = false); - - [ByRefEvent] private record struct GetAdjacentTileMixturesMethodEvent - (EntityUid Grid, Vector2i Tile, bool IncludeBlocked, bool Excite, - IEnumerable? Result = null, bool Handled = false); - - [ByRefEvent] private record struct HotspotExposeMethodEvent - (EntityUid Grid, EntityUid? SparkSourceUid, Vector2i Tile, float ExposedTemperature, float ExposedVolume, bool soh, bool Handled = false); - [ByRefEvent] private record struct HotspotExtinguishMethodEvent (EntityUid Grid, Vector2i Tile, bool Handled = false); [ByRefEvent] private record struct IsHotspotActiveMethodEvent (EntityUid Grid, Vector2i Tile, bool Result = false, bool Handled = false); - - [ByRefEvent] private record struct AddPipeNetMethodEvent - (EntityUid Grid, PipeNet PipeNet, bool Handled = false); - - [ByRefEvent] private record struct RemovePipeNetMethodEvent - (EntityUid Grid, PipeNet PipeNet, bool Handled = false); - - [ByRefEvent] private record struct AddAtmosDeviceMethodEvent - (EntityUid Grid, AtmosDeviceComponent Device, bool Result = false, bool Handled = false); - - [ByRefEvent] private record struct RemoveAtmosDeviceMethodEvent - (EntityUid Grid, AtmosDeviceComponent Device, bool Result = false, bool Handled = false); } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs index 1c18b8fe29..a5e37398c6 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs @@ -98,7 +98,7 @@ public sealed partial class AtmosphereSystem continue; } - if (tile.Immutable && !IsTileSpace(euid, transform.MapUid, indices, gridComp)) + if (tile.Immutable && !IsTileSpace(euid, transform.MapUid, indices)) { tile = new GasMixture(tile.Volume) { Temperature = tile.Temperature }; tileMain.Air = tile; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs index beddef4be7..bd45030896 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs @@ -20,22 +20,11 @@ public sealed partial class AtmosphereSystem #region Atmos API Subscriptions - SubscribeLocalEvent(GridHasAtmosphere); SubscribeLocalEvent(GridIsSimulated); SubscribeLocalEvent(GridGetAllMixtures); - SubscribeLocalEvent(GridGetTileMixture); - SubscribeLocalEvent(GridGetTileMixtures); SubscribeLocalEvent(GridReactTile); - SubscribeLocalEvent(GridIsTileSpace); - SubscribeLocalEvent(GridGetAdjacentTiles); - SubscribeLocalEvent(GridGetAdjacentTileMixtures); - SubscribeLocalEvent(GridHotspotExpose); SubscribeLocalEvent(GridHotspotExtinguish); SubscribeLocalEvent(GridIsHotspotActive); - SubscribeLocalEvent(GridAddPipeNet); - SubscribeLocalEvent(GridRemovePipeNet); - SubscribeLocalEvent(GridAddAtmosDevice); - SubscribeLocalEvent(GridRemoveAtmosDevice); #endregion } @@ -120,15 +109,6 @@ public sealed partial class AtmosphereSystem } } - private void GridHasAtmosphere(EntityUid uid, GridAtmosphereComponent component, ref HasAtmosphereMethodEvent args) - { - if (args.Handled) - return; - - args.Result = true; - args.Handled = true; - } - private void GridIsSimulated(EntityUid uid, GridAtmosphereComponent component, ref IsSimulatedGridMethodEvent args) { if (args.Handled) @@ -167,48 +147,6 @@ public sealed partial class AtmosphereSystem args.Handled = true; } - private void GridGetTileMixture(EntityUid uid, GridAtmosphereComponent component, - ref GetTileMixtureMethodEvent args) - { - if (args.Handled) - return; - - if (!component.Tiles.TryGetValue(args.Tile, out var tile)) - return; // Do NOT handle the event if we don't have that tile, the map will handle it instead. - - if (args.Excite) - component.InvalidatedCoords.Add(args.Tile); - - args.Mixture = tile.Air; - args.Handled = true; - } - - private void GridGetTileMixtures(EntityUid uid, GridAtmosphereComponent component, - ref GetTileMixturesMethodEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - args.Mixtures = new GasMixture?[args.Tiles.Count]; - - for (var i = 0; i < args.Tiles.Count; i++) - { - var tile = args.Tiles[i]; - if (!component.Tiles.TryGetValue(tile, out var atmosTile)) - { - // need to get map atmosphere - args.Handled = false; - continue; - } - - if (args.Excite) - component.InvalidatedCoords.Add(tile); - - args.Mixtures[i] = atmosTile.Air; - } - } - private void GridReactTile(EntityUid uid, GridAtmosphereComponent component, ref ReactTileMethodEvent args) { if (args.Handled) @@ -221,67 +159,6 @@ public sealed partial class AtmosphereSystem args.Handled = true; } - private void GridIsTileSpace(EntityUid uid, GridAtmosphereComponent component, ref IsTileSpaceMethodEvent args) - { - if (args.Handled) - return; - - // We don't have that tile, so let the map handle it. - if (!component.Tiles.TryGetValue(args.Tile, out var tile)) - return; - - args.Result = tile.Space; - args.Handled = true; - } - - private void GridGetAdjacentTiles(EntityUid uid, GridAtmosphereComponent component, - ref GetAdjacentTilesMethodEvent args) - { - if (args.Handled) - return; - - if (!component.Tiles.TryGetValue(args.Tile, out var tile)) - return; - - IEnumerable EnumerateAdjacent(GridAtmosphereComponent grid, TileAtmosphere t) - { - foreach (var adj in t.AdjacentTiles) - { - if (adj == null) - continue; - - yield return adj.GridIndices; - } - } - - args.Result = EnumerateAdjacent(component, tile); - args.Handled = true; - } - - private void GridGetAdjacentTileMixtures(EntityUid uid, GridAtmosphereComponent component, - ref GetAdjacentTileMixturesMethodEvent args) - { - if (args.Handled) - return; - - if (!component.Tiles.TryGetValue(args.Tile, out var tile)) - return; - - IEnumerable EnumerateAdjacent(GridAtmosphereComponent grid, TileAtmosphere t) - { - foreach (var adj in t.AdjacentTiles) - { - if (adj?.Air == null) - continue; - - yield return adj.Air; - } - } - - args.Result = EnumerateAdjacent(component, tile); - args.Handled = true; - } - /// /// Update array of adjacent tiles and the adjacency flags. Optionally activates all tiles with modified adjacencies. /// @@ -357,18 +234,6 @@ public sealed partial class AtmosphereSystem return (air, map.Space); } - private void GridHotspotExpose(EntityUid uid, GridAtmosphereComponent component, ref HotspotExposeMethodEvent args) - { - if (args.Handled) - return; - - if (!component.Tiles.TryGetValue(args.Tile, out var tile)) - return; - - HotspotExpose(component, tile, args.ExposedTemperature, args.ExposedVolume, args.soh, args.SparkSourceUid); - args.Handled = true; - } - private void GridHotspotExtinguish(EntityUid uid, GridAtmosphereComponent component, ref HotspotExtinguishMethodEvent args) { @@ -445,49 +310,6 @@ public sealed partial class AtmosphereSystem tile.Air.Temperature = totalTemperature / count; } - private void GridAddPipeNet(EntityUid uid, GridAtmosphereComponent component, ref AddPipeNetMethodEvent args) - { - if (args.Handled) - return; - - args.Handled = component.PipeNets.Add(args.PipeNet); - } - - private void GridRemovePipeNet(EntityUid uid, GridAtmosphereComponent component, ref RemovePipeNetMethodEvent args) - { - if (args.Handled) - return; - - args.Handled = component.PipeNets.Remove(args.PipeNet); - } - - private void GridAddAtmosDevice(Entity grid, ref AddAtmosDeviceMethodEvent args) - { - if (args.Handled) - return; - - if (!grid.Comp.AtmosDevices.Add((args.Device.Owner, args.Device))) - return; - - args.Device.JoinedGrid = grid; - args.Handled = true; - args.Result = true; - } - - private void GridRemoveAtmosDevice(EntityUid uid, GridAtmosphereComponent component, - ref RemoveAtmosDeviceMethodEvent args) - { - if (args.Handled) - return; - - if (!component.AtmosDevices.Remove((args.Device.Owner, args.Device))) - return; - - args.Device.JoinedGrid = null; - args.Handled = true; - args.Result = true; - } - /// /// Repopulates all tiles on a grid atmosphere. /// diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Map.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Map.cs index ed105c8d33..25b3b801f7 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Map.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Map.cs @@ -12,9 +12,6 @@ public partial class AtmosphereSystem { SubscribeLocalEvent(OnMapStartup); SubscribeLocalEvent(OnMapRemove); - SubscribeLocalEvent(MapIsTileSpace); - SubscribeLocalEvent(MapGetTileMixture); - SubscribeLocalEvent(MapGetTileMixtures); SubscribeLocalEvent(OnMapGetState); SubscribeLocalEvent(OnGridParentChanged); } @@ -31,37 +28,6 @@ public partial class AtmosphereSystem RefreshAllGridMapAtmospheres(uid); } - private void MapIsTileSpace(EntityUid uid, MapAtmosphereComponent component, ref IsTileSpaceMethodEvent args) - { - if (args.Handled) - return; - - args.Result = component.Space; - args.Handled = true; - } - - private void MapGetTileMixture(EntityUid uid, MapAtmosphereComponent component, ref GetTileMixtureMethodEvent args) - { - if (args.Handled) - return; - - args.Mixture = component.Mixture; - args.Handled = true; - } - - private void MapGetTileMixtures(EntityUid uid, MapAtmosphereComponent component, ref GetTileMixturesMethodEvent args) - { - if (args.Handled) - return; - args.Handled = true; - args.Mixtures ??= new GasMixture?[args.Tiles.Count]; - - for (var i = 0; i < args.Tiles.Count; i++) - { - args.Mixtures[i] ??= component.Mixture; - } - } - private void OnMapGetState(EntityUid uid, MapAtmosphereComponent component, ref ComponentGetState args) { args.State = new MapAtmosphereComponentState(component.Overlay); diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs index d2f40e7716..a553766582 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs @@ -42,6 +42,7 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem private float _exposedTimer = 0f; private EntityQuery _atmosQuery; + private EntityQuery _mapAtmosQuery; private EntityQuery _airtightQuery; private EntityQuery _firelockQuery; private HashSet _entSet = new(); @@ -59,6 +60,7 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem InitializeGridAtmosphere(); InitializeMap(); + _mapAtmosQuery = GetEntityQuery(); _atmosQuery = GetEntityQuery(); _airtightQuery = GetEntityQuery(); _firelockQuery = GetEntityQuery(); diff --git a/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs b/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs index 361e3cbbeb..80461f1beb 100644 --- a/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs +++ b/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs @@ -28,7 +28,7 @@ public sealed partial class AtmosDeviceComponent : Component /// /// If non-null, the grid that this device is part of. /// - [DataField] + [ViewVariables] public EntityUid? JoinedGrid = null; /// diff --git a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs index f4da68ab53..c15d31f7d6 100644 --- a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs +++ b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs @@ -1,7 +1,9 @@ +using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Piping.Components; using JetBrains.Annotations; using Robust.Shared.Timing; +using Robust.Shared.Utility; namespace Content.Server.Atmos.Piping.EntitySystems { @@ -32,6 +34,14 @@ namespace Content.Server.Atmos.Piping.EntitySystems public void JoinAtmosphere(Entity ent) { + if (ent.Comp.JoinedGrid != null) + { + DebugTools.Assert(HasComp(ent.Comp.JoinedGrid)); + DebugTools.Assert(Transform(ent).GridUid == ent.Comp.JoinedGrid); + DebugTools.Assert(ent.Comp.RequireAnchored == Transform(ent).Anchored); + return; + } + var component = ent.Comp; var transform = Transform(ent); @@ -39,7 +49,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems return; // Attempt to add device to a grid atmosphere. - bool onGrid = (transform.GridUid != null) && _atmosphereSystem.AddAtmosDevice(transform.GridUid!.Value, component); + bool onGrid = (transform.GridUid != null) && _atmosphereSystem.AddAtmosDevice(transform.GridUid!.Value, ent); if (!onGrid && component.JoinSystem) { @@ -55,7 +65,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems { var component = ent.Comp; // Try to remove the component from an atmosphere, and if not - if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component.JoinedGrid.Value, component)) + if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component.JoinedGrid.Value, ent)) { // The grid might have been removed but not us... This usually shouldn't happen. component.JoinedGrid = null; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs index 32591e9c54..5afa007e5e 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs @@ -77,7 +77,8 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems return; // Scrub adjacent tiles too. - foreach (var adjacent in _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true)) + var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true); + while (enumerator.MoveNext(out var adjacent)) { Scrub(timeDelta, scrubber, adjacent, outlet); } diff --git a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs index f1be5ac515..e986f8f991 100644 --- a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs +++ b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs @@ -86,7 +86,8 @@ namespace Content.Server.Atmos.Portable if (!running) return; // widenet - foreach (var adjacent in _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true)) + var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true); + while (enumerator.MoveNext(out var adjacent)) { Scrub(timeDelta, component, adjacent); } diff --git a/Content.Server/Atmos/Reactions/PlasmaFireReaction.cs b/Content.Server/Atmos/Reactions/PlasmaFireReaction.cs index 9adda3089c..e7ab7835fd 100644 --- a/Content.Server/Atmos/Reactions/PlasmaFireReaction.cs +++ b/Content.Server/Atmos/Reactions/PlasmaFireReaction.cs @@ -75,7 +75,7 @@ namespace Content.Server.Atmos.Reactions var mixTemperature = mixture.Temperature; if (mixTemperature > Atmospherics.FireMinimumTemperatureToExist) { - atmosphereSystem.HotspotExpose(location.GridIndex, location.GridIndices, mixTemperature, mixture.Volume); + atmosphereSystem.HotspotExpose(location, mixTemperature, mixture.Volume); } } diff --git a/Content.Server/Atmos/Reactions/TritiumFireReaction.cs b/Content.Server/Atmos/Reactions/TritiumFireReaction.cs index 37c9741e86..7103859a90 100644 --- a/Content.Server/Atmos/Reactions/TritiumFireReaction.cs +++ b/Content.Server/Atmos/Reactions/TritiumFireReaction.cs @@ -60,7 +60,7 @@ namespace Content.Server.Atmos.Reactions temperature = mixture.Temperature; if (temperature > Atmospherics.FireMinimumTemperatureToExist) { - atmosphereSystem.HotspotExpose(location.GridIndex, location.GridIndices, temperature, mixture.Volume); + atmosphereSystem.HotspotExpose(location, temperature, mixture.Volume); } } diff --git a/Content.Server/Atmos/TileMixtureEnumerator.cs b/Content.Server/Atmos/TileMixtureEnumerator.cs new file mode 100644 index 0000000000..20440032da --- /dev/null +++ b/Content.Server/Atmos/TileMixtureEnumerator.cs @@ -0,0 +1,29 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Content.Server.Atmos; + +public struct TileMixtureEnumerator +{ + public readonly TileAtmosphere?[] Tiles; + public int Index = 0; + + public static readonly TileMixtureEnumerator Empty = new(Array.Empty()); + + internal TileMixtureEnumerator(TileAtmosphere?[] tiles) + { + Tiles = tiles; + } + + public bool MoveNext([NotNullWhen(true)] out GasMixture? mix) + { + while (Index < Tiles.Length) + { + mix = Tiles[Index++]?.Air; + if (mix != null) + return true; + } + + mix = null; + return false; + } +} diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs index 7563d66dba..a60a2bfe22 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs @@ -119,8 +119,7 @@ public abstract partial class GameRuleSystem where T: IComponent var randomY = RobustRandom.Next((int) aabb.Bottom, (int) aabb.Top); tile = new Vector2i(randomX, randomY); - if (_atmosphere.IsTileSpace(targetGrid, Transform(targetGrid).MapUid, tile, - mapGridComp: gridComp) + if (_atmosphere.IsTileSpace(targetGrid, Transform(targetGrid).MapUid, tile) || _atmosphere.IsTileAirBlocked(targetGrid, tile, mapGridComp: gridComp)) { continue; diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index 621c583a55..06d1b463ec 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -88,7 +88,11 @@ namespace Content.Server.Lathe if (xform.GridUid != null) { - _environments.AddRange(_atmosphere.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true)); + var enumerator = _atmosphere.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true); + while (enumerator.MoveNext(out var mix)) + { + _environments.Add(mix); + } } if (_environments.Count > 0) diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/TemperatureArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/TemperatureArtifactSystem.cs index 163600c6dd..f314d4a4fb 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/TemperatureArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/TemperatureArtifactSystem.cs @@ -28,10 +28,10 @@ public sealed class TemperatureArtifactSystem : EntitySystem if (component.AffectAdjacentTiles && transform.GridUid != null) { - var adjacent = _atmosphereSystem.GetAdjacentTileMixtures(transform.GridUid.Value, + var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(transform.GridUid.Value, _transformSystem.GetGridOrMapTilePosition(uid, transform), excite: true); - foreach (var mixture in adjacent) + while (enumerator.MoveNext(out var mixture)) { UpdateTileTemperature(component, mixture); } From 8a6879bb1b660425faaa30e6bb20746e47603495 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Thu, 28 Mar 2024 17:05:04 +1300 Subject: [PATCH 16/56] Misc ItemToggleSystem changes (#26489) * Minor ItemToggleSystem tweaks * Update visuals on startup * Remove SetIgnited * Misc toggle fixes * Update ItemToggleHotComponent.cs --- .../ItemToggleDisarmMalusComponent.cs | 4 +- .../Components/ItemToggleSharpComponent.cs | 7 +- .../Item/ItemToggle/ItemToggleSystem.cs | 61 ++++++---------- .../Stunnable/Systems/StunbatonSystem.cs | 5 +- Content.Server/Tools/ToolSystem.Welder.cs | 62 ++++++++-------- .../Components/ItemToggleComponent.cs | 31 ++++---- .../Components/ItemToggleHotComponent.cs | 7 +- .../Item/ItemToggle/SharedItemToggleSystem.cs | 71 +++++++++++-------- 8 files changed, 114 insertions(+), 134 deletions(-) diff --git a/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs b/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs index 923a10b22a..30fa84ed90 100644 --- a/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs +++ b/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs @@ -1,19 +1,21 @@ namespace Content.Server.Item; /// -/// Handles whether this item applies a disarm malus when active. +/// Handles whether this item applies a disarm malus when active. /// [RegisterComponent] public sealed partial class ItemToggleDisarmMalusComponent : Component { /// /// Item has this modifier to the chance to disarm when activated. + /// If null, the value will be inferred from the current malus just before the malus is first deactivated. /// [ViewVariables(VVAccess.ReadOnly), DataField] public float? ActivatedDisarmMalus = null; /// /// Item has this modifier to the chance to disarm when deactivated. If none is mentioned, it uses the item's default disarm modifier. + /// If null, the value will be inferred from the current malus just before the malus is first activated. /// [ViewVariables(VVAccess.ReadOnly), DataField] public float? DeactivatedDisarmMalus = null; diff --git a/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs b/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs index ea2efae147..227491b16c 100644 --- a/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs +++ b/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs @@ -1,14 +1,9 @@ namespace Content.Server.Item; /// -/// Handles whether this item is sharp when toggled on. +/// Handles whether this item is sharp when toggled on. /// [RegisterComponent] public sealed partial class ItemToggleSharpComponent : Component { - /// - /// Item can be used to butcher when activated. - /// - [ViewVariables(VVAccess.ReadOnly), DataField] - public bool ActivatedSharp = true; } diff --git a/Content.Server/Item/ItemToggle/ItemToggleSystem.cs b/Content.Server/Item/ItemToggle/ItemToggleSystem.cs index 4507ace9d2..f98415eb08 100644 --- a/Content.Server/Item/ItemToggle/ItemToggleSystem.cs +++ b/Content.Server/Item/ItemToggle/ItemToggleSystem.cs @@ -1,9 +1,7 @@ -using Content.Shared.Item; using Content.Server.CombatMode.Disarm; using Content.Server.Kitchen.Components; using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle.Components; -using ItemToggleComponent = Content.Shared.Item.ItemToggle.Components.ItemToggleComponent; namespace Content.Server.Item; @@ -13,47 +11,34 @@ public sealed class ItemToggleSystem : SharedItemToggleSystem { base.Initialize(); - SubscribeLocalEvent(Toggle); + SubscribeLocalEvent(ToggleSharp); + SubscribeLocalEvent(ToggleMalus); } - private void Toggle(EntityUid uid, ItemToggleComponent comp, ref ItemToggledEvent args) + private void ToggleSharp(Entity ent, ref ItemToggledEvent args) { - if (args.Activated == true) - { - if (TryComp(uid, out var itemSharpness)) - { - if (itemSharpness.ActivatedSharp) - EnsureComp(uid); - } - - if (!TryComp(uid, out var itemToggleDisarmMalus) || - !TryComp(uid, out var malus)) - return; - - //Default the deactivated DisarmMalus to the item's value before activation happens. - itemToggleDisarmMalus.DeactivatedDisarmMalus ??= malus.Malus; - - if (itemToggleDisarmMalus.ActivatedDisarmMalus != null) - { - malus.Malus = (float) itemToggleDisarmMalus.ActivatedDisarmMalus; - } - } + // TODO generalize this into a "ToggleComponentComponent", though probably with a better name + if (args.Activated) + EnsureComp(ent); else + RemCompDeferred(ent); + } + + private void ToggleMalus(Entity ent, ref ItemToggledEvent args) + { + if (!TryComp(ent, out var malus)) + return; + + if (args.Activated) { - if (TryComp(uid, out var itemSharpness)) - { - if (itemSharpness.ActivatedSharp) - RemCompDeferred(uid); - } - - if (!TryComp(uid, out var itemToggleDisarmMalus) || - !TryComp(uid, out var malus)) - return; - - if (itemToggleDisarmMalus.DeactivatedDisarmMalus != null) - { - malus.Malus = (float) itemToggleDisarmMalus.DeactivatedDisarmMalus; - } + ent.Comp.DeactivatedDisarmMalus ??= malus.Malus; + if (ent.Comp.ActivatedDisarmMalus is {} activatedMalus) + malus.Malus = activatedMalus; + return; } + + ent.Comp.ActivatedDisarmMalus ??= malus.Malus; + if (ent.Comp.DeactivatedDisarmMalus is {} deactivatedMalus) + malus.Malus = deactivatedMalus; } } diff --git a/Content.Server/Stunnable/Systems/StunbatonSystem.cs b/Content.Server/Stunnable/Systems/StunbatonSystem.cs index 5f019490b5..c1782efaba 100644 --- a/Content.Server/Stunnable/Systems/StunbatonSystem.cs +++ b/Content.Server/Stunnable/Systems/StunbatonSystem.cs @@ -58,10 +58,7 @@ namespace Content.Server.Stunnable.Systems private void ToggleDone(Entity entity, ref ItemToggledEvent args) { - if (!TryComp(entity, out var item)) - return; - - _item.SetHeldPrefix(entity.Owner, args.Activated ? "on" : "off", component: item); + _item.SetHeldPrefix(entity.Owner, args.Activated ? "on" : "off"); } private void TryTurnOn(Entity entity, ref ItemToggleActivateAttemptEvent args) diff --git a/Content.Server/Tools/ToolSystem.Welder.cs b/Content.Server/Tools/ToolSystem.Welder.cs index 1eabd6c6d2..727526b398 100644 --- a/Content.Server/Tools/ToolSystem.Welder.cs +++ b/Content.Server/Tools/ToolSystem.Welder.cs @@ -7,7 +7,6 @@ using Content.Shared.DoAfter; using Content.Shared.Examine; using Content.Shared.FixedPoint; using Content.Shared.Interaction; -using Content.Shared.Item; using Content.Shared.Item.ItemToggle; using Content.Shared.Tools.Components; using Robust.Shared.GameStates; @@ -32,8 +31,8 @@ namespace Content.Server.Tools SubscribeLocalEvent>(OnWelderToolUseAttempt); SubscribeLocalEvent(OnWelderShutdown); SubscribeLocalEvent(OnWelderGetState); - SubscribeLocalEvent(TryTurnOn); - SubscribeLocalEvent(TurnOff); + SubscribeLocalEvent(OnToggle); + SubscribeLocalEvent(OnActivateAttempt); } public (FixedPoint2 fuel, FixedPoint2 capacity) GetWelderFuelAndCapacity(EntityUid uid, WelderComponent? welder = null, SolutionContainerManagerComponent? solutionContainer = null) @@ -45,55 +44,54 @@ namespace Content.Server.Tools return (fuelSolution.GetTotalPrototypeQuantity(welder.FuelReagent), fuelSolution.MaxVolume); } - public void TryTurnOn(Entity entity, ref ItemToggleActivateAttemptEvent args) + private void OnToggle(Entity entity, ref ItemToggledEvent args) { - if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution, out var solution) || - !TryComp(entity, out var transform)) + if (args.Activated) + TurnOn(entity, args.User); + else + TurnOff(entity, args.User); + } + + private void OnActivateAttempt(Entity entity, ref ItemToggleActivateAttemptEvent args) + { + if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution, out var solution)) { args.Cancelled = true; + args.Popup = Loc.GetString("welder-component-no-fuel-message"); return; } var fuel = solution.GetTotalPrototypeQuantity(entity.Comp.FuelReagent); - - // Not enough fuel to lit welder. if (fuel == FixedPoint2.Zero || fuel < entity.Comp.FuelLitCost) { - if (args.User != null) - { - _popup.PopupEntity(Loc.GetString("welder-component-no-fuel-message"), entity, (EntityUid) args.User); - } + args.Popup = Loc.GetString("welder-component-no-fuel-message"); args.Cancelled = true; - return; } + } + + public void TurnOn(Entity entity, EntityUid? user) + { + if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution)) + return; _solutionContainer.RemoveReagent(entity.Comp.FuelSolution.Value, entity.Comp.FuelReagent, entity.Comp.FuelLitCost); + AdminLogger.Add(LogType.InteractActivate, LogImpact.Low, + $"{ToPrettyString(user):user} toggled {ToPrettyString(entity.Owner):welder} on"); - // Logging - AdminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(entity.Owner):welder} on"); - - _ignitionSource.SetIgnited(entity.Owner); - - if (transform.GridUid is { } gridUid) + var xform = Transform(entity); + if (xform.GridUid is { } gridUid) { - var position = _transformSystem.GetGridOrMapTilePosition(entity.Owner, transform); + var position = _transformSystem.GetGridOrMapTilePosition(entity.Owner, xform); _atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, entity.Owner, true); } - Dirty(entity); - _activeWelders.Add(entity); } - public void TurnOff(Entity entity, ref ItemToggleDeactivateAttemptEvent args) + public void TurnOff(Entity entity, EntityUid? user) { - // Logging - AdminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(entity.Owner):welder} off"); - - _ignitionSource.SetIgnited(entity.Owner, false); - - Dirty(entity); - + AdminLogger.Add(LogType.InteractActivate, LogImpact.Low, + $"{ToPrettyString(user):user} toggled {ToPrettyString(entity.Owner):welder} off"); _activeWelders.Remove(entity); } @@ -186,7 +184,9 @@ namespace Content.Server.Tools if (_welderTimer < WelderUpdateTimer) return; - // TODO Use an "active welder" component instead, EntityQuery over that. + // TODO Serialization. _activeWelders is not serialized. + // Need to use some "active" component, and EntityQuery over that. + // Probably best to generalize it to a "ToggleableFuelDrain" component. foreach (var tool in _activeWelders.ToArray()) { if (!TryComp(tool, out WelderComponent? welder) diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs index 3ea80ec735..620ddfd194 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs @@ -16,7 +16,7 @@ public sealed partial class ItemToggleComponent : Component /// /// The item's toggle state. /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + [DataField, AutoNetworkedField] public bool Activated = false; /// @@ -45,6 +45,12 @@ public sealed partial class ItemToggleComponent : Component /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public SoundSpecifier? SoundFailToActivate; + + /// + /// Whether or not to toggle the entity's lights on or off. + /// + [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + public bool ToggleLight = true; } /// @@ -55,6 +61,11 @@ public record struct ItemToggleActivateAttemptEvent(EntityUid? User) { public bool Cancelled = false; public readonly EntityUid? User = User; + + /// + /// Pop-up that gets shown to users explaining why the attempt was cancelled. + /// + public string? Popup { get; set; } } /// @@ -77,21 +88,3 @@ public readonly record struct ItemToggledEvent(bool Predicted, bool Activated, E public readonly bool Activated = Activated; public readonly EntityUid? User = User; } - -/// -/// Raised directed on an entity when an itemtoggle is activated. -/// -[ByRefEvent] -public readonly record struct ItemToggleActivatedEvent(EntityUid? User) -{ - public readonly EntityUid? User = User; -} - -/// -/// Raised directed on an entity when an itemtoggle is deactivated. -/// -[ByRefEvent] -public readonly record struct ItemToggleDeactivatedEvent(EntityUid? User) -{ - public readonly EntityUid? User = User; -} diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs index c3eb1d1e58..4bcd6e25c0 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleHotComponent.cs @@ -5,12 +5,7 @@ namespace Content.Shared.Item.ItemToggle.Components; /// /// Handles whether the item is hot when toggled on. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[RegisterComponent, NetworkedComponent] public sealed partial class ItemToggleHotComponent : Component { - /// - /// Item becomes hot when active. - /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] - public bool IsHotWhenActivated = true; } diff --git a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs index e2e5fc2e3c..523f67bac3 100644 --- a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Interaction.Events; using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Popups; using Content.Shared.Temperature; using Content.Shared.Toggleable; using Content.Shared.Wieldable; @@ -20,11 +21,13 @@ public abstract class SharedItemToggleSystem : EntitySystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedPointLightSystem _light = default!; [Dependency] private readonly INetManager _netManager = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(TurnOffonUnwielded); SubscribeLocalEvent(TurnOnonWielded); SubscribeLocalEvent(OnUseInHand); @@ -34,6 +37,11 @@ public abstract class SharedItemToggleSystem : EntitySystem SubscribeLocalEvent(UpdateActiveSound); } + private void OnStartup(Entity ent, ref ComponentStartup args) + { + UpdateVisuals(ent); + } + private void OnUseInHand(EntityUid uid, ItemToggleComponent itemToggle, UseInHandEvent args) { if (args.Handled) @@ -73,6 +81,9 @@ public abstract class SharedItemToggleSystem : EntitySystem if (itemToggle.Activated) return true; + if (!itemToggle.Predictable && _netManager.IsClient) + return true; + var attempt = new ItemToggleActivateAttemptEvent(user); RaiseLocalEvent(uid, ref attempt); @@ -83,12 +94,16 @@ public abstract class SharedItemToggleSystem : EntitySystem else _audio.PlayPvs(itemToggle.SoundFailToActivate, uid); + if (attempt.Popup != null && user != null) + { + if (predicted) + _popup.PopupClient(attempt.Popup, uid, user.Value); + else + _popup.PopupEntity(attempt.Popup, uid, user.Value); + } + return false; } - // If the item's toggle is unpredictable because of something like requiring fuel or charge, then clients exit here. - // Otherwise you get stuff like an item activating client-side and then turning back off when it synchronizes with the server. - if (predicted == false && _netManager.IsClient) - return true; Activate(uid, itemToggle, predicted, user); @@ -103,6 +118,9 @@ public abstract class SharedItemToggleSystem : EntitySystem if (!Resolve(uid, ref itemToggle)) return false; + if (!itemToggle.Predictable && _netManager.IsClient) + return true; + if (!itemToggle.Activated) return true; @@ -114,10 +132,6 @@ public abstract class SharedItemToggleSystem : EntitySystem return false; } - // If the item's toggle is unpredictable because of something like requiring fuel or charge, then clients exit here. - if (predicted == false && _netManager.IsClient) - return true; - Deactivate(uid, itemToggle, predicted, user); return true; } @@ -135,7 +149,6 @@ public abstract class SharedItemToggleSystem : EntitySystem } var soundToPlay = itemToggle.SoundActivate; - if (predicted) _audio.PlayPredicted(soundToPlay, uid, user); else @@ -146,10 +159,8 @@ public abstract class SharedItemToggleSystem : EntitySystem var toggleUsed = new ItemToggledEvent(predicted, Activated: true, user); RaiseLocalEvent(uid, ref toggleUsed); - var activev = new ItemToggleActivatedEvent(user); - RaiseLocalEvent(uid, ref activev); - itemToggle.Activated = true; + UpdateVisuals((uid, itemToggle)); Dirty(uid, itemToggle); } @@ -158,35 +169,38 @@ public abstract class SharedItemToggleSystem : EntitySystem /// private void Deactivate(EntityUid uid, ItemToggleComponent itemToggle, bool predicted, EntityUid? user = null) { - // TODO: Fix this hardcoding - TryComp(uid, out AppearanceComponent? appearance); - _appearance.SetData(uid, ToggleableLightVisuals.Enabled, false, appearance); - _appearance.SetData(uid, ToggleVisuals.Toggled, false, appearance); - - if (_light.TryGetLight(uid, out var light)) - { - _light.SetEnabled(uid, false, light); - } - var soundToPlay = itemToggle.SoundDeactivate; - if (predicted) _audio.PlayPredicted(soundToPlay, uid, user); else _audio.PlayPvs(soundToPlay, uid); - // END FIX HARDCODING var toggleUsed = new ItemToggledEvent(predicted, Activated: false, user); RaiseLocalEvent(uid, ref toggleUsed); - var activev = new ItemToggleDeactivatedEvent(user); - RaiseLocalEvent(uid, ref activev); - itemToggle.Activated = false; + UpdateVisuals((uid, itemToggle)); Dirty(uid, itemToggle); } + private void UpdateVisuals(Entity ent) + { + if (TryComp(ent, out AppearanceComponent? appearance)) + { + _appearance.SetData(ent, ToggleVisuals.Toggled, ent.Comp.Activated, appearance); + + if (ent.Comp.ToggleLight) + _appearance.SetData(ent, ToggleableLightVisuals.Enabled, ent.Comp.Activated, appearance); + } + + if (!ent.Comp.ToggleLight) + return; + + if (_light.TryGetLight(ent, out var light)) + _light.SetEnabled(ent, ent.Comp.Activated, light); + } + /// /// Used for items that require to be wielded in both hands to activate. For instance the dual energy sword will turn off if not wielded. /// @@ -218,8 +232,7 @@ public abstract class SharedItemToggleSystem : EntitySystem /// private void OnIsHotEvent(EntityUid uid, ItemToggleHotComponent itemToggleHot, IsHotEvent args) { - if (itemToggleHot.IsHotWhenActivated) - args.IsHot = IsActivated(uid); + args.IsHot |= IsActivated(uid); } /// From 4d9a79d96b421e42d2f4cc673c70d92a70f6c625 Mon Sep 17 00:00:00 2001 From: blueDev2 <89804215+blueDev2@users.noreply.github.com> Date: Thu, 28 Mar 2024 00:06:00 -0400 Subject: [PATCH 17/56] Allow cargo bounties to be sold off-station (#26469) * Ported over code for delta-v to fix bounties * Added requested changes * Removed the station arg from "IsBountyComplete". It is unneeded and all calls just use a null value for it anyways --- .../Components/CargoBountyLabelComponent.cs | 10 ++++++- .../Cargo/Systems/CargoSystem.Bounty.cs | 26 ++++++++++++------- .../Cargo/Systems/CargoSystem.Shuttle.cs | 17 +++++------- .../Cargo/Systems/PriceGunSystem.cs | 2 +- 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/Content.Server/Cargo/Components/CargoBountyLabelComponent.cs b/Content.Server/Cargo/Components/CargoBountyLabelComponent.cs index 8eea00e099..2b9898be02 100644 --- a/Content.Server/Cargo/Components/CargoBountyLabelComponent.cs +++ b/Content.Server/Cargo/Components/CargoBountyLabelComponent.cs @@ -1,4 +1,6 @@ -namespace Content.Server.Cargo.Components; +using Content.Server.Station.Systems; + +namespace Content.Server.Cargo.Components; /// /// This is used for marking containers as @@ -17,4 +19,10 @@ public sealed partial class CargoBountyLabelComponent : Component /// Used to prevent recursion in calculating the price. /// public bool Calculating; + + /// + /// The Station System to check and remove bounties from + /// + [DataField] + public EntityUid? AssociatedStationId; } diff --git a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs index ce15542ec5..ee5ae631fd 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs @@ -4,6 +4,7 @@ using Content.Server.Cargo.Components; using Content.Server.Labels; using Content.Server.NameIdentifier; using Content.Server.Paper; +using Content.Server.Station.Systems; using Content.Shared.Cargo; using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Prototypes; @@ -65,16 +66,17 @@ public sealed partial class CargoSystem var label = Spawn(component.BountyLabelId, Transform(uid).Coordinates); component.NextPrintTime = _timing.CurTime + component.PrintDelay; - SetupBountyLabel(label, bounty.Value); + SetupBountyLabel(label, station, bounty.Value); _audio.PlayPvs(component.PrintSound, uid); } - public void SetupBountyLabel(EntityUid uid, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null) + public void SetupBountyLabel(EntityUid uid, EntityUid stationId, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null) { if (!Resolve(uid, ref paper, ref label) || !_protoMan.TryIndex(bounty.Bounty, out var prototype)) return; label.Id = bounty.Id; + label.AssociatedStationId = stationId; var msg = new FormattedMessage(); msg.AddText(Loc.GetString("bounty-manifest-header", ("id", bounty.Id))); msg.PushNewline(); @@ -103,7 +105,7 @@ public sealed partial class CargoSystem if (!_container.TryGetContainingContainer(uid, out var container) || container.ID != LabelSystem.ContainerName) return; - if (_station.GetOwningStation(uid) is not { } station || !TryComp(station, out var database)) + if (component.AssociatedStationId is not { } station || !TryComp(station, out var database)) return; if (database.CheckedBounties.Contains(component.Id)) @@ -131,14 +133,18 @@ public sealed partial class CargoSystem if (!TryGetBountyLabel(sold, out _, out var component)) continue; - if (!TryGetBountyFromId(args.Station, component.Id, out var bounty)) + if (component.AssociatedStationId is not { } station || !TryGetBountyFromId(station, component.Id, out var bounty)) + { continue; + } if (!IsBountyComplete(sold, bounty.Value)) + { continue; + } - TryRemoveBounty(args.Station, bounty.Value); - FillBountyDatabase(args.Station); + TryRemoveBounty(station, bounty.Value); + FillBountyDatabase(station); _adminLogger.Add(LogType.Action, LogImpact.Low, $"Bounty \"{bounty.Value.Bounty}\" (id:{bounty.Value.Id}) was fulfilled"); } } @@ -196,7 +202,7 @@ public sealed partial class CargoSystem FillBountyDatabase(entity); } - public bool IsBountyComplete(EntityUid container, EntityUid? station, out HashSet bountyEntities) + public bool IsBountyComplete(EntityUid container, out HashSet bountyEntities) { if (!TryGetBountyLabel(container, out _, out var component)) { @@ -204,7 +210,7 @@ public sealed partial class CargoSystem return false; } - station ??= _station.GetOwningStation(container); + var station = component.AssociatedStationId; if (station == null) { bountyEntities = new(); @@ -225,7 +231,7 @@ public sealed partial class CargoSystem return IsBountyComplete(container, data, out _); } - public bool IsBountyComplete(EntityUid container, CargoBountyData data, out HashSet bountyEntities) + public bool IsBountyComplete(EntityUid container, CargoBountyData data, out HashSet bountyEntities) { if (!_protoMan.TryIndex(data.Bounty, out var proto)) { @@ -314,7 +320,7 @@ public sealed partial class CargoSystem var children = GetBountyEntities(ent); foreach (var child in children) { - entities.Add(child); + entities.Add(child); } } } diff --git a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs index ab66e6ff3f..3bcd6d8d20 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs @@ -230,9 +230,8 @@ public sealed partial class CargoSystem #region Station - private bool SellPallets(EntityUid gridUid, EntityUid? station, out double amount) + private bool SellPallets(EntityUid gridUid, out double amount) { - station ??= _station.GetOwningStation(gridUid); GetPalletGoods(gridUid, out var toSell, out amount); Log.Debug($"Cargo sold {toSell.Count} entities for {amount}"); @@ -240,11 +239,9 @@ public sealed partial class CargoSystem if (toSell.Count == 0) return false; - if (station != null) - { - var ev = new EntitySoldEvent(station.Value, toSell); - RaiseLocalEvent(ref ev); - } + + var ev = new EntitySoldEvent(toSell); + RaiseLocalEvent(ref ev); foreach (var ent in toSell) { @@ -299,7 +296,7 @@ public sealed partial class CargoSystem return false; } - var complete = IsBountyComplete(uid, (EntityUid?) null, out var bountyEntities); + var complete = IsBountyComplete(uid, out var bountyEntities); // Recursively check for mobs at any point. var children = xform.ChildEnumerator; @@ -332,7 +329,7 @@ public sealed partial class CargoSystem return; } - if (!SellPallets(gridUid, null, out var price)) + if (!SellPallets(gridUid, out var price)) return; var stackPrototype = _protoMan.Index(component.CashType); @@ -354,4 +351,4 @@ public sealed partial class CargoSystem /// deleted but after the price has been calculated. /// [ByRefEvent] -public readonly record struct EntitySoldEvent(EntityUid Station, HashSet Sold); +public readonly record struct EntitySoldEvent(HashSet Sold); diff --git a/Content.Server/Cargo/Systems/PriceGunSystem.cs b/Content.Server/Cargo/Systems/PriceGunSystem.cs index 7bd49b857a..19fe07bd25 100644 --- a/Content.Server/Cargo/Systems/PriceGunSystem.cs +++ b/Content.Server/Cargo/Systems/PriceGunSystem.cs @@ -57,7 +57,7 @@ public sealed class PriceGunSystem : EntitySystem return; // Check if we're scanning a bounty crate - if (_bountySystem.IsBountyComplete(args.Target.Value, (EntityUid?) null, out _)) + if (_bountySystem.IsBountyComplete(args.Target.Value, out _)) { _popupSystem.PopupEntity(Loc.GetString("price-gun-bounty-complete"), args.User, args.User); } From 7d901759837f0e0c0669c1794504a9334472799e Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 28 Mar 2024 04:07:06 +0000 Subject: [PATCH 18/56] 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 4a430e3d74..53e8098f72 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: lapatison - changes: - - message: Guidebooks can now be found in random bookshelves. - type: Tweak - id: 5740 - time: '2024-01-19T00:32:00.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24036 - author: metalgearsloth changes: - message: Added trade stations for cargo instead of FTL. @@ -3795,3 +3788,10 @@ id: 6239 time: '2024-03-28T01:43:55.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26483 +- author: blueDev2 + changes: + - message: Generalized bounties to allow selling them off-station + type: Tweak + id: 6240 + time: '2024-03-28T04:06:00.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26469 From 578f7e4f2c1cde38e1c66fa2b5fbbb2903576dec Mon Sep 17 00:00:00 2001 From: superjj18 Date: Thu, 28 Mar 2024 01:41:20 -0400 Subject: [PATCH 19/56] Remove broadcast cooldown (#26492) * Removed inconsistent broadcast cooldown whenever the "Announce" button is pressed on the communications terminal. * Revert "Removed inconsistent broadcast cooldown whenever the "Announce" button is pressed on the communications terminal." This reverts commit c730d6499b6908f6ae7c52e21d5338fa3b7eb80e. * Reapply "Removed inconsistent broadcast cooldown whenever the "Announce" button is pressed on the communications terminal." This reverts commit 3c2d66af865a11ca55eb0e98db58a955c0d70c00. * -Removed cooldown entirely --- .../Communications/CommunicationsConsoleSystem.cs | 6 ------ .../Communications/SharedCommunicationsConsoleComponent.cs | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Content.Server/Communications/CommunicationsConsoleSystem.cs b/Content.Server/Communications/CommunicationsConsoleSystem.cs index 6b745c8cd9..6475e1a6d7 100644 --- a/Content.Server/Communications/CommunicationsConsoleSystem.cs +++ b/Content.Server/Communications/CommunicationsConsoleSystem.cs @@ -170,7 +170,6 @@ namespace Content.Server.Communications _uiSystem.SetUiState(ui, new CommunicationsConsoleInterfaceState( CanAnnounce(comp), - CanBroadcast(comp), CanCallOrRecall(comp), levels, currentLevel, @@ -184,11 +183,6 @@ namespace Content.Server.Communications return comp.AnnouncementCooldownRemaining <= 0f; } - private static bool CanBroadcast(CommunicationsConsoleComponent comp) - { - return comp.AnnouncementCooldownRemaining <= 0f; - } - private bool CanUse(EntityUid user, EntityUid console) { // This shouldn't technically be possible because of BUI but don't trust client. diff --git a/Content.Shared/Communications/SharedCommunicationsConsoleComponent.cs b/Content.Shared/Communications/SharedCommunicationsConsoleComponent.cs index bf0f5195b7..d48b99837f 100644 --- a/Content.Shared/Communications/SharedCommunicationsConsoleComponent.cs +++ b/Content.Shared/Communications/SharedCommunicationsConsoleComponent.cs @@ -19,10 +19,9 @@ namespace Content.Shared.Communications public string CurrentAlert; public float CurrentAlertDelay; - public CommunicationsConsoleInterfaceState(bool canAnnounce, bool canBroadcast, bool canCall, List? alertLevels, string currentAlert, float currentAlertDelay, TimeSpan? expectedCountdownEnd = null) + public CommunicationsConsoleInterfaceState(bool canAnnounce, bool canCall, List? alertLevels, string currentAlert, float currentAlertDelay, TimeSpan? expectedCountdownEnd = null) { CanAnnounce = canAnnounce; - CanBroadcast = canBroadcast; CanCall = canCall; ExpectedCountdownEnd = expectedCountdownEnd; CountdownStarted = expectedCountdownEnd != null; From 887cd42deb5cc5ef5298786a2c27cddae9eb00fd Mon Sep 17 00:00:00 2001 From: Simon <63975668+Simyon264@users.noreply.github.com> Date: Thu, 28 Mar 2024 06:41:56 +0100 Subject: [PATCH 20/56] Add DoorBumpOpener to space dragon (#26490) Add DoorBumpOpener to space dragon.yml --- Resources/Prototypes/Entities/Mobs/Player/dragon.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index aa10a631b5..025b8d917c 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml @@ -125,6 +125,7 @@ - type: Tag tags: - CannotSuicide + - DoorBumpOpener - type: entity parent: BaseMobDragon From b064985f24611f51ee9dbf9eca504352c52996b2 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 28 Mar 2024 05:43:02 +0000 Subject: [PATCH 21/56] 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 53e8098f72..e398d01c79 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Added trade stations for cargo instead of FTL. - type: Add - id: 5741 - time: '2024-01-19T02:02:28.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/23863 - author: Emisse changes: - message: Title2 to lobby music @@ -3795,3 +3788,10 @@ id: 6240 time: '2024-03-28T04:06:00.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26469 +- author: Simyon + changes: + - message: The space dragon can now open doors by bumping into them! + type: Tweak + id: 6241 + time: '2024-03-28T05:41:57.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26490 From 766192f4b509ff6bfc02ee1c665f1ba5b1bdc831 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:53:18 -0400 Subject: [PATCH 22/56] Use nav beacon locations for announcements (#26437) * use nav beacon locations for announcements * :thumbs_up: --- Content.Server/Dragon/DragonRiftSystem.cs | 6 +- .../Explosion/EntitySystems/TriggerSystem.cs | 10 +- Content.Server/Nuke/NukeSystem.cs | 4 +- Content.Server/Pinpointer/NavMapSystem.cs | 117 ++++++++++++++++ .../Systems/EmergencyShuttleSystem.cs | 10 +- .../ContentLocalizationManager.cs | 8 ++ .../Pinpointer/NavMapBeaconComponent.cs | 7 + .../Pinpointer/SharedNavMapSystem.cs | 14 -- Resources/Locale/en-US/_directions.ftl | 8 ++ Resources/Locale/en-US/dragon/rifts.ftl | 2 +- Resources/Locale/en-US/implant/implant.ftl | 4 +- .../en-US/navmap-beacons/station_map.ftl | 5 + .../Locale/en-US/nuke/nuke-component.ftl | 2 +- Resources/Locale/en-US/shuttles/emergency.ftl | 2 +- .../Objects/Devices/station_beacon.yml | 130 +++++++++--------- 15 files changed, 231 insertions(+), 98 deletions(-) create mode 100644 Resources/Locale/en-US/_directions.ftl diff --git a/Content.Server/Dragon/DragonRiftSystem.cs b/Content.Server/Dragon/DragonRiftSystem.cs index 7143280900..101f030bfc 100644 --- a/Content.Server/Dragon/DragonRiftSystem.cs +++ b/Content.Server/Dragon/DragonRiftSystem.cs @@ -13,6 +13,7 @@ using Robust.Shared.Serialization.Manager; using System.Numerics; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.Utility; namespace Content.Server.Dragon; @@ -69,8 +70,9 @@ public sealed class DragonRiftSystem : EntitySystem comp.State = DragonRiftState.AlmostFinished; Dirty(uid, comp); - var location = xform.LocalPosition; - _chat.DispatchGlobalAnnouncement(Loc.GetString("carp-rift-warning", ("location", location)), playSound: false, colorOverride: Color.Red); + var msg = Loc.GetString("carp-rift-warning", + ("location", FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((uid, xform))))); + _chat.DispatchGlobalAnnouncement(msg, playSound: false, colorOverride: Color.Red); _audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true); _navMap.SetBeaconEnabled(uid, true); } diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index e24de5a2f6..94f5585536 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Body.Systems; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Explosion.Components; using Content.Server.Flash; +using Content.Server.Pinpointer; using Content.Shared.Flash.Components; using Content.Server.Radio.EntitySystems; using Content.Shared.Chemistry.Components; @@ -31,6 +32,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Player; using Content.Shared.Coordinates; +using Robust.Shared.Utility; namespace Content.Server.Explosion.EntitySystems { @@ -67,6 +69,7 @@ namespace Content.Server.Explosion.EntitySystems [Dependency] private readonly BodySystem _body = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + [Dependency] private readonly NavMapSystem _navMap = default!; [Dependency] private readonly RadioSystem _radioSystem = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -185,12 +188,7 @@ namespace Content.Server.Explosion.EntitySystems return; // Gets location of the implant - var ownerXform = Transform(uid); - var pos = ownerXform.MapPosition; - var x = (int) pos.X; - var y = (int) pos.Y; - var posText = $"({x}, {y})"; - + var posText = FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString(uid)); var critMessage = Loc.GetString(component.CritMessage, ("user", implanted.ImplantedEntity.Value), ("position", posText)); var deathMessage = Loc.GetString(component.DeathMessage, ("user", implanted.ImplantedEntity.Value), ("position", posText)); diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index 17c25d1fe9..c996f05ec4 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -21,6 +21,7 @@ using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Player; using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Server.Nuke; @@ -463,7 +464,8 @@ public sealed class NukeSystem : EntitySystem // warn a crew var announcement = Loc.GetString("nuke-component-announcement-armed", - ("time", (int) component.RemainingTime), ("position", posText)); + ("time", (int) component.RemainingTime), + ("location", FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((uid, nukeXform))))); var sender = Loc.GetString("nuke-component-announcement-sender"); _chatSystem.DispatchStationAnnouncement(stationUid ?? uid, announcement, sender, false, null, Color.Red); diff --git a/Content.Server/Pinpointer/NavMapSystem.cs b/Content.Server/Pinpointer/NavMapSystem.cs index 36fb39975e..2a5639886e 100644 --- a/Content.Server/Pinpointer/NavMapSystem.cs +++ b/Content.Server/Pinpointer/NavMapSystem.cs @@ -1,12 +1,16 @@ +using System.Diagnostics.CodeAnalysis; using Content.Server.Administration.Logs; using Content.Server.Station.Systems; using Content.Server.Warps; using Content.Shared.Database; using Content.Shared.Examine; +using Content.Shared.Localizations; using Content.Shared.Pinpointer; using Content.Shared.Tag; +using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Shared.GameStates; +using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; @@ -22,10 +26,15 @@ public sealed class NavMapSystem : SharedNavMapSystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly TagSystem _tags = default!; [Dependency] private readonly MapSystem _map = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly TransformSystem _transform = default!; private EntityQuery _physicsQuery; private EntityQuery _tagQuery; + public const float CloseDistance = 15f; + public const float FarDistance = 30f; + public override void Initialize() { base.Initialize(); @@ -40,6 +49,7 @@ public sealed class NavMapSystem : SharedNavMapSystem SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnNavMapSplit); + SubscribeLocalEvent(OnNavMapBeaconMapInit); SubscribeLocalEvent(OnNavMapBeaconStartup); SubscribeLocalEvent(OnNavMapBeaconAnchor); @@ -57,6 +67,16 @@ public sealed class NavMapSystem : SharedNavMapSystem RefreshGrid(ev.GridId, comp, Comp(ev.GridId)); } + private void OnNavMapBeaconMapInit(EntityUid uid, NavMapBeaconComponent component, MapInitEvent args) + { + if (component.DefaultText == null || component.Text != null) + return; + + component.Text = Loc.GetString(component.DefaultText); + Dirty(uid, component); + RefreshNavGrid(uid); + } + private void OnNavMapBeaconStartup(EntityUid uid, NavMapBeaconComponent component, ComponentStartup args) { RefreshNavGrid(uid); @@ -384,4 +404,101 @@ public sealed class NavMapSystem : SharedNavMapSystem SetBeaconEnabled(uid, !comp.Enabled, comp); } + + /// + /// For a given position, tries to find the nearest configurable beacon that is marked as visible. + /// This is used for things like announcements where you want to find the closest "landmark" to something. + /// + [PublicAPI] + public bool TryGetNearestBeacon(Entity ent, + [NotNullWhen(true)] out Entity? beacon, + [NotNullWhen(true)] out MapCoordinates? beaconCoords) + { + beacon = null; + beaconCoords = null; + if (!Resolve(ent, ref ent.Comp)) + return false; + + return TryGetNearestBeacon(_transform.GetMapCoordinates(ent, ent.Comp), out beacon, out beaconCoords); + } + + /// + /// For a given position, tries to find the nearest configurable beacon that is marked as visible. + /// This is used for things like announcements where you want to find the closest "landmark" to something. + /// + public bool TryGetNearestBeacon(MapCoordinates coordinates, + [NotNullWhen(true)] out Entity? beacon, + [NotNullWhen(true)] out MapCoordinates? beaconCoords) + { + beacon = null; + beaconCoords = null; + var minDistance = float.PositiveInfinity; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out var navBeacon, out var xform)) + { + if (!navBeacon.Enabled) + continue; + + if (navBeacon.Text == null) + continue; + + if (coordinates.MapId != xform.MapID) + continue; + + var coords = _transform.GetWorldPosition(xform); + var distanceSquared = (coordinates.Position - coords).LengthSquared(); + if (!float.IsInfinity(minDistance) && distanceSquared >= minDistance) + continue; + + minDistance = distanceSquared; + beacon = (uid, navBeacon); + beaconCoords = new MapCoordinates(coords, xform.MapID); + } + + return beacon != null; + } + + [PublicAPI] + public string GetNearestBeaconString(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return Loc.GetString("nav-beacon-pos-no-beacons"); + + return GetNearestBeaconString(_transform.GetMapCoordinates(ent, ent.Comp)); + } + + public string GetNearestBeaconString(MapCoordinates coordinates) + { + if (!TryGetNearestBeacon(coordinates, out var beacon, out var pos)) + return Loc.GetString("nav-beacon-pos-no-beacons"); + + var gridOffset = Angle.Zero; + if (_mapManager.TryFindGridAt(pos.Value, out var grid, out _)) + gridOffset = Transform(grid).LocalRotation; + + // get the angle between the two positions, adjusted for the grid rotation so that + // we properly preserve north in relation to the grid. + var dir = (pos.Value.Position - coordinates.Position).ToWorldAngle(); + var adjustedDir = (dir - gridOffset).GetDir(); + + var length = (pos.Value.Position - coordinates.Position).Length(); + if (length < CloseDistance) + { + return Loc.GetString("nav-beacon-pos-format", + ("color", beacon.Value.Comp.Color), + ("marker", beacon.Value.Comp.Text!)); + } + + var modifier = length > FarDistance + ? Loc.GetString("nav-beacon-pos-format-direction-mod-far") + : string.Empty; + + // we can null suppress the text being null because TRyGetNearestVisibleStationBeacon always gives us a beacon with not-null text. + return Loc.GetString("nav-beacon-pos-format-direction", + ("modifier", modifier), + ("direction", ContentLocalizationManager.FormatDirection(adjustedDir).ToLowerInvariant()), + ("color", beacon.Value.Comp.Color), + ("marker", beacon.Value.Comp.Text!)); + } } diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index 39b76f7d32..a7f83f2e15 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -8,6 +8,7 @@ using Content.Server.Communications; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.GameTicking.Events; +using Content.Server.Pinpointer; using Content.Server.Popups; using Content.Server.RoundEnd; using Content.Server.Screens.Components; @@ -33,6 +34,7 @@ using Robust.Shared.Map.Components; using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Timing; +using Robust.Shared.Utility; namespace Content.Server.Shuttles.Systems; @@ -55,6 +57,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem [Dependency] private readonly DockingSystem _dock = default!; [Dependency] private readonly EntityManager _entityManager = default!; [Dependency] private readonly IdCardSystem _idSystem = default!; + [Dependency] private readonly NavMapSystem _navMap = default!; [Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly RoundEndSystem _roundEnd = default!; @@ -307,11 +310,8 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem } else { - if (TryComp(targetGrid.Value, out var targetXform)) - { - var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery); - _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("direction", angle.GetDir())), playDefaultSound: false); - } + var location = FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((stationShuttle.EmergencyShuttle.Value, xform))); + _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("direction", location)), playDefaultSound: false); _logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to find a valid docking port for {ToPrettyString(stationUid)}"); // TODO: Need filter extensions or something don't blame me. diff --git a/Content.Shared/Localizations/ContentLocalizationManager.cs b/Content.Shared/Localizations/ContentLocalizationManager.cs index 0a06d9ba4f..3c311f4382 100644 --- a/Content.Shared/Localizations/ContentLocalizationManager.cs +++ b/Content.Shared/Localizations/ContentLocalizationManager.cs @@ -119,6 +119,14 @@ namespace Content.Shared.Localizations }; } + /// + /// Formats a direction struct as a human-readable string. + /// + public static string FormatDirection(Direction dir) + { + return Loc.GetString($"zzzz-fmt-direction-{dir.ToString()}"); + } + private static ILocValue FormatLoc(LocArgs args) { var id = ((LocValueString) args.Args[0]).Value; diff --git a/Content.Shared/Pinpointer/NavMapBeaconComponent.cs b/Content.Shared/Pinpointer/NavMapBeaconComponent.cs index c3132ee37f..0a86bdd9d2 100644 --- a/Content.Shared/Pinpointer/NavMapBeaconComponent.cs +++ b/Content.Shared/Pinpointer/NavMapBeaconComponent.cs @@ -16,6 +16,13 @@ public sealed partial class NavMapBeaconComponent : Component [AutoNetworkedField] public string? Text; + /// + /// A localization string that populates if it is null at mapinit. + /// Used so that mappers can still override Text while mapping. + /// + [DataField] + public LocId? DefaultText; + [ViewVariables(VVAccess.ReadWrite), DataField] [AutoNetworkedField] public Color Color = Color.Orange; diff --git a/Content.Shared/Pinpointer/SharedNavMapSystem.cs b/Content.Shared/Pinpointer/SharedNavMapSystem.cs index 7a62e6aabe..17f86ac7e6 100644 --- a/Content.Shared/Pinpointer/SharedNavMapSystem.cs +++ b/Content.Shared/Pinpointer/SharedNavMapSystem.cs @@ -8,13 +8,6 @@ public abstract class SharedNavMapSystem : EntitySystem { public const byte ChunkSize = 4; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnNavMapBeaconMapInit); - } - /// /// Converts the chunk's tile into a bitflag for the slot. /// @@ -38,13 +31,6 @@ public abstract class SharedNavMapSystem : EntitySystem return new Vector2i(x, y); } - private void OnNavMapBeaconMapInit(EntityUid uid, NavMapBeaconComponent component, MapInitEvent args) - { - component.Text ??= string.Empty; - component.Text = Loc.GetString(component.Text); - Dirty(uid, component); - } - [Serializable, NetSerializable] protected sealed class NavMapComponentState : ComponentState { diff --git a/Resources/Locale/en-US/_directions.ftl b/Resources/Locale/en-US/_directions.ftl new file mode 100644 index 0000000000..7e4b82d1dc --- /dev/null +++ b/Resources/Locale/en-US/_directions.ftl @@ -0,0 +1,8 @@ +zzzz-fmt-direction-North = North +zzzz-fmt-direction-South = South +zzzz-fmt-direction-East = East +zzzz-fmt-direction-West = West +zzzz-fmt-direction-NorthEast = NorthEast +zzzz-fmt-direction-SouthEast = SouthEast +zzzz-fmt-direction-NorthWest = NorthWest +zzzz-fmt-direction-SouthWest = SouthWest diff --git a/Resources/Locale/en-US/dragon/rifts.ftl b/Resources/Locale/en-US/dragon/rifts.ftl index 5ad061abf9..c182ebf10c 100644 --- a/Resources/Locale/en-US/dragon/rifts.ftl +++ b/Resources/Locale/en-US/dragon/rifts.ftl @@ -1,4 +1,4 @@ -carp-rift-warning = A rift is causing an unnaturally large energy flux at {$location}. Stop it at all costs! +carp-rift-warning = A rift is causing an unnaturally large energy flux {$location}. Stop it at all costs! carp-rift-duplicate = Cannot have 2 charging rifts at the same time! carp-rift-examine = It is [color=yellow]{$percentage}%[/color] charged! carp-rift-max = You have reached your maximum amount of rifts diff --git a/Resources/Locale/en-US/implant/implant.ftl b/Resources/Locale/en-US/implant/implant.ftl index 2f6ab9e4e2..b93d43105a 100644 --- a/Resources/Locale/en-US/implant/implant.ftl +++ b/Resources/Locale/en-US/implant/implant.ftl @@ -22,5 +22,5 @@ scramble-implant-activated-popup = Your appearance shifts and changes! ## Implant Messages -deathrattle-implant-dead-message = {$user} has died at {$position}. -deathrattle-implant-critical-message = {$user} life signs critical, immediate assistance required at {$position}. +deathrattle-implant-dead-message = {$user} has died {$position}. +deathrattle-implant-critical-message = {$user} life signs critical, immediate assistance required {$position}. diff --git a/Resources/Locale/en-US/navmap-beacons/station_map.ftl b/Resources/Locale/en-US/navmap-beacons/station_map.ftl index d58d93bdb4..1563e0abaf 100644 --- a/Resources/Locale/en-US/navmap-beacons/station_map.ftl +++ b/Resources/Locale/en-US/navmap-beacons/station_map.ftl @@ -11,3 +11,8 @@ nav-beacon-examine-text = It is [color={$enabled -> [true] forestgreen]on *[false] crimson]off }[/color] and the display reads [color={$color}]"{$label}"[/color] + +nav-beacon-pos-no-beacons = in the middle of nowhere +nav-beacon-pos-format = [color={$color}]near {$marker}[/color] +nav-beacon-pos-format-direction = [color={$color}]{$modifier}{$direction} of {$marker}[/color] +nav-beacon-pos-format-direction-mod-far = far {""} diff --git a/Resources/Locale/en-US/nuke/nuke-component.ftl b/Resources/Locale/en-US/nuke/nuke-component.ftl index b2a61dfcc2..981dd8b6ae 100644 --- a/Resources/Locale/en-US/nuke/nuke-component.ftl +++ b/Resources/Locale/en-US/nuke/nuke-component.ftl @@ -1,6 +1,6 @@ nuke-component-cant-anchor-floor = The anchoring bolts fail to lock into the floor! nuke-component-announcement-sender = Nuclear Fission Explosive -nuke-component-announcement-armed = Attention! The station's self-destruct mechanism has been engaged at global coordinates {$position}. {$time} seconds until detonation. If this was made in error, the mechanism may still be disarmed. +nuke-component-announcement-armed = Attention! The station's self-destruct mechanism has been engaged {$location}. {$time} seconds until detonation. If this was made in error, the mechanism may still be disarmed. nuke-component-announcement-unarmed = The station's self-destruct was deactivated! Have a nice day! nuke-component-announcement-send-codes = Attention! Self-destruction codes have been sent to designated fax machines. nuke-component-doafter-warning = You start fiddling with wires and knobs in order to disarm the nuke.. This may take a while. diff --git a/Resources/Locale/en-US/shuttles/emergency.ftl b/Resources/Locale/en-US/shuttles/emergency.ftl index 57d4d09eff..c716291135 100644 --- a/Resources/Locale/en-US/shuttles/emergency.ftl +++ b/Resources/Locale/en-US/shuttles/emergency.ftl @@ -15,7 +15,7 @@ emergency-shuttle-left = The Emergency Shuttle has left the station. Estimate {$ emergency-shuttle-launch-time = The emergency shuttle will launch in {$consoleAccumulator} seconds. emergency-shuttle-docked = The Emergency Shuttle has docked with the station on the {$direction} side. It will leave in {$time} seconds. emergency-shuttle-good-luck = The Emergency Shuttle is unable to find a station. Good luck. -emergency-shuttle-nearby = The Emergency Shuttle is unable to find a valid docking port. It has warped in {$direction} of the station. +emergency-shuttle-nearby = The Emergency Shuttle is unable to find a valid docking port. It has warped {$direction}. # Emergency shuttle console popup / announcement emergency-shuttle-console-no-early-launches = Early launch is disabled diff --git a/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml b/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml index 6cf66ba042..fa9071d3e9 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml @@ -22,7 +22,7 @@ False: {state: icon} - type: ConfigurableNavMapBeacon - type: NavMapBeacon - text: station-beacon-general + defaultText: station-beacon-general color: "#D4D4D496" - type: WarpPoint - type: ActivatableUI @@ -118,7 +118,7 @@ suffix: Command components: - type: NavMapBeacon - text: station-beacon-command + defaultText: station-beacon-command color: "#FFFF00" - type: entity @@ -127,7 +127,7 @@ suffix: Bridge components: - type: NavMapBeacon - text: station-beacon-bridge + defaultText: station-beacon-bridge - type: entity parent: DefaultStationBeaconCommand @@ -135,7 +135,7 @@ suffix: Vault components: - type: NavMapBeacon - text: station-beacon-vault + defaultText: station-beacon-vault - type: entity parent: DefaultStationBeaconCommand @@ -143,7 +143,7 @@ suffix: Captain's Quarters components: - type: NavMapBeacon - text: station-beacon-captain + defaultText: station-beacon-captain - type: entity parent: DefaultStationBeaconCommand @@ -151,7 +151,7 @@ suffix: HOP's Office components: - type: NavMapBeacon - text: station-beacon-hop + defaultText: station-beacon-hop - type: entity parent: DefaultStationBeacon @@ -159,7 +159,7 @@ suffix: Security components: - type: NavMapBeacon - text: station-beacon-security + defaultText: station-beacon-security color: "#DE3A3A" - type: entity @@ -168,7 +168,7 @@ suffix: Brig components: - type: NavMapBeacon - text: station-beacon-brig + defaultText: station-beacon-brig - type: entity parent: DefaultStationBeaconSecurity @@ -176,7 +176,7 @@ suffix: Warden's Office components: - type: NavMapBeacon - text: station-beacon-warden + defaultText: station-beacon-warden - type: entity parent: DefaultStationBeaconSecurity @@ -184,7 +184,7 @@ suffix: HOS’s Room components: - type: NavMapBeacon - text: station-beacon-hos + defaultText: station-beacon-hos - type: entity parent: DefaultStationBeaconSecurity @@ -192,7 +192,7 @@ suffix: Armory components: - type: NavMapBeacon - text: station-beacon-armory + defaultText: station-beacon-armory - type: entity parent: DefaultStationBeaconSecurity @@ -200,7 +200,7 @@ suffix: Perma Brig components: - type: NavMapBeacon - text: station-beacon-perma-brig + defaultText: station-beacon-perma-brig - type: entity parent: DefaultStationBeaconSecurity @@ -208,7 +208,7 @@ suffix: Detective's Room components: - type: NavMapBeacon - text: station-beacon-detective + defaultText: station-beacon-detective - type: entity parent: DefaultStationBeaconSecurity @@ -216,7 +216,7 @@ suffix: Courtroom components: - type: NavMapBeacon - text: station-beacon-courtroom + defaultText: station-beacon-courtroom - type: entity parent: DefaultStationBeaconSecurity @@ -224,7 +224,7 @@ suffix: Law Office components: - type: NavMapBeacon - text: station-beacon-law + defaultText: station-beacon-law - type: entity parent: DefaultStationBeaconSecurity @@ -232,7 +232,7 @@ suffix: Sec Checkpoint components: - type: NavMapBeacon - text: station-beacon-security-checkpoint + defaultText: station-beacon-security-checkpoint - type: entity parent: DefaultStationBeacon @@ -240,7 +240,7 @@ suffix: Medical components: - type: NavMapBeacon - text: station-beacon-medical + defaultText: station-beacon-medical color: "#52B4E9" - type: entity @@ -249,7 +249,7 @@ suffix: Medbay components: - type: NavMapBeacon - text: station-beacon-medbay + defaultText: station-beacon-medbay - type: entity parent: DefaultStationBeaconMedical @@ -257,7 +257,7 @@ suffix: Chemistry components: - type: NavMapBeacon - text: station-beacon-chemistry + defaultText: station-beacon-chemistry - type: entity parent: DefaultStationBeaconMedical @@ -265,7 +265,7 @@ suffix: Cryonics components: - type: NavMapBeacon - text: station-beacon-cryonics + defaultText: station-beacon-cryonics - type: entity parent: DefaultStationBeaconMedical @@ -273,7 +273,7 @@ suffix: CMO's room components: - type: NavMapBeacon - text: station-beacon-cmo + defaultText: station-beacon-cmo - type: entity parent: DefaultStationBeaconMedical @@ -281,7 +281,7 @@ suffix: Morgue components: - type: NavMapBeacon - text: station-beacon-morgue + defaultText: station-beacon-morgue - type: entity parent: DefaultStationBeaconMedical @@ -289,7 +289,7 @@ suffix: Surgery components: - type: NavMapBeacon - text: station-beacon-surgery + defaultText: station-beacon-surgery - type: entity parent: DefaultStationBeacon @@ -297,7 +297,7 @@ suffix: Science components: - type: NavMapBeacon - text: station-beacon-science + defaultText: station-beacon-science color: "#D381C9" - type: entity @@ -306,7 +306,7 @@ suffix: Research and Development components: - type: NavMapBeacon - text: station-beacon-research-and-development + defaultText: station-beacon-research-and-development - type: entity parent: DefaultStationBeaconScience @@ -314,7 +314,7 @@ suffix: Research Server Room components: - type: NavMapBeacon - text: station-beacon-research-server + defaultText: station-beacon-research-server - type: entity parent: DefaultStationBeaconScience @@ -322,7 +322,7 @@ suffix: RD's Room components: - type: NavMapBeacon - text: station-beacon-research-director + defaultText: station-beacon-research-director - type: entity parent: DefaultStationBeaconScience @@ -330,7 +330,7 @@ suffix: Robotics components: - type: NavMapBeacon - text: station-beacon-robotics + defaultText: station-beacon-robotics - type: entity parent: DefaultStationBeaconScience @@ -338,7 +338,7 @@ suffix: Artifact Lab components: - type: NavMapBeacon - text: station-beacon-artifact-lab + defaultText: station-beacon-artifact-lab - type: entity parent: DefaultStationBeaconScience @@ -346,7 +346,7 @@ suffix: Anomaly Generator components: - type: NavMapBeacon - text: station-beacon-anomaly-gen + defaultText: station-beacon-anomaly-gen - type: entity parent: DefaultStationBeacon @@ -354,7 +354,7 @@ suffix: Supply components: - type: NavMapBeacon - text: station-beacon-supply + defaultText: station-beacon-supply color: "#A46106" - type: entity @@ -363,7 +363,7 @@ suffix: Cargo Reception components: - type: NavMapBeacon - text: station-beacon-cargo + defaultText: station-beacon-cargo - type: entity parent: DefaultStationBeaconSupply @@ -371,7 +371,7 @@ suffix: Cargo Bay components: - type: NavMapBeacon - text: station-beacon-cargo-bay + defaultText: station-beacon-cargo-bay - type: entity parent: DefaultStationBeaconSupply @@ -379,7 +379,7 @@ suffix: QM's Room components: - type: NavMapBeacon - text: station-beacon-qm + defaultText: station-beacon-qm - type: entity parent: DefaultStationBeaconSupply @@ -387,7 +387,7 @@ suffix: Salvage components: - type: NavMapBeacon - text: station-beacon-salvage + defaultText: station-beacon-salvage - type: entity parent: DefaultStationBeacon @@ -395,7 +395,7 @@ suffix: Engineering components: - type: NavMapBeacon - text: station-beacon-engineering + defaultText: station-beacon-engineering color: "#EFB341" - type: entity @@ -404,7 +404,7 @@ suffix: CE's Room components: - type: NavMapBeacon - text: station-beacon-ce + defaultText: station-beacon-ce - type: entity parent: DefaultStationBeaconEngineering @@ -412,7 +412,7 @@ suffix: AME components: - type: NavMapBeacon - text: station-beacon-ame + defaultText: station-beacon-ame - type: entity parent: DefaultStationBeaconEngineering @@ -420,7 +420,7 @@ suffix: Solars components: - type: NavMapBeacon - text: station-beacon-solars + defaultText: station-beacon-solars - type: entity parent: DefaultStationBeaconEngineering @@ -428,7 +428,7 @@ suffix: Grav Gen components: - type: NavMapBeacon - text: station-beacon-gravgen + defaultText: station-beacon-gravgen - type: entity parent: DefaultStationBeaconEngineering @@ -436,7 +436,7 @@ suffix: PA Control components: - type: NavMapBeacon - text: station-beacon-pa + defaultText: station-beacon-pa - type: entity parent: DefaultStationBeaconEngineering @@ -444,7 +444,7 @@ suffix: SMES Power Bank components: - type: NavMapBeacon - text: station-beacon-smes + defaultText: station-beacon-smes - type: entity parent: DefaultStationBeaconEngineering @@ -452,7 +452,7 @@ suffix: Telecoms components: - type: NavMapBeacon - text: station-beacon-telecoms + defaultText: station-beacon-telecoms - type: entity parent: DefaultStationBeaconEngineering @@ -460,7 +460,7 @@ suffix: Atmospherics components: - type: NavMapBeacon - text: station-beacon-atmos + defaultText: station-beacon-atmos - type: entity parent: DefaultStationBeaconEngineering @@ -468,7 +468,7 @@ suffix: TEG components: - type: NavMapBeacon - text: station-beacon-teg + defaultText: station-beacon-teg - type: entity parent: DefaultStationBeaconEngineering @@ -476,7 +476,7 @@ suffix: Tech Vault components: - type: NavMapBeacon - text: station-beacon-tech-vault + defaultText: station-beacon-tech-vault - type: entity parent: DefaultStationBeacon @@ -484,7 +484,7 @@ suffix: Service components: - type: NavMapBeacon - text: station-beacon-service + defaultText: station-beacon-service color: "#9FED58" - type: entity @@ -493,7 +493,7 @@ suffix: Kitchen components: - type: NavMapBeacon - text: station-beacon-kitchen + defaultText: station-beacon-kitchen - type: entity parent: DefaultStationBeaconService @@ -501,7 +501,7 @@ suffix: Bar components: - type: NavMapBeacon - text: station-beacon-bar + defaultText: station-beacon-bar - type: entity parent: DefaultStationBeaconService @@ -509,7 +509,7 @@ suffix: Botany components: - type: NavMapBeacon - text: station-beacon-botany + defaultText: station-beacon-botany - type: entity parent: DefaultStationBeaconService @@ -517,7 +517,7 @@ suffix: Janitor's Closet components: - type: NavMapBeacon - text: station-beacon-janitor + defaultText: station-beacon-janitor - type: entity parent: DefaultStationBeacon @@ -525,7 +525,7 @@ suffix: AI components: - type: NavMapBeacon - text: station-beacon-ai + defaultText: station-beacon-ai color: "#2ed2fd" - type: entity @@ -534,7 +534,7 @@ suffix: AI Satellite components: - type: NavMapBeacon - text: station-beacon-ai-sat + defaultText: station-beacon-ai-sat - type: entity parent: DefaultStationBeaconAI @@ -542,7 +542,7 @@ suffix: AI Core components: - type: NavMapBeacon - text: station-beacon-ai-core + defaultText: station-beacon-ai-core - type: entity parent: DefaultStationBeacon @@ -550,7 +550,7 @@ suffix: Arrivals components: - type: NavMapBeacon - text: station-beacon-arrivals + defaultText: station-beacon-arrivals - type: entity parent: DefaultStationBeacon @@ -558,7 +558,7 @@ suffix: Evac components: - type: NavMapBeacon - text: station-beacon-evac + defaultText: station-beacon-evac - type: entity parent: DefaultStationBeacon @@ -566,7 +566,7 @@ suffix: EVA Storage components: - type: NavMapBeacon - text: station-beacon-eva-storage + defaultText: station-beacon-eva-storage - type: entity parent: DefaultStationBeacon @@ -574,7 +574,7 @@ suffix: Chapel components: - type: NavMapBeacon - text: station-beacon-chapel + defaultText: station-beacon-chapel - type: entity parent: DefaultStationBeacon @@ -582,7 +582,7 @@ suffix: Library components: - type: NavMapBeacon - text: station-beacon-library + defaultText: station-beacon-library - type: entity parent: DefaultStationBeacon @@ -590,7 +590,7 @@ suffix: Theater components: - type: NavMapBeacon - text: station-beacon-theater + defaultText: station-beacon-theater - type: entity parent: DefaultStationBeacon @@ -598,7 +598,7 @@ suffix: Dorms components: - type: NavMapBeacon - text: station-beacon-dorms + defaultText: station-beacon-dorms - type: entity parent: DefaultStationBeacon @@ -606,7 +606,7 @@ suffix: Tool Room components: - type: NavMapBeacon - text: station-beacon-tools + defaultText: station-beacon-tools - type: entity parent: DefaultStationBeacon @@ -614,7 +614,7 @@ suffix: Disposals components: - type: NavMapBeacon - text: station-beacon-disposals + defaultText: station-beacon-disposals - type: entity parent: DefaultStationBeacon @@ -622,7 +622,7 @@ suffix: Cryosleep components: - type: NavMapBeacon - text: station-beacon-cryosleep + defaultText: station-beacon-cryosleep - type: entity parent: DefaultStationBeacon @@ -630,4 +630,4 @@ suffix: Escape Pod components: - type: NavMapBeacon - text: station-beacon-escape-pod + defaultText: station-beacon-escape-pod From a071bc5dbfdd5418b664af95d00f676d8d915482 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 28 Mar 2024 05:54:24 +0000 Subject: [PATCH 23/56] 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 e398d01c79..6c593e8495 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Emisse - changes: - - message: Title2 to lobby music - type: Add - id: 5742 - time: '2024-01-19T02:47:20.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24254 - author: Repo changes: - message: Round restarts require minimum ghost levels on populated servers. @@ -3795,3 +3788,11 @@ id: 6241 time: '2024-03-28T05:41:57.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26490 +- author: EmoGarbage404 + changes: + - message: Various announcements now announce location on the map (near Medical, + etc.) instead of simply displaying coordinates. + type: Tweak + id: 6242 + time: '2024-03-28T05:53:18.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26437 From b1ba6b5bb615615cc58f255dc9fe84c5ffe8822b Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 28 Mar 2024 02:28:45 -0400 Subject: [PATCH 24/56] Arcade machine improvements (#24200) * Give 'em something to talk about * Wire panel visuals * Wire graphics tweak * More ads and thanks * More ads for a noisy arcade * New screen for space villain machines * Implement EmitSoundIntervalComponent and a bunch of arcade noises * Require power for sounds * Allow earlier startup intervals * Orange glow * Audio attributions * Include the PR link * Replace EmitSoundInterval with expanded SpamEmitSound * Remove pacman-themed arcade sounds * Documentation good. * Updated methods to use Entity * Refactored SpamEmitSound to get rid of accumulator and chance. * Fixed prewarm logic * Moved stuff to Shared * Fix outdated YAML * Better prediction, auto pause handling * Make enable/disable reset the timer instead of trying to save it. --- .../BlockGame/BlockGameArcadeComponent.cs | 5 ++ .../Arcade/BlockGame/BlockGameArcadeSystem.cs | 13 ++++ .../SpaceVillainArcadeComponent.cs | 5 ++ .../SpaceVillainArcadeSystem.cs | 18 ++++++ Content.Server/Bed/Sleep/SleepingSystem.cs | 10 +-- .../Components/SpamEmitSoundComponent.cs | 27 --------- Content.Server/Sound/EmitSoundSystem.cs | 55 ++++++++++++++--- .../Sound/SpamEmitSoundRequirePowerSystem.cs | 33 ++++++++++ .../Bed/Sleep/SleepEmitSoundComponent.cs | 8 +-- .../Components/SpamEmitSoundComponent.cs | 44 ++++++++++++++ .../SpamEmitSoundRequirePowerComponent.cs | 10 +++ Content.Shared/Sound/SharedEmitSoundSystem.cs | 10 ++- .../SharedSpamEmitSoundRequirePowerSystem.cs | 6 ++ .../Audio/Machines/Arcade/attributions.yml | 4 ++ Resources/Audio/Machines/Arcade/hahaha.ogg | Bin 0 -> 57972 bytes Resources/Audio/Machines/Arcade/pew_pew.ogg | Bin 0 -> 24306 bytes Resources/Audio/Machines/Arcade/sting_01.ogg | Bin 0 -> 39700 bytes Resources/Audio/Machines/Arcade/sting_02.ogg | Bin 0 -> 57619 bytes Resources/Audio/Machines/Arcade/sting_03.ogg | Bin 0 -> 23663 bytes Resources/Audio/Machines/Arcade/sting_04.ogg | Bin 0 -> 29128 bytes Resources/Audio/Machines/Arcade/sting_05.ogg | Bin 0 -> 38925 bytes Resources/Audio/Machines/Arcade/sting_06.ogg | Bin 0 -> 31830 bytes .../en-US/advertisements/arcade/blockgame.ftl | 26 ++++++++ .../advertisements/arcade/spacevillain.ftl | 28 +++++++++ .../Arcade/Advertisements/blockgame.yml | 29 +++++++++ .../Arcade/Advertisements/spacevillain.yml | 31 ++++++++++ .../Structures/Machines/Computers/arcades.yml | 57 +++++++++++++++--- .../Prototypes/Roles/Jobs/Civilian/clown.yml | 1 - .../Prototypes/SoundCollections/arcade.yml | 11 ++++ Resources/Prototypes/Wires/layouts.yml | 1 + .../Structures/Machines/arcade.rsi/meta.json | 50 ++++++++++++++- .../Structures/Machines/arcade.rsi/panel.png | Bin 0 -> 236 bytes .../{blockgame.png => screen_blockgame.png} | Bin .../{invaders.png => screen_invaders.png} | Bin .../arcade.rsi/screen_spacevillain.png | Bin 0 -> 930 bytes 35 files changed, 425 insertions(+), 57 deletions(-) delete mode 100644 Content.Server/Sound/Components/SpamEmitSoundComponent.cs create mode 100644 Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs create mode 100644 Content.Shared/Sound/Components/SpamEmitSoundComponent.cs create mode 100644 Content.Shared/Sound/Components/SpamEmitSoundRequirePowerComponent.cs create mode 100644 Content.Shared/Sound/SharedSpamEmitSoundRequirePowerSystem.cs create mode 100644 Resources/Audio/Machines/Arcade/attributions.yml create mode 100644 Resources/Audio/Machines/Arcade/hahaha.ogg create mode 100644 Resources/Audio/Machines/Arcade/pew_pew.ogg create mode 100644 Resources/Audio/Machines/Arcade/sting_01.ogg create mode 100644 Resources/Audio/Machines/Arcade/sting_02.ogg create mode 100644 Resources/Audio/Machines/Arcade/sting_03.ogg create mode 100644 Resources/Audio/Machines/Arcade/sting_04.ogg create mode 100644 Resources/Audio/Machines/Arcade/sting_05.ogg create mode 100644 Resources/Audio/Machines/Arcade/sting_06.ogg create mode 100644 Resources/Locale/en-US/advertisements/arcade/blockgame.ftl create mode 100644 Resources/Locale/en-US/advertisements/arcade/spacevillain.ftl create mode 100644 Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml create mode 100644 Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml create mode 100644 Resources/Prototypes/SoundCollections/arcade.yml create mode 100644 Resources/Textures/Structures/Machines/arcade.rsi/panel.png rename Resources/Textures/Structures/Machines/arcade.rsi/{blockgame.png => screen_blockgame.png} (100%) rename Resources/Textures/Structures/Machines/arcade.rsi/{invaders.png => screen_invaders.png} (100%) create mode 100644 Resources/Textures/Structures/Machines/arcade.rsi/screen_spacevillain.png diff --git a/Content.Server/Arcade/BlockGame/BlockGameArcadeComponent.cs b/Content.Server/Arcade/BlockGame/BlockGameArcadeComponent.cs index 5613d91544..e2acec52a3 100644 --- a/Content.Server/Arcade/BlockGame/BlockGameArcadeComponent.cs +++ b/Content.Server/Arcade/BlockGame/BlockGameArcadeComponent.cs @@ -19,4 +19,9 @@ public sealed partial class BlockGameArcadeComponent : Component /// The players currently viewing (but not playing) the active session of NT-BG. /// public readonly List Spectators = new(); + + /// + /// Whether the game machine should thank (or otherwise talk to) the player when they leave + /// + public bool ShouldSayThankYou; } diff --git a/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs b/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs index 34a5689fd1..0d9487dab8 100644 --- a/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs +++ b/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Power.Components; using Content.Shared.UserInterface; +using Content.Server.Advertise; using Content.Shared.Arcade; using Robust.Server.GameObjects; using Robust.Shared.Player; @@ -9,6 +10,7 @@ namespace Content.Server.Arcade.BlockGame; public sealed class BlockGameArcadeSystem : EntitySystem { [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly AdvertiseSystem _advertise = default!; public override void Initialize() { @@ -89,7 +91,15 @@ public sealed class BlockGameArcadeSystem : EntitySystem UpdatePlayerStatus(uid, component.Player, blockGame: component); } else + { + // Everybody's gone component.Player = null; + if (component.ShouldSayThankYou && TryComp(uid, out var advertise)) + { + _advertise.SayThankYou(uid, advertise); + component.ShouldSayThankYou = false; + } + } UpdatePlayerStatus(uid, temp, blockGame: component); } @@ -103,6 +113,7 @@ public sealed class BlockGameArcadeSystem : EntitySystem _uiSystem.CloseAll(bui); component.Player = null; component.Spectators.Clear(); + component.ShouldSayThankYou = false; } private void OnPlayerAction(EntityUid uid, BlockGameArcadeComponent component, BlockGameMessages.BlockGamePlayerActionMessage msg) @@ -122,6 +133,8 @@ public sealed class BlockGameArcadeSystem : EntitySystem return; } + component.ShouldSayThankYou = true; + component.Game.ProcessInput(msg.PlayerAction); } } diff --git a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs index e93fcc6e8f..c3a8877393 100644 --- a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs +++ b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs @@ -110,4 +110,9 @@ public sealed partial class SpaceVillainArcadeComponent : SharedSpaceVillainArca /// [ViewVariables(VVAccess.ReadWrite)] public int RewardAmount = 0; + + /// + /// Whether the game machine should thank (or otherwise talk to) the player when they leave + /// + public bool ShouldSayThankYou; } diff --git a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs index d97c94fd99..24fa6e32d1 100644 --- a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs +++ b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Power.Components; using Content.Shared.UserInterface; +using Content.Server.Advertise; using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent; using Robust.Server.GameObjects; using Robust.Shared.Audio; @@ -13,6 +14,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly AdvertiseSystem _advertise = default!; public override void Initialize() { @@ -22,6 +24,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem SubscribeLocalEvent(OnAfterUIOpenSV); SubscribeLocalEvent(OnSVPlayerAction); SubscribeLocalEvent(OnSVillainPower); + SubscribeLocalEvent(OnBoundUIClosed); } /// @@ -79,6 +82,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem case PlayerAction.Heal: case PlayerAction.Recharge: component.Game.ExecutePlayerAction(uid, msg.PlayerAction, component); + component.ShouldSayThankYou = true; // Any sort of gameplay action counts break; case PlayerAction.NewGame: _audioSystem.PlayPvs(component.NewGameSound, uid, AudioParams.Default.WithVolume(-4f)); @@ -106,5 +110,19 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem if (_uiSystem.TryGetUi(uid, SpaceVillainArcadeUiKey.Key, out var bui)) _uiSystem.CloseAll(bui); + + component.ShouldSayThankYou = false; + } + + private void OnBoundUIClosed(Entity ent, ref BoundUIClosedEvent args) + { + if (args.UiKey is not SpaceVillainArcadeUiKey || (SpaceVillainArcadeUiKey) args.UiKey != SpaceVillainArcadeUiKey.Key) + return; + + if (ent.Comp.ShouldSayThankYou && TryComp(ent.Owner, out var advertise)) + { + _advertise.SayThankYou(ent.Owner, advertise); + ent.Comp.ShouldSayThankYou = false; + } } } diff --git a/Content.Server/Bed/Sleep/SleepingSystem.cs b/Content.Server/Bed/Sleep/SleepingSystem.cs index b497254433..5e4f0eddb5 100644 --- a/Content.Server/Bed/Sleep/SleepingSystem.cs +++ b/Content.Server/Bed/Sleep/SleepingSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Popups; -using Content.Server.Sound.Components; +using Content.Server.Sound; +using Content.Shared.Sound.Components; using Content.Shared.Actions; using Content.Shared.Audio; using Content.Shared.Bed.Sleep; @@ -30,6 +31,7 @@ namespace Content.Server.Bed.Sleep [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; + [Dependency] private readonly EmitSoundSystem _emitSound = default!; [ValidatePrototypeId] public const string SleepActionId = "ActionSleep"; @@ -71,8 +73,8 @@ namespace Content.Server.Bed.Sleep { emitSound.Sound = sleepSound.Snore; } - emitSound.PlayChance = sleepSound.Chance; - emitSound.RollInterval = sleepSound.Interval; + emitSound.MinInterval = sleepSound.Interval; + emitSound.MaxInterval = sleepSound.MaxInterval; emitSound.PopUp = sleepSound.PopUp; } @@ -128,7 +130,7 @@ namespace Content.Server.Bed.Sleep return; } if (TryComp(uid, out var spam)) - spam.Enabled = args.NewMobState == MobState.Alive; + _emitSound.SetEnabled((uid, spam), args.NewMobState == MobState.Alive); } private void AddWakeVerb(EntityUid uid, SleepingComponent component, GetVerbsEvent args) diff --git a/Content.Server/Sound/Components/SpamEmitSoundComponent.cs b/Content.Server/Sound/Components/SpamEmitSoundComponent.cs deleted file mode 100644 index d17bbb9e8f..0000000000 --- a/Content.Server/Sound/Components/SpamEmitSoundComponent.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Content.Shared.Sound.Components; - -namespace Content.Server.Sound.Components -{ - /// - /// Rolls to play a sound every few seconds. - /// - [RegisterComponent] - public sealed partial class SpamEmitSoundComponent : BaseEmitSoundComponent - { - [DataField("accumulator")] - public float Accumulator = 0f; - - [DataField("rollInterval")] - public float RollInterval = 2f; - - [DataField("playChance")] - public float PlayChance = 0.5f; - - // Always Pvs. - [DataField("popUp")] - public string? PopUp; - - [DataField("enabled")] - public bool Enabled = true; - } -} diff --git a/Content.Server/Sound/EmitSoundSystem.cs b/Content.Server/Sound/EmitSoundSystem.cs index 059800c3f9..5b9620990e 100644 --- a/Content.Server/Sound/EmitSoundSystem.cs +++ b/Content.Server/Sound/EmitSoundSystem.cs @@ -2,12 +2,17 @@ using Content.Server.Explosion.EntitySystems; using Content.Server.Sound.Components; using Content.Shared.UserInterface; using Content.Shared.Sound; -using Robust.Shared.Random; +using Content.Shared.Sound.Components; +using Robust.Shared.Timing; +using Robust.Shared.Network; namespace Content.Server.Sound; public sealed class EmitSoundSystem : SharedEmitSoundSystem { + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; + public override void Update(float frameTime) { base.Update(frameTime); @@ -18,18 +23,13 @@ public sealed class EmitSoundSystem : SharedEmitSoundSystem if (!soundSpammer.Enabled) continue; - soundSpammer.Accumulator += frameTime; - if (soundSpammer.Accumulator < soundSpammer.RollInterval) - { - continue; - } - soundSpammer.Accumulator -= soundSpammer.RollInterval; - - if (Random.Prob(soundSpammer.PlayChance)) + if (_timing.CurTime >= soundSpammer.NextSound) { if (soundSpammer.PopUp != null) Popup.PopupEntity(Loc.GetString(soundSpammer.PopUp), uid); TryEmitSound(uid, soundSpammer, predict: false); + + SpamEmitSoundReset((uid, soundSpammer)); } } } @@ -40,6 +40,8 @@ public sealed class EmitSoundSystem : SharedEmitSoundSystem SubscribeLocalEvent(HandleEmitSoundOnTrigger); SubscribeLocalEvent(HandleEmitSoundOnUIOpen); + + SubscribeLocalEvent(HandleSpamEmitSoundMapInit); } private void HandleEmitSoundOnUIOpen(EntityUid uid, EmitSoundOnUIOpenComponent component, AfterActivatableUIOpenEvent args) @@ -52,4 +54,39 @@ public sealed class EmitSoundSystem : SharedEmitSoundSystem TryEmitSound(uid, component, args.User, false); args.Handled = true; } + + private void HandleSpamEmitSoundMapInit(Entity entity, ref MapInitEvent args) + { + SpamEmitSoundReset(entity); + + // Prewarm so multiple entities have more variation. + entity.Comp.NextSound -= Random.Next(entity.Comp.MaxInterval); + Dirty(entity); + } + + private void SpamEmitSoundReset(Entity entity) + { + if (_net.IsClient) + return; + + entity.Comp.NextSound = _timing.CurTime + ((entity.Comp.MinInterval < entity.Comp.MaxInterval) + ? Random.Next(entity.Comp.MinInterval, entity.Comp.MaxInterval) + : entity.Comp.MaxInterval); + + Dirty(entity); + } + + public override void SetEnabled(Entity entity, bool enabled) + { + if (!Resolve(entity, ref entity.Comp, false)) + return; + + if (entity.Comp.Enabled == enabled) + return; + + entity.Comp.Enabled = enabled; + + if (enabled) + SpamEmitSoundReset((entity, entity.Comp)); + } } diff --git a/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs b/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs new file mode 100644 index 0000000000..9cc85060c6 --- /dev/null +++ b/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs @@ -0,0 +1,33 @@ +using Content.Server.Power.Components; +using Content.Server.Power.EntitySystems; +using Content.Shared.Sound; +using Content.Shared.Sound.Components; + +namespace Content.Server.Sound; + +public sealed partial class SpamEmitSoundRequirePowerSystem : SharedSpamEmitSoundRequirePowerSystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnPowerSupply); + } + + private void OnPowerChanged(Entity entity, ref PowerChangedEvent args) + { + if (TryComp(entity.Owner, out var comp)) + { + EmitSound.SetEnabled((entity, comp), args.Powered); + } + } + + private void OnPowerSupply(Entity entity, ref PowerNetBatterySupplyEvent args) + { + if (TryComp(entity.Owner, out var comp)) + { + EmitSound.SetEnabled((entity, comp), args.Supply); + } + } +} diff --git a/Content.Shared/Bed/Sleep/SleepEmitSoundComponent.cs b/Content.Shared/Bed/Sleep/SleepEmitSoundComponent.cs index 6313f633f2..9250a33077 100644 --- a/Content.Shared/Bed/Sleep/SleepEmitSoundComponent.cs +++ b/Content.Shared/Bed/Sleep/SleepEmitSoundComponent.cs @@ -12,16 +12,16 @@ public sealed partial class SleepEmitSoundComponent : Component public SoundSpecifier Snore = new SoundCollectionSpecifier("Snores", AudioParams.Default.WithVariation(0.2f)); /// - /// Interval between snore attempts in seconds + /// Minimum interval between snore attempts in seconds /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public float Interval = 5f; + public TimeSpan Interval = TimeSpan.FromSeconds(5); /// - /// Chance for snore attempt to succeed + /// Maximum interval between snore attempts in seconds /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public float Chance = 0.33f; + public TimeSpan MaxInterval = TimeSpan.FromSeconds(15); /// /// Popup for snore (e.g. Zzz...) diff --git a/Content.Shared/Sound/Components/SpamEmitSoundComponent.cs b/Content.Shared/Sound/Components/SpamEmitSoundComponent.cs new file mode 100644 index 0000000000..149728a5ba --- /dev/null +++ b/Content.Shared/Sound/Components/SpamEmitSoundComponent.cs @@ -0,0 +1,44 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Sound.Components; + +/// +/// Repeatedly plays a sound with a randomized delay. +/// +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState, AutoGenerateComponentPause] +public sealed partial class SpamEmitSoundComponent : BaseEmitSoundComponent +{ + /// + /// The time at which the next sound will play. + /// + [DataField, AutoPausedField, AutoNetworkedField] + public TimeSpan NextSound; + + /// + /// The minimum time in seconds between playing the sound. + /// + [DataField] + public TimeSpan MinInterval = TimeSpan.FromSeconds(2); + + /// + /// The maximum time in seconds between playing the sound. + /// + [DataField] + public TimeSpan MaxInterval = TimeSpan.FromSeconds(2); + + // Always Pvs. + /// + /// Content of a popup message to display whenever the sound plays. + /// + [DataField] + public LocId? PopUp; + + /// + /// Whether the timer is currently running and sounds are being played. + /// Do not set this directly, use + /// + [DataField, AutoNetworkedField] + [Access(typeof(SharedEmitSoundSystem))] + public bool Enabled = true; +} diff --git a/Content.Shared/Sound/Components/SpamEmitSoundRequirePowerComponent.cs b/Content.Shared/Sound/Components/SpamEmitSoundRequirePowerComponent.cs new file mode 100644 index 0000000000..b0547ea398 --- /dev/null +++ b/Content.Shared/Sound/Components/SpamEmitSoundRequirePowerComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Shared.Sound.Components; + +/// +/// Enables or disables an SpamEmitSound component depending +/// on the powered state of the entity. +/// +[RegisterComponent] +public sealed partial class SpamEmitSoundRequirePowerComponent : Component +{ +} diff --git a/Content.Shared/Sound/SharedEmitSoundSystem.cs b/Content.Shared/Sound/SharedEmitSoundSystem.cs index 56a51744ac..cd7828fc6b 100644 --- a/Content.Shared/Sound/SharedEmitSoundSystem.cs +++ b/Content.Shared/Sound/SharedEmitSoundSystem.cs @@ -24,7 +24,7 @@ namespace Content.Shared.Sound; [UsedImplicitly] public abstract class SharedEmitSoundSystem : EntitySystem { - [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] private readonly INetManager _netMan = default!; [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; [Dependency] protected readonly IRobustRandom Random = default!; @@ -124,7 +124,7 @@ public abstract class SharedEmitSoundSystem : EntitySystem !args.OtherFixture.Hard || !TryComp(uid, out var physics) || physics.LinearVelocity.Length() < component.MinimumVelocity || - _timing.CurTime < component.NextSound || + Timing.CurTime < component.NextSound || MetaData(uid).EntityPaused) { return; @@ -136,7 +136,7 @@ public abstract class SharedEmitSoundSystem : EntitySystem var fraction = MathF.Min(1f, (physics.LinearVelocity.Length() - component.MinimumVelocity) / MaxVolumeVelocity); var volume = MinVolume + (MaxVolume - MinVolume) * fraction; - component.NextSound = _timing.CurTime + EmitSoundOnCollideComponent.CollideCooldown; + component.NextSound = Timing.CurTime + EmitSoundOnCollideComponent.CollideCooldown; var sound = component.Sound; if (_netMan.IsServer && sound != null) @@ -144,4 +144,8 @@ public abstract class SharedEmitSoundSystem : EntitySystem _audioSystem.PlayPvs(_audioSystem.GetSound(sound), uid, AudioParams.Default.WithVolume(volume)); } } + + public virtual void SetEnabled(Entity entity, bool enabled) + { + } } diff --git a/Content.Shared/Sound/SharedSpamEmitSoundRequirePowerSystem.cs b/Content.Shared/Sound/SharedSpamEmitSoundRequirePowerSystem.cs new file mode 100644 index 0000000000..ad44cba8a5 --- /dev/null +++ b/Content.Shared/Sound/SharedSpamEmitSoundRequirePowerSystem.cs @@ -0,0 +1,6 @@ +namespace Content.Shared.Sound; + +public abstract partial class SharedSpamEmitSoundRequirePowerSystem : EntitySystem +{ + [Dependency] protected readonly SharedEmitSoundSystem EmitSound = default!; +} diff --git a/Resources/Audio/Machines/Arcade/attributions.yml b/Resources/Audio/Machines/Arcade/attributions.yml new file mode 100644 index 0000000000..afb120bee0 --- /dev/null +++ b/Resources/Audio/Machines/Arcade/attributions.yml @@ -0,0 +1,4 @@ +- files: ["hahaha.ogg", "pew_pew.ogg", "sting_*.ogg"] + license: "CC0-1.0" + copyright: "Recorded by https://github.com/Tayrtahn" + source: "https://github.com/space-wizards/space-station-14/pull/24200" diff --git a/Resources/Audio/Machines/Arcade/hahaha.ogg b/Resources/Audio/Machines/Arcade/hahaha.ogg new file mode 100644 index 0000000000000000000000000000000000000000..8a01e5dd999b97a4b0ac48accde27ad0875baeb4 GIT binary patch literal 57972 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π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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/Machines/Arcade/sting_01.ogg b/Resources/Audio/Machines/Arcade/sting_01.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e1b7e7fd779c9e016a7742b9cebd2dce993c9881 GIT binary patch literal 39700 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 Y2T*tsWdh!1{O_z^N&pi8000000AFCZK>z>% literal 0 HcmV?d00001 diff --git a/Resources/Audio/Machines/Arcade/sting_02.ogg b/Resources/Audio/Machines/Arcade/sting_02.ogg new file mode 100644 index 0000000000000000000000000000000000000000..83651e37a40b53a9262c2a561b609a4bb90b811e GIT binary patch literal 57619 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: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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/Machines/Arcade/sting_03.ogg b/Resources/Audio/Machines/Arcade/sting_03.ogg new file mode 100644 index 0000000000000000000000000000000000000000..99bb7259cb30b1f64cf34eee8486e4639de82296 GIT binary patch literal 23663 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 literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/advertisements/arcade/blockgame.ftl b/Resources/Locale/en-US/advertisements/arcade/blockgame.ftl new file mode 100644 index 0000000000..ec755abe8f --- /dev/null +++ b/Resources/Locale/en-US/advertisements/arcade/blockgame.ftl @@ -0,0 +1,26 @@ +advertisement-block-game-1 = Legally distinct! +advertisement-block-game-2 = What the hell is a T-spin? +advertisement-block-game-3 = These blocks aren't going to clear themselves! +advertisement-block-game-4 = Beep boop! Bwoooop! +advertisement-block-game-5 = Let's play a game! +advertisement-block-game-6 = 6 whole colors of gameplay! +advertisement-block-game-7 = Hot 8-bit action! +advertisement-block-game-8 = Blocks, blocks, blocks! +advertisement-block-game-9 = Think YOU can claim the high score? +advertisement-block-game-10 = Nanotrasen Block Game IS what TetrISN'T! +advertisement-block-game-11 = Now with blast processing! +advertisement-block-game-12 = Our lawyers are standing by! +advertisement-block-game-13 = Hallelujah, it's raining blocks! + +thankyou-block-game-1 = Play again soon! +thankyou-block-game-2 = Well played! +thankyou-block-game-3 = Just one more game? +thankyou-block-game-4 = Stopping so soon? +thankyou-block-game-5 = The blocks will miss you. +thankyou-block-game-6 = Thanks for playin'! +thankyou-block-game-7 = Come back soon! +thankyou-block-game-8 = Beep bwooop! +thankyou-block-game-9 = There's always time for another game! +thankyou-block-game-10 = Don't give up now! +thankyou-block-game-11 = There are always more blocks! +thankyou-block-game-12 = The blocks await your return! diff --git a/Resources/Locale/en-US/advertisements/arcade/spacevillain.ftl b/Resources/Locale/en-US/advertisements/arcade/spacevillain.ftl new file mode 100644 index 0000000000..145c380849 --- /dev/null +++ b/Resources/Locale/en-US/advertisements/arcade/spacevillain.ftl @@ -0,0 +1,28 @@ +advertisement-space-villain-1 = Are you a bad enough dude to beat this game? +advertisement-space-villain-2 = Beat the bad guy; win a prize! +advertisement-space-villain-3 = FIGHT ME! +advertisement-space-villain-4 = Space needs a hero! +advertisement-space-villain-5 = I'm holding out for a hero! +advertisement-space-villain-6 = Won't someone save us? +advertisement-space-villain-7 = Mua-hah-hah-hah! +advertisement-space-villain-8 = Spaaaaaaaace Villain! +advertisement-space-villain-9 = No one can defeat me! +advertisement-space-villain-10 = Tremble before me! +advertisement-space-villain-11 = CHALLENGE ME! +advertisement-space-villain-12 = FEAR ME! +advertisement-space-villain-13 = Do you dare to face me in battle!? +advertisement-space-villain-14 = Beware, I live! +advertisement-space-villain-15 = I hunger! + +thankyou-space-villain-1 = And where do you think you're going, punk? +thankyou-space-villain-2 = Is that all you've got? +thankyou-space-villain-3 = This fight isn't over! +thankyou-space-villain-4 = Challenge again soon! +thankyou-space-villain-5 = Who dares to challenge me next? +thankyou-space-villain-6 = I knew you couldn't defeat me! +thankyou-space-villain-7 = Too much for you to handle? +thankyou-space-villain-8 = Run, coward! +thankyou-space-villain-9 = You never stood a chance. +thankyou-space-villain-10 = Care for a rematch? +thankyou-space-villain-11 = Fight me again! +thankyou-space-villain-12 = Come back here and fight me! diff --git a/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml b/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml new file mode 100644 index 0000000000..efcb8934a8 --- /dev/null +++ b/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml @@ -0,0 +1,29 @@ +- type: advertisementsPack + id: BlockGameAds + advertisements: + - advertisement-block-game-1 + - advertisement-block-game-2 + - advertisement-block-game-3 + - advertisement-block-game-4 + - advertisement-block-game-5 + - advertisement-block-game-6 + - advertisement-block-game-7 + - advertisement-block-game-8 + - advertisement-block-game-9 + - advertisement-block-game-10 + - advertisement-block-game-11 + - advertisement-block-game-12 + - advertisement-block-game-13 + thankyous: + - thankyou-block-game-1 + - thankyou-block-game-2 + - thankyou-block-game-3 + - thankyou-block-game-4 + - thankyou-block-game-5 + - thankyou-block-game-6 + - thankyou-block-game-7 + - thankyou-block-game-8 + - thankyou-block-game-9 + - thankyou-block-game-10 + - thankyou-block-game-11 + - thankyou-block-game-12 diff --git a/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml b/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml new file mode 100644 index 0000000000..98063a62dd --- /dev/null +++ b/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml @@ -0,0 +1,31 @@ +- type: advertisementsPack + id: SpaceVillainAds + advertisements: + - advertisement-space-villain-1 + - advertisement-space-villain-2 + - advertisement-space-villain-3 + - advertisement-space-villain-4 + - advertisement-space-villain-5 + - advertisement-space-villain-6 + - advertisement-space-villain-7 + - advertisement-space-villain-8 + - advertisement-space-villain-9 + - advertisement-space-villain-10 + - advertisement-space-villain-11 + - advertisement-space-villain-12 + - advertisement-space-villain-13 + - advertisement-space-villain-14 + - advertisement-space-villain-15 + thankyous: + - thankyou-space-villain-1 + - thankyou-space-villain-2 + - thankyou-space-villain-3 + - thankyou-space-villain-4 + - thankyou-space-villain-5 + - thankyou-space-villain-6 + - thankyou-space-villain-7 + - thankyou-space-villain-8 + - thankyou-space-villain-9 + - thankyou-space-villain-10 + - thankyou-space-villain-11 + - thankyou-space-villain-12 diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml index 85f6dc6894..2970e9f854 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml @@ -10,29 +10,65 @@ priority: Low - type: ExtensionCableReceiver - type: PointLight - radius: 1.5 + radius: 1.8 energy: 1.6 color: "#3db83b" + - type: LitOnPowered - type: Sprite sprite: Structures/Machines/arcade.rsi layers: - map: ["computerLayerBody"] state: arcade - map: ["computerLayerScreen"] - state: invaders + state: screen_invaders + - map: ["enum.WiresVisualLayers.MaintenancePanel"] + state: panel + visible: false - type: Icon sprite: Structures/Machines/arcade.rsi state: arcade + - type: WiresPanel + - type: Wires + layoutId: Arcade + boardName: wires-board-name-arcade + - type: WiresVisuals + - type: TypingIndicator + proto: robot + - type: Speech + speechVerb: Robotic + speechSounds: Vending - type: Anchorable - type: Pullable - type: StaticPrice price: 300 + - type: SpamEmitSoundRequirePower + - type: SpamEmitSound + minInterval: 30 + maxInterval: 90 + sound: + collection: ArcadeNoise + params: + volume: -8 + maxDistance: 10 + variation: 0.05 - type: entity id: SpaceVillainArcade name: space villain arcade parent: ArcadeBase components: + - type: Sprite + sprite: Structures/Machines/arcade.rsi + layers: + - map: ["computerLayerBody"] + state: arcade + - map: ["computerLayerScreen"] + state: screen_spacevillain + - map: ["enum.WiresVisualLayers.MaintenancePanel"] + state: panel + visible: false + - type: PointLight + color: "#e3a136" - type: SpaceVillainArcade rewardAmount: 0 possibleRewards: @@ -108,6 +144,10 @@ type: WiresBoundUserInterface - type: Computer board: SpaceVillainArcadeComputerCircuitboard + - type: Advertise + pack: SpaceVillainAds + minWait: 60 # Arcades are noisy + maxWait: 240 - type: entity id: SpaceVillainArcadeFilled @@ -130,15 +170,14 @@ - map: ["computerLayerBody"] state: arcade - map: ["computerLayerScreen"] - state: blockgame + state: screen_blockgame + - map: ["enum.WiresVisualLayers.MaintenancePanel"] + state: panel + visible: false - type: BlockGameArcade - type: ActivatableUI key: enum.BlockGameUiKey.Key - type: ActivatableUIRequiresPower - - type: WiresPanel - - type: Wires - layoutId: Arcade - boardName: wires-board-name-arcade - type: UserInterface interfaces: - key: enum.BlockGameUiKey.Key @@ -147,3 +186,7 @@ type: WiresBoundUserInterface - type: Computer board: BlockGameArcadeComputerCircuitboard + - type: Advertise + pack: BlockGameAds + minWait: 60 # Arcades are noisy + maxWait: 240 diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml b/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml index 23c70d79cc..75c2ce3524 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml @@ -22,7 +22,6 @@ - type: SleepEmitSound snore: /Audio/Voice/Misc/silly_snore.ogg interval: 10 - chance: 1.0 - !type:AddImplantSpecial implants: [ SadTromboneImplant ] diff --git a/Resources/Prototypes/SoundCollections/arcade.yml b/Resources/Prototypes/SoundCollections/arcade.yml new file mode 100644 index 0000000000..40c8a0bc1e --- /dev/null +++ b/Resources/Prototypes/SoundCollections/arcade.yml @@ -0,0 +1,11 @@ +- type: soundCollection + id: ArcadeNoise + files: + - /Audio/Machines/Arcade/hahaha.ogg + - /Audio/Machines/Arcade/pew_pew.ogg + - /Audio/Machines/Arcade/sting_01.ogg + - /Audio/Machines/Arcade/sting_02.ogg + - /Audio/Machines/Arcade/sting_03.ogg + - /Audio/Machines/Arcade/sting_04.ogg + - /Audio/Machines/Arcade/sting_05.ogg + - /Audio/Machines/Arcade/sting_06.ogg diff --git a/Resources/Prototypes/Wires/layouts.yml b/Resources/Prototypes/Wires/layouts.yml index b30e68545d..8d6be674e8 100644 --- a/Resources/Prototypes/Wires/layouts.yml +++ b/Resources/Prototypes/Wires/layouts.yml @@ -87,6 +87,7 @@ id: Arcade wires: - !type:PowerWireAction + - !type:SpeechWireAction - !type:ArcadeOverflowWireAction - !type:ArcadePlayerInvincibleWireAction - !type:ArcadeEnemyInvincibleWireAction diff --git a/Resources/Textures/Structures/Machines/arcade.rsi/meta.json b/Resources/Textures/Structures/Machines/arcade.rsi/meta.json index 63820ec06a..38289e4b07 100644 --- a/Resources/Textures/Structures/Machines/arcade.rsi/meta.json +++ b/Resources/Textures/Structures/Machines/arcade.rsi/meta.json @@ -19,7 +19,11 @@ "directions": 4 }, { - "name": "invaders", + "name": "panel", + "directions": 4 + }, + { + "name": "screen_invaders", "directions": 4, "delays": [ [ @@ -42,7 +46,7 @@ ] }, { - "name": "blockgame", + "name": "screen_blockgame", "directions": 4, "delays": [ [ @@ -82,6 +86,48 @@ 4.8 ] ] + }, + { + "name": "screen_spacevillain", + "directions": 4, + "delays": [ + [ + 1.0, + 0.8, + 0.2, + 0.8, + 0.5, + 1.0, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.5, + 1.0, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.2, + 0.5, + 0.1, + 0.8, + 1.0 + ], + [ + 9.6 + ], + [ + 9.6 + ], + [ + 9.6 + ] + ] } ] } diff --git a/Resources/Textures/Structures/Machines/arcade.rsi/panel.png b/Resources/Textures/Structures/Machines/arcade.rsi/panel.png new file mode 100644 index 0000000000000000000000000000000000000000..fa651565796c0cc436328000349b4641e6c70202 GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=n>}3|Ln>~)y?K`RkO5EIL;fQS z`%+HDNH(e(MZ2FkD%}#S&GdA+AdBEV_f;`V+i&?5ZDIjx1A~VD8-8zXaprh>@%x<5 z%VzPt_epwvK6>U%m6LZ<(?aiL-Jf+t^SRcyC-1yB9?x8rWNvuusffzTFMF6iPhK+n z`@5s@>(;KVcbar6>T-47k?S_uMdy2AMl&!NbOPP$=GF2GIX#m~ a>fZ8bD+X?PUbd$eq}bEd&t;ucLK6V9{9s}L literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Machines/arcade.rsi/blockgame.png b/Resources/Textures/Structures/Machines/arcade.rsi/screen_blockgame.png similarity index 100% rename from Resources/Textures/Structures/Machines/arcade.rsi/blockgame.png rename to Resources/Textures/Structures/Machines/arcade.rsi/screen_blockgame.png diff --git a/Resources/Textures/Structures/Machines/arcade.rsi/invaders.png b/Resources/Textures/Structures/Machines/arcade.rsi/screen_invaders.png similarity index 100% rename from Resources/Textures/Structures/Machines/arcade.rsi/invaders.png rename to Resources/Textures/Structures/Machines/arcade.rsi/screen_invaders.png diff --git a/Resources/Textures/Structures/Machines/arcade.rsi/screen_spacevillain.png b/Resources/Textures/Structures/Machines/arcade.rsi/screen_spacevillain.png new file mode 100644 index 0000000000000000000000000000000000000000..4935ac81b538e77105f84812a80e5fe31e272ab1 GIT binary patch literal 930 zcmeAS@N?(olHy`uVBq!ia0vp^3m6!f8aUX1Ea^YRMhpzhnVv3=Ar-gY-igg?au8t) zFwHrdx8n7iwo^&B+S{fGcsi^ve*S#U68^2n^7R7Pg$rL#%zpR%_jzM^ zyYrt-lO}%peRluczZc%0-#>rxdiPUj*46SZ{j~VGeJx{M&0qF)hp*RHPe1c!*7X1* z`>ML=4S%+6dk{7C*1t1S*vh?7iWt-csFufEFEruxU3{KlR$_nz68^6#iy75VM& z+kf(3pM+Nb+dbdrEI-^@2xB_iZ$|g`&tL2A-&uFy;_shTr}Njz7o3#bI)DB4KWe`j zx3e$!BIWkpz30q)CBM3Bt_E55@n!bOOMp_w#@RFWw>_SJX@b@H&!v@0x42{WtKXV< zUT^Ar+y5OHzFe`h&f#b6)B90>*553($@Ieq@kwuIR?zG3m* z-(M~MujKx4BG~uJF9x;`#4yWWPUk@3j8L zwih+5QyIk*Prg~o{VcGqbJn&OHNbfJwk@G!7wb$Vr_EAU?&t2$`*10{<5c}hd4t{a s^Pc=st=`=>?=vH+@uNIKA@D$Qf9|)Ky?W<=Oa&?RboFyt=akR{06GvVg8%>k literal 0 HcmV?d00001 From 65fa3ae211970acc29da675070417166d35aeecf Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 28 Mar 2024 06:29:51 +0000 Subject: [PATCH 25/56] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 6c593e8495..7b98ec65c9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Repo - changes: - - message: Round restarts require minimum ghost levels on populated servers. - type: Fix - id: 5743 - time: '2024-01-19T07:10:05.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/23453 - author: metalgearsloth changes: - message: Added ability to put spesos directly into cargo request consoles. @@ -3796,3 +3789,12 @@ id: 6242 time: '2024-03-28T05:53:18.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26437 +- author: Tayrtahn + changes: + - message: Arcade machines now advertise and make noise to bring in players. + type: Add + - message: Space Villain arcade machines have a new screen animation. + type: Tweak + id: 6243 + time: '2024-03-28T06:28:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/24200 From 6863a7cc26f815ec9089003e8735777cea927d0f Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Thu, 28 Mar 2024 06:31:47 +0000 Subject: [PATCH 26/56] biggest gridinv update OF ALL TIME (#25834) * add SaveItemLocation keybind * make item direction public to avoid having to change between Angle for no reason * add item location saving * show * Added a better save keybind, made it draw saved positions, and trying to save in a position it has already been saved in removes that position. * w * code style * Make taken spots appear blue * style * ! --------- Co-authored-by: deltanedas <@deltanedas:kde.org> Co-authored-by: notquitehadouken Co-authored-by: I.K <45953835+notquitehadouken@users.noreply.github.com> --- Content.Client/Input/ContentContexts.cs | 1 + .../Options/UI/Tabs/KeyRebindTab.xaml.cs | 1 + .../Storage/Controls/StorageContainer.cs | 35 ++++++ .../Systems/Storage/StorageUIController.cs | 10 ++ Content.Shared/Input/ContentKeyFunctions.cs | 1 + .../EntitySystems/SharedStorageSystem.cs | 111 +++++++++++++++++- Content.Shared/Storage/ItemStorageLocation.cs | 8 +- Content.Shared/Storage/StorageComponent.cs | 22 ++++ .../en-US/escape-menu/ui/options-menu.ftl | 1 + Resources/keybinds.yml | 3 + 10 files changed, 188 insertions(+), 5 deletions(-) diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs index 03f4f3f38b..589de6d6a7 100644 --- a/Content.Client/Input/ContentContexts.cs +++ b/Content.Client/Input/ContentContexts.cs @@ -32,6 +32,7 @@ namespace Content.Client.Input common.AddFunction(ContentKeyFunctions.ToggleFullscreen); common.AddFunction(ContentKeyFunctions.MoveStoredItem); common.AddFunction(ContentKeyFunctions.RotateStoredItem); + common.AddFunction(ContentKeyFunctions.SaveItemLocation); common.AddFunction(ContentKeyFunctions.Point); common.AddFunction(ContentKeyFunctions.ZoomOut); common.AddFunction(ContentKeyFunctions.ZoomIn); diff --git a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs index f0537079b9..aca9efcfe2 100644 --- a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs @@ -183,6 +183,7 @@ namespace Content.Client.Options.UI.Tabs AddButton(ContentKeyFunctions.SwapHands); AddButton(ContentKeyFunctions.MoveStoredItem); AddButton(ContentKeyFunctions.RotateStoredItem); + AddButton(ContentKeyFunctions.SaveItemLocation); AddHeader("ui-options-header-interaction-adv"); AddButton(ContentKeyFunctions.SmartEquipBackpack); diff --git a/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs b/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs index bd952fe957..e39ac5d322 100644 --- a/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs +++ b/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs @@ -12,6 +12,7 @@ using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.Timing; +using Robust.Shared.Utility; namespace Content.Client.UserInterface.Systems.Storage.Controls; @@ -355,6 +356,40 @@ public sealed class StorageContainer : BaseWindow origin, currentLocation.Rotation); + foreach (var locations in storageComponent.SavedLocations) + { + if (!_entity.TryGetComponent(currentEnt, out var meta) || meta.EntityName != locations.Key) + continue; + + float spot = 0; + var marked = new List(); + + foreach (var location in locations.Value) + { + var shape = itemSystem.GetAdjustedItemShape(currentEnt, location); + var bound = shape.GetBoundingBox(); + + var spotFree = storageSystem.ItemFitsInGridLocation(currentEnt, StorageEntity.Value, location); + + if (spotFree) + spot++; + + for (var y = bound.Bottom; y <= bound.Top; y++) + { + for (var x = bound.Left; x <= bound.Right; x++) + { + if (TryGetBackgroundCell(x, y, out var cell) && shape.Contains(x, y) && !marked.Contains(cell)) + { + marked.Add(cell); + cell.ModulateSelfOverride = spotFree + ? Color.FromHsv((0.18f, 1 / spot, 0.5f / spot + 0.5f, 1f)) + : Color.FromHex("#2222CC"); + } + } + } + } + } + var validColor = usingInHand ? Color.Goldenrod : Color.FromHex("#1E8000"); for (var y = itemBounding.Bottom; y <= itemBounding.Top; y++) diff --git a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs index 5f3ae3a2da..b865b54dd0 100644 --- a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs +++ b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs @@ -240,6 +240,16 @@ public sealed class StorageUIController : UIController, IOnSystemChanged().DoExamine(control.Entity); diff --git a/Content.Shared/Input/ContentKeyFunctions.cs b/Content.Shared/Input/ContentKeyFunctions.cs index ee4a4e9023..cf874434ec 100644 --- a/Content.Shared/Input/ContentKeyFunctions.cs +++ b/Content.Shared/Input/ContentKeyFunctions.cs @@ -36,6 +36,7 @@ namespace Content.Shared.Input public static readonly BoundKeyFunction SwapHands = "SwapHands"; public static readonly BoundKeyFunction MoveStoredItem = "MoveStoredItem"; public static readonly BoundKeyFunction RotateStoredItem = "RotateStoredItem"; + public static readonly BoundKeyFunction SaveItemLocation = "SaveItemLocation"; public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand"; public static readonly BoundKeyFunction TryPullObject = "TryPullObject"; public static readonly BoundKeyFunction MovePulledObject = "MovePulledObject"; diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index ef1b2b7c44..9d364dded0 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -97,6 +97,7 @@ public abstract class SharedStorageSystem : EntitySystem SubscribeAllEvent(OnSetItemLocation); SubscribeAllEvent(OnInsertItemIntoLocation); SubscribeAllEvent(OnRemoveItem); + SubscribeAllEvent(OnSaveItemLocation); SubscribeLocalEvent(OnReclaimed); @@ -117,7 +118,8 @@ public abstract class SharedStorageSystem : EntitySystem Grid = new List(component.Grid), IsUiOpen = component.IsUiOpen, MaxItemSize = component.MaxItemSize, - StoredItems = storedItems + StoredItems = storedItems, + SavedLocations = component.SavedLocations }; } @@ -138,6 +140,8 @@ public abstract class SharedStorageSystem : EntitySystem var ent = EnsureEntity(nent, uid); component.StoredItems[ent] = location; } + + component.SavedLocations = state.SavedLocations; } public override void Shutdown() @@ -536,6 +540,35 @@ public abstract class SharedStorageSystem : EntitySystem InsertAt((storageEnt, storageComp), (itemEnt, null), msg.Location, out _, player, stackAutomatically: false); } + // TODO: if/when someone cleans up this shitcode please make all these + // handlers use a shared helper for checking that the ui is open etc, thanks + private void OnSaveItemLocation(StorageSaveItemLocationEvent msg, EntitySessionEventArgs args) + { + if (args.SenderSession.AttachedEntity is not {} player) + return; + + var storage = GetEntity(msg.Storage); + var item = GetEntity(msg.Item); + + if (!TryComp(storage, out var storageComp)) + return; + + if (!_ui.TryGetUi(storage, StorageComponent.StorageUiKey.Key, out var bui) || + !bui.SubscribedSessions.Contains(args.SenderSession)) + return; + + if (!Exists(item)) + { + Log.Error($"Player {args.SenderSession} saved location of non-existent item {msg.Item} stored in {ToPrettyString(storage)}"); + return; + } + + if (!ActionBlocker.CanInteract(player, item)) + return; + + SaveItemLocation(storage, item); + } + private void OnBoundUIOpen(EntityUid uid, StorageComponent storageComp, BoundUIOpenedEvent args) { if (!storageComp.IsUiOpen) @@ -945,6 +978,10 @@ public abstract class SharedStorageSystem : EntitySystem if (!Resolve(storageEnt, ref storageEnt.Comp) || !Resolve(itemEnt, ref itemEnt.Comp)) return false; + // if the item has an available saved location, use that + if (FindSavedLocation(storageEnt, itemEnt, out storageLocation)) + return true; + var storageBounding = storageEnt.Comp.Grid.GetBoundingBox(); Angle startAngle; @@ -987,6 +1024,76 @@ public abstract class SharedStorageSystem : EntitySystem return false; } + ///

+ /// Tries to find a saved location for an item from its name. + /// If none are saved or they are all blocked nothing is returned. + /// + public bool FindSavedLocation( + Entity ent, + Entity item, + [NotNullWhen(true)] out ItemStorageLocation? storageLocation) + { + storageLocation = null; + if (!Resolve(ent, ref ent.Comp)) + return false; + + var name = Name(item); + if (!ent.Comp.SavedLocations.TryGetValue(name, out var list)) + return false; + + foreach (var location in list) + { + if (ItemFitsInGridLocation(item, ent, location)) + { + storageLocation = location; + return true; + } + } + + return false; + } + + /// + /// Saves an item's location in the grid for later insertion to use. + /// + public void SaveItemLocation(Entity ent, Entity item) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + // needs to actually be stored in it somewhere to save it + if (!ent.Comp.StoredItems.TryGetValue(item, out var location)) + return; + + var name = Name(item, item.Comp); + if (ent.Comp.SavedLocations.TryGetValue(name, out var list)) + { + // iterate to make sure its not already been saved + for (int i = 0; i < list.Count; i++) + { + var saved = list[i]; + + if (saved == location) + { + list.Remove(location); + return; + } + } + + list.Add(location); + } + else + { + list = new List() + { + location + }; + ent.Comp.SavedLocations[name] = list; + } + + Dirty(ent, ent.Comp); + } + /// /// Checks if an item fits into a specific spot on a storage grid. /// @@ -1165,6 +1272,8 @@ public abstract class SharedStorageSystem : EntitySystem public Dictionary StoredItems = new(); + public Dictionary> SavedLocations = new(); + public List Grid = new(); public ProtoId? MaxItemSize; diff --git a/Content.Shared/Storage/ItemStorageLocation.cs b/Content.Shared/Storage/ItemStorageLocation.cs index a43be5a44f..25d55e1e1a 100644 --- a/Content.Shared/Storage/ItemStorageLocation.cs +++ b/Content.Shared/Storage/ItemStorageLocation.cs @@ -8,16 +8,16 @@ public partial record struct ItemStorageLocation /// /// The rotation, stored a cardinal direction in order to reduce rounding errors. /// - [DataField] - private Direction _rotation; + [DataField("_rotation")] + public Direction Direction; /// /// The rotation of the piece in storage. /// public Angle Rotation { - get => _rotation.ToAngle(); - set => _rotation = value.GetCardinalDir(); + get => Direction.ToAngle(); + set => Direction = value.GetCardinalDir(); } /// diff --git a/Content.Shared/Storage/StorageComponent.cs b/Content.Shared/Storage/StorageComponent.cs index 98e80de025..2cae12f07a 100644 --- a/Content.Shared/Storage/StorageComponent.cs +++ b/Content.Shared/Storage/StorageComponent.cs @@ -32,6 +32,14 @@ namespace Content.Shared.Storage [DataField, ViewVariables(VVAccess.ReadWrite)] public Dictionary StoredItems = new(); + /// + /// A dictionary storing each saved item to its location in the grid. + /// When trying to quick insert an item, if there is an empty location with the same name it will be placed there. + /// Multiple items with the same name can be saved, they will be checked individually. + /// + [DataField] + public Dictionary> SavedLocations = new(); + /// /// A list of boxes that comprise a combined grid that determines the location that items can be stored. /// @@ -171,6 +179,20 @@ namespace Content.Shared.Storage } } + [Serializable, NetSerializable] + public sealed class StorageSaveItemLocationEvent : EntityEventArgs + { + public readonly NetEntity Item; + + public readonly NetEntity Storage; + + public StorageSaveItemLocationEvent(NetEntity item, NetEntity storage) + { + Item = item; + Storage = storage; + } + } + /// /// Network event for displaying an animation of entities flying into a storage entity diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl index 46f5df48ad..ff56d54274 100644 --- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl +++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl @@ -135,6 +135,7 @@ ui-options-function-examine-entity = Examine ui-options-function-swap-hands = Swap hands ui-options-function-move-stored-item = Move stored item ui-options-function-rotate-stored-item = Rotate stored item +ui-options-function-save-item-location = Save item location ui-options-static-storage-ui = Lock storage window to hotbar ui-options-function-smart-equip-backpack = Smart-equip to backpack diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index 346156159a..886fec35de 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -171,6 +171,9 @@ binds: - function: RotateStoredItem type: State key: MouseRight +- function: SaveItemLocation + type: State + key: MouseMiddle - function: Drop type: State key: Q From d576f5cbbb3969d1399a25b9692c16b63bbf2a46 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Thu, 28 Mar 2024 02:32:56 -0400 Subject: [PATCH 27/56] dynamic alert sprites (#25452) * dynamic alert sprite * fix fat cooldowns --- .../Alerts/UpdateAlertSpriteEvent.cs | 21 ++++++ Content.Client/Revenant/RevenantSystem.cs | 15 +++++ .../Systems/Alerts/AlertsUIController.cs | 16 +++++ .../Systems/Alerts/Controls/AlertControl.cs | 60 ++++++++++++++---- .../Revenant/EntitySystems/RevenantSystem.cs | 3 +- Content.Shared/Alert/AlertPrototype.cs | 14 ++-- .../Revenant/Components/RevenantComponent.cs | 4 +- Content.Shared/Revenant/SharedRevenant.cs | 8 +++ Resources/Prototypes/Alerts/alerts.yml | 8 +++ Resources/Prototypes/Alerts/revenant.yml | 51 +++++---------- .../Alerts/essence_counter.rsi/0.png | Bin 0 -> 155 bytes .../Alerts/essence_counter.rsi/1.png | Bin 0 -> 150 bytes .../Alerts/essence_counter.rsi/2.png | Bin 0 -> 164 bytes .../Alerts/essence_counter.rsi/3.png | Bin 0 -> 156 bytes .../Alerts/essence_counter.rsi/4.png | Bin 0 -> 156 bytes .../Alerts/essence_counter.rsi/5.png | Bin 0 -> 168 bytes .../Alerts/essence_counter.rsi/6.png | Bin 0 -> 167 bytes .../Alerts/essence_counter.rsi/7.png | Bin 0 -> 154 bytes .../Alerts/essence_counter.rsi/8.png | Bin 0 -> 156 bytes .../Alerts/essence_counter.rsi/9.png | Bin 0 -> 164 bytes .../Alerts/essence_counter.rsi/essence0.png | Bin 319 -> 255 bytes .../Alerts/essence_counter.rsi/essence1.png | Bin 298 -> 0 bytes .../Alerts/essence_counter.rsi/essence10.png | Bin 308 -> 0 bytes .../Alerts/essence_counter.rsi/essence11.png | Bin 304 -> 0 bytes .../Alerts/essence_counter.rsi/essence12.png | Bin 311 -> 0 bytes .../Alerts/essence_counter.rsi/essence13.png | Bin 311 -> 0 bytes .../Alerts/essence_counter.rsi/essence14.png | Bin 321 -> 0 bytes .../Alerts/essence_counter.rsi/essence15.png | Bin 307 -> 0 bytes .../Alerts/essence_counter.rsi/essence16.png | Bin 313 -> 0 bytes .../Alerts/essence_counter.rsi/essence2.png | Bin 297 -> 0 bytes .../Alerts/essence_counter.rsi/essence3.png | Bin 299 -> 0 bytes .../Alerts/essence_counter.rsi/essence4.png | Bin 321 -> 0 bytes .../Alerts/essence_counter.rsi/essence5.png | Bin 302 -> 0 bytes .../Alerts/essence_counter.rsi/essence6.png | Bin 297 -> 0 bytes .../Alerts/essence_counter.rsi/essence7.png | Bin 317 -> 0 bytes .../Alerts/essence_counter.rsi/essence8.png | Bin 295 -> 0 bytes .../Alerts/essence_counter.rsi/essence9.png | Bin 292 -> 0 bytes .../Alerts/essence_counter.rsi/meta.json | 38 +++-------- 38 files changed, 154 insertions(+), 84 deletions(-) create mode 100644 Content.Client/Alerts/UpdateAlertSpriteEvent.cs create mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/0.png create mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/1.png create mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/2.png create mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/3.png create mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/4.png create mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/5.png create mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/6.png create mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/7.png create mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/8.png create mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/9.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence1.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence10.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence11.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence12.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence13.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence14.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence15.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence16.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence2.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence3.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence4.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence5.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence6.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence7.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence8.png delete mode 100644 Resources/Textures/Interface/Alerts/essence_counter.rsi/essence9.png diff --git a/Content.Client/Alerts/UpdateAlertSpriteEvent.cs b/Content.Client/Alerts/UpdateAlertSpriteEvent.cs new file mode 100644 index 0000000000..4f182c458c --- /dev/null +++ b/Content.Client/Alerts/UpdateAlertSpriteEvent.cs @@ -0,0 +1,21 @@ +using Content.Shared.Alert; +using Robust.Client.GameObjects; + +namespace Content.Client.Alerts; + +/// +/// Event raised on an entity with alerts in order to allow it to update visuals for the alert sprite entity. +/// +[ByRefEvent] +public record struct UpdateAlertSpriteEvent +{ + public Entity SpriteViewEnt; + + public AlertPrototype Alert; + + public UpdateAlertSpriteEvent(Entity spriteViewEnt, AlertPrototype alert) + { + SpriteViewEnt = spriteViewEnt; + Alert = alert; + } +} diff --git a/Content.Client/Revenant/RevenantSystem.cs b/Content.Client/Revenant/RevenantSystem.cs index 6e7d0d2a1b..49d29d8a5f 100644 --- a/Content.Client/Revenant/RevenantSystem.cs +++ b/Content.Client/Revenant/RevenantSystem.cs @@ -1,3 +1,5 @@ +using Content.Client.Alerts; +using Content.Shared.Alert; using Content.Shared.Revenant; using Content.Shared.Revenant.Components; using Robust.Client.GameObjects; @@ -13,6 +15,7 @@ public sealed class RevenantSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnAppearanceChange); + SubscribeLocalEvent(OnUpdateAlert); } private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref AppearanceChangeEvent args) @@ -36,4 +39,16 @@ public sealed class RevenantSystem : EntitySystem args.Sprite.LayerSetState(0, component.State); } } + + private void OnUpdateAlert(Entity ent, ref UpdateAlertSpriteEvent args) + { + if (args.Alert.AlertType != AlertType.Essence) + return; + + var sprite = args.SpriteViewEnt.Comp; + var essence = Math.Clamp(ent.Comp.Essence.Int(), 0, 999); + sprite.LayerSetState(RevenantVisualLayers.Digit1, $"{(essence / 100) % 10}"); + sprite.LayerSetState(RevenantVisualLayers.Digit2, $"{(essence / 10) % 10}"); + sprite.LayerSetState(RevenantVisualLayers.Digit3, $"{essence % 10}"); + } } diff --git a/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs b/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs index 826bbf199b..3b85972a9b 100644 --- a/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs +++ b/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs @@ -3,6 +3,8 @@ using Content.Client.Gameplay; using Content.Client.UserInterface.Systems.Alerts.Widgets; using Content.Client.UserInterface.Systems.Gameplay; using Content.Shared.Alert; +using Robust.Client.GameObjects; +using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; @@ -10,6 +12,8 @@ namespace Content.Client.UserInterface.Systems.Alerts; public sealed class AlertsUIController : UIController, IOnStateEntered, IOnSystemChanged { + [Dependency] private readonly IPlayerManager _player = default!; + [UISystemDependency] private readonly ClientAlertsSystem? _alertsSystem = default; private AlertsUI? UI => UIManager.GetActiveUIWidgetOrNull(); @@ -84,4 +88,16 @@ public sealed class AlertsUIController : UIController, IOnStateEntered(spriteViewEnt, out var sprite)) + return; + + var ev = new UpdateAlertSpriteEvent((spriteViewEnt, sprite), alert); + EntityManager.EventBus.RaiseLocalEvent(player, ref ev); + } } diff --git a/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs b/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs index 4445bb0cd0..9423f7288d 100644 --- a/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs +++ b/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs @@ -2,6 +2,8 @@ using Content.Client.Actions.UI; using Content.Client.Cooldown; using Content.Shared.Alert; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; @@ -33,9 +35,12 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls private short? _severity; private readonly IGameTiming _gameTiming; - private readonly AnimatedTextureRect _icon; + private readonly IEntityManager _entityManager; + private readonly SpriteView _icon; private readonly CooldownGraphic _cooldownGraphic; + private EntityUid _spriteViewEntity; + /// /// Creates an alert control reflecting the indicated alert + state /// @@ -44,19 +49,30 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls public AlertControl(AlertPrototype alert, short? severity) { _gameTiming = IoCManager.Resolve(); + _entityManager = IoCManager.Resolve(); TooltipSupplier = SupplyTooltip; Alert = alert; _severity = severity; - var specifier = alert.GetIcon(_severity); - _icon = new AnimatedTextureRect - { - DisplayRect = {TextureScale = new Vector2(2, 2)} - }; - _icon.SetFromSpriteSpecifier(specifier); + _spriteViewEntity = _entityManager.Spawn(Alert.AlertViewEntity); + if (_entityManager.TryGetComponent(_spriteViewEntity, out var sprite)) + { + var icon = Alert.GetIcon(_severity); + if (sprite.LayerMapTryGet(AlertVisualLayers.Base, out var layer)) + sprite.LayerSetSprite(layer, icon); + } + + _icon = new SpriteView + { + Scale = new Vector2(2, 2) + }; + _icon.SetEntity(_spriteViewEntity); Children.Add(_icon); - _cooldownGraphic = new CooldownGraphic(); + _cooldownGraphic = new CooldownGraphic + { + MaxSize = new Vector2(64, 64) + }; Children.Add(_cooldownGraphic); } @@ -72,16 +88,22 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls /// public void SetSeverity(short? severity) { - if (_severity != severity) - { - _severity = severity; - _icon.SetFromSpriteSpecifier(Alert.GetIcon(_severity)); - } + if (_severity == severity) + return; + _severity = severity; + + if (!_entityManager.TryGetComponent(_spriteViewEntity, out var sprite)) + return; + var icon = Alert.GetIcon(_severity); + if (sprite.LayerMapTryGet(AlertVisualLayers.Base, out var layer)) + sprite.LayerSetSprite(layer, icon); } protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); + UserInterfaceManager.GetUIController().UpdateAlertSpriteEntity(_spriteViewEntity, Alert); + if (!Cooldown.HasValue) { _cooldownGraphic.Visible = false; @@ -91,5 +113,17 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls _cooldownGraphic.FromTime(Cooldown.Value.Start, Cooldown.Value.End); } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + _entityManager.DeleteEntity(_spriteViewEntity); + } + } + + public enum AlertVisualLayers : byte + { + Base } } diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index b26cc4a320..86be70c41f 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -133,6 +133,7 @@ public sealed partial class RevenantSystem : EntitySystem return false; component.Essence += amount; + Dirty(uid, component); if (regenCap) FixedPoint2.Min(component.Essence, component.EssenceRegenCap); @@ -140,7 +141,7 @@ public sealed partial class RevenantSystem : EntitySystem if (TryComp(uid, out var store)) _store.UpdateUserInterface(uid, uid, store); - _alerts.ShowAlert(uid, AlertType.Essence, (short) Math.Clamp(Math.Round(component.Essence.Float() / 10f), 0, 16)); + _alerts.ShowAlert(uid, AlertType.Essence); if (component.Essence <= 0) { diff --git a/Content.Shared/Alert/AlertPrototype.cs b/Content.Shared/Alert/AlertPrototype.cs index ac43cb45f3..248cc00ba4 100644 --- a/Content.Shared/Alert/AlertPrototype.cs +++ b/Content.Shared/Alert/AlertPrototype.cs @@ -25,6 +25,12 @@ namespace Content.Shared.Alert [DataField("icons", required: true)] public List Icons = new(); + /// + /// An entity used for displaying the in the UI control. + /// + [DataField] + public EntProtoId AlertViewEntity = "AlertSpriteView"; + /// /// Name to show in tooltip window. Accepts formatting. /// @@ -83,13 +89,9 @@ namespace Content.Shared.Alert /// the icon path to the texture for the provided severity level public SpriteSpecifier GetIcon(short? severity = null) { - if (!SupportsSeverity && severity != null) - { - throw new InvalidOperationException($"This alert ({AlertKey}) does not support severity"); - } - var minIcons = SupportsSeverity - ? MaxSeverity - MinSeverity : 1; + ? MaxSeverity - MinSeverity + : 1; if (Icons.Count < minIcons) throw new InvalidOperationException($"Insufficient number of icons given for alert {AlertType}"); diff --git a/Content.Shared/Revenant/Components/RevenantComponent.cs b/Content.Shared/Revenant/Components/RevenantComponent.cs index b3d47e22f9..947c1a4b3f 100644 --- a/Content.Shared/Revenant/Components/RevenantComponent.cs +++ b/Content.Shared/Revenant/Components/RevenantComponent.cs @@ -9,13 +9,15 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy namespace Content.Shared.Revenant.Components; [RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState] public sealed partial class RevenantComponent : Component { /// /// The total amount of Essence the revenant has. Functions /// as health and is regenerated. /// - [ViewVariables(VVAccess.ReadWrite)] + [DataField, ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] public FixedPoint2 Essence = 75; [DataField("stolenEssenceCurrencyPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] diff --git a/Content.Shared/Revenant/SharedRevenant.cs b/Content.Shared/Revenant/SharedRevenant.cs index c44e4408aa..485ad26dd2 100644 --- a/Content.Shared/Revenant/SharedRevenant.cs +++ b/Content.Shared/Revenant/SharedRevenant.cs @@ -70,3 +70,11 @@ public enum RevenantVisuals : byte Stunned, Harvesting, } + +[NetSerializable, Serializable] +public enum RevenantVisualLayers : byte +{ + Digit1, + Digit2, + Digit3 +} diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index 2d1c9062e6..e9a7f9c958 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -25,6 +25,14 @@ - alertType: Magboots - alertType: Pacified +- type: entity + id: AlertSpriteView + categories: [ hideSpawnMenu ] + components: + - type: Sprite + layers: + - map: [ "enum.AlertVisualLayers.Base" ] + - type: alert id: LowOxygen category: Breathing diff --git a/Resources/Prototypes/Alerts/revenant.yml b/Resources/Prototypes/Alerts/revenant.yml index d859cd60f1..7f3f98949e 100644 --- a/Resources/Prototypes/Alerts/revenant.yml +++ b/Resources/Prototypes/Alerts/revenant.yml @@ -4,45 +4,26 @@ icons: - sprite: /Textures/Interface/Alerts/essence_counter.rsi state: essence0 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence1 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence2 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence3 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence4 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence5 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence6 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence7 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence8 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence9 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence10 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence11 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence12 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence13 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence14 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence15 - - sprite: /Textures/Interface/Alerts/essence_counter.rsi - state: essence16 + alertViewEntity: AlertEssenceSpriteView name: alerts-revenant-essence-name description: alerts-revenant-essence-desc - minSeverity: 0 - maxSeverity: 16 - type: alert id: Corporeal icons: [ /Textures/Mobs/Ghosts/revenant.rsi/icon.png ] name: alerts-revenant-corporeal-name - description: alerts-revenant-corporeal-desc \ No newline at end of file + description: alerts-revenant-corporeal-desc + +- type: entity + id: AlertEssenceSpriteView + categories: [ hideSpawnMenu ] + components: + - type: Sprite + sprite: /Textures/Interface/Alerts/essence_counter.rsi + layers: + - map: [ "enum.AlertVisualLayers.Base" ] + - map: [ "enum.RevenantVisualLayers.Digit1" ] + - map: [ "enum.RevenantVisualLayers.Digit2" ] + offset: 0.125, 0 + - map: [ "enum.RevenantVisualLayers.Digit3" ] + offset: 0.25, 0 diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/0.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/0.png new file mode 100644 index 0000000000000000000000000000000000000000..760a92bfe12905d0bc4509ff9cd19b3021163512 GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}p`I>|ArY-_ zuQ+lw81T4Ul>hiIyzlmLCXp2v4StLMzs4}>51+I~N8@^+HU>{uKbLh*2~7a{$TN8W literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/2.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/2.png new file mode 100644 index 0000000000000000000000000000000000000000..9807aa8161b8806f2ff4b123be9313fa743c1287 GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}ah@)YArY-_ zFFA5G81T4Uw14z>z0JCopw$XDZXDec>#g`wML-Y;?1QT}ygkkA`S@_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/3.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/3.png new file mode 100644 index 0000000000000000000000000000000000000000..a1776e8efdf319b56555ddfbdcca124bf169cc70 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}VV*9IArY-_ zFFA5G81T4Ul>hiITyFc(RSFrqe&$~f^04Ccn52@qQHtNw$acl5&v&PP>uySRuD`~X z@O$r}+ia7z{Q0x3F|XhHLSiM;>GA`DKAa`T^c$F8MTm*ctKISfXaj?%tDnm{r-UW| DDrPyt literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/4.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/4.png new file mode 100644 index 0000000000000000000000000000000000000000..a1b8c7013ef70babf386586ca963c9ce79680765 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}VV*9IArY-_ zFB|e5P~c&4wEqA9^Tj0|8(y9WOj+)zpc1yJ1E}gi;?FxCK|3eR{Cv|d{PTnCzfqh8 z$_sc4&elsl2w_rj{VZ+ESh;zx9s8UOJOzCZ@3R$4@ygkExHbXxGI+ZBxvXqop`gR3yJOLZJCn5@F0-j@toU|M`bCJ;hPzhG>+f$k{7qehv*egOgSec5uyk*QU>(pl N22WQ%mvv4FO#rzxIBfs` literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/6.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/6.png new file mode 100644 index 0000000000000000000000000000000000000000..1222a73b5e96afd7bf2b51ac48acba13c13e1cfc GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}iJmTwArY-_ zFB|e5P~dUC_teaQ$C3KLh}pwii?OC8qynD`0fZ7m&H@WxWn) O8H1;*pUXO@geCyOJ2}w+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/7.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/7.png new file mode 100644 index 0000000000000000000000000000000000000000..e6bef33ed5b8ff016f63df81a74ea10523f62063 GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}A)YRdArY-_ zFB|eTD2T9JjD7rf{T?$hmW>7z{SWTpbKc~S0|2$y>@2s7= zo>t5BYj{0^K7C4T$WMJ;dw`2Y;Dc1aPwjVanatY+#E+d<3jvzX;OXk;vd$@?2>>mA BHVFU# literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/8.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/8.png new file mode 100644 index 0000000000000000000000000000000000000000..c72dbb5433dce2a4ea26a135c9715c4ac5434e74 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}VV*9IArY-_ zuQ+lw81T4Ul>hiIyzlm=vE-$6CO!X@P+FefdOApbZS3u6{1-oD!M< DF~K+W literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/9.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/9.png new file mode 100644 index 0000000000000000000000000000000000000000..970eeb9740e5abc5e92be635af162c3da366fb7c GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}ah@)YArY-_ zFFSG_P~dU7IRDb`_{v?P%3@x^=AX;DCK$RI7#kaJKAaTe5o9GG`1#)acdiEw15$Gt z)_vlNXDYcbP0l+XkK{+u{n literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence0.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence0.png index 361fa844e1614ec52db3e4a1f0062ba726358cab..3523928f3cd11fe2cdc777c699097797e696ebc1 100644 GIT binary patch delta 87 zcmdnb^q+BpA>+=8Mor#Irx>@a5oF&ZBT>M9VU2NA-Q*z3_#%N>gTe~DWM4fN$nkK delta 152 zcmV;J0B8UI0lxx}F#-9JGJ=0XG35XM|L~N}$jHcu%*SZ}BO^r)!AOK_`1mmRFpMmZ zu8wLBpf?~Y4TOC9gww~wG}|a@k`V&-T1E_Nyu3K&DQ=R{8j$4xViO=mO)_G#ASo7+ zPx#s9@z)Y1w|6Uxo zijeY+v1&gz?yG@9X53uxyuZp4MQ*RlWSuR0_;jpI#qW=1HojpAK5c6fz@P#w#E7R? zkdjn`S^xkv?`J*%mG@{cKiL{qoq-}ChNK|`xJ$;)NRj~Nj?Dc+7QX_mPZT4{ZbUyx zL`41ncg~rF4BkMPuhY|=iO6W&0{se%4N^vDAndf?b^k18Z8NQ8jCyxK<^5#byppZW w|E*-Hg;WCAjPFJ%I<`PFPpktd1d_0y#N3J07*qoM6N<$f(8nIg8%>k diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence10.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence10.png deleted file mode 100644 index 62557685d960278e7a3f3f7d193810b3b8227678..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmV-40n7f0P)Px#?ny*JR9J=WmOT!_FbsvC5o@l|fejYMo`#8$b1*V-nvN{6F>s9@z$_Gn{={+I zDniOP#f@seH13muLS`tmdEB0Pjv|-GX|m2*dik)gO~tRbCb++n61>{hBtSwGU?E1l zcm*j*HK+vuK=Zuk6Hqya2I0=uuxJky0Wl;sAt1YCY>y-fklaynKcCsJKPx#>PbXFR9J=WRxu93FbuR2YrfHe4Hm|}hKZ4PFf#F)j4ZG*@QpqoOp#2J#&Mh~ zLdtEeRo^+eo(#~@7?>)a_g7bgof6X=h8Yst3A&92A)lfKSO>j}!^u(veF)it1M&&50gF&4Vat z;+zxh0Enm(@^b>7-1qca=riRLYKtmh=@`TjJpF2 z#w^y=E7{ukzm=?%sHy;JN^L4egGiDC+8}D4gdJZyfMj*-XyteS0000Px#@kvBMR9J=WRxu93Fbq5+)_kJ_8!U`{4HF~pU}WMo8ChUs;2V8_Sttr>6UTL{ z2q`zkvSMFi`(&V^Fev3bZqGbNlFQ>1&9jy+ANH*&`Ss?L{*@Hq)paHU5<~(P=dIF_Le%&Urh5(dDZs-obz9hp7-|3j002ov JPDHLkV1mhjdUyZ; diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence13.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence13.png deleted file mode 100644 index d853af038e32b3de7df83d52e0e023a249975208..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 311 zcmV-70m%M|P)Px#@kvBMR9J=WRk>+{v*4auwpN_Su`0d^X;~Oc#yX#B@B!~noh#@bw zASI~=Y61Yzz8?7qRL-GASlJq^s)0fv21z0WWS5N9ND=|b9VPc0WAPSfeWDmqb|ac3 zVP@3~Ktv`YKR3`9?{hLU#`)*=R!m-j@jz-!G~hd>?Px#`$$D@pip&HNV}<-uz4}@UAKs0Szhv4=m)> z5ez9BgJ=Q(P`)115m*!jO7tr`2BT^q6R=>A2m#$Ctr{6bKyydU{Unl)Al4_c5lwDH zjUvn(wF3~5i%3m`p*ayD)NMOu-iyXxkgBj14Gl1}NPx#?MXyIR9J=WR=X9#Fbq8LXtM@A6(!S9Q8EW571Ow+fsTSTGJt4sp24wYMaeUT zuktU_*}B{l&`=qa+dOX1JV%ntPx#^GQTOR9J=WmZ1*9Fcd{^33h&Z0*^wX_9-YN-+@H&DN~gga0tZo{63(ll9Hu; z?RzCf^dw8Owd=ddySpn;DU1qD9=B(cqsZlP^43{XFCTV$QSs|d2j^GR2CsP*37{Ya zNQek*a!DjlixbIIO<)SRe6 zR6K|j02pJ$|9zztOVG!JPx#<4Ht8R9J=WmOTyvAryoMW6L$Ru+zqJPh(~2IV`O_4W*5C7G7fy@T}IXKQO?< zpb1~H1$p5yz+@JnQ5h2|ZkKy0vB=@F^R6>zlQ-*LwfOPmg7q0|a2p1d00tGHAx1pC zf}Ex)BntolcRkk=&{+$Iu(MP6st1~Y7_v+V@RW@8NR|MWjx7DA6u*MhoM=H*J&2x~ zh)DAP-x#A3@^%8#ej-B0T7EN#raK*X={t#-XPfFt#^@I-bk=(7ER?7KrN=1$@g!qz v*QDb|0k6j%GGxhA9E0fbMUGWePr}9>MaynpeLAFW00000NkvXXu0mjfrcZi= diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence3.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence3.png deleted file mode 100644 index 7d22c2a9b6fd6abece4f214ea51257ada2d50535..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmV+`0o4A9P)Px#(FbqW<)HT=Wz(yCwo`#8$b1*V-nv5*4F>s9@z)VF!H2HBH zln!}Pht#V5lUTndYE;I=g2&yNOBA_0PQf}I`uMOPRmHD27maUFg4Z;u1R$UQ4JqQ` z733t9AQnJG0(xu7QM|Jy{Iv&~fE2QtkO;eEYL8?IK<{Cvy|*IL$TME*|aU4~8~Wo%Q8WQcJ)P;YH8j!X#(Q1%#P{Ug~@ x59AaQ3Gj<8753kibj8_-K2PFWMRg}^ya8MLYh*4q0I&c6002ovPDHLkV1nTIdgTBB diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence4.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence4.png deleted file mode 100644 index 98f6fc0764a46fe530dad69e6106a0ca8b4f1873..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 321 zcmV-H0lxl;P)Px#`$?00~0G zvn5Cg8iQy70MI_W`259gPhGmS&?7MggCMV#EY4$VZEEwSplPx#=t)FDR9J=WmN5>(FbqY15Noc{fejYMo`#8$b1*V-nv5*4F>s9@zznD;h&Dfo zgTjy}bx5q*zlrs0pivnU3hviODN*EfJIvNuvX^(;URC^jal!b4C3p;jN&tfj&=4b@ zUO`UM6p{r1fV*Go3FxeaL%6e3_-PL`0WoAXA;4WSwnwrAFn46`=TrO&QhlNsQFSBw zNFpN1|9@kQO32p<`1wSHuC@H95wS1z<1u5KY9wRys|`A9XXEOT40!d0ve$G1aw#&B zjrSiq0gXFO3h;|f754W^y5eX=?Px#<4Ht8R9J=WmN5>(FbqY15Noc{fejYMo`#8$b1*V-nv5*4F>s9@zzhf!S~ZUC zpfKb~9U`mtZ~OIYpivo@D(=@uDlz1AJIu~mayRd`y=nOQ5|Z@=*WfV>CIK9*01G+d z*-xNpDnTp&0K)xRPeA8A0@BWw@KX;o0Xe8l2=FPH>k*XzE*-h_iz)sIq&d-osCp26 zH6bGL|KB-h5;A!MaX%tr&sP33h`1+>>p5$iX(i+6R~vNR&(_T=8SwIj(Q7sVT8gb? vV^3%Xi3E&?rOqW&@f}2;EAwokdJ;CCIOS_%*kMnp00000NkvXXu0mjfc%gRF diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence7.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence7.png deleted file mode 100644 index febaf3d09c9d93c84aaf9e063739c2028df398cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317 zcmV-D0mA-?P)Px#_en%SR9J;$U>F4h3>fiAW*heYr-mg{&llsghlP;lRC9ZZET4VyAYOSULI&LW z{Fx#L5CQ}pKnWnm@dTrQ979O~Bs+kCfq~)RwQ~dK05x7-hJy_FQ#L7prfFc*0mJ~Z z5+MTv;Zl;=G(wgG2$hb6Nmj61b6V-IfhUM2fXrB1JHs9j$s88OdOXyB>*D?aG41cr$*s33XlN+4L)^F=lYI$ P00000NkvXXu0mjfqMUZm diff --git a/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence8.png b/Resources/Textures/Interface/Alerts/essence_counter.rsi/essence8.png deleted file mode 100644 index 4a2ba8d13ecebd70341adfc67e06c232a65a3952..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 295 zcmV+?0oeYDP)Px#;Ymb6R9J=WmOT!_FbsuX5Noc{fejYMo`#8$b1*V-nv5*4F>s9@z)VH`QDZw! z6^49L2P>-mQd_SEDur>e!}I=1If~p~m&H0;?&Z_5H5I=8F4awdDtxr@VDsDuh zBt)eC|2yYQLS|oJn2(6q*vfwzk>uumELq!3D;Y3u%anF^K;!-5xp^g98Px#-bqA3R9J=WmN5>(FbqW<#M*1HGcj@+CPvP|$i!(fc7ct7YxDqS3JRjlck+Y6 zkSBFWqT0W)^=pw%V_fR+xIJr)B$xYXG0z&keAw-&cSPWo)BHG6ZW%rV@~S#tSH=%19PEed!ihXPgxf qC!0$g@6~k0U_|f7xTa|Cgq;^t>uX|n0zv%%0000 Date: Thu, 28 Mar 2024 06:34:02 +0000 Subject: [PATCH 28/56] 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 7b98ec65c9..89ed3bac89 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Added ability to put spesos directly into cargo request consoles. - type: Add - id: 5744 - time: '2024-01-19T07:23:07.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24263 - author: metalgearsloth changes: - message: Fix bolting guns causing exceptions. @@ -3798,3 +3791,10 @@ id: 6243 time: '2024-03-28T06:28:45.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/24200 +- author: EmoGarbage404 + changes: + - message: The revenant essence alert now displays the exact amount of essence. + type: Add + id: 6244 + time: '2024-03-28T06:32:56.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25452 From d13da2837d3a919df2101b37eae945e633c76c56 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Thu, 28 Mar 2024 06:36:43 +0000 Subject: [PATCH 29/56] voicemask can select speech verb (#25768) * add Name field to SpeechVerbPrototype * extra locale for voice mask ui * SpeechVerb ui and handling * raaaaaaaaa * reeeeeeeeal Co-authored-by: Tayrtahn * fix sort * did you hear john syndicate died of ligma * Update Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml --------- Co-authored-by: deltanedas <@deltanedas:kde.org> Co-authored-by: Tayrtahn Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../VoiceMask/VoiceMaskBoundUserInterface.cs | 8 ++- .../VoiceMask/VoiceMaskNameChangeWindow.xaml | 15 +++-- .../VoiceMaskNameChangeWindow.xaml.cs | 67 +++++++++++++++++-- .../VoiceMask/VoiceMaskSystem.Equip.cs | 25 ++++--- Content.Server/VoiceMask/VoiceMaskSystem.cs | 21 +++++- .../VoiceMask/VoiceMaskerComponent.cs | 15 +++-- Content.Shared/Speech/SpeechVerbPrototype.cs | 6 ++ .../VoiceMask/SharedVoiceMaskSystem.cs | 22 +++++- .../en-US/chat/managers/chat-manager.ftl | 24 +++++++ Resources/Locale/en-US/voice-mask.ftl | 1 + Resources/Prototypes/Voice/speech_verbs.yml | 27 +++++++- 11 files changed, 200 insertions(+), 31 deletions(-) diff --git a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs index 0650482b10..f700c6663b 100644 --- a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs +++ b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs @@ -1,10 +1,13 @@ using Content.Shared.VoiceMask; using Robust.Client.GameObjects; +using Robust.Shared.Prototypes; namespace Content.Client.VoiceMask; public sealed class VoiceMaskBoundUserInterface : BoundUserInterface { + [Dependency] private readonly IPrototypeManager _proto = default!; + [ViewVariables] private VoiceMaskNameChangeWindow? _window; @@ -16,10 +19,11 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface { base.Open(); - _window = new(); + _window = new(_proto); _window.OpenCentered(); _window.OnNameChange += OnNameSelected; + _window.OnVerbChange += verb => SendMessage(new VoiceMaskChangeVerbMessage(verb)); _window.OnClose += Close; } @@ -35,7 +39,7 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface return; } - _window.UpdateState(cast.Name); + _window.UpdateState(cast.Name, cast.Verb); } protected override void Dispose(bool disposing) diff --git a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml index 2316ec9c7d..e23aca1239 100644 --- a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml +++ b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml @@ -1,11 +1,16 @@ - - + MinSize="5 30"> + [DataField("priority")] public int Priority = 0; + + /// + /// Name shown in the voicemask UI for this verb. + /// + [DataField(required: true)] + public LocId Name = string.Empty; } diff --git a/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs b/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs index 2bb87819e3..2855891946 100644 --- a/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs +++ b/Content.Shared/VoiceMask/SharedVoiceMaskSystem.cs @@ -11,21 +11,37 @@ public enum VoiceMaskUIKey : byte [Serializable, NetSerializable] public sealed class VoiceMaskBuiState : BoundUserInterfaceState { - public string Name { get; } + public readonly string Name; + public readonly string? Verb; - public VoiceMaskBuiState(string name) + public VoiceMaskBuiState(string name, string? verb) { Name = name; + Verb = verb; } } [Serializable, NetSerializable] public sealed class VoiceMaskChangeNameMessage : BoundUserInterfaceMessage { - public string Name { get; } + public readonly string Name; public VoiceMaskChangeNameMessage(string name) { Name = name; } } + +/// +/// Change the speech verb prototype to override, or null to use the user's verb. +/// +[Serializable, NetSerializable] +public sealed class VoiceMaskChangeVerbMessage : BoundUserInterfaceMessage +{ + public readonly string? Verb; + + public VoiceMaskChangeVerbMessage(string? verb) + { + Verb = verb; + } +} diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index cf8c384dee..1abe55da7e 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -58,57 +58,79 @@ chat-speech-verb-suffix-question = ? chat-speech-verb-suffix-stutter = - chat-speech-verb-suffix-mumble = .. +chat-speech-verb-name-none = None +chat-speech-verb-name-default = Default chat-speech-verb-default = says +chat-speech-verb-name-exclamation = Exclaiming chat-speech-verb-exclamation = exclaims +chat-speech-verb-name-exclamation-strong = Yelling chat-speech-verb-exclamation-strong = yells +chat-speech-verb-name-question = Asking chat-speech-verb-question = asks +chat-speech-verb-name-stutter = Stuttering chat-speech-verb-stutter = stutters +chat-speech-verb-name-mumble = Mumbling chat-speech-verb-mumble = mumbles +chat-speech-verb-name-arachnid = Arachnid chat-speech-verb-insect-1 = chitters chat-speech-verb-insect-2 = chirps chat-speech-verb-insect-3 = clicks +chat-speech-verb-name-moth = Moth chat-speech-verb-winged-1 = flutters chat-speech-verb-winged-2 = flaps chat-speech-verb-winged-3 = buzzes +chat-speech-verb-name-slime = Slime chat-speech-verb-slime-1 = sloshes chat-speech-verb-slime-2 = burbles chat-speech-verb-slime-3 = oozes +chat-speech-verb-name-plant = Diona chat-speech-verb-plant-1 = rustles chat-speech-verb-plant-2 = sways chat-speech-verb-plant-3 = creaks +chat-speech-verb-name-robotic = Robotic chat-speech-verb-robotic-1 = states chat-speech-verb-robotic-2 = beeps +chat-speech-verb-robotic-3 = boops +chat-speech-verb-name-reptilian = Reptilian chat-speech-verb-reptilian-1 = hisses chat-speech-verb-reptilian-2 = snorts chat-speech-verb-reptilian-3 = huffs +chat-speech-verb-name-skeleton = Skeleton chat-speech-verb-skeleton-1 = rattles chat-speech-verb-skeleton-2 = clacks chat-speech-verb-skeleton-3 = gnashes +chat-speech-verb-name-vox = Vox chat-speech-verb-vox-1 = screeches chat-speech-verb-vox-2 = shrieks chat-speech-verb-vox-3 = croaks +chat-speech-verb-name-canine = Canine chat-speech-verb-canine-1 = barks chat-speech-verb-canine-2 = woofs chat-speech-verb-canine-3 = howls +chat-speech-verb-name-small-mob = Mouse chat-speech-verb-small-mob-1 = squeaks chat-speech-verb-small-mob-2 = pieps +chat-speech-verb-name-large-mob = Carp chat-speech-verb-large-mob-1 = roars chat-speech-verb-large-mob-2 = growls +chat-speech-verb-name-monkey = Monkey chat-speech-verb-monkey-1 = chimpers chat-speech-verb-monkey-2 = screeches +chat-speech-verb-name-cluwne = Cluwne + chat-speech-verb-parrot-1 = squawks chat-speech-verb-parrot-2 = tweets chat-speech-verb-parrot-3 = chirps @@ -117,11 +139,13 @@ chat-speech-verb-cluwne-1 = giggles chat-speech-verb-cluwne-2 = guffaws chat-speech-verb-cluwne-3 = laughs +chat-speech-verb-name-ghost = Ghost chat-speech-verb-ghost-1 = complains chat-speech-verb-ghost-2 = breathes chat-speech-verb-ghost-3 = hums chat-speech-verb-ghost-4 = mutters +chat-speech-verb-name-electricity = Electricity chat-speech-verb-electricity-1 = crackles chat-speech-verb-electricity-2 = buzzes chat-speech-verb-electricity-3 = screeches diff --git a/Resources/Locale/en-US/voice-mask.ftl b/Resources/Locale/en-US/voice-mask.ftl index cb6eb7768e..2f5acefee4 100644 --- a/Resources/Locale/en-US/voice-mask.ftl +++ b/Resources/Locale/en-US/voice-mask.ftl @@ -1,5 +1,6 @@ voice-mask-name-change-window = Voice Mask Name Change voice-mask-name-change-info = Type in the name you want to mimic. +voice-mask-name-change-speech-style = Speech style voice-mask-name-change-set = Set name voice-mask-name-change-set-description = Change the name others hear to something else. diff --git a/Resources/Prototypes/Voice/speech_verbs.yml b/Resources/Prototypes/Voice/speech_verbs.yml index 26e9370c01..43fefe04e3 100644 --- a/Resources/Prototypes/Voice/speech_verbs.yml +++ b/Resources/Prototypes/Voice/speech_verbs.yml @@ -1,30 +1,36 @@ - type: speechVerb id: Default + name: chat-speech-verb-name-default speechVerbStrings: - chat-speech-verb-default - type: speechVerb id: DefaultQuestion + name: chat-speech-verb-name-question speechVerbStrings: - chat-speech-verb-question - type: speechVerb id: DefaultStutter + name: chat-speech-verb-name-stutter speechVerbStrings: - chat-speech-verb-stutter - type: speechVerb id: DefaultMumble + name: chat-speech-verb-name-mumble speechVerbStrings: - chat-speech-verb-mumble - type: speechVerb id: DefaultExclamation + name: chat-speech-verb-name-exclamation speechVerbStrings: - chat-speech-verb-exclamation - type: speechVerb id: DefaultExclamationStrong + name: chat-speech-verb-name-exclamation-strong bold: true speechVerbStrings: - chat-speech-verb-exclamation-strong @@ -32,6 +38,7 @@ - type: speechVerb id: Arachnid + name: chat-speech-verb-name-arachnid speechVerbStrings: - chat-speech-verb-insect-1 - chat-speech-verb-insect-2 @@ -40,6 +47,7 @@ - type: speechVerb id: Moth + name: chat-speech-verb-name-moth speechVerbStrings: - chat-speech-verb-winged-1 - chat-speech-verb-winged-2 @@ -48,12 +56,14 @@ - type: speechVerb id: Robotic + name: chat-speech-verb-name-robotic speechVerbStrings: - chat-speech-verb-robotic-1 - chat-speech-verb-robotic-2 - type: speechVerb id: Reptilian + name: chat-speech-verb-name-reptilian speechVerbStrings: - chat-speech-verb-reptilian-1 - chat-speech-verb-reptilian-2 @@ -62,6 +72,7 @@ - type: speechVerb id: Skeleton + name: chat-speech-verb-name-skeleton speechVerbStrings: - chat-speech-verb-skeleton-1 - chat-speech-verb-skeleton-2 @@ -69,6 +80,7 @@ - type: speechVerb id: Slime + name: chat-speech-verb-name-slime speechVerbStrings: - chat-speech-verb-slime-1 - chat-speech-verb-slime-2 @@ -76,13 +88,15 @@ - type: speechVerb id: Vox + name: chat-speech-verb-name-vox speechVerbStrings: - - chat-speech-verb-vox-1 - - chat-speech-verb-vox-2 - - chat-speech-verb-vox-3 + - chat-speech-verb-vox-1 + - chat-speech-verb-vox-2 + - chat-speech-verb-vox-3 - type: speechVerb id: Plant + name: chat-speech-verb-name-plant speechVerbStrings: - chat-speech-verb-plant-1 - chat-speech-verb-plant-2 @@ -90,6 +104,7 @@ - type: speechVerb id: Canine + name: chat-speech-verb-name-canine speechVerbStrings: - chat-speech-verb-canine-1 - chat-speech-verb-canine-2 @@ -97,18 +112,21 @@ - type: speechVerb id: LargeMob + name: chat-speech-verb-name-large-mob speechVerbStrings: - chat-speech-verb-large-mob-1 - chat-speech-verb-large-mob-2 - type: speechVerb id: SmallMob + name: chat-speech-verb-name-small-mob speechVerbStrings: - chat-speech-verb-small-mob-1 - chat-speech-verb-small-mob-2 - type: speechVerb id: Monkey + name: chat-speech-verb-name-monkey speechVerbStrings: - chat-speech-verb-monkey-1 - chat-speech-verb-monkey-2 @@ -122,6 +140,7 @@ - type: speechVerb id: Cluwne + name: chat-speech-verb-name-cluwne speechVerbStrings: - chat-speech-verb-cluwne-1 - chat-speech-verb-cluwne-2 @@ -129,6 +148,7 @@ - type: speechVerb id: Ghost + name: chat-speech-verb-name-ghost speechVerbStrings: - chat-speech-verb-ghost-1 - chat-speech-verb-ghost-2 @@ -138,6 +158,7 @@ - type: speechVerb id: Electricity + name: chat-speech-verb-name-electricity speechVerbStrings: - chat-speech-verb-electricity-1 - chat-speech-verb-electricity-2 From 29f34d8423d1033d33032edc28f02edebe861e37 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 28 Mar 2024 06:37:49 +0000 Subject: [PATCH 30/56] 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 89ed3bac89..db48f993d5 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix bolting guns causing exceptions. - type: Fix - id: 5745 - time: '2024-01-19T10:14:42.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24283 - author: mirrorcult changes: - message: Destruction & impact sounds have been reworked in general, you should @@ -3798,3 +3791,10 @@ id: 6244 time: '2024-03-28T06:32:56.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25452 +- author: deltanedas + changes: + - message: Voicemasks can now mimic speech verbs, like flutters or growls. + type: Tweak + id: 6245 + time: '2024-03-28T06:36:43.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25768 From 794a447bb7f0a36abaac802e0fa4d1ce7c8998ef Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 28 Mar 2024 02:46:26 -0400 Subject: [PATCH 31/56] Separate "thank you" messages from general ads (#25867) * Separated "thank you" messages from general ads * Moved MessagePackPrototype to shared, cleaned up AdvertiseComponent, and actually killed AdvertisementsPackPrototype. +More suggests changes. * Rename PackPrototypeID to Pack in both components. --- Content.Client/Entry/EntryPoint.cs | 1 - .../Advertise/AdvertiseComponent.cs | 42 ----- Content.Server/Advertise/AdvertiseSystem.cs | 154 ------------------ .../Components/AdvertiseComponent.cs | 45 +++++ .../Components/SpeakOnUIClosedComponent.cs | 36 ++++ .../EntitySystems/AdvertiseSystem.cs | 137 ++++++++++++++++ .../EntitySystems/SpeakOnUIClosedSystem.cs | 58 +++++++ .../AdvertisementsPackPrototype.cs | 18 -- .../VendingMachines/VendingMachineSystem.cs | 22 +-- .../Advertise/MessagePackPrototype.cs | 14 ++ .../VendingMachineComponent.cs | 2 - .../VendingMachines/Advertisements/ammo.yml | 6 +- .../Advertisements/atmosdrobe.yml | 6 +- .../Advertisements/bardrobe.yml | 6 +- .../Advertisements/boozeomat.yml | 9 +- .../Advertisements/cargodrobe.yml | 6 +- .../VendingMachines/Advertisements/chang.yml | 8 +- .../Advertisements/chefdrobe.yml | 6 +- .../Advertisements/chefvend.yml | 10 +- .../Advertisements/chemdrobe.yml | 7 +- .../VendingMachines/Advertisements/cigs.yml | 9 +- .../Advertisements/clothesmate.yml | 6 +- .../VendingMachines/Advertisements/coffee.yml | 10 +- .../VendingMachines/Advertisements/cola.yml | 10 +- .../Advertisements/condiments.yml | 6 +- .../Advertisements/curadrobe.yml | 6 +- .../Advertisements/detdrobe.yml | 6 +- .../Advertisements/dinnerware.yml | 6 +- .../Advertisements/discount.yml | 14 +- .../VendingMachines/Advertisements/donut.yml | 10 +- .../Advertisements/engidrobe.yml | 6 +- .../Advertisements/fatextractor.yml | 6 +- .../VendingMachines/Advertisements/games.yml | 10 +- .../Advertisements/genedrobe.yml | 6 +- .../Advertisements/happyhonk.yml | 10 +- .../Advertisements/hydrobe.yml | 6 +- .../Advertisements/janidrobe.yml | 6 +- .../Advertisements/lawdrobe.yml | 11 +- .../Advertisements/magivend.yml | 6 +- .../Advertisements/medidrobe.yml | 6 +- .../Advertisements/megaseed.yml | 6 +- .../Advertisements/nanomed.yml | 6 +- .../Advertisements/nutrimax.yml | 8 +- .../Advertisements/robodrobe.yml | 6 +- .../Advertisements/scidrobe.yml | 6 +- .../Advertisements/secdrobe.yml | 6 +- .../Advertisements/sectech.yml | 9 +- .../Advertisements/smartfridge.yml | 6 +- .../VendingMachines/Advertisements/snack.yml | 12 +- .../Advertisements/sovietsoda.yml | 9 +- .../Advertisements/syndiedrobe.yml | 11 +- .../Advertisements/theater.yml | 6 +- .../Advertisements/vendomat.yml | 6 +- .../Advertisements/virodrobe.yml | 6 +- .../VendingMachines/Goodbyes/boozeomat.yml | 7 + .../VendingMachines/Goodbyes/chang.yml | 6 + .../VendingMachines/Goodbyes/chefvend.yml | 8 + .../Catalog/VendingMachines/Goodbyes/cigs.yml | 7 + .../VendingMachines/Goodbyes/coffee.yml | 8 + .../Catalog/VendingMachines/Goodbyes/cola.yml | 8 + .../VendingMachines/Goodbyes/discount.yml | 12 ++ .../VendingMachines/Goodbyes/donut.yml | 8 + .../VendingMachines/Goodbyes/games.yml | 8 + .../VendingMachines/Goodbyes/generic.yml | 4 + .../VendingMachines/Goodbyes/happyhonk.yml | 8 + .../VendingMachines/Goodbyes/lawdrobe.yml | 8 + .../VendingMachines/Goodbyes/nutrimax.yml | 6 + .../VendingMachines/Goodbyes/sectech.yml | 7 + .../VendingMachines/Goodbyes/snack.yml | 10 ++ .../VendingMachines/Goodbyes/sovietsoda.yml | 7 + .../VendingMachines/Goodbyes/syndiedrobe.yml | 9 + .../Structures/Machines/vending_machines.yml | 90 ++++++++++ 72 files changed, 603 insertions(+), 470 deletions(-) delete mode 100644 Content.Server/Advertise/AdvertiseComponent.cs delete mode 100644 Content.Server/Advertise/AdvertiseSystem.cs create mode 100644 Content.Server/Advertise/Components/AdvertiseComponent.cs create mode 100644 Content.Server/Advertise/Components/SpeakOnUIClosedComponent.cs create mode 100644 Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs create mode 100644 Content.Server/Advertise/EntitySystems/SpeakOnUIClosedSystem.cs delete mode 100644 Content.Server/Advertisements/AdvertisementsPackPrototype.cs create mode 100644 Content.Shared/Advertise/MessagePackPrototype.cs create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/boozeomat.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/chang.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/chefvend.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/cigs.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/coffee.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/cola.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/discount.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/donut.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/games.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/generic.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/happyhonk.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/lawdrobe.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/nutrimax.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/sectech.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/snack.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/sovietsoda.yml create mode 100644 Resources/Prototypes/Catalog/VendingMachines/Goodbyes/syndiedrobe.yml diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 7dde4557cd..47f11ee161 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -106,7 +106,6 @@ namespace Content.Client.Entry _prototypeManager.RegisterIgnore("gameMap"); _prototypeManager.RegisterIgnore("gameMapPool"); _prototypeManager.RegisterIgnore("lobbyBackground"); - _prototypeManager.RegisterIgnore("advertisementsPack"); _prototypeManager.RegisterIgnore("gamePreset"); _prototypeManager.RegisterIgnore("noiseChannel"); _prototypeManager.RegisterIgnore("spaceBiome"); diff --git a/Content.Server/Advertise/AdvertiseComponent.cs b/Content.Server/Advertise/AdvertiseComponent.cs deleted file mode 100644 index f36cc7ae1e..0000000000 --- a/Content.Server/Advertise/AdvertiseComponent.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Content.Server.Advertisements; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Advertise -{ - [RegisterComponent, Access(typeof(AdvertiseSystem))] - public sealed partial class AdvertiseComponent : Component - { - /// - /// Minimum time in seconds to wait before saying a new ad, in seconds. Has to be larger than or equal to 1. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("minWait")] - public int MinimumWait { get; private set; } = 8 * 60; - - /// - /// Maximum time in seconds to wait before saying a new ad, in seconds. Has to be larger than or equal - /// to - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("maxWait")] - public int MaximumWait { get; private set; } = 10 * 60; - - /// - /// The identifier for the advertisements pack prototype. - /// - [DataField("pack", customTypeSerializer:typeof(PrototypeIdSerializer), required: true)] - public string PackPrototypeId { get; private set; } = string.Empty; - - /// - /// The next time an advertisement will be said. - /// - [ViewVariables(VVAccess.ReadWrite)] - public TimeSpan NextAdvertisementTime { get; set; } = TimeSpan.Zero; - - /// - /// Whether the entity will say advertisements or not. - /// - [ViewVariables(VVAccess.ReadWrite)] - public bool Enabled { get; set; } = true; - } -} diff --git a/Content.Server/Advertise/AdvertiseSystem.cs b/Content.Server/Advertise/AdvertiseSystem.cs deleted file mode 100644 index ab538f3c77..0000000000 --- a/Content.Server/Advertise/AdvertiseSystem.cs +++ /dev/null @@ -1,154 +0,0 @@ -using Content.Server.Advertisements; -using Content.Server.Chat.Systems; -using Content.Server.Power.Components; -using Content.Shared.VendingMachines; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; - -namespace Content.Server.Advertise -{ - public sealed class AdvertiseSystem : EntitySystem - { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly ChatSystem _chat = default!; - - /// - /// The maximum amount of time between checking if advertisements should be displayed - /// - private readonly TimeSpan _maximumNextCheckDuration = TimeSpan.FromSeconds(15); - - /// - /// The next time the game will check if advertisements should be displayed - /// - private TimeSpan _nextCheckTime = TimeSpan.MaxValue; - - public override void Initialize() - { - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnPowerChanged); - - SubscribeLocalEvent(OnPowerReceiverEnableChangeAttempt); - SubscribeLocalEvent(OnVendingEnableChangeAttempt); - - // The component inits will lower this. - _nextCheckTime = TimeSpan.MaxValue; - } - - private void OnComponentInit(EntityUid uid, AdvertiseComponent advertise, ComponentInit args) - { - RefreshTimer(uid, advertise); - } - - private void OnPowerChanged(EntityUid uid, AdvertiseComponent advertise, ref PowerChangedEvent args) - { - SetEnabled(uid, args.Powered, advertise); - } - - public void RefreshTimer(EntityUid uid, AdvertiseComponent? advertise = null) - { - if (!Resolve(uid, ref advertise)) - return; - - if (!advertise.Enabled) - return; - - var minDuration = Math.Max(1, advertise.MinimumWait); - var maxDuration = Math.Max(minDuration, advertise.MaximumWait); - var waitDuration = TimeSpan.FromSeconds(_random.Next(minDuration, maxDuration)); - var nextTime = _gameTiming.CurTime + waitDuration; - - advertise.NextAdvertisementTime = nextTime; - - _nextCheckTime = MathHelper.Min(nextTime, _nextCheckTime); - } - - public void SayAdvertisement(EntityUid uid, AdvertiseComponent? advertise = null) - { - if (!Resolve(uid, ref advertise)) - return; - - if (_prototypeManager.TryIndex(advertise.PackPrototypeId, out AdvertisementsPackPrototype? advertisements)) - _chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.Advertisements)), InGameICChatType.Speak, true); - } - - public void SayThankYou(EntityUid uid, AdvertiseComponent? advertise = null) - { - if (!Resolve(uid, ref advertise)) - return; - - if (_prototypeManager.TryIndex(advertise.PackPrototypeId, out AdvertisementsPackPrototype? advertisements)) - { - _chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.ThankYous), ("name", Name(uid))), InGameICChatType.Speak, true); - } - } - - public void SetEnabled(EntityUid uid, bool enable, AdvertiseComponent? advertise = null) - { - if (!Resolve(uid, ref advertise)) - return; - - if (advertise.Enabled == enable) - return; - - var attemptEvent = new AdvertiseEnableChangeAttemptEvent(enable); - RaiseLocalEvent(uid, attemptEvent); - - if (attemptEvent.Cancelled) - return; - - advertise.Enabled = enable; - RefreshTimer(uid, advertise); - } - - private static void OnPowerReceiverEnableChangeAttempt(EntityUid uid, ApcPowerReceiverComponent component, AdvertiseEnableChangeAttemptEvent args) - { - if(args.Enabling && !component.Powered) - args.Cancel(); - } - - private static void OnVendingEnableChangeAttempt(EntityUid uid, VendingMachineComponent component, AdvertiseEnableChangeAttemptEvent args) - { - if(args.Enabling && component.Broken) - args.Cancel(); - } - - public override void Update(float frameTime) - { - var curTime = _gameTiming.CurTime; - if (_nextCheckTime > curTime) - return; - - _nextCheckTime = curTime + _maximumNextCheckDuration; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var advert)) - { - if (!advert.Enabled) - continue; - - // If this isn't advertising yet - if (advert.NextAdvertisementTime > curTime) - { - _nextCheckTime = MathHelper.Min(advert.NextAdvertisementTime, _nextCheckTime); - continue; - } - - SayAdvertisement(uid, advert); - RefreshTimer(uid, advert); - } - } - } - - public sealed class AdvertiseEnableChangeAttemptEvent : CancellableEntityEventArgs - { - public bool Enabling { get; } - - public AdvertiseEnableChangeAttemptEvent(bool enabling) - { - Enabling = enabling; - } - } -} diff --git a/Content.Server/Advertise/Components/AdvertiseComponent.cs b/Content.Server/Advertise/Components/AdvertiseComponent.cs new file mode 100644 index 0000000000..140bc6b902 --- /dev/null +++ b/Content.Server/Advertise/Components/AdvertiseComponent.cs @@ -0,0 +1,45 @@ +using Content.Server.Advertise.EntitySystems; +using Content.Shared.Advertise; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Advertise.Components; + +/// +/// Makes this entity periodically advertise by speaking a randomly selected +/// message from a specified MessagePack into local chat. +/// +[RegisterComponent, Access(typeof(AdvertiseSystem))] +public sealed partial class AdvertiseComponent : Component +{ + /// + /// Minimum time in seconds to wait before saying a new ad, in seconds. Has to be larger than or equal to 1. + /// + [DataField] + public int MinimumWait { get; private set; } = 8 * 60; + + /// + /// Maximum time in seconds to wait before saying a new ad, in seconds. Has to be larger than or equal + /// to + /// + [DataField] + public int MaximumWait { get; private set; } = 10 * 60; + + /// + /// The identifier for the advertisements pack prototype. + /// + [DataField(required: true)] + public ProtoId Pack { get; private set; } + + /// + /// The next time an advertisement will be said. + /// + [DataField] + public TimeSpan NextAdvertisementTime { get; set; } = TimeSpan.Zero; + + /// + /// Whether the entity will say advertisements or not. + /// + [DataField] + public bool Enabled { get; set; } = true; +} diff --git a/Content.Server/Advertise/Components/SpeakOnUIClosedComponent.cs b/Content.Server/Advertise/Components/SpeakOnUIClosedComponent.cs new file mode 100644 index 0000000000..2a663b7f89 --- /dev/null +++ b/Content.Server/Advertise/Components/SpeakOnUIClosedComponent.cs @@ -0,0 +1,36 @@ +using Content.Shared.Advertise; +using Robust.Shared.Prototypes; + +namespace Content.Server.Advertise.Components; + +/// +/// Causes the entity to speak using the Chat system when its ActivatableUI is closed, optionally +/// requiring that a Flag be set as well. +/// +[RegisterComponent, Access(typeof(SpeakOnUIClosedSystem))] +public sealed partial class SpeakOnUIClosedComponent : Component +{ + /// + /// The identifier for the message pack prototype containing messages to be spoken by this entity. + /// + [DataField(required: true)] + public ProtoId Pack { get; private set; } + + /// + /// Is this component active? If false, no messages will be spoken. + /// + [DataField] + public bool Enabled = true; + + /// + /// Should messages be spoken only if the is set (true), or every time the UI is closed (false)? + /// + [DataField] + public bool RequireFlag = true; + + /// + /// State variable only used if is true. Set with . + /// + [DataField] + public bool Flag; +} diff --git a/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs b/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs new file mode 100644 index 0000000000..bb254d11ef --- /dev/null +++ b/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs @@ -0,0 +1,137 @@ +using Content.Server.Advertise.Components; +using Content.Server.Chat.Systems; +using Content.Server.Power.Components; +using Content.Shared.VendingMachines; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.Advertise.EntitySystems; + +public sealed class AdvertiseSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly ChatSystem _chat = default!; + + /// + /// The maximum amount of time between checking if advertisements should be displayed + /// + private readonly TimeSpan _maximumNextCheckDuration = TimeSpan.FromSeconds(15); + + /// + /// The next time the game will check if advertisements should be displayed + /// + private TimeSpan _nextCheckTime = TimeSpan.MaxValue; + + public override void Initialize() + { + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnPowerChanged); + + SubscribeLocalEvent(OnPowerReceiverEnableChangeAttempt); + SubscribeLocalEvent(OnVendingEnableChangeAttempt); + + // The component inits will lower this. + _nextCheckTime = TimeSpan.MaxValue; + } + + private void OnMapInit(EntityUid uid, AdvertiseComponent advertise, MapInitEvent args) + { + RefreshTimer(uid, advertise); + } + + private void OnPowerChanged(EntityUid uid, AdvertiseComponent advertise, ref PowerChangedEvent args) + { + SetEnabled(uid, args.Powered, advertise); + } + + public void RefreshTimer(EntityUid uid, AdvertiseComponent? advertise = null) + { + if (!Resolve(uid, ref advertise)) + return; + + if (!advertise.Enabled) + return; + + var minDuration = Math.Max(1, advertise.MinimumWait); + var maxDuration = Math.Max(minDuration, advertise.MaximumWait); + var waitDuration = TimeSpan.FromSeconds(_random.Next(minDuration, maxDuration)); + var nextTime = _gameTiming.CurTime + waitDuration; + + advertise.NextAdvertisementTime = nextTime; + + _nextCheckTime = MathHelper.Min(nextTime, _nextCheckTime); + } + + public void SayAdvertisement(EntityUid uid, AdvertiseComponent? advertise = null) + { + if (!Resolve(uid, ref advertise)) + return; + + if (_prototypeManager.TryIndex(advertise.Pack, out var advertisements)) + _chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.Messages)), InGameICChatType.Speak, hideChat: true); + } + + public void SetEnabled(EntityUid uid, bool enable, AdvertiseComponent? advertise = null) + { + if (!Resolve(uid, ref advertise)) + return; + + if (advertise.Enabled == enable) + return; + + var attemptEvent = new AdvertiseEnableChangeAttemptEvent(enable); + RaiseLocalEvent(uid, attemptEvent); + + if (attemptEvent.Cancelled) + return; + + advertise.Enabled = enable; + RefreshTimer(uid, advertise); + } + + private static void OnPowerReceiverEnableChangeAttempt(EntityUid uid, ApcPowerReceiverComponent component, AdvertiseEnableChangeAttemptEvent args) + { + if (args.Enabling && !component.Powered) + args.Cancel(); + } + + private static void OnVendingEnableChangeAttempt(EntityUid uid, VendingMachineComponent component, AdvertiseEnableChangeAttemptEvent args) + { + if (args.Enabling && component.Broken) + args.Cancel(); + } + + public override void Update(float frameTime) + { + var curTime = _gameTiming.CurTime; + if (_nextCheckTime > curTime) + return; + + _nextCheckTime = curTime + _maximumNextCheckDuration; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var advert)) + { + if (!advert.Enabled) + continue; + + // If this isn't advertising yet + if (advert.NextAdvertisementTime > curTime) + { + _nextCheckTime = MathHelper.Min(advert.NextAdvertisementTime, _nextCheckTime); + continue; + } + + SayAdvertisement(uid, advert); + RefreshTimer(uid, advert); + } + } +} + +public sealed class AdvertiseEnableChangeAttemptEvent(bool enabling) : CancellableEntityEventArgs +{ + public bool Enabling { get; } = enabling; +} diff --git a/Content.Server/Advertise/EntitySystems/SpeakOnUIClosedSystem.cs b/Content.Server/Advertise/EntitySystems/SpeakOnUIClosedSystem.cs new file mode 100644 index 0000000000..048f59b8d3 --- /dev/null +++ b/Content.Server/Advertise/EntitySystems/SpeakOnUIClosedSystem.cs @@ -0,0 +1,58 @@ +using Content.Server.Advertise.Components; +using Content.Server.Chat.Systems; +using Content.Server.UserInterface; +using Content.Shared.Advertise; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.Advertise; + +public sealed partial class SpeakOnUIClosedSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly ChatSystem _chat = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBoundUIClosed); + } + private void OnBoundUIClosed(Entity entity, ref BoundUIClosedEvent args) + { + if (!TryComp(entity, out ActivatableUIComponent? activatable) || !args.UiKey.Equals(activatable.Key)) + return; + + if (entity.Comp.RequireFlag && !entity.Comp.Flag) + return; + + TrySpeak((entity, entity.Comp)); + } + + public bool TrySpeak(Entity entity) + { + if (!Resolve(entity, ref entity.Comp)) + return false; + + if (!entity.Comp.Enabled) + return false; + + if (!_prototypeManager.TryIndex(entity.Comp.Pack, out MessagePackPrototype? messagePack)) + return false; + + var message = Loc.GetString(_random.Pick(messagePack.Messages), ("name", Name(entity))); + _chat.TrySendInGameICMessage(entity, message, InGameICChatType.Speak, true); + entity.Comp.Flag = false; + return true; + } + + public bool TrySetFlag(Entity entity, bool value = true) + { + if (!Resolve(entity, ref entity.Comp)) + return false; + + entity.Comp.Flag = value; + return true; + } +} diff --git a/Content.Server/Advertisements/AdvertisementsPackPrototype.cs b/Content.Server/Advertisements/AdvertisementsPackPrototype.cs deleted file mode 100644 index 641ab3c56f..0000000000 --- a/Content.Server/Advertisements/AdvertisementsPackPrototype.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Robust.Shared.Prototypes; - -namespace Content.Server.Advertisements -{ - [Serializable, Prototype("advertisementsPack")] - public sealed partial class AdvertisementsPackPrototype : IPrototype - { - [ViewVariables] - [IdDataField] - public string ID { get; private set; } = default!; - - [DataField("advertisements")] - public List Advertisements { get; private set; } = new(); - - [DataField("thankyous")] - public List ThankYous { get; private set; } = new(); - } -} diff --git a/Content.Server/VendingMachines/VendingMachineSystem.cs b/Content.Server/VendingMachines/VendingMachineSystem.cs index b5fa611a39..7c9aed188f 100644 --- a/Content.Server/VendingMachines/VendingMachineSystem.cs +++ b/Content.Server/VendingMachines/VendingMachineSystem.cs @@ -1,12 +1,11 @@ using System.Linq; using System.Numerics; using Content.Server.Advertise; +using Content.Server.Advertise.Components; using Content.Server.Cargo.Systems; -using Content.Server.Chat.Systems; using Content.Server.Emp; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; -using Content.Server.UserInterface; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Content.Shared.Actions; @@ -25,7 +24,6 @@ using Robust.Shared.Audio; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; -using Robust.Shared.Utility; namespace Content.Server.VendingMachines { @@ -39,7 +37,7 @@ namespace Content.Server.VendingMachines [Dependency] private readonly ThrowingSystem _throwingSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly AdvertiseSystem _advertise = default!; + [Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!; public override void Initialize() { @@ -58,7 +56,6 @@ namespace Content.Server.VendingMachines Subs.BuiEvents(VendingMachineUiKey.Key, subs => { subs.Event(OnBoundUIOpened); - subs.Event(OnBoundUIClosed); subs.Event(OnInventoryEjectMessage); }); @@ -114,16 +111,6 @@ namespace Content.Server.VendingMachines UpdateVendingMachineInterfaceState(uid, component); } - private void OnBoundUIClosed(EntityUid uid, VendingMachineComponent component, BoundUIClosedEvent args) - { - // Only vendors that advertise will send message after dispensing - if (component.ShouldSayThankYou && TryComp(uid, out var advertise)) - { - _advertise.SayThankYou(uid, advertise); - component.ShouldSayThankYou = false; - } - } - private void UpdateVendingMachineInterfaceState(EntityUid uid, VendingMachineComponent component) { var state = new VendingMachineInterfaceState(GetAllInventory(uid, component)); @@ -294,7 +281,10 @@ namespace Content.Server.VendingMachines vendComponent.Ejecting = true; vendComponent.NextItemToEject = entry.ID; vendComponent.ThrowNextItem = throwItem; - vendComponent.ShouldSayThankYou = true; + + if (TryComp(uid, out SpeakOnUIClosedComponent? speakComponent)) + _speakOnUIClosed.TrySetFlag((uid, speakComponent)); + entry.Amount--; UpdateVendingMachineInterfaceState(uid, vendComponent); TryUpdateVisualState(uid, vendComponent); diff --git a/Content.Shared/Advertise/MessagePackPrototype.cs b/Content.Shared/Advertise/MessagePackPrototype.cs new file mode 100644 index 0000000000..f7495d7e46 --- /dev/null +++ b/Content.Shared/Advertise/MessagePackPrototype.cs @@ -0,0 +1,14 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Advertise; + +[Serializable, Prototype("messagePack")] +public sealed partial class MessagePackPrototype : IPrototype +{ + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + [DataField] + public List Messages { get; private set; } = []; +} diff --git a/Content.Shared/VendingMachines/VendingMachineComponent.cs b/Content.Shared/VendingMachines/VendingMachineComponent.cs index 725dc60e3b..23130bb8f3 100644 --- a/Content.Shared/VendingMachines/VendingMachineComponent.cs +++ b/Content.Shared/VendingMachines/VendingMachineComponent.cs @@ -51,8 +51,6 @@ namespace Content.Shared.VendingMachines public bool Broken; - public bool ShouldSayThankYou; - /// /// When true, will forcefully throw any object it dispenses /// diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/ammo.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/ammo.yml index 6038134ea1..7e089a2825 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/ammo.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/ammo.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: AmmoVendAds - advertisements: + messages: - advertisement-ammo-1 - advertisement-ammo-2 - advertisement-ammo-3 @@ -11,5 +11,3 @@ - advertisement-ammo-8 - advertisement-ammo-9 - advertisement-ammo-10 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/atmosdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/atmosdrobe.yml index c5737d58bf..7946c06310 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/atmosdrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/atmosdrobe.yml @@ -1,8 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: AtmosDrobeAds - advertisements: + messages: - advertisement-atmosdrobe-1 - advertisement-atmosdrobe-2 - advertisement-atmosdrobe-3 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/bardrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/bardrobe.yml index 84501583d7..0a21acf2cb 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/bardrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/bardrobe.yml @@ -1,7 +1,5 @@ -- type: advertisementsPack +- type: messagePack id: BarDrobeAds - advertisements: + messages: - advertisement-bardrobe-1 - advertisement-bardrobe-2 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/boozeomat.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/boozeomat.yml index 943e571887..e6bcbbcb9c 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/boozeomat.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/boozeomat.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: BoozeOMatAds - advertisements: + messages: - advertisement-boozeomat-1 - advertisement-boozeomat-2 - advertisement-boozeomat-3 @@ -20,8 +20,3 @@ - advertisement-boozeomat-17 - advertisement-boozeomat-18 - advertisement-boozeomat-19 - thankyous: - - vending-machine-thanks - - thankyou-boozeomat-1 - - thankyou-boozeomat-2 - - thankyou-boozeomat-3 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cargodrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cargodrobe.yml index fdf90a1d0d..f2cd38f737 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cargodrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cargodrobe.yml @@ -1,8 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: CargoDrobeAds - advertisements: + messages: - advertisement-cargodrobe-1 - advertisement-cargodrobe-2 - advertisement-cargodrobe-3 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chang.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chang.yml index ebfe81286b..911d467e7d 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chang.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chang.yml @@ -1,12 +1,8 @@ -- type: advertisementsPack +- type: messagePack id: ChangAds - advertisements: + messages: - advertisement-chang-1 - advertisement-chang-2 - advertisement-chang-3 - advertisement-chang-4 - advertisement-chang-5 - thankyous: - - vending-machine-thanks - - thankyou-chang-1 - - thankyou-chang-2 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chefdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chefdrobe.yml index 2d45bd4e29..c71d8225e0 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chefdrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chefdrobe.yml @@ -1,8 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: ChefDrobeAds - advertisements: + messages: - advertisement-chefdrobe-1 - advertisement-chefdrobe-2 - advertisement-chefdrobe-3 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chefvend.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chefvend.yml index 9b1edb4b5b..d52fcf9894 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chefvend.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chefvend.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: ChefvendAds - advertisements: + messages: - advertisement-chefvend-1 - advertisement-chefvend-2 - advertisement-chefvend-3 @@ -10,9 +10,3 @@ - advertisement-chefvend-7 - advertisement-chefvend-8 - advertisement-chefvend-9 - thankyous: - - vending-machine-thanks - - thankyou-chefvend-1 - - thankyou-chefvend-2 - - thankyou-chefvend-3 - - thankyou-chefvend-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chemdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chemdrobe.yml index 7d45084738..69a37de184 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chemdrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/chemdrobe.yml @@ -1,9 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: ChemDrobeAds - advertisements: + messages: - advertisement-chemdrobe-1 - advertisement-chemdrobe-2 - advertisement-chemdrobe-3 - thankyous: - - vending-machine-thanks - diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cigs.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cigs.yml index fe6cb1bf26..db3d492c45 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cigs.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cigs.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: CigaretteMachineAds - advertisements: + messages: - advertisement-cigs-1 - advertisement-cigs-2 - advertisement-cigs-3 @@ -13,8 +13,3 @@ - advertisement-cigs-10 - advertisement-cigs-11 - advertisement-cigs-12 - thankyous: - - vending-machine-thanks - - thankyou-cigs-1 - - thankyou-cigs-2 - - thankyou-cigs-3 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/clothesmate.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/clothesmate.yml index 5c728513b8..dc109f8eb9 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/clothesmate.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/clothesmate.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: ClothesMateAds - advertisements: + messages: - advertisement-clothes-1 - advertisement-clothes-2 - advertisement-clothes-3 @@ -8,5 +8,3 @@ - advertisement-clothes-5 - advertisement-clothes-6 - advertisement-clothes-7 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/coffee.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/coffee.yml index 5153040165..4781768cf0 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/coffee.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/coffee.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: HotDrinksMachineAds - advertisements: + messages: - advertisement-coffee-1 - advertisement-coffee-2 - advertisement-coffee-3 @@ -15,9 +15,3 @@ - advertisement-coffee-12 - advertisement-coffee-13 - advertisement-coffee-14 - thankyous: - - vending-machine-thanks - - thankyou-coffee-1 - - thankyou-coffee-2 - - thankyou-coffee-3 - - thankyou-coffee-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cola.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cola.yml index 862846274d..d12d7eb92f 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cola.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/cola.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: RobustSoftdrinksAds - advertisements: + messages: - advertisement-cola-1 - advertisement-cola-2 - advertisement-cola-3 @@ -9,9 +9,3 @@ - advertisement-cola-6 - advertisement-cola-7 - advertisement-cola-8 - thankyous: - - vending-machine-thanks - - thankyou-cola-1 - - thankyou-cola-2 - - thankyou-cola-3 - - thankyou-cola-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/condiments.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/condiments.yml index 05dbc75887..d1d07df0c4 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/condiments.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/condiments.yml @@ -1,11 +1,9 @@ -- type: advertisementsPack +- type: messagePack id: CondimentVendAds - advertisements: + messages: - advertisement-condiment-1 - advertisement-condiment-2 - advertisement-condiment-3 - advertisement-condiment-4 - advertisement-condiment-5 - advertisement-condiment-6 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/curadrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/curadrobe.yml index b6e2d485ce..47523c6e93 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/curadrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/curadrobe.yml @@ -1,8 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: CuraDrobeAds - advertisements: + messages: - advertisement-curadrobe-1 - advertisement-curadrobe-2 - advertisement-curadrobe-3 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/detdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/detdrobe.yml index e64b519869..50024ce3d9 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/detdrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/detdrobe.yml @@ -1,8 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: DetDrobeAds - advertisements: + messages: - advertisement-detdrobe-1 - advertisement-detdrobe-2 - advertisement-detdrobe-3 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/dinnerware.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/dinnerware.yml index ebaba26b6f..ac31dc075b 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/dinnerware.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/dinnerware.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: DinnerwareAds - advertisements: + messages: - advertisement-dinnerware-1 - advertisement-dinnerware-2 - advertisement-dinnerware-3 @@ -11,5 +11,3 @@ - advertisement-dinnerware-8 - advertisement-dinnerware-9 - advertisement-dinnerware-10 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/discount.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/discount.yml index 5260e2bbcc..dce8646a60 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/discount.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/discount.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: DiscountDansAds - advertisements: + messages: - advertisement-discount-1 - advertisement-discount-2 - advertisement-discount-3 @@ -10,13 +10,3 @@ - advertisement-discount-7 - advertisement-discount-8 - advertisement-discount-9 - thankyous: - - vending-machine-thanks - - thankyou-discount-1 - - thankyou-discount-2 - - thankyou-discount-3 - - thankyou-discount-4 - - thankyou-discount-5 - - thankyou-discount-6 - - thankyou-discount-7 - - thankyou-discount-8 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/donut.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/donut.yml index 29ea90e765..73766aa749 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/donut.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/donut.yml @@ -1,14 +1,8 @@ -- type: advertisementsPack +- type: messagePack id: DonutAds - advertisements: + messages: - advertisement-donut-1 - advertisement-donut-2 - advertisement-donut-3 - advertisement-donut-4 - advertisement-donut-5 - thankyous: - - vending-machine-thanks - - thankyou-donut-1 - - thankyou-donut-2 - - thankyou-donut-3 - - thankyou-donut-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/engidrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/engidrobe.yml index 5d1290147e..ec92fbe4de 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/engidrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/engidrobe.yml @@ -1,10 +1,8 @@ -- type: advertisementsPack +- type: messagePack id: EngiDrobeAds - advertisements: + messages: - advertisement-engidrobe-1 - advertisement-engidrobe-2 - advertisement-engidrobe-3 - advertisement-engidrobe-4 - advertisement-engidrobe-5 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/fatextractor.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/fatextractor.yml index ad100c4626..7619ea1856 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/fatextractor.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/fatextractor.yml @@ -1,11 +1,9 @@ -- type: advertisementsPack +- type: messagePack id: FatExtractorFacts - advertisements: + messages: - fat-extractor-fact-1 - fat-extractor-fact-2 - fat-extractor-fact-3 - fat-extractor-fact-4 - fat-extractor-fact-5 - fat-extractor-fact-6 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/games.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/games.yml index e640b378de..1348635e1f 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/games.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/games.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: GoodCleanFunAds - advertisements: + messages: - advertisement-goodcleanfun-1 - advertisement-goodcleanfun-2 - advertisement-goodcleanfun-3 @@ -11,9 +11,3 @@ - advertisement-goodcleanfun-8 - advertisement-goodcleanfun-9 - advertisement-goodcleanfun-10 - thankyous: - - vending-machine-thanks - - thankyou-goodcleanfun-1 - - thankyou-goodcleanfun-2 - - thankyou-goodcleanfun-3 - - thankyou-goodcleanfun-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/genedrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/genedrobe.yml index 71d56bdccc..722388055b 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/genedrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/genedrobe.yml @@ -1,7 +1,5 @@ -- type: advertisementsPack +- type: messagePack id: GeneDrobeAds - advertisements: + messages: - advertisement-genedrobe-1 - advertisement-genedrobe-2 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/happyhonk.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/happyhonk.yml index 737ad02e29..e145ebcdac 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/happyhonk.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/happyhonk.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: HappyHonkAds - advertisements: + messages: - advertisement-happyhonk-1 - advertisement-happyhonk-2 - advertisement-happyhonk-3 @@ -11,9 +11,3 @@ - advertisement-happyhonk-8 - advertisement-happyhonk-9 - advertisement-happyhonk-10 - thankyous: - - vending-machine-thanks - - thankyou-happyhonk-1 - - thankyou-happyhonk-2 - - thankyou-happyhonk-3 - - thankyou-happyhonk-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/hydrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/hydrobe.yml index 213097db0d..5999c496f5 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/hydrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/hydrobe.yml @@ -1,9 +1,7 @@ -- type: advertisementsPack +- type: messagePack id: HyDrobeAds - advertisements: + messages: - advertisement-hydrobe-1 - advertisement-hydrobe-2 - advertisement-hydrobe-3 - advertisement-hydrobe-4 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/janidrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/janidrobe.yml index 2d1ee1e7df..8310136bf8 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/janidrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/janidrobe.yml @@ -1,8 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: JaniDrobeAds - advertisements: + messages: - advertisement-janidrobe-1 - advertisement-janidrobe-2 - advertisement-janidrobe-3 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/lawdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/lawdrobe.yml index 5f3892d0f9..a948413abd 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/lawdrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/lawdrobe.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: LawDrobeAds - advertisements: + messages: - advertisement-lawdrobe-1 - advertisement-lawdrobe-2 - advertisement-lawdrobe-3 @@ -9,10 +9,3 @@ - advertisement-lawdrobe-6 - advertisement-lawdrobe-7 - advertisement-lawdrobe-8 - thankyous: - - vending-machine-thanks - - thankyou-lawdrobe-1 - - thankyou-lawdrobe-2 - - thankyou-lawdrobe-3 - - thankyou-lawdrobe-4 - - thankyou-lawdrobe-5 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/magivend.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/magivend.yml index 5785f04bac..896a3853e7 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/magivend.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/magivend.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: MagiVendAds - advertisements: + messages: - advertisement-magivend-1 - advertisement-magivend-2 - advertisement-magivend-3 @@ -12,5 +12,3 @@ - advertisement-magivend-9 - advertisement-magivend-10 - advertisement-magivend-11 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/medidrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/medidrobe.yml index f5744722e4..b7b055231b 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/medidrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/medidrobe.yml @@ -1,8 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: MediDrobeAds - advertisements: + messages: - advertisement-medidrobe-1 - advertisement-medidrobe-2 - advertisement-medidrobe-3 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/megaseed.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/megaseed.yml index 77babcf55a..b6e6ae098e 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/megaseed.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/megaseed.yml @@ -1,11 +1,9 @@ -- type: advertisementsPack +- type: messagePack id: MegaSeedAds - advertisements: + messages: - advertisement-megaseed-1 - advertisement-megaseed-2 - advertisement-megaseed-3 - advertisement-megaseed-4 - advertisement-megaseed-5 - advertisement-megaseed-6 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/nanomed.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/nanomed.yml index 3e710bf7be..a79a4c8a6c 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/nanomed.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/nanomed.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: NanoMedAds - advertisements: + messages: - advertisement-nanomed-1 - advertisement-nanomed-2 - advertisement-nanomed-3 @@ -10,5 +10,3 @@ - advertisement-nanomed-7 - advertisement-nanomed-8 - advertisement-nanomed-9 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/nutrimax.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/nutrimax.yml index 40a39336ee..a3ade34960 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/nutrimax.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/nutrimax.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: NutriMaxAds - advertisements: + messages: - advertisement-nutrimax-1 - advertisement-nutrimax-2 - advertisement-nutrimax-3 @@ -8,7 +8,3 @@ - advertisement-nutrimax-5 - advertisement-nutrimax-6 - advertisement-nutrimax-7 - thankyous: - - vending-machine-thanks - - thankyou-nutrimax-1 - - thankyou-nutrimax-2 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/robodrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/robodrobe.yml index f4a8f14fee..82ece7d763 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/robodrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/robodrobe.yml @@ -1,9 +1,7 @@ -- type: advertisementsPack +- type: messagePack id: RoboDrobeAds - advertisements: + messages: - advertisement-robodrobe-1 - advertisement-robodrobe-2 - advertisement-robodrobe-3 - advertisement-robodrobe-4 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/scidrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/scidrobe.yml index 9a3a224360..f8b125073b 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/scidrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/scidrobe.yml @@ -1,8 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: SciDrobeAds - advertisements: + messages: - advertisement-scidrobe-1 - advertisement-scidrobe-2 - advertisement-scidrobe-3 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/secdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/secdrobe.yml index 886856c153..99c2c402de 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/secdrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/secdrobe.yml @@ -1,10 +1,8 @@ -- type: advertisementsPack +- type: messagePack id: SecDrobeAds - advertisements: + messages: - advertisement-secdrobe-1 - advertisement-secdrobe-2 - advertisement-secdrobe-3 - advertisement-secdrobe-4 - advertisement-secdrobe-5 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/sectech.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/sectech.yml index 8b9f986572..0b32ed166d 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/sectech.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/sectech.yml @@ -1,13 +1,8 @@ -- type: advertisementsPack +- type: messagePack id: SecTechAds - advertisements: + messages: - advertisement-sectech-1 - advertisement-sectech-2 - advertisement-sectech-3 - advertisement-sectech-4 - advertisement-sectech-5 - thankyous: - - vending-machine-thanks - - thankyou-sectech-1 - - thankyou-sectech-2 - - thankyou-sectech-3 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/smartfridge.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/smartfridge.yml index e7c20761a6..d92a95f70f 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/smartfridge.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/smartfridge.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: SmartFridgeAds - advertisements: + messages: - advertisement-smartfridge-1 - advertisement-smartfridge-2 - advertisement-smartfridge-3 @@ -9,5 +9,3 @@ - advertisement-smartfridge-6 - advertisement-smartfridge-7 - advertisement-smartfridge-8 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/snack.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/snack.yml index c8f960d972..3d9b93f9c2 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/snack.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/snack.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: GetmoreChocolateCorpAds - advertisements: + messages: - advertisement-snack-1 - advertisement-snack-2 - advertisement-snack-3 @@ -16,11 +16,3 @@ - advertisement-snack-13 - advertisement-snack-14 - advertisement-snack-15 - thankyous: - - vending-machine-thanks - - thankyou-snack-1 - - thankyou-snack-2 - - thankyou-snack-3 - - thankyou-snack-4 - - thankyou-snack-5 - - thankyou-snack-6 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/sovietsoda.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/sovietsoda.yml index ff49570153..630de1e615 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/sovietsoda.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/sovietsoda.yml @@ -1,14 +1,9 @@ -- type: advertisementsPack +- type: messagePack id: BodaAds - advertisements: + messages: - advertisement-sovietsoda-1 - advertisement-sovietsoda-2 - advertisement-sovietsoda-3 - advertisement-sovietsoda-4 - advertisement-sovietsoda-5 - advertisement-sovietsoda-6 - thankyous: - - vending-machine-thanks - - thankyou-sovietsoda-1 - - thankyou-sovietsoda-2 - - thankyou-sovietsoda-3 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/syndiedrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/syndiedrobe.yml index c9e3bbfe5c..dc89d04aa0 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/syndiedrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/syndiedrobe.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: SyndieDrobeAds - advertisements: + messages: - advertisement-syndiedrobe-1 - advertisement-syndiedrobe-2 - advertisement-syndiedrobe-3 @@ -32,10 +32,3 @@ - advertisement-syndiedrobe-29 - advertisement-syndiedrobe-30 - advertisement-syndiedrobe-31 - thankyous: - - vending-machine-thanks - - thankyou-syndiedrobe-1 - - thankyou-syndiedrobe-2 - - thankyou-syndiedrobe-3 - - thankyou-syndiedrobe-4 - - thankyou-syndiedrobe-5 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/theater.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/theater.yml index 090ec81cff..8b06cdb517 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/theater.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/theater.yml @@ -1,11 +1,9 @@ -- type: advertisementsPack +- type: messagePack id: AutoDrobeAds - advertisements: + messages: - advertisement-theater-1 - advertisement-theater-2 - advertisement-theater-3 - advertisement-theater-4 - advertisement-theater-5 - advertisement-theater-6 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/vendomat.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/vendomat.yml index aaa0c41133..31c0e0c241 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/vendomat.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/vendomat.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: VendomatAds - advertisements: + messages: - advertisement-vendomat-1 - advertisement-vendomat-2 - advertisement-vendomat-3 @@ -8,5 +8,3 @@ - advertisement-vendomat-5 - advertisement-vendomat-6 - advertisement-vendomat-7 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/virodrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/virodrobe.yml index a64bcd824c..c48f9e7d2e 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Advertisements/virodrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Advertisements/virodrobe.yml @@ -1,8 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: ViroDrobeAds - advertisements: + messages: - advertisement-virodrobe-1 - advertisement-virodrobe-2 - advertisement-virodrobe-3 - thankyous: - - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/boozeomat.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/boozeomat.yml new file mode 100644 index 0000000000..e5c96887b9 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/boozeomat.yml @@ -0,0 +1,7 @@ +- type: messagePack + id: BoozeOMatGoodbyes + messages: + - vending-machine-thanks + - thankyou-boozeomat-1 + - thankyou-boozeomat-2 + - thankyou-boozeomat-3 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/chang.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/chang.yml new file mode 100644 index 0000000000..a348d8fa9c --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/chang.yml @@ -0,0 +1,6 @@ +- type: messagePack + id: ChangGoodbyes + messages: + - vending-machine-thanks + - thankyou-chang-1 + - thankyou-chang-2 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/chefvend.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/chefvend.yml new file mode 100644 index 0000000000..93249586e1 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/chefvend.yml @@ -0,0 +1,8 @@ +- type: messagePack + id: ChefvendGoodbyes + messages: + - vending-machine-thanks + - thankyou-chefvend-1 + - thankyou-chefvend-2 + - thankyou-chefvend-3 + - thankyou-chefvend-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/cigs.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/cigs.yml new file mode 100644 index 0000000000..108a34a472 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/cigs.yml @@ -0,0 +1,7 @@ +- type: messagePack + id: CigaretteMachineGoodbyes + messages: + - vending-machine-thanks + - thankyou-cigs-1 + - thankyou-cigs-2 + - thankyou-cigs-3 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/coffee.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/coffee.yml new file mode 100644 index 0000000000..6f2aef01b8 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/coffee.yml @@ -0,0 +1,8 @@ +- type: messagePack + id: HotDrinksMachineGoodbyes + messages: + - vending-machine-thanks + - thankyou-coffee-1 + - thankyou-coffee-2 + - thankyou-coffee-3 + - thankyou-coffee-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/cola.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/cola.yml new file mode 100644 index 0000000000..76f00fddaf --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/cola.yml @@ -0,0 +1,8 @@ +- type: messagePack + id: RobustSoftdrinksGoodbyes + messages: + - vending-machine-thanks + - thankyou-cola-1 + - thankyou-cola-2 + - thankyou-cola-3 + - thankyou-cola-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/discount.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/discount.yml new file mode 100644 index 0000000000..d3f7d89a93 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/discount.yml @@ -0,0 +1,12 @@ +- type: messagePack + id: DiscountDansGoodbyes + messages: + - vending-machine-thanks + - thankyou-discount-1 + - thankyou-discount-2 + - thankyou-discount-3 + - thankyou-discount-4 + - thankyou-discount-5 + - thankyou-discount-6 + - thankyou-discount-7 + - thankyou-discount-8 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/donut.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/donut.yml new file mode 100644 index 0000000000..3c28741e3b --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/donut.yml @@ -0,0 +1,8 @@ +- type: messagePack + id: DonutGoodbyes + messages: + - vending-machine-thanks + - thankyou-donut-1 + - thankyou-donut-2 + - thankyou-donut-3 + - thankyou-donut-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/games.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/games.yml new file mode 100644 index 0000000000..5d842619bb --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/games.yml @@ -0,0 +1,8 @@ +- type: messagePack + id: GoodCleanFunGoodbyes + messages: + - vending-machine-thanks + - thankyou-goodcleanfun-1 + - thankyou-goodcleanfun-2 + - thankyou-goodcleanfun-3 + - thankyou-goodcleanfun-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/generic.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/generic.yml new file mode 100644 index 0000000000..98a2d7d17a --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/generic.yml @@ -0,0 +1,4 @@ +- type: messagePack + id: GenericVendGoodbyes + messages: + - vending-machine-thanks diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/happyhonk.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/happyhonk.yml new file mode 100644 index 0000000000..7859d55463 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/happyhonk.yml @@ -0,0 +1,8 @@ +- type: messagePack + id: HappyHonkGoodbyes + messages: + - vending-machine-thanks + - thankyou-happyhonk-1 + - thankyou-happyhonk-2 + - thankyou-happyhonk-3 + - thankyou-happyhonk-4 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/lawdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/lawdrobe.yml new file mode 100644 index 0000000000..56678c1017 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/lawdrobe.yml @@ -0,0 +1,8 @@ +- type: messagePack + id: LawDrobeGoodbyes + messages: + - thankyou-lawdrobe-1 + - thankyou-lawdrobe-2 + - thankyou-lawdrobe-3 + - thankyou-lawdrobe-4 + - thankyou-lawdrobe-5 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/nutrimax.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/nutrimax.yml new file mode 100644 index 0000000000..3568ae3031 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/nutrimax.yml @@ -0,0 +1,6 @@ +- type: messagePack + id: NutriMaxGoodbyes + messages: + - vending-machine-thanks + - thankyou-nutrimax-1 + - thankyou-nutrimax-2 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/sectech.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/sectech.yml new file mode 100644 index 0000000000..8909693473 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/sectech.yml @@ -0,0 +1,7 @@ +- type: messagePack + id: SecTechGoodbyes + messages: + - vending-machine-thanks + - thankyou-sectech-1 + - thankyou-sectech-2 + - thankyou-sectech-3 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/snack.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/snack.yml new file mode 100644 index 0000000000..bd86e074c6 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/snack.yml @@ -0,0 +1,10 @@ +- type: messagePack + id: GetmoreChocolateCorpGoodbyes + messages: + - vending-machine-thanks + - thankyou-snack-1 + - thankyou-snack-2 + - thankyou-snack-3 + - thankyou-snack-4 + - thankyou-snack-5 + - thankyou-snack-6 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/sovietsoda.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/sovietsoda.yml new file mode 100644 index 0000000000..01ab25a7ad --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/sovietsoda.yml @@ -0,0 +1,7 @@ +- type: messagePack + id: BodaGoodbyes + messages: + - vending-machine-thanks + - thankyou-sovietsoda-1 + - thankyou-sovietsoda-2 + - thankyou-sovietsoda-3 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/syndiedrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/syndiedrobe.yml new file mode 100644 index 0000000000..1246eb3089 --- /dev/null +++ b/Resources/Prototypes/Catalog/VendingMachines/Goodbyes/syndiedrobe.yml @@ -0,0 +1,9 @@ +- type: messagePack + id: SyndieDrobeGoodbyes + messages: + - vending-machine-thanks + - thankyou-syndiedrobe-1 + - thankyou-syndiedrobe-2 + - thankyou-syndiedrobe-3 + - thankyou-syndiedrobe-4 + - thankyou-syndiedrobe-5 diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index 472f40fdcc..c97dc4b9dc 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -130,6 +130,8 @@ density: 190 - type: Advertise pack: CondimentVendAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Speech - type: Transform noRot: false @@ -147,6 +149,8 @@ normalState: normal-unshaded - type: Advertise pack: AmmoVendAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/ammo.rsi @@ -174,6 +178,8 @@ loopDeny: false - type: Advertise pack: BoozeOMatAds + - type: SpeakOnUIClosed + pack: BoozeOMatGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/boozeomat.rsi @@ -235,6 +241,8 @@ ejectState: eject-unshaded - type: Advertise pack: ChefvendAds + - type: SpeakOnUIClosed + pack: ChefvendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/chefvend.rsi layers: @@ -269,6 +277,8 @@ denyState: deny-unshaded - type: Advertise pack: CigaretteMachineAds + - type: SpeakOnUIClosed + pack: CigaretteMachineGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/cigs.rsi @@ -295,6 +305,8 @@ denyState: deny-unshaded - type: Advertise pack: ClothesMateAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/clothing.rsi @@ -325,6 +337,8 @@ denyState: deny-unshaded - type: Advertise pack: ClothesMateAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/winterdrobe.rsi @@ -362,6 +376,8 @@ initialStockQuality: 0.33 - type: Advertise pack: HotDrinksMachineAds + - type: SpeakOnUIClosed + pack: HotDrinksMachineGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/coffee.rsi @@ -400,6 +416,8 @@ initialStockQuality: 0.33 - type: Advertise pack: RobustSoftdrinksAds + - type: SpeakOnUIClosed + pack: RobustSoftdrinksGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/cola.rsi @@ -579,6 +597,8 @@ initialStockQuality: 0.33 - type: Advertise pack: RobustSoftdrinksAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/pwrgame.rsi @@ -614,6 +634,8 @@ initialStockQuality: 0.33 - type: Advertise pack: RobustSoftdrinksAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/gib.rsi @@ -644,6 +666,8 @@ ejectState: eject-unshaded - type: Advertise pack: DinnerwareAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/dinnerware.rsi layers: @@ -674,6 +698,8 @@ normalState: normal-unshaded - type: Advertise pack: MagiVendAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/magivend.rsi layers: @@ -705,6 +731,8 @@ initialStockQuality: 0.33 - type: Advertise pack: DiscountDansAds + - type: SpeakOnUIClosed + pack: DiscountDansGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/discount.rsi @@ -767,6 +795,8 @@ ejectDelay: 0.6 - type: Advertise pack: NanoMedAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/medical.rsi layers: @@ -802,6 +832,8 @@ denyState: deny-unshaded - type: Advertise pack: NutriMaxAds + - type: SpeakOnUIClosed + pack: NutriMaxGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/nutri.rsi layers: @@ -834,6 +866,8 @@ denyState: deny-unshaded - type: Advertise pack: SecTechAds + - type: SpeakOnUIClosed + pack: SecTechGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/sec.rsi layers: @@ -870,6 +904,8 @@ denyState: deny-unshaded - type: Advertise pack: MegaSeedAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/seeds.rsi layers: @@ -911,6 +947,8 @@ initialStockQuality: 0.33 - type: Advertise pack: GetmoreChocolateCorpAds + - type: SpeakOnUIClosed + pack: GetmoreChocolateCorpGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/snack.rsi @@ -1055,6 +1093,8 @@ initialStockQuality: 0.33 - type: Advertise pack: BodaAds + - type: SpeakOnUIClosed + pack: BodaGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/sovietsoda.rsi @@ -1087,6 +1127,8 @@ screenState: screen - type: Advertise pack: AutoDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/theater.rsi @@ -1121,6 +1163,8 @@ denyState: deny-unshaded - type: Advertise pack: VendomatAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/vendomat.rsi @@ -1152,6 +1196,8 @@ denyState: deny-unshaded - type: Advertise pack: VendomatAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/robotics.rsi @@ -1216,6 +1262,8 @@ ejectDelay: 1.8 - type: Advertise pack: GoodCleanFunAds + - type: SpeakOnUIClosed + pack: GoodCleanFunGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/games.rsi layers: @@ -1247,6 +1295,8 @@ initialStockQuality: 0.33 - type: Advertise pack: ChangAds + - type: SpeakOnUIClosed + pack: ChangGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/changs.rsi @@ -1311,6 +1361,8 @@ initialStockQuality: 0.33 - type: Advertise pack: DonutAds + - type: SpeakOnUIClosed + pack: DonutGoodbyes - type: Speech - type: Sprite sprite: Structures/Machines/VendingMachines/donut.rsi @@ -1394,6 +1446,8 @@ normalState: normal-unshaded - type: Advertise pack: HyDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/hydrobe.rsi layers: @@ -1420,6 +1474,8 @@ normalState: normal-unshaded - type: Advertise pack: LawDrobeAds + - type: SpeakOnUIClosed + pack: LawDrobeGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/lawdrobe.rsi layers: @@ -1446,6 +1502,8 @@ normalState: normal-unshaded - type: Advertise pack: SecDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/secdrobe.rsi layers: @@ -1472,6 +1530,8 @@ normalState: normal-unshaded - type: Advertise pack: BarDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/bardrobe.rsi layers: @@ -1526,6 +1586,8 @@ normalState: normal-unshaded - type: Advertise pack: CargoDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/cargodrobe.rsi layers: @@ -1552,6 +1614,8 @@ normalState: normal-unshaded - type: Advertise pack: MediDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/medidrobe.rsi layers: @@ -1578,6 +1642,8 @@ normalState: normal-unshaded - type: Advertise pack: ChemDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/chemdrobe.rsi layers: @@ -1604,6 +1670,8 @@ normalState: normal-unshaded - type: Advertise pack: CuraDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/curadrobe.rsi layers: @@ -1630,6 +1698,8 @@ normalState: normal-unshaded - type: Advertise pack: AtmosDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/atmosdrobe.rsi layers: @@ -1656,6 +1726,8 @@ normalState: normal-unshaded - type: Advertise pack: EngiDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/engidrobe.rsi layers: @@ -1682,6 +1754,8 @@ normalState: normal-unshaded - type: Advertise pack: ChefDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/chefdrobe.rsi layers: @@ -1708,6 +1782,8 @@ normalState: normal-unshaded - type: Advertise pack: DetDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/detdrobe.rsi layers: @@ -1734,6 +1810,8 @@ normalState: normal-unshaded - type: Advertise pack: JaniDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/janidrobe.rsi layers: @@ -1760,6 +1838,8 @@ normalState: normal-unshaded - type: Advertise pack: SciDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/scidrobe.rsi layers: @@ -1786,6 +1866,8 @@ normalState: normal-unshaded - type: Advertise pack: SyndieDrobeAds + - type: SpeakOnUIClosed + pack: SyndieDrobeGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/syndiedrobe.rsi layers: @@ -1812,6 +1894,8 @@ normalState: normal-unshaded - type: Advertise pack: RoboDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/robodrobe.rsi layers: @@ -1838,6 +1922,8 @@ normalState: normal-unshaded - type: Advertise pack: GeneDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/genedrobe.rsi layers: @@ -1864,6 +1950,8 @@ normalState: normal-unshaded - type: Advertise pack: ViroDrobeAds + - type: SpeakOnUIClosed + pack: GenericVendGoodbyes - type: Sprite sprite: Structures/Machines/VendingMachines/virodrobe.rsi layers: @@ -1938,6 +2026,8 @@ color: "#3c5eb5" - type: Advertise pack: HappyHonkAds + - type: SpeakOnUIClosed + pack: HappyHonkGoodbyes - type: AccessReader access: [["Kitchen"], ["Theatre"]] From bbe08c0bfb00d2b16961e6b1d4e7aab6b7a02dba Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 28 Mar 2024 22:12:36 +1100 Subject: [PATCH 32/56] Fix master (#26501) * Fix master * this * messages * Fix missing verb name parrot * Fix messagePack for blockgame and spacevillain --------- Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> --- .../Arcade/BlockGame/BlockGameArcadeSystem.cs | 4 ++- .../SpaceVillainArcadeSystem.cs | 4 ++- .../en-US/chat/managers/chat-manager.ftl | 1 + .../Arcade/Advertisements/blockgame.yml | 30 +++++++++---------- .../Arcade/Advertisements/spacevillain.yml | 30 +++++++++---------- Resources/Prototypes/Voice/speech_verbs.yml | 1 + 6 files changed, 38 insertions(+), 32 deletions(-) diff --git a/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs b/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs index 0d9487dab8..96001a484b 100644 --- a/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs +++ b/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs @@ -1,6 +1,8 @@ using Content.Server.Power.Components; using Content.Shared.UserInterface; using Content.Server.Advertise; +using Content.Server.Advertise.Components; +using Content.Server.Advertise.EntitySystems; using Content.Shared.Arcade; using Robust.Server.GameObjects; using Robust.Shared.Player; @@ -96,7 +98,7 @@ public sealed class BlockGameArcadeSystem : EntitySystem component.Player = null; if (component.ShouldSayThankYou && TryComp(uid, out var advertise)) { - _advertise.SayThankYou(uid, advertise); + _advertise.SayAdvertisement(uid, advertise); component.ShouldSayThankYou = false; } } diff --git a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs index 24fa6e32d1..4d704b8a81 100644 --- a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs +++ b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs @@ -1,6 +1,8 @@ using Content.Server.Power.Components; using Content.Shared.UserInterface; using Content.Server.Advertise; +using Content.Server.Advertise.Components; +using Content.Server.Advertise.EntitySystems; using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent; using Robust.Server.GameObjects; using Robust.Shared.Audio; @@ -121,7 +123,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem if (ent.Comp.ShouldSayThankYou && TryComp(ent.Owner, out var advertise)) { - _advertise.SayThankYou(ent.Owner, advertise); + _advertise.SayAdvertisement(ent.Owner, advertise); ent.Comp.ShouldSayThankYou = false; } } diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index 1abe55da7e..38b12251be 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -131,6 +131,7 @@ chat-speech-verb-monkey-2 = screeches chat-speech-verb-name-cluwne = Cluwne +chat-speech-verb-name-parrot = Parrot chat-speech-verb-parrot-1 = squawks chat-speech-verb-parrot-2 = tweets chat-speech-verb-parrot-3 = chirps diff --git a/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml b/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml index efcb8934a8..226f0cb72b 100644 --- a/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml +++ b/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: BlockGameAds - advertisements: + messages: - advertisement-block-game-1 - advertisement-block-game-2 - advertisement-block-game-3 @@ -14,16 +14,16 @@ - advertisement-block-game-11 - advertisement-block-game-12 - advertisement-block-game-13 - thankyous: - - thankyou-block-game-1 - - thankyou-block-game-2 - - thankyou-block-game-3 - - thankyou-block-game-4 - - thankyou-block-game-5 - - thankyou-block-game-6 - - thankyou-block-game-7 - - thankyou-block-game-8 - - thankyou-block-game-9 - - thankyou-block-game-10 - - thankyou-block-game-11 - - thankyou-block-game-12 +# thankyous: +# - thankyou-block-game-1 +# - thankyou-block-game-2 +# - thankyou-block-game-3 +# - thankyou-block-game-4 +# - thankyou-block-game-5 +# - thankyou-block-game-6 +# - thankyou-block-game-7 +# - thankyou-block-game-8 +# - thankyou-block-game-9 +# - thankyou-block-game-10 +# - thankyou-block-game-11 +# - thankyou-block-game-12 diff --git a/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml b/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml index 98063a62dd..bee4ca2bae 100644 --- a/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml +++ b/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml @@ -1,6 +1,6 @@ -- type: advertisementsPack +- type: messagePack id: SpaceVillainAds - advertisements: + messages: - advertisement-space-villain-1 - advertisement-space-villain-2 - advertisement-space-villain-3 @@ -16,16 +16,16 @@ - advertisement-space-villain-13 - advertisement-space-villain-14 - advertisement-space-villain-15 - thankyous: - - thankyou-space-villain-1 - - thankyou-space-villain-2 - - thankyou-space-villain-3 - - thankyou-space-villain-4 - - thankyou-space-villain-5 - - thankyou-space-villain-6 - - thankyou-space-villain-7 - - thankyou-space-villain-8 - - thankyou-space-villain-9 - - thankyou-space-villain-10 - - thankyou-space-villain-11 - - thankyou-space-villain-12 +# thankyous: +# - thankyou-space-villain-1 +# - thankyou-space-villain-2 +# - thankyou-space-villain-3 +# - thankyou-space-villain-4 +# - thankyou-space-villain-5 +# - thankyou-space-villain-6 +# - thankyou-space-villain-7 +# - thankyou-space-villain-8 +# - thankyou-space-villain-9 +# - thankyou-space-villain-10 +# - thankyou-space-villain-11 +# - thankyou-space-villain-12 diff --git a/Resources/Prototypes/Voice/speech_verbs.yml b/Resources/Prototypes/Voice/speech_verbs.yml index 43fefe04e3..9879cefb75 100644 --- a/Resources/Prototypes/Voice/speech_verbs.yml +++ b/Resources/Prototypes/Voice/speech_verbs.yml @@ -133,6 +133,7 @@ - type: speechVerb id: Parrot + name: chat-speech-verb-name-parrot speechVerbStrings: - chat-speech-verb-parrot-1 - chat-speech-verb-parrot-2 From 192f092f35353c63b5befc2d4fa91e0bf8fe0e38 Mon Sep 17 00:00:00 2001 From: lapatison <100279397+lapatison@users.noreply.github.com> Date: Thu, 28 Mar 2024 18:13:14 +0300 Subject: [PATCH 33/56] Sentien artifact ghost role locale (#26509) localize --- .../Locale/en-US/ghost/roles/ghost-role-component.ftl | 5 +++++ Resources/Prototypes/XenoArch/Effects/utility_effects.yml | 8 +++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl index 8f2c7481c3..d8c88f3c34 100644 --- a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl +++ b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl @@ -224,3 +224,8 @@ ghost-role-information-syndicate-reinforcement-rules = Normal syndicate antagoni ghost-role-information-syndicate-monkey-reinforcement-name = Syndicate Monkey Agent ghost-role-information-syndicate-monkey-reinforcement-description = Someone needs reinforcements. You, a trained monkey, will help them. ghost-role-information-syndicate-monkey-reinforcement-rules = Normal syndicate antagonist rules apply. Work with whoever called you in, and don't harm them. + +ghost-role-information-artifact-name = Sentient Artifact +ghost-role-information-artifact-description = + Enact your eldritch whims. + Forcibly activate your nodes for good or for evil. diff --git a/Resources/Prototypes/XenoArch/Effects/utility_effects.yml b/Resources/Prototypes/XenoArch/Effects/utility_effects.yml index 0fdaa5b956..18f3acfae3 100644 --- a/Resources/Prototypes/XenoArch/Effects/utility_effects.yml +++ b/Resources/Prototypes/XenoArch/Effects/utility_effects.yml @@ -1,4 +1,4 @@ -# Utility effects permanently modify the entity in some way when triggered, and they generally make it 'useful' for some purpose, +# Utility effects permanently modify the entity in some way when triggered, and they generally make it 'useful' for some purpose, # like turning the artifact into a tool, or gun, or whatever. - type: artifactEffect id: EffectIntercom @@ -198,10 +198,8 @@ allowMovement: true allowSpeech: true makeSentient: true - name: sentient artifact - description: | - Enact your eldritch whims. - Forcibly activate your nodes for good or for evil. + name: ghost-role-information-artifact-name + description: ghost-role-information-artifact-description - type: GhostTakeoverAvailable - type: MovementSpeedModifier baseWalkSpeed: 0.25 From 7759d404c64b2f0701893dea9535cf73f8dc1a9c Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 28 Mar 2024 12:56:49 -0400 Subject: [PATCH 34/56] Fix arcade goodbye message implementation (#26514) --- .../BlockGame/BlockGameArcadeComponent.cs | 5 ----- .../Arcade/BlockGame/BlockGameArcadeSystem.cs | 17 +++----------- .../SpaceVillainArcadeComponent.cs | 5 ----- .../SpaceVillainArcadeSystem.cs | 22 ++++--------------- .../Arcade/Advertisements/blockgame.yml | 13 ----------- .../Arcade/Advertisements/spacevillain.yml | 13 ----------- .../Catalog/Arcade/Goodbyes/blockgame.yml | 15 +++++++++++++ .../Catalog/Arcade/Goodbyes/spacevillain.yml | 15 +++++++++++++ .../Structures/Machines/Computers/arcades.yml | 4 ++++ 9 files changed, 41 insertions(+), 68 deletions(-) create mode 100644 Resources/Prototypes/Catalog/Arcade/Goodbyes/blockgame.yml create mode 100644 Resources/Prototypes/Catalog/Arcade/Goodbyes/spacevillain.yml diff --git a/Content.Server/Arcade/BlockGame/BlockGameArcadeComponent.cs b/Content.Server/Arcade/BlockGame/BlockGameArcadeComponent.cs index e2acec52a3..5613d91544 100644 --- a/Content.Server/Arcade/BlockGame/BlockGameArcadeComponent.cs +++ b/Content.Server/Arcade/BlockGame/BlockGameArcadeComponent.cs @@ -19,9 +19,4 @@ public sealed partial class BlockGameArcadeComponent : Component /// The players currently viewing (but not playing) the active session of NT-BG. ///
public readonly List Spectators = new(); - - /// - /// Whether the game machine should thank (or otherwise talk to) the player when they leave - /// - public bool ShouldSayThankYou; } diff --git a/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs b/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs index 96001a484b..ad65c5cca6 100644 --- a/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs +++ b/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Power.Components; using Content.Shared.UserInterface; using Content.Server.Advertise; using Content.Server.Advertise.Components; -using Content.Server.Advertise.EntitySystems; using Content.Shared.Arcade; using Robust.Server.GameObjects; using Robust.Shared.Player; @@ -12,7 +11,7 @@ namespace Content.Server.Arcade.BlockGame; public sealed class BlockGameArcadeSystem : EntitySystem { [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; - [Dependency] private readonly AdvertiseSystem _advertise = default!; + [Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!; public override void Initialize() { @@ -92,16 +91,6 @@ public sealed class BlockGameArcadeSystem : EntitySystem component.Spectators.Remove(component.Player); UpdatePlayerStatus(uid, component.Player, blockGame: component); } - else - { - // Everybody's gone - component.Player = null; - if (component.ShouldSayThankYou && TryComp(uid, out var advertise)) - { - _advertise.SayAdvertisement(uid, advertise); - component.ShouldSayThankYou = false; - } - } UpdatePlayerStatus(uid, temp, blockGame: component); } @@ -115,7 +104,6 @@ public sealed class BlockGameArcadeSystem : EntitySystem _uiSystem.CloseAll(bui); component.Player = null; component.Spectators.Clear(); - component.ShouldSayThankYou = false; } private void OnPlayerAction(EntityUid uid, BlockGameArcadeComponent component, BlockGameMessages.BlockGamePlayerActionMessage msg) @@ -135,7 +123,8 @@ public sealed class BlockGameArcadeSystem : EntitySystem return; } - component.ShouldSayThankYou = true; + if (TryComp(uid, out var speakComponent)) + _speakOnUIClosed.TrySetFlag((uid, speakComponent)); component.Game.ProcessInput(msg.PlayerAction); } diff --git a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs index c3a8877393..e93fcc6e8f 100644 --- a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs +++ b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeComponent.cs @@ -110,9 +110,4 @@ public sealed partial class SpaceVillainArcadeComponent : SharedSpaceVillainArca ///
[ViewVariables(VVAccess.ReadWrite)] public int RewardAmount = 0; - - /// - /// Whether the game machine should thank (or otherwise talk to) the player when they leave - /// - public bool ShouldSayThankYou; } diff --git a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs index 4d704b8a81..f60d88ebf7 100644 --- a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs +++ b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Power.Components; using Content.Shared.UserInterface; using Content.Server.Advertise; using Content.Server.Advertise.Components; -using Content.Server.Advertise.EntitySystems; using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent; using Robust.Server.GameObjects; using Robust.Shared.Audio; @@ -16,7 +15,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; - [Dependency] private readonly AdvertiseSystem _advertise = default!; + [Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!; public override void Initialize() { @@ -26,7 +25,6 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem SubscribeLocalEvent(OnAfterUIOpenSV); SubscribeLocalEvent(OnSVPlayerAction); SubscribeLocalEvent(OnSVillainPower); - SubscribeLocalEvent(OnBoundUIClosed); } /// @@ -84,7 +82,9 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem case PlayerAction.Heal: case PlayerAction.Recharge: component.Game.ExecutePlayerAction(uid, msg.PlayerAction, component); - component.ShouldSayThankYou = true; // Any sort of gameplay action counts + // Any sort of gameplay action counts + if (TryComp(uid, out var speakComponent)) + _speakOnUIClosed.TrySetFlag((uid, speakComponent)); break; case PlayerAction.NewGame: _audioSystem.PlayPvs(component.NewGameSound, uid, AudioParams.Default.WithVolume(-4f)); @@ -112,19 +112,5 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem if (_uiSystem.TryGetUi(uid, SpaceVillainArcadeUiKey.Key, out var bui)) _uiSystem.CloseAll(bui); - - component.ShouldSayThankYou = false; - } - - private void OnBoundUIClosed(Entity ent, ref BoundUIClosedEvent args) - { - if (args.UiKey is not SpaceVillainArcadeUiKey || (SpaceVillainArcadeUiKey) args.UiKey != SpaceVillainArcadeUiKey.Key) - return; - - if (ent.Comp.ShouldSayThankYou && TryComp(ent.Owner, out var advertise)) - { - _advertise.SayAdvertisement(ent.Owner, advertise); - ent.Comp.ShouldSayThankYou = false; - } } } diff --git a/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml b/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml index 226f0cb72b..7fb0367295 100644 --- a/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml +++ b/Resources/Prototypes/Catalog/Arcade/Advertisements/blockgame.yml @@ -14,16 +14,3 @@ - advertisement-block-game-11 - advertisement-block-game-12 - advertisement-block-game-13 -# thankyous: -# - thankyou-block-game-1 -# - thankyou-block-game-2 -# - thankyou-block-game-3 -# - thankyou-block-game-4 -# - thankyou-block-game-5 -# - thankyou-block-game-6 -# - thankyou-block-game-7 -# - thankyou-block-game-8 -# - thankyou-block-game-9 -# - thankyou-block-game-10 -# - thankyou-block-game-11 -# - thankyou-block-game-12 diff --git a/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml b/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml index bee4ca2bae..7c94ab94f6 100644 --- a/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml +++ b/Resources/Prototypes/Catalog/Arcade/Advertisements/spacevillain.yml @@ -16,16 +16,3 @@ - advertisement-space-villain-13 - advertisement-space-villain-14 - advertisement-space-villain-15 -# thankyous: -# - thankyou-space-villain-1 -# - thankyou-space-villain-2 -# - thankyou-space-villain-3 -# - thankyou-space-villain-4 -# - thankyou-space-villain-5 -# - thankyou-space-villain-6 -# - thankyou-space-villain-7 -# - thankyou-space-villain-8 -# - thankyou-space-villain-9 -# - thankyou-space-villain-10 -# - thankyou-space-villain-11 -# - thankyou-space-villain-12 diff --git a/Resources/Prototypes/Catalog/Arcade/Goodbyes/blockgame.yml b/Resources/Prototypes/Catalog/Arcade/Goodbyes/blockgame.yml new file mode 100644 index 0000000000..460e8d13bf --- /dev/null +++ b/Resources/Prototypes/Catalog/Arcade/Goodbyes/blockgame.yml @@ -0,0 +1,15 @@ +- type: messagePack + id: BlockGameGoodbyes + messages: + - thankyou-block-game-1 + - thankyou-block-game-2 + - thankyou-block-game-3 + - thankyou-block-game-4 + - thankyou-block-game-5 + - thankyou-block-game-6 + - thankyou-block-game-7 + - thankyou-block-game-8 + - thankyou-block-game-9 + - thankyou-block-game-10 + - thankyou-block-game-11 + - thankyou-block-game-12 diff --git a/Resources/Prototypes/Catalog/Arcade/Goodbyes/spacevillain.yml b/Resources/Prototypes/Catalog/Arcade/Goodbyes/spacevillain.yml new file mode 100644 index 0000000000..09016afec3 --- /dev/null +++ b/Resources/Prototypes/Catalog/Arcade/Goodbyes/spacevillain.yml @@ -0,0 +1,15 @@ +- type: messagePack + id: SpaceVillainGoodbyes + messages: + - thankyou-space-villain-1 + - thankyou-space-villain-2 + - thankyou-space-villain-3 + - thankyou-space-villain-4 + - thankyou-space-villain-5 + - thankyou-space-villain-6 + - thankyou-space-villain-7 + - thankyou-space-villain-8 + - thankyou-space-villain-9 + - thankyou-space-villain-10 + - thankyou-space-villain-11 + - thankyou-space-villain-12 diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml index 2970e9f854..49f33d1cff 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/arcades.yml @@ -148,6 +148,8 @@ pack: SpaceVillainAds minWait: 60 # Arcades are noisy maxWait: 240 + - type: SpeakOnUIClosed + pack: SpaceVillainGoodbyes - type: entity id: SpaceVillainArcadeFilled @@ -190,3 +192,5 @@ pack: BlockGameAds minWait: 60 # Arcades are noisy maxWait: 240 + - type: SpeakOnUIClosed + pack: BlockGameGoodbyes From df631ff8a07325a889b45235120eec60fe844233 Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Thu, 28 Mar 2024 19:19:53 +0000 Subject: [PATCH 35/56] Fix Incorrect stealth messages appearing on readmin. (#26511) Fix major skill issue/Incorrect stealth messages appearing on readmin. --- Content.Server/Administration/Managers/AdminManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Administration/Managers/AdminManager.cs b/Content.Server/Administration/Managers/AdminManager.cs index 371755dcef..41171eef73 100644 --- a/Content.Server/Administration/Managers/AdminManager.cs +++ b/Content.Server/Administration/Managers/AdminManager.cs @@ -154,7 +154,7 @@ namespace Content.Server.Administration.Managers plyData.ExplicitlyDeadminned = false; reg.Data.Active = true; - if (reg.Data.Stealth) + if (!reg.Data.Stealth) { _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-self-re-admin-message", ("newAdminName", session.Name))); } From aba16198f6846d880b3a1c233b5e9252d21e6985 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:44:44 -0400 Subject: [PATCH 36/56] Go back to constant electrocution damage based on voltage (#26455) Makes electrocution damage based on the voltage of the wire and bring down the damage to a sane level. It's no longer primarily based on the power being received. LV Cable -> 10 damage MV Cable -> 20 damage HV Cable -> 30 damage Having a primarily power-based damage system causes there to be massive fluctuations in damage based on things outside of a regular player's control, like the station power output. This removes a lot of player agency and turns grilles into a risky gamble where they can either do no damage or instantly fry the player due to simply being hooked up to the engine. While this may be a more accurate simulation in some regards, the reality of the gameplay is that it's often just frustrating, resulting in constant death traps as players brushing against electrified grilles and punching wires take absurd amounts of damage. By making them flat rates, it's possible to actually balance the damage output. --- .../Components/ElectrifiedComponent.cs | 26 +++++++++- .../Components/ElectrocutionComponent.cs | 6 --- .../Electrocution/ElectrocutionSystem.cs | 52 +++++-------------- 3 files changed, 38 insertions(+), 46 deletions(-) diff --git a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs b/Content.Server/Electrocution/Components/ElectrifiedComponent.cs index 2f3def6e06..65a539eb08 100644 --- a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs +++ b/Content.Server/Electrocution/Components/ElectrifiedComponent.cs @@ -41,8 +41,32 @@ public sealed partial class ElectrifiedComponent : Component [DataField("lowVoltageNode")] public string? LowVoltageNode; + /// + /// Damage multiplier for HV electrocution + /// + [DataField] + public float HighVoltageDamageMultiplier = 3f; + + /// + /// Shock time multiplier for HV electrocution + /// + [DataField] + public float HighVoltageTimeMultiplier = 1.5f; + + /// + /// Damage multiplier for MV electrocution + /// + [DataField] + public float MediumVoltageDamageMultiplier = 2f; + + /// + /// Shock time multiplier for MV electrocution + /// + [DataField] + public float MediumVoltageTimeMultiplier = 1.25f; + [DataField("shockDamage")] - public int ShockDamage = 20; + public float ShockDamage = 7.5f; /// /// Shock time, in seconds. diff --git a/Content.Server/Electrocution/Components/ElectrocutionComponent.cs b/Content.Server/Electrocution/Components/ElectrocutionComponent.cs index 9da78c9134..7badc85257 100644 --- a/Content.Server/Electrocution/Components/ElectrocutionComponent.cs +++ b/Content.Server/Electrocution/Components/ElectrocutionComponent.cs @@ -15,10 +15,4 @@ public sealed partial class ElectrocutionComponent : Component [DataField("timeLeft")] public float TimeLeft; - - [DataField("accumDamage")] - public float AccumulatedDamage; - - [DataField("baseDamage")] - public float BaseDamage = 20f; } diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index d967013f65..1163306282 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -17,7 +17,6 @@ using Content.Shared.Interaction; using Content.Shared.Inventory; using Content.Shared.Jittering; using Content.Shared.Maps; -using Content.Shared.Mobs; using Content.Shared.Popups; using Content.Shared.Speech.EntitySystems; using Content.Shared.StatusEffect; @@ -98,29 +97,18 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem private void UpdateElectrocutions(float frameTime) { var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var electrocution, out var consumer)) + while (query.MoveNext(out var uid, out var electrocution, out _)) { var timePassed = Math.Min(frameTime, electrocution.TimeLeft); electrocution.TimeLeft -= timePassed; - electrocution.AccumulatedDamage += electrocution.BaseDamage * (consumer.ReceivedPower / consumer.DrawRate) * timePassed; if (!MathHelper.CloseTo(electrocution.TimeLeft, 0)) continue; - if (EntityManager.EntityExists(electrocution.Electrocuting)) - { - // TODO: damage should be scaled by shock damage multiplier - // TODO: better paralyze/jitter timing - var damage = new DamageSpecifier(_prototypeManager.Index(DamageType), (int) electrocution.AccumulatedDamage); + // We tried damage scaling based on power in the past and it really wasn't good. + // Various scaling types didn't fix tiders and HV grilles instantly critting players. - var actual = _damageable.TryChangeDamage(electrocution.Electrocuting, damage, origin: electrocution.Source); - if (actual != null) - { - _adminLogger.Add(LogType.Electrocution, - $"{ToPrettyString(electrocution.Electrocuting):entity} received {actual.GetTotal():damage} powered electrocution damage from {ToPrettyString(electrocution.Source):source}"); - } - } QueueDel(uid); } } @@ -198,7 +186,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem if (!_meleeWeapon.GetDamage(args.Used, args.User).Any()) return; - DoCommonElectrocution(args.User, uid, component.UnarmedHitShock, component.UnarmedHitStun, false, 1); + DoCommonElectrocution(args.User, uid, component.UnarmedHitShock, component.UnarmedHitStun, false); } private void OnElectrifiedInteractUsing(EntityUid uid, ElectrifiedComponent electrified, InteractUsingEvent args) @@ -213,16 +201,6 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem TryDoElectrifiedAct(uid, args.User, siemens, electrified); } - private float CalculateElectrifiedDamageScale(float power) - { - // A logarithm allows a curve of damage that grows quickly, but slows down dramatically past a value. This keeps the damage to a reasonable range. - const float DamageShift = 1.67f; // Shifts the curve for an overall higher or lower damage baseline - const float CeilingCoefficent = 1.35f; // Adjusts the approach to maximum damage, higher = Higher top damage - const float LogGrowth = 0.00001f; // Adjusts the growth speed of the curve - - return DamageShift + MathF.Log(power * LogGrowth) * CeilingCoefficent; - } - public bool TryDoElectrifiedAct(EntityUid uid, EntityUid targetUid, float siemens = 1, ElectrifiedComponent? electrified = null, @@ -263,19 +241,15 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem } var node = PoweredNode(uid, electrified, nodeContainer); - if (node?.NodeGroup is not IBasePowerNet powerNet) + if (node?.NodeGroup is not IBasePowerNet) return false; - var net = powerNet.NetworkNode; - var supp = net.LastCombinedMaxSupply; - - if (supp <= 0f) - return false; - - // Initial damage scales off of the available supply on the principle that the victim has shorted the entire powernet through their body. - var damageScale = CalculateElectrifiedDamageScale(supp); - if (damageScale <= 0f) - return false; + var (damageScalar, timeScalar) = node.NodeGroupID switch + { + NodeGroupID.HVPower => (electrified.HighVoltageDamageMultiplier, electrified.HighVoltageTimeMultiplier), + NodeGroupID.MVPower => (electrified.MediumVoltageDamageMultiplier, electrified.MediumVoltageTimeMultiplier), + _ => (1f, 1f) + }; { var lastRet = true; @@ -286,8 +260,8 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem entity, uid, node, - (int) MathF.Ceiling(electrified.ShockDamage * damageScale * MathF.Pow(RecursiveDamageMultiplier, depth)), - TimeSpan.FromSeconds(electrified.ShockTime * MathF.Min(1f + MathF.Log2(1f + damageScale), 3f) * MathF.Pow(RecursiveTimeMultiplier, depth)), + (int) (electrified.ShockDamage * MathF.Pow(RecursiveDamageMultiplier, depth) * damageScalar), + TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth) * timeScalar), true, electrified.SiemensCoefficient); } From daff65582dba2d305d1d2a4fd6a7f7b82250efbd Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 28 Mar 2024 20:45:52 +0000 Subject: [PATCH 37/56] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index db48f993d5..33bb70f3c8 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: mirrorcult - changes: - - message: Destruction & impact sounds have been reworked in general, you should - expect better sounds/more variance/actually playing sounds when applicable - type: Add - - message: Melee hit sounds being cut off when an entity is destroyed has been fixed - type: Fix - id: 5746 - time: '2024-01-19T15:33:08.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24282 - author: SpeltIncorrectyl changes: - message: Emagging the artifact crusher now stops it from being opened while it @@ -3798,3 +3788,11 @@ id: 6245 time: '2024-03-28T06:36:43.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25768 +- author: EmoGarbage404 + changes: + - message: Electrocution damage is no longer based on the power supplied and is + instead based on the wire voltage (LV, MV, or HV). + type: Tweak + id: 6246 + time: '2024-03-28T20:44:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26455 From 66e8e05c462a61b808c5e194798176936890ef4e Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Thu, 28 Mar 2024 21:06:23 -0400 Subject: [PATCH 38/56] more melee weapon sound fixes (#26520) g --- .../Entities/Objects/Specific/Janitorial/janitor.yml | 4 ++++ .../Entities/Objects/Weapons/Melee/weapon_toolbox.yml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index 2ddb21b9e6..db08481dc5 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -11,6 +11,8 @@ damage: types: Blunt: 10 + soundHit: + collection: MetalThud - type: Spillable solution: absorbed - type: Wieldable @@ -49,6 +51,8 @@ damage: types: Blunt: 10 + soundHit: + collection: MetalThud - type: Spillable solution: absorbed - type: Wieldable diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/weapon_toolbox.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/weapon_toolbox.yml index 366aabd2f2..240a17a0a4 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/weapon_toolbox.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/weapon_toolbox.yml @@ -17,3 +17,5 @@ damage: types: Blunt: 20 + soundHit: + path: "/Audio/Weapons/smash.ogg" From 8537e279e18b59080d5b86795fac71906534085f Mon Sep 17 00:00:00 2001 From: Valent <108285874+ValentFingerov@users.noreply.github.com> Date: Fri, 29 Mar 2024 06:07:38 +0500 Subject: [PATCH 39/56] Foldable ushanka (#26519) * UpdateUshankaPrototype * UpdateUshankaPrototype --- .../Entities/Clothing/Head/hats.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index dc57302619..fba77d885f 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -476,17 +476,30 @@ - WhitelistChameleon - type: entity - parent: ClothingHeadBase + parent: [ClothingHeadBase, BaseFoldable] id: ClothingHeadHatUshanka name: ushanka description: "Perfect for winter in Siberia, da?" components: - - type: Sprite - sprite: Clothing/Head/Hats/ushanka.rsi - type: Clothing sprite: Clothing/Head/Hats/ushanka.rsi + - type: Appearance - type: AddAccentClothing accent: RussianAccent + - type: Foldable + canFoldInsideContainer: true + - type: FoldableClothing + foldedEquippedPrefix: up + foldedHeldPrefix: up + - type: Sprite + sprite: Clothing/Head/Hats/ushanka.rsi + layers: + - state: icon + map: [ "unfoldedLayer" ] + - state: icon-up + map: ["foldedLayer"] + visible: false + - type: entity parent: ClothingHeadBase From cfc0ecbf80ab497a01443aea354f4ac3dc205b58 Mon Sep 17 00:00:00 2001 From: Jake Huxell Date: Thu, 28 Mar 2024 22:30:23 -0400 Subject: [PATCH 40/56] [BugFix] Persist Job Restrictions When New User Late Joins (#26498) * make sure to keep late join button disabled if requirements not met * more succinct representation of condition --- Content.Client/LateJoin/LateJoinGui.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Client/LateJoin/LateJoinGui.cs b/Content.Client/LateJoin/LateJoinGui.cs index d6a028d6c4..ba9351d674 100644 --- a/Content.Client/LateJoin/LateJoinGui.cs +++ b/Content.Client/LateJoin/LateJoinGui.cs @@ -309,7 +309,7 @@ namespace Content.Client.LateJoin if (matchingJobButton.Amount != updatedJobValue) { matchingJobButton.RefreshLabel(updatedJobValue); - matchingJobButton.Disabled = matchingJobButton.Amount == 0; + matchingJobButton.Disabled |= matchingJobButton.Amount == 0; } } } From 653d53db13ce07542ccc1e84701b776948b8e4ba Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 29 Mar 2024 02:31:29 +0000 Subject: [PATCH 41/56] 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 33bb70f3c8..7a832f5412 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: SpeltIncorrectyl - changes: - - message: Emagging the artifact crusher now stops it from being opened while it - is crushing. - type: Add - id: 5747 - time: '2024-01-19T15:35:02.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/23957 - author: EmoGarbage404 changes: - message: You can now sort lathe recipes by category. @@ -3796,3 +3788,10 @@ id: 6246 time: '2024-03-28T20:44:44.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26455 +- author: Jake Huxell + changes: + - message: Late join menu correctly respects client role restrictions. + type: Fix + id: 6247 + time: '2024-03-29T02:30:23.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26498 From 69a15ada7f83cd7ad6010c7d6ac80ab7fc6b1e23 Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Fri, 29 Mar 2024 06:03:34 +0100 Subject: [PATCH 42/56] Send what seleted for secret to admin chat (#26500) * Send what seleted for secret to admin chat * add line * Add localization support --- Content.Server/GameTicking/Rules/SecretRuleSystem.cs | 3 +++ Resources/Locale/en-US/game-ticking/game-rules/rule-secret.ftl | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 Resources/Locale/en-US/game-ticking/game-rules/rule-secret.ftl diff --git a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs index 6a00eb7d10..fa5f17b4f3 100644 --- a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Administration.Logs; +using Content.Server.Chat.Managers; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; using Content.Shared.Random; @@ -17,6 +18,7 @@ public sealed class SecretRuleSystem : GameRuleSystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly IChatManager _chatManager = default!; protected override void Added(EntityUid uid, SecretRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { @@ -42,6 +44,7 @@ public sealed class SecretRuleSystem : GameRuleSystem var preset = _prototypeManager.Index(presetString).Pick(_random); Log.Info($"Selected {preset} for secret."); _adminLogger.Add(LogType.EventStarted, $"Selected {preset} for secret."); + _chatManager.SendAdminAnnouncement(Loc.GetString("rule-secret-selected-preset", ("preset", preset))); var rules = _prototypeManager.Index(preset).Rules; foreach (var rule in rules) diff --git a/Resources/Locale/en-US/game-ticking/game-rules/rule-secret.ftl b/Resources/Locale/en-US/game-ticking/game-rules/rule-secret.ftl new file mode 100644 index 0000000000..c38220cca1 --- /dev/null +++ b/Resources/Locale/en-US/game-ticking/game-rules/rule-secret.ftl @@ -0,0 +1,2 @@ +# Sent to admin chat +rule-secret-selected-preset = Selected {$preset} for secret. From cdfa7c3c7031ac01dd6a1479b6df7abc9e435190 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 29 Mar 2024 05:04:40 +0000 Subject: [PATCH 43/56] Automatic changelog update --- Resources/Changelog/Admin.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 48cbebdb29..b4b7f699fe 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -129,5 +129,12 @@ Entries: id: 17 time: '2024-03-24T15:39:54.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26263 +- author: lzk228 + changes: + - message: Selected preset for secret is now sent to admin chat. + type: Tweak + id: 18 + time: '2024-03-29T05:03:34.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26500 Name: Admin Order: 1 From 59e46aab93ca38f8d57fcad4e3a2c893737d9ad4 Mon Sep 17 00:00:00 2001 From: Jake Huxell Date: Fri, 29 Mar 2024 01:28:16 -0400 Subject: [PATCH 44/56] Reduced Warning Count By 130 For Full Rebuilds (#26518) * remove deprecated entity coordinate extension functions. Reduces warning count by approximately 50 * final toCoords Removed * Remove all unused variables and dead code paths * remove always true variable, should be a cvar or something instead * remove superfluous variables from tests --- Content.Client/Alerts/ClientAlertsSystem.cs | 2 - .../Clothing/ClientClothingSystem.cs | 2 +- Content.Client/Doors/DoorSystem.cs | 2 - .../IconSmoothing/IconSmoothSystem.cs | 2 - Content.Client/Items/Systems/ItemSystem.cs | 2 +- Content.Client/NPC/PathfindingSystem.cs | 1 - .../Shuttles/UI/BaseShuttleControl.xaml.cs | 1 - Content.IntegrationTests/PoolManager.cs | 5 - .../Tests/Fluids/PuddleTest.cs | 2 +- .../Tests/Power/PowerTest.cs | 167 ++++++++++-------- .../Tests/PrototypeSaveTest.cs | 4 +- .../Commands/PersistenceSaveCommand.cs | 6 - .../Commands/VariantizeCommand.cs | 2 - .../Anomaly/Effects/EntityAnomalySystem.cs | 2 - .../Atmos/EntitySystems/AirFilterSystem.cs | 3 - .../Atmos/EntitySystems/GasTankSystem.cs | 4 - .../EntitySystems/GasVolumePumpSystem.cs | 1 - .../Unary/EntitySystems/GasCanisterSystem.cs | 3 - Content.Server/Body/Systems/BodySystem.cs | 4 - .../Body/Systems/InternalsSystem.cs | 2 - Content.Server/Cargo/Systems/CargoSystem.cs | 1 - Content.Server/Cargo/Systems/PricingSystem.cs | 2 - .../EntitySystems/SolutionContainerSystem.cs | 3 - .../EntitySystems/ReagentDispenserSystem.cs | 2 - .../Decals/Commands/EditDecalCommand.cs | 2 - .../Decals/Commands/RemoveDecalCommand.cs | 3 - .../Unit/EntitySystems/DisposableSystem.cs | 3 - .../ExplosionSystem.Processing.cs | 11 -- .../EyeProtection/EyeProtectionSystem.cs | 3 - .../Gateway/Systems/GatewayGeneratorSystem.cs | 8 - .../Instruments/InstrumentComponent.cs | 1 - .../Interaction/InteractionSystem.cs | 2 - Content.Server/Mech/Systems/MechSystem.cs | 3 - .../Medical/SuitSensors/SuitSensorSystem.cs | 2 - .../Ninja/Systems/StunProviderSystem.cs | 4 - .../EntitySystems/SliceableFoodSystem.cs | 2 - .../Systems/NinjaConditionsSystem.cs | 2 - .../ParticleAcceleratorSystem.cs | 2 - .../Polymorph/Systems/PolymorphSystem.cs | 1 - .../EntitySystems/ExtensionCableSystem.cs | 3 - .../Generator/PortableGeneratorSystem.cs | 2 - Content.Server/RatKing/RatKingSystem.cs | 2 - Content.Server/Roles/RemoveRoleCommand.cs | 2 - .../Sandbox/Commands/ColorNetworkCommand.cs | 1 - .../Shuttles/Systems/ArrivalsSystem.cs | 5 - .../Shuttles/Systems/ThrusterSystem.cs | 2 - .../Silicons/Laws/SiliconLawSystem.cs | 2 - .../EntitySystems/RadiationCollectorSystem.cs | 1 - Content.Server/Spreader/SpreaderSystem.cs | 1 - .../Station/Systems/StationJobsSystem.cs | 1 - .../Station/Systems/StationSystem.cs | 1 - .../StationEvents/Events/NinjaSpawnRule.cs | 3 - .../SurveillanceCameraSpeakerSystem.cs | 5 - .../Temperature/Systems/TemperatureSystem.cs | 2 - .../Weapons/Ranged/Systems/GunSystem.cs | 2 - .../Systems/ArtifactGasTriggerSystem.cs | 2 - .../Systems/ArtifactHeatTriggerSystem.cs | 2 - .../Systems/ArtifactPressureTriggerSystem.cs | 2 - Content.Server/Zombies/ZombieSystem.cs | 3 - .../Climbing/Systems/ClimbSystem.cs | 1 - .../Construction/SharedFlatpackSystem.cs | 3 +- .../EntityCoordinatesExtensions.cs | 13 -- Content.Shared/Maps/TurfSystem.cs | 1 - Content.Shared/RCD/Systems/RCDSystem.cs | 5 +- .../EntitySystems/SharedDoorRemoteSystem.cs | 3 - .../Shuttles/Components/IFFComponent.cs | 5 - .../Systems/SharedShuttleSystem.IFF.cs | 5 - .../Shuttles/Systems/SharedShuttleSystem.cs | 1 - .../Systems/SharedGunSystem.Interactions.cs | 2 +- 69 files changed, 101 insertions(+), 256 deletions(-) diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs index 9c4ebb9cd2..223bf7876a 100644 --- a/Content.Client/Alerts/ClientAlertsSystem.cs +++ b/Content.Client/Alerts/ClientAlertsSystem.cs @@ -4,7 +4,6 @@ using JetBrains.Annotations; using Robust.Client.Player; using Robust.Shared.Player; using Robust.Shared.Prototypes; -using Robust.Shared.Timing; namespace Content.Client.Alerts; @@ -13,7 +12,6 @@ public sealed class ClientAlertsSystem : AlertsSystem { public AlertOrderPrototype? AlertOrder { get; set; } - [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; diff --git a/Content.Client/Clothing/ClientClothingSystem.cs b/Content.Client/Clothing/ClientClothingSystem.cs index fbe9d5ec5b..7e78ac7d70 100644 --- a/Content.Client/Clothing/ClientClothingSystem.cs +++ b/Content.Client/Clothing/ClientClothingSystem.cs @@ -133,7 +133,7 @@ public sealed class ClientClothingSystem : ClothingSystem else if (TryComp(uid, out SpriteComponent? sprite)) rsi = sprite.BaseRSI; - if (rsi == null || rsi.Path == null) + if (rsi == null) return false; var correctedSlot = slot; diff --git a/Content.Client/Doors/DoorSystem.cs b/Content.Client/Doors/DoorSystem.cs index 473ae97059..bc52730b0e 100644 --- a/Content.Client/Doors/DoorSystem.cs +++ b/Content.Client/Doors/DoorSystem.cs @@ -4,14 +4,12 @@ using Robust.Client.Animations; using Robust.Client.GameObjects; using Robust.Client.ResourceManagement; using Robust.Shared.Serialization.TypeSerializers.Implementations; -using Robust.Shared.Timing; namespace Content.Client.Doors; public sealed class DoorSystem : SharedDoorSystem { [Dependency] private readonly AnimationPlayerSystem _animationSystem = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IResourceCache _resourceCache = default!; public override void Initialize() diff --git a/Content.Client/IconSmoothing/IconSmoothSystem.cs b/Content.Client/IconSmoothing/IconSmoothSystem.cs index 20a80c42a3..4b02560846 100644 --- a/Content.Client/IconSmoothing/IconSmoothSystem.cs +++ b/Content.Client/IconSmoothing/IconSmoothSystem.cs @@ -16,8 +16,6 @@ namespace Content.Client.IconSmoothing [UsedImplicitly] public sealed partial class IconSmoothSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; - private readonly Queue _dirtyEntities = new(); private readonly Queue _anchorChangedEntities = new(); diff --git a/Content.Client/Items/Systems/ItemSystem.cs b/Content.Client/Items/Systems/ItemSystem.cs index e406ba2b55..5e60d06d0c 100644 --- a/Content.Client/Items/Systems/ItemSystem.cs +++ b/Content.Client/Items/Systems/ItemSystem.cs @@ -93,7 +93,7 @@ public sealed class ItemSystem : SharedItemSystem else if (TryComp(uid, out SpriteComponent? sprite)) rsi = sprite.BaseRSI; - if (rsi == null || rsi.Path == null) + if (rsi == null) return false; var state = (item.HeldPrefix == null) diff --git a/Content.Client/NPC/PathfindingSystem.cs b/Content.Client/NPC/PathfindingSystem.cs index 7bf3df1f0b..709601a57b 100644 --- a/Content.Client/NPC/PathfindingSystem.cs +++ b/Content.Client/NPC/PathfindingSystem.cs @@ -289,7 +289,6 @@ namespace Content.Client.NPC var invGridMatrix = gridXform.InvWorldMatrix; DebugPathPoly? nearest = null; - var nearestDistance = float.MaxValue; foreach (var poly in tile) { diff --git a/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs b/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs index fed2a9f171..284c668190 100644 --- a/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs +++ b/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs @@ -88,7 +88,6 @@ public partial class BaseShuttleControl : MapGridControl var cornerDistance = MathF.Sqrt(WorldRange * WorldRange + WorldRange * WorldRange); var origin = ScalePosition(-new Vector2(Offset.X, -Offset.Y)); - var distOffset = -24f; for (var radius = minDistance; radius <= maxDistance; radius *= EquatorialMultiplier) { diff --git a/Content.IntegrationTests/PoolManager.cs b/Content.IntegrationTests/PoolManager.cs index 6046ed65e4..b544fe2854 100644 --- a/Content.IntegrationTests/PoolManager.cs +++ b/Content.IntegrationTests/PoolManager.cs @@ -306,11 +306,6 @@ public static partial class PoolManager Pairs[fallback!] = true; } - if (fallback == null && _pairId > 8) - { - var x = 2; - } - return fallback; } } diff --git a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs index 3213bba51f..611af67380 100644 --- a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs @@ -67,7 +67,7 @@ namespace Content.IntegrationTests.Tests.Fluids await server.WaitAssertion(() => { - var coordinates = grid.ToCoordinates(); + var coordinates = grid.Owner.ToCoordinates(); var solution = new Solution("Water", FixedPoint2.New(20)); Assert.That(spillSystem.TrySpillAt(coordinates, solution, out _), Is.False); diff --git a/Content.IntegrationTests/Tests/Power/PowerTest.cs b/Content.IntegrationTests/Tests/Power/PowerTest.cs index d4e2cde9b0..a6af3e6a65 100644 --- a/Content.IntegrationTests/Tests/Power/PowerTest.cs +++ b/Content.IntegrationTests/Tests/Power/PowerTest.cs @@ -176,16 +176,18 @@ namespace Content.IntegrationTests.Tests.Power var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; + // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates()); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); consumer1 = entityManager.GetComponent(consumerEnt1); @@ -237,16 +239,18 @@ namespace Content.IntegrationTests.Tests.Power var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; + // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates()); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); consumer1 = entityManager.GetComponent(consumerEnt1); @@ -292,16 +296,17 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates()); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); consumer = entityManager.GetComponent(consumerEnt); @@ -383,16 +388,17 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.ToCoordinates()); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", gridOwner.ToCoordinates()); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); netBattery = entityManager.GetComponent(generatorEnt); battery = entityManager.GetComponent(generatorEnt); @@ -486,17 +492,18 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates()); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1)); - var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1)); + var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", gridOwner.ToCoordinates(0, 2)); netBattery = entityManager.GetComponent(batteryEnt); battery = entityManager.GetComponent(batteryEnt); supplier = entityManager.GetComponent(generatorEnt); @@ -577,16 +584,17 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates()); - var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); + var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", gridOwner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); var netBattery = entityManager.GetComponent(batteryEnt); @@ -635,20 +643,21 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 4; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3)); + var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3)); consumer = entityManager.GetComponent(consumerEnt); supplier = entityManager.GetComponent(supplyEnt); @@ -712,20 +721,21 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 4; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3)); + var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3)); consumer = entityManager.GetComponent(consumerEnt); supplier = entityManager.GetComponent(supplyEnt); @@ -787,6 +797,7 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Map layout here is // C - consumer @@ -800,18 +811,18 @@ namespace Content.IntegrationTests.Tests.Power for (var i = 0; i < 5; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2)); - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2)); + entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 1)); - var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 3)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 2)); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 4)); + var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 1)); + var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 3)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 2)); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 4)); consumer1 = entityManager.GetComponent(consumerEnt1); consumer2 = entityManager.GetComponent(consumerEnt2); @@ -888,6 +899,7 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Layout is two generators, two batteries, and one load. As to why two: because previously this test // would fail ONLY if there were more than two batteries present, because each of them tries to supply @@ -900,16 +912,16 @@ namespace Content.IntegrationTests.Tests.Power for (var i = -2; i <= 2; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2)); - var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, -2)); + var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, -2)); - var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 1)); - var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, -1)); + var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 1)); + var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, -1)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0)); consumer = entityManager.GetComponent(consumerEnt); supplier1 = entityManager.GetComponent(supplyEnt1); @@ -981,6 +993,7 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Map layout here is // C - consumer @@ -994,18 +1007,18 @@ namespace Content.IntegrationTests.Tests.Power for (var i = 0; i < 5; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2)); - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2)); + entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 1)); - var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 3)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 2)); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 4)); + var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 1)); + var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 3)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 2)); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 4)); consumer1 = entityManager.GetComponent(consumerEnt1); consumer2 = entityManager.GetComponent(consumerEnt2); @@ -1068,20 +1081,21 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 4; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3)); + var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3)); consumer = entityManager.GetComponent(consumerEnt); supplier = entityManager.GetComponent(supplyEnt); @@ -1153,6 +1167,7 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 4; i++) @@ -1160,15 +1175,15 @@ namespace Content.IntegrationTests.Tests.Power grid.SetTile(new Vector2i(0, i), new Tile(1)); } - var leftEnt = entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 0)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 2)); - var rightEnt = entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 3)); + var leftEnt = entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 0)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 1)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 2)); + var rightEnt = entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 3)); - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var battery = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2)); + var battery = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); var batteryNodeContainer = entityManager.GetComponent(battery); if (nodeContainer.TryGetNode(entityManager.GetComponent(leftEnt), @@ -1216,6 +1231,7 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 3; i++) @@ -1223,14 +1239,14 @@ namespace Content.IntegrationTests.Tests.Power grid.SetTile(new Vector2i(0, i), new Tile(1)); } - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 0)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 1)); - entityManager.SpawnEntity("CableMV", grid.ToCoordinates(0, 1)); - entityManager.SpawnEntity("CableMV", grid.ToCoordinates(0, 2)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 0)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 1)); + entityManager.SpawnEntity("CableMV", gridOwner.ToCoordinates(0, 1)); + entityManager.SpawnEntity("CableMV", gridOwner.ToCoordinates(0, 2)); - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0)); - var substationEnt = entityManager.SpawnEntity("SubstationDummy", grid.ToCoordinates(0, 1)); - var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); + var substationEnt = entityManager.SpawnEntity("SubstationDummy", gridOwner.ToCoordinates(0, 1)); + var apcEnt = entityManager.SpawnEntity("ApcDummy", gridOwner.ToCoordinates(0, 2)); var generatorSupplier = entityManager.GetComponent(generatorEnt); substationNetBattery = entityManager.GetComponent(substationEnt); @@ -1273,6 +1289,7 @@ namespace Content.IntegrationTests.Tests.Power { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; const int range = 5; @@ -1282,15 +1299,15 @@ namespace Content.IntegrationTests.Tests.Power grid.SetTile(new Vector2i(0, i), new Tile(1)); } - var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.ToCoordinates(0, 0)); - var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", grid.ToCoordinates(0, 0)); + var apcEnt = entityManager.SpawnEntity("ApcDummy", gridOwner.ToCoordinates(0, 0)); + var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", gridOwner.ToCoordinates(0, 0)); // Create a powered receiver in range (range is 0 indexed) - var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.ToCoordinates(0, range - 1)); + var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", gridOwner.ToCoordinates(0, range - 1)); receiver = entityManager.GetComponent(powerReceiverEnt); // Create an unpowered receiver outside range - var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.ToCoordinates(0, range)); + var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", gridOwner.ToCoordinates(0, range)); unpoweredReceiver = entityManager.GetComponent(unpoweredReceiverEnt); var battery = entityManager.GetComponent(apcEnt); diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs index 6096c497ef..e4a9c1a840 100644 --- a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs +++ b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs @@ -59,7 +59,7 @@ public sealed class PrototypeSaveTest var tileDefinition = tileDefinitionManager["FloorSteel"]; // Wires n such disable ambiance while under the floor var tile = new Tile(tileDefinition.TileId); - var coordinates = grid.ToCoordinates(); + var coordinates = grid.Owner.ToCoordinates(); grid.SetTile(coordinates, tile); }); @@ -94,7 +94,7 @@ public sealed class PrototypeSaveTest await server.WaitAssertion(() => { Assert.That(!mapManager.IsMapInitialized(mapId)); - var testLocation = grid.ToCoordinates(); + var testLocation = grid.Owner.ToCoordinates(); Assert.Multiple(() => { diff --git a/Content.Server/Administration/Commands/PersistenceSaveCommand.cs b/Content.Server/Administration/Commands/PersistenceSaveCommand.cs index 2684e85d5f..7ef1932c56 100644 --- a/Content.Server/Administration/Commands/PersistenceSaveCommand.cs +++ b/Content.Server/Administration/Commands/PersistenceSaveCommand.cs @@ -1,11 +1,6 @@ -using Content.Server.GameTicking; -using Content.Server.Ghost.Components; -using Content.Server.Players; using Content.Shared.Administration; using Content.Shared.CCVar; -using Content.Shared.Ghost; using Robust.Server.GameObjects; -using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.Map; @@ -17,7 +12,6 @@ namespace Content.Server.Administration.Commands; public sealed class PersistenceSave : IConsoleCommand { [Dependency] private readonly IConfigurationManager _config = default!; - [Dependency] private readonly IEntityManager _entities = default!; [Dependency] private readonly IEntitySystemManager _system = default!; [Dependency] private readonly IMapManager _map = default!; diff --git a/Content.Server/Administration/Commands/VariantizeCommand.cs b/Content.Server/Administration/Commands/VariantizeCommand.cs index 7aabd76335..3f9b7efd07 100644 --- a/Content.Server/Administration/Commands/VariantizeCommand.cs +++ b/Content.Server/Administration/Commands/VariantizeCommand.cs @@ -3,7 +3,6 @@ using Content.Shared.Maps; using Robust.Shared.Console; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Random; namespace Content.Server.Administration.Commands; @@ -11,7 +10,6 @@ namespace Content.Server.Administration.Commands; public sealed class VariantizeCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; public string Command => "variantize"; diff --git a/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs b/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs index 7c397d6888..90a655fbba 100644 --- a/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs @@ -2,7 +2,6 @@ using Content.Shared.Anomaly; using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Effects; using Content.Shared.Anomaly.Effects.Components; -using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Random; @@ -12,7 +11,6 @@ namespace Content.Server.Anomaly.Effects; public sealed class EntityAnomalySystem : SharedEntityAnomalySystem { [Dependency] private readonly SharedAnomalySystem _anomaly = default!; - [Dependency] private readonly IMapManager _map = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; diff --git a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs index 416045fc5e..d947e60b6d 100644 --- a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs @@ -1,8 +1,6 @@ -using Content.Server.Atmos; using Content.Server.Atmos.Components; using Content.Server.Atmos.Piping.Components; using Content.Shared.Atmos; -using Robust.Shared.GameObjects; using Robust.Shared.Map; using System.Diagnostics.CodeAnalysis; @@ -15,7 +13,6 @@ 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/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index dfe8447340..aed00432e1 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -1,4 +1,3 @@ -using System.Numerics; using Content.Server.Atmos.Components; using Content.Server.Body.Components; using Content.Server.Body.Systems; @@ -17,8 +16,6 @@ using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Player; using Robust.Shared.Random; namespace Content.Server.Atmos.EntitySystems @@ -33,7 +30,6 @@ namespace Content.Server.Atmos.EntitySystems [Dependency] private readonly SharedContainerSystem _containers = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ThrowingSystem _throwing = default!; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs index 10b9cccc09..8e478bd2b5 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs @@ -27,7 +27,6 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems public sealed class GasVolumePumpSystem : EntitySystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index ad647fad1b..170586339d 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -13,7 +13,6 @@ using Content.Shared.Atmos; using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; -using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Lock; using Robust.Server.GameObjects; @@ -29,8 +28,6 @@ public sealed class GasCanisterSystem : EntitySystem [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index 92074d06ff..e10158cf35 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -10,8 +10,6 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Random; using Robust.Shared.Timing; using System.Numerics; @@ -23,9 +21,7 @@ public sealed class BodySystem : SharedBodySystem [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!; [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!; - [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index 999aa40077..972967fb15 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Components; -using Content.Server.Hands.Systems; using Content.Server.Popups; using Content.Shared.Alert; using Content.Shared.Atmos; @@ -11,7 +10,6 @@ using Content.Shared.Internals; using Content.Shared.Inventory; using Content.Shared.Verbs; using Robust.Shared.Containers; -using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Body.Systems; diff --git a/Content.Server/Cargo/Systems/CargoSystem.cs b/Content.Server/Cargo/Systems/CargoSystem.cs index d4be68efc8..badad9e57b 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.cs @@ -37,7 +37,6 @@ public sealed partial class CargoSystem : SharedCargoSystem [Dependency] private readonly PricingSystem _pricing = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedTransformSystem _xformSystem = default!; [Dependency] private readonly ShuttleConsoleSystem _console = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly StationSystem _station = default!; diff --git a/Content.Server/Cargo/Systems/PricingSystem.cs b/Content.Server/Cargo/Systems/PricingSystem.cs index 6fb36c9608..9e1970d63c 100644 --- a/Content.Server/Cargo/Systems/PricingSystem.cs +++ b/Content.Server/Cargo/Systems/PricingSystem.cs @@ -12,7 +12,6 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Stacks; using Robust.Shared.Console; using Robust.Shared.Containers; -using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -27,7 +26,6 @@ public sealed class PricingSystem : EntitySystem { [Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly IConsoleHost _consoleHost = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly BodySystem _bodySystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; diff --git a/Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs b/Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs index 7926121c2b..468212f5ea 100644 --- a/Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs +++ b/Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs @@ -4,7 +4,6 @@ using Content.Shared.Chemistry.EntitySystems; using Content.Shared.FixedPoint; using Robust.Shared.Containers; using Robust.Shared.Map; -using Robust.Shared.Network; using Robust.Shared.Utility; using System.Numerics; @@ -12,8 +11,6 @@ namespace Content.Server.Chemistry.Containers.EntitySystems; public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem { - [Dependency] private readonly INetManager _netManager = default!; - public override void Initialize() { base.Initialize(); diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs index b93498fe31..a8583e6bcb 100644 --- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.Administration.Logs; using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Nutrition.EntitySystems; @@ -30,7 +29,6 @@ namespace Content.Server.Chemistry.EntitySystems [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly OpenableSystem _openable = default!; public override void Initialize() diff --git a/Content.Server/Decals/Commands/EditDecalCommand.cs b/Content.Server/Decals/Commands/EditDecalCommand.cs index 2344b0a367..812e131832 100644 --- a/Content.Server/Decals/Commands/EditDecalCommand.cs +++ b/Content.Server/Decals/Commands/EditDecalCommand.cs @@ -2,7 +2,6 @@ using System.Numerics; using Content.Server.Administration; using Content.Shared.Administration; using Robust.Shared.Console; -using Robust.Shared.Map; using Robust.Shared.Map.Components; namespace Content.Server.Decals; @@ -11,7 +10,6 @@ namespace Content.Server.Decals; public sealed class EditDecalCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; public string Command => "editdecal"; public string Description => "Edits a decal."; diff --git a/Content.Server/Decals/Commands/RemoveDecalCommand.cs b/Content.Server/Decals/Commands/RemoveDecalCommand.cs index 3fc5c75e10..fc6af4d009 100644 --- a/Content.Server/Decals/Commands/RemoveDecalCommand.cs +++ b/Content.Server/Decals/Commands/RemoveDecalCommand.cs @@ -1,9 +1,7 @@ using Content.Server.Administration; using Content.Shared.Administration; using Robust.Shared.Console; -using Robust.Shared.Map; using Robust.Shared.Map.Components; -using SQLitePCL; namespace Content.Server.Decals.Commands { @@ -11,7 +9,6 @@ namespace Content.Server.Decals.Commands public sealed class RemoveDecalCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; public string Command => "rmdecal"; public string Description => "removes a decal"; diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs index eb3cda4db9..38e3923803 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs @@ -1,4 +1,3 @@ -using System.Linq; using Content.Server.Atmos.EntitySystems; using Content.Server.Disposal.Tube; using Content.Server.Disposal.Tube.Components; @@ -12,14 +11,12 @@ using Robust.Shared.Containers; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; -using Robust.Shared.Random; namespace Content.Server.Disposal.Unit.EntitySystems { public sealed class DisposableSystem : EntitySystem { [Dependency] private readonly ThrowingSystem _throwing = default!; - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly DisposalUnitSystem _disposalUnitSystem = default!; diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index 85d705846e..60db6bdf5b 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -1,4 +1,3 @@ -using System.Linq; using System.Numerics; using Content.Shared.CCVar; using Content.Shared.Damage; @@ -17,7 +16,6 @@ using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.Utility; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; -using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; namespace Content.Server.Explosion.EntitySystems; @@ -48,13 +46,6 @@ public sealed partial class ExplosionSystem /// private Explosion? _activeExplosion; - /// - /// While processing an explosion, the "progress" is sent to clients, so that the explosion fireball effect - /// syncs up with the damage. When the tile iteration increments, an update needs to be sent to clients. - /// This integer keeps track of the last value sent to clients. - /// - private int _previousTileIteration; - /// /// This list is used when raising to avoid allocating a new list per event. /// @@ -112,8 +103,6 @@ public sealed partial class ExplosionSystem if (_activeExplosion == null) continue; - _previousTileIteration = 0; - // just a lil nap if (SleepNodeSys) { diff --git a/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs b/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs index 24ee2b7154..2d54c03b51 100644 --- a/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs +++ b/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs @@ -1,10 +1,8 @@ using Content.Shared.StatusEffect; using Content.Shared.Inventory; -using Content.Shared.Item; using Content.Shared.Eye.Blinding.Components; using Content.Shared.Eye.Blinding.Systems; using Content.Shared.Tools.Components; -using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle.Components; namespace Content.Server.Eye.Blinding.EyeProtection @@ -13,7 +11,6 @@ namespace Content.Server.Eye.Blinding.EyeProtection { [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; [Dependency] private readonly BlindableSystem _blindingSystem = default!; - [Dependency] private readonly SharedItemToggleSystem _itemToggle = default!; public override void Initialize() { diff --git a/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs b/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs index 7558f7afc0..c934fb66bf 100644 --- a/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs +++ b/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs @@ -1,23 +1,16 @@ using System.Linq; -using System.Numerics; using Content.Server.Gateway.Components; using Content.Server.Parallax; using Content.Server.Procedural; -using Content.Server.Salvage; using Content.Shared.CCVar; using Content.Shared.Dataset; using Content.Shared.Maps; -using Content.Shared.Movement.Components; using Content.Shared.Parallax.Biomes; -using Content.Shared.Physics; using Content.Shared.Procedural; using Content.Shared.Salvage; using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Physics.Collision.Shapes; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -40,7 +33,6 @@ public sealed class GatewayGeneratorSystem : EntitySystem [Dependency] private readonly DungeonSystem _dungeon = default!; [Dependency] private readonly GatewaySystem _gateway = default!; [Dependency] private readonly MetaDataSystem _metadata = default!; - [Dependency] private readonly RestrictedRangeSystem _restricted = default!; [Dependency] private readonly SharedMapSystem _maps = default!; [Dependency] private readonly TileSystem _tile = default!; diff --git a/Content.Server/Instruments/InstrumentComponent.cs b/Content.Server/Instruments/InstrumentComponent.cs index 4302ab6791..1b7913386d 100644 --- a/Content.Server/Instruments/InstrumentComponent.cs +++ b/Content.Server/Instruments/InstrumentComponent.cs @@ -1,6 +1,5 @@ using Content.Server.UserInterface; using Content.Shared.Instruments; -using Robust.Server.GameObjects; using Robust.Shared.Player; namespace Content.Server.Instruments; diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs index 6692886dae..203781bcda 100644 --- a/Content.Server/Interaction/InteractionSystem.cs +++ b/Content.Server/Interaction/InteractionSystem.cs @@ -1,4 +1,3 @@ -using Content.Shared.ActionBlocker; using Content.Shared.Interaction; using Content.Shared.Storage; using JetBrains.Annotations; @@ -14,7 +13,6 @@ namespace Content.Server.Interaction [UsedImplicitly] public sealed partial class InteractionSystem : SharedInteractionSystem { - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index 2f5f8bf433..78034e0fc3 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -20,7 +20,6 @@ using Content.Server.Body.Systems; using Robust.Server.Containers; using Robust.Server.GameObjects; using Robust.Shared.Containers; -using Robust.Shared.Map; using Robust.Shared.Player; namespace Content.Server.Mech.Systems; @@ -33,8 +32,6 @@ public sealed partial class MechSystem : SharedMechSystem [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly DamageableSystem _damageable = default!; - [Dependency] private readonly IMapManager _map = default!; - [Dependency] private readonly MapSystem _mapSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs index b807b63e21..29e4ceebbe 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs @@ -9,7 +9,6 @@ using Content.Server.Popups; using Content.Server.Station.Systems; using Content.Shared.Damage; using Content.Shared.DeviceNetwork; -using Content.Shared.Emp; using Content.Shared.Examine; using Content.Shared.Inventory.Events; using Content.Shared.Medical.SuitSensor; @@ -27,7 +26,6 @@ public sealed class SuitSensorSystem : EntitySystem { [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly CrewMonitoringServerSystem _monitoringServerSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; [Dependency] private readonly IdCardSystem _idCardSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; diff --git a/Content.Server/Ninja/Systems/StunProviderSystem.cs b/Content.Server/Ninja/Systems/StunProviderSystem.cs index 636037060a..970ca78e2c 100644 --- a/Content.Server/Ninja/Systems/StunProviderSystem.cs +++ b/Content.Server/Ninja/Systems/StunProviderSystem.cs @@ -1,14 +1,11 @@ using Content.Server.Ninja.Events; using Content.Server.Power.EntitySystems; using Content.Shared.Damage; -using Content.Shared.Damage.Prototypes; using Content.Shared.Interaction; using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Systems; using Content.Shared.Popups; using Content.Shared.Stunnable; -using Content.Shared.Whitelist; -using Robust.Shared.Audio; using Robust.Shared.Prototypes; using Robust.Shared.Audio.Systems; using Robust.Shared.Timing; @@ -23,7 +20,6 @@ public sealed class StunProviderSystem : SharedStunProviderSystem [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; diff --git a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs index e966daf5e5..ea422afdf0 100644 --- a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs @@ -5,7 +5,6 @@ using Content.Shared.Nutrition.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Examine; using Content.Shared.FixedPoint; -using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Robust.Server.GameObjects; using Robust.Shared.Audio; @@ -19,7 +18,6 @@ namespace Content.Server.Nutrition.EntitySystems [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly TransformSystem _xformSystem = default!; public override void Initialize() diff --git a/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs b/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs index 2bd8538af1..888a365a5d 100644 --- a/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs +++ b/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Objectives.Components; using Content.Server.Warps; using Content.Shared.Objectives.Components; -using Content.Shared.Mind; using Content.Shared.Ninja.Components; using Robust.Shared.Random; using Content.Server.Roles; @@ -16,7 +15,6 @@ public sealed class NinjaConditionsSystem : EntitySystem { [Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly NumberObjectiveSystem _number = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() diff --git a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs index ddc7e2a083..e9b62bc4a8 100644 --- a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs +++ b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Administration.Logs; using Content.Server.Chat.Managers; using Content.Server.Projectiles; using Robust.Shared.Physics.Systems; -using Robust.Shared.Map; using Robust.Shared.Timing; using Robust.Server.GameObjects; using Robust.Shared.Configuration; @@ -13,7 +12,6 @@ public sealed partial class ParticleAcceleratorSystem : EntitySystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IChatManager _chat = default!; [Dependency] private readonly ProjectileSystem _projectileSystem = default!; diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs index 66dc9dab99..5fe29dcd30 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs @@ -32,7 +32,6 @@ public sealed partial class PolymorphSystem : EntitySystem [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly ActionsSystem _actions = default!; - [Dependency] private readonly ActionContainerSystem _actionContainer = default!; [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly SharedBuckleSystem _buckle = default!; [Dependency] private readonly ContainerSystem _container = default!; diff --git a/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs b/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs index acfb8ff87b..85e553031f 100644 --- a/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs +++ b/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using Content.Server.Power.Components; -using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; @@ -9,8 +8,6 @@ namespace Content.Server.Power.EntitySystems { public sealed class ExtensionCableSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; - public override void Initialize() { base.Initialize(); diff --git a/Content.Server/Power/Generator/PortableGeneratorSystem.cs b/Content.Server/Power/Generator/PortableGeneratorSystem.cs index 3cd18d7d56..a95a3fd423 100644 --- a/Content.Server/Power/Generator/PortableGeneratorSystem.cs +++ b/Content.Server/Power/Generator/PortableGeneratorSystem.cs @@ -1,5 +1,4 @@ using Content.Server.DoAfter; -using Content.Server.NodeContainer.NodeGroups; using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -28,7 +27,6 @@ public sealed class PortableGeneratorSystem : SharedPortableGeneratorSystem [Dependency] private readonly GeneratorSystem _generator = default!; [Dependency] private readonly PowerSwitchableSystem _switchable = default!; [Dependency] private readonly ActiveGeneratorRevvingSystem _revving = default!; - [Dependency] private readonly PowerNetSystem _powerNet = default!; public override void Initialize() { diff --git a/Content.Server/RatKing/RatKingSystem.cs b/Content.Server/RatKing/RatKingSystem.cs index f676e89ac3..4b82dba335 100644 --- a/Content.Server/RatKing/RatKingSystem.cs +++ b/Content.Server/RatKing/RatKingSystem.cs @@ -11,7 +11,6 @@ using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Pointing; using Content.Shared.RatKing; -using Robust.Server.GameObjects; using Robust.Shared.Map; using Robust.Shared.Random; @@ -26,7 +25,6 @@ namespace Content.Server.RatKing [Dependency] private readonly HungerSystem _hunger = default!; [Dependency] private readonly NPCSystem _npc = default!; [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly TransformSystem _xform = default!; public override void Initialize() { diff --git a/Content.Server/Roles/RemoveRoleCommand.cs b/Content.Server/Roles/RemoveRoleCommand.cs index edb29da624..feba63a253 100644 --- a/Content.Server/Roles/RemoveRoleCommand.cs +++ b/Content.Server/Roles/RemoveRoleCommand.cs @@ -5,14 +5,12 @@ using Content.Shared.Roles; using Content.Shared.Roles.Jobs; using Robust.Server.Player; using Robust.Shared.Console; -using Robust.Shared.Prototypes; namespace Content.Server.Roles { [AdminCommand(AdminFlags.Admin)] public sealed class RemoveRoleCommand : IConsoleCommand { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; public string Command => "rmrole"; diff --git a/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs b/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs index 2ab29d1b2f..6ce8edd1d8 100644 --- a/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs +++ b/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs @@ -11,7 +11,6 @@ namespace Content.Server.Sandbox.Commands [AnyCommand] public sealed class ColorNetworkCommand : IConsoleCommand { - [Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IEntityManager _entManager = default!; public string Command => "colornetwork"; diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index f4dd502b37..ae742cf1f9 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -1,13 +1,10 @@ using System.Linq; -using System.Numerics; using Content.Server.Administration; using Content.Server.GameTicking; using Content.Server.GameTicking.Events; using Content.Server.Parallax; -using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; -using Content.Server.Salvage; using Content.Server.Screens.Components; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; @@ -22,7 +19,6 @@ using Content.Shared.Movement.Components; using Content.Shared.Parallax.Biomes; using Content.Shared.Salvage; using Content.Shared.Shuttles.Components; -using Robust.Shared.Spawners; using Content.Shared.Tiles; using Robust.Server.GameObjects; using Robust.Shared.Collections; @@ -51,7 +47,6 @@ public sealed class ArrivalsSystem : EntitySystem [Dependency] private readonly GameTicker _ticker = default!; [Dependency] private readonly MapLoaderSystem _loader = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; - [Dependency] private readonly RestrictedRangeSystem _restricted = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly ShuttleSystem _shuttles = default!; [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; diff --git a/Content.Server/Shuttles/Systems/ThrusterSystem.cs b/Content.Server/Shuttles/Systems/ThrusterSystem.cs index 97fe19ea74..74c42ccbc5 100644 --- a/Content.Server/Shuttles/Systems/ThrusterSystem.cs +++ b/Content.Server/Shuttles/Systems/ThrusterSystem.cs @@ -1,6 +1,5 @@ using System.Numerics; using Content.Server.Audio; -using Content.Server.Construction; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Shuttles.Components; @@ -25,7 +24,6 @@ namespace Content.Server.Shuttles.Systems; public sealed class ThrusterSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly AmbientSoundSystem _ambient = default!; [Dependency] private readonly FixtureSystem _fixtureSystem = default!; diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs index 4584a9e88b..010682bc0d 100644 --- a/Content.Server/Silicons/Laws/SiliconLawSystem.cs +++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs @@ -19,7 +19,6 @@ using Content.Shared.Silicons.Laws.Components; using Content.Shared.Stunnable; using Content.Shared.Wires; using Robust.Server.GameObjects; -using Robust.Shared.Audio.Systems; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; @@ -38,7 +37,6 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem [Dependency] private readonly SharedStunSystem _stunSystem = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly SharedRoleSystem _roles = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; /// public override void Initialize() diff --git a/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs b/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs index 92b963e201..b26ab301c6 100644 --- a/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs +++ b/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs @@ -24,7 +24,6 @@ public sealed class RadiationCollectorSystem : EntitySystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; - [Dependency] private readonly BatterySystem _batterySystem = default!; private const string GasTankContainer = "gas_tank"; diff --git a/Content.Server/Spreader/SpreaderSystem.cs b/Content.Server/Spreader/SpreaderSystem.cs index 5b2f3298a2..671c281d1f 100644 --- a/Content.Server/Spreader/SpreaderSystem.cs +++ b/Content.Server/Spreader/SpreaderSystem.cs @@ -18,7 +18,6 @@ namespace Content.Server.Spreader; /// public sealed class SpreaderSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly SharedMapSystem _map = default!; diff --git a/Content.Server/Station/Systems/StationJobsSystem.cs b/Content.Server/Station/Systems/StationJobsSystem.cs index a3b7a57354..debac8902e 100644 --- a/Content.Server/Station/Systems/StationJobsSystem.cs +++ b/Content.Server/Station/Systems/StationJobsSystem.cs @@ -25,7 +25,6 @@ public sealed partial class StationJobsSystem : EntitySystem [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly GameTicker _gameTicker = default!; - [Dependency] private readonly StationSystem _stationSystem = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; /// diff --git a/Content.Server/Station/Systems/StationSystem.cs b/Content.Server/Station/Systems/StationSystem.cs index b9ff8a4339..492f15c8e2 100644 --- a/Content.Server/Station/Systems/StationSystem.cs +++ b/Content.Server/Station/Systems/StationSystem.cs @@ -29,7 +29,6 @@ public sealed class StationSystem : EntitySystem { [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ChatSystem _chatSystem = default!; diff --git a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs index c60f3298e7..8ad5c8602e 100644 --- a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs +++ b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs @@ -2,10 +2,8 @@ using Content.Server.GameTicking.Rules.Components; using Content.Server.Ninja.Systems; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; -using Robust.Server.GameObjects; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Random; namespace Content.Server.StationEvents.Events; @@ -14,7 +12,6 @@ namespace Content.Server.StationEvents.Events; ///
public sealed class NinjaSpawnRule : StationEventSystem { - [Dependency] private readonly SpaceNinjaSystem _ninja = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; protected override void Started(EntityUid uid, NinjaSpawnRuleComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSpeakerSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSpeakerSystem.cs index 7544fc376b..0e694a801e 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSpeakerSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSpeakerSystem.cs @@ -1,10 +1,7 @@ using Content.Server.Chat.Systems; using Content.Server.Speech; using Content.Shared.Speech; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; using Robust.Shared.Timing; namespace Content.Server.SurveillanceCamera; @@ -18,8 +15,6 @@ public sealed class SurveillanceCameraSpeakerSystem : EntitySystem [Dependency] private readonly SpeechSoundSystem _speechSound = default!; [Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; /// public override void Initialize() diff --git a/Content.Server/Temperature/Systems/TemperatureSystem.cs b/Content.Server/Temperature/Systems/TemperatureSystem.cs index aef4b89d50..6c9e99e5f3 100644 --- a/Content.Server/Temperature/Systems/TemperatureSystem.cs +++ b/Content.Server/Temperature/Systems/TemperatureSystem.cs @@ -11,7 +11,6 @@ using Content.Shared.Database; using Content.Shared.Inventory; using Content.Shared.Rejuvenate; using Content.Shared.Temperature; -using Robust.Server.GameObjects; using Robust.Shared.Physics.Components; namespace Content.Server.Temperature.Systems; @@ -22,7 +21,6 @@ public sealed class TemperatureSystem : EntitySystem [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly TransformSystem _transform = default!; /// /// All the components that will have their damage updated at the end of the tick. diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index b8f8f12211..e64657743d 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -1,6 +1,5 @@ using System.Linq; using System.Numerics; -using Content.Server.Administration.Logs; using Content.Server.Cargo.Systems; using Content.Server.Interaction; using Content.Server.Power.EntitySystems; @@ -29,7 +28,6 @@ namespace Content.Server.Weapons.Ranged.Systems; public sealed partial class GunSystem : SharedGunSystem { - [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly DamageExamineSystem _damageExamine = default!; diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs index 96f1dc3783..00f409f553 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Xenoarchaeology.XenoArtifacts.Events; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; -using Robust.Server.GameObjects; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -9,7 +8,6 @@ public sealed class ArtifactGasTriggerSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() { diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs index 33d1a43c12..5525cdf359 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs @@ -3,7 +3,6 @@ using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Shared.Interaction; using Content.Shared.Temperature; using Content.Shared.Weapons.Melee.Events; -using Robust.Server.GameObjects; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -11,7 +10,6 @@ public sealed class ArtifactHeatTriggerSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() { diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactPressureTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactPressureTriggerSystem.cs index 4388756cce..8777ab0a8c 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactPressureTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactPressureTriggerSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; -using Robust.Server.GameObjects; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -11,7 +10,6 @@ public sealed class ArtifactPressureTriggerSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Update(float frameTime) { diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index bef57eceb3..080bef44e7 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -2,9 +2,7 @@ using System.Linq; using Content.Server.Body.Systems; using Content.Server.Chat; using Content.Server.Chat.Systems; -using Content.Server.Cloning; using Content.Server.Emoting.Systems; -using Content.Server.Inventory; using Content.Server.Speech.EntitySystems; using Content.Shared.Bed.Sleep; using Content.Shared.Cloning; @@ -31,7 +29,6 @@ namespace Content.Server.Zombies [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly BloodstreamSystem _bloodstream = default!; [Dependency] private readonly DamageableSystem _damageable = default!; - [Dependency] private readonly ServerInventorySystem _inv = default!; [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly AutoEmoteSystem _autoEmote = default!; [Dependency] private readonly EmoteOnDamageSystem _emoteOnDamage = default!; diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index ec4ec17acd..5471f07250 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -34,7 +34,6 @@ public sealed partial class ClimbSystem : VirtualController [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly FixtureSystem _fixtureSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedBodySystem _bodySystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; diff --git a/Content.Shared/Construction/SharedFlatpackSystem.cs b/Content.Shared/Construction/SharedFlatpackSystem.cs index a62488d6f3..8b21bca52a 100644 --- a/Content.Shared/Construction/SharedFlatpackSystem.cs +++ b/Content.Shared/Construction/SharedFlatpackSystem.cs @@ -114,8 +114,7 @@ public abstract class SharedFlatpackSystem : EntitySystem if (!Resolve(ent, ref ent.Comp)) return; - EntProtoId machinePrototypeId; - string? entityPrototype; + var machinePrototypeId = new EntProtoId(); if (TryComp(board, out var machineBoard) && machineBoard.Prototype is not null) machinePrototypeId = machineBoard.Prototype; else if (TryComp(board, out var computerBoard) && computerBoard.Prototype is not null) diff --git a/Content.Shared/Coordinates/EntityCoordinatesExtensions.cs b/Content.Shared/Coordinates/EntityCoordinatesExtensions.cs index b9083eabe1..47d359d387 100644 --- a/Content.Shared/Coordinates/EntityCoordinatesExtensions.cs +++ b/Content.Shared/Coordinates/EntityCoordinatesExtensions.cs @@ -1,6 +1,5 @@ using System.Numerics; using Robust.Shared.Map; -using Robust.Shared.Map.Components; namespace Content.Shared.Coordinates { @@ -20,17 +19,5 @@ namespace Content.Shared.Coordinates { return new EntityCoordinates(id, x, y); } - - [Obsolete] - public static EntityCoordinates ToCoordinates(this MapGridComponent grid, float x, float y) - { - return ToCoordinates(grid.Owner, x, y); - } - - [Obsolete] - public static EntityCoordinates ToCoordinates(this MapGridComponent grid) - { - return ToCoordinates(grid.Owner, Vector2.Zero); - } } } diff --git a/Content.Shared/Maps/TurfSystem.cs b/Content.Shared/Maps/TurfSystem.cs index c0757c5573..ad8b3ddea8 100644 --- a/Content.Shared/Maps/TurfSystem.cs +++ b/Content.Shared/Maps/TurfSystem.cs @@ -13,7 +13,6 @@ public sealed class TurfSystem : EntitySystem { [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly IMapManager _mapMan = default!; /// /// Returns true if a given tile is blocked by physics-enabled entities. diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index 50a7c0fef9..6282a117bb 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -25,7 +25,6 @@ namespace Content.Shared.RCD.Systems; public sealed class RCDSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IMapManager _mapMan = default!; [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; @@ -39,7 +38,7 @@ public sealed class RCDSystem : EntitySystem [Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - private readonly int RcdModeCount = Enum.GetValues(typeof(RcdMode)).Length; + private readonly int _rcdModeCount = Enum.GetValues(typeof(RcdMode)).Length; public override void Initialize() { @@ -310,7 +309,7 @@ public sealed class RCDSystem : EntitySystem _audio.PlayPredicted(comp.SwapModeSound, uid, user); var mode = (int) comp.Mode; - mode = ++mode % RcdModeCount; + mode = ++mode % _rcdModeCount; comp.Mode = (RcdMode) mode; Dirty(uid, comp); diff --git a/Content.Shared/Remotes/EntitySystems/SharedDoorRemoteSystem.cs b/Content.Shared/Remotes/EntitySystems/SharedDoorRemoteSystem.cs index 72d807e6a0..e9bbd27ada 100644 --- a/Content.Shared/Remotes/EntitySystems/SharedDoorRemoteSystem.cs +++ b/Content.Shared/Remotes/EntitySystems/SharedDoorRemoteSystem.cs @@ -1,4 +1,3 @@ -using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Interaction.Events; using Content.Shared.Remotes.Components; @@ -8,8 +7,6 @@ namespace Content.Shared.Remotes.EntitySystems; public abstract class SharedDoorRemoteSystem : EntitySystem { [Dependency] protected readonly SharedPopupSystem Popup = default!; - [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; - // I'm so sorry [Dependency] private readonly SharedAirlockSystem _sharedAirlockSystem = default!; public override void Initialize() { diff --git a/Content.Shared/Shuttles/Components/IFFComponent.cs b/Content.Shared/Shuttles/Components/IFFComponent.cs index a7e6ac1152..6bacbd2b5b 100644 --- a/Content.Shared/Shuttles/Components/IFFComponent.cs +++ b/Content.Shared/Shuttles/Components/IFFComponent.cs @@ -10,11 +10,6 @@ namespace Content.Shared.Shuttles.Components; [Access(typeof(SharedShuttleSystem))] public sealed partial class IFFComponent : Component { - /// - /// Should we show IFF by default? - /// - public const bool ShowIFFDefault = true; - public static readonly Color SelfColor = Color.MediumSpringGreen; /// diff --git a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs index ed687d48f4..8231e48e2d 100644 --- a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs +++ b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs @@ -28,11 +28,6 @@ public abstract partial class SharedShuttleSystem public string? GetIFFLabel(EntityUid gridUid, bool self = false, IFFComponent? component = null) { - if (!IFFComponent.ShowIFFDefault) - { - return null; - } - var entName = MetaData(gridUid).EntityName; if (self) diff --git a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs index 324fd65c86..ca25a49b23 100644 --- a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs +++ b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs @@ -146,7 +146,6 @@ public abstract partial class SharedShuttleSystem : EntitySystem // Just checks if any grids inside of a buffer range at the target position. _grids.Clear(); - var ftlRange = FTLRange; var mapCoordinates = coordinates.ToMap(EntityManager, XformSystem); var ourPos = Maps.GetGridPosition((shuttleUid, shuttlePhysics, shuttleXform)); diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs index d47d024de5..274828a208 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs @@ -102,7 +102,7 @@ public abstract partial class SharedGunSystem // TODO: Actions need doing for guns anyway. private sealed partial class CycleModeEvent : InstantActionEvent { - public SelectiveFire Mode; + public SelectiveFire Mode = default; } private void OnCycleMode(EntityUid uid, GunComponent component, CycleModeEvent args) From 52ca0c2ec280cccae16118a10cc48d9605db7394 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 29 Mar 2024 05:29:22 +0000 Subject: [PATCH 45/56] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7a832f5412..489993d1cc 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: EmoGarbage404 - changes: - - message: You can now sort lathe recipes by category. - type: Add - - message: Recipes in lathes are now sorted alphabetically. - type: Add - id: 5748 - time: '2024-01-20T00:45:04.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24247 - author: Scribbles0 changes: - message: Added a new trait, the Unrevivable trait. @@ -3795,3 +3786,10 @@ id: 6247 time: '2024-03-29T02:30:23.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26498 +- author: Jake Huxell + changes: + - message: Reduced game build warning count. + type: Fix + id: 6248 + time: '2024-03-29T05:28:16.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26518 From a25801b35b6170ce7b371957ec97f9db516f1094 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 29 Mar 2024 16:55:05 +1100 Subject: [PATCH 46/56] Update audioparams (#26387) Drops busname + attenuation. --- Content.Client/Audio/AmbientSoundSystem.cs | 1 - Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs | 4 ++-- .../Light/Components/SharedExpendableLightComponent.cs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Content.Client/Audio/AmbientSoundSystem.cs b/Content.Client/Audio/AmbientSoundSystem.cs index 9d30cabb1e..0206017bae 100644 --- a/Content.Client/Audio/AmbientSoundSystem.cs +++ b/Content.Client/Audio/AmbientSoundSystem.cs @@ -50,7 +50,6 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem private static AudioParams _params = AudioParams.Default .WithVariation(0.01f) .WithLoop(true) - .WithAttenuation(Attenuation.LinearDistance) .WithMaxDistance(7f); /// diff --git a/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs b/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs index 0fdcc7a86d..92c5b7a419 100644 --- a/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs +++ b/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs @@ -23,8 +23,8 @@ public sealed partial class ContentAudioSystem [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IResourceCache _resourceCache = default!; - private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f); - private readonly AudioParams _roundEndSoundEffectParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f); + private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, 0, 0, 0, false, 0f); + private readonly AudioParams _roundEndSoundEffectParams = new(-5f, 1, 0, 0, 0, false, 0f); /// /// EntityUid of lobby restart sound component. diff --git a/Content.Shared/Light/Components/SharedExpendableLightComponent.cs b/Content.Shared/Light/Components/SharedExpendableLightComponent.cs index c802700b62..e40174ab78 100644 --- a/Content.Shared/Light/Components/SharedExpendableLightComponent.cs +++ b/Content.Shared/Light/Components/SharedExpendableLightComponent.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Light.Components; [NetworkedComponent] public abstract partial class SharedExpendableLightComponent : Component { - public static readonly AudioParams LoopedSoundParams = new(0, 1, "Master", 62.5f, 1, 1, true, 0.3f); + public static readonly AudioParams LoopedSoundParams = new(0, 1, 62.5f, 1, 1, true, 0.3f); [ViewVariables(VVAccess.ReadOnly)] public ExpendableLightState CurrentState { get; set; } From f008732a188d9d7a6f5605c79bc07e6fb01fa92d Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 29 Mar 2024 17:06:50 +1100 Subject: [PATCH 47/56] Update submodule to 217.0.0 (#26524) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 8607ba1f16..4002cbddb9 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 8607ba1f16ce676a849b59a41efd389a6e467f5c +Subproject commit 4002cbddb9c9de9030a81480b45b13d978b87526 From b0416372632d963338475c04d743b6916d4a1d61 Mon Sep 17 00:00:00 2001 From: wafehling Date: Fri, 29 Mar 2024 01:13:52 -0500 Subject: [PATCH 48/56] Chemically-created Crystal Shards (#26269) Added chemical reaction for making crystal shards --- .../Objects/Materials/crystal_shard.yml | 15 +++++++++++++++ Resources/Prototypes/Recipes/Reactions/fun.yml | 17 ++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml b/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml index 884a5531e7..8f522abce4 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml @@ -134,3 +134,18 @@ tags: - Trash - CrystalRed + +- type: entity + parent: ShardCrystalBase + id: ShardCrystalRandom + name: random crystal shard + components: + - type: RandomSpawner + prototypes: + - ShardCrystalGreen + - ShardCrystalPink + - ShardCrystalOrange + - ShardCrystalBlue + - ShardCrystalCyan + - ShardCrystalRed + chance: 1 diff --git a/Resources/Prototypes/Recipes/Reactions/fun.yml b/Resources/Prototypes/Recipes/Reactions/fun.yml index fd1f42f101..5ae173c0ee 100644 --- a/Resources/Prototypes/Recipes/Reactions/fun.yml +++ b/Resources/Prototypes/Recipes/Reactions/fun.yml @@ -171,6 +171,21 @@ products: Laughter: 2 +- type: reaction + id: CreateCrystals + quantized: true + minTemp: 374 + reactants: + Sugar: + amount: 15 + Water: + amount: 15 + Ethanol: + amount: 5 + effects: + - !type:CreateEntityReactionEffect + entity: ShardCrystalRandom + - type: reaction id: Gunpowder impact: Low @@ -185,4 +200,4 @@ amount: 2 effects: - !type:CreateEntityReactionEffect - entity: MaterialGunpowder \ No newline at end of file + entity: MaterialGunpowder From d830922603646574dcee23f2ffe79f953f437276 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 29 Mar 2024 06:14:58 +0000 Subject: [PATCH 49/56] 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 489993d1cc..9a0b1e9f81 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Scribbles0 - changes: - - message: Added a new trait, the Unrevivable trait. - type: Add - id: 5749 - time: '2024-01-20T02:22:15.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24226 - author: Drayff changes: - message: Animations for ToolBoxes! @@ -3793,3 +3786,10 @@ id: 6248 time: '2024-03-29T05:28:16.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26518 +- author: wafehling + changes: + - message: Added a chemistry recipe to make crystal shards. + type: Add + id: 6249 + time: '2024-03-29T06:13:52.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26269 From cdfee5aa0c95ea626271136b9e6ad78ad2000f49 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 29 Mar 2024 17:26:30 +1100 Subject: [PATCH 50/56] Transform cleanups (#25963) * Transform cleanups * Fix build * ascension --- .../Tests/Buckle/BuckleTest.cs | 23 ++++---- .../Components/ActionBlocking/HandCuffTest.cs | 5 +- .../Anomaly/Effects/BluespaceAnomalySystem.cs | 12 ++--- .../Disposal/SharedDisposalUnitSystem.cs | 3 -- .../Storage/EntitySystems/DumpableSystem.cs | 53 ++++++++----------- 5 files changed, 40 insertions(+), 56 deletions(-) diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs index 6e2a080370..7c700d9fb8 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs @@ -181,9 +181,8 @@ namespace Content.IntegrationTests.Tests.Buckle #pragma warning restore NUnit2045 // Move away from the chair - var xformQuery = entityManager.GetEntityQuery(); - var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery); - xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1000, 1000), xformQuery); + var oldWorldPosition = xformSystem.GetWorldPosition(chair); + xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1000, 1000)); // Out of range #pragma warning disable NUnit2045 // Interdependent asserts. @@ -193,8 +192,8 @@ namespace Content.IntegrationTests.Tests.Buckle #pragma warning restore NUnit2045 // Move near the chair - oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery); - xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(0.5f, 0), xformQuery); + oldWorldPosition = xformSystem.GetWorldPosition(chair); + xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(0.5f, 0)); // In range #pragma warning disable NUnit2045 // Interdependent asserts. @@ -220,8 +219,8 @@ namespace Content.IntegrationTests.Tests.Buckle Assert.That(buckleSystem.TryBuckle(human, human, chair, buckleComp: buckle)); // Move away from the chair - oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery); - xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1, 0), xformQuery); + oldWorldPosition = xformSystem.GetWorldPosition(chair); + xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1, 0)); }); await server.WaitRunTicks(1); @@ -371,9 +370,8 @@ namespace Content.IntegrationTests.Tests.Buckle }); // Move the buckled entity away - var xformQuery = entityManager.GetEntityQuery(); - var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery); - xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(100, 0), xformQuery); + var oldWorldPosition = xformSystem.GetWorldPosition(chair); + xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(100, 0)); }); await PoolManager.WaitUntil(server, () => !buckle.Buckled, 10); @@ -383,9 +381,8 @@ namespace Content.IntegrationTests.Tests.Buckle await server.WaitAssertion(() => { // Move the now unbuckled entity back onto the chair - var xformQuery = entityManager.GetEntityQuery(); - var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery); - xformSystem.SetWorldPosition(human, oldWorldPosition, xformQuery); + var oldWorldPosition = xformSystem.GetWorldPosition(chair); + xformSystem.SetWorldPosition(human, oldWorldPosition); // Buckle Assert.That(buckleSystem.TryBuckle(human, human, chair, buckleComp: buckle)); diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs index 1d5dd6d34e..c6a8e618cc 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs @@ -58,7 +58,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking var cuffableSys = entityManager.System(); var xformSys = entityManager.System(); - var xformQuery = entityManager.GetEntityQuery(); // Spawn the entities human = entityManager.SpawnEntity("HumanHandcuffDummy", coordinates); @@ -66,8 +65,8 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking cuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates); secondCuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates); - var coords = xformSys.GetWorldPosition(otherHuman, xformQuery); - xformSys.SetWorldPosition(human, coords, xformQuery); + var coords = xformSys.GetWorldPosition(otherHuman); + xformSys.SetWorldPosition(human, coords); // Test for components existing Assert.Multiple(() => diff --git a/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs b/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs index 87c0ba4a4e..dd2da82c9d 100644 --- a/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Mobs.Components; using Content.Shared.Teleportation.Components; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.Collections; using Robust.Shared.Random; namespace Content.Server.Anomaly.Effects; @@ -35,20 +36,19 @@ public sealed class BluespaceAnomalySystem : EntitySystem var range = component.MaxShuffleRadius * args.Severity; var mobs = new HashSet>(); _lookup.GetEntitiesInRange(xform.Coordinates, range, mobs); - var allEnts = new List(mobs.Select(m => m.Owner)) { uid }; - var coords = new List(); + var allEnts = new ValueList(mobs.Select(m => m.Owner)) { uid }; + var coords = new ValueList(); foreach (var ent in allEnts) { - if (xformQuery.TryGetComponent(ent, out var xf)) - coords.Add(xf.MapPosition.Position); + if (xformQuery.TryGetComponent(ent, out var allXform)) + coords.Add(_xform.GetWorldPosition(allXform)); } _random.Shuffle(coords); for (var i = 0; i < allEnts.Count; i++) { - _adminLogger.Add(LogType.Teleport, $"{ToPrettyString(allEnts[i])} has been shuffled to {coords[i]} by the {ToPrettyString(uid)} at {xform.Coordinates}"); - _xform.SetWorldPosition(allEnts[i], coords[i], xformQuery); + _xform.SetWorldPosition(allEnts[i], coords[i]); } } diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs index 600036a891..9afd683cbd 100644 --- a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs +++ b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs @@ -127,9 +127,6 @@ public abstract class SharedDisposalUnitSystem : EntitySystem return damageState != null && (!component.MobsCanEnter || _mobState.IsDead(entity, damageState)); } - /// - /// TODO: Proper prediction - /// public abstract void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null); [Serializable, NetSerializable] diff --git a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs index 2b804cf732..8a8b636a67 100644 --- a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs +++ b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs @@ -19,17 +19,16 @@ public sealed class DumpableSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedDisposalUnitSystem _disposalUnitSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - private EntityQuery _xformQuery; + private EntityQuery _itemQuery; public override void Initialize() { base.Initialize(); - _xformQuery = GetEntityQuery(); + _itemQuery = GetEntityQuery(); SubscribeLocalEvent(OnAfterInteract, after: new[]{ typeof(SharedEntityStorageSystem) }); SubscribeLocalEvent>(AddDumpVerb); SubscribeLocalEvent>(AddUtilityVerbs); @@ -111,7 +110,7 @@ public sealed class DumpableSystem : EntitySystem } } - private void StartDoAfter(EntityUid storageUid, EntityUid? targetUid, EntityUid userUid, DumpableComponent dumpable) + private void StartDoAfter(EntityUid storageUid, EntityUid targetUid, EntityUid userUid, DumpableComponent dumpable) { if (!TryComp(storageUid, out var storage)) return; @@ -120,7 +119,7 @@ public sealed class DumpableSystem : EntitySystem foreach (var entity in storage.Container.ContainedEntities) { - if (!TryComp(entity, out var itemComp) || + if (!_itemQuery.TryGetComponent(entity, out var itemComp) || !_prototypeManager.TryIndex(itemComp.Size, out var itemSize)) { continue; @@ -138,33 +137,16 @@ public sealed class DumpableSystem : EntitySystem }); } - private void OnDoAfter(EntityUid uid, DumpableComponent component, DoAfterEvent args) + private void OnDoAfter(EntityUid uid, DumpableComponent component, DumpableDoAfterEvent args) { - if (args.Handled || args.Cancelled || !TryComp(uid, out var storage)) + if (args.Handled || args.Cancelled || !TryComp(uid, out var storage) || storage.Container.ContainedEntities.Count == 0) return; - Queue dumpQueue = new(); - foreach (var entity in storage.Container.ContainedEntities) - { - dumpQueue.Enqueue(entity); - } - - if (dumpQueue.Count == 0) - return; - - foreach (var entity in dumpQueue) - { - var transform = Transform(entity); - _container.AttachParentToContainerOrGrid((entity, transform)); - _transformSystem.SetLocalPositionRotation(entity, transform.LocalPosition + _random.NextVector2Box() / 2, _random.NextAngle(), transform); - } - - if (args.Args.Target == null) - return; + var dumpQueue = new Queue(storage.Container.ContainedEntities); var dumped = false; - if (_disposalUnitSystem.HasDisposals(args.Args.Target.Value)) + if (_disposalUnitSystem.HasDisposals(args.Args.Target)) { dumped = true; @@ -173,22 +155,31 @@ public sealed class DumpableSystem : EntitySystem _disposalUnitSystem.DoInsertDisposalUnit(args.Args.Target.Value, entity, args.Args.User); } } - else if (HasComp(args.Args.Target.Value)) + else if (HasComp(args.Args.Target)) { dumped = true; - var targetPos = _xformQuery.GetComponent(args.Args.Target.Value).LocalPosition; + var targetPos = _transformSystem.GetWorldPosition(args.Args.Target.Value); foreach (var entity in dumpQueue) { - _transformSystem.SetLocalPosition(entity, targetPos + _random.NextVector2Box() / 4); + _transformSystem.SetWorldPosition(entity, targetPos + _random.NextVector2Box() / 4); + } + } + else + { + var targetPos = _transformSystem.GetWorldPosition(uid); + + foreach (var entity in dumpQueue) + { + var transform = Transform(entity); + _transformSystem.SetWorldPositionRotation(entity, targetPos + _random.NextVector2Box() / 4, _random.NextAngle(), transform); } } if (dumped) { - // TODO: Predicted when above predicted - _audio.PlayPvs(component.DumpSound, uid); + _audio.PlayPredicted(component.DumpSound, uid, args.User); } } } From a606909d30477453bc23765df4c78f04b2ead522 Mon Sep 17 00:00:00 2001 From: Crotalus Date: Fri, 29 Mar 2024 07:30:50 +0100 Subject: [PATCH 51/56] Add auto modes to reagent grinder (#26290) * Add auto-mode to reagent grinder * Remove redundant stuff with DataField * Use margin instead of dummy control * Resolve grinder component --- Content.Client/Kitchen/UI/GrinderMenu.xaml | 10 ++++++---- Content.Client/Kitchen/UI/GrinderMenu.xaml.cs | 14 ++++++++++++++ .../UI/ReagentGrinderBoundUserInterface.cs | 5 +++++ .../Components/ReagentGrinderComponent.cs | 15 +++++++++------ .../EntitySystems/ReagentGrinderSystem.cs | 19 +++++++++++++++++++ .../Kitchen/SharedReagentGrinder.cs | 18 +++++++++++++++++- .../components/reagent-grinder-component.ftl | 3 +++ 7 files changed, 73 insertions(+), 11 deletions(-) diff --git a/Content.Client/Kitchen/UI/GrinderMenu.xaml b/Content.Client/Kitchen/UI/GrinderMenu.xaml index b83128d004..dacddd0df6 100644 --- a/Content.Client/Kitchen/UI/GrinderMenu.xaml +++ b/Content.Client/Kitchen/UI/GrinderMenu.xaml @@ -3,10 +3,12 @@ xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" Title="{Loc grinder-menu-title}" MinSize="768 256"> - -