From 8eecd9cc2db16f584471571432c9f0b3a8c292d1 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 22 Apr 2024 18:46:22 -0400 Subject: [PATCH 01/33] Prevent ghosts from triggering examine artifact triggers (#27249) --- .../Triggers/Systems/ArtifactExamineTriggerSystem.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactExamineTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactExamineTriggerSystem.cs index cbade1682e5..b7afbcfc8b4 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactExamineTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactExamineTriggerSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Shared.Examine; +using Content.Shared.Ghost; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -15,6 +16,10 @@ public override void Initialize() private void OnExamine(EntityUid uid, ArtifactExamineTriggerComponent component, ExaminedEvent args) { + // Prevent ghosts from activating this trigger unless they have CanGhostInteract + if (TryComp(args.Examiner, out var ghost) && !ghost.CanGhostInteract) + return; + _artifact.TryActivateArtifact(uid); } } From ee0f63d2736936e4bdeae0043f42a25de0779733 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 22 Apr 2024 22:47:28 +0000 Subject: [PATCH 02/33] 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 77b8f7a4b1d..d0553e3c93c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: PoorMansDreams - changes: - - message: Buyable Janitorial Trolley! - type: Add - id: 5922 - time: '2024-02-12T06:35:19.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25139 - author: Plykiya changes: - message: Stun batons and stun prods now display their hits remaining. @@ -3855,3 +3848,10 @@ id: 6421 time: '2024-04-22T12:18:28.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27222 +- author: Tayrtahn + changes: + - message: Ghosts can no longer trigger artifacts by examining them. + type: Fix + id: 6422 + time: '2024-04-22T22:46:22.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27249 From 06a5e00c99ae0eff70ef29b68a432a9159fe55cb Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Tue, 23 Apr 2024 16:28:09 +1200 Subject: [PATCH 03/33] Update engine to v219.1.3 (#27257) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 73da147b881..4e87d930097 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 73da147b8811c8d032e0f119ef507a1c11ff4073 +Subproject commit 4e87d93009748778c27ae835b16297596e5b1563 From 0e2f5d6bf730c67cc5adeeca2f0ba5fbe6f1f9f4 Mon Sep 17 00:00:00 2001 From: Tyzemol <85772526+Tyzemol@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:48:26 +0500 Subject: [PATCH 04/33] Fix slime storage issue (#27260) fix_slime_storage --- Resources/Prototypes/Entities/Mobs/Species/slime.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index 5599825a0c0..de2f88e59de 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -14,6 +14,7 @@ requiredLegs: 2 # they like eat it idk lol - type: Storage + clickInsert: false grid: - 0,0,1,2 maxItemSize: Large From c4a6bcca5c4b0f5ee14fd6d556df181ba5016e39 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 23 Apr 2024 08:49:33 +0000 Subject: [PATCH 05/33] 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 d0553e3c93c..2ae8a2fb599 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Plykiya - changes: - - message: Stun batons and stun prods now display their hits remaining. - type: Tweak - id: 5923 - time: '2024-02-12T06:36:06.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25141 - author: Ilya246 changes: - message: Autolathes may now make empty air tanks. @@ -3855,3 +3848,10 @@ id: 6422 time: '2024-04-22T22:46:22.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27249 +- author: Tyzemol + changes: + - message: Slimes no longer absorb all items used on them + type: Fix + id: 6423 + time: '2024-04-23T08:48:26.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27260 From 32c1f8f8cd2cff03d8de11be139cfd46f552c2ea Mon Sep 17 00:00:00 2001 From: Skye <57879983+Rainbeon@users.noreply.github.com> Date: Tue, 23 Apr 2024 04:57:09 -0400 Subject: [PATCH 06/33] Fix: Suit Sensors Vitals (#27259) Vitals now functions correctly --- Content.Shared/Medical/SuitSensor/SharedSuitSensor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Medical/SuitSensor/SharedSuitSensor.cs b/Content.Shared/Medical/SuitSensor/SharedSuitSensor.cs index e3ca466b085..27539dd22bd 100644 --- a/Content.Shared/Medical/SuitSensor/SharedSuitSensor.cs +++ b/Content.Shared/Medical/SuitSensor/SharedSuitSensor.cs @@ -24,7 +24,7 @@ public SuitSensorStatus(NetEntity suitSensorUid, string name, string job, string public bool IsAlive; public int? TotalDamage; public int? TotalDamageThreshold; - public float? DamagePercentage => TotalDamageThreshold == null || TotalDamage == null ? null : TotalDamage / TotalDamageThreshold; + public float? DamagePercentage => TotalDamageThreshold == null || TotalDamage == null ? null : TotalDamage / (float) TotalDamageThreshold; public NetCoordinates? Coordinates; } From 31b9f98e6d2a44b6d2700b5399b47a261fb8771c Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 23 Apr 2024 08:58:15 +0000 Subject: [PATCH 07/33] 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 2ae8a2fb599..77c4e376dfd 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Ilya246 - changes: - - message: Autolathes may now make empty air tanks. - type: Add - id: 5924 - time: '2024-02-12T06:37:28.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25130 - author: Ubaser changes: - message: Resprited the CE's jetpack. @@ -3855,3 +3848,10 @@ id: 6423 time: '2024-04-23T08:48:26.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27260 +- author: Rainbeon + changes: + - message: Suit sensor vitals now function again. + type: Fix + id: 6424 + time: '2024-04-23T08:57:09.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27259 From 88b56efba102c0f537c268f3b23e21715d5c34d6 Mon Sep 17 00:00:00 2001 From: Ghagliiarghii <68826635+Ghagliiarghii@users.noreply.github.com> Date: Tue, 23 Apr 2024 07:24:58 -0400 Subject: [PATCH 08/33] Increase availibility of combat knives to security (#27224) * Increase availibility of combat knives to security * whitelisted combatknife in secbelt for Diona * Revert SecDrobe change for MGS --- .../Catalog/VendingMachines/Inventories/sec.yml | 1 + Resources/Prototypes/Entities/Clothing/Belt/belts.yml | 1 + Resources/Prototypes/Recipes/Lathes/security.yml | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/sec.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/sec.yml index 0aa814196af..b54c2cdbcfa 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/sec.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/sec.yml @@ -14,6 +14,7 @@ ClothingEyesHudSecurity: 2 ClothingEyesEyepatchHudSecurity: 2 ClothingBeltSecurityWebbing: 5 + CombatKnife: 3 Zipties: 12 RiotShield: 2 RiotLaserShield: 2 diff --git a/Resources/Prototypes/Entities/Clothing/Belt/belts.yml b/Resources/Prototypes/Entities/Clothing/Belt/belts.yml index 1f90b421526..0b0d9e018a4 100644 --- a/Resources/Prototypes/Entities/Clothing/Belt/belts.yml +++ b/Resources/Prototypes/Entities/Clothing/Belt/belts.yml @@ -472,6 +472,7 @@ - Sidearm - MagazinePistol - MagazineMagnum + - CombatKnife components: - Stunbaton - FlashOnTrigger diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index b3100ed70bf..f536242238a 100644 --- a/Resources/Prototypes/Recipes/Lathes/security.yml +++ b/Resources/Prototypes/Recipes/Lathes/security.yml @@ -30,6 +30,15 @@ Steel: 300 Plastic: 300 +- type: latheRecipe + id: CombatKnife + result: CombatKnife + category: Weapons + completetime: 2 + materials: + Steel: 250 + Plastic: 100 + - type: latheRecipe id: WeaponLaserCarbine result: WeaponLaserCarbine From 2f0c02ef3548be651fbb2ed90d79ba87d87dfb9b Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 23 Apr 2024 11:26:04 +0000 Subject: [PATCH 09/33] 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 77c4e376dfd..6b6d251e994 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Ubaser - changes: - - message: Resprited the CE's jetpack. - type: Tweak - id: 5925 - time: '2024-02-12T20:44:23.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25150 - author: EdenTheLiznerd changes: - message: Deathnettles are no longer able to bypass armor and don't do as much @@ -3855,3 +3848,11 @@ id: 6424 time: '2024-04-23T08:57:09.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27259 +- author: Ghagliiarghii + changes: + - message: Security can now find replacements for their trusty Combat Knife in the + SecVend, or craft them with the Sec Lathe. + type: Tweak + id: 6425 + time: '2024-04-23T11:24:58.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27224 From eee056eaf063caa690516bf8b4eb7e65a0199e9c Mon Sep 17 00:00:00 2001 From: Whisper <121047731+QuietlyWhisper@users.noreply.github.com> Date: Tue, 23 Apr 2024 07:30:01 -0400 Subject: [PATCH 10/33] lower max firestacks to 10, refactor flammable (#27159) * lower max firestacks to 10, refactor flammable * fix * uncap fire stack damage, lower fire stack damage --- .../Systems/AdminVerbSystem.Smites.cs | 2 +- .../Atmos/Components/FlammableComponent.cs | 34 ++++++++++++++----- .../Atmos/EntitySystems/FlammableSystem.cs | 20 ++++------- Resources/Prototypes/Entities/Mobs/base.yml | 2 +- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index 942882f7aef..042bac39566 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -152,7 +152,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Act = () => { // Fuck you. Burn Forever. - flammable.FireStacks = FlammableSystem.MaximumFireStacks; + flammable.FireStacks = flammable.MaximumFireStacks; _flammableSystem.Ignite(args.Target, args.User); var xform = Transform(args.Target); _popupSystem.PopupEntity(Loc.GetString("admin-smite-set-alight-self"), args.Target, diff --git a/Content.Server/Atmos/Components/FlammableComponent.cs b/Content.Server/Atmos/Components/FlammableComponent.cs index 679b5510586..e00f5efbdc5 100644 --- a/Content.Server/Atmos/Components/FlammableComponent.cs +++ b/Content.Server/Atmos/Components/FlammableComponent.cs @@ -11,49 +11,65 @@ public sealed partial class FlammableComponent : Component [ViewVariables(VVAccess.ReadWrite)] [DataField] - public bool OnFire { get; set; } + public bool OnFire; [ViewVariables(VVAccess.ReadWrite)] [DataField] - public float FireStacks { get; set; } + public float FireStacks; [ViewVariables(VVAccess.ReadWrite)] - [DataField("fireSpread")] + [DataField] + public float MaximumFireStacks = 10f; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField] + public float MinimumFireStacks = -10f; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField] + public string FlammableFixtureID = "flammable"; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField] + public float MinIgnitionTemperature = 373.15f; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField] public bool FireSpread { get; private set; } = false; [ViewVariables(VVAccess.ReadWrite)] - [DataField("canResistFire")] + [DataField] public bool CanResistFire { get; private set; } = false; - [DataField("damage", required: true)] + [DataField(required: true)] [ViewVariables(VVAccess.ReadWrite)] public DamageSpecifier Damage = new(); // Empty by default, we don't want any funny NREs. /// /// Used for the fixture created to handle passing firestacks when two flammable objects collide. /// - [DataField("flammableCollisionShape")] + [DataField] public IPhysShape FlammableCollisionShape = new PhysShapeCircle(0.35f); /// /// Should the component be set on fire by interactions with isHot entities /// [ViewVariables(VVAccess.ReadWrite)] - [DataField("alwaysCombustible")] + [DataField] public bool AlwaysCombustible = false; /// /// Can the component anyhow lose its FireStacks? /// [ViewVariables(VVAccess.ReadWrite)] - [DataField("canExtinguish")] + [DataField] public bool CanExtinguish = true; /// /// How many firestacks should be applied to component when being set on fire? /// [ViewVariables(VVAccess.ReadWrite)] - [DataField("firestacksOnIgnite")] + [DataField] public float FirestacksOnIgnite = 2.0f; /// diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index 058faf443ee..c569997ffed 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -49,13 +49,9 @@ public sealed class FlammableSystem : EntitySystem private EntityQuery _physicsQuery; - public const float MinimumFireStacks = -10f; - public const float MaximumFireStacks = 20f; + // This should probably be moved to the component, requires a rewrite, all fires tick at the same time private const float UpdateTime = 1f; - public const float MinIgnitionTemperature = 373.15f; - public const string FlammableFixtureID = "flammable"; - private float _timer; private readonly Dictionary, float> _fireEvents = new(); @@ -134,7 +130,7 @@ private void OnMapInit(EntityUid uid, FlammableComponent component, MapInitEvent if (!TryComp(uid, out var body)) return; - _fixture.TryCreateFixture(uid, component.FlammableCollisionShape, FlammableFixtureID, hard: false, + _fixture.TryCreateFixture(uid, component.FlammableCollisionShape, component.FlammableFixtureID, hard: false, collisionMask: (int) CollisionGroup.FullTileLayer, body: body); } @@ -192,7 +188,7 @@ private void OnCollide(EntityUid uid, FlammableComponent flammable, ref StartCol // Normal hard collisions, though this isn't generally possible since most flammable things are mobs // which don't collide with one another, shouldn't work here. - if (args.OtherFixtureId != FlammableFixtureID && args.OurFixtureId != FlammableFixtureID) + if (args.OtherFixtureId != flammable.FlammableFixtureID && args.OurFixtureId != flammable.FlammableFixtureID) return; if (!flammable.FireSpread) @@ -254,7 +250,7 @@ private void OnIsHot(EntityUid uid, FlammableComponent flammable, IsHotEvent arg private void OnTileFire(Entity ent, ref TileFireEvent args) { - var tempDelta = args.Temperature - MinIgnitionTemperature; + var tempDelta = args.Temperature - ent.Comp.MinIgnitionTemperature; _fireEvents.TryGetValue(ent, out var maxTemp); @@ -287,7 +283,7 @@ public void AdjustFireStacks(EntityUid uid, float relativeFireStacks, FlammableC if (!Resolve(uid, ref flammable)) return; - flammable.FireStacks = MathF.Min(MathF.Max(MinimumFireStacks, flammable.FireStacks + relativeFireStacks), MaximumFireStacks); + flammable.FireStacks = MathF.Min(MathF.Max(flammable.MinimumFireStacks, flammable.FireStacks + relativeFireStacks), flammable.MaximumFireStacks); if (flammable.OnFire && flammable.FireStacks <= 0) Extinguish(uid, flammable); @@ -439,12 +435,10 @@ public override void Update(float frameTime) EnsureComp(uid); _ignitionSourceSystem.SetIgnited(uid); - var damageScale = MathF.Min( flammable.FireStacks, 5); - if (TryComp(uid, out TemperatureComponent? temp)) - _temperatureSystem.ChangeHeat(uid, 12500 * damageScale, false, temp); + _temperatureSystem.ChangeHeat(uid, 12500 * flammable.FireStacks, false, temp); - _damageableSystem.TryChangeDamage(uid, flammable.Damage * damageScale, interruptsDoAfters: false); + _damageableSystem.TryChangeDamage(uid, flammable.Damage * flammable.FireStacks, interruptsDoAfters: false); AdjustFireStacks(uid, flammable.FirestackFade * (flammable.Resisting ? 10f : 1f), flammable); } diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 065d62c748d..0a2b68d0a1d 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -192,7 +192,7 @@ canResistFire: true damage: #per second, scales with number of fire 'stacks' types: - Heat: 3 + Heat: 1.5 - type: FireVisuals sprite: Mobs/Effects/onfire.rsi normalState: Generic_mob_burning From 401911b7347ea483b21b5c197fc0bef6b8483c73 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 23 Apr 2024 11:31:07 +0000 Subject: [PATCH 11/33] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 6b6d251e994..0d430fc5bee 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: EdenTheLiznerd - changes: - - message: Deathnettles are no longer able to bypass armor and don't do as much - damage - type: Tweak - id: 5926 - time: '2024-02-13T01:20:02.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25068 - author: Vasilis changes: - message: You can now inspect peoples id's and health though glass if you are within @@ -3856,3 +3848,11 @@ id: 6425 time: '2024-04-23T11:24:58.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27224 +- author: Whisper + changes: + - message: Fire stack limit reduced from 20 to 10. Fire transfers will be less effective, + and fires will not last as long. + type: Tweak + id: 6426 + time: '2024-04-23T11:30:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27159 From 905102064cd1311943093c9feeecca8bbd8bc358 Mon Sep 17 00:00:00 2001 From: brainfood1183 <113240905+brainfood1183@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:31:48 +0100 Subject: [PATCH 12/33] Maints Exit Sign (#26831) * Maints Exit Sign * change description * id, sprites changed from maints to exit prototype now has an unshaded layer so it can be seen in the dark. --- .../Structures/Wallmounts/Signs/signs.yml | 12 ++++++ .../Wallmounts/signs.rsi/direction_exit.png | Bin 0 -> 15393 bytes .../signs.rsi/direction_exit_unshaded.png | Bin 0 -> 15747 bytes .../Structures/Wallmounts/signs.rsi/meta.json | 38 +++++++++++++++++- 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 Resources/Textures/Structures/Wallmounts/signs.rsi/direction_exit.png create mode 100644 Resources/Textures/Structures/Wallmounts/signs.rsi/direction_exit_unshaded.png diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml index 9aac49a6bd7..18af2ec06cf 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml @@ -169,6 +169,18 @@ - type: Sprite state: direction_library +- type: entity + parent: BaseSignDirectional + id: SignDirectionalExit + name: exit sign + description: A directional sign pointing toward the nearest exit. + components: + - type: Sprite + layers: + - state: direction_exit + - state: direction_exit_unshaded + shader: unshaded + - type: entity parent: BaseSignDirectional id: SignDirectionalMed diff --git a/Resources/Textures/Structures/Wallmounts/signs.rsi/direction_exit.png b/Resources/Textures/Structures/Wallmounts/signs.rsi/direction_exit.png new file mode 100644 index 0000000000000000000000000000000000000000..d2b60cb5fed39d1feea375101b56198381c7aa59 GIT binary patch literal 15393 zcmeI3Yitx%6o7BhAXZU~4~QCF7f?~AGqXFp+Z}ed#a*B^EYxX&pG#g_nfoyXI9qDn=`)r;&K4M_}ZFCJ^HS;-s8ri&n@chpQ5ibQ#A_>0M0t! zdY6GGo|*)J|AZQAG#jIHMOjZ)NQ&MJD>BIxnhijpDwC4rHfTDUVT-B-o!{^7b~;og z=v?55a?w;6wyHHt(y(F4yqLVCP4+9!s!(|#BO(V$XiAPuGNBn_Cg{xi713{Nm~}d` zQ_QxYbEXy0(HO0Bg!MFZcq+V%%z1qdpTC0l_nZjN!eMS&9qmm~jj zhRV^eKw625^^w{6a7YR|TTL@1vTSE(XGN#GLQl7_yx;Fgb69?*X1(1EI-ORa!YjY8WU3HIGxF>ZMK&X)SiXW|OBpG0!6wz7 zPGpy+$Sh32B-Bg;+3_XqQmwkF8?E{XAq(me1EVvEMhme^t~Z%18QL&sbsz+Jq$Mql zSXT@T#eW3bLBvTrd7yDeBA4L}9dO9hY zAvGzrKsKed1lZ!BZDt{>#IT;w(`Z>R8*wg!0n(9uCfLd|xl zl$180RewR}U;!3dTA^cEHrYBBCE4n2A=xSqs07#{qs3(vEc0gS@Z%gEf}DSw`N%Ok zTcJiOubivwtdX4VI$$hSc%Tj$w3F^*4@T zqz?7}9YZmT`+NkmnQGD^x;4eLKOG)7d^hYhQrc!j(Tt|)1^a3n*UB5#;GD%)*Ywgz*f=%x?dZ?c6O z&SB+Q=wkookK&I0O|O_Q8UzDE7AY<=A52SeVL-?t#YN_WX(=uY2w9}K$b2v@#f1SO zixd}`52mHKFd$@+;v)0Gv=kQxge+2AWImXd;=+KCMT(2e2h&nq7!a~Zagq68T8aw; zLKZ15G9OG!abZBnBE?1KgJ~%)3Vor!L$?? z281k9Tx33&mg2&IkVT4%%m>p_To@3tNO6(*U|Na`140%lE;1iXOL1X9$Rfo>=7VV| zE({1+q`1g@FfGM}0U?V-T;;__IH87~+w4RSXdYdEeG)y~>5yycqW~4t z^nDC~b`F4V7Xu)!17M>5NYk6M0Vvy68<`o)^qqLJO1qc4;H|0!hh}hldQM*aw448V z?8~RsKeWc*FhyIlqhZB4b8m5+ad$({yN|bBws_B!+kOvD+q*My*ZitOUmsk0!=m0R z;!CglayB@<+ICIY{N2YZrtkMIopIaYo1(weerURX-=sd)Tb}8f-y?kX^^3>D3wCd~ zO#k)$?_TeizwEr~A5WeQ-dQ!K>xOl&nD4FH`}2;&FKpj-e^2)!7A!yfi)%~wCC2mD z>~C5fgX-za-a7QviR1FgN5((9?Rfaqa}R91=%YtluiA8={e!oU^_)7nZ_VkB#1)rf(bY%&FVQ&ezzHYF=vz@lTIL(@a9i|@VfqBS+- zTIh*#(NrDi)arZFV1937tJ2%01XS1ba8)oX!2n5M$#gcE&`l{Da^>Pm@YrrnM_T_U1Ov>SUwO4u$;gO0t0(6W{+;kSw=S}=7SXD zM1ZNJwUnh9I&H_5V@9_Xa=Gk8rLAyX$y6zkZWh=5E%}B0!B%FQ&XK{qW~S3pA~3>#`RO_ewx1I6Qu zGd!mR7#|S5jNcPb89wgzDSputi~H3a#Awv%J|J|&P`d5a0z>Bui)tt^eEGCEAb9+$ z8ermHo@00(04An-eM~&&=TzVq+_5-cOymeQAW>6VgLO|%IQrPvqQYc8cTDugVRV`E zco~rsVG14yjN(;zS@g>uUWBCbC@O>*kg8rY;cV@3%wjl~bCqpASk}L_C?vF8g-Xe3 z6WHr7fv8lK!e>|{s$wMUy1!|VzJH+tf4{su|k6N7uEtfSNCAuAJA5wGx`2) zb{$Q0=lc5u4sI-O{}x=GGV0oncXi6BYuR&wu32HBkPT^ec-hZB`5W_m_?QmDUXRD^ z39@K2>d~xaL82|9!8@pF-wC{8!3Py8I<^ny=nx8y`Jr+=Q0J;=z7;;M!+#2F>94|o z!dkljT+f5@6_aHChBKK{o+a_VdrBbM{GEJPJjC1vEA#l-#oi%($Ed#Uz#@8cyR0CMV%)vpKkg5 z^zrVaZ=N`qk=twUQePRz4?TbRobmXvrtyzB&Z__6oT_Du={sj%-85yzfHG!^6+N5uWkPp2hUW&vtF!vMsc9 Date: Tue, 23 Apr 2024 11:32:54 +0000 Subject: [PATCH 13/33] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0d430fc5bee..afb19b11fe1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Vasilis - changes: - - message: You can now inspect peoples id's and health though glass if you are within - details range. - type: Tweak - id: 5927 - time: '2024-02-13T06:41:56.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25163 - author: metalgearsloth changes: - message: Fix decal error spam in console due to a missing overlay. @@ -3856,3 +3848,10 @@ id: 6426 time: '2024-04-23T11:30:01.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27159 +- author: brainfood1183 + changes: + - message: Directional Exit signs for maints! + type: Add + id: 6427 + time: '2024-04-23T11:31:48.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26831 From c129bb177c8f4e7eec0ae43c09a48aa5bc0f7268 Mon Sep 17 00:00:00 2001 From: pigeonpeas <147350443+pigeonpeas@users.noreply.github.com> Date: Tue, 23 Apr 2024 07:34:09 -0400 Subject: [PATCH 14/33] Allow the purchase of emitters from cargo. (#27229) add emitter crates back to cargo + changes the contents of an emittercrate from emitter to emitterflatpack :+1: --- .../Catalog/Cargo/cargo_engines.yml | 22 +++++++++--------- .../Catalog/Fills/Crates/engines.yml | 2 +- .../Entities/Objects/Devices/flatpack.yml | 14 +++++++++++ .../Objects/Devices/flatpack.rsi/emitter.png | Bin 0 -> 326 bytes .../Objects/Devices/flatpack.rsi/meta.json | 5 +++- 5 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 Resources/Textures/Objects/Devices/flatpack.rsi/emitter.png diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_engines.yml b/Resources/Prototypes/Catalog/Cargo/cargo_engines.yml index bd00b0c2d4c..8d3bea5075c 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_engines.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_engines.yml @@ -28,17 +28,17 @@ category: cargoproduct-category-name-engineering group: market -#- type: cargoProduct -# name: "emitter crate" -# id: EngineSingularityEmitter -# description: "Contains an emitter. Used only for dangerous applications." -# icon: -# sprite: Structures/Power/Generation/Singularity/emitter.rsi -# state: emitter2 -# product: CrateEngineeringSingularityEmitter -# cost: 3000 -# category: cargoproduct-category-name-engineering -# group: market +- type: cargoProduct + name: "emitter crate" + id: EngineSingularityEmitter + description: "Contains an emitter. Used only for dangerous applications." + icon: + sprite: Structures/Power/Generation/Singularity/emitter.rsi + state: emitter2 + product: CrateEngineeringSingularityEmitter + cost: 3000 + category: cargoproduct-category-name-engineering + group: market - type: cargoProduct id: EngineSingularityCollector diff --git a/Resources/Prototypes/Catalog/Fills/Crates/engines.yml b/Resources/Prototypes/Catalog/Fills/Crates/engines.yml index 9b47036b017..79698b550a7 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/engines.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/engines.yml @@ -42,7 +42,7 @@ components: - type: StorageFill contents: - - id: Emitter # TODO change to flatpack + - id: EmitterFlatpack # TODO change to flatpack - type: entity id: CrateEngineeringSingularityCollector diff --git a/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml b/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml index 5499348bd19..2aecd132880 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml @@ -110,6 +110,20 @@ - type: GuideHelp guides: [ Singularity, Power ] +- type: entity + parent: BaseFlatpack + id: EmitterFlatpack + name: emitter flatpack + description: A flatpack used for constructing an emitter. + components: + - type: Flatpack + entity: Emitter + - type: Sprite + layers: + - state: emitter + - type: GuideHelp + guides: [ Singularity, Power ] + - type: entity parent: BaseFlatpack id: TeslaGeneratorFlatpack diff --git a/Resources/Textures/Objects/Devices/flatpack.rsi/emitter.png b/Resources/Textures/Objects/Devices/flatpack.rsi/emitter.png new file mode 100644 index 0000000000000000000000000000000000000000..c663886f9cce33df769cbe389312c741626769c3 GIT binary patch literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijK0(?ST4UBZ%J)P4s5(({UE>gxK>zz`c7 zYiDQ2(q;zKq!wP11f&E?g8YJkYLEfL1Lasrpd4p`M`SSr1K(i~W;~w1A_XYe?CIhd zV&VUG;%mMJ1rE1^-et!3|K~QloL%~jp|sB8TcH`7Q^iAZCD#Ti&u)ea_JBt^4VN-> zk^{77$T4Y^aITVWDl9%Ub6P@v(_2n%L!oOP@i)p2$u|qm{=Uz;Y1;!e`?tC#MZcR~ zO5e%;aV@W|%=jJ2@yWZX#U;?X#);!0%VY~9-rbe4j6mlwc)I$ztaD0e0sv(1 Bdo%z5 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/flatpack.rsi/meta.json b/Resources/Textures/Objects/Devices/flatpack.rsi/meta.json index 2d1ca371418..8f46a0ca53b 100644 --- a/Resources/Textures/Objects/Devices/flatpack.rsi/meta.json +++ b/Resources/Textures/Objects/Devices/flatpack.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC0-1.0", - "copyright": "Created by EmoGarbage404 (github) for SS14, solar-assembly-part taken from tgstation and modified at https://tgstation13.org/wiki/Guide_to_construction#Solar_Panels_and_Trackers, ame-part taken from vgstation at https://github.com/vgstation-coders/vgstation13/commit/1b7952787c06c21ef1623e494dcfe7cb1f46e041; singularity-generator, tesla-generator, radiation-collector, containment-field-generator, tesla-coil, grounding-rod inner icons made by lzk228", + "copyright": "Created by EmoGarbage404 (github) for SS14, solar-assembly-part taken from tgstation and modified at https://tgstation13.org/wiki/Guide_to_construction#Solar_Panels_and_Trackers, ame-part taken from vgstation at https://github.com/vgstation-coders/vgstation13/commit/1b7952787c06c21ef1623e494dcfe7cb1f46e041; singularity-generator, tesla-generator, radiation-collector, containment-field-generator, tesla-coil, grounding-rod inner icons made by lzk228; emitter made by pigeonpeas", "size": { "x": 32, "y": 32 @@ -39,6 +39,9 @@ }, { "name": "containment-field-generator" + }, + { + "name": "emitter" } ] } From 8117131c91a9cb7eab0f9d565bec3e9ebf27500f Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 23 Apr 2024 11:35:15 +0000 Subject: [PATCH 15/33] 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 afb19b11fe1..996b3d5d015 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix decal error spam in console due to a missing overlay. - type: Fix - id: 5928 - time: '2024-02-13T07:10:44.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25170 - author: jamessimo changes: - message: Can now "wink" with the text emote ;) or ;] @@ -3855,3 +3848,10 @@ id: 6427 time: '2024-04-23T11:31:48.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26831 +- author: pigeonpeas + changes: + - message: Adds the ability to purchase emitters in cargo. + type: Add + id: 6428 + time: '2024-04-23T11:34:09.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27229 From 1bfc63c546617ee2e9be9960b14abf16eee19ad5 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Tue, 23 Apr 2024 08:07:12 -0400 Subject: [PATCH 16/33] fix cargo teleporter (#27255) * fix cargo teleporter * don't delete orders * basado --- .../StationCargoOrderDatabaseComponent.cs | 16 ++++ .../Cargo/Systems/CargoSystem.Orders.cs | 33 +++++--- .../Cargo/Systems/CargoSystem.Telepad.cs | 82 +++++++++++++++---- .../StationEvents/Events/CargoGiftsRule.cs | 2 +- Content.Shared/Cargo/CargoOrderData.cs | 24 ++++-- .../Components/SharedCargoTelepadComponent.cs | 5 +- .../Entities/Structures/cargo_telepad.yml | 2 + .../Prototypes/name_identifier_groups.yml | 6 ++ 8 files changed, 133 insertions(+), 37 deletions(-) diff --git a/Content.Server/Cargo/Components/StationCargoOrderDatabaseComponent.cs b/Content.Server/Cargo/Components/StationCargoOrderDatabaseComponent.cs index c30db08bbe5..2e3b2c21157 100644 --- a/Content.Server/Cargo/Components/StationCargoOrderDatabaseComponent.cs +++ b/Content.Server/Cargo/Components/StationCargoOrderDatabaseComponent.cs @@ -1,4 +1,6 @@ +using Content.Server.Station.Components; using Content.Shared.Cargo; +using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -38,3 +40,17 @@ public sealed partial class StationCargoOrderDatabaseComponent : Component [DataField] public EntProtoId PrinterOutput = "PaperCargoInvoice"; } + +/// +/// Event broadcast before a cargo order is fulfilled, allowing alternate systems to fulfill the order. +/// +[ByRefEvent] +public record struct FulfillCargoOrderEvent(Entity Station, CargoOrderData Order, Entity OrderConsole) +{ + public Entity OrderConsole = OrderConsole; + public Entity Station = Station; + public CargoOrderData Order = Order; + + public EntityUid? FulfillmentEntity; + public bool Handled = false; +} diff --git a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs index 7a81e1a4241..13a1d3d565b 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs @@ -170,13 +170,20 @@ private void OnApproveOrderMessage(EntityUid uid, CargoOrderConsoleComponent com return; } - var tradeDestination = TryFulfillOrder(stationData, order, orderDatabase); + var ev = new FulfillCargoOrderEvent((station.Value, stationData), order, (uid, component)); + RaiseLocalEvent(ref ev); + ev.FulfillmentEntity ??= station.Value; - if (tradeDestination == null) + if (!ev.Handled) { - ConsolePopup(args.Session, Loc.GetString("cargo-console-unfulfilled")); - PlayDenySound(uid, component); - return; + ev.FulfillmentEntity = TryFulfillOrder((station.Value, stationData), order, orderDatabase); + + if (ev.FulfillmentEntity == null) + { + ConsolePopup(args.Session, Loc.GetString("cargo-console-unfulfilled")); + PlayDenySound(uid, component); + return; + } } _idCardSystem.TryFindIdCard(player, out var idCard); @@ -186,14 +193,14 @@ private void OnApproveOrderMessage(EntityUid uid, CargoOrderConsoleComponent com var approverName = idCard.Comp?.FullName ?? Loc.GetString("access-reader-unknown-id"); var approverJob = idCard.Comp?.JobTitle ?? Loc.GetString("access-reader-unknown-id"); - var message = Loc.GetString("cargo-console-unlock-approved-order-broadcast", + var message = Loc.GetString("cargo-console-unlock-approved-order-broadcast", ("productName", Loc.GetString(order.ProductName)), ("orderAmount", order.OrderQuantity), ("approverName", approverName), ("approverJob", approverJob), ("cost", cost)); _radio.SendRadioMessage(uid, message, component.AnnouncementChannel, uid, escapeMarkup: false); - ConsolePopup(args.Session, Loc.GetString("cargo-console-trade-station", ("destination", MetaData(tradeDestination.Value).EntityName))); + ConsolePopup(args.Session, Loc.GetString("cargo-console-trade-station", ("destination", MetaData(ev.FulfillmentEntity.Value).EntityName))); // Log order approval _adminLogger.Add(LogType.Action, LogImpact.Low, @@ -201,10 +208,10 @@ private void OnApproveOrderMessage(EntityUid uid, CargoOrderConsoleComponent com orderDatabase.Orders.Remove(order); DeductFunds(bank, cost); - UpdateOrders(station.Value, orderDatabase); + UpdateOrders(station.Value); } - private EntityUid? TryFulfillOrder(StationDataComponent stationData, CargoOrderData order, StationCargoOrderDatabaseComponent orderDatabase) + private EntityUid? TryFulfillOrder(Entity stationData, CargoOrderData order, StationCargoOrderDatabaseComponent orderDatabase) { // No slots at the trade station _listEnts.Clear(); @@ -357,7 +364,7 @@ public static int GetOutstandingOrderCount(StationCargoOrderDatabaseComponent co /// Updates all of the cargo-related consoles for a particular station. /// This should be called whenever orders change. /// - private void UpdateOrders(EntityUid dbUid, StationCargoOrderDatabaseComponent _) + private void UpdateOrders(EntityUid dbUid) { // Order added so all consoles need updating. var orderQuery = AllEntityQuery(); @@ -392,7 +399,7 @@ public bool AddAndApproveOrder( string description, string dest, StationCargoOrderDatabaseComponent component, - StationDataComponent stationData + Entity stationData ) { DebugTools.Assert(_protoMan.HasIndex(spawnId)); @@ -414,7 +421,7 @@ StationDataComponent stationData private bool TryAddOrder(EntityUid dbUid, CargoOrderData data, StationCargoOrderDatabaseComponent component) { component.Orders.Add(data); - UpdateOrders(dbUid, component); + UpdateOrders(dbUid); return true; } @@ -432,7 +439,7 @@ public void RemoveOrder(EntityUid dbUid, int index, StationCargoOrderDatabaseCom { orderDB.Orders.RemoveAt(sequenceIdx); } - UpdateOrders(dbUid, orderDB); + UpdateOrders(dbUid); } public void ClearOrders(StationCargoOrderDatabaseComponent component) diff --git a/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs b/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs index 42aabf2578e..f83ec1a5123 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs @@ -1,9 +1,13 @@ +using System.Linq; using Content.Server.Cargo.Components; using Content.Server.Power.Components; +using Content.Server.Power.EntitySystems; +using Content.Server.Station.Components; using Content.Shared.Cargo; using Content.Shared.Cargo.Components; using Content.Shared.DeviceLinking; using Robust.Shared.Audio; +using Robust.Shared.Random; using Robust.Shared.Utility; namespace Content.Server.Cargo.Systems; @@ -13,10 +17,44 @@ public sealed partial class CargoSystem private void InitializeTelepad() { SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnTelepadPowerChange); // Shouldn't need re-anchored event SubscribeLocalEvent(OnTelepadAnchorChange); + SubscribeLocalEvent(OnTelepadFulfillCargoOrder); } + + private void OnTelepadFulfillCargoOrder(ref FulfillCargoOrderEvent args) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var tele, out var xform)) + { + if (tele.CurrentState != CargoTelepadState.Idle) + continue; + + if (!this.IsPowered(uid, EntityManager)) + continue; + + if (_station.GetOwningStation(uid, xform) != args.Station) + continue; + + // todo cannot be fucking asked to figure out device linking rn but this shouldn't just default to the first port. + if (!TryComp(uid, out var sinkComponent) || + sinkComponent.LinkedSources.FirstOrNull() is not { } console || + console != args.OrderConsole.Owner) + continue; + + for (var i = 0; i < args.Order.OrderQuantity; i++) + { + tele.CurrentOrders.Add(args.Order); + } + tele.Accumulator = tele.Delay; + args.Handled = true; + args.FulfillmentEntity = uid; + return; + } + } + private void UpdateTelepad(float frameTime) { var query = EntityQueryEnumerator(); @@ -33,14 +71,6 @@ private void UpdateTelepad(float frameTime) continue; } - if (!TryComp(uid, out var sinkComponent) || - sinkComponent.LinkedSources.FirstOrNull() is not { } console || - !HasComp(console)) - { - comp.Accumulator = comp.Delay; - continue; - } - comp.Accumulator -= frameTime; // Uhh listen teleporting takes time and I just want the 1 float. @@ -51,21 +81,22 @@ private void UpdateTelepad(float frameTime) continue; } - var station = _station.GetOwningStation(console); - - if (!TryComp(station, out var orderDatabase) || - orderDatabase.Orders.Count == 0) + if (comp.CurrentOrders.Count == 0) { comp.Accumulator += comp.Delay; continue; } var xform = Transform(uid); - if (FulfillNextOrder(orderDatabase, xform.Coordinates, comp.PrinterOutput)) + var currentOrder = comp.CurrentOrders.First(); + if (FulfillOrder(currentOrder, xform.Coordinates, comp.PrinterOutput)) { _audio.PlayPvs(_audio.GetSound(comp.TeleportSound), uid, AudioParams.Default.WithVolume(-8f)); - UpdateOrders(station.Value, orderDatabase); + if (_station.GetOwningStation(uid) is { } station) + UpdateOrders(station); + + comp.CurrentOrders.Remove(currentOrder); comp.CurrentState = CargoTelepadState.Teleporting; _appearance.SetData(uid, CargoTelepadVisuals.State, CargoTelepadState.Teleporting, appearance); } @@ -79,6 +110,29 @@ private void OnInit(EntityUid uid, CargoTelepadComponent telepad, ComponentInit _linker.EnsureSinkPorts(uid, telepad.ReceiverPort); } + private void OnShutdown(Entity ent, ref ComponentShutdown args) + { + if (ent.Comp.CurrentOrders.Count == 0) + return; + + if (_station.GetStations().Count == 0) + return; + + if (_station.GetOwningStation(ent) is not { } station) + { + station = _random.Pick(_station.GetStations().Where(HasComp).ToList()); + } + + if (!TryComp(station, out var db) || + !TryComp(station, out var data)) + return; + + foreach (var order in ent.Comp.CurrentOrders) + { + TryFulfillOrder((station, data), order, db); + } + } + private void SetEnabled(EntityUid uid, CargoTelepadComponent component, ApcPowerReceiverComponent? receiver = null, TransformComponent? xform = null) { diff --git a/Content.Server/StationEvents/Events/CargoGiftsRule.cs b/Content.Server/StationEvents/Events/CargoGiftsRule.cs index 194786fca7a..0c8c9b6dc55 100644 --- a/Content.Server/StationEvents/Events/CargoGiftsRule.cs +++ b/Content.Server/StationEvents/Events/CargoGiftsRule.cs @@ -69,7 +69,7 @@ protected override void ActiveTick(EntityUid uid, CargoGiftsRuleComponent compon Loc.GetString(component.Description), Loc.GetString(component.Dest), cargoDb, - stationData! + (station.Value, stationData) )) { break; diff --git a/Content.Shared/Cargo/CargoOrderData.cs b/Content.Shared/Cargo/CargoOrderData.cs index 831010cedd7..ce05d922362 100644 --- a/Content.Shared/Cargo/CargoOrderData.cs +++ b/Content.Shared/Cargo/CargoOrderData.cs @@ -1,47 +1,55 @@ using Robust.Shared.Serialization; -using Content.Shared.Access.Components; using System.Text; namespace Content.Shared.Cargo { - [NetSerializable, Serializable] - public sealed class CargoOrderData + [DataDefinition, NetSerializable, Serializable] + public sealed partial class CargoOrderData { /// /// Price when the order was added. /// + [DataField] public int Price; /// /// A unique (arbitrary) ID which identifies this order. /// - public readonly int OrderId; + [DataField] + public int OrderId { get; private set; } /// /// Prototype Id for the item to be created /// - public readonly string ProductId; + [DataField] + public string ProductId { get; private set; } /// /// Prototype Name /// - public readonly string ProductName; + [DataField] + public string ProductName { get; private set; } /// /// The number of items in the order. Not readonly, as it might change /// due to caps on the amount of orders that can be placed. /// + [DataField] public int OrderQuantity; /// /// How many instances of this order that we've already dispatched /// + [DataField] public int NumDispatched = 0; - public readonly string Requester; + [DataField] + public string Requester { get; private set; } // public String RequesterRank; // TODO Figure out how to get Character ID card data // public int RequesterId; - public readonly string Reason; + [DataField] + public string Reason { get; private set; } public bool Approved => Approver is not null; + [DataField] public string? Approver; public CargoOrderData(int orderId, string productId, string productName, int price, int amount, string requester, string reason) diff --git a/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs b/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs index 911ea41cca5..5bc39347683 100644 --- a/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs +++ b/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs @@ -12,11 +12,14 @@ namespace Content.Shared.Cargo.Components; [RegisterComponent, NetworkedComponent, Access(typeof(SharedCargoSystem))] public sealed partial class CargoTelepadComponent : Component { + [DataField] + public List CurrentOrders = new(); + /// /// The actual amount of time it takes to teleport from the telepad /// [DataField("delay"), ViewVariables(VVAccess.ReadWrite)] - public float Delay = 10f; + public float Delay = 5f; /// /// How much time we've accumulated until next teleport. diff --git a/Resources/Prototypes/Entities/Structures/cargo_telepad.yml b/Resources/Prototypes/Entities/Structures/cargo_telepad.yml index d395235a533..9dc9f77cffc 100644 --- a/Resources/Prototypes/Entities/Structures/cargo_telepad.yml +++ b/Resources/Prototypes/Entities/Structures/cargo_telepad.yml @@ -47,3 +47,5 @@ board: CargoTelepadMachineCircuitboard - type: Appearance - type: CollideOnAnchor + - type: NameIdentifier + group: CargoTelepads diff --git a/Resources/Prototypes/name_identifier_groups.yml b/Resources/Prototypes/name_identifier_groups.yml index 82c2f3bce9a..4823e31f55d 100644 --- a/Resources/Prototypes/name_identifier_groups.yml +++ b/Resources/Prototypes/name_identifier_groups.yml @@ -37,3 +37,9 @@ id: Bounty minValue: 0 maxValue: 999 + +- type: nameIdentifierGroup + id: CargoTelepads + prefix: TELE + minValue: 0 + maxValue: 999 From 0c851d256d0c5b90cbe7d8c85a47cef4fcff1a51 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 23 Apr 2024 12:08:18 +0000 Subject: [PATCH 17/33] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 996b3d5d015..37b7aa84dcb 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,15 +1,4 @@ Entries: -- author: jamessimo - changes: - - message: Can now "wink" with the text emote ;) or ;] - type: Add - - message: Can now "tearfully smile" with the text emote :') and similar variants - type: Add - - message: Added more ways to "cry" with the emote :'( and similar variants - type: Tweak - id: 5929 - time: '2024-02-13T15:43:20.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25129 - author: icekot8 changes: - message: Medical berets added to MediDrobe @@ -3855,3 +3844,10 @@ id: 6428 time: '2024-04-23T11:34:09.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27229 +- author: EmoGarbage404 + changes: + - message: Fixed cargo telepads not teleporting in orders from linked consoles. + type: Fix + id: 6429 + time: '2024-04-23T12:07:12.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27255 From 10ad53df7296506485033a3dae572e4016553630 Mon Sep 17 00:00:00 2001 From: Flareguy <78941145+Flareguy@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:40:26 -0500 Subject: [PATCH 18/33] Revert "Maints Exit Sign" (#27271) Revert "Maints Exit Sign (#26831)" This reverts commit 905102064cd1311943093c9feeecca8bbd8bc358. --- .../Structures/Wallmounts/Signs/signs.yml | 12 ------ .../Wallmounts/signs.rsi/direction_exit.png | Bin 15393 -> 0 bytes .../signs.rsi/direction_exit_unshaded.png | Bin 15747 -> 0 bytes .../Structures/Wallmounts/signs.rsi/meta.json | 38 +----------------- 4 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 Resources/Textures/Structures/Wallmounts/signs.rsi/direction_exit.png delete mode 100644 Resources/Textures/Structures/Wallmounts/signs.rsi/direction_exit_unshaded.png diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml index 18af2ec06cf..9aac49a6bd7 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml @@ -169,18 +169,6 @@ - type: Sprite state: direction_library -- type: entity - parent: BaseSignDirectional - id: SignDirectionalExit - name: exit sign - description: A directional sign pointing toward the nearest exit. - components: - - type: Sprite - layers: - - state: direction_exit - - state: direction_exit_unshaded - shader: unshaded - - type: entity parent: BaseSignDirectional id: SignDirectionalMed diff --git a/Resources/Textures/Structures/Wallmounts/signs.rsi/direction_exit.png b/Resources/Textures/Structures/Wallmounts/signs.rsi/direction_exit.png deleted file mode 100644 index d2b60cb5fed39d1feea375101b56198381c7aa59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15393 zcmeI3Yitx%6o7BhAXZU~4~QCF7f?~AGqXFp+Z}ed#a*B^EYxX&pG#g_nfoyXI9qDn=`)r;&K4M_}ZFCJ^HS;-s8ri&n@chpQ5ibQ#A_>0M0t! zdY6GGo|*)J|AZQAG#jIHMOjZ)NQ&MJD>BIxnhijpDwC4rHfTDUVT-B-o!{^7b~;og z=v?55a?w;6wyHHt(y(F4yqLVCP4+9!s!(|#BO(V$XiAPuGNBn_Cg{xi713{Nm~}d` zQ_QxYbEXy0(HO0Bg!MFZcq+V%%z1qdpTC0l_nZjN!eMS&9qmm~jj zhRV^eKw625^^w{6a7YR|TTL@1vTSE(XGN#GLQl7_yx;Fgb69?*X1(1EI-ORa!YjY8WU3HIGxF>ZMK&X)SiXW|OBpG0!6wz7 zPGpy+$Sh32B-Bg;+3_XqQmwkF8?E{XAq(me1EVvEMhme^t~Z%18QL&sbsz+Jq$Mql zSXT@T#eW3bLBvTrd7yDeBA4L}9dO9hY zAvGzrKsKed1lZ!BZDt{>#IT;w(`Z>R8*wg!0n(9uCfLd|xl zl$180RewR}U;!3dTA^cEHrYBBCE4n2A=xSqs07#{qs3(vEc0gS@Z%gEf}DSw`N%Ok zTcJiOubivwtdX4VI$$hSc%Tj$w3F^*4@T zqz?7}9YZmT`+NkmnQGD^x;4eLKOG)7d^hYhQrc!j(Tt|)1^a3n*UB5#;GD%)*Ywgz*f=%x?dZ?c6O z&SB+Q=wkookK&I0O|O_Q8UzDE7AY<=A52SeVL-?t#YN_WX(=uY2w9}K$b2v@#f1SO zixd}`52mHKFd$@+;v)0Gv=kQxge+2AWImXd;=+KCMT(2e2h&nq7!a~Zagq68T8aw; zLKZ15G9OG!abZBnBE?1KgJ~%)3Vor!L$?? z281k9Tx33&mg2&IkVT4%%m>p_To@3tNO6(*U|Na`140%lE;1iXOL1X9$Rfo>=7VV| zE({1+q`1g@FfGM}0U?V-T;;__IH87~+w4RSXdYdEeG)y~>5yycqW~4t z^nDC~b`F4V7Xu)!17M>5NYk6M0Vvy68<`o)^qqLJO1qc4;H|0!hh}hldQM*aw448V z?8~RsKeWc*FhyIlqhZB4b8m5+ad$({yN|bBws_B!+kOvD+q*My*ZitOUmsk0!=m0R z;!CglayB@<+ICIY{N2YZrtkMIopIaYo1(weerURX-=sd)Tb}8f-y?kX^^3>D3wCd~ zO#k)$?_TeizwEr~A5WeQ-dQ!K>xOl&nD4FH`}2;&FKpj-e^2)!7A!yfi)%~wCC2mD z>~C5fgX-za-a7QviR1FgN5((9?Rfaqa}R91=%YtluiA8={e!oU^_)7nZ_VkB#1)rf(bY%&FVQ&ezzHYF=vz@lTIL(@a9i|@VfqBS+- zTIh*#(NrDi)arZFV1937tJ2%01XS1ba8)oX!2n5M$#gcE&`l{Da^>Pm@YrrnM_T_U1Ov>SUwO4u$;gO0t0(6W{+;kSw=S}=7SXD zM1ZNJwUnh9I&H_5V@9_Xa=Gk8rLAyX$y6zkZWh=5E%}B0!B%FQ&XK{qW~S3pA~3>#`RO_ewx1I6Qu zGd!mR7#|S5jNcPb89wgzDSputi~H3a#Awv%J|J|&P`d5a0z>Bui)tt^eEGCEAb9+$ z8ermHo@00(04An-eM~&&=TzVq+_5-cOymeQAW>6VgLO|%IQrPvqQYc8cTDugVRV`E zco~rsVG14yjN(;zS@g>uUWBCbC@O>*kg8rY;cV@3%wjl~bCqpASk}L_C?vF8g-Xe3 z6WHr7fv8lK!e>|{s$wMUy1!|VzJH+tf4{su|k6N7uEtfSNCAuAJA5wGx`2) zb{$Q0=lc5u4sI-O{}x=GGV0oncXi6BYuR&wu32HBkPT^ec-hZB`5W_m_?QmDUXRD^ z39@K2>d~xaL82|9!8@pF-wC{8!3Py8I<^ny=nx8y`Jr+=Q0J;=z7;;M!+#2F>94|o z!dkljT+f5@6_aHChBKK{o+a_VdrBbM{GEJPJjC1vEA#l-#oi%($Ed#Uz#@8cyR0CMV%)vpKkg5 z^zrVaZ=N`qk=twUQePRz4?TbRobmXvrtyzB&Z__6oT_Du={sj%-85yzfHG!^6+N5uWkPp2hUW&vtF!vMsc9 Date: Tue, 23 Apr 2024 19:42:34 -0700 Subject: [PATCH 19/33] Do-afters belonging to other players are now shaded (#27273) Make do-afters belonging to other players shaded Co-authored-by: Plykiya --- Content.Client/DoAfter/DoAfterOverlay.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Content.Client/DoAfter/DoAfterOverlay.cs b/Content.Client/DoAfter/DoAfterOverlay.cs index 2e23dd44cac..45981159f06 100644 --- a/Content.Client/DoAfter/DoAfterOverlay.cs +++ b/Content.Client/DoAfter/DoAfterOverlay.cs @@ -21,7 +21,7 @@ public sealed class DoAfterOverlay : Overlay private readonly ProgressColorSystem _progressColor; private readonly Texture _barTexture; - private readonly ShaderInstance _shader; + private readonly ShaderInstance _unshadedShader; /// /// Flash time for cancelled DoAfters @@ -45,7 +45,7 @@ public DoAfterOverlay(IEntityManager entManager, IPrototypeManager protoManager, var sprite = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/progress_bar.rsi"), "icon"); _barTexture = _entManager.EntitySysManager.GetEntitySystem().Frame0(sprite); - _shader = protoManager.Index("unshaded").Instance(); + _unshadedShader = protoManager.Index("unshaded").Instance(); } protected override void Draw(in OverlayDrawArgs args) @@ -58,7 +58,6 @@ protected override void Draw(in OverlayDrawArgs args) const float scale = 1f; var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale)); var rotationMatrix = Matrix3.CreateRotation(-rotation); - handle.UseShader(_shader); var curTime = _timing.CurTime; @@ -79,6 +78,13 @@ protected override void Draw(in OverlayDrawArgs args) if (!bounds.Contains(worldPosition)) continue; + // shades the do-after bar if the do-after bar belongs to other players + // does not shade do-afters belonging to the local player + if (uid != localEnt) + handle.UseShader(null); + else + handle.UseShader(_unshadedShader); + // If the entity is paused, we will draw the do-after as it was when the entity got paused. var meta = metaQuery.GetComponent(uid); var time = meta.EntityPaused From 829b453e3ca786ae09edb0ad1d23f6a4f642b214 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 24 Apr 2024 02:43:41 +0000 Subject: [PATCH 20/33] 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 37b7aa84dcb..31587e20749 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: icekot8 - changes: - - message: Medical berets added to MediDrobe - type: Add - id: 5930 - time: '2024-02-13T21:37:23.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25176 - author: PoorMansDreams changes: - message: Alternate ammo now has the proper appearance when spent. @@ -3851,3 +3844,11 @@ id: 6429 time: '2024-04-23T12:07:12.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27255 +- author: Plykiya + changes: + - message: Do-after bars of other players are now shaded and harder to see in the + dark. + type: Tweak + id: 6430 + time: '2024-04-24T02:42:34.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27273 From 49f1c8b197687024d572e61807050fea42518b35 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Wed, 24 Apr 2024 01:35:16 -0400 Subject: [PATCH 21/33] fix analysis console not saving bias button state (#27275) --- Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs index 2acf35da25b..ecbb8e96620 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs @@ -73,6 +73,7 @@ public void SetButtonsDisabled(AnalysisConsoleUpdateState state) { ScanButton.Disabled = !state.CanScan; PrintButton.Disabled = !state.CanPrint; + DownBiasButton.Pressed = state.IsTraversalDown; var disabled = !state.ServerConnected || !state.CanScan || state.PointAmount <= 0; From 91aa16f08a642ae463bcd875393dd47f1657813d Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Wed, 24 Apr 2024 17:38:43 +1200 Subject: [PATCH 22/33] Add NukeOps Test (#27207) * Add NukeOps Test * Update EvacShuttleTest to also check mapinit * Update RuleMaxTimeRestartTest * Fix cvar cleanup * A * Revert some changes * comments * Add MappingTests * Finally fix the test * A --- .../Pair/TestPair.Timing.cs | 19 +- Content.IntegrationTests/PoolManager.Cvars.cs | 1 + .../Tests/GameRules/NukeOpsTest.cs | 187 ++++++++++++++++++ .../Tests/GameRules/RuleMaxTimeRestartTest.cs | 9 + .../Interaction/InteractionTest.Helpers.cs | 7 +- .../Tests/Interaction/InteractionTest.cs | 1 - .../Tests/Mapping/MappingTests.cs | 102 ++++++++++ .../Tests/Station/EvacShuttleTest.cs | 34 ++-- .../Systems/CriminalRecordsConsoleSystem.cs | 2 + .../GameTicking/GameTicker.GamePreset.cs | 2 +- .../GameTicking/GameTicker.RoundFlow.cs | 2 +- .../Rules/Components/NukeopsRuleComponent.cs | 11 +- .../Rules/MaxTimeRestartRuleSystem.cs | 2 + Content.Server/Mapping/MappingCommand.cs | 2 +- .../Systems/StationRecordsSystem.cs | 2 +- Content.Shared/Roles/SharedRoleSystem.cs | 3 + 16 files changed, 355 insertions(+), 31 deletions(-) create mode 100644 Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs create mode 100644 Content.IntegrationTests/Tests/Mapping/MappingTests.cs diff --git a/Content.IntegrationTests/Pair/TestPair.Timing.cs b/Content.IntegrationTests/Pair/TestPair.Timing.cs index 3487ea68010..e0859660d42 100644 --- a/Content.IntegrationTests/Pair/TestPair.Timing.cs +++ b/Content.IntegrationTests/Pair/TestPair.Timing.cs @@ -1,5 +1,4 @@ #nullable enable -using Robust.Shared.Timing; namespace Content.IntegrationTests.Pair; @@ -19,6 +18,22 @@ public async Task RunTicksSync(int ticks) } } + /// + /// Convert a time interval to some number of ticks. + /// + public int SecondsToTicks(float seconds) + { + return (int) Math.Ceiling(seconds / Server.Timing.TickPeriod.TotalSeconds); + } + + /// + /// Run the server & client in sync for some amount of time + /// + public async Task RunSeconds(float seconds) + { + await RunTicksSync(SecondsToTicks(seconds)); + } + /// /// Runs the server-client pair in sync, but also ensures they are both idle each tick. /// @@ -59,4 +74,4 @@ public async Task SyncTicks(int targetDelta = 1) delta = cTick - sTick; Assert.That(delta, Is.EqualTo(targetDelta)); } -} \ No newline at end of file +} diff --git a/Content.IntegrationTests/PoolManager.Cvars.cs b/Content.IntegrationTests/PoolManager.Cvars.cs index 327ec627f52..d39c7284d06 100644 --- a/Content.IntegrationTests/PoolManager.Cvars.cs +++ b/Content.IntegrationTests/PoolManager.Cvars.cs @@ -32,6 +32,7 @@ private static readonly (string cvar, string value)[] TestCvars = (CCVars.GameLobbyEnabled.Name, "false"), (CCVars.ConfigPresetDevelopment.Name, "false"), (CCVars.AdminLogsEnabled.Name, "false"), + (CCVars.AutosaveEnabled.Name, "false"), (CVars.NetBufferSize.Name, "0") }; diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs new file mode 100644 index 00000000000..5833db0a109 --- /dev/null +++ b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs @@ -0,0 +1,187 @@ +#nullable enable +using System.Linq; +using Content.Server.Body.Components; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Presets; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Mind; +using Content.Server.Pinpointer; +using Content.Server.Roles; +using Content.Server.Shuttles.Components; +using Content.Server.Station.Components; +using Content.Shared.CCVar; +using Content.Shared.Damage; +using Content.Shared.FixedPoint; +using Content.Shared.GameTicking; +using Content.Shared.Hands.Components; +using Content.Shared.Inventory; +using Content.Shared.NPC.Systems; +using Content.Shared.NukeOps; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Map.Components; + +namespace Content.IntegrationTests.Tests.GameRules; + +[TestFixture] +public sealed class NukeOpsTest +{ + /// + /// Check that a nuke ops game mode can start without issue. I.e., that the nuke station and such all get loaded. + /// + [Test] + public async Task TryStopNukeOpsFromConstantlyFailing() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings + { + Dirty = true, + DummyTicker = false, + Connected = true, + InLobby = true + }); + + var server = pair.Server; + var client = pair.Client; + var entMan = server.EntMan; + var mapSys = server.System(); + var ticker = server.System(); + var mindSys = server.System(); + var roleSys = server.System(); + var invSys = server.System(); + var factionSys = server.System(); + + Assert.That(server.CfgMan.GetCVar(CCVars.GridFill), Is.False); + server.CfgMan.SetCVar(CCVars.GridFill, true); + + // Initially in the lobby + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); + Assert.That(client.AttachedEntity, Is.Null); + Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay)); + + // There are no grids or maps + Assert.That(entMan.Count(), Is.Zero); + Assert.That(entMan.Count(), Is.Zero); + Assert.That(entMan.Count(), Is.Zero); + Assert.That(entMan.Count(), Is.Zero); + Assert.That(entMan.Count(), Is.Zero); + + // And no nukie related components + Assert.That(entMan.Count(), Is.Zero); + Assert.That(entMan.Count(), Is.Zero); + Assert.That(entMan.Count(), Is.Zero); + Assert.That(entMan.Count(), Is.Zero); + Assert.That(entMan.Count(), Is.Zero); + + // Ready up and start nukeops + await pair.WaitClientCommand("toggleready True"); + Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.ReadyToPlay)); + await pair.WaitCommand("forcepreset Nukeops"); + await pair.RunTicksSync(10); + + // Game should have started + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound)); + Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.JoinedGame)); + Assert.That(client.EntMan.EntityExists(client.AttachedEntity)); + var player = pair.Player!.AttachedEntity!.Value; + Assert.That(entMan.EntityExists(player)); + + // Maps now exist + Assert.That(entMan.Count(), Is.GreaterThan(0)); + Assert.That(entMan.Count(), Is.GreaterThan(0)); + Assert.That(entMan.Count(), Is.EqualTo(2)); // The main station & nukie station + Assert.That(entMan.Count(), Is.GreaterThan(3)); // Each station has at least 1 grid, plus some shuttles + Assert.That(entMan.Count(), Is.EqualTo(1)); + + // And we now have nukie related components + Assert.That(entMan.Count(), Is.EqualTo(1)); + Assert.That(entMan.Count(), Is.EqualTo(1)); + Assert.That(entMan.Count(), Is.EqualTo(1)); + Assert.That(entMan.Count(), Is.EqualTo(1)); + + // The player entity should be the nukie commander + var mind = mindSys.GetMind(player)!.Value; + Assert.That(entMan.HasComponent(player)); + Assert.That(roleSys.MindIsAntagonist(mind)); + Assert.That(roleSys.MindHasRole(mind)); + Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True); + Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False); + + var roles = roleSys.MindGetAllRoles(mind); + var cmdRoles = roles.Where(x => x.Prototype == "NukeopsCommander" && x.Component is NukeopsRoleComponent); + Assert.That(cmdRoles.Count(), Is.EqualTo(1)); + + // The game rule exists, and all the stations/shuttles/maps are properly initialized + var rule = entMan.AllComponents().Single().Component; + Assert.That(entMan.EntityExists(rule.NukieOutpost)); + Assert.That(entMan.EntityExists(rule.NukieShuttle)); + Assert.That(entMan.EntityExists(rule.TargetStation)); + + Assert.That(entMan.HasComponent(rule.NukieOutpost)); + Assert.That(entMan.HasComponent(rule.NukieShuttle)); + + Assert.That(entMan.HasComponent(rule.NukieOutpost)); + Assert.That(entMan.HasComponent(rule.TargetStation)); + + var nukieStation = entMan.GetComponent(rule.NukieOutpost!.Value); + Assert.That(entMan.EntityExists(nukieStation.Station)); + Assert.That(nukieStation.Station, Is.Not.EqualTo(rule.TargetStation)); + + Assert.That(server.MapMan.MapExists(rule.NukiePlanet)); + var nukieMap = mapSys.GetMap(rule.NukiePlanet!.Value); + + var targetStation = entMan.GetComponent(rule.TargetStation!.Value); + var targetGrid = targetStation.Grids.First(); + var targetMap = entMan.GetComponent(targetGrid).MapUid!.Value; + Assert.That(targetMap, Is.Not.EqualTo(nukieMap)); + + Assert.That(entMan.GetComponent(player).MapUid, Is.EqualTo(nukieMap)); + Assert.That(entMan.GetComponent(rule.NukieOutpost!.Value).MapUid, Is.EqualTo(nukieMap)); + Assert.That(entMan.GetComponent(rule.NukieShuttle!.Value).MapUid, Is.EqualTo(nukieMap)); + + // The maps are all map-initialized, including the player + // Yes, this is necessary as this has repeatedly been broken somehow. + Assert.That(mapSys.IsInitialized(nukieMap)); + Assert.That(mapSys.IsInitialized(targetMap)); + Assert.That(mapSys.IsPaused(nukieMap), Is.False); + Assert.That(mapSys.IsPaused(targetMap), Is.False); + + EntityLifeStage LifeStage(EntityUid? uid) => entMan.GetComponent(uid!.Value).EntityLifeStage; + Assert.That(LifeStage(player), Is.GreaterThan(EntityLifeStage.Initialized)); + Assert.That(LifeStage(nukieMap), Is.GreaterThan(EntityLifeStage.Initialized)); + Assert.That(LifeStage(targetMap), Is.GreaterThan(EntityLifeStage.Initialized)); + Assert.That(LifeStage(rule.NukieOutpost), Is.GreaterThan(EntityLifeStage.Initialized)); + Assert.That(LifeStage(rule.NukieShuttle), Is.GreaterThan(EntityLifeStage.Initialized)); + Assert.That(LifeStage(rule.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized)); + + // Make sure the player has hands. We've had fucking disarmed nukies before. + Assert.That(entMan.HasComponent(player)); + Assert.That(entMan.GetComponent(player).Hands.Count, Is.GreaterThan(0)); + + // While we're at it, lets make sure they aren't naked. I don't know how many inventory slots all mobs will be + // likely to have in the future. But nukies should probably have at least 3 slots with something in them. + var enumerator = invSys.GetSlotEnumerator(player); + int total = 0; + while (enumerator.NextItem(out _)) + { + total++; + } + Assert.That(total, Is.GreaterThan(3)); + + // Finally lets check the nukie commander passed basic training and figured out how to breathe. + var totalSeconds = 30; + var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds); + int increment = 5; + var resp = entMan.GetComponent(player); + var damage = entMan.GetComponent(player); + for (var tick = 0; tick < totalTicks; tick += increment) + { + await pair.RunTicksSync(increment); + Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold)); + Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero)); + } + + ticker.SetGamePreset((GamePresetPrototype?)null); + server.CfgMan.SetCVar(CCVars.GridFill, false); + await pair.CleanReturnAsync(); + } +} diff --git a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs index 1e3f9c9854f..0707bd64c6f 100644 --- a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs @@ -19,6 +19,9 @@ public async Task RestartTest() await using var pair = await PoolManager.GetServerClient(new PoolSettings { InLobby = true }); var server = pair.Server; + Assert.That(server.EntMan.Count(), Is.Zero); + Assert.That(server.EntMan.Count(), Is.Zero); + var entityManager = server.ResolveDependency(); var sGameTicker = server.ResolveDependency().GetEntitySystem(); var sGameTiming = server.ResolveDependency(); @@ -26,6 +29,9 @@ public async Task RestartTest() sGameTicker.StartGameRule("MaxTimeRestart", out var ruleEntity); Assert.That(entityManager.TryGetComponent(ruleEntity, out var maxTime)); + Assert.That(server.EntMan.Count(), Is.EqualTo(1)); + Assert.That(server.EntMan.Count(), Is.EqualTo(1)); + await server.WaitAssertion(() => { Assert.That(sGameTicker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); @@ -33,6 +39,9 @@ await server.WaitAssertion(() => sGameTicker.StartRound(); }); + Assert.That(server.EntMan.Count(), Is.EqualTo(1)); + Assert.That(server.EntMan.Count(), Is.EqualTo(1)); + await server.WaitAssertion(() => { Assert.That(sGameTicker.RunLevel, Is.EqualTo(GameRunLevel.InRound)); diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs index 480fd9cde6f..d45290c8664 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs @@ -767,14 +767,9 @@ protected async Task RunTicks(int ticks) await Pair.RunTicksSync(ticks); } - protected int SecondsToTicks(float seconds) - { - return (int) Math.Ceiling(seconds / TickPeriod); - } - protected async Task RunSeconds(float seconds) { - await RunTicks(SecondsToTicks(seconds)); + await Pair.RunSeconds(seconds); } #endregion diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index a4ed31e9983..42f64b344cd 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -12,7 +12,6 @@ using Content.Shared.DoAfter; using Content.Shared.Hands.Components; using Content.Shared.Interaction; -using Content.Server.Item; using Content.Shared.Mind; using Content.Shared.Players; using Robust.Client.Input; diff --git a/Content.IntegrationTests/Tests/Mapping/MappingTests.cs b/Content.IntegrationTests/Tests/Mapping/MappingTests.cs new file mode 100644 index 00000000000..287e30eb8b1 --- /dev/null +++ b/Content.IntegrationTests/Tests/Mapping/MappingTests.cs @@ -0,0 +1,102 @@ +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; + +namespace Content.IntegrationTests.Tests.Mapping; + +[TestFixture] +public sealed class MappingTests +{ + /// + /// Checks that the mapping command creates paused & uninitialized maps. + /// + [Test] + public async Task MappingTest() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings {Dirty = true, Connected = true, DummyTicker = false}); + + var server = pair.Server; + var entMan = server.EntMan; + var mapSys = server.System(); + + await pair.RunTicksSync(5); + var mapId = 1; + while (mapSys.MapExists(new(mapId))) + { + mapId++; + } + + await pair.WaitClientCommand($"mapping {mapId}"); + var map = mapSys.GetMap(new MapId(mapId)); + + var mapXform = server.Transform(map); + Assert.That(mapXform.MapUid, Is.EqualTo(map)); + Assert.That(mapXform.MapID, Is.EqualTo(new MapId(mapId))); + + var xform = server.Transform(pair.Player!.AttachedEntity!.Value); + + Assert.That(xform.MapUid, Is.EqualTo(map)); + Assert.That(mapSys.IsInitialized(map), Is.False); + Assert.That(mapSys.IsPaused(map), Is.True); + Assert.That(server.MetaData(map).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized)); + Assert.That(server.MetaData(map).EntityPaused, Is.True); + + // Spawn a new entity + EntityUid ent = default; + await server.WaitPost(() => + { + ent = entMan.Spawn(null, new MapCoordinates(default, new(mapId))); + }); + await pair.RunTicksSync(5); + Assert.That(server.MetaData(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized)); + Assert.That(server.MetaData(ent).EntityPaused, Is.True); + + // Save the map + var file = $"{nameof(MappingTest)}.yml"; + await pair.WaitClientCommand($"savemap {mapId} {file}"); + + // Mapinitialize it + await pair.WaitClientCommand($"mapinit {mapId}"); + Assert.That(mapSys.IsInitialized(map), Is.True); + Assert.That(mapSys.IsPaused(map), Is.False); + Assert.That(server.MetaData(map).EntityLifeStage, Is.EqualTo(EntityLifeStage.MapInitialized)); + Assert.That(server.MetaData(map).EntityPaused, Is.False); + Assert.That(server.MetaData(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.MapInitialized)); + Assert.That(server.MetaData(ent).EntityPaused, Is.False); + + await server.WaitPost(() => entMan.DeleteEntity(map)); + + // Load the saved map + mapId++; + while (mapSys.MapExists(new(mapId))) + { + mapId++; + } + + await pair.WaitClientCommand($"mapping {mapId} {file}"); + map = mapSys.GetMap(new MapId(mapId)); + + // And it should all be paused and un-initialized + xform = server.Transform(pair.Player!.AttachedEntity!.Value); + Assert.That(xform.MapUid, Is.EqualTo(map)); + Assert.That(mapSys.IsInitialized(map), Is.False); + Assert.That(mapSys.IsPaused(map), Is.True); + Assert.That(server.MetaData(map).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized)); + Assert.That(server.MetaData(map).EntityPaused, Is.True); + + mapXform = server.Transform(map); + Assert.That(mapXform.MapUid, Is.EqualTo(map)); + Assert.That(mapXform.MapID, Is.EqualTo(new MapId(mapId))); + Assert.That(mapXform.ChildCount, Is.EqualTo(2)); + + mapXform.ChildEnumerator.MoveNext(out ent); + if (ent == pair.Player.AttachedEntity) + mapXform.ChildEnumerator.MoveNext(out ent); + + Assert.That(server.MetaData(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized)); + Assert.That(server.MetaData(ent).EntityPaused, Is.True); + + await server.WaitPost(() => entMan.DeleteEntity(map)); + await pair.CleanReturnAsync(); + } +} diff --git a/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs b/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs index 5750be09c24..532e481ac29 100644 --- a/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs +++ b/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs @@ -28,10 +28,11 @@ public async Task EmergencyEvacTest() // Dummy ticker tests should not have centcomm Assert.That(entMan.Count(), Is.Zero); - var shuttleEnabled = pair.Server.CfgMan.GetCVar(CCVars.EmergencyShuttleEnabled); - pair.Server.CfgMan.SetCVar(CCVars.GameMap, "Saltern"); - pair.Server.CfgMan.SetCVar(CCVars.GameDummyTicker, false); + Assert.That(pair.Server.CfgMan.GetCVar(CCVars.GridFill), Is.False); pair.Server.CfgMan.SetCVar(CCVars.EmergencyShuttleEnabled, true); + pair.Server.CfgMan.SetCVar(CCVars.GameDummyTicker, false); + var gameMap = pair.Server.CfgMan.GetCVar(CCVars.GameMap); + pair.Server.CfgMan.SetCVar(CCVars.GameMap, "Saltern"); await server.WaitPost(() => ticker.RestartRound()); await pair.RunTicksSync(25); @@ -71,6 +72,20 @@ public async Task EmergencyEvacTest() Assert.That(shuttleXform.MapUid, Is.Not.Null); Assert.That(shuttleXform.MapUid, Is.EqualTo(centcommMap)); + // All of these should have been map-initialized. + var mapSys = entMan.System(); + Assert.That(mapSys.IsInitialized(centcommMap), Is.True); + Assert.That(mapSys.IsInitialized(salternXform.MapUid), Is.True); + Assert.That(mapSys.IsPaused(centcommMap), Is.False); + Assert.That(mapSys.IsPaused(salternXform.MapUid!.Value), Is.False); + + EntityLifeStage LifeStage(EntityUid uid) => entMan.GetComponent(uid).EntityLifeStage; + Assert.That(LifeStage(saltern), Is.EqualTo(EntityLifeStage.MapInitialized)); + Assert.That(LifeStage(shuttle), Is.EqualTo(EntityLifeStage.MapInitialized)); + Assert.That(LifeStage(centcomm), Is.EqualTo(EntityLifeStage.MapInitialized)); + Assert.That(LifeStage(centcommMap), Is.EqualTo(EntityLifeStage.MapInitialized)); + Assert.That(LifeStage(salternXform.MapUid.Value), Is.EqualTo(EntityLifeStage.MapInitialized)); + // Set up shuttle timing var evacSys = server.System(); evacSys.TransitTime = ShuttleSystem.DefaultTravelTime; // Absolute minimum transit time, so the test has to run for at least this long @@ -78,19 +93,15 @@ public async Task EmergencyEvacTest() var dockTime = server.CfgMan.GetCVar(CCVars.EmergencyShuttleDockTime); server.CfgMan.SetCVar(CCVars.EmergencyShuttleDockTime, 2); - async Task RunSeconds(float seconds) - { - await pair.RunTicksSync((int) Math.Ceiling(seconds / server.Timing.TickPeriod.TotalSeconds)); - } // Call evac shuttle. await pair.WaitCommand("callshuttle 0:02"); - await RunSeconds(3); + await pair.RunSeconds(3); // Shuttle should have arrived on the station Assert.That(shuttleXform.MapUid, Is.EqualTo(salternXform.MapUid)); - await RunSeconds(2); + await pair.RunSeconds(2); // Shuttle should be FTLing back to centcomm Assert.That(entMan.Count(), Is.EqualTo(1)); @@ -101,14 +112,15 @@ async Task RunSeconds(float seconds) Assert.That(shuttleXform.MapUid, Is.EqualTo(ftl.Owner)); // Shuttle should have arrived at centcomm - await RunSeconds(ShuttleSystem.DefaultTravelTime); + await pair.RunSeconds(ShuttleSystem.DefaultTravelTime); Assert.That(shuttleXform.MapUid, Is.EqualTo(centcommMap)); // Round should be ending now Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PostRound)); server.CfgMan.SetCVar(CCVars.EmergencyShuttleDockTime, dockTime); - pair.Server.CfgMan.SetCVar(CCVars.EmergencyShuttleEnabled, shuttleEnabled); + pair.Server.CfgMan.SetCVar(CCVars.EmergencyShuttleEnabled, false); + pair.Server.CfgMan.SetCVar(CCVars.GameMap, gameMap); await pair.CleanReturnAsync(); } } diff --git a/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs b/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs index 4290726cc40..fe53ea268c7 100644 --- a/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs +++ b/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs @@ -253,6 +253,8 @@ public void CheckNewIdentity(EntityUid uid) { var name = Identity.Name(uid, EntityManager); var xform = Transform(uid); + + // TODO use the entity's station? Not the station of the map that it happens to currently be on? var station = _station.GetStationInMap(xform.MapID); if (station != null && _stationRecords.GetRecordByName(station.Value, name) is { } id) diff --git a/Content.Server/GameTicking/GameTicker.GamePreset.cs b/Content.Server/GameTicking/GameTicker.GamePreset.cs index b97a16ab993..a1946d34a0a 100644 --- a/Content.Server/GameTicking/GameTicker.GamePreset.cs +++ b/Content.Server/GameTicking/GameTicker.GamePreset.cs @@ -100,7 +100,7 @@ private void InitializeGamePreset() SetGamePreset(LobbyEnabled ? _configurationManager.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox"); } - public void SetGamePreset(GamePresetPrototype preset, bool force = false) + public void SetGamePreset(GamePresetPrototype? preset, bool force = false) { // Do nothing if this game ticker is a dummy! if (DummyTicker) diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index 202daf256d2..792d838169c 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -165,7 +165,7 @@ public IReadOnlyList LoadGameMap(GameMapPrototype map, MapId targetMa var gridIds = _map.LoadMap(targetMapId, ev.GameMap.MapPath.ToString(), ev.Options); - _metaData.SetEntityName(_mapManager.GetMapEntityId(targetMapId), "Station map"); + _metaData.SetEntityName(_mapManager.GetMapEntityId(targetMapId), $"station map - {map.MapName}"); var gridUids = gridIds.ToList(); RaiseLocalEvent(new PostGameMapLoad(map, targetMapId, gridUids, stationName)); diff --git a/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs index c66a9d12a1c..8f11e70560f 100644 --- a/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs @@ -6,11 +6,7 @@ using Content.Shared.Roles; using Robust.Shared.Map; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Utility; - namespace Content.Server.GameTicking.Rules.Components; @@ -116,13 +112,14 @@ public sealed partial class NukeopsRuleComponent : Component [DataField] public List WinConditions = new (); - public MapId? NukiePlanet; - + // TODO full game save // TODO: use components, don't just cache entity UIDs // There have been (and probably still are) bugs where these refer to deleted entities from old rounds. + // Whenever this gets fixed, update NukiesTest. public EntityUid? NukieOutpost; public EntityUid? NukieShuttle; public EntityUid? TargetStation; + public MapId? NukiePlanet; /// /// Data to be used in for an operative once the Mind has been added. @@ -131,7 +128,7 @@ public sealed partial class NukeopsRuleComponent : Component public Dictionary OperativeMindPendingData = new(); [DataField(required: true)] - public ProtoId Faction = default!; + public ProtoId Faction; [DataField] public NukeopSpawnPreset CommanderSpawnDetails = new() { AntagRoleProto = "NukeopsCommander", GearProto = "SyndicateCommanderGearFull", NamePrefix = "nukeops-role-commander", NameList = "SyndicateNamesElite" }; diff --git a/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs b/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs index e792a004df5..2522ebb53b5 100644 --- a/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs @@ -33,6 +33,7 @@ protected override void Ended(EntityUid uid, MaxTimeRestartRuleComponent compone public void RestartTimer(MaxTimeRestartRuleComponent component) { + // TODO FULL GAME SAVE component.TimerCancel.Cancel(); component.TimerCancel = new CancellationTokenSource(); Timer.Spawn(component.RoundMaxTime, () => TimerFired(component), component.TimerCancel.Token); @@ -49,6 +50,7 @@ private void TimerFired(MaxTimeRestartRuleComponent component) _chatManager.DispatchServerAnnouncement(Loc.GetString("rule-restarting-in-seconds",("seconds", (int) component.RoundEndDelay.TotalSeconds))); + // TODO FULL GAME SAVE Timer.Spawn(component.RoundEndDelay, () => GameTicker.RestartRound()); } diff --git a/Content.Server/Mapping/MappingCommand.cs b/Content.Server/Mapping/MappingCommand.cs index 08f3dcccf9f..46534f7059d 100644 --- a/Content.Server/Mapping/MappingCommand.cs +++ b/Content.Server/Mapping/MappingCommand.cs @@ -53,7 +53,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) } #if DEBUG - shell.WriteError(Loc.GetString("cmd-mapping-warning")); + shell.WriteLine(Loc.GetString("cmd-mapping-warning")); #endif MapId mapId; diff --git a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs index 67f50d7a4e1..58c4c876c50 100644 --- a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs +++ b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs @@ -211,7 +211,7 @@ public bool TryGetRecord(StationRecordKey key, [NotNullWhen(true)] out T? ent /// public uint? GetRecordByName(EntityUid station, string name, StationRecordsComponent? records = null) { - if (!Resolve(station, ref records)) + if (!Resolve(station, ref records, false)) return null; foreach (var (id, record) in GetRecordsOfType(station, records)) diff --git a/Content.Shared/Roles/SharedRoleSystem.cs b/Content.Shared/Roles/SharedRoleSystem.cs index e8053e4c678..c25ac1968d5 100644 --- a/Content.Shared/Roles/SharedRoleSystem.cs +++ b/Content.Shared/Roles/SharedRoleSystem.cs @@ -137,11 +137,13 @@ public bool MindTryRemoveRole(EntityUid mindId) where T : IComponent public bool MindHasRole(EntityUid mindId) where T : IComponent { + DebugTools.Assert(HasComp(mindId)); return HasComp(mindId); } public List MindGetAllRoles(EntityUid mindId) { + DebugTools.Assert(HasComp(mindId)); var ev = new MindGetAllRolesEvent(new List()); RaiseLocalEvent(mindId, ref ev); return ev.Roles; @@ -152,6 +154,7 @@ public bool MindIsAntagonist(EntityUid? mindId) if (mindId == null) return false; + DebugTools.Assert(HasComp(mindId)); var ev = new MindIsAntagonistEvent(); RaiseLocalEvent(mindId.Value, ref ev); return ev.IsAntagonist; From 480d26aba64865f1460c4ca82b8d341d98268670 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Wed, 24 Apr 2024 09:02:43 -0400 Subject: [PATCH 23/33] Fix climbing and bonking simultaneously (#27268) * Properly network ClumsyComponent * Fix being able to climb and bonk at the same time. Also properly subscribe to AttemptClimbEvent by-ref. * Update Content.Shared/Interaction/Components/ClumsyComponent.cs --- Content.Shared/Climbing/Systems/BonkSystem.cs | 11 ++++--- .../Climbing/Systems/ClimbSystem.cs | 3 +- .../Interaction/Components/ClumsyComponent.cs | 30 ++++++++++--------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Content.Shared/Climbing/Systems/BonkSystem.cs b/Content.Shared/Climbing/Systems/BonkSystem.cs index ea4e04c621a..f59fe925736 100644 --- a/Content.Shared/Climbing/Systems/BonkSystem.cs +++ b/Content.Shared/Climbing/Systems/BonkSystem.cs @@ -107,17 +107,16 @@ private bool TryStartBonk(EntityUid uid, EntityUid user, EntityUid climber, Bonk var doAfterArgs = new DoAfterArgs(EntityManager, user, bonkableComponent.BonkDelay, new BonkDoAfterEvent(), uid, target: uid, used: climber) { BreakOnMove = true, - BreakOnDamage = true + BreakOnDamage = true, + DuplicateCondition = DuplicateConditions.SameTool | DuplicateConditions.SameTarget }; - _doAfter.TryStartDoAfter(doAfterArgs); - - return true; + return _doAfter.TryStartDoAfter(doAfterArgs); } - private void OnAttemptClimb(EntityUid uid, BonkableComponent component, AttemptClimbEvent args) + private void OnAttemptClimb(EntityUid uid, BonkableComponent component, ref AttemptClimbEvent args) { - if (args.Cancelled || !HasComp(args.Climber) || !HasComp(args.User)) + if (args.Cancelled) return; if (TryStartBonk(uid, args.User, args.Climber, component)) diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index 1bdaecf730f..ac01c4e9acb 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -222,7 +222,8 @@ public bool TryClimb( used: entityToMove) { BreakOnMove = true, - BreakOnDamage = true + BreakOnDamage = true, + DuplicateCondition = DuplicateConditions.SameTool | DuplicateConditions.SameTarget }; _audio.PlayPredicted(comp.StartClimbSound, climbable, user); diff --git a/Content.Shared/Interaction/Components/ClumsyComponent.cs b/Content.Shared/Interaction/Components/ClumsyComponent.cs index 5b72fc224c8..824696c8385 100644 --- a/Content.Shared/Interaction/Components/ClumsyComponent.cs +++ b/Content.Shared/Interaction/Components/ClumsyComponent.cs @@ -1,22 +1,24 @@ using Content.Shared.Damage; using Robust.Shared.Audio; +using Robust.Shared.GameStates; -namespace Content.Shared.Interaction.Components +namespace Content.Shared.Interaction.Components; + +/// +/// A simple clumsy tag-component. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ClumsyComponent : Component { /// - /// A simple clumsy tag-component. + /// Damage dealt to a clumsy character when they try to fire a gun. /// - [RegisterComponent] - public sealed partial class ClumsyComponent : Component - { - [DataField("clumsyDamage", required: true)] - [ViewVariables(VVAccess.ReadWrite)] - public DamageSpecifier ClumsyDamage = default!; + [DataField(required: true), AutoNetworkedField] + public DamageSpecifier ClumsyDamage = default!; - /// - /// Sound to play when clumsy interactions fail - /// - [DataField("clumsySound")] - public SoundSpecifier ClumsySound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg"); - } + /// + /// Sound to play when clumsy interactions fail. + /// + [DataField] + public SoundSpecifier ClumsySound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg"); } From 4290d5f13e16f01fd6a33af897665e0ba7795b3b Mon Sep 17 00:00:00 2001 From: Alzore <140123969+Blackern5000@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:21:28 -0500 Subject: [PATCH 24/33] Make the cargo telepad T2 civilian (#26270) therearetwowaystodothis --- Resources/Prototypes/Research/civilianservices.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/Research/civilianservices.yml b/Resources/Prototypes/Research/civilianservices.yml index afb1f0ff503..b990eb6ae40 100644 --- a/Resources/Prototypes/Research/civilianservices.yml +++ b/Resources/Prototypes/Research/civilianservices.yml @@ -191,8 +191,6 @@ - WeaponSprayNozzle - ClothingBackpackWaterTank -# Tier 3 - - type: technology id: BluespaceCargoTransport name: research-technology-bluespace-cargo-transport @@ -200,11 +198,13 @@ sprite: Structures/cargo_telepad.rsi state: display discipline: CivilianServices - tier: 3 + tier: 2 cost: 15000 recipeUnlocks: - CargoTelepadMachineCircuitboard +# Tier 3 + - type: technology id: QuantumFiberWeaving name: research-technology-quantum-fiber-weaving From 7f6f9c12093fde119aed6cc458301b5b02ddc139 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 24 Apr 2024 13:22:36 +0000 Subject: [PATCH 25/33] 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 31587e20749..c4053e1b323 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: PoorMansDreams - changes: - - message: Alternate ammo now has the proper appearance when spent. - type: Fix - id: 5931 - time: '2024-02-13T21:40:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25167 - author: Agoichi changes: - message: All hoods have tag "Hides Hair" @@ -3852,3 +3845,10 @@ id: 6430 time: '2024-04-24T02:42:34.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27273 +- author: Blackern5000 + changes: + - message: The cargo telepad is now tier 2 technology rather than tier 3. + type: Tweak + id: 6431 + time: '2024-04-24T13:21:29.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26270 From 5fa424a43b9a04c4dc6839a9e5244f80d8e576e7 Mon Sep 17 00:00:00 2001 From: FungiFellow <151778459+FungiFellow@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:43:56 -0500 Subject: [PATCH 26/33] Add Truncheon to Secbelt Whitelist (#27281) * Add Truncheon to Secbelt Whitelist * Update belts.yml * Added Truncehon Tag in Tags.yml * Update security.yml * Update security.yml --- Resources/Prototypes/Entities/Clothing/Belt/belts.yml | 1 + Resources/Prototypes/Entities/Objects/Weapons/security.yml | 3 +++ Resources/Prototypes/tags.yml | 3 +++ 3 files changed, 7 insertions(+) diff --git a/Resources/Prototypes/Entities/Clothing/Belt/belts.yml b/Resources/Prototypes/Entities/Clothing/Belt/belts.yml index 0b0d9e018a4..1bae86a0222 100644 --- a/Resources/Prototypes/Entities/Clothing/Belt/belts.yml +++ b/Resources/Prototypes/Entities/Clothing/Belt/belts.yml @@ -473,6 +473,7 @@ - MagazinePistol - MagazineMagnum - CombatKnife + - Truncheon components: - Stunbaton - FlashOnTrigger diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index b9d409fb3d9..101314a1fb3 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -102,6 +102,9 @@ bluntStaminaDamageFactor: 1.5 - type: Item size: Normal + - type: Tag + tags: + - Truncheon - type: Clothing sprite: Objects/Weapons/Melee/truncheon.rsi quickEquip: false diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index f6cedeb9377..91b79f3d8e2 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -1235,6 +1235,9 @@ - type: Tag id: TrashBag +- type: Tag + id: Truncheon + - type: Tag id: Unimplantable From 38f490e5ebd3fe945464eafaa20f6fe7a8328920 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 24 Apr 2024 13:45:03 +0000 Subject: [PATCH 27/33] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c4053e1b323..c1bdcacff3a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: Agoichi - changes: - - message: All hoods have tag "Hides Hair" - type: Tweak - - message: Plague Doctor's hat and Witch hat (with red hair) have tag "Hides Hair" - type: Tweak - id: 5932 - time: '2024-02-13T21:43:19.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25142 - author: Blackern5000 changes: - message: Advanced topical meds now cost significantly less chemicals @@ -3852,3 +3843,10 @@ id: 6431 time: '2024-04-24T13:21:29.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26270 +- author: FungiFellow + changes: + - message: Truncheon now fits in SecBelt + type: Tweak + id: 6432 + time: '2024-04-24T13:43:56.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27281 From 7b90c08a2c7315b2c5a6cb17dfd82a5ac9863a53 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Wed, 24 Apr 2024 16:01:31 +0200 Subject: [PATCH 28/33] Various item status fixes/tweaks (#27267) * Always display item status panel fully Initial feedback from the UI changes seems to be that a lot of people go "why is there empty space" so let's fix that. * Fix item status middle hand being on the wrong side I think I switched this around when fixing the left/right being inverted in the UI code. * Minor status panel UI tweaks Bottom-align contents now that the panel itself doesn't dynamically expand, prevent weird gaps. Clip contents for panel * Fix clipping on implanters and network configurators. Made them take less space. For implanters the name has to be cut off, which I did by adding a new ClipControl to achieve that in rich text. * Update visibility of item status panels based on whether you have hands at all. This avoids UI for borgs looking silly. Added a new "HandUILocation" enum that doesn't have middle hands to avoid confusion in UI code. * Use BulletRender for laser guns too. Provides all the benefits like fixing layout overflow and allowing multi-line stuff. Looks great now. This involved generalizing BulletRender a bit so it can be used for not-just-bullets. * Fix geiger word wrapping if you're really fucked --- .../Implants/UI/ImplanterStatusControl.cs | 16 +- Content.Client/Stylesheets/StyleNano.cs | 7 + .../UserInterface/Controls/ClipControl.cs | 55 +++++ .../Systems/Hands/Controls/HandButton.cs | 3 + .../Systems/Hands/HandsUIController.cs | 30 ++- .../Systems/Hotbar/Widgets/HotbarGui.xaml | 4 +- .../Systems/Hotbar/Widgets/HotbarGui.xaml.cs | 16 +- .../Inventory/Controls/ItemStatusPanel.xaml | 43 ++-- .../Controls/ItemStatusPanel.xaml.cs | 22 +- .../Weapons/Ranged/ItemStatus/BulletRender.cs | 216 ++++++++++++------ .../Ranged/Systems/GunSystem.AmmoCounter.cs | 59 +---- .../Hands/Components/HandsComponent.cs | 34 +++ .../en-US/devices/network-configurator.ftl | 4 +- Resources/Locale/en-US/implant/implant.ftl | 5 +- .../Locale/en-US/inventory/item-status.ftl | 1 + .../en-US/radiation/geiger-component.ftl | 2 +- 16 files changed, 335 insertions(+), 182 deletions(-) create mode 100644 Content.Client/UserInterface/Controls/ClipControl.cs create mode 100644 Resources/Locale/en-US/inventory/item-status.ftl diff --git a/Content.Client/Implants/UI/ImplanterStatusControl.cs b/Content.Client/Implants/UI/ImplanterStatusControl.cs index f3f0cdea7d7..e2ffabd17d9 100644 --- a/Content.Client/Implants/UI/ImplanterStatusControl.cs +++ b/Content.Client/Implants/UI/ImplanterStatusControl.cs @@ -1,5 +1,6 @@ using Content.Client.Message; using Content.Client.Stylesheets; +using Content.Client.UserInterface.Controls; using Content.Shared.Implants.Components; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; @@ -17,7 +18,7 @@ public ImplanterStatusControl(ImplanterComponent parent) _parent = parent; _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; _label.MaxWidth = 350; - AddChild(_label); + AddChild(new ClipControl { Children = { _label } }); Update(); } @@ -42,17 +43,12 @@ private void Update() _ => Loc.GetString("injector-invalid-injector-toggle-mode") }; - var (implantName, implantDescription) = _parent.ImplanterSlot.HasItem switch - { - false => (Loc.GetString("implanter-empty-text"), ""), - true => (_parent.ImplantData.Item1, _parent.ImplantData.Item2), - }; - + var implantName = _parent.ImplanterSlot.HasItem + ? _parent.ImplantData.Item1 + : Loc.GetString("implanter-empty-text"); _label.SetMarkup(Loc.GetString("implanter-label", ("implantName", implantName), - ("implantDescription", implantDescription), - ("modeString", modeStringLocalized), - ("lineBreak", "\n"))); + ("modeString", modeStringLocalized))); } } diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs index 5fc17447c37..8707d707660 100644 --- a/Content.Client/Stylesheets/StyleNano.cs +++ b/Content.Client/Stylesheets/StyleNano.cs @@ -136,6 +136,8 @@ public sealed class StyleNano : StyleBase public const string StyleClassPowerStateGood = "PowerStateGood"; public const string StyleClassItemStatus = "ItemStatus"; + public const string StyleClassItemStatusNotHeld = "ItemStatusNotHeld"; + public static readonly Color ItemStatusNotHeldColor = Color.Gray; //Background public const string StyleClassBackgroundBaseDark = "PanelBackgroundBaseDark"; @@ -1234,6 +1236,11 @@ public StyleNano(IResourceCache resCache) : base(resCache) new StyleProperty("font", notoSans10), }), + Element() + .Class(StyleClassItemStatusNotHeld) + .Prop("font", notoSansItalic10) + .Prop("font-color", ItemStatusNotHeldColor), + Element() .Class(StyleClassItemStatus) .Prop(nameof(RichTextLabel.LineHeightScale), 0.7f) diff --git a/Content.Client/UserInterface/Controls/ClipControl.cs b/Content.Client/UserInterface/Controls/ClipControl.cs new file mode 100644 index 00000000000..1fca3c0f474 --- /dev/null +++ b/Content.Client/UserInterface/Controls/ClipControl.cs @@ -0,0 +1,55 @@ +using System.Numerics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.UserInterface.Controls; + +/// +/// Pretends to child controls that there's infinite space. +/// This can be used to make something like a clip instead of wrapping. +/// +public sealed class ClipControl : Control +{ + private bool _clipHorizontal = true; + private bool _clipVertical = true; + + public bool ClipHorizontal + { + get => _clipHorizontal; + set + { + _clipHorizontal = value; + InvalidateMeasure(); + } + } + + public bool ClipVertical + { + get => _clipVertical; + set + { + _clipVertical = value; + InvalidateMeasure(); + } + } + + protected override Vector2 MeasureOverride(Vector2 availableSize) + { + if (ClipHorizontal) + availableSize = availableSize with { X = float.PositiveInfinity }; + if (ClipVertical) + availableSize = availableSize with { Y = float.PositiveInfinity }; + + return base.MeasureOverride(availableSize); + } + + protected override Vector2 ArrangeOverride(Vector2 finalSize) + { + foreach (var child in Children) + { + child.Arrange(UIBox2.FromDimensions(Vector2.Zero, child.DesiredSize)); + } + + return finalSize; + } +} diff --git a/Content.Client/UserInterface/Systems/Hands/Controls/HandButton.cs b/Content.Client/UserInterface/Systems/Hands/Controls/HandButton.cs index 574e0c47075..d5794f71955 100644 --- a/Content.Client/UserInterface/Systems/Hands/Controls/HandButton.cs +++ b/Content.Client/UserInterface/Systems/Hands/Controls/HandButton.cs @@ -5,8 +5,11 @@ namespace Content.Client.UserInterface.Systems.Hands.Controls; public sealed class HandButton : SlotControl { + public HandLocation HandLocation { get; } + public HandButton(string handName, HandLocation handLocation) { + HandLocation = handLocation; Name = "hand_" + handName; SlotName = handName; SetBackground(handLocation); diff --git a/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs b/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs index e57c15462e3..99d7bc77b81 100644 --- a/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs +++ b/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs @@ -256,7 +256,8 @@ private void SetActiveHand(string? handName) _player.LocalSession?.AttachedEntity is { } playerEntity && _handsSystem.TryGetHand(playerEntity, handName, out var hand, _playerHandsComponent)) { - if (hand.Location == HandLocation.Left) + var foldedLocation = hand.Location.GetUILocation(); + if (foldedLocation == HandUILocation.Left) { _statusHandLeft = handControl; HandsGui.UpdatePanelEntityLeft(hand.HeldEntity); @@ -268,7 +269,7 @@ private void SetActiveHand(string? handName) HandsGui.UpdatePanelEntityRight(hand.HeldEntity); } - HandsGui.SetHighlightHand(hand.Location); + HandsGui.SetHighlightHand(foldedLocation); } } @@ -299,11 +300,13 @@ private HandButton AddHand(string handName, HandLocation location) // If we don't have a status for this hand type yet, set it. // This means we have status filled by default in most scenarios, // otherwise the user'd need to switch hands to "activate" the hands the first time. - if (location == HandLocation.Left) + if (location.GetUILocation() == HandUILocation.Left) _statusHandLeft ??= button; else _statusHandRight ??= button; + UpdateVisibleStatusPanels(); + return button; } @@ -369,9 +372,30 @@ private bool RemoveHand(string handName, out HandButton? handButton) _handLookup.Remove(handName); handButton.Dispose(); + UpdateVisibleStatusPanels(); return true; } + private void UpdateVisibleStatusPanels() + { + var leftVisible = false; + var rightVisible = false; + + foreach (var hand in _handLookup.Values) + { + if (hand.HandLocation.GetUILocation() == HandUILocation.Left) + { + leftVisible = true; + } + else + { + rightVisible = true; + } + } + + HandsGui?.UpdateStatusVisibility(leftVisible, rightVisible); + } + public string RegisterHandContainer(HandsContainer handContainer) { var name = "HandContainer_" + _backupSuffix; diff --git a/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml b/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml index 3afe11ba330..00ba1878b48 100644 --- a/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml +++ b/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml @@ -32,7 +32,7 @@ Name="StatusPanelRight" HorizontalAlignment="Center" Margin="0 0 -2 2" SetWidth="125" - MaxHeight="60"/> + SetHeight="60"/> + SetHeight="60"/> (); hotbarController.Setup(HandContainer, StoragePanel); @@ -29,9 +29,15 @@ public void UpdatePanelEntityRight(EntityUid? entity) StatusPanelRight.Update(entity); } - public void SetHighlightHand(HandLocation? hand) + public void SetHighlightHand(HandUILocation? hand) { - StatusPanelLeft.UpdateHighlight(hand is HandLocation.Left); - StatusPanelRight.UpdateHighlight(hand is HandLocation.Middle or HandLocation.Right); + StatusPanelLeft.UpdateHighlight(hand is HandUILocation.Left); + StatusPanelRight.UpdateHighlight(hand is HandUILocation.Right); + } + + public void UpdateStatusVisibility(bool left, bool right) + { + StatusPanelLeft.Visible = left; + StatusPanelRight.Visible = right; } } diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml index 81142d64d28..3b1257b44cd 100644 --- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml +++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml @@ -4,25 +4,26 @@ xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client" VerticalAlignment="Bottom" HorizontalAlignment="Center"> - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs index e1fe6ab246c..95951fa1b0d 100644 --- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs +++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs @@ -1,17 +1,13 @@ -using System.Numerics; using Content.Client.Items; -using Content.Client.Resources; using Content.Shared.Hands.Components; using Content.Shared.IdentityManagement; using Content.Shared.Inventory.VirtualItem; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Timing; using Robust.Shared.Utility; -using static Content.Client.IoC.StaticIoC; namespace Content.Client.UserInterface.Systems.Inventory.Controls; @@ -23,17 +19,15 @@ public sealed partial class ItemStatusPanel : Control [ViewVariables] private EntityUid? _entity; // Tracked so we can re-run SetSide() if the theme changes. - private HandLocation _side; + private HandUILocation _side; public ItemStatusPanel() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - - SetSide(HandLocation.Middle); } - public void SetSide(HandLocation location) + public void SetSide(HandUILocation location) { // AN IMPORTANT REMINDER ABOUT THIS CODE: // In the UI, the RIGHT hand is on the LEFT on the screen. @@ -47,15 +41,14 @@ public void SetSide(HandLocation location) switch (location) { - case HandLocation.Right: + case HandUILocation.Right: texture = Theme.ResolveTexture("item_status_right"); textureHighlight = Theme.ResolveTexture("item_status_right_highlight"); cutOut = StyleBox.Margin.Left; flat = StyleBox.Margin.Right; contentMargin = MarginFromThemeColor("_itemstatus_content_margin_right"); break; - case HandLocation.Middle: - case HandLocation.Left: + case HandUILocation.Left: texture = Theme.ResolveTexture("item_status_left"); textureHighlight = Theme.ResolveTexture("item_status_left_highlight"); cutOut = StyleBox.Margin.Right; @@ -104,11 +97,14 @@ protected override void FrameUpdate(FrameEventArgs args) public void Update(EntityUid? entity) { + ItemNameLabel.Visible = entity != null; + NoItemLabel.Visible = entity == null; + if (entity == null) { + ItemNameLabel.Text = ""; ClearOldStatus(); _entity = null; - VisWrapper.Visible = false; return; } @@ -119,8 +115,6 @@ public void Update(EntityUid? entity) UpdateItemName(); } - - VisWrapper.Visible = true; } public void UpdateHighlight(bool highlight) diff --git a/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs b/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs index 492fad38720..8aea5d7ee62 100644 --- a/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs +++ b/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs @@ -6,40 +6,10 @@ namespace Content.Client.Weapons.Ranged.ItemStatus; -/// -/// Renders one or more rows of bullets for item status. -/// -/// -/// This is a custom control to allow complex responsive layout logic. -/// -public sealed class BulletRender : Control +public abstract class BaseBulletRenderer : Control { - private static readonly Color ColorA = Color.FromHex("#b68f0e"); - private static readonly Color ColorB = Color.FromHex("#d7df60"); - private static readonly Color ColorGoneA = Color.FromHex("#000000"); - private static readonly Color ColorGoneB = Color.FromHex("#222222"); - - /// - /// Try to ensure there's at least this many bullets on one row. - /// - /// - /// For example, if there are two rows and the second row has only two bullets, - /// we "steal" some bullets from the row below it to make it look nicer. - /// - public const int MinCountPerRow = 7; - - public const int BulletHeight = 12; - public const int BulletSeparationNormal = 3; - public const int BulletSeparationTiny = 2; - public const int BulletWidthNormal = 5; - public const int BulletWidthTiny = 2; - public const int VerticalSeparation = 2; - - private readonly Texture _bulletTiny; - private readonly Texture _bulletNormal; - private int _capacity; - private BulletType _type = BulletType.Normal; + private LayoutParameters _params; public int Rows { get; set; } = 2; public int Count { get; set; } @@ -49,35 +19,31 @@ public int Capacity get => _capacity; set { + if (_capacity == value) + return; + _capacity = value; InvalidateMeasure(); } } - public BulletType Type + protected LayoutParameters Parameters { - get => _type; + get => _params; set { - _type = value; + _params = value; InvalidateMeasure(); } } - public BulletRender() - { - var resC = IoCManager.Resolve(); - _bulletTiny = resC.GetTexture("/Textures/Interface/ItemStatus/Bullets/tiny.png"); - _bulletNormal = resC.GetTexture("/Textures/Interface/ItemStatus/Bullets/normal.png"); - } - protected override Vector2 MeasureOverride(Vector2 availableSize) { var countPerRow = Math.Min(Capacity, CountPerRow(availableSize.X)); var rows = Math.Min((int) MathF.Ceiling(Capacity / (float) countPerRow), Rows); - var height = BulletHeight * rows + (BulletSeparationNormal * rows - 1); + var height = _params.ItemHeight * rows + (_params.VerticalSeparation * rows - 1); var width = RowWidth(countPerRow); return new Vector2(width, height); @@ -91,13 +57,8 @@ protected override void Draw(DrawingHandleScreen handle) var countPerRow = CountPerRow(Size.X); - var (separation, _) = BulletParams(); - var texture = Type == BulletType.Normal ? _bulletNormal : _bulletTiny; - var pos = new Vector2(); - var altColor = false; - var spent = Capacity - Count; var bulletsDone = 0; @@ -105,7 +66,7 @@ protected override void Draw(DrawingHandleScreen handle) // Draw by rows, bottom to top. for (var row = 0; row < Rows; row++) { - altColor = false; + var altColor = false; var thisRowCount = Math.Min(countPerRow, Capacity - bulletsDone); if (thisRowCount <= 0) @@ -116,9 +77,10 @@ protected override void Draw(DrawingHandleScreen handle) // 1. The next row would have less than MinCountPerRow bullets. // 2. The next row is actually visible (we aren't the last row). // 3. MinCountPerRow is actually smaller than the count per row (avoid degenerate cases). + // 4. There's enough bullets that at least one will end up on the next row. var nextRowCount = Capacity - bulletsDone - thisRowCount; - if (nextRowCount < MinCountPerRow && row != Rows - 1 && MinCountPerRow < countPerRow) - thisRowCount -= MinCountPerRow - nextRowCount; + if (nextRowCount < _params.MinCountPerRow && row != Rows - 1 && _params.MinCountPerRow < countPerRow && nextRowCount > 0) + thisRowCount -= _params.MinCountPerRow - nextRowCount; // Account for row width to right-align. var rowWidth = RowWidth(thisRowCount); @@ -128,46 +90,130 @@ protected override void Draw(DrawingHandleScreen handle) for (var bullet = 0; bullet < thisRowCount; bullet++) { var absIdx = Capacity - bulletsDone - thisRowCount + bullet; - Color color; - if (absIdx >= spent) - color = altColor ? ColorA : ColorB; - else - color = altColor ? ColorGoneA : ColorGoneB; var renderPos = pos; - renderPos.Y = Size.Y - renderPos.Y - BulletHeight; - handle.DrawTexture(texture, renderPos, color); - pos.X += separation; + renderPos.Y = Size.Y - renderPos.Y - _params.ItemHeight; + + DrawItem(handle, renderPos, absIdx < spent, altColor); + + pos.X += _params.ItemSeparation; altColor ^= true; } bulletsDone += thisRowCount; pos.X = 0; - pos.Y += BulletHeight + VerticalSeparation; + pos.Y += _params.ItemHeight + _params.VerticalSeparation; } } + protected abstract void DrawItem(DrawingHandleScreen handle, Vector2 renderPos, bool spent, bool altColor); + private int CountPerRow(float width) { - var (separation, bulletWidth) = BulletParams(); - return (int) ((width - bulletWidth + separation) / separation); + return (int) ((width - _params.ItemWidth + _params.ItemSeparation) / _params.ItemSeparation); + } + + private int RowWidth(int count) + { + return (count - 1) * _params.ItemSeparation + _params.ItemWidth; } - private (int separation, int width) BulletParams() + protected struct LayoutParameters { - return Type switch + public int ItemHeight; + public int ItemSeparation; + public int ItemWidth; + public int VerticalSeparation; + + /// + /// Try to ensure there's at least this many bullets on one row. + /// + /// + /// For example, if there are two rows and the second row has only two bullets, + /// we "steal" some bullets from the row below it to make it look nicer. + /// + public int MinCountPerRow; + } +} + +/// +/// Renders one or more rows of bullets for item status. +/// +/// +/// This is a custom control to allow complex responsive layout logic. +/// +public sealed class BulletRender : BaseBulletRenderer +{ + public const int MinCountPerRow = 7; + + public const int BulletHeight = 12; + public const int VerticalSeparation = 2; + + private static readonly LayoutParameters LayoutNormal = new LayoutParameters + { + ItemHeight = BulletHeight, + ItemSeparation = 3, + ItemWidth = 5, + VerticalSeparation = VerticalSeparation, + MinCountPerRow = MinCountPerRow + }; + + private static readonly LayoutParameters LayoutTiny = new LayoutParameters + { + ItemHeight = BulletHeight, + ItemSeparation = 2, + ItemWidth = 2, + VerticalSeparation = VerticalSeparation, + MinCountPerRow = MinCountPerRow + }; + + private static readonly Color ColorA = Color.FromHex("#b68f0e"); + private static readonly Color ColorB = Color.FromHex("#d7df60"); + private static readonly Color ColorGoneA = Color.FromHex("#000000"); + private static readonly Color ColorGoneB = Color.FromHex("#222222"); + + private readonly Texture _bulletTiny; + private readonly Texture _bulletNormal; + + private BulletType _type = BulletType.Normal; + + public BulletType Type + { + get => _type; + set { - BulletType.Normal => (BulletSeparationNormal, BulletWidthNormal), - BulletType.Tiny => (BulletSeparationTiny, BulletWidthTiny), - _ => throw new ArgumentOutOfRangeException() - }; + if (_type == value) + return; + + Parameters = _type switch + { + BulletType.Normal => LayoutNormal, + BulletType.Tiny => LayoutTiny, + _ => throw new ArgumentOutOfRangeException() + }; + + _type = value; + } } - private int RowWidth(int count) + public BulletRender() { - var (separation, bulletWidth) = BulletParams(); + var resC = IoCManager.Resolve(); + _bulletTiny = resC.GetTexture("/Textures/Interface/ItemStatus/Bullets/tiny.png"); + _bulletNormal = resC.GetTexture("/Textures/Interface/ItemStatus/Bullets/normal.png"); + Parameters = LayoutNormal; + } - return (count - 1) * separation + bulletWidth; + protected override void DrawItem(DrawingHandleScreen handle, Vector2 renderPos, bool spent, bool altColor) + { + Color color; + if (spent) + color = altColor ? ColorGoneA : ColorGoneB; + else + color = altColor ? ColorA : ColorB; + + var texture = _type == BulletType.Tiny ? _bulletTiny : _bulletNormal; + handle.DrawTexture(texture, renderPos, color); } public enum BulletType @@ -176,3 +222,31 @@ public enum BulletType Tiny } } + +public sealed class BatteryBulletRenderer : BaseBulletRenderer +{ + private static readonly Color ItemColor = Color.FromHex("#E00000"); + private static readonly Color ItemColorGone = Color.Black; + + private const int SizeH = 10; + private const int SizeV = 10; + private const int Separation = 4; + + public BatteryBulletRenderer() + { + Parameters = new LayoutParameters + { + ItemWidth = SizeH, + ItemHeight = SizeV, + ItemSeparation = SizeH + Separation, + MinCountPerRow = 3, + VerticalSeparation = Separation + }; + } + + protected override void DrawItem(DrawingHandleScreen handle, Vector2 renderPos, bool spent, bool altColor) + { + var color = spent ? ItemColorGone : ItemColor; + handle.DrawRect(UIBox2.FromDimensions(renderPos, new Vector2(SizeH, SizeV)), color); + } +} diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs index cc7405b0479..84eaa9af1b0 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs @@ -116,7 +116,7 @@ public void Update(int count, int capacity) public sealed class BoxesStatusControl : Control { - private readonly BoxContainer _bulletsList; + private readonly BatteryBulletRenderer _bullets; private readonly Label _ammoCount; public BoxesStatusControl() @@ -128,27 +128,18 @@ public BoxesStatusControl() AddChild(new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal, - HorizontalExpand = true, Children = { - new Control + (_bullets = new BatteryBulletRenderer { - HorizontalExpand = true, - Children = - { - (_bulletsList = new BoxContainer - { - Orientation = BoxContainer.LayoutOrientation.Horizontal, - VerticalAlignment = VAlignment.Center, - SeparationOverride = 4 - }), - } - }, - new Control() { MinSize = new Vector2(5, 0) }, + Margin = new Thickness(0, 0, 5, 0), + HorizontalExpand = true + }), (_ammoCount = new Label { StyleClasses = { StyleNano.StyleClassItemStatus }, HorizontalAlignment = HAlignment.Right, + VerticalAlignment = VAlignment.Bottom }), } }); @@ -156,46 +147,12 @@ public BoxesStatusControl() public void Update(int count, int max) { - _bulletsList.RemoveAllChildren(); - _ammoCount.Visible = true; _ammoCount.Text = $"x{count:00}"; - max = Math.Min(max, 8); - FillBulletRow(_bulletsList, count, max); - } - private static void FillBulletRow(Control container, int count, int capacity) - { - var colorGone = Color.FromHex("#000000"); - var color = Color.FromHex("#E00000"); - - // Draw the empty ones - for (var i = count; i < capacity; i++) - { - container.AddChild(new PanelContainer - { - PanelOverride = new StyleBoxFlat() - { - BackgroundColor = colorGone, - }, - MinSize = new Vector2(10, 15), - }); - } - - // Draw the full ones, but limit the count to the capacity - count = Math.Min(count, capacity); - for (var i = 0; i < count; i++) - { - container.AddChild(new PanelContainer - { - PanelOverride = new StyleBoxFlat() - { - BackgroundColor = color, - }, - MinSize = new Vector2(10, 15), - }); - } + _bullets.Capacity = max; + _bullets.Count = count; } } diff --git a/Content.Shared/Hands/Components/HandsComponent.cs b/Content.Shared/Hands/Components/HandsComponent.cs index f1f25a69f7a..919d55f294a 100644 --- a/Content.Shared/Hands/Components/HandsComponent.cs +++ b/Content.Shared/Hands/Components/HandsComponent.cs @@ -126,9 +126,43 @@ public HandsComponentState(HandsComponent handComp) /// /// What side of the body this hand is on. /// +/// +/// public enum HandLocation : byte { Left, Middle, Right } + +/// +/// What side of the UI a hand is on. +/// +/// +/// +public enum HandUILocation : byte +{ + Left, + Right +} + +/// +/// Helper functions for working with . +/// +public static class HandLocationExt +{ + /// + /// Convert a into the appropriate . + /// This maps "middle" hands to . + /// + public static HandUILocation GetUILocation(this HandLocation location) + { + return location switch + { + HandLocation.Left => HandUILocation.Left, + HandLocation.Middle => HandUILocation.Right, + HandLocation.Right => HandUILocation.Right, + _ => throw new ArgumentOutOfRangeException(nameof(location), location, null) + }; + } +} diff --git a/Resources/Locale/en-US/devices/network-configurator.ftl b/Resources/Locale/en-US/devices/network-configurator.ftl index e1bcbc4c943..cd4955ed365 100644 --- a/Resources/Locale/en-US/devices/network-configurator.ftl +++ b/Resources/Locale/en-US/devices/network-configurator.ftl @@ -41,5 +41,5 @@ network-configurator-examine-current-mode = Current mode: {$mode} network-configurator-examine-switch-modes = Press {$key} to switch modes # item status -network-configurator-item-status-label = Current mode: {$mode} -{$keybinding} to switch mode +network-configurator-item-status-label = Mode: {$mode} + Switch: {$keybinding} diff --git a/Resources/Locale/en-US/implant/implant.ftl b/Resources/Locale/en-US/implant/implant.ftl index b93d43105a8..c3002a73ae3 100644 --- a/Resources/Locale/en-US/implant/implant.ftl +++ b/Resources/Locale/en-US/implant/implant.ftl @@ -10,9 +10,10 @@ implanter-component-implant-already = {$target} already has the {$implant}! implanter-draw-text = Draw implanter-inject-text = Inject -implanter-empty-text = None +implanter-empty-text = Empty -implanter-label = Implant: [color=green]{$implantName}[/color] | [color=white]{$modeString}[/color]{$lineBreak}{$implantDescription} +implanter-label = [color=green]{$implantName}[/color] + Mode: [color=white]{$modeString}[/color] implanter-contained-implant-text = [color=green]{$desc}[/color] diff --git a/Resources/Locale/en-US/inventory/item-status.ftl b/Resources/Locale/en-US/inventory/item-status.ftl new file mode 100644 index 00000000000..a53ba8be7dc --- /dev/null +++ b/Resources/Locale/en-US/inventory/item-status.ftl @@ -0,0 +1 @@ +item-status-not-held = No held item diff --git a/Resources/Locale/en-US/radiation/geiger-component.ftl b/Resources/Locale/en-US/radiation/geiger-component.ftl index 0e7d2a8a353..726c7190f20 100644 --- a/Resources/Locale/en-US/radiation/geiger-component.ftl +++ b/Resources/Locale/en-US/radiation/geiger-component.ftl @@ -1,3 +1,3 @@ -geiger-item-control-status = Radiation: [color={$color}]{$rads} rads[/color] +geiger-item-control-status = [color={$color}]{$rads} rads[/color] geiger-item-control-disabled = Disabled geiger-component-examine = Current radiation: [color={$color}]{$rads} rads[/color] From 8aabb1c2e5f006ca604daa92f2da0b8856f02ee7 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Thu, 25 Apr 2024 02:27:13 +1200 Subject: [PATCH 29/33] Content changes & tests for engine prototype validation PR (#27188) * Content changes & tests for engine prototype validation PR * A --- .../Tests/Linter/StaticFieldValidationTest.cs | 150 ++++++++++++++++++ .../Components/IdentityBlockerComponent.cs | 1 + Content.YAMLLinter/Program.cs | 2 +- 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs diff --git a/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs b/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs new file mode 100644 index 00000000000..30724b50a6d --- /dev/null +++ b/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; +using System.Linq; +using Content.Shared.Tag; +using Robust.Shared.Prototypes; +using Robust.Shared.Reflection; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.IntegrationTests.Tests.Linter; + +/// +/// Verify that the yaml linter successfully validates static fields +/// +[TestFixture] +public sealed class StaticFieldValidationTest +{ + [Test] + public async Task TestStaticFieldValidation() + { + await using var pair = await PoolManager.GetServerClient(); + var protoMan = pair.Server.ProtoMan; + + var protos = new Dictionary>(); + foreach (var kind in protoMan.EnumeratePrototypeKinds()) + { + var ids = protoMan.EnumeratePrototypes(kind).Select(x => x.ID).ToHashSet(); + protos.Add(kind, ids); + } + + Assert.That(protoMan.ValidateStaticFields(typeof(StringValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdSetValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(PrivateProtoIdArrayValid), protos).Count, Is.Zero); + + Assert.That(protoMan.ValidateStaticFields(typeof(StringInvalid), protos).Count, Is.EqualTo(1)); + Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayInvalid), protos).Count, Is.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdInvalid), protos).Count, Is.EqualTo(1)); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayInvalid), protos).Count, Is.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestInvalid), protos).Count, Is.EqualTo(1)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayInvalid), protos).Count, Is.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListInvalid), protos).Count, Is.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdSetInvalid), protos).Count, Is.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(PrivateProtoIdArrayInvalid), protos).Count, Is.EqualTo(2)); + + await pair.CleanReturnAsync(); + } + + [TestPrototypes] + private const string TestPrototypes = @" +- type: entity + id: StaticFieldTestEnt + +- type: Tag + id: StaticFieldTestTag +"; + + [Reflect(false)] private sealed class StringValid + { + [ValidatePrototypeId] public static string Tag = "StaticFieldTestTag"; + } + + [Reflect(false)] private sealed class StringInvalid + { + [ValidatePrototypeId] public static string Tag = string.Empty; + } + + [Reflect(false)] private sealed class StringArrayValid + { + [ValidatePrototypeId] public static string[] Tag = {"StaticFieldTestTag", "StaticFieldTestTag"}; + } + + [Reflect(false)] private sealed class StringArrayInvalid + { + [ValidatePrototypeId] public static string[] Tag = {string.Empty, "StaticFieldTestTag", string.Empty}; + } + + [Reflect(false)] private sealed class EntProtoIdValid + { + public static EntProtoId Tag = "StaticFieldTestEnt"; + } + + [Reflect(false)] private sealed class EntProtoIdInvalid + { + public static EntProtoId Tag = string.Empty; + } + + [Reflect(false)] private sealed class EntProtoIdArrayValid + { + public static EntProtoId[] Tag = {"StaticFieldTestEnt", "StaticFieldTestEnt"}; + } + + [Reflect(false)] private sealed class EntProtoIdArrayInvalid + { + public static EntProtoId[] Tag = {string.Empty, "StaticFieldTestEnt", string.Empty}; + } + + [Reflect(false)] private sealed class ProtoIdTestValid + { + public static ProtoId Tag = "StaticFieldTestTag"; + } + + [Reflect(false)] private sealed class ProtoIdTestInvalid + { + public static ProtoId Tag = string.Empty; + } + + [Reflect(false)] private sealed class ProtoIdArrayValid + { + public static ProtoId[] Tag = {"StaticFieldTestTag", "StaticFieldTestTag"}; + } + + [Reflect(false)] private sealed class ProtoIdArrayInvalid + { + public static ProtoId[] Tag = {string.Empty, "StaticFieldTestTag", string.Empty}; + } + + [Reflect(false)] private sealed class ProtoIdListValid + { + public static List> Tag = new() {"StaticFieldTestTag", "StaticFieldTestTag"}; + } + + [Reflect(false)] private sealed class ProtoIdListInvalid + { + public static List> Tag = new() {string.Empty, "StaticFieldTestTag", string.Empty}; + } + + [Reflect(false)] private sealed class ProtoIdSetValid + { + public static HashSet> Tag = new() {"StaticFieldTestTag", "StaticFieldTestTag"}; + } + + [Reflect(false)] private sealed class ProtoIdSetInvalid + { + public static HashSet> Tag = new() {string.Empty, "StaticFieldTestTag", string.Empty, " "}; + } + + [Reflect(false)] private sealed class PrivateProtoIdArrayValid + { + private static ProtoId[] Tag = {"StaticFieldTestTag", "StaticFieldTestTag"}; + } + + [Reflect(false)] private sealed class PrivateProtoIdArrayInvalid + { + private static ProtoId[] Tag = {string.Empty, "StaticFieldTestTag", string.Empty}; + } +} diff --git a/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs b/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs index 3857063783d..e7a88b6ef29 100644 --- a/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs +++ b/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs @@ -6,6 +6,7 @@ namespace Content.Shared.IdentityManagement.Components; [RegisterComponent, NetworkedComponent] public sealed partial class IdentityBlockerComponent : Component { + [DataField] public bool Enabled = true; /// diff --git a/Content.YAMLLinter/Program.cs b/Content.YAMLLinter/Program.cs index b23faa48fcd..7f0b740fe8c 100644 --- a/Content.YAMLLinter/Program.cs +++ b/Content.YAMLLinter/Program.cs @@ -99,7 +99,7 @@ await instance.WaitPost(() => yamlErrors[kind] = set; } - fieldErrors = protoMan.ValidateFields(prototypes); + fieldErrors = protoMan.ValidateStaticFields(prototypes); }); return (yamlErrors, fieldErrors); From 17160fad8393193bbf76c7dbd119f263a7faaeb2 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 25 Apr 2024 00:35:56 +1000 Subject: [PATCH 30/33] Update submodule to 219.2.0 (#27294) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 4e87d930097..eb638099999 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 4e87d93009748778c27ae835b16297596e5b1563 +Subproject commit eb638099999dce3a43d90772ca976ae010d649c0 From d797e3753f4da64de6912b1087fa2fd341552d69 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Wed, 24 Apr 2024 19:40:54 +0000 Subject: [PATCH 31/33] saltern bar stool (#27263) saltern stool Co-authored-by: deltanedas <@deltanedas:kde.org> --- Resources/Maps/saltern.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index 22b08b60b6f..b622f697990 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -3273,12 +3273,13 @@ entities: - type: MetaData - type: Transform - type: Map + mapPaused: True - type: PhysicsMap + - type: GridTree + - type: MovedGrids - type: Broadphase - type: OccluderTree - type: LoadedMap - - type: GridTree - - type: MovedGrids - proto: AcousticGuitarInstrument entities: - uid: 3146 @@ -6119,8 +6120,6 @@ entities: - data: null ReagentId: Leporazine Quantity: 40 - - type: MixableSolution - solution: beaker - uid: 10800 components: - type: Transform @@ -32842,9 +32841,6 @@ entities: - type: Transform pos: 38.5,-5.5 parent: 31 - - type: Door - secondsUntilStateChange: -9978.97 - state: Closing - uid: 4019 components: - type: Transform @@ -33106,9 +33102,6 @@ entities: - type: Transform pos: 1.5,-2.5 parent: 31 - - type: Door - secondsUntilStateChange: -80.957985 - state: Closing - uid: 3944 components: - type: Transform @@ -58376,6 +58369,11 @@ entities: - type: Transform pos: 42.5,-7.5 parent: 31 + - uid: 11452 + components: + - type: Transform + pos: -4.5,-3.5 + parent: 31 - proto: StorageCanister entities: - uid: 1108 From ef0a4d64c82ae10b2039e82ea06a28765499e540 Mon Sep 17 00:00:00 2001 From: pigeonpeas <147350443+pigeonpeas@users.noreply.github.com> Date: Wed, 24 Apr 2024 17:27:34 -0400 Subject: [PATCH 32/33] Add a trash bag to the advanced cleaning borg module. (#27226) add the trash bag puts the trash bag in the advanced cleaning module --- .../Entities/Objects/Specific/Robotics/borg_modules.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index d37523bd735..daa0d9bc203 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -318,6 +318,7 @@ - HoloprojectorBorg - SprayBottleSpaceCleaner - Dropper + - TrashBag # medical modules - type: entity From eb648dd0eb4b4cec3f85a59040ccd66cbe4f3617 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 24 Apr 2024 21:28:40 +0000 Subject: [PATCH 33/33] 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 c1bdcacff3a..b40dc7fbe63 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Blackern5000 - changes: - - message: Advanced topical meds now cost significantly less chemicals - type: Tweak - id: 5933 - time: '2024-02-13T22:03:13.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/24948 - author: Tayrtahn changes: - message: Drink bottles can now be opened and closed from the verbs menu, and some @@ -3850,3 +3843,10 @@ id: 6432 time: '2024-04-24T13:43:56.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27281 +- author: pigeonpeas + changes: + - message: Adds a trash bag to the advanced cleaning module. + type: Add + id: 6433 + time: '2024-04-24T21:27:34.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27226