From d215419f9ac6108185b724e40185d2cf27f7a749 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 30 Mar 2024 13:28:42 +1100 Subject: [PATCH 01/14] Revert "Fix scram implant's teleportation out of containers" (#25030) * Revert "Fix scram implant's teleportation out of containers (#24827)" This reverts commit d4434dbb5e586544a5dbd067a3fee4a6bd4e9e89. * Remove EntityQuery pass-ins --- Content.Server/Implants/SubdermalImplantSystem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 6b58f6eb09..fd95720732 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -139,7 +139,6 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem break; } _xform.SetWorldPosition(ent, targetCoords.Position); - _xform.AttachToGridOrMap(ent, xform); _audio.PlayPvs(implant.TeleportSound, ent); args.Handled = true; From bb5ca720fccd035e310c12b266f71081f85edf47 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sat, 30 Mar 2024 15:34:55 +1300 Subject: [PATCH 02/14] Split GasTileOverlaySystem update over two ticks (#26542) Split GasTileOverlaySystem update over ticks --- .../EntitySystems/GasTileOverlaySystem.cs | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs index 6d49feb018..003eed59e0 100644 --- a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -52,6 +52,8 @@ namespace Content.Server.Atmos.EntitySystems new DefaultObjectPool>>( new DefaultPooledObjectPolicy>>(), 64); + private bool _doSessionUpdate; + /// /// Overlay update interval, in seconds. /// @@ -192,7 +194,7 @@ namespace Content.Server.Atmos.EntitySystems /// /// Updates the visuals for a tile on some grid chunk. Returns true if the visuals have changed. /// - private bool UpdateChunkTile(GridAtmosphereComponent gridAtmosphere, GasOverlayChunk chunk, Vector2i index, GameTick curTick) + private bool UpdateChunkTile(GridAtmosphereComponent gridAtmosphere, GasOverlayChunk chunk, Vector2i index) { ref var oldData = ref chunk.TileData[chunk.GetDataIndex(index)]; if (!gridAtmosphere.Tiles.TryGetValue(index, out var tile)) @@ -200,7 +202,7 @@ namespace Content.Server.Atmos.EntitySystems if (oldData.Equals(default)) return false; - chunk.LastUpdate = curTick; + chunk.LastUpdate = _gameTiming.CurTick; oldData = default; return true; } @@ -258,11 +260,11 @@ namespace Content.Server.Atmos.EntitySystems if (!changed) return false; - chunk.LastUpdate = curTick; + chunk.LastUpdate = _gameTiming.CurTick; return true; } - private void UpdateOverlayData(GameTick curTick) + private void UpdateOverlayData() { // TODO parallelize? var query = EntityQueryEnumerator(); @@ -276,7 +278,7 @@ namespace Content.Server.Atmos.EntitySystems if (!overlay.Chunks.TryGetValue(chunkIndex, out var chunk)) overlay.Chunks[chunkIndex] = chunk = new GasOverlayChunk(chunkIndex); - changed |= UpdateChunkTile(gam, chunk, index, curTick); + changed |= UpdateChunkTile(gam, chunk, index); } if (changed) @@ -291,13 +293,28 @@ namespace Content.Server.Atmos.EntitySystems base.Update(frameTime); AccumulatedFrameTime += frameTime; - if (AccumulatedFrameTime < _updateInterval) return; + if (_doSessionUpdate) + { + UpdateSessions(); + return; + } + + if (AccumulatedFrameTime < _updateInterval) + return; + AccumulatedFrameTime -= _updateInterval; - var curTick = _gameTiming.CurTick; - // First, update per-chunk visual data for any invalidated tiles. - UpdateOverlayData(curTick); + UpdateOverlayData(); + + // Then, next tick we send the data to players. + // This is to avoid doing all the work in the same tick. + _doSessionUpdate = true; + } + + public void UpdateSessions() + { + _doSessionUpdate = false; if (!PvsEnabled) return; @@ -315,11 +332,11 @@ namespace Content.Server.Atmos.EntitySystems _sessions.Add(player); } - if (_sessions.Count > 0) - { - _updateJob.CurrentTick = curTick; - _parMan.ProcessNow(_updateJob, _sessions.Count); - } + if (_sessions.Count == 0) + return; + + _parMan.ProcessNow(_updateJob, _sessions.Count); + _updateJob.LastSessionUpdate = _gameTiming.CurTick; } public void Reset(RoundRestartCleanupEvent ev) @@ -352,7 +369,7 @@ namespace Content.Server.Atmos.EntitySystems public ObjectPool> ChunkIndexPool; public ObjectPool>> ChunkViewerPool; - public GameTick CurrentTick; + public GameTick LastSessionUpdate; public Dictionary>> LastSentChunks; public List Sessions; @@ -415,7 +432,7 @@ namespace Content.Server.Atmos.EntitySystems if (previousChunks != null && previousChunks.Contains(gIndex) && - value.LastUpdate != CurrentTick) + value.LastUpdate > LastSessionUpdate) { continue; } From 6ef592ddba336a41330eaf3760a600c805907e1d Mon Sep 17 00:00:00 2001 From: Boaz1111 <149967078+Boaz1111@users.noreply.github.com> Date: Sat, 30 Mar 2024 03:46:20 +0100 Subject: [PATCH 03/14] Industrial Reagent Grinder (#25020) * all work done * adress whitespace * millions must factorio * conflict fix * i forgot --- .../Circuitboards/Machine/production.yml | 13 ++++++ .../Entities/Structures/Machines/lathe.yml | 1 + .../Structures/Machines/reagent_grinder.yml | 42 +++++++++++++++++++ .../Entities/Structures/Machines/recycler.yml | 2 +- .../Prototypes/Recipes/Lathes/electronics.yml | 9 ++++ .../Prototypes/Research/civilianservices.yml | 1 + 6 files changed, 67 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 093c0524b4..7a9e60ac56 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -1357,3 +1357,16 @@ materialRequirements: Steel: 5 CableHV: 2 + +- type: entity + parent: BaseMachineCircuitboard + id: ReagentGrinderIndustrialMachineCircuitboard + name: industrial reagent grinder machine board + components: + - type: MachineBoard + prototype: ReagentGrinderIndustrial + requirements: + MatterBin: 1 + Manipulator: 3 + materialRequirements: + Glass: 1 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 28bc62894e..5fbe84a3fe 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -451,6 +451,7 @@ - ArtifactCrusherMachineCircuitboard - TelecomServerCircuitboard - MassMediaCircuitboard + - ReagentGrinderIndustrialMachineCircuitboard - type: MaterialStorage whitelist: tags: diff --git a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml index d19e237997..3bb1458b8c 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml @@ -56,3 +56,45 @@ inputContainer: !type:Container machine_board: !type:Container machine_parts: !type:Container + +- type: entity + parent: Recycler #too different so different parent + id: ReagentGrinderIndustrial + name: industrial reagent grinder + description: An industrial reagent grinder. + components: + - type: SolutionContainerManager + solutions: + output: + maxVol: 400 #*slaps roof of machine* This baby can fit so much omnizine in it + - type: MaterialReclaimer + whitelist: + components: + - Extractable #same as reagent grinder + blacklist: + tags: + - HighRiskItem #ian meat + efficiency: 0.9 + - type: Sprite + sprite: Structures/Machines/recycling.rsi + layers: + - state: grinder-b0 + - type: Machine + board: ReagentGrinderIndustrialMachineCircuitboard + - type: GenericVisualizer + visuals: + enum.ConveyorVisuals.State: + enum.RecyclerVisualLayers.Main: + Forward: { state: grinder-b1 } + Reverse: { state: grinder-b1 } + Off: { state: grinder-b0 } + - type: ContainerContainer + containers: + machine_board: !type:Container + machine_parts: !type:Container + - type: Construction + graph: Machine + node: machine + containers: + - machine_parts + - machine_board \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Machines/recycler.yml b/Resources/Prototypes/Entities/Structures/Machines/recycler.yml index 814852125a..138795cbf1 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/recycler.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/recycler.yml @@ -116,4 +116,4 @@ interactSuccessString: petting-success-recycler interactFailureString: petting-failure-generic interactSuccessSound: - path: /Audio/Items/drill_hit.ogg + path: /Audio/Items/drill_hit.ogg \ No newline at end of file diff --git a/Resources/Prototypes/Recipes/Lathes/electronics.yml b/Resources/Prototypes/Recipes/Lathes/electronics.yml index f0c59a0bdf..c4417f868b 100644 --- a/Resources/Prototypes/Recipes/Lathes/electronics.yml +++ b/Resources/Prototypes/Recipes/Lathes/electronics.yml @@ -956,3 +956,12 @@ Steel: 100 Glass: 900 Gold: 100 + +- type: latheRecipe + id: ReagentGrinderIndustrialMachineCircuitboard + result: ReagentGrinderIndustrialMachineCircuitboard + completetime: 5 + materials: + Steel: 100 + Glass: 900 + Gold: 100 diff --git a/Resources/Prototypes/Research/civilianservices.yml b/Resources/Prototypes/Research/civilianservices.yml index 61f95894ee..79dac27510 100644 --- a/Resources/Prototypes/Research/civilianservices.yml +++ b/Resources/Prototypes/Research/civilianservices.yml @@ -14,6 +14,7 @@ - BorgModuleHarvesting - SeedExtractorMachineCircuitboard - HydroponicsTrayMachineCircuitboard + - ReagentGrinderIndustrialMachineCircuitboard - type: technology id: CritterMechs From ac129820492ff0189dd581033848dc9aa3a15b5c Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 02:47:27 +0000 Subject: [PATCH 04/14] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c52c19a50c..420db73301 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: casperr04 - changes: - - message: Fixed players being able to re-anchor items after fultoning them. - type: Fix - - message: Changed which objects can be fultoned. - type: Tweak - id: 5754 - time: '2024-01-20T04:57:05.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/20628 - author: Blackern5000 changes: - message: Crushers can no longer be researched. @@ -3795,3 +3786,11 @@ id: 6253 time: '2024-03-30T01:25:43.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26541 +- author: Boaz1111 + changes: + - message: Added an industrial reagent grinder to the basic hydroponics research. + It grinds things into reagents like a recycler. + type: Add + id: 6254 + time: '2024-03-30T02:46:20.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25020 From 52db14a19dcafa5592be0e2d23a4a628c9ab981e Mon Sep 17 00:00:00 2001 From: SonicHDC <100022571+SonicHDC@users.noreply.github.com> Date: Sat, 30 Mar 2024 15:31:32 +1200 Subject: [PATCH 05/14] Zippable coats (#26494) * Update base_clothingouter.yml * Update coats.yml * Change Flipped to Opened * labcoat * coat * Update meta.json * Update meta.json * Update meta.json * Update meta.json * Update meta.json * Update meta.json * Update meta.json * cmo * gene * rd * robo * sci * viro * Locale zip-unzip * Missing meta * Fix wrong sprites --- .../components/foldable-component.ftl | 5 +- .../OuterClothing/base_clothingouter.yml | 42 ++++++++++++++ .../Entities/Clothing/OuterClothing/coats.yml | 54 +++++++++++++++--- .../OuterClothing/Coats/labcoat.rsi/meta.json | 8 +++ .../Coats/labcoat.rsi/open-inhand-left.png | Bin 0 -> 390 bytes .../Coats/labcoat.rsi/open-inhand-right.png | Bin 0 -> 394 bytes .../Coats/labcoat_chem.rsi/meta.json | 8 +++ .../labcoat_chem.rsi/open-inhand-left.png | Bin 0 -> 408 bytes .../labcoat_chem.rsi/open-inhand-right.png | Bin 0 -> 410 bytes .../Coats/labcoat_cmo.rsi/meta.json | 8 +++ .../labcoat_cmo.rsi/open-inhand-left.png | Bin 0 -> 382 bytes .../labcoat_cmo.rsi/open-inhand-right.png | Bin 0 -> 383 bytes .../Coats/labcoat_gene.rsi/meta.json | 8 +++ .../labcoat_gene.rsi/open-inhand-left.png | Bin 0 -> 408 bytes .../labcoat_gene.rsi/open-inhand-right.png | Bin 0 -> 410 bytes .../Coats/labcoat_rd.rsi/meta.json | 8 +++ .../Coats/labcoat_rd.rsi/open-inhand-left.png | Bin 0 -> 1352 bytes .../labcoat_rd.rsi/open-inhand-right.png | Bin 0 -> 1349 bytes .../Coats/labcoat_robo.rsi/meta.json | 8 +++ .../labcoat_robo.rsi/open-inhand-left.png | Bin 0 -> 395 bytes .../labcoat_robo.rsi/open-inhand-right.png | Bin 0 -> 399 bytes .../Coats/labcoat_sci.rsi/meta.json | 8 +++ .../labcoat_sci.rsi/open-inhand-left.png | Bin 0 -> 408 bytes .../labcoat_sci.rsi/open-inhand-right.png | Bin 0 -> 410 bytes .../Coats/labcoat_viro.rsi/meta.json | 8 +++ .../labcoat_viro.rsi/open-inhand-left.png | Bin 0 -> 408 bytes .../labcoat_viro.rsi/open-inhand-right.png | Bin 0 -> 410 bytes 27 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-right.png diff --git a/Resources/Locale/en-US/foldable/components/foldable-component.ftl b/Resources/Locale/en-US/foldable/components/foldable-component.ftl index 539b4fd9e7..525820920b 100644 --- a/Resources/Locale/en-US/foldable/components/foldable-component.ftl +++ b/Resources/Locale/en-US/foldable/components/foldable-component.ftl @@ -4,4 +4,7 @@ foldable-deploy-fail = You can't deploy the {$object} here. fold-verb = Fold unfold-verb = Unfold -fold-flip-verb = Flip \ No newline at end of file +fold-flip-verb = Flip + +fold-zip-verb = Zip up +fold-unzip-verb = Unzip diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml index 82df2c21e8..310661f6ca 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml @@ -43,6 +43,48 @@ - type: StaticPrice price: 80 +- type: entity + abstract: true + parent: [ClothingOuterStorageBase, BaseFoldable] + id: ClothingOuterStorageFoldableBase + components: + - type: Appearance + - type: Foldable + canFoldInsideContainer: true + unfoldVerbText: fold-zip-verb + foldVerbText: fold-unzip-verb + - type: FoldableClothing + foldedEquippedPrefix: open + foldedHeldPrefix: open + - type: Sprite + layers: + - state: icon + map: [ "unfoldedLayer" ] + - state: icon-open + map: ["foldedLayer"] + visible: false + +- type: entity + abstract: true + parent: ClothingOuterStorageFoldableBase + id: ClothingOuterStorageFoldableBaseOpened + suffix: opened + components: + - type: Foldable + folded: true + - type: Clothing + equippedPrefix: open + - type: Item + heldPrefix: open + - type: Sprite + layers: + - state: icon + map: [ "unfoldedLayer" ] + visible: false + - state: icon-open + map: ["foldedLayer"] + visible: true + - type: entity abstract: true parent: ClothingOuterStorageBase diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml index bb2f598cd0..ca0e2d4b4d 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml @@ -114,7 +114,7 @@ Quantity: 20 - type: entity - parent: ClothingOuterStorageBase + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatLab name: lab coat description: A suit that protects against minor chemical spills. @@ -129,7 +129,12 @@ Caustic: 0.75 - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatLab] + id: ClothingOuterCoatLabOpened + name: lab coat + +- type: entity + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatLabChem name: chemist lab coat description: A suit that protects against minor chemical spills. Has an orange stripe on the shoulder. @@ -144,7 +149,12 @@ Caustic: 0.75 - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatLabChem] + id: ClothingOuterCoatLabChemOpened + name: chemist lab coat + +- type: entity + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatLabViro name: virologist lab coat description: A suit that protects against bacteria and viruses. Has an green stripe on the shoulder. @@ -158,9 +168,13 @@ coefficients: Caustic: 0.75 +- type: entity + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatLabViro] + id: ClothingOuterCoatLabViroOpened + name: virologist lab coat - type: entity - parent: ClothingOuterStorageBase + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatLabGene name: geneticist lab coat description: A suit that protects against minor chemical spills. Has an blue stripe on the shoulder. @@ -174,9 +188,13 @@ coefficients: Caustic: 0.75 +- type: entity + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatLabGene] + id: ClothingOuterCoatLabGeneOpened + name: geneticist lab coat - type: entity - parent: ClothingOuterStorageBase + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatLabCmo name: chief medical officer's lab coat description: Bluer than the standard model. @@ -191,7 +209,12 @@ Caustic: 0.75 - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatLabCmo] + id: ClothingOuterCoatLabCmoOpened + name: chief medical officer's lab coat + +- type: entity + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatRnd name: scientist lab coat description: A suit that protects against minor chemical spills. Has a purple stripe on the shoulder. @@ -206,7 +229,12 @@ Caustic: 0.75 - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatRnd] + id: ClothingOuterCoatRndOpened + name: scientist lab coat + +- type: entity + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatRobo name: roboticist lab coat description: More like an eccentric coat than a labcoat. Helps pass off bloodstains as part of the aesthetic. Comes with red shoulder pads. @@ -221,7 +249,12 @@ Caustic: 0.75 - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatRobo] + id: ClothingOuterCoatRoboOpened + name: roboticist lab coat + +- type: entity + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatRD name: research director lab coat description: Woven with top of the line technology, this labcoat helps protect against radiation in similar way to the experimental hardsuit. @@ -236,6 +269,11 @@ Caustic: 0.75 Radiation: 0.9 +- type: entity + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatRD] + id: ClothingOuterCoatRDOpened + name: research director lab coat + - type: entity parent: ClothingOuterStorageBase id: ClothingOuterCoatPirate diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/meta.json index 775a3c2151..02abb07806 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..489f958b9e9e6ae91a0428ecde2b4fc97585c26d GIT binary patch literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1k1^9%xPMkRL|Ns9_pFX{K@nTn3myeH+lao_+cJ`4YM}SIp%?x@4q_|3g z{DOhXAb?@h8J+V$QO*L7$YKTtzC$3)D5~Mr02I9K>Eaj?;r@2oTdrmU9@oqKrM3UX z<1Sx~nk;=&BALUoXVvzbnm>+vKlP4zz;ujNFQHD|BLBwo1#_phR<=7u-`>m6B2r=F z!!}n&!EsGuPWJM9$LDEJXwY~Xa{XV=B<3ksH;Wa{@+(_lazy5+_mqfUDsxXmk3lFrI%5O6Bgvg6U4Xnq~ zc|=(acG~ppH(sUDaC1(NUf@Mn#-4T8nIdI>l_u>LJMrqmdgE)_DItM7X4LlI=Ju*& Zy2#R{xb*nZhd^&Kc)I$ztaD0e0stYfo5}zH literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..54bd14c8aac98862f49d070485b2adb226a9a434 GIT binary patch literal 394 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1k1^9%xPMkRL|Ns9_pFX{K@nTn3myeH+lao_+cJ`4YM}SIp%?x@4q_|3g z{DOhXAb?@h8J+V$QO*L7$YKTtzC$3)D5~Mr02I9M>Eaj?;r@2oO}=IW9+tT+i~s!R zH#cLuCS!T!+rmli9T6w1CRB80{uB`3(NNgPJtLpX%T|pMFtR`{Kn%2t1eP~LtWHU^>RSU*L)2K zR(d^1))kz*&WH1h)K(^g{C9t~MJ_K;EfAWrtVL(W78bvzOH(GWZl2cA8NRIL_GYJj fz5B&=PD?M}(WQ7~>H2>_uQGVL`njxgN@xNA7*wRN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/meta.json index 775a3c2151..02abb07806 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..57d36185047e17e51ac20411891363e034207aa1 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkRLOd8*RAebQe^y$-!7cX{ob@}-CI5|0GXJ@bQVLNi<2uN)V zmkyBPEeY}q1}TOC2HO`(Z9oam0*}aI1_r)EAj~ML;ne^XeCX-o7!u+BcG_FMLkc{u zjR)k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkRLOd8*RAebQe^y$-!7cX{ob@}-CI5|0GXJ@bQVLNi<2uN)V zmkyBPEeY}q1}TOC2HO`(Z9oam0*}aI1_r)EAj~ML;ne^XeC+As7!u+BcA6(&vjLB* zFWa)B|DVmw*mBxIXb@5-KQZ&#jtlJJ*#R$O4a_+E zzUa-E#o}Hyn_+%b%C3J`nAHpzlbD1R)PA%sxT^8_)xG^jpVtU7eXy`Az1cWLJyIj2$8? oDi(V0xO@7^>mStzH|=43vvS9*=ieN!1HH@O>FVdQ&MBb@0AV+%Pyhe` literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/meta.json index 775a3c2151..02abb07806 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..31025b7ddad3b10f1804e7ded6722bacd63ff539 GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`031o(uwsw7nv?6@4V`m|xo0!fz;F>6nUg-3ysZ(hlN0a6?#L4LviA%Njt z^WI3H7-xY;WHAE+-$4*&+%YlxEl}{br;B4qg!|iR7x|hMcwD&`{rNAyUUfxmwf8oi z)0>vKD?bb3Zxi?W>l`$JNs(1kV4r%$ypHRR#>(1NZKo>w%9%MZ29Qw^Hdf*=w({5 zRQVRuyw7V7%ZUDtN{DW^)Vc+xvKHxz#4O&hqPBOx_?LeS?DMmmyJ}t(0KLiJ>FVdQ I&MBb@09{g&H~;_u literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..01e8e0fbc1123246578f528efe824e1890af8bff GIT binary patch literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`031o(uwsw7nv?6@4V`m|xo0!fz;F>6nUg-3ysZ(hlN0a6?#L4LviA%Njt z^WI3H7-xY;WHAE+-$4*&+%YlxEl}`|r;B4qg!|iRH-(xNcwF^&eE46VBjnJmbb3qe z3!gr}&H~GAwTc|_Nld~9Y6gs-br0@Qf8U{eC-nQoIo_F)4bir2RL$f75`@}d$aONhU0GU1+9Gza<^ic zrkRFv&DB5mSN%hE>RYvpQcjf{K`VD~M#VC5TG=nO_^ustUFxUdzc_(IjL)>Pn_2!b Ry8->l;OXk;vd$@?2>_OdlcxXx literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/meta.json index 775a3c2151..02abb07806 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..d95cfab8a7f44f1632971643716f6ae8f98143a0 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkPVcgCLo|NkrRdi(V0(~B1`c6D|6`1m+EIb~;Ot7kVIIdTN3 zwyTu63`p^o1o;I66~h68?F*$gpaf@uM`SSr1K&XqX529``z=uLp{I*uNQC>_X>a)s zDe$;99<-bCy*^H}jOXRsMUg#*FFPA6U-EU<@6n4t!lI_&lfd+-y6HOeU%8S;nVhAm zH7l}fyQ)1$t*!uK6RY%C4bZu6sDw=w{<3f#(vx zglC$F9MocQz7)VRTY$%txxLTM*68NJm+UhRu(WXcILvs!&SconYJQJ(lSV>gIM4Y- z6MiphU^%J!#?_UPd&+*p5Rn5=?47YFTfaTH@U*fo#)*sBB8$IJ92Rx`i1 nz2)!Gv8cf4w_LsciJy#0u{&l}`;`;`JgTe~DWM4fx-*~S literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbbaa54df7255e742a138c820a5e7957ddcd584 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkPVcgCLo|NkrRdi(V0(~B1`c6D|6`1m+EIb~;Ot7kVIIdTN3 zwyTu63`p^o1o;I66~h68?F*$gpaf@uM`SSr1K&XqX529``z=uLv8Rh;NQC>_X`Xz| z20X65Y|D!Ne>O8?%W31u+P&h5`ji<@o*&d}d+O|yz;uK~&0(Kv#`hfcYdecgf@0f> z`Yx+5^yklA|C@hq94jMJ@0O6JqL(cq3`gviO`PWS!eoAbJ6_x2loUL(l#!NRVL^Yh+F zt}A=%8I@f0RD&kG7b=+T-RUL1i=~1$hBGDZ?(cAgBlEludQNyPG%4XpP=U~sT`ejz rc8IK~Sm?du?&&A5e^ejbw1@G{${n+we{;MJ^e%&^tDnm{r-UW|atNy= literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/meta.json index f10d1d8014..5a13fa99f3 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..3a3f3f85b0d6115bb8e3125ef30ff36c984f41b2 GIT binary patch literal 1352 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkO~f+6Go|Nk=?W)?EEJbn7~;>C+yU0pstK2AtCq9nrC$0|8L zS1&OoKPgqOBDVmjnt{Q_zM>#8IXksPAt^OIGtXA({qFrr3YjUkO5vuy2EGN(sTr9b zRYj@6RemAKRoTgwDN6Qsyj(UFRzMSSQ%e#RDspr3imfVamB0pD0ofp7eI*63l9Fs& zr3l{u1?T*tR0T6V13d#JpmqfXo1&C7s~{IQsCFRFRw<*Tq`*pFzr4I$uiRKKzbIYb z(9+UU-@r)U$VeBcLbtdwuOzWTH?LS3VhGe2m(=3qqRfJl%=|nBkgZI5j|wgoG(l0)!-PaNq+|H;`we56>zf9z46H<^l6q5im1FHN8j& zX2iFiE{-7)?r*2P=4)2qaa$R)r{{b9y{$Q1)A)rq>ztl6Phye$*9CtT>eY8}`Y6me zz;ft2%k$2URvB$6Gfw?zTV^z^npI!|zYSab>)%V5SoDe(E{|Al@o`P&BrYKfuVSU^ z9J`h@8O3E!T%Yk`vaE`N{PT)m5A7@^IIj6EnAF^u@R~)-@<6pi(bg{~`9$sA5$;_&p7L|)^X zbyBPfGlF;AaJj3&aIi`@pqEJ?=Q`6>4F<=u!{2W%kY-icar8cSQ5_TWpPlk{ug)z5 O6+xb^elF{r5}E+CH_lD~ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..7be0d597256ad965a8844c4be42e510b94b1c30a GIT binary patch literal 1349 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkO~f+6Go|Nk=?W)?EEJbn7~;>C+yU0pstK2AtCq9nrC$0|8L zS1&OoKPgqOBDVmjnt{Q_zM>#8IXksPAt^OIGtXA({qFrr3YjUkO5vuy2EGN(sTr9b zRYj@6RemAKRoTgwDN6Qsyj(UFRzMSSQ%e#RDspr3imfVamB0pD0ofp7eI*63l9Fs& zr3l{u1?T*tR0T6V13d#JpmqfXo1&C7s~{IQsCFRFRw<*Tq`*pFzr4I$uiRKKzbIYb z(9+UU-@r)U$VeBcLbtdwuOzWTH?LS3VhGe2m(=3qqRfJl%=|nBkgZI5j|wgoG(l0)!-PaNq+|H;`we56>zf9z46H<^l6q5im1FHN8j& zX2e&XE{-7)?r*2vER)}TjsoGYdW>?y;Y^wu@BSJ;ICNs^ z1BDf(oEkHBipa2R)njDkp5F9{2dGvg@!6|r@p}D^kDOOrcgoLf&({K#Kc23BF6*2U FngG4m&Uyd< literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/meta.json index 775a3c2151..02abb07806 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..83d3f527c8c2448a3946b945f51d52cec933bb21 GIT binary patch literal 395 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E1o(uwS}-sdvoKAZIPw4g|4*MjO=e*5WMJPO7pE$%Fw4>L;>C+Vg)1XX z6Mz&?NswPKP#Fv`2;Dz-4k*r9;1OBOz`%D9gc)~C%zg_Lyy5BM7!u+BcG_FMW(6MC zrG6Jb{(HY$Y=6T&Jwt9o@2aGZY0o(RJmh=n9`k_dAgi9hFV0t-zwI;Bt~*&8x7a-U z&K9sF@YZ3E+vcsmC&_ zd0(IERE(IiVC9^WNXMw~O0SZq3|#LXFo|>M6*N9*VrN+;&+W68k86i$+>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E1o(uwS}-sdvoKAZIPw4g|4*MjO=e*5WMJPO7pE$%Fw4>L;>C+Vg)1XX z6Mz&?NswPKP#Fv`2;Dz-4k*r9;1OBOz`%C^gc-B0b#ej)?|8a6hD5l(opzD0S%JrO z>53OW{{NR1UnN$$+Nw6WPz;k3rEElijwVmEH^+ELX2`v8m8?ULpc^BHG e{b#SjG4rgVRV_tr87qPQWbkzLb6Mw<&;$TYIii~Y literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/meta.json index 775a3c2151..02abb07806 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..5dc3a92fdcdbb9441a1813f956b59cb2ef668d2a GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkQgkfG)O|Nk=?WPNgLYHC*T-p=@w|MyD6+@!WoKjMOTNzfJ$ms+Skx4J5||!UH(h7`D_8O;le09n zW<_?b90Qa2#U=CmlxNB)IO-&>(GH%c{G3UpK=16zHGgDP*)_Dwbr0tn-E6!h@Lb}T z@JthtgIX-kmjYO33-EX{xA)oE8r?kjl6}SjmKIJQhZzspnGE|`&F`^p(nx3w=Q+P< z!tX^5EGJdpxVkcOPuXu6B68qKQFxrXOCZDM;-FnFj$*6^yXJ8pdv&1tc$uEWYUcO0 nxBNXi78Mx%maEr4@sm+0cE_x0zmfu=XBj+Q{an^LB{Ts5GV`69 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c28ef522612bf5ae467a6afd3f5f32f1fc41a27f GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkQgkfG)O|Nk=?WyyH`9>pEBdg^MiVAPn~@dn2xZhIqXx-_@1MFZD+AbP;6UK z-(?ks{`|S?fAi0cV`XIO-4fDN^s+^S;fUR`iPOAZn9Of92&t2wn0al-1@`dlfS0ib zW}JOr^k&Rraj%-qFh43~*S{;wY6gr+Ou`CkKUx=D)%g7C-hQLcYXq4-SlE?ue%>3& zb!BfoqmqlBYS4uDLItzEJH5nru~hKJaHhoF{T;4wWS;jy&k3)ECM7%xDiC_It3_qT r4v`fV3%z&TJ^keMkLrV)_AtI#xntJzZ;sc2-evG~^>bP0l+XkK<|nAz literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/meta.json index 775a3c2151..02abb07806 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..2dde993a3df6075fcb5df9e01f536fbe0336ddc8 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkPVyq)j=|Nn|Bq@F%~dhz1LuC6X0A0Hc0P<4y1TXg8YJkis694_JvX#P=d3-BeIx*f$ty)Gwzs}{T3+r(9^{+B*Ojew6}bR z6nI=4586%nULU7f#`E&+qR1Y@mz|B3FZnv__vpnRVNp}?NnmwrYjpGAOZFKDSXwxJ9A-RVXEN+(HNVHYNh6^#oag+a z3BMOLu$)wV1 n-tzb8SX5y2TdrRJ#7{=0*d4Q~{Ynaeo@MZK^>bP0l+XkK`uU!i literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..285c57c33f339c5e14303a550e55cf94a630c244 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkPVyq)j=|Nn|Bq@F%~dhz1LuC6X0A0Hc0P<4y1TXg8YJkis694_JvX#P=d3-BeIx*f$ty)Gwzs}{T3+r*we)^B*OjeG*7-} z10Gjjwq-^CKbx7c<+Sl+?OyRjeaeg{&kyRgJ$3d;U^>F0=CDsS<9m+!wVlN#L9uN` zeV0`j`t#?m|II%)j+K$AcS}f9(aRPQh9h>%CQkEuVKTqbAf!%yV&=6S7udtI175}& zm~r-f(VH=g#l31a!~Cd}UH`5ys~IpRF$pWE{b*fqRpaxkd;5((uMuSWU}0Cr`FU?7 z*Ok5Xj7lzgszDRp3l+@v?(`Dh#Zti=!tnw0P)s6gn+t`?OU rJ49AgEcD)S_w Date: Sat, 30 Mar 2024 14:31:47 +1100 Subject: [PATCH 06/14] Fix itemslots swapping (#25634) Fix itemslots prediction --- Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 3463be2e71..83e09b1a6d 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -279,7 +279,7 @@ namespace Content.Shared.Containers.ItemSlots if (ev.Cancelled) return false; - return _containers.CanInsert(usedUid, slot.ContainerSlot, assumeEmpty: true); + return _containers.CanInsert(usedUid, slot.ContainerSlot, assumeEmpty: swap); } /// From 3af54a286ee3630a00502db20a65f958863e3321 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 03:32:37 +0000 Subject: [PATCH 07/14] 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 420db73301..2a3ec713b3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Blackern5000 - changes: - - message: Crushers can no longer be researched. - type: Remove - id: 5755 - time: '2024-01-20T05:11:02.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24319 - author: metalgearsloth changes: - message: Fix buckle sound playing twice in some instances. @@ -3794,3 +3787,10 @@ id: 6254 time: '2024-03-30T02:46:20.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25020 +- author: SonicHDC + changes: + - message: Added unzipping for lab coats! + type: Add + id: 6255 + time: '2024-03-30T03:31:32.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26494 From 39753892c2b7a86fb7a10480a32a9dd8252ce80f Mon Sep 17 00:00:00 2001 From: Zealith-Gamer <61980908+Zealith-Gamer@users.noreply.github.com> Date: Fri, 29 Mar 2024 20:35:42 -0700 Subject: [PATCH 08/14] Stop items that are being pulled from spinning (#26504) * Fixed pulled items spinning when moved * edited out others issues * more reverts * requested fix * Removed "Optional:" --- .../Movement/Pulling/Systems/PullingSystem.cs | 2 +- Content.Shared/Throwing/ThrowingSystem.cs | 38 +++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index b347c6da16..acca7aafd0 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -269,7 +269,7 @@ public sealed class PullingSystem : EntitySystem } Dirty(player, pullerComp); - _throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false); + _throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false, doSpin: false); return false; } diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs index 38772eaf34..7d94ada924 100644 --- a/Content.Shared/Throwing/ThrowingSystem.cs +++ b/Content.Shared/Throwing/ThrowingSystem.cs @@ -49,7 +49,8 @@ public sealed class ThrowingSystem : EntitySystem float pushbackRatio = PushbackDefault, bool recoil = true, bool animated = true, - bool playSound = true) + bool playSound = true, + bool doSpin = true) { var thrownPos = _transform.GetMapCoordinates(uid); var mapPos = _transform.ToMapCoordinates(coordinates); @@ -57,7 +58,7 @@ public sealed class ThrowingSystem : EntitySystem if (mapPos.MapId != thrownPos.MapId) return; - TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, recoil: recoil, animated: animated, playSound: playSound); + TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin); } /// @@ -67,6 +68,7 @@ public sealed class ThrowingSystem : EntitySystem /// A vector pointing from the entity to its destination. /// How much the direction vector should be multiplied for velocity. /// The ratio of impulse applied to the thrower - defaults to 10 because otherwise it's not enough to properly recover from getting spaced + /// Whether spin will be applied to the thrown entity. public void TryThrow(EntityUid uid, Vector2 direction, float strength = 1.0f, @@ -74,7 +76,8 @@ public sealed class ThrowingSystem : EntitySystem float pushbackRatio = PushbackDefault, bool recoil = true, bool animated = true, - bool playSound = true) + bool playSound = true, + bool doSpin = true) { var physicsQuery = GetEntityQuery(); if (!physicsQuery.TryGetComponent(uid, out var physics)) @@ -90,7 +93,7 @@ public sealed class ThrowingSystem : EntitySystem projectileQuery, strength, user, - pushbackRatio, recoil: recoil, animated: animated, playSound: playSound); + pushbackRatio, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin); } /// @@ -100,6 +103,7 @@ public sealed class ThrowingSystem : EntitySystem /// A vector pointing from the entity to its destination. /// How much the direction vector should be multiplied for velocity. /// The ratio of impulse applied to the thrower - defaults to 10 because otherwise it's not enough to properly recover from getting spaced + /// Whether spin will be applied to the thrown entity. public void TryThrow(EntityUid uid, Vector2 direction, PhysicsComponent physics, @@ -110,7 +114,8 @@ public sealed class ThrowingSystem : EntitySystem float pushbackRatio = PushbackDefault, bool recoil = true, bool animated = true, - bool playSound = true) + bool playSound = true, + bool doSpin = true) { if (strength <= 0 || direction == Vector2Helpers.Infinity || direction == Vector2Helpers.NaN || direction == Vector2.Zero) return; @@ -147,17 +152,20 @@ public sealed class ThrowingSystem : EntitySystem ThrowingAngleComponent? throwingAngle = null; // Give it a l'il spin. - if (physics.InvI > 0f && (!TryComp(uid, out throwingAngle) || throwingAngle.AngularVelocity)) + if (doSpin) { - _physics.ApplyAngularImpulse(uid, ThrowAngularImpulse / physics.InvI, body: physics); - } - else - { - Resolve(uid, ref throwingAngle, false); - var gridRot = _transform.GetWorldRotation(transform.ParentUid); - var angle = direction.ToWorldAngle() - gridRot; - var offset = throwingAngle?.Angle ?? Angle.Zero; - _transform.SetLocalRotation(uid, angle + offset); + if (physics.InvI > 0f && (!TryComp(uid, out throwingAngle) || throwingAngle.AngularVelocity)) + { + _physics.ApplyAngularImpulse(uid, ThrowAngularImpulse / physics.InvI, body: physics); + } + else + { + Resolve(uid, ref throwingAngle, false); + var gridRot = _transform.GetWorldRotation(transform.ParentUid); + var angle = direction.ToWorldAngle() - gridRot; + var offset = throwingAngle?.Angle ?? Angle.Zero; + _transform.SetLocalRotation(uid, angle + offset); + } } var throwEvent = new ThrownEvent(user, uid); From 3fc02edd4ea10d914f280e367c49238ec6126f02 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 03:36:48 +0000 Subject: [PATCH 09/14] 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 2a3ec713b3..b4ec1e0b33 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix buckle sound playing twice in some instances. - type: Fix - id: 5756 - time: '2024-01-20T06:22:19.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24321 - author: tday changes: - message: Added admin log messages for adding and ending game rules, and for the @@ -3794,3 +3787,10 @@ id: 6255 time: '2024-03-30T03:31:32.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26494 +- author: Zealith-Gamer + changes: + - message: Items being pulled no longer spin when being thrown. + type: Fix + id: 6256 + time: '2024-03-30T03:35:43.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26504 From f192d7901fedd134c38a6cab38731f8e93994492 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Fri, 29 Mar 2024 20:59:16 -0700 Subject: [PATCH 10/14] Hyposprays Draw from Jugs (#25544) * Hyposprays Draw from Jugs * Fix last onlyMobs usage in yml * Some Suggested Changes * Remove unnecessary datafield name declarations * Remove unnecessary dirtying of component * Same line parentheses * Added client-side HypospraySystem * Cache UI values and only updates if values change * empty line * Update label * Label change * Reimplement ReactionMixerSystem * Remove DataField from Hypospray Toggle Mode * Change ToggleMode from enum to Bool OnlyAffectsMobs * Add DataField required back since it's required for replays...? * update EligibleEntity and uses of it * Add user argument back * Adds newline Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Guard for dirty entity * Adds summary tag --------- Co-authored-by: Plykiya Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../Components/HyposprayComponent.cs | 16 -- .../EntitySystems/HypospraySystem.cs | 15 ++ .../Chemistry/EntitySystems/InjectorSystem.cs | 13 -- .../Chemistry/UI/HyposprayStatusControl.cs | 48 +++-- .../Body/Components/BloodstreamComponent.cs | 2 +- .../Components/HyposprayComponent.cs | 28 --- .../EntitySystems/ChemistrySystem.cs | 26 --- .../EntitySystems/ChemistrySystemHypospray.cs | 164 --------------- .../EntitySystems/HypospraySystem.cs | 197 ++++++++++++++++++ ...ySystemMixer.cs => ReactionMixerSystem.cs} | 11 +- .../Components/HyposprayComponent.cs | 33 +++ .../Components/SharedHyposprayComponent.cs | 25 --- .../EntitySystems/SharedHypospraySystem.cs | 61 ++++++ .../EntitySystems/SharedInjectorSystem.cs | 3 - .../components/hypospray-component.ftl | 12 +- .../Objects/Specific/Medical/hypospray.yml | 14 +- 16 files changed, 369 insertions(+), 299 deletions(-) delete mode 100644 Content.Client/Chemistry/Components/HyposprayComponent.cs create mode 100644 Content.Client/Chemistry/EntitySystems/HypospraySystem.cs delete mode 100644 Content.Server/Chemistry/Components/HyposprayComponent.cs delete mode 100644 Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs delete mode 100644 Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs create mode 100644 Content.Server/Chemistry/EntitySystems/HypospraySystem.cs rename Content.Server/Chemistry/EntitySystems/{ChemistrySystemMixer.cs => ReactionMixerSystem.cs} (76%) create mode 100644 Content.Shared/Chemistry/Components/HyposprayComponent.cs delete mode 100644 Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs create mode 100644 Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs diff --git a/Content.Client/Chemistry/Components/HyposprayComponent.cs b/Content.Client/Chemistry/Components/HyposprayComponent.cs deleted file mode 100644 index 705b79ad84..0000000000 --- a/Content.Client/Chemistry/Components/HyposprayComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Content.Shared.Chemistry.Components; -using Content.Shared.FixedPoint; - -namespace Content.Client.Chemistry.Components -{ - [RegisterComponent] - public sealed partial class HyposprayComponent : SharedHyposprayComponent - { - [ViewVariables] - public FixedPoint2 CurrentVolume; - [ViewVariables] - public FixedPoint2 TotalVolume; - [ViewVariables(VVAccess.ReadWrite)] - public bool UiUpdateNeeded; - } -} diff --git a/Content.Client/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Client/Chemistry/EntitySystems/HypospraySystem.cs new file mode 100644 index 0000000000..ee7aa3aafe --- /dev/null +++ b/Content.Client/Chemistry/EntitySystems/HypospraySystem.cs @@ -0,0 +1,15 @@ +using Content.Client.Chemistry.UI; +using Content.Client.Items; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; + +namespace Content.Client.Chemistry.EntitySystems; + +public sealed class HypospraySystem : SharedHypospraySystem +{ + public override void Initialize() + { + base.Initialize(); + Subs.ItemStatus(ent => new HyposprayStatusControl(ent, _solutionContainers)); + } +} diff --git a/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs b/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs index 12eb7f3d14..0131a283c8 100644 --- a/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs +++ b/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs @@ -1,4 +1,3 @@ -using Content.Client.Chemistry.Components; using Content.Client.Chemistry.UI; using Content.Client.Items; using Content.Shared.Chemistry.Components; @@ -13,17 +12,5 @@ public sealed class InjectorSystem : SharedInjectorSystem { base.Initialize(); Subs.ItemStatus(ent => new InjectorStatusControl(ent, SolutionContainers)); - SubscribeLocalEvent(OnHandleHyposprayState); - Subs.ItemStatus(ent => new HyposprayStatusControl(ent)); - } - - private void OnHandleHyposprayState(EntityUid uid, HyposprayComponent component, ref ComponentHandleState args) - { - if (args.Current is not HyposprayComponentState cState) - return; - - component.CurrentVolume = cState.CurVolume; - component.TotalVolume = cState.MaxVolume; - component.UiUpdateNeeded = true; } } diff --git a/Content.Client/Chemistry/UI/HyposprayStatusControl.cs b/Content.Client/Chemistry/UI/HyposprayStatusControl.cs index bd85cd546c..4a4d90dc4d 100644 --- a/Content.Client/Chemistry/UI/HyposprayStatusControl.cs +++ b/Content.Client/Chemistry/UI/HyposprayStatusControl.cs @@ -1,6 +1,8 @@ -using Content.Client.Chemistry.Components; using Content.Client.Message; using Content.Client.Stylesheets; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.FixedPoint; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; @@ -9,34 +11,48 @@ namespace Content.Client.Chemistry.UI; public sealed class HyposprayStatusControl : Control { - private readonly HyposprayComponent _parent; + private readonly Entity _parent; private readonly RichTextLabel _label; + private readonly SharedSolutionContainerSystem _solutionContainers; - public HyposprayStatusControl(HyposprayComponent parent) + private FixedPoint2 PrevVolume; + private FixedPoint2 PrevMaxVolume; + private bool PrevOnlyAffectsMobs; + + public HyposprayStatusControl(Entity parent, SharedSolutionContainerSystem solutionContainers) { _parent = parent; - _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}}; + _solutionContainers = solutionContainers; + _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; AddChild(_label); - - Update(); } protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); - if (!_parent.UiUpdateNeeded) + + if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution)) return; - Update(); - } - public void Update() - { + // only updates the UI if any of the details are different than they previously were + if (PrevVolume == solution.Volume + && PrevMaxVolume == solution.MaxVolume + && PrevOnlyAffectsMobs == _parent.Comp.OnlyAffectsMobs) + return; - _parent.UiUpdateNeeded = false; + PrevVolume = solution.Volume; + PrevMaxVolume = solution.MaxVolume; + PrevOnlyAffectsMobs = _parent.Comp.OnlyAffectsMobs; - _label.SetMarkup(Loc.GetString( - "hypospray-volume-text", - ("currentVolume", _parent.CurrentVolume), - ("totalVolume", _parent.TotalVolume))); + var modeStringLocalized = Loc.GetString(_parent.Comp.OnlyAffectsMobs switch + { + false => "hypospray-all-mode-text", + true => "hypospray-mobs-only-mode-text", + }); + + _label.SetMarkup(Loc.GetString("hypospray-volume-label", + ("currentVolume", solution.Volume), + ("totalVolume", solution.MaxVolume), + ("modeString", modeStringLocalized))); } } diff --git a/Content.Server/Body/Components/BloodstreamComponent.cs b/Content.Server/Body/Components/BloodstreamComponent.cs index d448c4aab2..1d8aa9ffd3 100644 --- a/Content.Server/Body/Components/BloodstreamComponent.cs +++ b/Content.Server/Body/Components/BloodstreamComponent.cs @@ -11,7 +11,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components { - [RegisterComponent, Access(typeof(BloodstreamSystem), (typeof(ChemistrySystem)))] + [RegisterComponent, Access(typeof(BloodstreamSystem), typeof(ReactionMixerSystem))] public sealed partial class BloodstreamComponent : Component { public static string DefaultChemicalsSolutionName = "chemicals"; diff --git a/Content.Server/Chemistry/Components/HyposprayComponent.cs b/Content.Server/Chemistry/Components/HyposprayComponent.cs deleted file mode 100644 index 2a80cec801..0000000000 --- a/Content.Server/Chemistry/Components/HyposprayComponent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Content.Shared.Chemistry.Components; -using Content.Shared.FixedPoint; -using Robust.Shared.Audio; - -namespace Content.Server.Chemistry.Components -{ - [RegisterComponent] - public sealed partial class HyposprayComponent : SharedHyposprayComponent - { - // TODO: This should be on clumsycomponent. - [DataField("clumsyFailChance")] - [ViewVariables(VVAccess.ReadWrite)] - public float ClumsyFailChance = 0.5f; - - [DataField("transferAmount")] - [ViewVariables(VVAccess.ReadWrite)] - public FixedPoint2 TransferAmount = FixedPoint2.New(5); - - [DataField("injectSound")] - public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg"); - - /// - /// Whether or not the hypo is able to inject only into mobs. On false you can inject into beakers/jugs - /// - [DataField("onlyMobs")] - public bool OnlyMobs = true; - } -} diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs deleted file mode 100644 index c4f22dc63a..0000000000 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Content.Server.Administration.Logs; -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Interaction; -using Content.Server.Popups; -using Content.Shared.Chemistry; -using Robust.Shared.Audio.Systems; - -namespace Content.Server.Chemistry.EntitySystems; - -public sealed partial class ChemistrySystem : EntitySystem -{ - [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly IEntityManager _entMan = default!; - [Dependency] private readonly InteractionSystem _interaction = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly ReactiveSystem _reactiveSystem = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainers = default!; - - public override void Initialize() - { - // Why ChemMaster duplicates reagentdispenser nobody knows. - InitializeHypospray(); - InitializeMixing(); - } -} diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs deleted file mode 100644 index be8faec984..0000000000 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs +++ /dev/null @@ -1,164 +0,0 @@ -using Content.Server.Chemistry.Components; -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Components.SolutionManager; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Chemistry.Reagent; -using Content.Shared.Database; -using Content.Shared.FixedPoint; -using Content.Shared.Forensics; -using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Mobs.Components; -using Content.Shared.Timing; -using Content.Shared.Weapons.Melee.Events; -using Robust.Shared.GameStates; -using System.Diagnostics.CodeAnalysis; -using System.Linq; - -namespace Content.Server.Chemistry.EntitySystems -{ - public sealed partial class ChemistrySystem - { - [Dependency] private readonly UseDelaySystem _useDelay = default!; - - private void InitializeHypospray() - { - SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent(OnAttack); - SubscribeLocalEvent(OnSolutionChange); - SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnHypoGetState); - } - - private void OnHypoGetState(Entity entity, ref ComponentGetState args) - { - args.State = _solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out _, out var solution) - ? new HyposprayComponentState(solution.Volume, solution.MaxVolume) - : new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero); - } - - private void OnUseInHand(Entity entity, ref UseInHandEvent args) - { - if (args.Handled) - return; - - TryDoInject(entity, args.User, args.User); - args.Handled = true; - } - - private void OnSolutionChange(Entity entity, ref SolutionContainerChangedEvent args) - { - Dirty(entity); - } - - public void OnAfterInteract(Entity entity, ref AfterInteractEvent args) - { - if (!args.CanReach) - return; - - var target = args.Target; - var user = args.User; - - TryDoInject(entity, target, user); - } - - public void OnAttack(Entity entity, ref MeleeHitEvent args) - { - if (!args.HitEntities.Any()) - return; - - TryDoInject(entity, args.HitEntities.First(), args.User); - } - - public bool TryDoInject(Entity hypo, EntityUid? target, EntityUid user) - { - var (uid, component) = hypo; - - if (!EligibleEntity(target, _entMan, component)) - return false; - - if (TryComp(uid, out UseDelayComponent? delayComp)) - { - if (_useDelay.IsDelayed((uid, delayComp))) - return false; - } - - - string? msgFormat = null; - - if (target == user) - msgFormat = "hypospray-component-inject-self-message"; - else if (EligibleEntity(user, _entMan, component) && _interaction.TryRollClumsy(user, component.ClumsyFailChance)) - { - msgFormat = "hypospray-component-inject-self-clumsy-message"; - target = user; - } - - if (!_solutionContainers.TryGetSolution(uid, component.SolutionName, out var hypoSpraySoln, out var hypoSpraySolution) || hypoSpraySolution.Volume == 0) - { - _popup.PopupCursor(Loc.GetString("hypospray-component-empty-message"), user); - return true; - } - - if (!_solutionContainers.TryGetInjectableSolution(target.Value, out var targetSoln, out var targetSolution)) - { - _popup.PopupCursor(Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target.Value, _entMan))), user); - return false; - } - - _popup.PopupCursor(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message", ("other", target)), user); - - if (target != user) - { - _popup.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target.Value, target.Value); - // TODO: This should just be using melee attacks... - // meleeSys.SendLunge(angle, user); - } - - _audio.PlayPvs(component.InjectSound, user); - - // Medipens and such use this system and don't have a delay, requiring extra checks - // BeginDelay function returns if item is already on delay - if (delayComp != null) - _useDelay.TryResetDelay((uid, delayComp)); - - // Get transfer amount. May be smaller than component.TransferAmount if not enough room - var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume); - - if (realTransferAmount <= 0) - { - _popup.PopupCursor(Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target)), user); - return true; - } - - // Move units from attackSolution to targetSolution - var removedSolution = _solutionContainers.SplitSolution(hypoSpraySoln.Value, realTransferAmount); - - if (!targetSolution.CanAddSolution(removedSolution)) - return true; - _reactiveSystem.DoEntityReaction(target.Value, removedSolution, ReactionMethod.Injection); - _solutionContainers.TryAddSolution(targetSoln.Value, removedSolution); - - var ev = new TransferDnaEvent { Donor = target.Value, Recipient = uid }; - RaiseLocalEvent(target.Value, ref ev); - - // same LogType as syringes... - _adminLogger.Add(LogType.ForceFeed, $"{_entMan.ToPrettyString(user):user} injected {_entMan.ToPrettyString(target.Value):target} with a solution {SolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {_entMan.ToPrettyString(uid):using}"); - - return true; - } - - static bool EligibleEntity([NotNullWhen(true)] EntityUid? entity, IEntityManager entMan, HyposprayComponent component) - { - // TODO: Does checking for BodyComponent make sense as a "can be hypospray'd" tag? - // In SS13 the hypospray ONLY works on mobs, NOT beakers or anything else. - // But this is 14, we dont do what SS13 does just because SS13 does it. - return component.OnlyMobs - ? entMan.HasComponent(entity) && - entMan.HasComponent(entity) - : entMan.HasComponent(entity); - } - } -} diff --git a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs new file mode 100644 index 0000000000..dfbe45c035 --- /dev/null +++ b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs @@ -0,0 +1,197 @@ +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Database; +using Content.Shared.FixedPoint; +using Content.Shared.Forensics; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Mobs.Components; +using Content.Shared.Timing; +using Content.Shared.Weapons.Melee.Events; +using Content.Server.Interaction; +using Content.Server.Body.Components; +using Content.Server.Chemistry.Containers.EntitySystems; +using Robust.Shared.GameStates; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Robust.Server.Audio; + +namespace Content.Server.Chemistry.EntitySystems; + +public sealed class HypospraySystem : SharedHypospraySystem +{ + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly InteractionSystem _interaction = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnAttack); + SubscribeLocalEvent(OnUseInHand); + } + + private void UseHypospray(Entity entity, EntityUid target, EntityUid user) + { + // if target is ineligible but is a container, try to draw from the container + if (!EligibleEntity(target, EntityManager, entity) + && _solutionContainers.TryGetDrawableSolution(target, out var drawableSolution, out _)) + { + TryDraw(entity, target, drawableSolution.Value, user); + } + + TryDoInject(entity, target, user); + } + + private void OnUseInHand(Entity entity, ref UseInHandEvent args) + { + if (args.Handled) + return; + + TryDoInject(entity, args.User, args.User); + args.Handled = true; + } + + public void OnAfterInteract(Entity entity, ref AfterInteractEvent args) + { + if (args.Handled || !args.CanReach || args.Target == null) + return; + + UseHypospray(entity, args.Target.Value, args.User); + args.Handled = true; + } + + public void OnAttack(Entity entity, ref MeleeHitEvent args) + { + if (!args.HitEntities.Any()) + return; + + TryDoInject(entity, args.HitEntities.First(), args.User); + } + + public bool TryDoInject(Entity entity, EntityUid target, EntityUid user) + { + var (uid, component) = entity; + + if (!EligibleEntity(target, EntityManager, component)) + return false; + + if (TryComp(uid, out UseDelayComponent? delayComp)) + { + if (_useDelay.IsDelayed((uid, delayComp))) + return false; + } + + string? msgFormat = null; + + if (target == user) + msgFormat = "hypospray-component-inject-self-message"; + else if (EligibleEntity(user, EntityManager, component) && _interaction.TryRollClumsy(user, component.ClumsyFailChance)) + { + msgFormat = "hypospray-component-inject-self-clumsy-message"; + target = user; + } + + if (!_solutionContainers.TryGetSolution(uid, component.SolutionName, out var hypoSpraySoln, out var hypoSpraySolution) || hypoSpraySolution.Volume == 0) + { + _popup.PopupEntity(Loc.GetString("hypospray-component-empty-message"), target, user); + return true; + } + + if (!_solutionContainers.TryGetInjectableSolution(target, out var targetSoln, out var targetSolution)) + { + _popup.PopupEntity(Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target, EntityManager))), target, user); + return false; + } + + _popup.PopupEntity(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message", ("other", target)), target, user); + + if (target != user) + { + _popup.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target, target); + // TODO: This should just be using melee attacks... + // meleeSys.SendLunge(angle, user); + } + + _audio.PlayPvs(component.InjectSound, user); + + // Medipens and such use this system and don't have a delay, requiring extra checks + // BeginDelay function returns if item is already on delay + if (delayComp != null) + _useDelay.TryResetDelay((uid, delayComp)); + + // Get transfer amount. May be smaller than component.TransferAmount if not enough room + var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume); + + if (realTransferAmount <= 0) + { + _popup.PopupEntity(Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target)), target, user); + return true; + } + + // Move units from attackSolution to targetSolution + var removedSolution = _solutionContainers.SplitSolution(hypoSpraySoln.Value, realTransferAmount); + + if (!targetSolution.CanAddSolution(removedSolution)) + return true; + _reactiveSystem.DoEntityReaction(target, removedSolution, ReactionMethod.Injection); + _solutionContainers.TryAddSolution(targetSoln.Value, removedSolution); + + var ev = new TransferDnaEvent { Donor = target, Recipient = uid }; + RaiseLocalEvent(target, ref ev); + + // same LogType as syringes... + _adminLogger.Add(LogType.ForceFeed, $"{EntityManager.ToPrettyString(user):user} injected {EntityManager.ToPrettyString(target):target} with a solution {SolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {EntityManager.ToPrettyString(uid):using}"); + + return true; + } + + private void TryDraw(Entity entity, Entity target, Entity targetSolution, EntityUid user) + { + if (!_solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, + out var solution) || solution.AvailableVolume == 0) + { + return; + } + + // Get transfer amount. May be smaller than _transferAmount if not enough room, also make sure there's room in the injector + var realTransferAmount = FixedPoint2.Min(entity.Comp.TransferAmount, targetSolution.Comp.Solution.Volume, + solution.AvailableVolume); + + if (realTransferAmount <= 0) + { + _popup.PopupEntity( + Loc.GetString("injector-component-target-is-empty-message", + ("target", Identity.Entity(target, EntityManager))), + entity.Owner, user); + return; + } + + var removedSolution = _solutionContainers.Draw(target.Owner, targetSolution, realTransferAmount); + + if (!_solutionContainers.TryAddSolution(soln.Value, removedSolution)) + { + return; + } + + _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message", + ("amount", removedSolution.Volume), + ("target", Identity.Entity(target, EntityManager))), entity.Owner, user); + } + + private bool EligibleEntity(EntityUid entity, IEntityManager entMan, HyposprayComponent component) + { + // TODO: Does checking for BodyComponent make sense as a "can be hypospray'd" tag? + // In SS13 the hypospray ONLY works on mobs, NOT beakers or anything else. + // But this is 14, we dont do what SS13 does just because SS13 does it. + return component.OnlyAffectsMobs + ? entMan.HasComponent(entity) && + entMan.HasComponent(entity) + : entMan.HasComponent(entity); + } +} diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystemMixer.cs b/Content.Server/Chemistry/EntitySystems/ReactionMixerSystem.cs similarity index 76% rename from Content.Server/Chemistry/EntitySystems/ChemistrySystemMixer.cs rename to Content.Server/Chemistry/EntitySystems/ReactionMixerSystem.cs index 0230671ec9..032374d4a5 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystemMixer.cs +++ b/Content.Server/Chemistry/EntitySystems/ReactionMixerSystem.cs @@ -1,13 +1,20 @@ using Content.Shared.Chemistry.Reaction; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Popups; namespace Content.Server.Chemistry.EntitySystems; -public sealed partial class ChemistrySystem +public sealed partial class ReactionMixerSystem : EntitySystem { - public void InitializeMixing() + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainers = default!; + + public override void Initialize() { + base.Initialize(); + SubscribeLocalEvent(OnAfterInteract); } diff --git a/Content.Shared/Chemistry/Components/HyposprayComponent.cs b/Content.Shared/Chemistry/Components/HyposprayComponent.cs new file mode 100644 index 0000000000..05d202aaaa --- /dev/null +++ b/Content.Shared/Chemistry/Components/HyposprayComponent.cs @@ -0,0 +1,33 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Audio; + +namespace Content.Shared.Chemistry.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class HyposprayComponent : Component +{ + [DataField] + public string SolutionName = "hypospray"; + + // TODO: This should be on clumsycomponent. + [DataField] + [ViewVariables(VVAccess.ReadWrite)] + public float ClumsyFailChance = 0.5f; + + [DataField] + [ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 TransferAmount = FixedPoint2.New(5); + + [DataField] + public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg"); + + /// + /// Decides whether you can inject everything or just mobs. + /// When you can only affect mobs, you're capable of drawing from beakers. + /// + [AutoNetworkedField] + [DataField(required: true)] + public bool OnlyAffectsMobs = false; +} diff --git a/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs b/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs deleted file mode 100644 index a8df6be109..0000000000 --- a/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Content.Shared.FixedPoint; -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared.Chemistry.Components; - -[NetworkedComponent()] -public abstract partial class SharedHyposprayComponent : Component -{ - [DataField("solutionName")] - public string SolutionName = "hypospray"; -} - -[Serializable, NetSerializable] -public sealed class HyposprayComponentState : ComponentState -{ - public FixedPoint2 CurVolume { get; } - public FixedPoint2 MaxVolume { get; } - - public HyposprayComponentState(FixedPoint2 curVolume, FixedPoint2 maxVolume) - { - CurVolume = curVolume; - MaxVolume = maxVolume; - } -} diff --git a/Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs new file mode 100644 index 0000000000..f91e5621f0 --- /dev/null +++ b/Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs @@ -0,0 +1,61 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Timing; +using Content.Shared.Verbs; +using Content.Shared.Popups; +using Robust.Shared.Player; +using Content.Shared.Administration.Logs; + +namespace Content.Shared.Chemistry.EntitySystems; + +public abstract class SharedHypospraySystem : EntitySystem +{ + [Dependency] protected readonly UseDelaySystem _useDelay = default!; + [Dependency] protected readonly SharedPopupSystem _popup = default!; + [Dependency] protected readonly SharedSolutionContainerSystem _solutionContainers = default!; + [Dependency] protected readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] protected readonly ReactiveSystem _reactiveSystem = default!; + + public override void Initialize() + { + SubscribeLocalEvent>(AddToggleModeVerb); + } + + // + // Uses the OnlyMobs field as a check to implement the ability + // to draw from jugs and containers with the hypospray + // Toggleable to allow people to inject containers if they prefer it over drawing + // + private void AddToggleModeVerb(Entity entity, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || args.Hands == null) + return; + + var (_, component) = entity; + var user = args.User; + var verb = new AlternativeVerb + { + Text = Loc.GetString("hypospray-verb-mode-label"), + Act = () => + { + ToggleMode(entity, user); + } + }; + args.Verbs.Add(verb); + } + + private void ToggleMode(Entity entity, EntityUid user) + { + SetMode(entity, !entity.Comp.OnlyAffectsMobs); + string msg = entity.Comp.OnlyAffectsMobs ? "hypospray-verb-mode-inject-mobs-only" : "hypospray-verb-mode-inject-all"; + _popup.PopupClient(Loc.GetString(msg), entity, user); + } + + public void SetMode(Entity entity, bool onlyAffectsMobs) + { + if (entity.Comp.OnlyAffectsMobs == onlyAffectsMobs) + return; + + entity.Comp.OnlyAffectsMobs = onlyAffectsMobs; + Dirty(entity); + } +} diff --git a/Content.Shared/Chemistry/EntitySystems/SharedInjectorSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedInjectorSystem.cs index 7e41cb39bd..6c43c1d5f0 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedInjectorSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedInjectorSystem.cs @@ -37,10 +37,7 @@ public abstract class SharedInjectorSystem : EntitySystem if (!args.CanAccess || !args.CanInteract || args.Hands == null) return; - if (!HasComp(args.User)) - return; var user = args.User; - var (_, component) = entity; var min = component.MinimumTransferAmount; diff --git a/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl b/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl index 7acbe8664c..52dbf9010e 100644 --- a/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl +++ b/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl @@ -1,13 +1,21 @@ ## UI -hypospray-volume-text = Volume: [color=white]{$currentVolume}/{$totalVolume}[/color] +hypospray-all-mode-text = Only Injects +hypospray-mobs-only-mode-text = Draws and Injects +hypospray-invalid-text = Invalid +hypospray-volume-label = Volume: [color=white]{$currentVolume}/{$totalVolume}u[/color] + Mode: [color=white]{$modeString}[/color] ## Entity hypospray-component-inject-other-message = You inject {$other}. hypospray-component-inject-self-message = You inject yourself. hypospray-component-inject-self-clumsy-message = Oops! You injected yourself. -hypospray-component-empty-message = It's empty! +hypospray-component-empty-message = Nothing to inject. hypospray-component-feel-prick-message = You feel a tiny prick! hypospray-component-transfer-already-full-message = {$owner} is already full! hypospray-cant-inject = Can't inject into {$target}! + +hypospray-verb-mode-label = Toggle Container Draw +hypospray-verb-mode-inject-all = You cannot draw from containers anymore. +hypospray-verb-mode-inject-mobs-only = You can now draw from containers. diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml index 3d28487d68..abcabd7481 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml @@ -18,7 +18,7 @@ - type: ExaminableSolution solution: hypospray - type: Hypospray - onlyMobs: false + onlyAffectsMobs: false - type: UseDelay delay: 0.5 - type: StaticPrice @@ -49,7 +49,7 @@ - type: ExaminableSolution solution: hypospray - type: Hypospray - onlyMobs: false + onlyAffectsMobs: false - type: UseDelay delay: 0.5 @@ -73,6 +73,7 @@ - type: ExaminableSolution solution: hypospray - type: Hypospray + onlyAffectsMobs: false - type: UseDelay delay: 0.5 @@ -113,6 +114,7 @@ - type: Hypospray solutionName: pen transferAmount: 15 + onlyAffectsMobs: false - type: Appearance - type: SolutionContainerVisuals maxFillLevels: 1 @@ -202,6 +204,7 @@ - type: Hypospray solutionName: pen transferAmount: 20 + onlyAffectsMobs: false - type: SolutionContainerManager solutions: pen: @@ -232,6 +235,7 @@ - type: Hypospray solutionName: pen transferAmount: 20 + onlyAffectsMobs: false - type: SolutionContainerManager solutions: pen: @@ -262,6 +266,7 @@ - type: Hypospray solutionName: pen transferAmount: 20 + onlyAffectsMobs: false - type: SolutionContainerManager solutions: pen: @@ -293,6 +298,7 @@ - type: Hypospray solutionName: pen transferAmount: 30 + onlyAffectsMobs: false - type: SolutionContainerManager solutions: pen: @@ -330,6 +336,7 @@ - type: Hypospray solutionName: pen transferAmount: 30 + onlyAffectsMobs: false - type: StaticPrice price: 500 - type: Tag @@ -389,6 +396,7 @@ - type: Hypospray solutionName: pen transferAmount: 30 + onlyAffectsMobs: false - type: StaticPrice price: 500 - type: Tag @@ -410,7 +418,7 @@ - type: ExaminableSolution solution: hypospray - type: Hypospray - onlyMobs: false + onlyAffectsMobs: false - type: UseDelay delay: 0.5 - type: StaticPrice # A new shitcurity meta From 431c3ad3b893b01b80e18ce0c00daffd2f77b046 Mon Sep 17 00:00:00 2001 From: EdenTheLiznerd <138748328+EdenTheLiznerd@users.noreply.github.com> Date: Fri, 29 Mar 2024 22:00:21 -0600 Subject: [PATCH 11/14] Rebalance amatoxin so it is a slower killer (#25830) * Balancing? Balancing!!! * Additional changes --- Resources/Prototypes/Reagents/toxins.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Reagents/toxins.yml b/Resources/Prototypes/Reagents/toxins.yml index 02e0b99519..6e1ad75a35 100644 --- a/Resources/Prototypes/Reagents/toxins.yml +++ b/Resources/Prototypes/Reagents/toxins.yml @@ -425,11 +425,12 @@ color: "#D6CE7B" metabolisms: Poison: + metabolismRate: 0.2 effects: - !type:HealthChange damage: types: - Poison: 6 + Poison: 3 - type: reagent id: VentCrud From 299625772d86f28d847cde3104b6bf809033a2e5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 04:00:22 +0000 Subject: [PATCH 12/14] 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 b4ec1e0b33..2ca08113a3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: tday - changes: - - message: Added admin log messages for adding and ending game rules, and for the - commands to do so. - type: Add - - message: Added admin log messages for secret mode rule selection. - type: Add - id: 5757 - time: '2024-01-20T18:02:13.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24092 - author: Nimfar11 changes: - message: Adds snake kebab and its recipe. @@ -3794,3 +3784,11 @@ id: 6256 time: '2024-03-30T03:35:43.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26504 +- author: Plykiya + changes: + - message: Hyposprays can now be toggled to draw from solution containers like jugs + and beakers. + type: Tweak + id: 6257 + time: '2024-03-30T03:59:17.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25544 From aa96baeb5fd68f7877c60ed20fdfdd3edf0ade02 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 04:01:31 +0000 Subject: [PATCH 13/14] 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 2ca08113a3..8c70fdc638 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Nimfar11 - changes: - - message: Adds snake kebab and its recipe. - type: Add - id: 5758 - time: '2024-01-20T23:38:11.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24341 - author: Alekshhh changes: - message: Cerberus now has a wideswing that works similarly to spears. @@ -3792,3 +3785,11 @@ id: 6257 time: '2024-03-30T03:59:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25544 +- author: EdenTheLiznerd + changes: + - message: Amanita toxin now kills you slightly slower, providing you time to seek + charcoal before it's too late + type: Tweak + id: 6258 + time: '2024-03-30T04:00:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25830 From 888a3bda515a214733bf748c5d6a7e7c923a301e Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:17:53 +1300 Subject: [PATCH 14/14] Atmos device performance improvements (#26493) * Atmos device performance improvements * AtmosDirection perf improvements * Fix errors * Add GasTileOverlayComponent arguments * Make excite no longer invalidate a tile --- .../Atmos/EntitySystems/AirFilterSystem.cs | 10 +-- .../Atmos/EntitySystems/AtmosExposedSystem.cs | 12 +-- .../EntitySystems/AtmosphereSystem.API.cs | 86 +++++++++++-------- .../AtmosphereSystem.ExcitedGroup.cs | 12 ++- .../AtmosphereSystem.GridAtmosphere.cs | 11 +-- .../EntitySystems/AtmosphereSystem.Hotspot.cs | 14 +-- .../EntitySystems/AtmosphereSystem.LINDA.cs | 18 ++-- .../AtmosphereSystem.Monstermos.cs | 40 +++++---- .../AtmosphereSystem.Processing.cs | 46 ++++++---- .../AtmosphereSystem.Superconductivity.cs | 2 +- .../EntitySystems/AtmosphereSystem.Utils.cs | 12 ++- .../Atmos/EntitySystems/AtmosphereSystem.cs | 2 +- .../Atmos/EntitySystems/GasTankSystem.cs | 2 +- .../EntitySystems/GasTileOverlaySystem.cs | 8 +- .../EntitySystems/HeatExchangerSystem.cs | 20 ++--- .../Monitor/Systems/AtmosMonitoringSystem.cs | 6 +- .../EntitySystems/GasPassiveGateSystem.cs | 6 +- .../EntitySystems/GasPressurePumpSystem.cs | 4 +- .../Binary/EntitySystems/GasRecyclerSystem.cs | 10 +-- .../Binary/EntitySystems/GasValveSystem.cs | 5 +- .../EntitySystems/GasVolumePumpSystem.cs | 7 +- .../Piping/Components/AtmosDeviceComponent.cs | 20 +++-- .../Piping/EntitySystems/AtmosDeviceSystem.cs | 3 +- .../Other/EntitySystems/GasMinerSystem.cs | 4 +- .../Trinary/EntitySystems/GasFilterSystem.cs | 15 ++-- .../Trinary/EntitySystems/GasMixerSystem.cs | 14 +-- .../PressureControlledValveSystem.cs | 6 +- .../Unary/EntitySystems/GasCanisterSystem.cs | 4 +- .../Unary/EntitySystems/GasCondenserSystem.cs | 5 +- .../EntitySystems/GasOutletInjectorSystem.cs | 10 +-- .../EntitySystems/GasPassiveVentSystem.cs | 7 +- .../Unary/EntitySystems/GasPortableSystem.cs | 5 +- .../EntitySystems/GasThermoMachineSystem.cs | 5 +- .../Unary/EntitySystems/GasVentPumpSystem.cs | 12 +-- .../EntitySystems/GasVentScrubberSystem.cs | 19 ++-- .../Atmos/Portable/PortableScrubberSystem.cs | 32 +++---- .../Atmos/Portable/SpaceHeaterSystem.cs | 2 +- .../Electrocution/ElectrocutionNode.cs | 9 +- .../EntitySystems/ExplosionGridTileFlood.cs | 4 +- .../EntitySystems/ExplosionSpaceTileFlood.cs | 2 +- Content.Server/Mech/Systems/MechSystem.cs | 11 ++- Content.Server/Medical/CryoPodSystem.cs | 15 +--- .../EntitySystems/NodeContainerSystem.cs | 74 ++++++++++++++++ .../PneumaticCannon/PneumaticCannonSystem.cs | 2 +- .../Power/Components/CableVisComponent.cs | 4 +- .../Power/EntitySystems/CableVisSystem.cs | 7 +- .../Power/Generator/GasPowerReceiverSystem.cs | 8 +- Content.Server/Spreader/SpreaderSystem.cs | 7 +- Content.Shared/Atmos/AtmosDirection.cs | 24 +++++- .../Tools/Systems/WeldableSystem.cs | 32 +++---- .../Entities/Virtual/electrocution.yml | 2 +- 51 files changed, 373 insertions(+), 324 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs index d947e60b6d..2ab15cfb17 100644 --- a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs @@ -13,6 +13,7 @@ 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() { @@ -31,7 +32,7 @@ public sealed class AirFilterSystem : EntitySystem if (air.Pressure >= intake.Pressure) return; - var environment = _atmosphere.GetContainingMixture(uid, true, true); + var environment = _atmosphere.GetContainingMixture(uid, args.Grid, args.Map, true, true); // nothing to intake from if (environment == null) return; @@ -63,12 +64,11 @@ public sealed class AirFilterSystem : EntitySystem var oxygen = air.GetMoles(filter.Oxygen) / air.TotalMoles; var gases = oxygen >= filter.TargetOxygen ? filter.Gases : filter.OverflowGases; - var coordinates = Transform(uid).MapPosition; GasMixture? destination = null; - if (_map.TryFindGridAt(coordinates, out _, out var grid)) + if (args.Grid is {} grid) { - var tile = grid.GetTileRef(coordinates); - destination = _atmosphere.GetTileMixture(tile.GridUid, null, tile.GridIndices, true); + var position = _transform.GetGridTilePositionOrDefault(uid); + destination = _atmosphere.GetTileMixture(grid, args.Map, position, true); } if (destination != null) diff --git a/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs b/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs index 4be4d8271f..9590b9aa54 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs @@ -24,6 +24,8 @@ namespace Content.Server.Atmos.EntitySystems /// /// Event that tries to query the mixture a certain entity is exposed to. + /// This is mainly intended for use with entities inside of containers. + /// This event is not raised for entities that are directly parented to the grid. /// [ByRefEvent] public struct AtmosExposedGetAirEvent @@ -31,7 +33,7 @@ namespace Content.Server.Atmos.EntitySystems /// /// The entity we want to query this for. /// - public readonly EntityUid Entity; + public readonly Entity Entity; /// /// The mixture that the entity is exposed to. Output parameter. @@ -39,9 +41,9 @@ namespace Content.Server.Atmos.EntitySystems public GasMixture? Gas = null; /// - /// Whether to invalidate the mixture, if possible. + /// Whether to excite the mixture, if possible. /// - public bool Invalidate = false; + public readonly bool Excite = false; /// /// Whether this event has been handled or not. @@ -49,10 +51,10 @@ namespace Content.Server.Atmos.EntitySystems /// public bool Handled = false; - public AtmosExposedGetAirEvent(EntityUid entity, bool invalidate = false) + public AtmosExposedGetAirEvent(Entity entity, bool excite = false) { Entity = entity; - Invalidate = invalidate; + Excite = excite; } } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs index fb94fe414b..614d550c2f 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs @@ -4,6 +4,7 @@ using Content.Server.Atmos.Piping.Components; using Content.Server.Atmos.Reactions; using Content.Server.NodeContainer.NodeGroups; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; using Robust.Shared.Map.Components; using Robust.Shared.Utility; @@ -11,41 +12,39 @@ namespace Content.Server.Atmos.EntitySystems; public partial class AtmosphereSystem { - public GasMixture? GetContainingMixture(EntityUid uid, bool ignoreExposed = false, bool excite = false, TransformComponent? transform = null) + public GasMixture? GetContainingMixture(Entity ent, bool ignoreExposed = false, bool excite = false) { - if (!ignoreExposed) + if (!Resolve(ent, ref ent.Comp)) + return null; + + return GetContainingMixture(ent, ent.Comp.GridUid, ent.Comp.MapUid, ignoreExposed, excite); + } + + public GasMixture? GetContainingMixture( + Entity ent, + Entity? grid, + Entity? map, + bool ignoreExposed = false, + bool excite = false) + { + if (!Resolve(ent, ref ent.Comp)) + return null; + + if (!ignoreExposed && !ent.Comp.Anchored) { // Used for things like disposals/cryo to change which air people are exposed to. - var ev = new AtmosExposedGetAirEvent(uid, excite); - - // Give the entity itself a chance to handle this. - RaiseLocalEvent(uid, ref ev, false); - + var ev = new AtmosExposedGetAirEvent((ent, ent.Comp), excite); + RaiseLocalEvent(ent, ref ev); if (ev.Handled) return ev.Gas; - // We need to get the parent now, so we need the transform... If the parent is invalid, we can't do much else. - if(!Resolve(uid, ref transform) || !transform.ParentUid.IsValid() || transform.MapUid == null) - return GetTileMixture(null, null, Vector2i.Zero, excite); - - // Give the parent entity a chance to handle the event... - RaiseLocalEvent(transform.ParentUid, ref ev, false); - - if (ev.Handled) - return ev.Gas; - } - // Oops, we did a little bit of code duplication... - else if(!Resolve(uid, ref transform)) - { - return GetTileMixture(null, null, Vector2i.Zero, excite); + // TODO ATMOS: recursively iterate up through parents + // This really needs recursive InContainer metadata flag for performance + // And ideally some fast way to get the innermost airtight container. } - - var gridUid = transform.GridUid; - var mapUid = transform.MapUid; - var position = _transformSystem.GetGridOrMapTilePosition(uid, transform); - - return GetTileMixture(gridUid, mapUid, position, excite); + var position = _transformSystem.GetGridTilePositionOrDefault((ent, ent.Comp)); + return GetTileMixture(grid, map, position, excite); } public bool HasAtmosphere(EntityUid gridUid) => _atmosQuery.HasComponent(gridUid); @@ -84,21 +83,28 @@ public partial class AtmosphereSystem entity.Comp.InvalidatedCoords.Add(tile); } - public GasMixture?[]? GetTileMixtures(Entity? grid, Entity? map, List tiles, bool excite = false) + public GasMixture?[]? GetTileMixtures( + Entity? grid, + Entity? map, + List tiles, + bool excite = false) { GasMixture?[]? mixtures = null; var handled = false; // If we've been passed a grid, try to let it handle it. - if (grid is {} gridEnt && Resolve(gridEnt, ref gridEnt.Comp)) + if (grid is {} gridEnt && Resolve(gridEnt, ref gridEnt.Comp1)) { + if (excite) + Resolve(gridEnt, ref gridEnt.Comp2); + 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)) + if (!gridEnt.Comp1.Tiles.TryGetValue(tile, out var atmosTile)) { // need to get map atmosphere handled = false; @@ -108,7 +114,10 @@ public partial class AtmosphereSystem mixtures[i] = atmosTile.Air; if (excite) - gridEnt.Comp.InvalidatedCoords.Add(tile); + { + AddActiveTile(gridEnt.Comp1, atmosTile); + InvalidateVisuals((gridEnt.Owner, gridEnt.Comp2), tile); + } } } @@ -146,15 +155,22 @@ public partial class AtmosphereSystem return GetTileMixture(entity.Comp.GridUid, entity.Comp.MapUid, indices, excite); } - public GasMixture? GetTileMixture(Entity? grid, Entity? map, Vector2i gridTile, bool excite = false) + public GasMixture? GetTileMixture( + Entity? grid, + Entity? map, + Vector2i gridTile, + bool excite = false) { // If we've been passed a grid, try to let it handle it. if (grid is {} gridEnt - && Resolve(gridEnt, ref gridEnt.Comp, false) - && gridEnt.Comp.Tiles.TryGetValue(gridTile, out var tile)) + && Resolve(gridEnt, ref gridEnt.Comp1, false) + && gridEnt.Comp1.Tiles.TryGetValue(gridTile, out var tile)) { if (excite) - gridEnt.Comp.InvalidatedCoords.Add(gridTile); + { + AddActiveTile(gridEnt.Comp1, tile); + InvalidateVisuals((grid.Value.Owner, grid.Value.Comp2), gridTile); + } return tile.Air; } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs index de4c9199cf..c1b58f7a77 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs @@ -1,5 +1,7 @@ using Content.Server.Atmos.Components; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; +using Robust.Shared.Map.Components; using Robust.Shared.Utility; namespace Content.Server.Atmos.EntitySystems @@ -64,10 +66,12 @@ namespace Content.Server.Atmos.EntitySystems excitedGroup.DismantleCooldown = 0; } - private void ExcitedGroupSelfBreakdown(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup) + private void ExcitedGroupSelfBreakdown( + Entity ent, + ExcitedGroup excitedGroup) { DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!"); - DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!"); + DebugTools.Assert(ent.Comp1.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!"); var combined = new GasMixture(Atmospherics.CellVolume); var tileSize = excitedGroup.Tiles.Count; @@ -77,7 +81,7 @@ namespace Content.Server.Atmos.EntitySystems if (tileSize == 0) { - ExcitedGroupDispose(gridAtmosphere, excitedGroup); + ExcitedGroupDispose(ent.Comp1, excitedGroup); return; } @@ -103,7 +107,7 @@ namespace Content.Server.Atmos.EntitySystems continue; tile.Air.CopyFromMutable(combined); - InvalidateVisuals(tile.GridIndex, tile.GridIndices); + InvalidateVisuals(ent, tile); } excitedGroup.BreakdownCooldown = 0; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs index bd45030896..4b9ef49a40 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs @@ -1,4 +1,3 @@ -using System.Linq; using Content.Server.Atmos.Components; using Content.Server.Atmos.Reactions; using Content.Shared.Atmos; @@ -160,7 +159,7 @@ public sealed partial class AtmosphereSystem } /// - /// Update array of adjacent tiles and the adjacency flags. Optionally activates all tiles with modified adjacencies. + /// Update array of adjacent tiles and the adjacency flags. /// private void UpdateAdjacentTiles( Entity ent, @@ -195,14 +194,16 @@ public sealed partial class AtmosphereSystem if (activate) AddActiveTile(atmos, adjacent); - var oppositeDirection = direction.GetOpposite(); + var oppositeIndex = i.ToOppositeIndex(); + var oppositeDirection = (AtmosDirection) (1 << oppositeIndex); + if (adjBlockDirs.IsFlagSet(oppositeDirection) || blockedDirs.IsFlagSet(direction)) { // Adjacency is blocked by some airtight entity. tile.AdjacentBits &= ~direction; adjacent.AdjacentBits &= ~oppositeDirection; tile.AdjacentTiles[i] = null; - adjacent.AdjacentTiles[oppositeDirection.ToIndex()] = null; + adjacent.AdjacentTiles[oppositeIndex] = null; } else { @@ -210,7 +211,7 @@ public sealed partial class AtmosphereSystem tile.AdjacentBits |= direction; adjacent.AdjacentBits |= oppositeDirection; tile.AdjacentTiles[i] = adjacent; - adjacent.AdjacentTiles[oppositeDirection.ToIndex()] = tile; + adjacent.AdjacentTiles[oppositeIndex] = tile; } DebugTools.Assert(!(tile.AdjacentBits.IsFlagSet(direction) ^ diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs index 713d1c4682..7163b4cd44 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs @@ -1,10 +1,12 @@ using Content.Server.Atmos.Components; using Content.Server.Atmos.Reactions; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; using Content.Shared.Audio; using Content.Shared.Database; using Robust.Shared.Audio; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Player; namespace Content.Server.Atmos.EntitySystems @@ -18,18 +20,18 @@ namespace Content.Server.Atmos.EntitySystems [ViewVariables(VVAccess.ReadWrite)] public string? HotspotSound { get; private set; } = "/Audio/Effects/fire.ogg"; - private void ProcessHotspot(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) + private void ProcessHotspot( + Entity ent, + TileAtmosphere tile) { + var gridAtmosphere = ent.Comp1; if (!tile.Hotspot.Valid) { gridAtmosphere.HotspotTiles.Remove(tile); return; } - if (!tile.Excited) - { - AddActiveTile(gridAtmosphere, tile); - } + AddActiveTile(gridAtmosphere, tile); if (!tile.Hotspot.SkippedFirstProcess) { @@ -44,7 +46,7 @@ namespace Content.Server.Atmos.EntitySystems || tile.Air == null || tile.Air.GetMoles(Gas.Oxygen) < 0.5f || (tile.Air.GetMoles(Gas.Plasma) < 0.5f && tile.Air.GetMoles(Gas.Tritium) < 0.5f)) { tile.Hotspot = new Hotspot(); - InvalidateVisuals(tile.GridIndex, tile.GridIndices); + InvalidateVisuals(ent, tile); return; } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs index c27e18b55b..fb2375899d 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs @@ -1,14 +1,18 @@ using Content.Server.Atmos.Components; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; +using Robust.Shared.Map.Components; using Robust.Shared.Utility; namespace Content.Server.Atmos.EntitySystems { public sealed partial class AtmosphereSystem { - private void ProcessCell(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int fireCount, GasTileOverlayComponent visuals) + private void ProcessCell( + Entity ent, + TileAtmosphere tile, int fireCount) { + var gridAtmosphere = ent.Comp1; // Can't process a tile without air if (tile.Air == null) { @@ -52,11 +56,7 @@ namespace Content.Server.Atmos.EntitySystems shouldShareAir = true; } else if (CompareExchange(tile.Air, enemyTile.Air) != GasCompareResult.NoExchange) { - if (!enemyTile.Excited) - { - AddActiveTile(gridAtmosphere, enemyTile); - } - + AddActiveTile(gridAtmosphere, enemyTile); if (ExcitedGroups) { var excitedGroup = tile.ExcitedGroup; @@ -91,7 +91,7 @@ namespace Content.Server.Atmos.EntitySystems } else { - ConsiderPressureDifference(gridAtmosphere, enemyTile, direction.GetOpposite(), -difference); + ConsiderPressureDifference(gridAtmosphere, enemyTile, i.ToOppositeDir(), -difference); } } @@ -102,7 +102,7 @@ namespace Content.Server.Atmos.EntitySystems if(tile.Air != null) React(tile.Air, tile); - InvalidateVisuals(tile.GridIndex, tile.GridIndices, visuals); + InvalidateVisuals(ent, tile); var remove = true; @@ -146,7 +146,7 @@ namespace Content.Server.Atmos.EntitySystems /// Tile Atmosphere to be activated. private void AddActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { - if (tile.Air == null) + if (tile.Air == null || tile.Excited) return; tile.Excited = true; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs index dcbc1e86ee..08193027d6 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs @@ -230,7 +230,7 @@ namespace Content.Server.Atmos.EntitySystems if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; _equalizeQueue[queueLength++] = otherTile2; otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; - otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); + otherTile2.MonstermosInfo.CurrentTransferDirection = k.ToOppositeDir(); otherTile2.MonstermosInfo.CurrentTransferAmount = 0; if (otherTile2.MonstermosInfo.MoleDelta < 0) { @@ -296,7 +296,7 @@ namespace Content.Server.Atmos.EntitySystems if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; _equalizeQueue[queueLength++] = otherTile2; otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; - otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); + otherTile2.MonstermosInfo.CurrentTransferDirection = k.ToOppositeDir(); otherTile2.MonstermosInfo.CurrentTransferAmount = 0; if (otherTile2.MonstermosInfo.MoleDelta > 0) @@ -338,7 +338,7 @@ namespace Content.Server.Atmos.EntitySystems for (var i = 0; i < tileCount; i++) { var otherTile = _equalizeTiles[i]!; - FinalizeEq(gridAtmosphere, otherTile, ent); + FinalizeEq(ent, otherTile); } for (var i = 0; i < tileCount; i++) @@ -473,7 +473,7 @@ namespace Content.Server.Atmos.EntitySystems if(tile2.Space) continue; - tile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); + tile2.MonstermosInfo.CurrentTransferDirection = j.ToOppositeDir(); tile2.MonstermosInfo.CurrentTransferAmount = 0.0f; tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget; tile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; @@ -549,7 +549,7 @@ namespace Content.Server.Atmos.EntitySystems otherTile.Air.Temperature = Atmospherics.TCMB; } - InvalidateVisuals(otherTile.GridIndex, otherTile.GridIndices, visuals); + InvalidateVisuals(ent, otherTile); HandleDecompressionFloorRip(mapGrid, otherTile, otherTile.MonstermosInfo.CurrentTransferAmount); } @@ -598,11 +598,13 @@ namespace Content.Server.Atmos.EntitySystems UpdateAdjacentTiles(ent, tile); UpdateAdjacentTiles(ent, other); - InvalidateVisuals(tile.GridIndex, tile.GridIndices, ent); - InvalidateVisuals(other.GridIndex, other.GridIndices, ent); + InvalidateVisuals(ent, tile); + InvalidateVisuals(ent, other); } - private void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, GasTileOverlayComponent? visuals) + private void FinalizeEq( + Entity ent, + TileAtmosphere tile) { Span transferDirections = stackalloc float[Atmospherics.Directions]; var hasTransferDirs = false; @@ -629,17 +631,19 @@ namespace Content.Server.Atmos.EntitySystems // Everything that calls this method already ensures that Air will not be null. if (tile.Air!.TotalMoles < amount) - FinalizeEqNeighbors(gridAtmosphere, tile, transferDirections, visuals); + FinalizeEqNeighbors(ent, tile, transferDirections); - otherTile.MonstermosInfo[direction.GetOpposite()] = 0; + otherTile.MonstermosInfo[i.ToOppositeDir()] = 0; Merge(otherTile.Air, tile.Air.Remove(amount)); - InvalidateVisuals(tile.GridIndex, tile.GridIndices, visuals); - InvalidateVisuals(otherTile.GridIndex, otherTile.GridIndices, visuals); - ConsiderPressureDifference(gridAtmosphere, tile, direction, amount); + InvalidateVisuals(ent, tile); + InvalidateVisuals(ent, otherTile); + ConsiderPressureDifference(ent, tile, direction, amount); } } - private void FinalizeEqNeighbors(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, ReadOnlySpan transferDirs, GasTileOverlayComponent? visuals) + private void FinalizeEqNeighbors( + Entity ent, + TileAtmosphere tile, ReadOnlySpan transferDirs) { for (var i = 0; i < Atmospherics.Directions; i++) { @@ -647,7 +651,7 @@ namespace Content.Server.Atmos.EntitySystems var amount = transferDirs[i]; // Since AdjacentBits is set, AdjacentTiles[i] wouldn't be null, and neither would its air. if(amount < 0 && tile.AdjacentBits.IsFlagSet(direction)) - FinalizeEq(gridAtmosphere, tile.AdjacentTiles[i]!, visuals); // A bit of recursion if needed. + FinalizeEq(ent, tile.AdjacentTiles[i]!); // A bit of recursion if needed. } } @@ -664,7 +668,9 @@ namespace Content.Server.Atmos.EntitySystems Log.Error($"Encountered null-tile in {nameof(AdjustEqMovement)}. Trace: {Environment.StackTrace}"); return; } - var adj = tile.AdjacentTiles[direction.ToIndex()]; + + var idx = direction.ToIndex(); + var adj = tile.AdjacentTiles[idx]; if (adj == null) { var nonNull = tile.AdjacentTiles.Where(x => x != null).Count(); @@ -673,7 +679,7 @@ namespace Content.Server.Atmos.EntitySystems } tile.MonstermosInfo[direction] += amount; - adj.MonstermosInfo[direction.GetOpposite()] -= amount; + adj.MonstermosInfo[idx.ToOppositeDir()] -= amount; } private void HandleDecompressionFloorRip(MapGridComponent mapGrid, TileAtmosphere tile, float sum) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs index eba398c182..bd023e8574 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs @@ -86,7 +86,7 @@ namespace Content.Server.Atmos.EntitySystems DebugTools.Assert(atmosphere.Tiles.GetValueOrDefault(tile.GridIndices) == tile); UpdateAdjacentTiles(ent, tile, activate: true); UpdateTileAir(ent, tile, volume); - InvalidateVisuals(uid, tile.GridIndices, visuals); + InvalidateVisuals(ent, tile); if (number++ < InvalidCoordinatesLagCheckIterations) continue; @@ -313,15 +313,17 @@ namespace Content.Server.Atmos.EntitySystems return true; } - private bool ProcessActiveTiles(GridAtmosphereComponent atmosphere, GasTileOverlayComponent visuals) + private bool ProcessActiveTiles( + Entity ent) { + var atmosphere = ent.Comp1; if(!atmosphere.ProcessingPaused) QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.ActiveTiles); var number = 0; while (atmosphere.CurrentRunTiles.TryDequeue(out var tile)) { - ProcessCell(atmosphere, tile, atmosphere.UpdateCounter, visuals); + ProcessCell(ent, tile, atmosphere.UpdateCounter); if (number++ < LagCheckIterations) continue; @@ -337,8 +339,10 @@ namespace Content.Server.Atmos.EntitySystems return true; } - private bool ProcessExcitedGroups(GridAtmosphereComponent gridAtmosphere) + private bool ProcessExcitedGroups( + Entity ent) { + var gridAtmosphere = ent.Comp1; if (!gridAtmosphere.ProcessingPaused) { gridAtmosphere.CurrentRunExcitedGroups.Clear(); @@ -356,7 +360,7 @@ namespace Content.Server.Atmos.EntitySystems excitedGroup.DismantleCooldown++; if (excitedGroup.BreakdownCooldown > Atmospherics.ExcitedGroupBreakdownCycles) - ExcitedGroupSelfBreakdown(gridAtmosphere, excitedGroup); + ExcitedGroupSelfBreakdown(ent, excitedGroup); else if (excitedGroup.DismantleCooldown > Atmospherics.ExcitedGroupsDismantleCycles) DeactivateGroupTiles(gridAtmosphere, excitedGroup); // TODO ATMOS. What is the point of this? why is this only de-exciting the group? Shouldn't it also dismantle it? @@ -411,15 +415,17 @@ namespace Content.Server.Atmos.EntitySystems return true; } - private bool ProcessHotspots(GridAtmosphereComponent atmosphere) + private bool ProcessHotspots( + Entity ent) { + var atmosphere = ent.Comp1; if(!atmosphere.ProcessingPaused) QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.HotspotTiles); var number = 0; while (atmosphere.CurrentRunTiles.TryDequeue(out var hotspot)) { - ProcessHotspot(atmosphere, hotspot); + ProcessHotspot(ent, hotspot); if (number++ < LagCheckIterations) continue; @@ -507,8 +513,11 @@ namespace Content.Server.Atmos.EntitySystems return num * AtmosTime; } - private bool ProcessAtmosDevices(GridAtmosphereComponent atmosphere) + private bool ProcessAtmosDevices( + Entity ent, + Entity map) { + var atmosphere = ent.Comp1; if (!atmosphere.ProcessingPaused) { atmosphere.CurrentRunAtmosDevices.Clear(); @@ -521,7 +530,7 @@ namespace Content.Server.Atmos.EntitySystems var time = _gameTiming.CurTime; var number = 0; - var ev = new AtmosDeviceUpdateEvent(RealAtmosTime()); + var ev = new AtmosDeviceUpdateEvent(RealAtmosTime(), (ent, ent.Comp1, ent.Comp2), map); while (atmosphere.CurrentRunAtmosDevices.TryDequeue(out var device)) { RaiseLocalEvent(device, ref ev); @@ -565,12 +574,11 @@ namespace Content.Server.Atmos.EntitySystems var ent = _currentRunAtmosphere[_currentRunAtmosphereIndex]; var (owner, atmosphere, visuals, grid, xform) = ent; - if (!TryComp(owner, out TransformComponent? x) - || x.MapUid == null - || TerminatingOrDeleted(x.MapUid.Value) - || x.MapID == MapId.Nullspace) + if (xform.MapUid == null + || TerminatingOrDeleted(xform.MapUid.Value) + || xform.MapID == MapId.Nullspace) { - Log.Error($"Attempted to process atmos without a map? Entity: {ToPrettyString(owner)}. Map: {ToPrettyString(x?.MapUid)}. MapId: {x?.MapID}"); + Log.Error($"Attempted to process atmos without a map? Entity: {ToPrettyString(owner)}. Map: {ToPrettyString(xform?.MapUid)}. MapId: {xform?.MapID}"); continue; } @@ -585,6 +593,8 @@ namespace Content.Server.Atmos.EntitySystems // We subtract it so it takes lost time into account. atmosphere.Timer -= AtmosTime; + var map = new Entity(xform.MapUid.Value, _mapAtmosQuery.CompOrNull(xform.MapUid.Value)); + switch (atmosphere.State) { case AtmosphereProcessingState.Revalidate: @@ -614,7 +624,7 @@ namespace Content.Server.Atmos.EntitySystems atmosphere.State = AtmosphereProcessingState.ActiveTiles; continue; case AtmosphereProcessingState.ActiveTiles: - if (!ProcessActiveTiles(ent, ent)) + if (!ProcessActiveTiles(ent)) { atmosphere.ProcessingPaused = true; return; @@ -625,7 +635,7 @@ namespace Content.Server.Atmos.EntitySystems atmosphere.State = ExcitedGroups ? AtmosphereProcessingState.ExcitedGroups : AtmosphereProcessingState.HighPressureDelta; continue; case AtmosphereProcessingState.ExcitedGroups: - if (!ProcessExcitedGroups(atmosphere)) + if (!ProcessExcitedGroups(ent)) { atmosphere.ProcessingPaused = true; return; @@ -645,7 +655,7 @@ namespace Content.Server.Atmos.EntitySystems atmosphere.State = AtmosphereProcessingState.Hotspots; continue; case AtmosphereProcessingState.Hotspots: - if (!ProcessHotspots(atmosphere)) + if (!ProcessHotspots(ent)) { atmosphere.ProcessingPaused = true; return; @@ -680,7 +690,7 @@ namespace Content.Server.Atmos.EntitySystems atmosphere.State = AtmosphereProcessingState.AtmosDevices; continue; case AtmosphereProcessingState.AtmosDevices: - if (!ProcessAtmosDevices(atmosphere)) + if (!ProcessAtmosDevices(ent, map)) { atmosphere.ProcessingPaused = true; return; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs index 5c73cf1124..8ed92a9d0e 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs @@ -16,7 +16,7 @@ namespace Content.Server.Atmos.EntitySystems if (!directions.IsFlagSet(direction)) continue; - var adjacent = tile.AdjacentTiles[direction.ToIndex()]; + var adjacent = tile.AdjacentTiles[i]; // TODO ATMOS handle adjacent being null. if (adjacent == null || adjacent.ThermalConductivity == 0f) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs index 67c6d5998d..cf4c73aa2f 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs @@ -36,9 +36,17 @@ public partial class AtmosphereSystem } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void InvalidateVisuals(EntityUid gridUid, Vector2i tile, GasTileOverlayComponent? comp = null) + public void InvalidateVisuals(Entity grid, Vector2i tile) { - _gasTileOverlaySystem.Invalidate(gridUid, tile, comp); + _gasTileOverlaySystem.Invalidate(grid, tile); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void InvalidateVisuals( + Entity ent, + TileAtmosphere tile) + { + _gasTileOverlaySystem.Invalidate((ent.Owner, ent.Comp2), tile.GridIndices); } /// diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs index a553766582..44bfa4cc10 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs @@ -96,7 +96,7 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out _, out var transform)) { - var air = GetContainingMixture(uid, transform:transform); + var air = GetContainingMixture((uid, transform)); if (air == null) continue; diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index aed00432e1..80842416e8 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -168,7 +168,7 @@ namespace Content.Server.Atmos.EntitySystems private void ReleaseGas(Entity gasTank) { var removed = RemoveAirVolume(gasTank, gasTank.Comp.ValveOutputRate * TimerDelay); - var environment = _atmosphereSystem.GetContainingMixture(gasTank, false, true); + var environment = _atmosphereSystem.GetContainingMixture(gasTank.Owner, false, true); if (environment != null) { _atmosphereSystem.Merge(environment, removed); diff --git a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs index 003eed59e0..c42cfd08da 100644 --- a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -60,6 +60,7 @@ namespace Content.Server.Atmos.EntitySystems private float _updateInterval; private int _thresholds; + private EntityQuery _query; public override void Initialize() { @@ -84,6 +85,7 @@ namespace Content.Server.Atmos.EntitySystems SubscribeLocalEvent(Reset); SubscribeLocalEvent(OnStartup); + _query = GetEntityQuery(); } private void OnStartup(EntityUid uid, GasTileOverlayComponent component, ComponentStartup args) @@ -132,10 +134,10 @@ namespace Content.Server.Atmos.EntitySystems private void UpdateThresholds(int value) => _thresholds = value; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Invalidate(EntityUid grid, Vector2i index, GasTileOverlayComponent? comp = null) + public void Invalidate(Entity grid, Vector2i index) { - if (Resolve(grid, ref comp)) - comp.InvalidTiles.Add(index); + if (_query.Resolve(grid.Owner, ref grid.Comp)) + grid.Comp.InvalidTiles.Add(index); } private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) diff --git a/Content.Server/Atmos/EntitySystems/HeatExchangerSystem.cs b/Content.Server/Atmos/EntitySystems/HeatExchangerSystem.cs index 286b154e96..b3644e88b7 100644 --- a/Content.Server/Atmos/EntitySystems/HeatExchangerSystem.cs +++ b/Content.Server/Atmos/EntitySystems/HeatExchangerSystem.cs @@ -40,24 +40,16 @@ public sealed class HeatExchangerSystem : EntitySystem private void OnAtmosUpdate(EntityUid uid, HeatExchangerComponent comp, ref AtmosDeviceUpdateEvent args) { - if (!TryComp(uid, out NodeContainerComponent? nodeContainer) - || !TryComp(uid, out AtmosDeviceComponent? device) - || !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? outlet)) + // make sure that the tile the device is on isn't blocked by a wall or something similar. + if (args.Grid is {} grid + && _transform.TryGetGridTilePosition(uid, out var tile) + && _atmosphereSystem.IsTileAirBlocked(grid, tile)) { return; } - // make sure that the tile the device is on isn't blocked by a wall or something similar. - var xform = Transform(uid); - if (_transform.TryGetGridTilePosition(uid, out var tile)) - { - // TryGetGridTilePosition() already returns false if GridUid is null, but the null checker isn't smart enough yet - if (xform.GridUid != null && _atmosphereSystem.IsTileAirBlocked(xform.GridUid.Value, tile)) - { - return; - } - } + if (!_nodeContainer.TryGetNodes(uid, comp.InletName, comp.OutletName, out PipeNode? inlet, out PipeNode? outlet)) + return; var dt = args.dt; diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs index 28a0a01c99..c1a5256fdd 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs @@ -204,11 +204,7 @@ public sealed class AtmosMonitorSystem : EntitySystem if (!this.IsPowered(uid, EntityManager)) return; - // can't hurt - // (in case something is making AtmosDeviceUpdateEvents - // outside the typical device loop) - if (!TryComp(uid, out var atmosDeviceComponent) - || atmosDeviceComponent.JoinedGrid == null) + if (args.Grid == null) return; // if we're not monitoring atmos, don't bother diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs index 77bab4775c..fced4d7988 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs @@ -26,11 +26,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, ref AtmosDeviceUpdateEvent args) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, gate.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, gate.OutletName, out PipeNode? outlet)) + if (!_nodeContainer.TryGetNodes(uid, gate.InletName, gate.OutletName, out PipeNode? inlet, out PipeNode? outlet)) return; var n1 = inlet.Air.TotalMoles; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs index 49b69fc673..af25d04df9 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs @@ -66,9 +66,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems private void OnPumpUpdated(EntityUid uid, GasPressurePumpComponent pump, ref AtmosDeviceUpdateEvent args) { if (!pump.Enabled - || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, pump.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, pump.OutletName, out PipeNode? outlet)) + || !_nodeContainer.TryGetNodes(uid, pump.InletName, pump.OutletName, out PipeNode? inlet, out PipeNode? outlet)) { _ambientSoundSystem.SetAmbience(uid, false); return; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs index e14069b8a7..3ebc509492 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs @@ -41,12 +41,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems if (!EntityManager.GetComponent(ent).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. return; - if (!EntityManager.TryGetComponent(ent, out NodeContainerComponent? nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? _)) - { + if (!_nodeContainer.TryGetNode(ent.Owner, comp.InletName, out PipeNode? inlet)) return; - } using (args.PushGroup(nameof(GasRecyclerComponent))) { @@ -72,9 +68,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems private void OnUpdate(Entity ent, ref AtmosDeviceUpdateEvent args) { var comp = ent.Comp; - if (!EntityManager.TryGetComponent(ent, out NodeContainerComponent? nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? outlet)) + if (!_nodeContainer.TryGetNodes(ent.Owner, comp.InletName, comp.OutletName, out PipeNode? inlet, out PipeNode? outlet)) { _ambientSoundSystem.SetAmbience(ent, false); return; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs index 934ce8a7a4..ed7567428e 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs @@ -59,9 +59,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems public void Set(EntityUid uid, GasValveComponent component, bool value) { component.Open = value; - if (TryComp(uid, out NodeContainerComponent? nodeContainer) - && _nodeContainer.TryGetNode(nodeContainer, component.InletName, out PipeNode? inlet) - && _nodeContainer.TryGetNode(nodeContainer, component.OutletName, out PipeNode? outlet)) + + if (_nodeContainer.TryGetNodes(uid, component.InletName, component.OutletName, out PipeNode? inlet, out PipeNode? outlet)) { if (TryComp(uid, out var appearance)) { diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs index 8e478bd2b5..e4767c4061 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs @@ -71,11 +71,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems private void OnVolumePumpUpdated(EntityUid uid, GasVolumePumpComponent pump, ref AtmosDeviceUpdateEvent args) { - if (!pump.Enabled - || !TryComp(uid, out NodeContainerComponent? nodeContainer) - || !TryComp(uid, out AtmosDeviceComponent? device) - || !_nodeContainer.TryGetNode(nodeContainer, pump.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, pump.OutletName, out PipeNode? outlet)) + if (!pump.Enabled || + !_nodeContainer.TryGetNodes(uid, pump.InletName, pump.OutletName, out PipeNode? inlet, out PipeNode? outlet)) { _ambientSoundSystem.SetAmbience(uid, false); return; diff --git a/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs b/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs index 80461f1beb..b70262e857 100644 --- a/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs +++ b/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Atmos.Components; +using Content.Shared.Atmos.Components; namespace Content.Server.Atmos.Piping.Components; @@ -46,18 +47,25 @@ public sealed partial class AtmosDeviceComponent : Component /// Use this for atmos devices instead of . /// [ByRefEvent] -public readonly struct AtmosDeviceUpdateEvent +public readonly struct AtmosDeviceUpdateEvent(float dt, Entity? grid, Entity? map) { /// /// Time elapsed since last update, in seconds. Multiply values used in the update handler /// by this number to make them tickrate-invariant. Use this number instead of AtmosphereSystem.AtmosTime. /// - public readonly float dt; + public readonly float dt = dt; - public AtmosDeviceUpdateEvent(float dt) - { - this.dt = dt; - } + /// + /// The grid that this device is currently on. + /// + public readonly Entity? Grid = grid == null + ? null + : (grid.Value, grid.Value, grid.Value); + + /// + /// The map that the device & grid is on. + /// + public readonly Entity? Map = map; } /// diff --git a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs index c15d31f7d6..3c73a8f64e 100644 --- a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs +++ b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs @@ -129,9 +129,10 @@ namespace Content.Server.Atmos.Piping.EntitySystems _timer -= _atmosphereSystem.AtmosTime; var time = _gameTiming.CurTime; - var ev = new AtmosDeviceUpdateEvent(_atmosphereSystem.AtmosTime); + var ev = new AtmosDeviceUpdateEvent(_atmosphereSystem.AtmosTime, null, null); foreach (var device in _joinedDevices) { + DebugTools.Assert(!HasComp(Transform(device).GridUid)); RaiseLocalEvent(device, ref ev); device.Comp.LastProcess = time; } diff --git a/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs b/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs index 9853a17f82..aa206dbc68 100644 --- a/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs +++ b/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs @@ -38,9 +38,9 @@ namespace Content.Server.Atmos.Piping.Other.EntitySystems private bool CheckMinerOperation(Entity ent, [NotNullWhen(true)] out GasMixture? environment) { var (uid, miner) = ent; - environment = _atmosphereSystem.GetContainingMixture(uid, true, true); - var transform = Transform(uid); + environment = _atmosphereSystem.GetContainingMixture((uid, transform), true, true); + var position = _transformSystem.GetGridOrMapTilePosition(uid, transform); // Space. diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs index faf06a6079..f856946a92 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs @@ -53,11 +53,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems private void OnFilterUpdated(EntityUid uid, GasFilterComponent filter, ref AtmosDeviceUpdateEvent args) { if (!filter.Enabled - || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) - || !EntityManager.TryGetComponent(uid, out AtmosDeviceComponent? device) - || !_nodeContainer.TryGetNode(nodeContainer, filter.InletName, out PipeNode? inletNode) - || !_nodeContainer.TryGetNode(nodeContainer, filter.FilterName, out PipeNode? filterNode) - || !_nodeContainer.TryGetNode(nodeContainer, filter.OutletName, out PipeNode? outletNode) + || !_nodeContainer.TryGetNodes(uid, filter.InletName, filter.OutletName, filter.FilterName, out PipeNode? inletNode, out PipeNode? filterNode, out PipeNode? outletNode) || outletNode.Air.Pressure >= Atmospherics.MaxOutputPressure) // No need to transfer if target is full. { _ambientSoundSystem.SetAmbience(uid, false); @@ -187,16 +183,15 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) return; - var gasMixDict = new Dictionary(); + args.GasMixtures ??= new Dictionary(); if(_nodeContainer.TryGetNode(nodeContainer, component.InletName, out PipeNode? inlet)) - gasMixDict.Add(Loc.GetString("gas-analyzer-window-text-inlet"), inlet.Air); + args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-inlet"), inlet.Air); if(_nodeContainer.TryGetNode(nodeContainer, component.FilterName, out PipeNode? filterNode)) - gasMixDict.Add(Loc.GetString("gas-analyzer-window-text-filter"), filterNode.Air); + args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-filter"), filterNode.Air); if(_nodeContainer.TryGetNode(nodeContainer, component.OutletName, out PipeNode? outlet)) - gasMixDict.Add(Loc.GetString("gas-analyzer-window-text-outlet"), outlet.Air); + args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-outlet"), outlet.Air); - args.GasMixtures = gasMixDict; args.DeviceFlipped = inlet != null && filterNode != null && inlet.CurrentPipeDirection.ToDirection() == filterNode.CurrentPipeDirection.ToDirection().GetClockwise90Degrees(); } } diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs index fb65c17f61..ba8ebf3c9a 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs @@ -54,18 +54,8 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems { // TODO ATMOS: Cache total moles since it's expensive. - if (!mixer.Enabled) - { - _ambientSoundSystem.SetAmbience(uid, false); - return; - } - - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, mixer.InletOneName, out PipeNode? inletOne) - || !_nodeContainer.TryGetNode(nodeContainer, mixer.InletTwoName, out PipeNode? inletTwo) - || !_nodeContainer.TryGetNode(nodeContainer, mixer.OutletName, out PipeNode? outlet)) + if (!mixer.Enabled + || !_nodeContainer.TryGetNodes(uid, mixer.InletOneName, mixer.InletTwoName, mixer.OutletName, out PipeNode? inletOne, out PipeNode? inletTwo, out PipeNode? outlet)) { _ambientSoundSystem.SetAmbience(uid, false); return; diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs index 2c2f1584a5..1bab2abd8e 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs @@ -33,11 +33,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems private void OnUpdate(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceUpdateEvent args) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) - || !EntityManager.TryGetComponent(uid, out AtmosDeviceComponent? device) - || !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inletNode) - || !_nodeContainer.TryGetNode(nodeContainer, comp.ControlName, out PipeNode? controlNode) - || !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? outletNode)) + if (!_nodeContainer.TryGetNodes(uid, comp.InletName, comp.ControlName, comp.OutletName, out PipeNode? inletNode, out PipeNode? controlNode, out PipeNode? outletNode)) { _ambientSoundSystem.SetAmbience(uid, false); comp.Enabled = false; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index 170586339d..3e4340bf1d 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -60,7 +60,7 @@ public sealed class GasCanisterSystem : EntitySystem if (!Resolve(uid, ref canister, ref transform)) return; - var environment = _atmos.GetContainingMixture(uid, false, true); + var environment = _atmos.GetContainingMixture((uid, transform), false, true); if (environment is not null) _atmos.Merge(environment, canister.Air); @@ -168,7 +168,7 @@ public sealed class GasCanisterSystem : EntitySystem } else { - var environment = _atmos.GetContainingMixture(uid, false, true); + var environment = _atmos.GetContainingMixture(uid, args.Grid, args.Map, false, true); _atmos.ReleaseGasTo(canister.Air, environment, canister.ReleasePressure); } } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCondenserSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCondenserSystem.cs index 852542ec6c..e903ceedaf 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCondenserSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCondenserSystem.cs @@ -30,9 +30,8 @@ public sealed class GasCondenserSystem : EntitySystem private void OnCondenserUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) { - if (!(_power.IsPowered(entity) && TryComp(entity, out var receiver)) - || !TryComp(entity, out var nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, entity.Comp.Inlet, out PipeNode? inlet) + if (!(TryComp(entity, out var receiver) && _power.IsPowered(entity, receiver)) + || !_nodeContainer.TryGetNode(entity.Owner, entity.Comp.Inlet, out PipeNode? inlet) || !_solution.ResolveSolution(entity.Owner, entity.Comp.SolutionId, ref entity.Comp.Solution, out var solution)) { return; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs index 8029a09556..834a1dfb0b 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs @@ -50,16 +50,10 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems if (!injector.Enabled) return; - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) + if (!_nodeContainer.TryGetNode(uid, injector.InletName, out PipeNode? inlet)) return; - if (!TryComp(uid, out AtmosDeviceComponent? device)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, injector.InletName, out PipeNode? inlet)) - return; - - var environment = _atmosphereSystem.GetContainingMixture(uid, true, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map, true, true); if (environment == null) return; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs index c8fd23d466..72812cb523 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs @@ -24,15 +24,12 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems private void OnPassiveVentUpdated(EntityUid uid, GasPassiveVentComponent vent, ref AtmosDeviceUpdateEvent args) { - var environment = _atmosphereSystem.GetContainingMixture(uid, true, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map, true, true); if (environment == null) return; - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, vent.InletName, out PipeNode? inlet)) + if (!_nodeContainer.TryGetNode(uid, vent.InletName, out PipeNode? inlet)) return; var inletAir = inlet.Air.RemoveRatio(1f); diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs index 4ddd19dd45..7cb8102a38 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs @@ -39,10 +39,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems private void OnAnchorChanged(EntityUid uid, GasPortableComponent portable, ref AnchorStateChangedEvent args) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, portable.PortName, out PipeNode? portableNode)) + if (!_nodeContainer.TryGetNode(uid, portable.PortName, out PipeNode? portableNode)) return; portableNode.ConnectionsEnabled = args.Anchored; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs index d4ddd65a8e..9b61044f03 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs @@ -110,7 +110,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems _atmosphereSystem.AddHeat(heatExchangeGasMixture, dQPipe); thermoMachine.LastEnergyDelta = dQPipe; - if (dQLeak != 0f && _atmosphereSystem.GetContainingMixture(uid, excite: true) is { } containingMixture) + if (dQLeak != 0f && _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map, excite: true) is { } containingMixture) _atmosphereSystem.AddHeat(containingMixture, dQLeak); } @@ -130,8 +130,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems } else { - if (!TryComp(uid, out var nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, thermoMachine.InletName, out PipeNode? inlet)) + if (!_nodeContainer.TryGetNode(uid, thermoMachine.InletName, out PipeNode? inlet)) return; heatExchangeGasMixture = inlet.Air; } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs index 3a3ccf7523..a986385f5e 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs @@ -67,15 +67,12 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems _ => throw new ArgumentOutOfRangeException() }; - if (!vent.Enabled - || !TryComp(uid, out AtmosDeviceComponent? device) - || !TryComp(uid, out NodeContainerComponent? nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, nodeName, out PipeNode? pipe)) + if (!vent.Enabled || !_nodeContainer.TryGetNode(uid, nodeName, out PipeNode? pipe)) { return; } - var environment = _atmosphereSystem.GetContainingMixture(uid, true, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map, true, true); // We're in an air-blocked tile... Do nothing. if (environment == null) @@ -295,9 +292,6 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems /// private void OnAnalyzed(EntityUid uid, GasVentPumpComponent component, GasAnalyzerScanEvent args) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; - var gasMixDict = new Dictionary(); // these are both called pipe, above it switches using this so I duplicated that...? @@ -307,7 +301,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems VentPumpDirection.Siphoning => component.Outlet, _ => throw new ArgumentOutOfRangeException() }; - if (_nodeContainer.TryGetNode(nodeContainer, nodeName, out PipeNode? pipe)) + if (_nodeContainer.TryGetNode(uid, nodeName, out PipeNode? pipe)) gasMixDict.Add(nodeName, pipe.Air); args.GasMixtures = gasMixDict; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs index 5afa007e5e..b27689ed58 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs @@ -49,27 +49,18 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems private void OnVentScrubberUpdated(EntityUid uid, GasVentScrubberComponent scrubber, ref AtmosDeviceUpdateEvent args) { if (_weldable.IsWelded(uid)) - { - return; - } - - if (!TryComp(uid, out AtmosDeviceComponent? device)) return; var timeDelta = args.dt; - if (!scrubber.Enabled - || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, scrubber.OutletName, out PipeNode? outlet)) + if (!scrubber.Enabled || !_nodeContainer.TryGetNode(uid, scrubber.OutletName, out PipeNode? outlet)) return; - var xform = Transform(uid); - - if (xform.GridUid == null) + if (args.Grid is not {} grid) return; - var position = _transformSystem.GetGridTilePositionOrDefault((uid,xform)); - var environment = _atmosphereSystem.GetTileMixture(xform.GridUid, xform.MapUid, position, true); + var position = _transformSystem.GetGridTilePositionOrDefault(uid); + var environment = _atmosphereSystem.GetTileMixture(grid, args.Map, position, true); Scrub(timeDelta, scrubber, environment, outlet); @@ -77,7 +68,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems return; // Scrub adjacent tiles too. - var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true); + var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(grid, 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 e986f8f991..f9043c091a 100644 --- a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs +++ b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs @@ -47,17 +47,13 @@ namespace Content.Server.Atmos.Portable private void OnDeviceUpdated(EntityUid uid, PortableScrubberComponent component, ref AtmosDeviceUpdateEvent args) { - if (!TryComp(uid, out AtmosDeviceComponent? device)) - return; - var timeDelta = args.dt; if (!component.Enabled) return; // If we are on top of a connector port, empty into it. - if (TryComp(uid, out var nodeContainer) - && _nodeContainer.TryGetNode(nodeContainer, component.PortName, out PortablePipeNode? portableNode) + if (_nodeContainer.TryGetNode(uid, component.PortName, out PortablePipeNode? portableNode) && portableNode.ConnectionsEnabled) { _atmosphereSystem.React(component.Air, portableNode); @@ -71,13 +67,11 @@ namespace Content.Server.Atmos.Portable return; } - var xform = Transform(uid); - - if (xform.GridUid == null) + if (args.Grid is not {} grid) return; - var position = _transformSystem.GetGridTilePositionOrDefault((uid,xform)); - var environment = _atmosphereSystem.GetTileMixture(xform.GridUid, xform.MapUid, position, true); + var position = _transformSystem.GetGridTilePositionOrDefault(uid); + var environment = _atmosphereSystem.GetTileMixture(grid, args.Map, position, true); var running = Scrub(timeDelta, component, environment); @@ -85,8 +79,9 @@ namespace Content.Server.Atmos.Portable // We scrub once to see if we can and set the animation if (!running) return; + // widenet - var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true); + var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(grid, position, false, true); while (enumerator.MoveNext(out var adjacent)) { Scrub(timeDelta, component, adjacent); @@ -98,10 +93,7 @@ namespace Content.Server.Atmos.Portable /// private void OnAnchorChanged(EntityUid uid, PortableScrubberComponent component, ref AnchorStateChangedEvent args) { - if (!TryComp(uid, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, component.PortName, out PipeNode? portableNode)) + if (!_nodeContainer.TryGetNode(uid, component.PortName, out PipeNode? portableNode)) return; portableNode.ConnectionsEnabled = (args.Anchored && _gasPortableSystem.FindGasPortIn(Transform(uid).GridUid, Transform(uid).Coordinates, out _)); @@ -159,14 +151,10 @@ namespace Content.Server.Atmos.Portable /// private void OnScrubberAnalyzed(EntityUid uid, PortableScrubberComponent component, GasAnalyzerScanEvent args) { - var gasMixDict = new Dictionary { { Name(uid), component.Air } }; + args.GasMixtures ??= new Dictionary { { Name(uid), component.Air } }; // If it's connected to a port, include the port side - if (TryComp(uid, out NodeContainerComponent? nodeContainer)) - { - if (_nodeContainer.TryGetNode(nodeContainer, component.PortName, out PipeNode? port)) - gasMixDict.Add(component.PortName, port.Air); - } - args.GasMixtures = gasMixDict; + if (_nodeContainer.TryGetNode(uid, component.PortName, out PipeNode? port)) + args.GasMixtures.Add(component.PortName, port.Air); } } } diff --git a/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs b/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs index 1631132821..fff15f696c 100644 --- a/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs +++ b/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs @@ -71,7 +71,7 @@ public sealed class SpaceHeaterSystem : EntitySystem // If in automatic temperature mode, check if we need to adjust the heat exchange direction if (spaceHeater.Mode == SpaceHeaterMode.Auto) { - var environment = _atmosphereSystem.GetContainingMixture(uid); + var environment = _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map); if (environment == null) return; diff --git a/Content.Server/Electrocution/ElectrocutionNode.cs b/Content.Server/Electrocution/ElectrocutionNode.cs index 7abcba7666..c8e437d353 100644 --- a/Content.Server/Electrocution/ElectrocutionNode.cs +++ b/Content.Server/Electrocution/ElectrocutionNode.cs @@ -9,7 +9,7 @@ namespace Content.Server.Electrocution public sealed partial class ElectrocutionNode : Node { [DataField("cable")] - public EntityUid CableEntity; + public EntityUid? CableEntity; [DataField("node")] public string? NodeName; @@ -19,12 +19,11 @@ namespace Content.Server.Electrocution MapGridComponent? grid, IEntityManager entMan) { - var _nodeContainer = entMan.System(); - - if (!nodeQuery.TryGetComponent(CableEntity, out var nodeContainer)) + if (CableEntity == null || NodeName == null) yield break; - if (_nodeContainer.TryGetNode(nodeContainer, NodeName, out Node? node)) + var _nodeContainer = entMan.System(); + if (_nodeContainer.TryGetNode(CableEntity.Value, NodeName, out Node? node)) yield return node; } } diff --git a/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs b/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs index 8d2a699de2..7db1f513f7 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs @@ -271,7 +271,7 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood var direction = (AtmosDirection) (1 << i); if (ignoreTileBlockers || !blockedDirections.IsFlagSet(direction)) { - ProcessNewTile(iteration, tile.Offset(direction), direction.GetOpposite()); + ProcessNewTile(iteration, tile.Offset(direction), i.ToOppositeDir()); } } @@ -300,7 +300,7 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood var direction = (AtmosDirection) (1 << i); if (blockedDirections.IsFlagSet(direction)) { - list.Add((tile.Offset(direction), direction.GetOpposite())); + list.Add((tile.Offset(direction), i.ToOppositeDir())); } } } diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs b/Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs index f8c917c1cd..313b03e03a 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs @@ -97,7 +97,7 @@ public sealed class ExplosionSpaceTileFlood : ExplosionTileFlood if (!unblockedDirections.IsFlagSet(direction)) continue; // explosion cannot propagate in this direction. Ever. - ProcessNewTile(iteration, tile.Offset(direction), direction.GetOpposite()); + ProcessNewTile(iteration, tile.Offset(direction), i.ToOppositeDir()); } } } diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index 78034e0fc3..9e546dc33f 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -404,14 +404,17 @@ public sealed partial class MechSystem : SharedMechSystem if (args.Handled) return; - if (!TryComp(component.Mech, out var mech) || - !TryComp(component.Mech, out var mechAir)) + if (!TryComp(component.Mech, out MechComponent? mech)) + return; + + if (mech.Airtight && TryComp(component.Mech, out MechAirComponent? air)) { + args.Handled = true; + args.Gas = air.Air; return; } - args.Gas = mech.Airtight ? mechAir.Air : _atmosphere.GetContainingMixture(component.Mech); - + args.Gas = _atmosphere.GetContainingMixture(component.Mech, excite: args.Excite); args.Handled = true; } diff --git a/Content.Server/Medical/CryoPodSystem.cs b/Content.Server/Medical/CryoPodSystem.cs index a7d12d9f0f..a949d980be 100644 --- a/Content.Server/Medical/CryoPodSystem.cs +++ b/Content.Server/Medical/CryoPodSystem.cs @@ -257,10 +257,7 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem private void OnCryoPodUpdateAtmosphere(Entity entity, ref AtmosDeviceUpdateEvent args) { - if (!TryComp(entity, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, entity.Comp.PortName, out PortablePipeNode? portNode)) + if (!_nodeContainer.TryGetNode(entity.Owner, entity.Comp.PortName, out PortablePipeNode? portNode)) return; if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir)) @@ -279,14 +276,10 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir)) return; - var gasMixDict = new Dictionary { { Name(entity.Owner), cryoPodAir.Air } }; + args.GasMixtures ??= new Dictionary { { Name(entity.Owner), cryoPodAir.Air } }; // If it's connected to a port, include the port side - if (TryComp(entity, out NodeContainerComponent? nodeContainer)) - { - if (_nodeContainer.TryGetNode(nodeContainer, entity.Comp.PortName, out PipeNode? port)) - gasMixDict.Add(entity.Comp.PortName, port.Air); - } - args.GasMixtures = gasMixDict; + if (_nodeContainer.TryGetNode(entity.Owner, entity.Comp.PortName, out PipeNode? port)) + args.GasMixtures.Add(entity.Comp.PortName, port.Air); } private void OnEjected(Entity cryoPod, ref EntRemovedFromContainerMessage args) diff --git a/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs b/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs index 99d18aeb3f..19b811a287 100644 --- a/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs +++ b/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs @@ -14,6 +14,7 @@ namespace Content.Server.NodeContainer.EntitySystems public sealed class NodeContainerSystem : EntitySystem { [Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!; + private EntityQuery _query; public override void Initialize() { @@ -26,6 +27,8 @@ namespace Content.Server.NodeContainer.EntitySystems SubscribeLocalEvent(OnReAnchor); SubscribeLocalEvent(OnMoveEvent); SubscribeLocalEvent(OnExamine); + + _query = GetEntityQuery(); } public bool TryGetNode(NodeContainerComponent component, string? identifier, [NotNullWhen(true)] out T? node) where T : Node @@ -46,6 +49,77 @@ namespace Content.Server.NodeContainer.EntitySystems return false; } + public bool TryGetNode(Entity ent, string identifier, [NotNullWhen(true)] out T? node) where T : Node + { + if (_query.Resolve(ent, ref ent.Comp, false) + && ent.Comp.Nodes.TryGetValue(identifier, out var n) + && n is T t) + { + node = t; + return true; + } + + node = null; + return false; + } + + public bool TryGetNodes( + Entity ent, + string id1, + string id2, + [NotNullWhen(true)] out T1? node1, + [NotNullWhen(true)] out T2? node2) + where T1 : Node + where T2 : Node + { + if (_query.Resolve(ent, ref ent.Comp, false) + && ent.Comp.Nodes.TryGetValue(id1, out var n1) + && n1 is T1 t1 + && ent.Comp.Nodes.TryGetValue(id2, out var n2) + && n2 is T2 t2) + { + node1 = t1; + node2 = t2; + return true; + } + + node1 = null; + node2 = null; + return false; + } + + public bool TryGetNodes( + Entity ent, + string id1, + string id2, + string id3, + [NotNullWhen(true)] out T1? node1, + [NotNullWhen(true)] out T2? node2, + [NotNullWhen(true)] out T3? node3) + where T1 : Node + where T2 : Node + where T3 : Node + { + if (_query.Resolve(ent, ref ent.Comp, false) + && ent.Comp.Nodes.TryGetValue(id1, out var n1) + && n1 is T1 t1 + && ent.Comp.Nodes.TryGetValue(id2, out var n2) + && n2 is T2 t2 + && ent.Comp.Nodes.TryGetValue(id3, out var n3) + && n2 is T3 t3) + { + node1 = t1; + node2 = t2; + node3 = t3; + return true; + } + + node1 = null; + node2 = null; + node3 = null; + return false; + } + private void OnInitEvent(EntityUid uid, NodeContainerComponent component, ComponentInit args) { foreach (var (key, node) in component.Nodes) diff --git a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs index 60f0603074..6e0e0c503a 100644 --- a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs +++ b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs @@ -94,7 +94,7 @@ public sealed class PneumaticCannonSystem : SharedPneumaticCannonSystem return; // this should always be possible, as we'll eject the gas tank when it no longer is - var environment = _atmos.GetContainingMixture(cannon, false, true); + var environment = _atmos.GetContainingMixture(cannon.Owner, false, true); var removed = _gasTank.RemoveAir(gas.Value, component.GasUsage); if (environment != null && removed != null) { diff --git a/Content.Server/Power/Components/CableVisComponent.cs b/Content.Server/Power/Components/CableVisComponent.cs index bd9c62ba80..51d68b99fd 100644 --- a/Content.Server/Power/Components/CableVisComponent.cs +++ b/Content.Server/Power/Components/CableVisComponent.cs @@ -4,7 +4,7 @@ public sealed partial class CableVisComponent : Component { [ViewVariables(VVAccess.ReadWrite)] - [DataField("node")] - public string? Node; + [DataField("node", required:true)] + public string Node; } } diff --git a/Content.Server/Power/EntitySystems/CableVisSystem.cs b/Content.Server/Power/EntitySystems/CableVisSystem.cs index ec08523d44..1a68e87ad6 100644 --- a/Content.Server/Power/EntitySystems/CableVisSystem.cs +++ b/Content.Server/Power/EntitySystems/CableVisSystem.cs @@ -23,10 +23,7 @@ namespace Content.Server.Power.EntitySystems private void UpdateAppearance(EntityUid uid, CableVisComponent cableVis, ref NodeGroupsRebuilt args) { - if (!TryComp(uid, out NodeContainerComponent? nodeContainer) || !TryComp(uid, out AppearanceComponent? appearance)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, cableVis.Node, out var node)) + if (!_nodeContainer.TryGetNode(uid, cableVis.Node, out CableNode? node)) return; var transform = Transform(uid); @@ -55,7 +52,7 @@ namespace Content.Server.Power.EntitySystems }; } - _appearance.SetData(uid, WireVisVisuals.ConnectedMask, mask, appearance); + _appearance.SetData(uid, WireVisVisuals.ConnectedMask, mask); } } } diff --git a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs index 76cf90c369..5f79906c99 100644 --- a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs +++ b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs @@ -26,12 +26,8 @@ public sealed class GasPowerReceiverSystem : EntitySystem { var timeDelta = args.dt; - if (!HasComp(uid) - || !TryComp(uid, out var nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, "pipe", out var pipe)) - { + if (!_nodeContainer.TryGetNode(uid, "pipe", out PipeNode? pipe)) return; - } // if we're below the max temperature, then we are simply consuming our target gas if (pipe.Air.Temperature <= component.MaxTemperature) @@ -57,7 +53,7 @@ public sealed class GasPowerReceiverSystem : EntitySystem if (component.OffVentGas) { // eject the gas into the atmosphere - var mix = _atmosphereSystem.GetContainingMixture(uid, false, true); + var mix = _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map, false, true); if (mix is not null) _atmosphereSystem.Merge(res, mix); } diff --git a/Content.Server/Spreader/SpreaderSystem.cs b/Content.Server/Spreader/SpreaderSystem.cs index 671c281d1f..fe14d86aa1 100644 --- a/Content.Server/Spreader/SpreaderSystem.cs +++ b/Content.Server/Spreader/SpreaderSystem.cs @@ -231,10 +231,9 @@ public sealed class SpreaderSystem : EntitySystem // Add the normal neighbors. for (var i = 0; i < 4; i++) { - var direction = (Direction) (i * 2); - var atmosDir = direction.ToAtmosDirection(); - var neighborPos = SharedMapSystem.GetDirection(tile, direction); - neighborTiles.Add((comp.GridUid.Value, grid, neighborPos, atmosDir, atmosDir.GetOpposite())); + var atmosDir = (AtmosDirection) (1 << i); + var neighborPos = tile.Offset(atmosDir); + neighborTiles.Add((comp.GridUid.Value, grid, neighborPos, atmosDir, i.ToOppositeDir())); } foreach (var (neighborEnt, neighborGrid, neighborPos, ourAtmosDir, otherAtmosDir) in neighborTiles) diff --git a/Content.Shared/Atmos/AtmosDirection.cs b/Content.Shared/Atmos/AtmosDirection.cs index a25c9d553c..09ba521aa9 100644 --- a/Content.Shared/Atmos/AtmosDirection.cs +++ b/Content.Shared/Atmos/AtmosDirection.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System.Numerics; +using System.Runtime.CompilerServices; using Robust.Shared.Serialization; namespace Content.Shared.Atmos @@ -15,6 +16,8 @@ namespace Content.Shared.Atmos South = 1 << 1, // 2 East = 1 << 2, // 4 West = 1 << 3, // 8 + // If more directions are added, note that AtmosDirectionHelpers.ToOppositeIndex() expects opposite directions + // to come in pairs NorthEast = North | East, // 5 SouthEast = South | East, // 6 @@ -42,6 +45,22 @@ namespace Content.Shared.Atmos }; } + /// + /// This returns the index that corresponds to the opposite direction of some other direction index. + /// I.e., 1<<OppositeIndex(i) == (1<<i).GetOpposite() + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ToOppositeIndex(this int index) + { + return index ^ 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static AtmosDirection ToOppositeDir(this int index) + { + return (AtmosDirection) (1 << (index ^ 1)); + } + public static Direction ToDirection(this AtmosDirection direction) { return direction switch @@ -119,10 +138,11 @@ namespace Content.Shared.Atmos return angle.GetDir().ToAtmosDirection(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ToIndex(this AtmosDirection direction) { // This will throw if you pass an invalid direction. Not this method's fault, but yours! - return (int) Math.Log2((int) direction); + return BitOperations.Log2((uint)direction); } public static AtmosDirection WithFlag(this AtmosDirection direction, AtmosDirection other) diff --git a/Content.Shared/Tools/Systems/WeldableSystem.cs b/Content.Shared/Tools/Systems/WeldableSystem.cs index f887ed3049..b0ea68f713 100644 --- a/Content.Shared/Tools/Systems/WeldableSystem.cs +++ b/Content.Shared/Tools/Systems/WeldableSystem.cs @@ -15,14 +15,7 @@ public sealed class WeldableSystem : EntitySystem [Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - - public bool IsWelded(EntityUid uid, WeldableComponent? component = null) - { - if (!Resolve(uid, ref component, false)) - return false; - - return component.IsWelded; - } + private EntityQuery _query; public override void Initialize() { @@ -31,6 +24,13 @@ public sealed class WeldableSystem : EntitySystem SubscribeLocalEvent(OnWeldFinished); SubscribeLocalEvent(OnWeldChanged); SubscribeLocalEvent(OnExamine); + + _query = GetEntityQuery(); + } + + public bool IsWelded(EntityUid uid, WeldableComponent? component = null) + { + return _query.Resolve(uid, ref component, false) && component.IsWelded; } private void OnExamine(EntityUid uid, WeldableComponent component, ExaminedEvent args) @@ -49,7 +49,7 @@ public sealed class WeldableSystem : EntitySystem private bool CanWeld(EntityUid uid, EntityUid tool, EntityUid user, WeldableComponent? component = null) { - if (!Resolve(uid, ref component)) + if (!_query.Resolve(uid, ref component)) return false; // Other component systems @@ -63,7 +63,7 @@ public sealed class WeldableSystem : EntitySystem private bool TryWeld(EntityUid uid, EntityUid tool, EntityUid user, WeldableComponent? component = null) { - if (!Resolve(uid, ref component)) + if (!_query.Resolve(uid, ref component)) return false; if (!CanWeld(uid, tool, user, component)) @@ -115,17 +115,13 @@ public sealed class WeldableSystem : EntitySystem private void UpdateAppearance(EntityUid uid, WeldableComponent? component = null) { - if (!Resolve(uid, ref component)) - return; - - if (!TryComp(uid, out AppearanceComponent? appearance)) - return; - _appearance.SetData(uid, WeldableVisuals.IsWelded, component.IsWelded, appearance); + if (_query.Resolve(uid, ref component)) + _appearance.SetData(uid, WeldableVisuals.IsWelded, component.IsWelded); } public void SetWeldedState(EntityUid uid, bool state, WeldableComponent? component = null) { - if (!Resolve(uid, ref component)) + if (!_query.Resolve(uid, ref component)) return; if (component.IsWelded == state) @@ -141,7 +137,7 @@ public sealed class WeldableSystem : EntitySystem public void SetWeldingTime(EntityUid uid, TimeSpan time, WeldableComponent? component = null) { - if (!Resolve(uid, ref component)) + if (!_query.Resolve(uid, ref component)) return; if (component.WeldingTime.Equals(time)) diff --git a/Resources/Prototypes/Entities/Virtual/electrocution.yml b/Resources/Prototypes/Entities/Virtual/electrocution.yml index 497071ee93..ac65245191 100644 --- a/Resources/Prototypes/Entities/Virtual/electrocution.yml +++ b/Resources/Prototypes/Entities/Virtual/electrocution.yml @@ -1,7 +1,7 @@ # Special entity used to attach to power networks as load when somebody gets electrocuted. - type: entity id: VirtualElectrocutionLoadBase - noSpawn: true + abstract: true components: - type: Electrocution - type: Icon