From dce2bfe2b80bb0d962daf10899fc9e40283114ba Mon Sep 17 00:00:00 2001 From: Verm <32827189+Vermidia@users.noreply.github.com> Date: Mon, 20 May 2024 06:24:15 -0500 Subject: [PATCH 001/568] Remove extra gear prototype (#28062) Remove extra prototype --- Resources/Prototypes/Entities/Mobs/Player/humanoid.yml | 2 +- .../Prototypes/Roles/Jobs/Fun/misc_startinggear.yml | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml index 7a4aedd5e16a..c5805f0ed921 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml @@ -888,7 +888,7 @@ settings: short - type: GhostTakeoverAvailable - type: Loadout - prototypes: [ SyndicateOperativeGearDisasterVictim ] + prototypes: [ SyndicateOperativeGearCivilian ] - type: RandomMetadata nameSegments: - names_first diff --git a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml index 181746a64abd..d4cfc3e29cf8 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml @@ -79,19 +79,9 @@ back: ClothingBackpackDuffelSyndicate shoes: ClothingShoesBootsCombat gloves: ClothingHandsGlovesColorBlack - -# Syndicate Operative Outfit - Disaster Victim -- type: startingGear - id: SyndicateOperativeGearDisasterVictim - equipment: - jumpsuit: ClothingUniformJumpsuitSyndieFormal - back: ClothingBackpackDuffelSyndicate - shoes: ClothingShoesBootsCombat - gloves: ClothingHandsGlovesColorBlack id: SyndiPDA ears: ClothingHeadsetAltSyndicate - #Syndicate Operative Outfit - Basic - type: startingGear id: SyndicateOperativeGearBasic From 68ec204a674641953d7d983821d6f9f3bb83b05b Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 20 May 2024 09:32:25 -0400 Subject: [PATCH 002/568] Slightly emphasize popup when someone points at you (#28152) Slightly emphasize when someone points at you --- Content.Server/Pointing/EntitySystems/PointingSystem.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index 7bbf6409cdd3..960c8dc28aa5 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -88,7 +88,10 @@ private void SendMessage(EntityUid source, IEnumerable viewers, ? viewerPointedAtMessage : viewerMessage; - RaiseNetworkEvent(new PopupEntityEvent(message, PopupType.Small, netSource), viewerEntity); + // Someone pointing at YOU is slightly more important + var popupType = viewerEntity == pointed ? PopupType.Medium : PopupType.Small; + + RaiseNetworkEvent(new PopupEntityEvent(message, popupType, netSource), viewerEntity); } _replay.RecordServerMessage(new PopupEntityEvent(viewerMessage, PopupType.Small, netSource)); From 4ed12b60d3df7b6de7317d2dd3100924811bb963 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 20 May 2024 13:33:31 +0000 Subject: [PATCH 003/568] 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 a7de6f036971..73771ef3cdfd 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Nimfar11 - changes: - - message: Slimes can now drink FourteenLoko without harming their bodies. - type: Tweak - id: 6108 - time: '2024-03-07T20:50:10.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25889 - author: NakataRin changes: - message: Evacuation shuttle arrives 10 minutes on green alert now. @@ -3868,3 +3861,11 @@ id: 6607 time: '2024-05-19T23:01:44.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28038 +- author: Tayrtahn + changes: + - message: The popup for someone pointing at you is now slightly larger than normal + popups. + type: Tweak + id: 6608 + time: '2024-05-20T13:32:25.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28152 From fa06783986a0f890e0f3466f059e72df1eba0680 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 20 May 2024 09:52:49 -0400 Subject: [PATCH 004/568] Prevent ghosts from spawning on terminating maps/grids (#28099) * Extra checks to prevent ghosts spawning on terminating maps/grids * Add test for grid deletion --- .../Tests/Minds/GhostTests.cs | 16 +++++++++++ .../GameTicking/GameTicker.Spawning.cs | 15 +++++++--- Content.Server/Ghost/GhostSystem.cs | 28 +++++++++++++++---- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/Content.IntegrationTests/Tests/Minds/GhostTests.cs b/Content.IntegrationTests/Tests/Minds/GhostTests.cs index ad9d53a70dbf..7a156e71e419 100644 --- a/Content.IntegrationTests/Tests/Minds/GhostTests.cs +++ b/Content.IntegrationTests/Tests/Minds/GhostTests.cs @@ -156,4 +156,20 @@ public async Task TestGridGhostOnQueueDelete() await data.Pair.CleanReturnAsync(); } + [Test] + public async Task TestGhostGridNotTerminating() + { + var data = await SetupData(); + + Assert.DoesNotThrowAsync(async () => + { + // Delete the grid + await data.Server.WaitPost(() => data.SEntMan.DeleteEntity(data.MapData.Grid.Owner)); + }); + + await data.Pair.RunTicksSync(5); + + await data.Pair.CleanReturnAsync(); + } + } diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 74785603799b..843122e0feaa 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -369,11 +369,16 @@ public void SpawnObserver(ICommonSession player) public EntityCoordinates GetObserverSpawnPoint() { _possiblePositions.Clear(); - - foreach (var (point, transform) in EntityManager.EntityQuery(true)) + var spawnPointQuery = EntityManager.EntityQueryEnumerator(); + while (spawnPointQuery.MoveNext(out var uid, out var point, out var transform)) { - if (point.SpawnType != SpawnPointType.Observer) + if (point.SpawnType != SpawnPointType.Observer + || TerminatingOrDeleted(uid) + || transform.MapUid == null + || TerminatingOrDeleted(transform.MapUid.Value)) + { continue; + } _possiblePositions.Add(transform.Coordinates); } @@ -415,7 +420,9 @@ public EntityCoordinates GetObserverSpawnPoint() if (_mapManager.MapExists(DefaultMap)) { - return new EntityCoordinates(_mapManager.GetMapEntityId(DefaultMap), Vector2.Zero); + var mapUid = _mapManager.GetMapEntityId(DefaultMap); + if (!TerminatingOrDeleted(mapUid)) + return new EntityCoordinates(mapUid, Vector2.Zero); } // Just pick a point at this point I guess. diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index f4e6a4d607da..6c3d69fea7f1 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -409,23 +409,41 @@ public bool DoGhostBooEvent(EntityUid target) return SpawnGhost(mind, spawnPosition, canReturn); } + private bool IsValidSpawnPosition(EntityCoordinates? spawnPosition) + { + if (spawnPosition?.IsValid(EntityManager) != true) + return false; + + var mapUid = spawnPosition?.GetMapUid(EntityManager); + var gridUid = spawnPosition?.EntityId; + // Test if the map is being deleted + if (mapUid == null || TerminatingOrDeleted(mapUid.Value)) + return false; + // Test if the grid is being deleted + if (gridUid != null && TerminatingOrDeleted(gridUid.Value)) + return false; + + return true; + } + public EntityUid? SpawnGhost(Entity mind, EntityCoordinates? spawnPosition = null, bool canReturn = false) { if (!Resolve(mind, ref mind.Comp)) return null; - // Test if the map is being deleted - var mapUid = spawnPosition?.GetMapUid(EntityManager); - if (mapUid == null || TerminatingOrDeleted(mapUid.Value)) + // Test if the map or grid is being deleted + if (!IsValidSpawnPosition(spawnPosition)) spawnPosition = null; + // If it's bad, look for a valid point to spawn spawnPosition ??= _ticker.GetObserverSpawnPoint(); - if (!spawnPosition.Value.IsValid(EntityManager)) + // Make sure the new point is valid too + if (!IsValidSpawnPosition(spawnPosition)) { Log.Warning($"No spawn valid ghost spawn position found for {mind.Comp.CharacterName}" - + " \"{ToPrettyString(mind)}\""); + + $" \"{ToPrettyString(mind)}\""); _minds.TransferTo(mind.Owner, null, createGhost: false, mind: mind.Comp); return null; } From 8995810ade1daf49daac4738d3b1eb247302acc6 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 20 May 2024 10:12:05 -0400 Subject: [PATCH 005/568] Fix non-ghosts and admins counting toward most followed (#28120) * Fixed non-ghosts and admins counting toward most followed * Redone to better leverage EntityQueryEnumerator * Remember to test your code before you commit it, kids * Review revisions * Update Content.Shared/Follower/FollowerSystem.cs Co-authored-by: ShadowCommander * Update Content.Shared/Follower/FollowerSystem.cs Co-authored-by: ShadowCommander * Update Content.Shared/Follower/FollowerSystem.cs * Clean up --------- Co-authored-by: ShadowCommander Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> --- Content.Server/Ghost/GhostSystem.cs | 2 +- Content.Shared/Follower/FollowerSystem.cs | 35 ++++++++++++++++------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 6c3d69fea7f1..1a411f13a05f 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -306,7 +306,7 @@ private void OnGhostnadoRequest(GhostnadoRequestEvent msg, EntitySessionEventArg return; } - if (_followerSystem.GetMostFollowed() is not {} target) + if (_followerSystem.GetMostGhostFollowed() is not {} target) return; WarpTo(uid, target); diff --git a/Content.Shared/Follower/FollowerSystem.cs b/Content.Shared/Follower/FollowerSystem.cs index 6c02b130762f..8027ee449c4a 100644 --- a/Content.Shared/Follower/FollowerSystem.cs +++ b/Content.Shared/Follower/FollowerSystem.cs @@ -1,11 +1,11 @@ using System.Numerics; +using Content.Shared.Administration.Managers; using Content.Shared.Database; using Content.Shared.Follower.Components; using Content.Shared.Ghost; using Content.Shared.Hands; using Content.Shared.Movement.Events; using Content.Shared.Movement.Pulling.Events; -using Content.Shared.Movement.Systems; using Content.Shared.Tag; using Content.Shared.Verbs; using Robust.Shared.Containers; @@ -14,6 +14,7 @@ using Robust.Shared.Network; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; +using Robust.Shared.Player; using Robust.Shared.Utility; namespace Content.Shared.Follower; @@ -26,6 +27,7 @@ public sealed class FollowerSystem : EntitySystem [Dependency] private readonly SharedJointSystem _jointSystem = default!; [Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!; [Dependency] private readonly INetManager _netMan = default!; + [Dependency] private readonly ISharedAdminManager _adminManager = default!; public override void Initialize() { @@ -249,20 +251,33 @@ public void StopAllFollowers(EntityUid uid, } /// - /// Get the most followed entity. + /// Gets the entity with the most non-admin ghosts following it. /// - public EntityUid? GetMostFollowed() + public EntityUid? GetMostGhostFollowed() { EntityUid? picked = null; - int most = 0; - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) + var most = 0; + + // Keep a tally of how many ghosts are following each entity + var followedEnts = new Dictionary(); + + // Look for followers that are ghosts and are player controlled + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out var follower, out _, out var actor)) { - var count = comp.Following.Count; - if (count > most) + // Exclude admins + if (_adminManager.IsAdmin(actor.PlayerSession)) + continue; + + var followed = follower.Following; + // Add new entry or increment existing + followedEnts.TryGetValue(followed, out var currentValue); + followedEnts[followed] = currentValue + 1; + + if (followedEnts[followed] > most) { - picked = uid; - most = count; + picked = followed; + most = followedEnts[followed]; } } From c22329b86ce6c808c511f3861c831e8ab677228c Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 20 May 2024 14:13:11 +0000 Subject: [PATCH 006/568] 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 73771ef3cdfd..08d13027c8fb 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: NakataRin - changes: - - message: Evacuation shuttle arrives 10 minutes on green alert now. - type: Tweak - id: 6109 - time: '2024-03-07T21:01:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25906 - author: Admiral-Obvious-001 changes: - message: Zombies are now tougher to kill. @@ -3869,3 +3862,11 @@ id: 6608 time: '2024-05-20T13:32:25.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28152 +- author: Tayrtahn + changes: + - message: '"Warp to most followed" ghost option no longer includes admins and non-ghost + followers.' + type: Fix + id: 6609 + time: '2024-05-20T14:12:05.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28120 From 3b6136894661e3a2858dccdf5a5c68afa95e6bb6 Mon Sep 17 00:00:00 2001 From: Killerqu00 <47712032+Killerqu00@users.noreply.github.com> Date: Mon, 20 May 2024 21:59:07 +0200 Subject: [PATCH 007/568] rotate forensic scanner stored sprite (#28162) rotate forensic scanner --- .../Prototypes/Entities/Objects/Devices/forensic_scanner.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Objects/Devices/forensic_scanner.yml b/Resources/Prototypes/Entities/Objects/Devices/forensic_scanner.yml index 170751766bed..a0ada5ebe34b 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/forensic_scanner.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/forensic_scanner.yml @@ -9,6 +9,7 @@ state: forensicnew - type: Item size: Small + storedRotation: 90 - type: Clothing sprite: Objects/Devices/forensic_scanner.rsi quickEquip: false From 0f6e1196d85ff08b5766d29adcd98b83a60191a9 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Tue, 21 May 2024 17:40:35 +1200 Subject: [PATCH 008/568] Use non-generic `TryComp()` for metadata & transform (#28133) --- Content.Client/Gravity/GravitySystem.Shake.cs | 4 ++-- Content.Client/Maps/GridDraggingSystem.cs | 4 ++-- Content.Client/Salvage/SalvageSystem.cs | 2 +- Content.Client/Sprite/SpriteFadeSystem.cs | 2 +- Content.Client/Weapons/Misc/TetherGunSystem.cs | 2 +- .../Atmos/EntitySystems/AtmosphereSystem.Commands.cs | 2 +- Content.Server/Bible/BibleSystem.cs | 2 +- .../EntitySystems/DisassembleOnAltVerbSystem.cs | 2 +- .../Explosion/EntitySystems/TriggerSystem.cs | 2 +- Content.Server/Fax/FaxSystem.cs | 4 ++-- Content.Server/Gravity/GravityGeneratorSystem.cs | 4 ++-- Content.Server/Medical/MedicalScannerSystem.cs | 2 +- .../NPC/Pathfinding/PathfindingSystem.Distance.cs | 4 ++-- .../NPC/Pathfinding/PathfindingSystem.Grid.cs | 2 +- Content.Server/NPC/Pathfinding/PathfindingSystem.cs | 10 +++++----- Content.Server/NPC/Systems/NPCUtilitySystem.cs | 8 ++++---- Content.Server/PAI/PAISystem.cs | 2 +- Content.Server/Paper/PaperSystem.cs | 2 +- Content.Server/Physics/Controllers/MoverController.cs | 2 +- Content.Server/Polymorph/Systems/PolymorphSystem.cs | 2 +- Content.Server/Projectiles/ProjectileSystem.cs | 2 +- Content.Server/Salvage/FultonSystem.cs | 2 +- Content.Server/Salvage/SalvageSystem.Runner.cs | 2 +- Content.Server/Shuttles/Systems/ArrivalsSystem.cs | 4 ++-- .../Shuttles/Systems/EmergencyShuttleSystem.cs | 6 +++--- .../Shuttles/Systems/ShuttleConsoleSystem.cs | 2 +- .../Shuttles/Systems/ShuttleSystem.GridFill.cs | 4 ++-- Content.Server/Shuttles/Systems/ShuttleSystem.IFF.cs | 6 +++--- .../Spawners/EntitySystems/SpawnOnDespawnSystem.cs | 2 +- Content.Shared/Interaction/SharedInteractionSystem.cs | 4 ++-- Content.Shared/Movement/Systems/SharedJetpackSystem.cs | 2 +- .../Movement/Systems/SharedMoverController.Input.cs | 2 +- .../EntitySystems/PressurizedSolutionSystem.cs | 2 +- .../Nutrition/EntitySystems/SharedDrinkSystem.cs | 2 +- Content.Shared/Random/RulesSystem.cs | 10 +++++----- Content.Shared/Sound/SharedEmitSoundSystem.cs | 2 +- .../Storage/EntitySystems/SharedStorageSystem.cs | 2 +- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 10 +++++----- 38 files changed, 65 insertions(+), 65 deletions(-) diff --git a/Content.Client/Gravity/GravitySystem.Shake.cs b/Content.Client/Gravity/GravitySystem.Shake.cs index c4356588d355..9b9918ca3e74 100644 --- a/Content.Client/Gravity/GravitySystem.Shake.cs +++ b/Content.Client/Gravity/GravitySystem.Shake.cs @@ -25,7 +25,7 @@ private void OnShakeInit(EntityUid uid, GravityShakeComponent component, Compone { var localPlayer = _playerManager.LocalEntity; - if (!TryComp(localPlayer, out var xform) || + if (!TryComp(localPlayer, out TransformComponent? xform) || xform.GridUid != uid && xform.MapUid != uid) { return; @@ -46,7 +46,7 @@ protected override void ShakeGrid(EntityUid uid, GravityComponent? gravity = nul var localPlayer = _playerManager.LocalEntity; - if (!TryComp(localPlayer, out var xform)) + if (!TryComp(localPlayer, out TransformComponent? xform)) return; if (xform.GridUid != uid || diff --git a/Content.Client/Maps/GridDraggingSystem.cs b/Content.Client/Maps/GridDraggingSystem.cs index 16357c898387..e82786847e31 100644 --- a/Content.Client/Maps/GridDraggingSystem.cs +++ b/Content.Client/Maps/GridDraggingSystem.cs @@ -61,7 +61,7 @@ private void StopDragging() { if (_dragging == null) return; - if (_lastMousePosition != null && TryComp(_dragging.Value, out var xform) && + if (_lastMousePosition != null && TryComp(_dragging.Value, out TransformComponent? xform) && TryComp(_dragging.Value, out var body) && xform.MapID == _lastMousePosition.Value.MapId) { @@ -104,7 +104,7 @@ public override void Update(float frameTime) StartDragging(gridUid, Transform(gridUid).InvWorldMatrix.Transform(mousePos.Position)); } - if (!TryComp(_dragging, out var xform)) + if (!TryComp(_dragging, out TransformComponent? xform)) { StopDragging(); return; diff --git a/Content.Client/Salvage/SalvageSystem.cs b/Content.Client/Salvage/SalvageSystem.cs index fb305c5fdc4a..e1bce367caea 100644 --- a/Content.Client/Salvage/SalvageSystem.cs +++ b/Content.Client/Salvage/SalvageSystem.cs @@ -38,7 +38,7 @@ private void OnPlayAmbientMusic(ref PlayAmbientMusicEvent ev) var player = _playerManager.LocalEntity; - if (!TryComp(player, out var xform) || + if (!TryComp(player, out TransformComponent? xform) || !TryComp(xform.MapUid, out var expedition) || expedition.Stage < ExpeditionStage.MusicCountdown) { diff --git a/Content.Client/Sprite/SpriteFadeSystem.cs b/Content.Client/Sprite/SpriteFadeSystem.cs index d9584b60a653..676a6e583d51 100644 --- a/Content.Client/Sprite/SpriteFadeSystem.cs +++ b/Content.Client/Sprite/SpriteFadeSystem.cs @@ -45,7 +45,7 @@ public override void FrameUpdate(float frameTime) var spriteQuery = GetEntityQuery(); var change = ChangeRate * frameTime; - if (TryComp(player, out var playerXform) && + if (TryComp(player, out TransformComponent? playerXform) && _stateManager.CurrentState is GameplayState state && spriteQuery.TryGetComponent(player, out var playerSprite)) { diff --git a/Content.Client/Weapons/Misc/TetherGunSystem.cs b/Content.Client/Weapons/Misc/TetherGunSystem.cs index 634dbd24e792..398aeabb839b 100644 --- a/Content.Client/Weapons/Misc/TetherGunSystem.cs +++ b/Content.Client/Weapons/Misc/TetherGunSystem.cs @@ -82,7 +82,7 @@ public override void Update(float frameTime) const float BufferDistance = 0.1f; - if (TryComp(gun.TetherEntity, out var tetherXform) && + if (TryComp(gun.TetherEntity, out TransformComponent? tetherXform) && tetherXform.Coordinates.TryDistance(EntityManager, TransformSystem, coords, out var distance) && distance < BufferDistance) { diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs index f711b235af6f..5a41a7567b27 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs @@ -165,7 +165,7 @@ private CompletionResult FixGridAtmosCommandCompletions(IConsoleShell shell, str foreach (var grid in _mapManager.GetAllGrids(playerMap.Value).OrderBy(o => o.Owner)) { var uid = grid.Owner; - if (!TryComp(uid, out var gridXform)) + if (!TryComp(uid, out TransformComponent? gridXform)) continue; options.Add(new CompletionOption(uid.ToString(), $"{MetaData(uid).EntityName} - Map {gridXform.MapID}")); diff --git a/Content.Server/Bible/BibleSystem.cs b/Content.Server/Bible/BibleSystem.cs index 0c60e40dacb0..2cb0ac1dd76e 100644 --- a/Content.Server/Bible/BibleSystem.cs +++ b/Content.Server/Bible/BibleSystem.cs @@ -167,7 +167,7 @@ private void AddSummonVerb(EntityUid uid, SummonableComponent component, GetVerb { Act = () => { - if (!TryComp(args.User, out var userXform)) + if (!TryComp(args.User, out TransformComponent? userXform)) return; AttemptSummon((uid, component), args.User, userXform); diff --git a/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs b/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs index 61b6f3d93d2f..d694f84a9c74 100644 --- a/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs +++ b/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs @@ -56,7 +56,7 @@ public async void AttemptDisassemble(EntityUid uid, EntityUid user, EntityUid ta if (component.Deleted || Deleted(uid)) return; - if (!TryComp(uid, out var transformComp)) + if (!TryComp(uid, out TransformComponent? transformComp)) return; var entity = EntityManager.SpawnEntity(component.Prototype, transformComp.Coordinates); diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index 8e0a75ea24f9..7c6b5df7f143 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -166,7 +166,7 @@ private void HandleDeleteTrigger(EntityUid uid, DeleteOnTriggerComponent compone private void HandleGibTrigger(EntityUid uid, GibOnTriggerComponent component, TriggerEvent args) { - if (!TryComp(uid, out var xform)) + if (!TryComp(uid, out TransformComponent? xform)) return; if (component.DeleteItems) { diff --git a/Content.Server/Fax/FaxSystem.cs b/Content.Server/Fax/FaxSystem.cs index e86dbca4a135..16d2d391f65a 100644 --- a/Content.Server/Fax/FaxSystem.cs +++ b/Content.Server/Fax/FaxSystem.cs @@ -459,7 +459,7 @@ public void Copy(EntityUid uid, FaxMachineComponent? component, FaxCopyMessage a if (sendEntity == null) return; - if (!TryComp(sendEntity, out var metadata) || + if (!TryComp(sendEntity, out MetaDataComponent? metadata) || !TryComp(sendEntity, out var paper)) return; @@ -506,7 +506,7 @@ public void Send(EntityUid uid, FaxMachineComponent? component, FaxSendMessage a if (!component.KnownFaxes.TryGetValue(component.DestinationFaxAddress, out var faxName)) return; - if (!TryComp(sendEntity, out var metadata) || + if (!TryComp(sendEntity, out MetaDataComponent? metadata) || !TryComp(sendEntity, out var paper)) return; diff --git a/Content.Server/Gravity/GravityGeneratorSystem.cs b/Content.Server/Gravity/GravityGeneratorSystem.cs index 8e4da75fac82..0b53df63fd0a 100644 --- a/Content.Server/Gravity/GravityGeneratorSystem.cs +++ b/Content.Server/Gravity/GravityGeneratorSystem.cs @@ -41,7 +41,7 @@ private void OnParentChanged(EntityUid uid, GravityGeneratorComponent component, private void OnComponentShutdown(EntityUid uid, GravityGeneratorComponent component, ComponentShutdown args) { if (component.GravityActive && - TryComp(uid, out var xform) && + TryComp(uid, out TransformComponent? xform) && TryComp(xform.ParentUid, out GravityComponent? gravity)) { component.GravityActive = false; @@ -114,7 +114,7 @@ public override void Update(float frameTime) UpdateUI(ent, chargeRate); if (active != gravGen.GravityActive && - TryComp(uid, out var xform) && + TryComp(uid, out TransformComponent? xform) && TryComp(xform.ParentUid, out var gravity)) { // Force it on in the faster path. diff --git a/Content.Server/Medical/MedicalScannerSystem.cs b/Content.Server/Medical/MedicalScannerSystem.cs index 91184ddc1628..b24690e204a9 100644 --- a/Content.Server/Medical/MedicalScannerSystem.cs +++ b/Content.Server/Medical/MedicalScannerSystem.cs @@ -86,7 +86,7 @@ private void AddInsertOtherVerb(EntityUid uid, MedicalScannerComponent component return; var name = "Unknown"; - if (TryComp(args.Using.Value, out var metadata)) + if (TryComp(args.Using.Value, out MetaDataComponent? metadata)) name = metadata.EntityName; InteractionVerb verb = new() diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs index 95d5c9c46510..5daf38c4209e 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs @@ -30,8 +30,8 @@ private Vector2 GetDiff(PathPoly start, PathPoly end) if (end.GraphUid != start.GraphUid) { - if (!TryComp(start.GraphUid, out var startXform) || - !TryComp(end.GraphUid, out var endXform)) + if (!TryComp(start.GraphUid, out TransformComponent? startXform) || + !TryComp(end.GraphUid, out TransformComponent? endXform)) { return Vector2.Zero; } diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs index 6462c10fe55b..52f7db77ed65 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs @@ -261,7 +261,7 @@ private void OnCollisionLayerChange(ref CollisionLayerChangeEvent ev) private void OnBodyTypeChange(ref PhysicsBodyTypeChangedEvent ev) { - if (TryComp(ev.Entity, out var xform) && + if (TryComp(ev.Entity, out TransformComponent? xform) && xform.GridUid != null) { var aabb = _lookup.GetAABBNoContainer(ev.Entity, xform.Coordinates.Position, xform.LocalRotation); diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.cs index a59af88ff58d..3672ad047b44 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.cs @@ -264,7 +264,7 @@ public async Task GetRandomPath( int limit = 40, PathFlags flags = PathFlags.None) { - if (!TryComp(entity, out var start)) + if (!TryComp(entity, out TransformComponent? start)) return new PathResultEvent(PathResult.NoPath, new List()); var layer = 0; @@ -294,7 +294,7 @@ public async Task GetRandomPath( CancellationToken cancelToken, PathFlags flags = PathFlags.None) { - if (!TryComp(entity, out var start)) + if (!TryComp(entity, out TransformComponent? start)) return null; var request = GetRequest(entity, start.Coordinates, end, range, cancelToken, flags); @@ -325,8 +325,8 @@ public async Task GetPath( CancellationToken cancelToken, PathFlags flags = PathFlags.None) { - if (!TryComp(entity, out var xform) || - !TryComp(target, out var targetXform)) + if (!TryComp(entity, out TransformComponent? xform) || + !TryComp(target, out TransformComponent? targetXform)) return new PathResultEvent(PathResult.NoPath, new List()); var request = GetRequest(entity, xform.Coordinates, targetXform.Coordinates, range, cancelToken, flags); @@ -400,7 +400,7 @@ public async void GetPathEvent( var gridUid = coordinates.GetGridUid(EntityManager); if (!TryComp(gridUid, out var comp) || - !TryComp(gridUid, out var xform)) + !TryComp(gridUid, out TransformComponent? xform)) { return null; } diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 4b0ccafa1d40..2e8c628b5030 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -260,8 +260,8 @@ private float GetScore(NPCBlackboard blackboard, EntityUid targetUid, UtilityCon { var radius = blackboard.GetValueOrDefault(NPCBlackboard.VisionRadius, EntityManager); - if (!TryComp(targetUid, out var targetXform) || - !TryComp(owner, out var xform)) + if (!TryComp(targetUid, out TransformComponent? targetXform) || + !TryComp(owner, out TransformComponent? xform)) { return 0f; } @@ -308,8 +308,8 @@ private float GetScore(NPCBlackboard blackboard, EntityUid targetUid, UtilityCon if (blackboard.TryGetValue("Target", out var currentTarget, EntityManager) && currentTarget == targetUid && - TryComp(owner, out var xform) && - TryComp(targetUid, out var targetXform) && + TryComp(owner, out TransformComponent? xform) && + TryComp(targetUid, out TransformComponent? targetXform) && xform.Coordinates.TryDistance(EntityManager, _transform, targetXform.Coordinates, out var distance) && distance <= radius + bufferRange) { diff --git a/Content.Server/PAI/PAISystem.cs b/Content.Server/PAI/PAISystem.cs index 091afb155767..0cdb0bc29a2d 100644 --- a/Content.Server/PAI/PAISystem.cs +++ b/Content.Server/PAI/PAISystem.cs @@ -111,7 +111,7 @@ public void PAITurningOff(EntityUid uid) if (TryComp(uid, out var instrument)) _instrumentSystem.Clean(uid, instrument); - if (TryComp(uid, out var metadata)) + if (TryComp(uid, out MetaDataComponent? metadata)) { var proto = metadata.EntityPrototype; if (proto != null) diff --git a/Content.Server/Paper/PaperSystem.cs b/Content.Server/Paper/PaperSystem.cs index d10d04cfb9a9..4a7828c78c8f 100644 --- a/Content.Server/Paper/PaperSystem.cs +++ b/Content.Server/Paper/PaperSystem.cs @@ -148,7 +148,7 @@ private void OnInputTextMessage(EntityUid uid, PaperComponent paperComp, PaperIn if (TryComp(uid, out var appearance)) _appearance.SetData(uid, PaperVisuals.Status, PaperStatus.Written, appearance); - if (TryComp(uid, out var meta)) + if (TryComp(uid, out MetaDataComponent? meta)) _metaSystem.SetEntityDescription(uid, "", meta); _adminLogger.Add(LogType.Chat, LogImpact.Low, diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index 759b8ef29c6e..6edc202d153b 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -271,7 +271,7 @@ private void HandleShuttleMovement(float frameTime) consoleEnt = cargoConsole.Entity; } - if (!TryComp(consoleEnt, out var xform)) continue; + if (!TryComp(consoleEnt, out TransformComponent? xform)) continue; var gridId = xform.GridUid; // This tries to see if the grid is a shuttle and if the console should work. diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs index e6ba1d02afdc..d4a9159d44ff 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs @@ -248,7 +248,7 @@ private void OnDestruction(Entity ent, ref Destructi } } - if (configuration.TransferName && TryComp(uid, out var targetMeta)) + if (configuration.TransferName && TryComp(uid, out MetaDataComponent? targetMeta)) _metaData.SetEntityName(child, targetMeta.EntityName); if (configuration.TransferHumanoidAppearance) diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 0061b16e47c5..f8c8ef64b791 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -72,7 +72,7 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St if (component.DeleteOnCollide) QueueDel(uid); - if (component.ImpactEffect != null && TryComp(uid, out var xform)) + if (component.ImpactEffect != null && TryComp(uid, out TransformComponent? xform)) { RaiseNetworkEvent(new ImpactEffectEvent(component.ImpactEffect, GetNetCoordinates(xform.Coordinates)), Filter.Pvs(xform.Coordinates, entityMan: EntityManager)); } diff --git a/Content.Server/Salvage/FultonSystem.cs b/Content.Server/Salvage/FultonSystem.cs index a24bab458468..ad998e535989 100644 --- a/Content.Server/Salvage/FultonSystem.cs +++ b/Content.Server/Salvage/FultonSystem.cs @@ -53,7 +53,7 @@ public override void Update(float frameTime) private void Fulton(EntityUid uid, FultonedComponent component) { if (!Deleted(component.Beacon) && - TryComp(component.Beacon, out var beaconXform) && + TryComp(component.Beacon, out TransformComponent? beaconXform) && !Container.IsEntityOrParentInContainer(component.Beacon.Value, xform: beaconXform) && CanFulton(uid)) { diff --git a/Content.Server/Salvage/SalvageSystem.Runner.cs b/Content.Server/Salvage/SalvageSystem.Runner.cs index 23607e2bdc55..161b79108446 100644 --- a/Content.Server/Salvage/SalvageSystem.Runner.cs +++ b/Content.Server/Salvage/SalvageSystem.Runner.cs @@ -32,7 +32,7 @@ private void InitializeRunner() private void OnConsoleFTLAttempt(ref ConsoleFTLAttemptEvent ev) { - if (!TryComp(ev.Uid, out var xform) || + if (!TryComp(ev.Uid, out TransformComponent? xform) || !TryComp(xform.MapUid, out var salvage)) { return; diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 23f7e10a3e32..e87e781e620e 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -316,7 +316,7 @@ public void HandlePlayerSpawning(PlayerSpawningEvent ev) TryGetArrivals(out var arrivals); - if (TryComp(arrivals, out var arrivalsXform)) + if (TryComp(arrivals, out TransformComponent? arrivalsXform)) { var mapId = arrivalsXform.MapID; @@ -413,7 +413,7 @@ public override void Update(float frameTime) var curTime = _timing.CurTime; TryGetArrivals(out var arrivals); - if (TryComp(arrivals, out var arrivalsXform)) + if (TryComp(arrivals, out TransformComponent? arrivalsXform)) { while (query.MoveNext(out var uid, out var comp, out var shuttle, out var xform)) { diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index 9bfe1bf98666..2d8ae4b735e5 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -261,7 +261,7 @@ public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleCo if (!Resolve(stationUid, ref stationShuttle)) return; - if (!TryComp(stationShuttle.EmergencyShuttle, out var xform) || + if (!TryComp(stationShuttle.EmergencyShuttle, out TransformComponent? xform) || !TryComp(stationShuttle.EmergencyShuttle, out var shuttle)) { Log.Error($"Attempted to call an emergency shuttle for an uninitialized station? Station: {ToPrettyString(stationUid)}. Shuttle: {ToPrettyString(stationShuttle.EmergencyShuttle)}"); @@ -284,7 +284,7 @@ public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleCo if (_shuttle.TryFTLDock(stationShuttle.EmergencyShuttle.Value, shuttle, targetGrid.Value, DockTag)) { - if (TryComp(targetGrid.Value, out var targetXform)) + if (TryComp(targetGrid.Value, out TransformComponent? targetXform)) { var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery); _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir())), playDefaultSound: false); @@ -330,7 +330,7 @@ private void OnStationInit(EntityUid uid, StationCentcommComponent component, Ma return; // Post mapinit? fancy - if (TryComp(component.Entity, out var xform)) + if (TryComp(component.Entity, out TransformComponent? xform)) { component.MapEntity = xform.MapUid; return; diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs index 89dc114cafca..2b5769881d27 100644 --- a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs @@ -242,7 +242,7 @@ private void UpdateState(EntityUid consoleUid, ref DockingInterfaceState? dockSt RaiseLocalEvent(entity.Value, ref getShuttleEv); entity = getShuttleEv.Console; - TryComp(entity, out var consoleXform); + TryComp(entity, out TransformComponent? consoleXform); var shuttleGridUid = consoleXform?.GridUid; NavInterfaceState navState; diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs index c4cf2820e246..853548add37c 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs @@ -184,7 +184,7 @@ private void OnGridFillMapInit(EntityUid uid, GridFillComponent component, MapIn return; if (!TryComp(uid, out var dock) || - !TryComp(uid, out var xform) || + !TryComp(uid, out TransformComponent? xform) || xform.GridUid == null) { return; @@ -196,7 +196,7 @@ private void OnGridFillMapInit(EntityUid uid, GridFillComponent component, MapIn if (_loader.TryLoad(mapId, component.Path.ToString(), out var ent) && ent.Count == 1 && - TryComp(ent[0], out var shuttleXform)) + TryComp(ent[0], out TransformComponent? shuttleXform)) { var escape = GetSingleDock(ent[0]); diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.IFF.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.IFF.cs index ce79466b5891..ed5d109e852e 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.IFF.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.IFF.cs @@ -16,7 +16,7 @@ private void InitializeIFF() private void OnIFFShow(EntityUid uid, IFFConsoleComponent component, IFFShowIFFMessage args) { - if (!TryComp(uid, out var xform) || xform.GridUid == null || + if (!TryComp(uid, out TransformComponent? xform) || xform.GridUid == null || (component.AllowedFlags & IFFFlags.HideLabel) == 0x0) { return; @@ -34,7 +34,7 @@ private void OnIFFShow(EntityUid uid, IFFConsoleComponent component, IFFShowIFFM private void OnIFFShowVessel(EntityUid uid, IFFConsoleComponent component, IFFShowVesselMessage args) { - if (!TryComp(uid, out var xform) || xform.GridUid == null || + if (!TryComp(uid, out TransformComponent? xform) || xform.GridUid == null || (component.AllowedFlags & IFFFlags.Hide) == 0x0) { return; @@ -54,7 +54,7 @@ private void OnIFFConsoleAnchor(EntityUid uid, IFFConsoleComponent component, re { // If we anchor / re-anchor then make sure flags up to date. if (!args.Anchored || - !TryComp(uid, out var xform) || + !TryComp(uid, out TransformComponent? xform) || !TryComp(xform.GridUid, out var iff)) { _uiSystem.SetUiState(uid, IFFConsoleUiKey.Key, new IFFConsoleBoundUserInterfaceState() diff --git a/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs b/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs index 77927c9bba94..f5a34728dc86 100644 --- a/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs +++ b/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs @@ -14,7 +14,7 @@ public override void Initialize() private void OnDespawn(EntityUid uid, SpawnOnDespawnComponent comp, ref TimedDespawnEvent args) { - if (!TryComp(uid, out var xform)) + if (!TryComp(uid, out TransformComponent? xform)) return; Spawn(comp.Prototype, xform.Coordinates); diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 8b3431cb0246..c82a749755d6 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -570,7 +570,7 @@ public bool InRangeUnobstructed( Ignored? predicate = null, bool popup = false) { - if (!TryComp(other, out var otherXform)) + if (!TryComp(other, out TransformComponent? otherXform)) return false; return InRangeUnobstructed(origin, other, otherXform.Coordinates, otherXform.LocalRotation, range, collisionMask, predicate, @@ -633,7 +633,7 @@ public bool InRangeUnobstructed( fixtureA.FixtureCount > 0 && TryComp(other, out var fixtureB) && fixtureB.FixtureCount > 0 && - TryComp(origin, out var xformA)) + TryComp(origin, out TransformComponent? xformA)) { var (worldPosA, worldRotA) = xformA.GetWorldPositionRotation(); var xfA = new Transform(worldPosA, worldRotA); diff --git a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs index 8c42511f8469..724eca682fbe 100644 --- a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs +++ b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs @@ -113,7 +113,7 @@ private void OnJetpackToggle(EntityUid uid, JetpackComponent component, ToggleJe if (args.Handled) return; - if (TryComp(uid, out var xform) && !CanEnableOnGrid(xform.GridUid)) + if (TryComp(uid, out TransformComponent? xform) && !CanEnableOnGrid(xform.GridUid)) { _popup.PopupClient(Loc.GetString("jetpack-no-station"), uid, args.Performer); diff --git a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs index ba175b345f8c..1ccb7f0c3f14 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs @@ -313,7 +313,7 @@ private void HandleDirChange(EntityUid entity, Direction dir, ushort subTick, bo // For stuff like "Moving out of locker" or the likes // We'll relay a movement input to the parent. if (_container.IsEntityInContainer(entity) && - TryComp(entity, out var xform) && + TryComp(entity, out TransformComponent? xform) && xform.ParentUid.IsValid() && _mobState.IsAlive(entity)) { diff --git a/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs b/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs index d63b8e7326c9..82d90f4c928c 100644 --- a/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs @@ -179,7 +179,7 @@ public bool TrySpray(Entity entity, EntityUid? ta var solution = _solutionContainer.SplitSolution(soln.Value, interactions.Volume); // Spray the solution onto the ground and anyone nearby - if (TryComp(entity, out var transform)) + if (TryComp(entity, out TransformComponent? transform)) _puddle.TrySplashSpillAt(entity, transform.Coordinates, solution, out _, sound: false); var drinkName = Identity.Entity(entity, EntityManager); diff --git a/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs b/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs index 7cae3b920866..bf1e585fab9a 100644 --- a/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs @@ -81,7 +81,7 @@ private string HalfEmptyOrHalfFull(ExaminedEvent args) { string remainingString = "drink-component-on-examine-is-half-full"; - if (TryComp(args.Examiner, out var examiner) && examiner.EntityName.Length > 0 + if (TryComp(args.Examiner, out MetaDataComponent? examiner) && examiner.EntityName.Length > 0 && string.Compare(examiner.EntityName.Substring(0, 1), "m", StringComparison.InvariantCultureIgnoreCase) > 0) remainingString = "drink-component-on-examine-is-half-empty"; diff --git a/Content.Shared/Random/RulesSystem.cs b/Content.Shared/Random/RulesSystem.cs index f8711fb63e01..6b8a58abb712 100644 --- a/Content.Shared/Random/RulesSystem.cs +++ b/Content.Shared/Random/RulesSystem.cs @@ -26,7 +26,7 @@ public bool IsTrue(EntityUid uid, RulesPrototype rules) break; case GridInRangeRule griddy: { - if (!TryComp(uid, out var xform)) + if (!TryComp(uid, out TransformComponent? xform)) { return false; } @@ -50,7 +50,7 @@ public bool IsTrue(EntityUid uid, RulesPrototype rules) } case InSpaceRule: { - if (!TryComp(uid, out var xform) || + if (!TryComp(uid, out TransformComponent? xform) || xform.GridUid != null) { return false; @@ -146,7 +146,7 @@ public bool IsTrue(EntityUid uid, RulesPrototype rules) } case NearbyEntitiesRule entity: { - if (!TryComp(uid, out var xform) || + if (!TryComp(uid, out TransformComponent? xform) || xform.MapUid == null) { return false; @@ -177,7 +177,7 @@ public bool IsTrue(EntityUid uid, RulesPrototype rules) } case NearbyTilesPercentRule tiles: { - if (!TryComp(uid, out var xform) || + if (!TryComp(uid, out TransformComponent? xform) || !TryComp(xform.GridUid, out var grid)) { return false; @@ -227,7 +227,7 @@ public bool IsTrue(EntityUid uid, RulesPrototype rules) } case OnMapGridRule: { - if (!TryComp(uid, out var xform) || + if (!TryComp(uid, out TransformComponent? xform) || xform.GridUid != xform.MapUid || xform.MapUid == null) { diff --git a/Content.Shared/Sound/SharedEmitSoundSystem.cs b/Content.Shared/Sound/SharedEmitSoundSystem.cs index a9a50698d7d5..0dcdc44c9f21 100644 --- a/Content.Shared/Sound/SharedEmitSoundSystem.cs +++ b/Content.Shared/Sound/SharedEmitSoundSystem.cs @@ -72,7 +72,7 @@ private void OnEmitSpawnOnInit(EntityUid uid, EmitSoundOnSpawnComponent componen private void OnEmitSoundOnLand(EntityUid uid, BaseEmitSoundComponent component, ref LandEvent args) { if (!args.PlaySound || - !TryComp(uid, out var xform) || + !TryComp(uid, out TransformComponent? xform) || !TryComp(xform.GridUid, out var grid)) { return; diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 4f0b760345b5..6dd7f9d41844 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -463,7 +463,7 @@ private void AfterInteract(EntityUid uid, StorageComponent storageComp, AfterInt return; } - if (_xformQuery.TryGetComponent(uid, out var transformOwner) && TryComp(target, out var transformEnt)) + if (TryComp(uid, out TransformComponent? transformOwner) && TryComp(target, out TransformComponent? transformEnt)) { var parent = transformOwner.ParentUid; diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 6fa11f87d2a9..0fb6ac3eff96 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -315,7 +315,7 @@ public void AttemptLightAttackMiss(EntityUid user, EntityUid weaponUid, MeleeWea public bool AttemptLightAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target) { - if (!TryComp(target, out var targetXform)) + if (!TryComp(target, out TransformComponent? targetXform)) return false; return AttemptAttack(user, weaponUid, weapon, new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(targetXform.Coordinates)), null); @@ -323,7 +323,7 @@ public bool AttemptLightAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponC public bool AttemptDisarmAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target) { - if (!TryComp(target, out var targetXform)) + if (!TryComp(target, out TransformComponent? targetXform)) return false; return AttemptAttack(user, weaponUid, weapon, new DisarmAttackEvent(GetNetEntity(target), GetNetCoordinates(targetXform.Coordinates)), null); @@ -446,7 +446,7 @@ protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, Entity // For consistency with wide attacks stuff needs damageable. if (Deleted(target) || !HasComp(target) || - !TryComp(target, out var targetXform) || + !TryComp(target, out TransformComponent? targetXform) || // Not in LOS. !InRange(user, target.Value, component.Range, session)) { @@ -534,7 +534,7 @@ protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, Entity private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session) { // TODO: This is copy-paste as fuck with DoPreciseAttack - if (!TryComp(user, out var userXform)) + if (!TryComp(user, out TransformComponent? userXform)) return false; var targetMap = GetCoordinates(ev.Coordinates).ToMap(EntityManager, TransformSystem); @@ -748,7 +748,7 @@ protected virtual bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid private void DoLungeAnimation(EntityUid user, EntityUid weapon, Angle angle, MapCoordinates coordinates, float length, string? animation) { // TODO: Assert that offset eyes are still okay. - if (!TryComp(user, out var userXform)) + if (!TryComp(user, out TransformComponent? userXform)) return; var invMatrix = TransformSystem.GetInvWorldMatrix(userXform); From c0f158bd19afdc50d5d8c71d37de8bf0045583b4 Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Tue, 21 May 2024 12:23:55 +0300 Subject: [PATCH 009/568] Ed anomalies (#28175) Update CODEOWNERS --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 69c5f0138ff5..21c3070b8022 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -27,7 +27,7 @@ /Content.YAMLLinter @DrSmugleaf /Content.Shared/Damage/ @DrSmugleaf -/Content.*/Anomaly/ @EmoGarbage404 +/Content.*/Anomaly/ @EmoGarbage404 @TheShuEd /Content.*/Lathe/ @EmoGarbage404 /Content.*/Materials/ @EmoGarbage404 /Content.*/Mech/ @EmoGarbage404 @@ -35,7 +35,7 @@ /Content.*/Stack/ @EmoGarbage404 /Content.*/Xenoarchaeology/ @EmoGarbage404 /Content.*/Zombies/ @EmoGarbage404 -/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @EmoGarbage404 +/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @EmoGarbage404 @TheShuEd /Resources/Prototypes/Research/ @EmoGarbage404 /Content.*/Forensics/ @ficcialfaint From 79c77635faf1db1ecb2dd8f46e424503a000e13c Mon Sep 17 00:00:00 2001 From: Killerqu00 <47712032+Killerqu00@users.noreply.github.com> Date: Tue, 21 May 2024 12:16:35 +0200 Subject: [PATCH 010/568] sleeper agent appear later into the round and only once (#28160) --- Resources/Prototypes/GameRules/events.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 19c9f7d5dc4f..3b9ad5aadf2c 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -433,10 +433,10 @@ noSpawn: true components: - type: StationEvent - earliestStart: 25 + earliestStart: 30 weight: 8 minimumPlayers: 15 - reoccurrenceDelay: 30 + maxOccurrences: 1 # can only happen once per round startAnnouncement: station-event-communication-interception startAudio: path: /Audio/Announcements/intercept.ogg From 68a0e568e4a5fdbd0ee551b5254b94dd6ca38375 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 21 May 2024 10:17:43 +0000 Subject: [PATCH 011/568] 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 08d13027c8fb..12da3a2a9009 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Admiral-Obvious-001 - changes: - - message: Zombies are now tougher to kill. - type: Tweak - id: 6110 - time: '2024-03-07T21:02:50.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25876 - author: TheShuEd changes: - message: Removed Hamlet, Smile and Pun Pun from Thief objectives @@ -3870,3 +3863,10 @@ id: 6609 time: '2024-05-20T14:12:05.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28120 +- author: Killerqu00 + changes: + - message: Sleeper Agents only appear once per round. + type: Tweak + id: 6610 + time: '2024-05-21T10:16:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28160 From dbb7dcd16d97d7cf7a7d1430a07bb24cce55c3d9 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Tue, 21 May 2024 05:11:49 -0700 Subject: [PATCH 012/568] Add defib event, add fields to be able to disable crit defib and do after movement (#28174) * Add defib event, add fields to be able to disable crit defib and do after movement * Fix check --- Content.Server/Medical/DefibrillatorSystem.cs | 10 +++++++++- Content.Shared/Medical/DefibrillatorComponent.cs | 6 ++++++ Content.Shared/Medical/TargetDefibrillatedEvent.cs | 4 ++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 Content.Shared/Medical/TargetDefibrillatedEvent.cs diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index 64861fdc5158..4373532f0186 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -162,6 +162,9 @@ public bool CanZap(EntityUid uid, EntityUid target, EntityUid? user = null, Defi if (_mobState.IsAlive(target, mobState)) return false; + if (!component.CanDefibCrit && _mobState.IsCritical(target, mobState)) + return false; + return true; } @@ -179,7 +182,8 @@ public bool TryStartZap(EntityUid uid, EntityUid target, EntityUid user, Defibri { BlockDuplicate = true, BreakOnHandChange = true, - NeedHand = true + NeedHand = true, + BreakOnMove = !component.AllowDoAfterMovement }); } @@ -254,6 +258,10 @@ public void Zap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorCo // if we don't have enough power left for another shot, turn it off if (!_powerCell.HasActivatableCharge(uid)) TryDisable(uid, component); + + // TODO clean up this clown show above + var ev = new TargetDefibrillatedEvent(user, (uid, component)); + RaiseLocalEvent(target, ref ev); } public override void Update(float frameTime) diff --git a/Content.Shared/Medical/DefibrillatorComponent.cs b/Content.Shared/Medical/DefibrillatorComponent.cs index 2da528528547..61a02187d09b 100644 --- a/Content.Shared/Medical/DefibrillatorComponent.cs +++ b/Content.Shared/Medical/DefibrillatorComponent.cs @@ -60,6 +60,12 @@ public sealed partial class DefibrillatorComponent : Component [DataField("doAfterDuration"), ViewVariables(VVAccess.ReadWrite)] public TimeSpan DoAfterDuration = TimeSpan.FromSeconds(3); + [DataField] + public bool AllowDoAfterMovement = true; + + [DataField] + public bool CanDefibCrit = true; + /// /// The sound when someone is zapped. /// diff --git a/Content.Shared/Medical/TargetDefibrillatedEvent.cs b/Content.Shared/Medical/TargetDefibrillatedEvent.cs new file mode 100644 index 000000000000..60d1a2158459 --- /dev/null +++ b/Content.Shared/Medical/TargetDefibrillatedEvent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared.Medical; + +[ByRefEvent] +public readonly record struct TargetDefibrillatedEvent(EntityUid User, Entity Defibrillator); From ede1406128a2ad4d6f378bc0dae35e777d904a34 Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Wed, 22 May 2024 10:52:57 +0300 Subject: [PATCH 013/568] Artifact portal effect changes (#28200) Update portal.yml --- Resources/Prototypes/Entities/Effects/portal.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Effects/portal.yml b/Resources/Prototypes/Entities/Effects/portal.yml index eb69ac821f58..75300693cd94 100644 --- a/Resources/Prototypes/Entities/Effects/portal.yml +++ b/Resources/Prototypes/Entities/Effects/portal.yml @@ -65,7 +65,7 @@ energy: 1 netsync: false - type: TimedDespawn - lifetime: 120 + lifetime: 1 - type: Portal canTeleportToOtherMaps: true From 6a9139b4e1427fa8ea397383c04f9391ee287fb7 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 22 May 2024 07:54:04 +0000 Subject: [PATCH 014/568] 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 12da3a2a9009..d20cf44cdb96 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: TheShuEd - changes: - - message: Removed Hamlet, Smile and Pun Pun from Thief objectives - type: Remove - id: 6111 - time: '2024-03-07T23:53:36.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25921 - author: MACMAN2003 changes: - message: Diagonal windows no longer space you when in a pressurized environment. @@ -3870,3 +3863,10 @@ id: 6610 time: '2024-05-21T10:16:35.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28160 +- author: TheShuEd + changes: + - message: Artifact portal lifespan reduced from 120 seconds to 1 second + type: Tweak + id: 6611 + time: '2024-05-22T07:52:58.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28200 From df8b9a659ecbd412243acdbf02fa96e2a74d5789 Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Wed, 22 May 2024 11:16:20 +0000 Subject: [PATCH 015/568] Firelock improvements part 1 (#26582) * Change prying system and pryunpoweredcomp to allow for custom time modifiers This will be useful if I go the route of making firelocks pryable when unpowered instead of just being able to open and close instantly when unpowered. * Make firelocks properly predicted Shared system made. Since atmos checks can only be done on the server we just have it set relevant bools on the component and then dirty it. Ditched atmos checks on trying to open, they now only happen whenever firelocks are updated. * Make firelocks pryable without a crowbar While this usually would only allow you to do this when a door is unpowered, firelocks do not have the airlock component which actually does that check. As such firelocks will always allow you to pry them open/closed by hand. * Clean up System. Change update interval to be based on ticks. Move as much as possible to shared * Make firelocks unable to emergency close for 2 seconds after being pried open * Clean up * More cleanup * Reorganize SharedFirelockSystem methods to match Initialize order --------- Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> --- Content.Client/Doors/FirelockSystem.cs | 3 +- .../Doors/Systems/FirelockSystem.cs | 134 +++--------------- .../Doors/Components/FirelockComponent.cs | 48 ++++++- .../Doors/Systems/SharedDoorSystem.cs | 3 +- .../Doors/Systems/SharedFirelockSystem.cs | 125 ++++++++++++++++ .../Components/PryUnpoweredComponent.cs | 2 + Content.Shared/Prying/Systems/PryingSystem.cs | 8 +- .../Structures/Doors/Firelocks/firelock.yml | 2 + 8 files changed, 200 insertions(+), 125 deletions(-) create mode 100644 Content.Shared/Doors/Systems/SharedFirelockSystem.cs diff --git a/Content.Client/Doors/FirelockSystem.cs b/Content.Client/Doors/FirelockSystem.cs index cfd84a471336..f64b4c8e5222 100644 --- a/Content.Client/Doors/FirelockSystem.cs +++ b/Content.Client/Doors/FirelockSystem.cs @@ -1,9 +1,10 @@ using Content.Shared.Doors.Components; +using Content.Shared.Doors.Systems; using Robust.Client.GameObjects; namespace Content.Client.Doors; -public sealed class FirelockSystem : EntitySystem +public sealed class FirelockSystem : SharedFirelockSystem { [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; diff --git a/Content.Server/Doors/Systems/FirelockSystem.cs b/Content.Server/Doors/Systems/FirelockSystem.cs index 3d4c8a4ec59e..5ad86fb20aab 100644 --- a/Content.Server/Doors/Systems/FirelockSystem.cs +++ b/Content.Server/Doors/Systems/FirelockSystem.cs @@ -1,67 +1,56 @@ using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Monitor.Systems; -using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Shuttles.Components; -using Content.Shared.Access.Systems; using Content.Shared.Atmos; using Content.Shared.Atmos.Monitor; using Content.Shared.Doors; using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; -using Content.Shared.Popups; -using Content.Shared.Prying.Components; using Robust.Shared.Map.Components; namespace Content.Server.Doors.Systems { - public sealed class FirelockSystem : EntitySystem + public sealed class FirelockSystem : SharedFirelockSystem { - [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly SharedDoorSystem _doorSystem = default!; [Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!; [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; + [Dependency] private readonly SharedMapSystem _mapping = default!; - private static float _visualUpdateInterval = 0.5f; - private float _accumulatedFrameTime; + private const int UpdateInterval = 30; + private int _accumulatedTicks; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnBeforeDoorOpened); - SubscribeLocalEvent(OnDoorGetPryTimeModifier); - SubscribeLocalEvent(OnUpdateState); SubscribeLocalEvent(OnBeforeDoorAutoclose); SubscribeLocalEvent(OnAtmosAlarm); - // Visuals - SubscribeLocalEvent(UpdateVisuals); - SubscribeLocalEvent(UpdateVisuals); SubscribeLocalEvent(PowerChanged); + } private void PowerChanged(EntityUid uid, FirelockComponent component, ref PowerChangedEvent args) { // TODO this should REALLLLY not be door specific appearance thing. _appearance.SetData(uid, DoorVisuals.Powered, args.Powered); + component.Powered = args.Powered; + Dirty(uid, component); } - #region Visuals - private void UpdateVisuals(EntityUid uid, FirelockComponent component, EntityEventArgs args) => UpdateVisuals(uid, component); - public override void Update(float frameTime) { - _accumulatedFrameTime += frameTime; - if (_accumulatedFrameTime < _visualUpdateInterval) + _accumulatedTicks += 1; + if (_accumulatedTicks < UpdateInterval) return; - _accumulatedFrameTime -= _visualUpdateInterval; + _accumulatedTicks = 0; var airtightQuery = GetEntityQuery(); var appearanceQuery = GetEntityQuery(); @@ -84,94 +73,13 @@ public override void Update(float frameTime) { var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery); _appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance); + firelock.Temperature = fire; + firelock.Pressure = pressure; + Dirty(uid, firelock); } } } - private void UpdateVisuals(EntityUid uid, - FirelockComponent? firelock = null, - DoorComponent? door = null, - AirtightComponent? airtight = null, - AppearanceComponent? appearance = null, - TransformComponent? xform = null) - { - if (!Resolve(uid, ref door, ref appearance, false)) - return; - - // only bother to check pressure on doors that are some variation of closed. - if (door.State != DoorState.Closed - && door.State != DoorState.Welded - && door.State != DoorState.Denying) - { - _appearance.SetData(uid, DoorVisuals.ClosedLights, false, appearance); - return; - } - - var query = GetEntityQuery(); - if (!Resolve(uid, ref firelock, ref airtight, ref appearance, ref xform, false) || !query.Resolve(uid, ref airtight, false)) - return; - - var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, query); - _appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance); - } - #endregion - - public bool EmergencyPressureStop(EntityUid uid, FirelockComponent? firelock = null, DoorComponent? door = null) - { - if (!Resolve(uid, ref firelock, ref door)) - return false; - - if (door.State == DoorState.Open) - { - if (_doorSystem.TryClose(uid, door)) - { - return _doorSystem.OnPartialClose(uid, door); - } - } - return false; - } - - private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args) - { - // Give the Door remote the ability to force a firelock open even if it is holding back dangerous gas - var overrideAccess = (args.User != null) && _accessReaderSystem.IsAllowed(args.User.Value, uid); - - if (!this.IsPowered(uid, EntityManager) || (!overrideAccess && IsHoldingPressureOrFire(uid, component))) - args.Cancel(); - } - - private void OnDoorGetPryTimeModifier(EntityUid uid, FirelockComponent component, ref GetPryTimeModifierEvent args) - { - var state = CheckPressureAndFire(uid, component); - - if (state.Fire) - { - _popupSystem.PopupEntity(Loc.GetString("firelock-component-is-holding-fire-message"), - uid, args.User, PopupType.MediumCaution); - } - else if (state.Pressure) - { - _popupSystem.PopupEntity(Loc.GetString("firelock-component-is-holding-pressure-message"), - uid, args.User, PopupType.MediumCaution); - } - - if (state.Fire || state.Pressure) - args.PryTimeModifier *= component.LockedPryTimeModifier; - } - - private void OnUpdateState(EntityUid uid, FirelockComponent component, DoorStateChangedEvent args) - { - var ev = new BeforeDoorAutoCloseEvent(); - RaiseLocalEvent(uid, ev); - UpdateVisuals(uid, component, args); - if (ev.Cancelled) - { - return; - } - - _doorSystem.SetNextStateChange(uid, component.AutocloseDelay); - } - private void OnBeforeDoorAutoclose(EntityUid uid, FirelockComponent component, BeforeDoorAutoCloseEvent args) { if (!this.IsPowered(uid, EntityManager)) @@ -204,12 +112,6 @@ private void OnAtmosAlarm(EntityUid uid, FirelockComponent component, AtmosAlarm } } - public bool IsHoldingPressureOrFire(EntityUid uid, FirelockComponent firelock) - { - var result = CheckPressureAndFire(uid, firelock); - return result.Pressure || result.Fire; - } - public (bool Pressure, bool Fire) CheckPressureAndFire(EntityUid uid, FirelockComponent firelock) { var query = GetEntityQuery(); @@ -234,17 +136,17 @@ public bool IsHoldingPressureOrFire(EntityUid uid, FirelockComponent firelock) return (false, false); } - if (!TryComp(xform.ParentUid, out GridAtmosphereComponent? gridAtmosphere)) + if (!HasComp(xform.ParentUid)) return (false, false); var grid = Comp(xform.ParentUid); - var pos = grid.CoordinatesToTile(xform.Coordinates); + var pos = _mapping.CoordinatesToTile(xform.ParentUid, grid, xform.Coordinates); var minPressure = float.MaxValue; var maxPressure = float.MinValue; var minTemperature = float.MaxValue; var maxTemperature = float.MinValue; - bool holdingFire = false; - bool holdingPressure = false; + var holdingFire = false; + var holdingPressure = false; // We cannot simply use `_atmosSystem.GetAdjacentTileMixtures` because of how the `includeBlocked` option // works, we want to ignore the firelock's blocking, while including blockers on other tiles. @@ -284,7 +186,7 @@ public bool IsHoldingPressureOrFire(EntityUid uid, FirelockComponent firelock) { // Is there some airtight entity blocking this direction? If yes, don't include this direction in the // pressure differential - if (HasAirtightBlocker(grid.GetAnchoredEntities(adjacentPos), dir.GetOpposite(), airtightQuery)) + if (HasAirtightBlocker(_mapping.GetAnchoredEntities(xform.ParentUid, grid, adjacentPos), dir.GetOpposite(), airtightQuery)) continue; var p = gas.Pressure; diff --git a/Content.Shared/Doors/Components/FirelockComponent.cs b/Content.Shared/Doors/Components/FirelockComponent.cs index 97e57185cac9..ca62daaa0fd1 100644 --- a/Content.Shared/Doors/Components/FirelockComponent.cs +++ b/Content.Shared/Doors/Components/FirelockComponent.cs @@ -1,4 +1,4 @@ -using Content.Shared.Doors.Components; +using Robust.Shared.GameStates; namespace Content.Shared.Doors.Components { @@ -7,9 +7,11 @@ namespace Content.Shared.Doors.Components /// auto-closing on depressurization, air/fire alarm interactions, and preventing normal door functions when /// retaining pressure.. /// - [RegisterComponent] + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class FirelockComponent : Component { + #region Settings + /// /// Pry time modifier to be used when the firelock is currently closed due to fire or pressure. /// @@ -39,5 +41,47 @@ public sealed partial class FirelockComponent : Component /// [DataField("alarmAutoClose"), ViewVariables(VVAccess.ReadWrite)] public bool AlarmAutoClose = true; + + /// + /// The cooldown duration before a firelock can automatically close due to a hazardous environment after it has + /// been pried open. Measured in seconds. + /// + [DataField] + public TimeSpan EmergencyCloseCooldownDuration = TimeSpan.FromSeconds(2); + + #endregion + + #region Set by system + + /// + /// When the firelock will be allowed to automatically close again due to a hazardous environment. + /// + [DataField] + public TimeSpan? EmergencyCloseCooldown; + + /// + /// Whether the firelock can open, or is locked due to its environment. + /// + public bool IsLocked => Pressure || Temperature; + + /// + /// Whether the firelock is holding back a hazardous pressure. + /// + [DataField, AutoNetworkedField] + public bool Pressure; + + /// + /// Whether the firelock is holding back extreme temperatures. + /// + [DataField, AutoNetworkedField] + public bool Temperature; + + /// + /// Whether the airlock is powered. + /// + [DataField, AutoNetworkedField] + public bool Powered; + + #endregion } } diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.cs index b58b7b265e9a..20456c14777b 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.cs @@ -6,7 +6,6 @@ using Content.Shared.Database; using Content.Shared.Doors.Components; using Content.Shared.Emag.Systems; -using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Physics; using Content.Shared.Popups; @@ -466,7 +465,7 @@ public bool OnPartialClose(EntityUid uid, DoorComponent? door = null, PhysicsCom door.Partial = true; - // Make sure no entity waled into the airlock when it started closing. + // Make sure no entity walked into the airlock when it started closing. if (!CanClose(uid, door)) { door.NextStateChange = GameTiming.CurTime + door.OpenTimeTwo; diff --git a/Content.Shared/Doors/Systems/SharedFirelockSystem.cs b/Content.Shared/Doors/Systems/SharedFirelockSystem.cs new file mode 100644 index 000000000000..7d033efdd408 --- /dev/null +++ b/Content.Shared/Doors/Systems/SharedFirelockSystem.cs @@ -0,0 +1,125 @@ +using Content.Shared.Access.Systems; +using Content.Shared.Doors.Components; +using Content.Shared.Popups; +using Content.Shared.Prying.Components; +using Robust.Shared.Timing; + +namespace Content.Shared.Doors.Systems; + +public abstract class SharedFirelockSystem : EntitySystem +{ + [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedDoorSystem _doorSystem = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUpdateState); + + // Access/Prying + SubscribeLocalEvent(OnBeforeDoorOpened); + SubscribeLocalEvent(OnDoorGetPryTimeModifier); + SubscribeLocalEvent(OnAfterPried); + + // Visuals + SubscribeLocalEvent(UpdateVisuals); + SubscribeLocalEvent(UpdateVisuals); + } + + public bool EmergencyPressureStop(EntityUid uid, FirelockComponent? firelock = null, DoorComponent? door = null) + { + if (!Resolve(uid, ref firelock, ref door)) + return false; + + if (door.State != DoorState.Open + || firelock.EmergencyCloseCooldown != null + && _gameTiming.CurTime < firelock.EmergencyCloseCooldown) + return false; + + if (!_doorSystem.TryClose(uid, door)) + return false; + + return _doorSystem.OnPartialClose(uid, door); + } + + private void OnUpdateState(EntityUid uid, FirelockComponent component, DoorStateChangedEvent args) + { + var ev = new BeforeDoorAutoCloseEvent(); + RaiseLocalEvent(uid, ev); + UpdateVisuals(uid, component, args); + if (ev.Cancelled) + { + return; + } + + _doorSystem.SetNextStateChange(uid, component.AutocloseDelay); + } + + #region Access/Prying + + private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args) + { + // Give the Door remote the ability to force a firelock open even if it is holding back dangerous gas + var overrideAccess = (args.User != null) && _accessReaderSystem.IsAllowed(args.User.Value, uid); + + if (!component.Powered || (!overrideAccess && component.IsLocked)) + args.Cancel(); + } + + private void OnDoorGetPryTimeModifier(EntityUid uid, FirelockComponent component, ref GetPryTimeModifierEvent args) + { + if (component.Temperature) + { + _popupSystem.PopupClient(Loc.GetString("firelock-component-is-holding-fire-message"), + uid, args.User, PopupType.MediumCaution); + } + else if (component.Pressure) + { + _popupSystem.PopupClient(Loc.GetString("firelock-component-is-holding-pressure-message"), + uid, args.User, PopupType.MediumCaution); + } + + if (component.IsLocked) + args.PryTimeModifier *= component.LockedPryTimeModifier; + } + + private void OnAfterPried(EntityUid uid, FirelockComponent component, ref PriedEvent args) + { + component.EmergencyCloseCooldown = _gameTiming.CurTime + component.EmergencyCloseCooldownDuration; + } + + #endregion + + #region Visuals + + private void UpdateVisuals(EntityUid uid, FirelockComponent component, EntityEventArgs args) => UpdateVisuals(uid, component); + + private void UpdateVisuals(EntityUid uid, + FirelockComponent? firelock = null, + DoorComponent? door = null, + AppearanceComponent? appearance = null) + { + if (!Resolve(uid, ref door, ref appearance, false)) + return; + + // only bother to check pressure on doors that are some variation of closed. + if (door.State != DoorState.Closed + && door.State != DoorState.Welded + && door.State != DoorState.Denying) + { + _appearance.SetData(uid, DoorVisuals.ClosedLights, false, appearance); + return; + } + + if (!Resolve(uid, ref firelock, ref appearance, false)) + return; + + _appearance.SetData(uid, DoorVisuals.ClosedLights, firelock.IsLocked, appearance); + } + + #endregion +} diff --git a/Content.Shared/Prying/Components/PryUnpoweredComponent.cs b/Content.Shared/Prying/Components/PryUnpoweredComponent.cs index f0e61dc9685d..b6b69a2577d2 100644 --- a/Content.Shared/Prying/Components/PryUnpoweredComponent.cs +++ b/Content.Shared/Prying/Components/PryUnpoweredComponent.cs @@ -8,4 +8,6 @@ namespace Content.Shared.Prying.Components; [RegisterComponent, NetworkedComponent] public sealed partial class PryUnpoweredComponent : Component { + [DataField] + public float PryModifier = 0.1f; } diff --git a/Content.Shared/Prying/Systems/PryingSystem.cs b/Content.Shared/Prying/Systems/PryingSystem.cs index ab87585c706f..372c89c9ae09 100644 --- a/Content.Shared/Prying/Systems/PryingSystem.cs +++ b/Content.Shared/Prying/Systems/PryingSystem.cs @@ -93,17 +93,17 @@ public bool TryPry(EntityUid target, EntityUid user, out DoAfterId? id) id = null; // We don't care about displaying a message if no tool was used. - if (!CanPry(target, user, out _)) + if (!TryComp(target, out var unpoweredComp) || !CanPry(target, user, out _, unpoweredComp: unpoweredComp)) // If we have reached this point we want the event that caused this // to be marked as handled. return true; // hand-prying is much slower - var modifier = CompOrNull(user)?.SpeedModifier ?? 0.1f; + var modifier = CompOrNull(user)?.SpeedModifier ?? unpoweredComp.PryModifier; return StartPry(target, user, null, modifier, out id); } - private bool CanPry(EntityUid target, EntityUid user, out string? message, PryingComponent? comp = null) + private bool CanPry(EntityUid target, EntityUid user, out string? message, PryingComponent? comp = null, PryUnpoweredComponent? unpoweredComp = null) { BeforePryEvent canev; @@ -113,7 +113,7 @@ private bool CanPry(EntityUid target, EntityUid user, out string? message, Pryin } else { - if (!TryComp(target, out _)) + if (!Resolve(target, ref unpoweredComp)) { message = null; return false; diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml index fcdb432dce60..860db862aeef 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml @@ -108,6 +108,8 @@ - type: DoorBolt - type: AccessReader access: [ [ "Engineering" ] ] + - type: PryUnpowered + pryModifier: 0.5 - type: entity id: Firelock From 456e6f07ec4c98a72a0900bde6aee4051ae7ee4f Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 22 May 2024 11:17:27 +0000 Subject: [PATCH 016/568] Automatic changelog update --- Resources/Changelog/Changelog.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index d20cf44cdb96..a8e3a5f155b4 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: MACMAN2003 - changes: - - message: Diagonal windows no longer space you when in a pressurized environment. - type: Fix - id: 6112 - time: '2024-03-08T07:50:34.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25926 - author: mryikes changes: - message: New Nuke Detonation Song "Clearly Nuclear". @@ -3870,3 +3863,15 @@ id: 6611 time: '2024-05-22T07:52:58.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28200 +- author: nikthechampiongr + changes: + - message: Firelocks can now be pried by hand. + type: Tweak + - message: Firelocks will now not automatically close for 2 seconds after you pry + them open. + type: Tweak + - message: Firelocks are now properly predicted. + type: Fix + id: 6612 + time: '2024-05-22T11:16:20.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26582 From 687bf8caaa1f3c75224f8cdb367a358d6e1ebfbe Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Wed, 22 May 2024 05:29:19 -0700 Subject: [PATCH 017/568] Revert "Make hotplate and grill anchorable on table" (#28202) Revert "Make hotplate and grill anchorable on table (#28026)" This reverts commit 26747be232c364abcb4e43fb934c85ef0e3e1264. --- .../Prototypes/Entities/Structures/Machines/hotplate.yml | 4 +++- .../Prototypes/Entities/Structures/Machines/microwave.yml | 1 - .../Entities/Structures/Machines/reagent_grinder.yml | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Machines/hotplate.yml b/Resources/Prototypes/Entities/Structures/Machines/hotplate.yml index 05cbb60ed305..3764f1359156 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/hotplate.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/hotplate.yml @@ -15,7 +15,9 @@ mask: - TabletopMachineMask layer: - - TabletopMachineLayer + - Impassable + - MidImpassable + - LowImpassable hard: false - type: ApcPowerReceiver powerLoad: 300 diff --git a/Resources/Prototypes/Entities/Structures/Machines/microwave.yml b/Resources/Prototypes/Entities/Structures/Machines/microwave.yml index d78960030d63..fe4eb1451832 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/microwave.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/microwave.yml @@ -58,7 +58,6 @@ - TabletopMachineMask layer: - TabletopMachineLayer - hard: false - type: Sprite sprite: Structures/Machines/microwave.rsi drawdepth: SmallObjects diff --git a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml index 9894a2691fea..d6e733331330 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml @@ -32,7 +32,6 @@ - TabletopMachineMask layer: - TabletopMachineLayer - hard: false - type: Sprite sprite: Structures/Machines/juicer.rsi drawdepth: SmallObjects From 1fa493e1af0e3f31b53c40bda0d7cefe885af343 Mon Sep 17 00:00:00 2001 From: Julian Giebel Date: Wed, 22 May 2024 16:23:55 +0200 Subject: [PATCH 018/568] Implement permissive version of AddMarkup and use it for tips (#28204) * Implement permissive version of ddMarkup Use permissive ddMarkup for news article input Use permissive ddMarkup for tips * Fix doc comment format --- .../MassMedia/Ui/ArticleEditorPanel.xaml.cs | 2 +- Content.Client/Message/RichTextLabelExt.cs | 18 ++++++++++++++++++ Content.Client/Tips/TippyUIController.cs | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Content.Client/MassMedia/Ui/ArticleEditorPanel.xaml.cs b/Content.Client/MassMedia/Ui/ArticleEditorPanel.xaml.cs index 7f98e3e0c3db..5e068f1e9c53 100644 --- a/Content.Client/MassMedia/Ui/ArticleEditorPanel.xaml.cs +++ b/Content.Client/MassMedia/Ui/ArticleEditorPanel.xaml.cs @@ -76,7 +76,7 @@ private void OnPreview(BaseButton.ButtonEventArgs eventArgs) TextEditPanel.Visible = !_preview; PreviewPanel.Visible = _preview; - PreviewLabel.SetMarkup(Rope.Collapse(ContentField.TextRope)); + PreviewLabel.SetMarkupPermissive(Rope.Collapse(ContentField.TextRope)); } private void OnCancel(BaseButton.ButtonEventArgs eventArgs) diff --git a/Content.Client/Message/RichTextLabelExt.cs b/Content.Client/Message/RichTextLabelExt.cs index ab6d17bf44d5..7ff6390764b2 100644 --- a/Content.Client/Message/RichTextLabelExt.cs +++ b/Content.Client/Message/RichTextLabelExt.cs @@ -5,9 +5,27 @@ namespace Content.Client.Message; public static class RichTextLabelExt { + + + /// + /// Sets the labels markup. + /// + /// + /// Invalid markup will cause exceptions to be thrown. Don't use this for user input! + /// public static RichTextLabel SetMarkup(this RichTextLabel label, string markup) { label.SetMessage(FormattedMessage.FromMarkup(markup)); return label; } + + /// + /// Sets the labels markup.
+ /// Uses FormatedMessage.FromMarkupPermissive which treats invalid markup as text. + ///
+ public static RichTextLabel SetMarkupPermissive(this RichTextLabel label, string markup) + { + label.SetMessage(FormattedMessage.FromMarkupPermissive(markup)); + return label; + } } diff --git a/Content.Client/Tips/TippyUIController.cs b/Content.Client/Tips/TippyUIController.cs index 67c3ee45a7ed..2cc694d97d46 100644 --- a/Content.Client/Tips/TippyUIController.cs +++ b/Content.Client/Tips/TippyUIController.cs @@ -175,7 +175,7 @@ private void NextState(TippyUI tippy) sprite.LayerSetVisible("hiding", false); } sprite.Rotation = 0; - tippy.Label.SetMarkup(_currentMessage.Msg); + tippy.Label.SetMarkupPermissive(_currentMessage.Msg); tippy.Label.Visible = false; tippy.LabelPanel.Visible = false; tippy.Visible = true; From 79d4c0eee0861e8499134a34db0231a189206b0f Mon Sep 17 00:00:00 2001 From: Doctor-Cpu <77215380+Doctor-Cpu@users.noreply.github.com> Date: Wed, 22 May 2024 17:59:33 +0100 Subject: [PATCH 019/568] misc atlas fixes (#28191) misc fixes --- Resources/Maps/atlas.yml | 111 ++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 67 deletions(-) diff --git a/Resources/Maps/atlas.yml b/Resources/Maps/atlas.yml index cfd5f3b5acde..cb7dc8e778bd 100644 --- a/Resources/Maps/atlas.yml +++ b/Resources/Maps/atlas.yml @@ -89,7 +89,7 @@ entities: version: 6 0,1: ind: 0,1 - tiles: AwAAAAAAAwAAAAAAAwAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAALAAAAAAAeQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAPgAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAPgAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAPgAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAALgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAWQAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAWQAAAAAA + tiles: AwAAAAAAAwAAAAAAAwAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAALAAAAAAAeQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAPgAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAPgAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAPgAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAALgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAWQAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAWQAAAAAA version: 6 1,1: ind: 1,1 @@ -3360,6 +3360,13 @@ entities: - type: Transform pos: -44.5,-15.5 parent: 30 +- proto: AirlockArmoryGlassLocked + entities: + - uid: 1448 + components: + - type: Transform + pos: 9.5,14.5 + parent: 30 - proto: AirlockAtmosphericsGlassLocked entities: - uid: 241 @@ -3940,6 +3947,9 @@ entities: - type: Transform pos: 18.5,-6.5 parent: 30 + - type: DeviceLinkSource + lastSignals: + DoorStatus: True - uid: 2282 components: - type: Transform @@ -4053,12 +4063,6 @@ entities: parent: 30 - proto: AirlockHeadOfSecurityGlassLocked entities: - - uid: 1448 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 9.5,14.5 - parent: 30 - uid: 7378 components: - type: Transform @@ -4239,6 +4243,9 @@ entities: - type: Transform pos: -6.5,17.5 parent: 30 + - type: DeviceLinkSource + lastSignals: + DoorStatus: True - proto: AirlockMedicalGlass entities: - uid: 2408 @@ -7082,7 +7089,7 @@ entities: - uid: 2074 components: - type: Transform - pos: -10.5,8.5 + pos: -7.5,8.5 parent: 30 - uid: 2075 components: @@ -23899,14 +23906,6 @@ entities: rot: 1.5707963267948966 rad pos: -44.5,-16.5 parent: 30 -- proto: EncryptionKeyCargo - entities: - - uid: 8041 - components: - - type: Transform - parent: 8035 - - type: Physics - canCollide: False - proto: EncryptionKeyCommand entities: - uid: 6716 @@ -23931,14 +23930,6 @@ entities: parent: 6972 - type: Physics canCollide: False -- proto: EncryptionKeyMedical - entities: - - uid: 7916 - components: - - type: Transform - parent: 7915 - - type: Physics - canCollide: False - proto: EncryptionKeyScience entities: - uid: 7393 @@ -38622,6 +38613,14 @@ entities: rot: -1.5707963267948966 rad pos: -11.5,-9.5 parent: 30 +- proto: HydroponicsToolClippers + entities: + - uid: 7916 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 2.368904,17.565664 + parent: 30 - proto: HydroponicsToolHatchet entities: - uid: 1498 @@ -41743,14 +41742,6 @@ entities: parent: 30 - type: ApcPowerReceiver powerLoad: 0 - - uid: 7438 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -30.5,-11.5 - parent: 30 - - type: ApcPowerReceiver - powerLoad: 0 - uid: 7439 components: - type: Transform @@ -41773,6 +41764,12 @@ entities: rot: 3.141592653589793 rad pos: 3.5,-15.5 parent: 30 + - uid: 8041 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -31.5,-11.5 + parent: 30 - uid: 8112 components: - type: Transform @@ -46316,6 +46313,14 @@ entities: - type: Transform pos: 10.5,15.5 parent: 30 +- proto: SpawnMobMonkeyPunpun + entities: + - uid: 8035 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,3.5 + parent: 30 - proto: SpawnMobMouse entities: - uid: 1570 @@ -48692,48 +48697,20 @@ entities: showEnts: False occludes: True ents: [] -- proto: TelecomServerFilled +- proto: TelecomServerFilledCargo entities: - - uid: 7915 + - uid: 7438 components: - type: Transform - pos: -47.5,6.5 + pos: -48.5,6.5 parent: 30 - - type: ContainerContainer - containers: - key_slots: !type:Container - showEnts: False - occludes: True - ents: - - 7916 - machine_board: !type:Container - showEnts: False - occludes: True - ents: [] - machine_parts: !type:Container - showEnts: False - occludes: True - ents: [] - - uid: 8035 +- proto: TelecomServerFilledMedical + entities: + - uid: 7915 components: - type: Transform - pos: -48.5,6.5 + pos: -47.5,6.5 parent: 30 - - type: ContainerContainer - containers: - key_slots: !type:Container - showEnts: False - occludes: True - ents: - - 8041 - machine_board: !type:Container - showEnts: False - occludes: True - ents: [] - machine_parts: !type:Container - showEnts: False - occludes: True - ents: [] - proto: ToiletDirtyWater entities: - uid: 7674 From c082773b5fa50741cd6d35dea95452a940feb36e Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Wed, 22 May 2024 18:59:49 +0200 Subject: [PATCH 020/568] Remove dupe closet janitor bombsuit (#28177) * Remove dupe closet janitor bombsuit * Update Resources/migration.yml Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com> --------- Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com> --- .../Prototypes/Catalog/Cargo/cargo_service.yml | 2 +- .../Prototypes/Catalog/Fills/Crates/service.yml | 13 ------------- .../Prototypes/Catalog/Fills/Lockers/service.yml | 2 +- Resources/migration.yml | 3 +++ 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_service.yml b/Resources/Prototypes/Catalog/Cargo/cargo_service.yml index ebcd9dfc5e6e..d2ca08e1166d 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_service.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_service.yml @@ -173,7 +173,7 @@ icon: sprite: Clothing/Head/Helmets/janitor_bombsuit.rsi state: icon - product: CrateJanitorExplosive + product: ClosetJanitorBombFilled cost: 1000 category: cargoproduct-category-name-service group: market diff --git a/Resources/Prototypes/Catalog/Fills/Crates/service.yml b/Resources/Prototypes/Catalog/Fills/Crates/service.yml index 35e66ac4d351..5b16b91f8b48 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/service.yml @@ -321,16 +321,3 @@ prob: 0.1 - id: ShardGlassPlasma prob: 0.1 - -- type: entity - id: CrateJanitorExplosive - parent: ClosetJanitorBomb - name: janitorial bomb suit crate - description: Supplies a bomb suit for cleaning up any explosive compounds, buy one today! - components: - - type: StorageFill - contents: - - id: ClothingOuterSuitJanitorBomb - amount: 1 - - id: ClothingHeadHelmetJanitorBombSuit - amount: 1 diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/service.yml b/Resources/Prototypes/Catalog/Fills/Lockers/service.yml index 945ae0dd7b8c..6d0577a16eb0 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/service.yml @@ -130,7 +130,7 @@ - type: entity id: ClosetJanitorBombFilled parent: ClosetJanitorBomb - suffix: Filled + suffix: DO NOT MAP, Filled components: - type: StorageFill contents: diff --git a/Resources/migration.yml b/Resources/migration.yml index 28d63be99208..c19f1408707c 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -339,3 +339,6 @@ DrinkBottleGoldschlager: DrinkBottleGildlager # 2024-05-14 soda_dispenser: SodaDispenser chem_master: ChemMaster + +# 2024-05-21 +CrateJanitorExplosive: ClosetJanitorBombFilled From 0e0f50f3d1d76c0ba1283d5fa2a92678e3de0466 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Wed, 22 May 2024 21:16:14 -0700 Subject: [PATCH 021/568] Prioritize empty item slots when inserting (#28203) * Prioritize empty item slots when inserting * Revert "Prioritize empty item slots when inserting" This reverts commit 4272a65cba5fc18df801812b0af20123aec08409. * Prioritize empty item slots when inserting * Try drop * Check for any can insert before dropping --- .../Containers/ItemSlot/ItemSlotsSystem.cs | 76 ++++++++++++++++++- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 914b34d3c125..2e3f9ed461a3 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -197,6 +197,7 @@ private void OnInteractUsing(EntityUid uid, ItemSlotsComponent itemSlots, Intera if (!EntityManager.TryGetComponent(args.User, out HandsComponent? hands)) return; + var slots = new List(); foreach (var slot in itemSlots.Slots.Values) { if (!slot.InsertOnInteract) @@ -205,10 +206,20 @@ private void OnInteractUsing(EntityUid uid, ItemSlotsComponent itemSlots, Intera if (!CanInsert(uid, args.Used, args.User, slot, swap: slot.Swap, popup: args.User)) continue; - // Drop the held item onto the floor. Return if the user cannot drop. - if (!_handsSystem.TryDrop(args.User, args.Used, handsComp: hands)) - return; + slots.Add(slot); + } + if (slots.Count == 0) + return; + + // Drop the held item onto the floor. Return if the user cannot drop. + if (!_handsSystem.TryDrop(args.User, args.Used, handsComp: hands)) + return; + + slots.Sort(SortEmpty); + + foreach (var slot in slots) + { if (slot.Item != null) _handsSystem.TryPickupAnyHand(args.User, slot.Item.Value, handsComp: hands); @@ -333,6 +344,65 @@ public bool TryInsertFromHand(EntityUid uid, ItemSlot slot, EntityUid user, Hand Insert(uid, slot, held, user, excludeUserAudio: excludeUserAudio); return true; } + + /// + /// Tries to insert an item into any empty slot. + /// + /// The entity that has the item slots. + /// The item to be inserted. + /// The entity performing the interaction. + /// + /// If true, will exclude the user when playing sound. Does nothing client-side. + /// Useful for predicted interactions + /// + /// False if failed to insert item + public bool TryInsertEmpty(Entity ent, EntityUid item, EntityUid? user, bool excludeUserAudio = false) + { + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + var slots = new List(); + foreach (var slot in ent.Comp.Slots.Values) + { + if (slot.ContainerSlot?.ContainedEntity != null) + continue; + + if (CanInsert(ent, item, user, slot)) + slots.Add(slot); + } + + if (slots.Count == 0) + return false; + + if (user != null && _handsSystem.IsHolding(user.Value, item)) + { + if (!_handsSystem.TryDrop(user.Value, item)) + return false; + } + + slots.Sort(SortEmpty); + + foreach (var slot in slots) + { + if (TryInsert(ent, slot, item, user, excludeUserAudio: excludeUserAudio)) + return true; + } + + return false; + } + + private static int SortEmpty(ItemSlot a, ItemSlot b) + { + var aEnt = a.ContainerSlot?.ContainedEntity; + var bEnt = b.ContainerSlot?.ContainedEntity; + if (aEnt == null && bEnt == null) + return a.Priority.CompareTo(b.Priority); + + if (aEnt == null) + return -1; + + return 1; + } #endregion #region Eject From 2ccd8e234d9acfb368c51fcf79485deb3d21ad4c Mon Sep 17 00:00:00 2001 From: blueDev2 <89804215+blueDev2@users.noreply.github.com> Date: Thu, 23 May 2024 16:27:45 -0400 Subject: [PATCH 022/568] Add Fruit Tag to watermelon slice (#28226) --- .../Prototypes/Entities/Objects/Consumable/Food/produce.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index 1bd895829bde..888e4e4e3530 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -1647,6 +1647,9 @@ reagents: - ReagentId: JuiceWatermelon Quantity: 4 + - type: Tag + tags: + - Fruit - type: entity name: grapes From 5cb914e4a62270d07d2c2f87e1d4762d14bc722f Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 23 May 2024 20:28:53 +0000 Subject: [PATCH 023/568] 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 a8e3a5f155b4..9ae89dad4a3f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: mryikes - changes: - - message: New Nuke Detonation Song "Clearly Nuclear". - type: Add - id: 6113 - time: '2024-03-09T01:44:54.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25765 - author: Plykiya changes: - message: Pre-filled syringes start in inject mode now. @@ -3875,3 +3868,10 @@ id: 6612 time: '2024-05-22T11:16:20.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26582 +- author: blueDev2 + changes: + - message: Watermelon slices are now considered fruit + type: Fix + id: 6613 + time: '2024-05-23T20:27:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28226 From 594a8982602ca9e7cfe10dd77745e55b953088fb Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Thu, 23 May 2024 18:23:55 -0700 Subject: [PATCH 024/568] Fix whatever the fuck is going on in storage system slightly (#28236) * Fix whatever the fuck is going on in storage system slightly * Fix inverted check * h * Add silent bool * Silent --- .../BypassInteractionChecksComponent.cs | 6 ++ .../Inventory/InventorySystem.Slots.cs | 2 +- Content.Shared/Lock/LockSystem.cs | 9 +++ .../EntitySystems/SharedStorageSystem.cs | 62 ++++++++++--------- Content.Shared/Storage/StorageComponent.cs | 4 +- .../Entities/Mobs/Player/admin_ghost.yml | 1 + 6 files changed, 52 insertions(+), 32 deletions(-) create mode 100644 Content.Shared/Interaction/Components/BypassInteractionChecksComponent.cs diff --git a/Content.Shared/Interaction/Components/BypassInteractionChecksComponent.cs b/Content.Shared/Interaction/Components/BypassInteractionChecksComponent.cs new file mode 100644 index 000000000000..ca0ff963151a --- /dev/null +++ b/Content.Shared/Interaction/Components/BypassInteractionChecksComponent.cs @@ -0,0 +1,6 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Interaction.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class BypassInteractionChecksComponent : Component; diff --git a/Content.Shared/Inventory/InventorySystem.Slots.cs b/Content.Shared/Inventory/InventorySystem.Slots.cs index 19831278b0a5..228b788722ec 100644 --- a/Content.Shared/Inventory/InventorySystem.Slots.cs +++ b/Content.Shared/Inventory/InventorySystem.Slots.cs @@ -75,7 +75,7 @@ private void OnOpenSlotStorage(OpenSlotStorageNetworkMessage ev, EntitySessionEv if (TryGetSlotEntity(uid, ev.Slot, out var entityUid) && TryComp(entityUid, out var storageComponent)) { - _storageSystem.OpenStorageUI(entityUid.Value, uid, storageComponent); + _storageSystem.OpenStorageUI(entityUid.Value, uid, storageComponent, false); } } diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index 54f5d801ea0c..4115358d9d78 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Popups; +using Content.Shared.Storage; using Content.Shared.Storage.Components; using Content.Shared.Verbs; using Content.Shared.Wires; @@ -42,11 +43,13 @@ public override void Initialize() SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnDoAfterLock); SubscribeLocalEvent(OnDoAfterUnlock); + SubscribeLocalEvent(OnStorageInteractAttempt); SubscribeLocalEvent(OnLockToggleAttempt); SubscribeLocalEvent(OnAttemptChangePanel); SubscribeLocalEvent(OnUnanchorAttempt); } + private void OnStartup(EntityUid uid, LockComponent lockComp, ComponentStartup args) { _appearanceSystem.SetData(uid, LockVisuals.Locked, lockComp.Locked); @@ -293,6 +296,12 @@ private void OnDoAfterUnlock(EntityUid uid, LockComponent component, UnlockDoAft TryUnlock(uid, args.User, skipDoAfter: true); } + private void OnStorageInteractAttempt(Entity ent, ref StorageInteractAttemptEvent args) + { + if (ent.Comp.Locked) + args.Cancelled = true; + } + private void OnLockToggleAttempt(Entity ent, ref LockToggleAttemptEvent args) { if (args.Cancelled) diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 6dd7f9d41844..ea0c9632f0ec 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -2,17 +2,15 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.ActionBlocker; -using Content.Shared.Administration; -using Content.Shared.Administration.Managers; using Content.Shared.Containers.ItemSlots; using Content.Shared.Destructible; using Content.Shared.DoAfter; -using Content.Shared.Ghost; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Implants.Components; using Content.Shared.Input; using Content.Shared.Interaction; +using Content.Shared.Interaction.Components; using Content.Shared.Inventory; using Content.Shared.Item; using Content.Shared.Lock; @@ -40,7 +38,6 @@ public abstract class SharedStorageSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] protected readonly IRobustRandom Random = default!; - [Dependency] private readonly ISharedAdminManager _admin = default!; [Dependency] protected readonly ActionBlockerSystem ActionBlocker = default!; [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; @@ -249,17 +246,8 @@ private void OnBoundUIClosed(EntityUid uid, StorageComponent storageComp, BoundU private void AddUiVerb(EntityUid uid, StorageComponent component, GetVerbsEvent args) { - var silent = false; - if (!args.CanAccess || !args.CanInteract || TryComp(uid, out var lockComponent) && lockComponent.Locked) - { - // we allow admins to open the storage anyways - if (!_admin.HasAdminFlag(args.User, AdminFlags.Admin)) - return; - - silent = true; - } - - silent |= HasComp(args.User); + if (!CanInteract(args.User, (uid, component), args.CanAccess && args.CanInteract)) + return; // Does this player currently have the storage UI open? var uiOpen = _ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.User); @@ -274,7 +262,7 @@ private void AddUiVerb(EntityUid uid, StorageComponent component, GetVerbsEvent< } else { - OpenStorageUI(uid, args.User, component, silent); + OpenStorageUI(uid, args.User, component); } } }; @@ -298,13 +286,16 @@ private void AddUiVerb(EntityUid uid, StorageComponent component, GetVerbsEvent< /// Opens the storage UI for an entity /// /// The entity to open the UI for - public void OpenStorageUI(EntityUid uid, EntityUid entity, StorageComponent? storageComp = null, bool silent = false) + public void OpenStorageUI(EntityUid uid, EntityUid entity, StorageComponent? storageComp = null, bool silent = true) { if (!Resolve(uid, ref storageComp, false)) return; // prevent spamming bag open / honkerton honk sound silent |= TryComp(uid, out var useDelay) && UseDelay.IsDelayed((uid, useDelay)); + if (!CanInteract(entity, (uid, storageComp), silent: silent)) + return; + if (!silent) { if (!_ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key)) @@ -326,7 +317,7 @@ private void AddTransferVerbs(EntityUid uid, StorageComponent component, GetVerb var entities = component.Container.ContainedEntities; - if (entities.Count == 0 || TryComp(uid, out LockComponent? lockComponent) && lockComponent.Locked) + if (entities.Count == 0 || !CanInteract(args.User, (uid, component))) return; // if the target is storage, add a verb to transfer storage. @@ -337,7 +328,7 @@ private void AddTransferVerbs(EntityUid uid, StorageComponent component, GetVerb { Text = Loc.GetString("storage-component-transfer-verb"), IconEntity = GetNetEntity(args.Using), - Act = () => TransferEntities(uid, args.Target, args.User, component, lockComponent, targetStorage, targetLock) + Act = () => TransferEntities(uid, args.Target, args.User, component, null, targetStorage, targetLock) }; args.Verbs.Add(verb); @@ -350,7 +341,7 @@ private void AddTransferVerbs(EntityUid uid, StorageComponent component, GetVerb /// true if inserted, false otherwise private void OnInteractUsing(EntityUid uid, StorageComponent storageComp, InteractUsingEvent args) { - if (args.Handled || !storageComp.ClickInsert || TryComp(uid, out LockComponent? lockComponent) && lockComponent.Locked) + if (args.Handled || !CanInteract(args.User, (uid, storageComp), storageComp.ClickInsert, false)) return; if (HasComp(uid)) @@ -368,7 +359,7 @@ private void OnInteractUsing(EntityUid uid, StorageComponent storageComp, Intera /// private void OnActivate(EntityUid uid, StorageComponent storageComp, ActivateInWorldEvent args) { - if (args.Handled || TryComp(uid, out var lockComponent) && lockComponent.Locked) + if (args.Handled || !CanInteract(args.User, (uid, storageComp), storageComp.ClickInsert)) return; // Toggle @@ -378,7 +369,7 @@ private void OnActivate(EntityUid uid, StorageComponent storageComp, ActivateInW } else { - OpenStorageUI(uid, args.User, storageComp); + OpenStorageUI(uid, args.User, storageComp, false); } args.Handled = true; @@ -392,7 +383,7 @@ private void OnImplantActivate(EntityUid uid, StorageComponent storageComp, Open if (args.Handled) return; - OpenStorageUI(uid, args.Performer, storageComp); + OpenStorageUI(uid, args.Performer, storageComp, false); args.Handled = true; } @@ -1403,7 +1394,7 @@ public ItemSizePrototype GetMaxItemSize(Entity uid) } /// - /// Checks if a storage's UI is open by anyone when locked, and closes it unless they're an admin. + /// Checks if a storage's UI is open by anyone when locked, and closes it. /// private void OnLockToggled(EntityUid uid, StorageComponent component, ref LockToggledEvent args) { @@ -1413,11 +1404,8 @@ private void OnLockToggled(EntityUid uid, StorageComponent component, ref LockTo // Gets everyone looking at the UI foreach (var actor in _ui.GetActors(uid, StorageComponent.StorageUiKey.Key).ToList()) { - if (_admin.HasAdminFlag(actor, AdminFlags.Admin)) - continue; - - // And closes it unless they're an admin - _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, actor); + if (!CanInteract(actor, (uid, component))) + _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, actor); } } @@ -1457,7 +1445,7 @@ private void HandleToggleSlotUI(ICommonSession? session, string slot) if (!_ui.IsUiOpen(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt)) { - OpenStorageUI(storageEnt.Value, playerEnt); + OpenStorageUI(storageEnt.Value, playerEnt, silent: false); } else { @@ -1472,6 +1460,20 @@ protected void ClearCantFillReasons() #endif } + private bool CanInteract(EntityUid user, Entity storage, bool canInteract = true, bool silent = true) + { + if (HasComp(user)) + return true; + + if (!canInteract) + return false; + + var ev = new StorageInteractAttemptEvent(silent); + RaiseLocalEvent(storage, ref ev); + + return !ev.Cancelled; + } + /// /// Plays a clientside pickup animation for the specified uid. /// diff --git a/Content.Shared/Storage/StorageComponent.cs b/Content.Shared/Storage/StorageComponent.cs index ef682dd4f94c..2860f8dacfe8 100644 --- a/Content.Shared/Storage/StorageComponent.cs +++ b/Content.Shared/Storage/StorageComponent.cs @@ -5,7 +5,6 @@ using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Map; -using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -228,6 +227,9 @@ public AnimateInsertingEntitiesEvent(NetEntity storage, List storedEn } } + [ByRefEvent] + public record struct StorageInteractAttemptEvent(bool Silent, bool Cancelled = false); + [NetSerializable] [Serializable] public enum StorageVisuals : byte diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index 8171ec0053ca..b294729e07a4 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -90,6 +90,7 @@ - type: InventorySlots - type: Loadout prototypes: [ MobAghostGear ] + - type: BypassInteractionChecks - type: entity id: ActionAGhostShowSolar From 8a95cb186c922eee64c4c389c5ff247090861210 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Thu, 23 May 2024 22:43:04 -0400 Subject: [PATCH 025/568] Remove AlertType and AlertCategory (#27933) --- Content.Client/Alerts/ClientAlertsSystem.cs | 2 +- Content.Client/Revenant/RevenantSystem.cs | 3 +- .../Systems/Alerts/AlertsUIController.cs | 3 +- .../Systems/Alerts/Widgets/AlertsUI.xaml.cs | 13 +- .../Components/Mobs/AlertsComponentTests.cs | 15 +- .../Tests/Gravity/WeightlessStatusTests.cs | 8 +- .../Abilities/Mime/MimePowersComponent.cs | 8 + .../Abilities/Mime/MimePowersSystem.cs | 11 +- Content.Server/Alert/Commands/ClearAlert.cs | 4 +- Content.Server/Alert/Commands/ShowAlert.cs | 4 +- .../Atmos/Components/BarotraumaComponent.cs | 10 + .../Atmos/Components/FlammableComponent.cs | 5 + .../Atmos/EntitySystems/BarotraumaSystem.cs | 10 +- .../Atmos/EntitySystems/FlammableSystem.cs | 4 +- .../Body/Components/BloodstreamComponent.cs | 4 + .../Body/Components/InternalsComponent.cs | 6 + .../Body/Components/LungComponent.cs | 4 +- .../Body/Systems/BloodstreamSystem.cs | 4 +- .../Body/Systems/InternalsSystem.cs | 14 +- .../Chemistry/ReagentEffects/AdjustAlert.cs | 8 +- Content.Server/Clothing/MagbootsSystem.cs | 4 +- .../Ensnaring/EnsnareableSystem.Ensnaring.cs | 4 +- .../Ninja/Systems/SpaceNinjaSystem.cs | 11 +- .../Revenant/EntitySystems/RevenantSystem.cs | 2 +- .../Shuttles/Systems/ShuttleConsoleSystem.cs | 4 +- Content.Server/Silicons/Borgs/BorgSystem.cs | 18 +- .../Components/TemperatureComponent.cs | 8 + .../Temperature/Systems/TemperatureSystem.cs | 14 +- Content.Shared/Alert/AlertCategory.cs | 20 -- .../Alert/AlertCategoryPrototype.cs | 14 ++ Content.Shared/Alert/AlertKey.cs | 12 +- Content.Shared/Alert/AlertOrderPrototype.cs | 35 +-- Content.Shared/Alert/AlertPrototype.cs | 206 +++++++++--------- Content.Shared/Alert/AlertState.cs | 3 +- Content.Shared/Alert/AlertType.cs | 59 ----- Content.Shared/Alert/AlertsSystem.cs | 26 +-- Content.Shared/Alert/ClickAlertEvent.cs | 7 +- .../Buckle/Components/StrapComponent.cs | 3 +- .../Buckle/SharedBuckleSystem.Buckle.cs | 5 +- Content.Shared/Clothing/MagbootsComponent.cs | 4 + .../Pacification/PacificationSystem.cs | 5 +- .../Pacification/PacifiedComponent.cs | 4 + .../Cuffs/Components/CuffableComponent.cs | 5 + Content.Shared/Cuffs/SharedCuffableSystem.cs | 4 +- .../Damage/Components/StaminaComponent.cs | 5 + .../Damage/Systems/StaminaSystem.cs | 8 +- .../Components/EnsnareableComponent.cs | 5 + Content.Shared/Gravity/SharedGravitySystem.cs | 15 +- .../Mobs/Components/MobThresholdsComponent.cs | 28 ++- .../Mobs/Systems/MobThresholdSystem.cs | 2 +- .../Pulling/Components/PullableComponent.cs | 5 + .../Pulling/Components/PullerComponent.cs | 7 +- .../Movement/Pulling/Systems/PullingSystem.cs | 8 +- .../Ninja/Components/SpaceNinjaComponent.cs | 4 + .../Nutrition/Components/HungerComponent.cs | 14 +- .../Nutrition/Components/ThirstComponent.cs | 12 +- .../Nutrition/EntitySystems/HungerSystem.cs | 4 +- .../Nutrition/EntitySystems/ThirstSystem.cs | 2 +- .../Revenant/Components/RevenantComponent.cs | 4 + .../Shuttles/Components/PilotComponent.cs | 5 + .../Borgs/Components/BorgChassisComponent.cs | 10 +- .../StatusEffect/StatusEffectPrototype.cs | 2 +- .../StatusEffect/StatusEffectsSystem.cs | 2 +- .../Weapons/Reflect/ReflectSystem.cs | 7 +- .../Shared/Alert/AlertManagerTests.cs | 17 +- .../Shared/Alert/AlertOrderPrototypeTests.cs | 26 +-- .../Shared/Alert/AlertPrototypeTests.cs | 6 +- .../Alert/ServerAlertsComponentTests.cs | 17 +- Resources/Prototypes/Alerts/categories.yml | 35 +++ 69 files changed, 482 insertions(+), 385 deletions(-) delete mode 100644 Content.Shared/Alert/AlertCategory.cs create mode 100644 Content.Shared/Alert/AlertCategoryPrototype.cs delete mode 100644 Content.Shared/Alert/AlertType.cs create mode 100644 Resources/Prototypes/Alerts/categories.yml diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs index 223bf7876ac0..359c8957f9d7 100644 --- a/Content.Client/Alerts/ClientAlertsSystem.cs +++ b/Content.Client/Alerts/ClientAlertsSystem.cs @@ -91,7 +91,7 @@ private void OnPlayerDetached(EntityUid uid, AlertsComponent component, LocalPla ClearAlerts?.Invoke(this, EventArgs.Empty); } - public void AlertClicked(AlertType alertType) + public void AlertClicked(ProtoId alertType) { RaiseNetworkEvent(new ClickAlertEvent(alertType)); } diff --git a/Content.Client/Revenant/RevenantSystem.cs b/Content.Client/Revenant/RevenantSystem.cs index 49d29d8a5f4c..e050fe35aa2c 100644 --- a/Content.Client/Revenant/RevenantSystem.cs +++ b/Content.Client/Revenant/RevenantSystem.cs @@ -1,5 +1,4 @@ using Content.Client.Alerts; -using Content.Shared.Alert; using Content.Shared.Revenant; using Content.Shared.Revenant.Components; using Robust.Client.GameObjects; @@ -42,7 +41,7 @@ private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref private void OnUpdateAlert(Entity ent, ref UpdateAlertSpriteEvent args) { - if (args.Alert.AlertType != AlertType.Essence) + if (args.Alert.ID != ent.Comp.EssenceAlert) return; var sprite = args.SpriteViewEnt.Comp; diff --git a/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs b/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs index 3b85972a9b24..5c1951203892 100644 --- a/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs +++ b/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs @@ -7,6 +7,7 @@ using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; +using Robust.Shared.Prototypes; namespace Content.Client.UserInterface.Systems.Alerts; @@ -43,7 +44,7 @@ private void OnScreenLoad() SyncAlerts(); } - private void OnAlertPressed(object? sender, AlertType e) + private void OnAlertPressed(object? sender, ProtoId e) { _alertsSystem?.AlertClicked(e); } diff --git a/Content.Client/UserInterface/Systems/Alerts/Widgets/AlertsUI.xaml.cs b/Content.Client/UserInterface/Systems/Alerts/Widgets/AlertsUI.xaml.cs index a1a494c47b33..d6a79a81c461 100644 --- a/Content.Client/UserInterface/Systems/Alerts/Widgets/AlertsUI.xaml.cs +++ b/Content.Client/UserInterface/Systems/Alerts/Widgets/AlertsUI.xaml.cs @@ -4,6 +4,7 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Input; +using Robust.Shared.Prototypes; namespace Content.Client.UserInterface.Systems.Alerts.Widgets; @@ -21,8 +22,10 @@ public AlertsUI() RobustXamlLoader.Load(this); } - public void SyncControls(AlertsSystem alertsSystem, AlertOrderPrototype? alertOrderPrototype, - IReadOnlyDictionary alertStates) + public void SyncControls(AlertsSystem alertsSystem, + AlertOrderPrototype? alertOrderPrototype, + IReadOnlyDictionary alertStates) { // remove any controls with keys no longer present if (SyncRemoveControls(alertStates)) @@ -46,7 +49,7 @@ public void ClearAllControls() _alertControls.Clear(); } - public event EventHandler? AlertPressed; + public event EventHandler>? AlertPressed; private bool SyncRemoveControls(IReadOnlyDictionary alertStates) { @@ -88,7 +91,7 @@ private void SyncUpdateControls(AlertsSystem alertsSystem, AlertOrderPrototype? } if (_alertControls.TryGetValue(newAlert.AlertKey, out var existingAlertControl) && - existingAlertControl.Alert.AlertType == newAlert.AlertType) + existingAlertControl.Alert.ID == newAlert.ID) { // key is the same, simply update the existing control severity / cooldown existingAlertControl.SetSeverity(alertState.Severity); @@ -155,6 +158,6 @@ private void AlertControlPressed(BaseButton.ButtonEventArgs args) if (args.Event.Function != EngineKeyFunctions.UIClick) return; - AlertPressed?.Invoke(this, control.Alert.AlertType); + AlertPressed?.Invoke(this, control.Alert.ID); } } diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs index 1da77ac55892..ef4e6326cda5 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs @@ -5,7 +5,6 @@ using Robust.Client.UserInterface; using Robust.Server.Player; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs { @@ -45,8 +44,8 @@ await server.WaitAssertion(() => Assert.That(alerts, Is.Not.Null); var alertCount = alerts.Count; - alertsSystem.ShowAlert(playerUid, AlertType.Debug1); - alertsSystem.ShowAlert(playerUid, AlertType.Debug2); + alertsSystem.ShowAlert(playerUid, "Debug1"); + alertsSystem.ShowAlert(playerUid, "Debug2"); Assert.That(alerts, Has.Count.EqualTo(alertCount + 2)); }); @@ -87,14 +86,14 @@ static AlertsUI FindAlertsUI(Control control) // we should be seeing 3 alerts - our health, and the 2 debug alerts, in a specific order. Assert.That(clientAlertsUI.AlertContainer.ChildCount, Is.GreaterThanOrEqualTo(3)); var alertControls = clientAlertsUI.AlertContainer.Children.Select(c => (AlertControl) c); - var alertIDs = alertControls.Select(ac => ac.Alert.AlertType).ToArray(); - var expectedIDs = new[] { AlertType.HumanHealth, AlertType.Debug1, AlertType.Debug2 }; + var alertIDs = alertControls.Select(ac => ac.Alert.ID).ToArray(); + var expectedIDs = new[] { "HumanHealth", "Debug1", "Debug2" }; Assert.That(alertIDs, Is.SupersetOf(expectedIDs)); }); await server.WaitAssertion(() => { - alertsSystem.ClearAlert(playerUid, AlertType.Debug1); + alertsSystem.ClearAlert(playerUid, "Debug1"); }); await pair.RunTicksSync(5); @@ -104,8 +103,8 @@ await client.WaitAssertion(() => // we should be seeing 2 alerts now because one was cleared Assert.That(clientAlertsUI.AlertContainer.ChildCount, Is.GreaterThanOrEqualTo(2)); var alertControls = clientAlertsUI.AlertContainer.Children.Select(c => (AlertControl) c); - var alertIDs = alertControls.Select(ac => ac.Alert.AlertType).ToArray(); - var expectedIDs = new[] { AlertType.HumanHealth, AlertType.Debug2 }; + var alertIDs = alertControls.Select(ac => ac.Alert.ID).ToArray(); + var expectedIDs = new[] { "HumanHealth", "Debug2" }; Assert.That(alertIDs, Is.SupersetOf(expectedIDs)); }); diff --git a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs index 0ad198d6ef2e..74641126aee8 100644 --- a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs +++ b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs @@ -1,5 +1,6 @@ using Content.Server.Gravity; using Content.Shared.Alert; +using Content.Shared.Gravity; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Gravity @@ -38,6 +39,7 @@ public async Task WeightlessStatusTest() var entityManager = server.ResolveDependency(); var alertsSystem = server.ResolveDependency().GetEntitySystem(); + var weightlessAlert = SharedGravitySystem.WeightlessAlert; EntityUid human = default; @@ -56,7 +58,7 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { // No gravity without a gravity generator - Assert.That(alertsSystem.IsShowingAlert(human, AlertType.Weightless)); + Assert.That(alertsSystem.IsShowingAlert(human, weightlessAlert)); generatorUid = entityManager.SpawnEntity("WeightlessGravityGeneratorDummy", entityManager.GetComponent(human).Coordinates); }); @@ -66,7 +68,7 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - Assert.That(alertsSystem.IsShowingAlert(human, AlertType.Weightless), Is.False); + Assert.That(alertsSystem.IsShowingAlert(human, weightlessAlert), Is.False); // This should kill gravity entityManager.DeleteEntity(generatorUid); @@ -76,7 +78,7 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - Assert.That(alertsSystem.IsShowingAlert(human, AlertType.Weightless)); + Assert.That(alertsSystem.IsShowingAlert(human, weightlessAlert)); }); await pair.RunTicksSync(10); diff --git a/Content.Server/Abilities/Mime/MimePowersComponent.cs b/Content.Server/Abilities/Mime/MimePowersComponent.cs index fd4fc2c2af9c..d56644ed1914 100644 --- a/Content.Server/Abilities/Mime/MimePowersComponent.cs +++ b/Content.Server/Abilities/Mime/MimePowersComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Alert; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -47,5 +48,12 @@ public sealed partial class MimePowersComponent : Component /// [DataField("vowCooldown")] public TimeSpan VowCooldown = TimeSpan.FromMinutes(5); + + [DataField] + public ProtoId VowAlert = "VowOfSilence"; + + [DataField] + public ProtoId VowBrokenAlert = "VowBroken"; + } } diff --git a/Content.Server/Abilities/Mime/MimePowersSystem.cs b/Content.Server/Abilities/Mime/MimePowersSystem.cs index 629fe993448e..f3bf6e703f52 100644 --- a/Content.Server/Abilities/Mime/MimePowersSystem.cs +++ b/Content.Server/Abilities/Mime/MimePowersSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Popups; -using Content.Server.Speech.Muting; using Content.Shared.Actions; using Content.Shared.Actions.Events; using Content.Shared.Alert; @@ -54,7 +53,7 @@ public override void Update(float frameTime) private void OnComponentInit(EntityUid uid, MimePowersComponent component, ComponentInit args) { EnsureComp(uid); - _alertsSystem.ShowAlert(uid, AlertType.VowOfSilence); + _alertsSystem.ShowAlert(uid, component.VowAlert); _actionsSystem.AddAction(uid, ref component.InvisibleWallActionEntity, component.InvisibleWallAction, uid); } @@ -115,8 +114,8 @@ public void BreakVow(EntityUid uid, MimePowersComponent? mimePowers = null) mimePowers.VowBroken = true; mimePowers.VowRepentTime = _timing.CurTime + mimePowers.VowCooldown; RemComp(uid); - _alertsSystem.ClearAlert(uid, AlertType.VowOfSilence); - _alertsSystem.ShowAlert(uid, AlertType.VowBroken); + _alertsSystem.ClearAlert(uid, mimePowers.VowAlert); + _alertsSystem.ShowAlert(uid, mimePowers.VowBrokenAlert); _actionsSystem.RemoveAction(uid, mimePowers.InvisibleWallActionEntity); } @@ -138,8 +137,8 @@ public void RetakeVow(EntityUid uid, MimePowersComponent? mimePowers = null) mimePowers.ReadyToRepent = false; mimePowers.VowBroken = false; AddComp(uid); - _alertsSystem.ClearAlert(uid, AlertType.VowBroken); - _alertsSystem.ShowAlert(uid, AlertType.VowOfSilence); + _alertsSystem.ClearAlert(uid, mimePowers.VowAlert); + _alertsSystem.ShowAlert(uid, mimePowers.VowBrokenAlert); _actionsSystem.AddAction(uid, ref mimePowers.InvisibleWallActionEntity, mimePowers.InvisibleWallAction, uid); } } diff --git a/Content.Server/Alert/Commands/ClearAlert.cs b/Content.Server/Alert/Commands/ClearAlert.cs index 73a6ca52c70f..2e317de75472 100644 --- a/Content.Server/Alert/Commands/ClearAlert.cs +++ b/Content.Server/Alert/Commands/ClearAlert.cs @@ -40,13 +40,13 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var alertType = args[0]; var alertsSystem = _e.System(); - if (!alertsSystem.TryGet(Enum.Parse(alertType), out var alert)) + if (!alertsSystem.TryGet(alertType, out var alert)) { shell.WriteLine("unrecognized alertType " + alertType); return; } - alertsSystem.ClearAlert(attachedEntity, alert.AlertType); + alertsSystem.ClearAlert(attachedEntity, alert.ID); } } } diff --git a/Content.Server/Alert/Commands/ShowAlert.cs b/Content.Server/Alert/Commands/ShowAlert.cs index f37ab23f2fa3..cae24ff33604 100644 --- a/Content.Server/Alert/Commands/ShowAlert.cs +++ b/Content.Server/Alert/Commands/ShowAlert.cs @@ -41,7 +41,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var alertType = args[0]; var severity = args[1]; var alertsSystem = _e.System(); - if (!alertsSystem.TryGet(Enum.Parse(alertType), out var alert)) + if (!alertsSystem.TryGet(alertType, out var alert)) { shell.WriteLine("unrecognized alertType " + alertType); return; @@ -53,7 +53,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) } short? severity1 = sevint == -1 ? null : sevint; - alertsSystem.ShowAlert(attachedEntity, alert.AlertType, severity1, null); + alertsSystem.ShowAlert(attachedEntity, alert.ID, severity1, null); } } } diff --git a/Content.Server/Atmos/Components/BarotraumaComponent.cs b/Content.Server/Atmos/Components/BarotraumaComponent.cs index 4e29699872e7..d261c5ab0308 100644 --- a/Content.Server/Atmos/Components/BarotraumaComponent.cs +++ b/Content.Server/Atmos/Components/BarotraumaComponent.cs @@ -1,5 +1,7 @@ +using Content.Shared.Alert; using Content.Shared.Damage; using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; namespace Content.Server.Atmos.Components { @@ -46,5 +48,13 @@ public sealed partial class BarotraumaComponent : Component [ViewVariables(VVAccess.ReadWrite)] public bool HasImmunity = false; + [DataField] + public ProtoId HighPressureAlert = "HighPressure"; + + [DataField] + public ProtoId LowPressureAlert = "LowPressure"; + + [DataField] + public ProtoId PressureAlertCategory = "Pressure"; } } diff --git a/Content.Server/Atmos/Components/FlammableComponent.cs b/Content.Server/Atmos/Components/FlammableComponent.cs index e00f5efbdc5c..9ae99a15136e 100644 --- a/Content.Server/Atmos/Components/FlammableComponent.cs +++ b/Content.Server/Atmos/Components/FlammableComponent.cs @@ -1,5 +1,7 @@ +using Content.Shared.Alert; using Content.Shared.Damage; using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Prototypes; namespace Content.Server.Atmos.Components { @@ -77,5 +79,8 @@ public sealed partial class FlammableComponent : Component /// [DataField, ViewVariables(VVAccess.ReadWrite)] public float FirestackFade = -0.1f; + + [DataField] + public ProtoId FireAlert = "Fire"; } } diff --git a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs index 98a5ffa70a86..ec508790ba87 100644 --- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs +++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs @@ -245,7 +245,7 @@ public override void Update(float frameTime) _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} started taking low pressure damage"); } - _alertsSystem.ShowAlert(uid, AlertType.LowPressure, 2); + _alertsSystem.ShowAlert(uid, barotrauma.LowPressureAlert, 2); } else if (pressure >= Atmospherics.HazardHighPressure) { @@ -260,7 +260,7 @@ public override void Update(float frameTime) _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} started taking high pressure damage"); } - _alertsSystem.ShowAlert(uid, AlertType.HighPressure, 2); + _alertsSystem.ShowAlert(uid, barotrauma.HighPressureAlert, 2); } else { @@ -275,13 +275,13 @@ public override void Update(float frameTime) switch (pressure) { case <= Atmospherics.WarningLowPressure: - _alertsSystem.ShowAlert(uid, AlertType.LowPressure, 1); + _alertsSystem.ShowAlert(uid, barotrauma.LowPressureAlert, 1); break; case >= Atmospherics.WarningHighPressure: - _alertsSystem.ShowAlert(uid, AlertType.HighPressure, 1); + _alertsSystem.ShowAlert(uid, barotrauma.HighPressureAlert, 1); break; default: - _alertsSystem.ClearAlertCategory(uid, AlertCategory.Pressure); + _alertsSystem.ClearAlertCategory(uid, barotrauma.PressureAlertCategory); break; } } diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index 4a8cbbdc884d..e8721920dd8c 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -415,11 +415,11 @@ public override void Update(float frameTime) if (!flammable.OnFire) { - _alertsSystem.ClearAlert(uid, AlertType.Fire); + _alertsSystem.ClearAlert(uid, flammable.FireAlert); continue; } - _alertsSystem.ShowAlert(uid, AlertType.Fire); + _alertsSystem.ShowAlert(uid, flammable.FireAlert); if (flammable.FireStacks > 0) { diff --git a/Content.Server/Body/Components/BloodstreamComponent.cs b/Content.Server/Body/Components/BloodstreamComponent.cs index 1d8aa9ffd3d4..a6d2afab2191 100644 --- a/Content.Server/Body/Components/BloodstreamComponent.cs +++ b/Content.Server/Body/Components/BloodstreamComponent.cs @@ -1,5 +1,6 @@ using Content.Server.Body.Systems; using Content.Server.Chemistry.EntitySystems; +using Content.Shared.Alert; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.Damage; @@ -171,5 +172,8 @@ public sealed partial class BloodstreamComponent : Component /// [ViewVariables(VVAccess.ReadWrite)] public TimeSpan StatusTime; + + [DataField] + public ProtoId BleedingAlert = "Bleed"; } } diff --git a/Content.Server/Body/Components/InternalsComponent.cs b/Content.Server/Body/Components/InternalsComponent.cs index 18caab8dcf04..098f1789218f 100644 --- a/Content.Server/Body/Components/InternalsComponent.cs +++ b/Content.Server/Body/Components/InternalsComponent.cs @@ -1,3 +1,6 @@ +using Content.Shared.Alert; +using Robust.Shared.Prototypes; + namespace Content.Server.Body.Components { /// @@ -18,5 +21,8 @@ public sealed partial class InternalsComponent : Component [ViewVariables(VVAccess.ReadWrite)] [DataField] public TimeSpan Delay = TimeSpan.FromSeconds(3); + + [DataField] + public ProtoId InternalsAlert = "Internals"; } } diff --git a/Content.Server/Body/Components/LungComponent.cs b/Content.Server/Body/Components/LungComponent.cs index 46600b302073..72af4d9e63a9 100644 --- a/Content.Server/Body/Components/LungComponent.cs +++ b/Content.Server/Body/Components/LungComponent.cs @@ -1,8 +1,8 @@ -using Content.Server.Atmos; using Content.Server.Body.Systems; using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Chemistry.Components; +using Robust.Shared.Prototypes; namespace Content.Server.Body.Components; @@ -33,5 +33,5 @@ public sealed partial class LungComponent : Component /// The type of gas this lung needs. Used only for the breathing alerts, not actual metabolism. /// [DataField] - public AlertType Alert = AlertType.LowOxygen; + public ProtoId Alert = "LowOxygen"; } diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index ddeb154e790a..f961307fc6a0 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -392,11 +392,11 @@ public bool TryModifyBleedAmount(EntityUid uid, float amount, BloodstreamCompone component.BleedAmount = Math.Clamp(component.BleedAmount, 0, component.MaxBleedAmount); if (component.BleedAmount == 0) - _alertsSystem.ClearAlert(uid, AlertType.Bleed); + _alertsSystem.ClearAlert(uid, component.BleedingAlert); else { var severity = (short) Math.Clamp(Math.Round(component.BleedAmount, MidpointRounding.ToZero), 0, 10); - _alertsSystem.ShowAlert(uid, AlertType.Bleed, severity); + _alertsSystem.ShowAlert(uid, component.BleedingAlert, severity); } return true; diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index 8afd1c767f66..c1e1de2baad8 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -144,12 +144,12 @@ private void OnDoAfter(Entity ent, ref InternalsDoAfterEvent private void OnInternalsStartup(Entity ent, ref ComponentStartup args) { - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); + _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } private void OnInternalsShutdown(Entity ent, ref ComponentShutdown args) { - _alerts.ClearAlert(ent, AlertType.Internals); + _alerts.ClearAlert(ent, ent.Comp.InternalsAlert); } private void OnInhaleLocation(Entity ent, ref InhaleLocationEvent args) @@ -159,7 +159,7 @@ private void OnInhaleLocation(Entity ent, ref InhaleLocation var gasTank = Comp(ent.Comp.GasTankEntity!.Value); args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume); // TODO: Should listen to gas tank updates instead I guess? - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); + _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } } public void DisconnectBreathTool(Entity ent) @@ -173,7 +173,7 @@ public void DisconnectBreathTool(Entity ent) DisconnectTank(ent); } - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); + _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } public void ConnectBreathTool(Entity ent, EntityUid toolEntity) @@ -184,7 +184,7 @@ public void ConnectBreathTool(Entity ent, EntityUid toolEnti } ent.Comp.BreathToolEntity = toolEntity; - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); + _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } public void DisconnectTank(InternalsComponent? component) @@ -196,7 +196,7 @@ public void DisconnectTank(InternalsComponent? component) _gasTank.DisconnectFromInternals((component.GasTankEntity.Value, tank)); component.GasTankEntity = null; - _alerts.ShowAlert(component.Owner, AlertType.Internals, GetSeverity(component)); + _alerts.ShowAlert(component.Owner, component.InternalsAlert, GetSeverity(component)); } public bool TryConnectTank(Entity ent, EntityUid tankEntity) @@ -208,7 +208,7 @@ public bool TryConnectTank(Entity ent, EntityUid tankEntity) _gasTank.DisconnectFromInternals((ent.Comp.GasTankEntity.Value, tank)); ent.Comp.GasTankEntity = tankEntity; - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); + _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); return true; } diff --git a/Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs b/Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs index 8d475570ad01..40858176bd15 100644 --- a/Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs +++ b/Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs @@ -10,8 +10,8 @@ public sealed partial class AdjustAlert : ReagentEffect /// /// The specific Alert that will be adjusted /// - [DataField("alertType", required: true)] - public AlertType Type; + [DataField(required: true)] + public ProtoId AlertType; /// /// If true, the alert is removed after Time seconds. If Time was not specified the alert is removed immediately. @@ -42,7 +42,7 @@ public override void Effect(ReagentEffectArgs args) if (Clear && Time <= 0) { - alertSys.ClearAlert(args.SolutionEntity, Type); + alertSys.ClearAlert(args.SolutionEntity, AlertType); } else { @@ -52,7 +52,7 @@ public override void Effect(ReagentEffectArgs args) if ((ShowCooldown || Clear) && Time > 0) cooldown = (timing.CurTime, timing.CurTime + TimeSpan.FromSeconds(Time)); - alertSys.ShowAlert(args.SolutionEntity, Type, cooldown: cooldown, autoRemove: Clear, showCooldown: ShowCooldown); + alertSys.ShowAlert(args.SolutionEntity, AlertType, cooldown: cooldown, autoRemove: Clear, showCooldown: ShowCooldown); } } diff --git a/Content.Server/Clothing/MagbootsSystem.cs b/Content.Server/Clothing/MagbootsSystem.cs index f12558389e3b..3838ad168d1b 100644 --- a/Content.Server/Clothing/MagbootsSystem.cs +++ b/Content.Server/Clothing/MagbootsSystem.cs @@ -29,11 +29,11 @@ protected override void UpdateMagbootEffects(EntityUid parent, EntityUid uid, bo if (state) { - _alerts.ShowAlert(parent, AlertType.Magboots); + _alerts.ShowAlert(parent, component.MagbootsAlert); } else { - _alerts.ClearAlert(parent, AlertType.Magboots); + _alerts.ClearAlert(parent, component.MagbootsAlert); } } diff --git a/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs b/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs index 105a8f9720de..51d3242ce4f2 100644 --- a/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs +++ b/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs @@ -163,8 +163,8 @@ public void ForceFree(EntityUid ensnare, EnsnaringComponent component) public void UpdateAlert(EntityUid target, EnsnareableComponent component) { if (!component.IsEnsnared) - _alerts.ClearAlert(target, AlertType.Ensnared); + _alerts.ClearAlert(target, component.EnsnaredAlert); else - _alerts.ShowAlert(target, AlertType.Ensnared); + _alerts.ShowAlert(target, component.EnsnaredAlert); } } diff --git a/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs b/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs index 1dfaf4f3393b..0c1e88653fa4 100644 --- a/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs +++ b/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs @@ -102,20 +102,23 @@ private int Download(EntityUid uid, List ids) /// public void SetSuitPowerAlert(EntityUid uid, SpaceNinjaComponent? comp = null) { - if (!Resolve(uid, ref comp, false) || comp.Deleted || comp.Suit == null) + if (!Resolve(uid, ref comp, false)) + return; + + if (comp.Deleted || comp.Suit == null) { - _alerts.ClearAlert(uid, AlertType.SuitPower); + _alerts.ClearAlert(uid, comp.SuitPowerAlert); return; } if (GetNinjaBattery(uid, out _, out var battery)) { var severity = ContentHelpers.RoundToLevels(MathF.Max(0f, battery.CurrentCharge), battery.MaxCharge, 8); - _alerts.ShowAlert(uid, AlertType.SuitPower, (short) severity); + _alerts.ShowAlert(uid, comp.SuitPowerAlert, (short) severity); } else { - _alerts.ClearAlert(uid, AlertType.SuitPower); + _alerts.ClearAlert(uid, comp.SuitPowerAlert); } } diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index 86be70c41fea..c390432f3a1b 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -141,7 +141,7 @@ public bool ChangeEssenceAmount(EntityUid uid, FixedPoint2 amount, RevenantCompo if (TryComp(uid, out var store)) _store.UpdateUserInterface(uid, uid, store); - _alerts.ShowAlert(uid, AlertType.Essence); + _alerts.ShowAlert(uid, component.EssenceAlert); if (component.Essence <= 0) { diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs index 2b5769881d27..7a19fd13b2e9 100644 --- a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs @@ -317,7 +317,7 @@ public void AddPilot(EntityUid uid, EntityUid entity, ShuttleConsoleComponent co component.SubscribedPilots.Add(entity); - _alertsSystem.ShowAlert(entity, AlertType.PilotingShuttle); + _alertsSystem.ShowAlert(entity, pilotComponent.PilotingAlert); pilotComponent.Console = uid; ActionBlockerSystem.UpdateCanMove(entity); @@ -339,7 +339,7 @@ public void RemovePilot(EntityUid pilotUid, PilotComponent pilotComponent) if (!helm.SubscribedPilots.Remove(pilotUid)) return; - _alertsSystem.ClearAlert(pilotUid, AlertType.PilotingShuttle); + _alertsSystem.ClearAlert(pilotUid, pilotComponent.PilotingAlert); _popup.PopupEntity(Loc.GetString("shuttle-pilot-end"), pilotUid, pilotUid); diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index ceab044d4c1c..97adfd00eb4c 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -84,7 +84,7 @@ public override void Initialize() private void OnMapInit(EntityUid uid, BorgChassisComponent component, MapInitEvent args) { - UpdateBatteryAlert(uid); + UpdateBatteryAlert((uid, component)); _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); } @@ -183,7 +183,7 @@ private void OnMobStateChanged(EntityUid uid, BorgChassisComponent component, Mo private void OnPowerCellChanged(EntityUid uid, BorgChassisComponent component, PowerCellChangedEvent args) { - UpdateBatteryAlert(uid); + UpdateBatteryAlert((uid, component)); if (!TryComp(uid, out var draw)) return; @@ -256,12 +256,12 @@ private void OnBrainPointAttempt(EntityUid uid, BorgBrainComponent component, Po args.Cancel(); } - private void UpdateBatteryAlert(EntityUid uid, PowerCellSlotComponent? slotComponent = null) + private void UpdateBatteryAlert(Entity ent, PowerCellSlotComponent? slotComponent = null) { - if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery, slotComponent)) + if (!_powerCell.TryGetBatteryFromSlot(ent, out var battery, slotComponent)) { - _alerts.ClearAlert(uid, AlertType.BorgBattery); - _alerts.ShowAlert(uid, AlertType.BorgBatteryNone); + _alerts.ClearAlert(ent, ent.Comp.BatteryAlert); + _alerts.ShowAlert(ent, ent.Comp.NoBatteryAlert); return; } @@ -269,13 +269,13 @@ private void UpdateBatteryAlert(EntityUid uid, PowerCellSlotComponent? slotCompo // we make sure 0 only shows if they have absolutely no battery. // also account for floating point imprecision - if (chargePercent == 0 && _powerCell.HasDrawCharge(uid, cell: slotComponent)) + if (chargePercent == 0 && _powerCell.HasDrawCharge(ent, cell: slotComponent)) { chargePercent = 1; } - _alerts.ClearAlert(uid, AlertType.BorgBatteryNone); - _alerts.ShowAlert(uid, AlertType.BorgBattery, chargePercent); + _alerts.ClearAlert(ent, ent.Comp.NoBatteryAlert); + _alerts.ShowAlert(ent, ent.Comp.BatteryAlert, chargePercent); } /// diff --git a/Content.Server/Temperature/Components/TemperatureComponent.cs b/Content.Server/Temperature/Components/TemperatureComponent.cs index ec00a570f964..3bfa12f26938 100644 --- a/Content.Server/Temperature/Components/TemperatureComponent.cs +++ b/Content.Server/Temperature/Components/TemperatureComponent.cs @@ -1,7 +1,9 @@ using Content.Server.Temperature.Systems; +using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Damage; using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; namespace Content.Server.Temperature.Components; @@ -78,4 +80,10 @@ public float HeatCapacity /// [DataField] public bool TakingDamage = false; + + [DataField] + public ProtoId HotAlert = "Hot"; + + [DataField] + public ProtoId ColdAlert = "Cold"; } diff --git a/Content.Server/Temperature/Systems/TemperatureSystem.cs b/Content.Server/Temperature/Systems/TemperatureSystem.cs index 6c9e99e5f3b9..23c8cb6783f1 100644 --- a/Content.Server/Temperature/Systems/TemperatureSystem.cs +++ b/Content.Server/Temperature/Systems/TemperatureSystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Rejuvenate; using Content.Shared.Temperature; using Robust.Shared.Physics.Components; +using Robust.Shared.Prototypes; namespace Content.Server.Temperature.Systems; @@ -33,6 +34,9 @@ public sealed class TemperatureSystem : EntitySystem private float _accumulatedFrametime; + [ValidatePrototypeId] + public const string TemperatureAlertCategory = "Temperature"; + public override void Initialize() { SubscribeLocalEvent(EnqueueDamage); @@ -180,13 +184,13 @@ private void OnRejuvenate(EntityUid uid, TemperatureComponent comp, RejuvenateEv private void ServerAlert(EntityUid uid, AlertsComponent status, OnTemperatureChangeEvent args) { - AlertType type; + ProtoId type; float threshold; float idealTemp; if (!TryComp(uid, out var temperature)) { - _alerts.ClearAlertCategory(uid, AlertCategory.Temperature); + _alerts.ClearAlertCategory(uid, TemperatureAlertCategory); return; } @@ -203,12 +207,12 @@ private void ServerAlert(EntityUid uid, AlertsComponent status, OnTemperatureCha if (args.CurrentTemperature <= idealTemp) { - type = AlertType.Cold; + type = temperature.ColdAlert; threshold = temperature.ColdDamageThreshold; } else { - type = AlertType.Hot; + type = temperature.HotAlert; threshold = temperature.HeatDamageThreshold; } @@ -230,7 +234,7 @@ private void ServerAlert(EntityUid uid, AlertsComponent status, OnTemperatureCha break; case > 0.66f: - _alerts.ClearAlertCategory(uid, AlertCategory.Temperature); + _alerts.ClearAlertCategory(uid, TemperatureAlertCategory); break; } } diff --git a/Content.Shared/Alert/AlertCategory.cs b/Content.Shared/Alert/AlertCategory.cs deleted file mode 100644 index 7450f585a4e0..000000000000 --- a/Content.Shared/Alert/AlertCategory.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Content.Shared.Alert; - -/// -/// Every category of alert. Corresponds to category field in alert prototypes defined in YML -/// -public enum AlertCategory -{ - Pressure, - Temperature, - Breathing, - Buckled, - Health, - Internals, - Stamina, - Piloting, - Hunger, - Thirst, - Toxins, - Battery -} diff --git a/Content.Shared/Alert/AlertCategoryPrototype.cs b/Content.Shared/Alert/AlertCategoryPrototype.cs new file mode 100644 index 000000000000..7c7d04752149 --- /dev/null +++ b/Content.Shared/Alert/AlertCategoryPrototype.cs @@ -0,0 +1,14 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Alert; + +/// +/// This is a prototype for a category for marking alerts as mutually exclusive. +/// +[Prototype] +public sealed partial class AlertCategoryPrototype : IPrototype +{ + /// + [IdDataField] + public string ID { get; } = default!; +} diff --git a/Content.Shared/Alert/AlertKey.cs b/Content.Shared/Alert/AlertKey.cs index c784af4cd48a..c5c3a7643eca 100644 --- a/Content.Shared/Alert/AlertKey.cs +++ b/Content.Shared/Alert/AlertKey.cs @@ -1,5 +1,5 @@ -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.Manager; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; namespace Content.Shared.Alert; @@ -11,13 +11,13 @@ namespace Content.Shared.Alert; [Serializable, NetSerializable] public struct AlertKey { - public AlertType? AlertType { get; private set; } = Alert.AlertType.Error; - public readonly AlertCategory? AlertCategory; + public ProtoId? AlertType { get; private set; } = default!; + public readonly ProtoId? AlertCategory; /// NOTE: if the alert has a category you must pass the category for this to work /// properly as a key. I.e. if the alert has a category and you pass only the alert type, and you /// compare this to another AlertKey that has both the category and the same alert type, it will not consider them equal. - public AlertKey(AlertType? alertType, AlertCategory? alertCategory) + public AlertKey(ProtoId? alertType, ProtoId? alertCategory) { AlertCategory = alertCategory; AlertType = alertType; @@ -49,7 +49,7 @@ public override int GetHashCode() /// alert category, must not be null /// An alert key for the provided alert category. This must only be used for /// queries and never storage, as it is lacking an alert type. - public static AlertKey ForCategory(AlertCategory category) + public static AlertKey ForCategory(ProtoId category) { return new(null, category); } diff --git a/Content.Shared/Alert/AlertOrderPrototype.cs b/Content.Shared/Alert/AlertOrderPrototype.cs index 8279d592b4bc..af4241a27e71 100644 --- a/Content.Shared/Alert/AlertOrderPrototype.cs +++ b/Content.Shared/Alert/AlertOrderPrototype.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Alert /// /// Defines the order of alerts so they show up in a consistent order. /// - [Prototype("alertOrder")] + [Prototype] [DataDefinition] public sealed partial class AlertOrderPrototype : IPrototype, IComparer { @@ -15,7 +15,7 @@ public sealed partial class AlertOrderPrototype : IPrototype, IComparer(alert)] = i++; + _typeToIdx[alert] = i++; break; case "category": - _categoryToIdx[Enum.Parse(alert)] = i++; + _categoryToIdx[alert] = i++; break; default: throw new ArgumentException(); @@ -58,17 +58,17 @@ public sealed partial class AlertOrderPrototype : IPrototype, IComparer _typeToIdx = new(); - private readonly Dictionary _categoryToIdx = new(); + private readonly Dictionary, int> _typeToIdx = new(); + private readonly Dictionary, int> _categoryToIdx = new(); private int GetOrderIndex(AlertPrototype alert) { - if (_typeToIdx.TryGetValue(alert.AlertType, out var idx)) + if (_typeToIdx.TryGetValue(alert.ID, out var idx)) { return idx; } if (alert.Category != null && - _categoryToIdx.TryGetValue((AlertCategory) alert.Category, out idx)) + _categoryToIdx.TryGetValue(alert.Category.Value, out idx)) { return idx; } @@ -78,20 +78,25 @@ private int GetOrderIndex(AlertPrototype alert) public int Compare(AlertPrototype? x, AlertPrototype? y) { - if ((x == null) && (y == null)) return 0; - if (x == null) return 1; - if (y == null) return -1; + if (x == null && y == null) + return 0; + if (x == null) + return 1; + if (y == null) + return -1; var idx = GetOrderIndex(x); var idy = GetOrderIndex(y); if (idx == -1 && idy == -1) { // break ties by type value // Must cast to int to avoid integer overflow when subtracting (enum's unsigned) - return (int)x.AlertType - (int)y.AlertType; + return string.Compare(x.ID, y.ID, StringComparison.InvariantCulture); } - if (idx == -1) return 1; - if (idy == -1) return -1; + if (idx == -1) + return 1; + if (idy == -1) + return -1; var result = idx - idy; // not strictly necessary (we don't care about ones that go at the same index) // but it makes the sort stable @@ -99,7 +104,7 @@ public int Compare(AlertPrototype? x, AlertPrototype? y) { // break ties by type value // Must cast to int to avoid integer overflow when subtracting (enum's unsigned) - return (int)x.AlertType - (int)y.AlertType; + return string.Compare(x.ID, y.ID, StringComparison.InvariantCulture); } return result; diff --git a/Content.Shared/Alert/AlertPrototype.cs b/Content.Shared/Alert/AlertPrototype.cs index 248cc00ba401..f53da27c0dea 100644 --- a/Content.Shared/Alert/AlertPrototype.cs +++ b/Content.Shared/Alert/AlertPrototype.cs @@ -1,120 +1,116 @@ using Robust.Shared.Prototypes; using Robust.Shared.Utility; -namespace Content.Shared.Alert +namespace Content.Shared.Alert; + +/// +/// An alert popup with associated icon, tooltip, and other data. +/// +[Prototype] +public sealed partial class AlertPrototype : IPrototype { /// - /// An alert popup with associated icon, tooltip, and other data. + /// Type of alert, no 2 alert prototypes should have the same one. /// - [Prototype("alert")] - public sealed partial class AlertPrototype : IPrototype - { - [ViewVariables] - string IPrototype.ID => AlertType.ToString(); - - /// - /// Type of alert, no 2 alert prototypes should have the same one. - /// - [IdDataField] - public AlertType AlertType { get; private set; } - - /// - /// List of icons to use for this alert. Each entry corresponds to a different severity level, starting from the - /// minimum and incrementing upwards. If severities are not supported, the first entry is used. - /// - [DataField("icons", required: true)] - public List Icons = new(); - - /// - /// An entity used for displaying the in the UI control. - /// - [DataField] - public EntProtoId AlertViewEntity = "AlertSpriteView"; - - /// - /// Name to show in tooltip window. Accepts formatting. - /// - [DataField("name")] - public string Name { get; private set; } = ""; - - /// - /// Description to show in tooltip window. Accepts formatting. - /// - [DataField("description")] - public string Description { get; private set; } = ""; - - /// - /// Category the alert belongs to. Only one alert of a given category - /// can be shown at a time. If one is shown while another is already being shown, - /// it will be replaced. This can be useful for categories of alerts which should naturally - /// replace each other and are mutually exclusive, for example lowpressure / highpressure, - /// hot / cold. If left unspecified, the alert will not replace or be replaced by any other alerts. - /// - [DataField("category")] - public AlertCategory? Category { get; private set; } - - /// - /// Key which is unique w.r.t category semantics (alerts with same category have equal keys, - /// alerts with no category have different keys). - /// - public AlertKey AlertKey => new(AlertType, Category); - - /// - /// -1 (no effect) unless MaxSeverity is specified. Defaults to 1. Minimum severity level supported by this state. - /// - public short MinSeverity => MaxSeverity == -1 ? (short) -1 : _minSeverity; - - [DataField("minSeverity")] private short _minSeverity = 1; - - /// - /// Maximum severity level supported by this state. -1 (default) indicates - /// no severity levels are supported by the state. - /// - [DataField("maxSeverity")] - public short MaxSeverity = -1; - - /// - /// Indicates whether this state support severity levels - /// - public bool SupportsSeverity => MaxSeverity != -1; - - /// - /// Defines what to do when the alert is clicked. - /// This will always be null on clientside. - /// - [DataField("onClick", serverOnly: true)] - public IAlertClick? OnClick { get; private set; } - - /// severity level, if supported by this alert - /// the icon path to the texture for the provided severity level - public SpriteSpecifier GetIcon(short? severity = null) - { - var minIcons = SupportsSeverity - ? MaxSeverity - MinSeverity - : 1; + [IdDataField] + public string ID { get; private set; } = default!; - if (Icons.Count < minIcons) - throw new InvalidOperationException($"Insufficient number of icons given for alert {AlertType}"); + /// + /// List of icons to use for this alert. Each entry corresponds to a different severity level, starting from the + /// minimum and incrementing upwards. If severities are not supported, the first entry is used. + /// + [DataField(required: true)] + public List Icons = new(); - if (!SupportsSeverity) - return Icons[0]; + /// + /// An entity used for displaying the in the UI control. + /// + [DataField] + public EntProtoId AlertViewEntity = "AlertSpriteView"; - if (severity == null) - { - throw new ArgumentException($"No severity specified but this alert ({AlertKey}) has severity.", nameof(severity)); - } + /// + /// Name to show in tooltip window. Accepts formatting. + /// + [DataField] + public string Name { get; private set; } = string.Empty; - if (severity < MinSeverity) - { - throw new ArgumentOutOfRangeException(nameof(severity), $"Severity below minimum severity in {AlertKey}."); - } + /// + /// Description to show in tooltip window. Accepts formatting. + /// + [DataField] + public string Description { get; private set; } = string.Empty; - if (severity > MaxSeverity) - { - throw new ArgumentOutOfRangeException(nameof(severity), $"Severity above maximum severity in {AlertKey}."); - } + /// + /// Category the alert belongs to. Only one alert of a given category + /// can be shown at a time. If one is shown while another is already being shown, + /// it will be replaced. This can be useful for categories of alerts which should naturally + /// replace each other and are mutually exclusive, for example lowpressure / highpressure, + /// hot / cold. If left unspecified, the alert will not replace or be replaced by any other alerts. + /// + [DataField] + public ProtoId? Category { get; private set; } + + /// + /// Key which is unique w.r.t category semantics (alerts with same category have equal keys, + /// alerts with no category have different keys). + /// + public AlertKey AlertKey => new(ID, Category); - return Icons[severity.Value - _minSeverity]; + /// + /// -1 (no effect) unless MaxSeverity is specified. Defaults to 1. Minimum severity level supported by this state. + /// + public short MinSeverity => MaxSeverity == -1 ? (short) -1 : _minSeverity; + + [DataField("minSeverity")] private short _minSeverity = 1; + + /// + /// Maximum severity level supported by this state. -1 (default) indicates + /// no severity levels are supported by the state. + /// + [DataField] + public short MaxSeverity = -1; + + /// + /// Indicates whether this state support severity levels + /// + public bool SupportsSeverity => MaxSeverity != -1; + + /// + /// Defines what to do when the alert is clicked. + /// This will always be null on clientside. + /// + [DataField(serverOnly: true)] + public IAlertClick? OnClick { get; private set; } + + /// severity level, if supported by this alert + /// the icon path to the texture for the provided severity level + public SpriteSpecifier GetIcon(short? severity = null) + { + var minIcons = SupportsSeverity + ? MaxSeverity - MinSeverity + : 1; + + if (Icons.Count < minIcons) + throw new InvalidOperationException($"Insufficient number of icons given for alert {ID}"); + + if (!SupportsSeverity) + return Icons[0]; + + if (severity == null) + { + throw new ArgumentException($"No severity specified but this alert ({AlertKey}) has severity.", nameof(severity)); + } + + if (severity < MinSeverity) + { + throw new ArgumentOutOfRangeException(nameof(severity), $"Severity below minimum severity in {AlertKey}."); } + + if (severity > MaxSeverity) + { + throw new ArgumentOutOfRangeException(nameof(severity), $"Severity above maximum severity in {AlertKey}."); + } + + return Icons[severity.Value - _minSeverity]; } } diff --git a/Content.Shared/Alert/AlertState.cs b/Content.Shared/Alert/AlertState.cs index effd9522036a..d6309f6b4265 100644 --- a/Content.Shared/Alert/AlertState.cs +++ b/Content.Shared/Alert/AlertState.cs @@ -1,3 +1,4 @@ +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Alert; @@ -9,5 +10,5 @@ public struct AlertState public (TimeSpan, TimeSpan)? Cooldown; public bool AutoRemove; public bool ShowCooldown; - public AlertType Type; + public ProtoId Type; } diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs deleted file mode 100644 index b989b8d4b6f5..000000000000 --- a/Content.Shared/Alert/AlertType.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace Content.Shared.Alert -{ - /// - /// Every kind of alert. Corresponds to alertType field in alert prototypes defined in YML - /// NOTE: Using byte for a compact encoding when sending this in messages, can upgrade - /// to ushort - /// - public enum AlertType : byte - { - Error, - LowOxygen, - LowNitrogen, - LowPressure, - HighPressure, - Fire, - Cold, - Hot, - Weightless, - Stun, - Handcuffed, - Ensnared, - Buckled, - HumanCrit, - HumanDead, - HumanHealth, - BorgBattery, - BorgBatteryNone, - PilotingShuttle, - Peckish, - Starving, - Thirsty, - Parched, - Stamina, - Pulled, - Pulling, - Magboots, - Internals, - Toxins, - Muted, - VowOfSilence, - VowBroken, - Essence, - Corporeal, - Bleed, - Pacified, - Debug1, - Debug2, - Debug3, - Debug4, - Debug5, - Debug6, - SuitPower, - BorgHealth, - BorgCrit, - BorgDead, - Deflecting - } - -} diff --git a/Content.Shared/Alert/AlertsSystem.cs b/Content.Shared/Alert/AlertsSystem.cs index 5b888e30c4c7..83c6fcd0dd74 100644 --- a/Content.Shared/Alert/AlertsSystem.cs +++ b/Content.Shared/Alert/AlertsSystem.cs @@ -11,7 +11,7 @@ public abstract class AlertsSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IGameTiming _timing = default!; - private FrozenDictionary _typeToAlert = default!; + private FrozenDictionary, AlertPrototype> _typeToAlert = default!; public IReadOnlyDictionary? GetActiveAlerts(EntityUid euid) { @@ -20,23 +20,23 @@ public abstract class AlertsSystem : EntitySystem : null; } - public short GetSeverityRange(AlertType alertType) + public short GetSeverityRange(ProtoId alertType) { var minSeverity = _typeToAlert[alertType].MinSeverity; return (short)MathF.Max(minSeverity,_typeToAlert[alertType].MaxSeverity - minSeverity); } - public short GetMaxSeverity(AlertType alertType) + public short GetMaxSeverity(ProtoId alertType) { return _typeToAlert[alertType].MaxSeverity; } - public short GetMinSeverity(AlertType alertType) + public short GetMinSeverity(ProtoId alertType) { return _typeToAlert[alertType].MinSeverity; } - public bool IsShowingAlert(EntityUid euid, AlertType alertType) + public bool IsShowingAlert(EntityUid euid, ProtoId alertType) { if (!EntityManager.TryGetComponent(euid, out AlertsComponent? alertsComponent)) return false; @@ -51,7 +51,7 @@ public bool IsShowingAlert(EntityUid euid, AlertType alertType) } /// true iff an alert of the indicated alert category is currently showing - public bool IsShowingAlertCategory(EntityUid euid, AlertCategory alertCategory) + public bool IsShowingAlertCategory(EntityUid euid, ProtoId alertCategory) { return EntityManager.TryGetComponent(euid, out AlertsComponent? alertsComponent) && alertsComponent.Alerts.ContainsKey(AlertKey.ForCategory(alertCategory)); @@ -78,7 +78,7 @@ public bool TryGetAlertState(EntityUid euid, AlertKey key, out AlertState alertS /// be erased if there is currently a cooldown for the alert) /// if true, the alert will be removed at the end of the cooldown /// if true, the cooldown will be visibly shown over the alert icon - public void ShowAlert(EntityUid euid, AlertType alertType, short? severity = null, (TimeSpan, TimeSpan)? cooldown = null, bool autoRemove = false, bool showCooldown = true ) + public void ShowAlert(EntityUid euid, ProtoId alertType, short? severity = null, (TimeSpan, TimeSpan)? cooldown = null, bool autoRemove = false, bool showCooldown = true ) { // This should be handled as part of networking. if (_timing.ApplyingState) @@ -131,7 +131,7 @@ public void ShowAlert(EntityUid euid, AlertType alertType, short? severity = nul /// /// Clear the alert with the given category, if one is currently showing. /// - public void ClearAlertCategory(EntityUid euid, AlertCategory category) + public void ClearAlertCategory(EntityUid euid, ProtoId category) { if(!TryComp(euid, out AlertsComponent? alertsComponent)) return; @@ -150,7 +150,7 @@ public void ClearAlertCategory(EntityUid euid, AlertCategory category) /// /// Clear the alert of the given type if it is currently showing. /// - public void ClearAlert(EntityUid euid, AlertType alertType) + public void ClearAlert(EntityUid euid, ProtoId alertType) { if (_timing.ApplyingState) return; @@ -286,13 +286,13 @@ private void HandlePrototypesReloaded(PrototypesReloadedEventArgs obj) protected virtual void LoadPrototypes() { - var dict = new Dictionary(); + var dict = new Dictionary, AlertPrototype>(); foreach (var alert in _prototypeManager.EnumeratePrototypes()) { - if (!dict.TryAdd(alert.AlertType, alert)) + if (!dict.TryAdd(alert.ID, alert)) { Log.Error("Found alert with duplicate alertType {0} - all alerts must have" + - " a unique alertType, this one will be skipped", alert.AlertType); + " a unique alertType, this one will be skipped", alert.ID); } } @@ -303,7 +303,7 @@ protected virtual void LoadPrototypes() /// Tries to get the alert of the indicated type /// /// true if found - public bool TryGet(AlertType alertType, [NotNullWhen(true)] out AlertPrototype? alert) + public bool TryGet(ProtoId alertType, [NotNullWhen(true)] out AlertPrototype? alert) { return _typeToAlert.TryGetValue(alertType, out alert); } diff --git a/Content.Shared/Alert/ClickAlertEvent.cs b/Content.Shared/Alert/ClickAlertEvent.cs index fe7ca97e4c27..43dd086b5628 100644 --- a/Content.Shared/Alert/ClickAlertEvent.cs +++ b/Content.Shared/Alert/ClickAlertEvent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.Serialization; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; namespace Content.Shared.Alert; @@ -8,9 +9,9 @@ namespace Content.Shared.Alert; [Serializable, NetSerializable] public sealed class ClickAlertEvent : EntityEventArgs { - public readonly AlertType Type; + public readonly ProtoId Type; - public ClickAlertEvent(AlertType alertType) + public ClickAlertEvent(ProtoId alertType) { Type = alertType; } diff --git a/Content.Shared/Buckle/Components/StrapComponent.cs b/Content.Shared/Buckle/Components/StrapComponent.cs index 72c92ebf84b9..9a19cea0c9a7 100644 --- a/Content.Shared/Buckle/Components/StrapComponent.cs +++ b/Content.Shared/Buckle/Components/StrapComponent.cs @@ -3,6 +3,7 @@ using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Buckle.Components; @@ -115,7 +116,7 @@ public sealed partial class StrapComponent : Component /// [DataField] [ViewVariables(VVAccess.ReadWrite)] - public AlertType BuckledAlertType = AlertType.Buckled; + public ProtoId BuckledAlertType = "Buckled"; /// /// The sum of the sizes of all the buckled entities in this strap diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index 0d67473ffee9..4e94c6134b4a 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -40,6 +40,9 @@ private void InitializeBuckle() SubscribeLocalEvent(OnBuckleUpdateCanMove); } + [ValidatePrototypeId] + public const string BuckledAlertCategory = "Buckled"; + private void OnBuckleComponentStartup(EntityUid uid, BuckleComponent component, ComponentStartup args) { UpdateBuckleStatus(uid, component); @@ -165,7 +168,7 @@ private void UpdateBuckleStatus(EntityUid uid, BuckleComponent buckleComp, Strap } else { - _alerts.ClearAlertCategory(uid, AlertCategory.Buckled); + _alerts.ClearAlertCategory(uid, BuckledAlertCategory); } } diff --git a/Content.Shared/Clothing/MagbootsComponent.cs b/Content.Shared/Clothing/MagbootsComponent.cs index 0d0d59f89f5f..0d074ff38b69 100644 --- a/Content.Shared/Clothing/MagbootsComponent.cs +++ b/Content.Shared/Clothing/MagbootsComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Alert; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -16,4 +17,7 @@ public sealed partial class MagbootsComponent : Component [DataField("on"), AutoNetworkedField] public bool On; + + [DataField] + public ProtoId MagbootsAlert = "Magboots"; } diff --git a/Content.Shared/CombatMode/Pacification/PacificationSystem.cs b/Content.Shared/CombatMode/Pacification/PacificationSystem.cs index 6d94c087af6a..a927e1a69702 100644 --- a/Content.Shared/CombatMode/Pacification/PacificationSystem.cs +++ b/Content.Shared/CombatMode/Pacification/PacificationSystem.cs @@ -7,7 +7,6 @@ using Content.Shared.Popups; using Content.Shared.Throwing; using Content.Shared.Weapons.Ranged.Events; -using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Timing; namespace Content.Shared.CombatMode.Pacification; @@ -109,7 +108,7 @@ private void OnStartup(EntityUid uid, PacifiedComponent component, ComponentStar _actionsSystem.SetEnabled(combatMode.CombatToggleActionEntity, false); } - _alertsSystem.ShowAlert(uid, AlertType.Pacified); + _alertsSystem.ShowAlert(uid, component.PacifiedAlert); } private void OnShutdown(EntityUid uid, PacifiedComponent component, ComponentShutdown args) @@ -121,7 +120,7 @@ private void OnShutdown(EntityUid uid, PacifiedComponent component, ComponentShu _combatSystem.SetCanDisarm(uid, true, combatMode); _actionsSystem.SetEnabled(combatMode.CombatToggleActionEntity, true); - _alertsSystem.ClearAlert(uid, AlertType.Pacified); + _alertsSystem.ClearAlert(uid, component.PacifiedAlert); } private void OnBeforeThrow(Entity ent, ref BeforeThrowEvent args) diff --git a/Content.Shared/CombatMode/Pacification/PacifiedComponent.cs b/Content.Shared/CombatMode/Pacification/PacifiedComponent.cs index 464ef778851c..96081e5dc67e 100644 --- a/Content.Shared/CombatMode/Pacification/PacifiedComponent.cs +++ b/Content.Shared/CombatMode/Pacification/PacifiedComponent.cs @@ -1,4 +1,6 @@ +using Content.Shared.Alert; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; namespace Content.Shared.CombatMode.Pacification; @@ -42,4 +44,6 @@ public sealed partial class PacifiedComponent : Component [DataField] public EntityUid? LastAttackedEntity = null; + [DataField] + public ProtoId PacifiedAlert = "Pacified"; } diff --git a/Content.Shared/Cuffs/Components/CuffableComponent.cs b/Content.Shared/Cuffs/Components/CuffableComponent.cs index 5da6fa41a5fd..4ddfe1b53ee1 100644 --- a/Content.Shared/Cuffs/Components/CuffableComponent.cs +++ b/Content.Shared/Cuffs/Components/CuffableComponent.cs @@ -1,6 +1,8 @@ +using Content.Shared.Alert; using Content.Shared.Damage; using Robust.Shared.Containers; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -39,6 +41,9 @@ public sealed partial class CuffableComponent : Component /// [DataField("canStillInteract"), ViewVariables(VVAccess.ReadWrite)] public bool CanStillInteract = true; + + [DataField] + public ProtoId CuffedAlert = "Handcuffed"; } [Serializable, NetSerializable] diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index 0077f5a358ef..f0f9a949839a 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -172,9 +172,9 @@ public void UpdateCuffState(EntityUid uid, CuffableComponent component) _actionBlocker.UpdateCanMove(uid); if (component.CanStillInteract) - _alerts.ClearAlert(uid, AlertType.Handcuffed); + _alerts.ClearAlert(uid, component.CuffedAlert); else - _alerts.ShowAlert(uid, AlertType.Handcuffed); + _alerts.ShowAlert(uid, component.CuffedAlert); var ev = new CuffedStateChangeEvent(); RaiseLocalEvent(uid, ref ev); diff --git a/Content.Shared/Damage/Components/StaminaComponent.cs b/Content.Shared/Damage/Components/StaminaComponent.cs index 5a2fba497016..14c3f6d9f538 100644 --- a/Content.Shared/Damage/Components/StaminaComponent.cs +++ b/Content.Shared/Damage/Components/StaminaComponent.cs @@ -1,4 +1,6 @@ +using Content.Shared.Alert; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Damage.Components; @@ -51,4 +53,7 @@ public sealed partial class StaminaComponent : Component [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] [AutoPausedField] public TimeSpan NextUpdate = TimeSpan.Zero; + + [DataField] + public ProtoId StaminaAlert = "Stamina"; } diff --git a/Content.Shared/Damage/Systems/StaminaSystem.cs b/Content.Shared/Damage/Systems/StaminaSystem.cs index 840b2e043118..1f9a7f1dd841 100644 --- a/Content.Shared/Damage/Systems/StaminaSystem.cs +++ b/Content.Shared/Damage/Systems/StaminaSystem.cs @@ -79,8 +79,7 @@ private void OnShutdown(EntityUid uid, StaminaComponent component, ComponentShut { RemCompDeferred(uid); } - - SetStaminaAlert(uid); + _alerts.ClearAlert(uid, component.StaminaAlert); } private void OnStartup(EntityUid uid, StaminaComponent component, ComponentStartup args) @@ -204,13 +203,10 @@ private void OnCollide(EntityUid uid, StaminaDamageOnCollideComponent component, private void SetStaminaAlert(EntityUid uid, StaminaComponent? component = null) { if (!Resolve(uid, ref component, false) || component.Deleted) - { - _alerts.ClearAlert(uid, AlertType.Stamina); return; - } var severity = ContentHelpers.RoundToLevels(MathF.Max(0f, component.CritThreshold - component.StaminaDamage), component.CritThreshold, 7); - _alerts.ShowAlert(uid, AlertType.Stamina, (short) severity); + _alerts.ShowAlert(uid, component.StaminaAlert, (short) severity); } /// diff --git a/Content.Shared/Ensnaring/Components/EnsnareableComponent.cs b/Content.Shared/Ensnaring/Components/EnsnareableComponent.cs index 553f6df1c77d..2536fac4edcc 100644 --- a/Content.Shared/Ensnaring/Components/EnsnareableComponent.cs +++ b/Content.Shared/Ensnaring/Components/EnsnareableComponent.cs @@ -1,5 +1,7 @@ +using Content.Shared.Alert; using Robust.Shared.Containers; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Ensnaring.Components; @@ -40,6 +42,9 @@ public sealed partial class EnsnareableComponent : Component [DataField("state")] public string? State; + + [DataField] + public ProtoId EnsnaredAlert = "Ensnared"; } [Serializable, NetSerializable] diff --git a/Content.Shared/Gravity/SharedGravitySystem.cs b/Content.Shared/Gravity/SharedGravitySystem.cs index 100d2ee74fb8..df13be51fd4e 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.cs @@ -17,6 +17,9 @@ public abstract partial class SharedGravitySystem : EntitySystem [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly InventorySystem _inventory = default!; + [ValidatePrototypeId] + public const string WeightlessAlert = "Weightless"; + public bool IsWeightless(EntityUid uid, PhysicsComponent? body = null, TransformComponent? xform = null) { Resolve(uid, ref body, false); @@ -93,11 +96,11 @@ private void OnGravityChange(ref GravityChangedEvent ev) if (!ev.HasGravity) { - _alerts.ShowAlert(uid, AlertType.Weightless); + _alerts.ShowAlert(uid, WeightlessAlert); } else { - _alerts.ClearAlert(uid, AlertType.Weightless); + _alerts.ClearAlert(uid, WeightlessAlert); } } } @@ -106,11 +109,11 @@ private void OnAlertsSync(AlertSyncEvent ev) { if (IsWeightless(ev.Euid)) { - _alerts.ShowAlert(ev.Euid, AlertType.Weightless); + _alerts.ShowAlert(ev.Euid, WeightlessAlert); } else { - _alerts.ClearAlert(ev.Euid, AlertType.Weightless); + _alerts.ClearAlert(ev.Euid, WeightlessAlert); } } @@ -118,11 +121,11 @@ private void OnAlertsParentChange(EntityUid uid, AlertsComponent component, ref { if (IsWeightless(uid)) { - _alerts.ShowAlert(uid, AlertType.Weightless); + _alerts.ShowAlert(uid, WeightlessAlert); } else { - _alerts.ClearAlert(uid, AlertType.Weightless); + _alerts.ClearAlert(uid, WeightlessAlert); } } diff --git a/Content.Shared/Mobs/Components/MobThresholdsComponent.cs b/Content.Shared/Mobs/Components/MobThresholdsComponent.cs index e97d3672a215..0e37cf9b10e8 100644 --- a/Content.Shared/Mobs/Components/MobThresholdsComponent.cs +++ b/Content.Shared/Mobs/Components/MobThresholdsComponent.cs @@ -2,6 +2,7 @@ using Content.Shared.FixedPoint; using Content.Shared.Mobs.Systems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Mobs.Components; @@ -24,13 +25,16 @@ public sealed partial class MobThresholdsComponent : Component /// Used for alternate health alerts (silicons, for example) /// [DataField("stateAlertDict")] - public Dictionary StateAlertDict = new() + public Dictionary> StateAlertDict = new() { - {MobState.Alive, AlertType.HumanHealth}, - {MobState.Critical, AlertType.HumanCrit}, - {MobState.Dead, AlertType.HumanDead}, + {MobState.Alive, "HumanHealth"}, + {MobState.Critical, "HumanCrit"}, + {MobState.Dead, "HumanDead"}, }; + [DataField] + public ProtoId HealthAlertCategory = "Health"; + /// /// Whether or not this entity should display damage overlays (robots don't feel pain, black out etc.) /// @@ -53,19 +57,19 @@ public sealed class MobThresholdsComponentState : ComponentState public MobState CurrentThresholdState; - public Dictionary StateAlertDict = new() - { - {MobState.Alive, AlertType.HumanHealth}, - {MobState.Critical, AlertType.HumanCrit}, - {MobState.Dead, AlertType.HumanDead}, - }; + public Dictionary> StateAlertDict; public bool ShowOverlays; public bool AllowRevives; - public MobThresholdsComponentState(Dictionary unsortedThresholds, bool triggersAlerts, MobState currentThresholdState, - Dictionary stateAlertDict, bool showOverlays, bool allowRevives) + public MobThresholdsComponentState(Dictionary unsortedThresholds, + bool triggersAlerts, + MobState currentThresholdState, + Dictionary> stateAlertDict, + bool showOverlays, + bool allowRevives) { UnsortedThresholds = unsortedThresholds; TriggersAlerts = triggersAlerts; diff --git a/Content.Shared/Mobs/Systems/MobThresholdSystem.cs b/Content.Shared/Mobs/Systems/MobThresholdSystem.cs index 59d9fb4c2390..b11de9eac56d 100644 --- a/Content.Shared/Mobs/Systems/MobThresholdSystem.cs +++ b/Content.Shared/Mobs/Systems/MobThresholdSystem.cs @@ -431,7 +431,7 @@ private void MobThresholdStartup(EntityUid target, MobThresholdsComponent thresh private void MobThresholdShutdown(EntityUid target, MobThresholdsComponent component, ComponentShutdown args) { if (component.TriggersAlerts) - _alerts.ClearAlertCategory(target, AlertCategory.Health); + _alerts.ClearAlertCategory(target, component.HealthAlertCategory); } private void OnUpdateMobState(EntityUid target, MobThresholdsComponent component, ref UpdateMobStateEvent args) diff --git a/Content.Shared/Movement/Pulling/Components/PullableComponent.cs b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs index db889e7e3bd9..100cd9d6d3e6 100644 --- a/Content.Shared/Movement/Pulling/Components/PullableComponent.cs +++ b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs @@ -1,4 +1,6 @@ +using Content.Shared.Alert; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; namespace Content.Shared.Movement.Pulling.Components; @@ -36,4 +38,7 @@ public sealed partial class PullableComponent : Component [Access(typeof(Systems.PullingSystem), Other = AccessPermissions.ReadExecute)] [AutoNetworkedField, DataField] public bool PrevFixedRotation; + + [DataField] + public ProtoId PulledAlert = "Pulled"; } diff --git a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs index 1fc9b731bd5f..061ec13ed2ab 100644 --- a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs +++ b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs @@ -1,5 +1,7 @@ -using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Alert; +using Content.Shared.Movement.Pulling.Systems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Movement.Pulling.Components; @@ -38,4 +40,7 @@ public sealed partial class PullerComponent : Component /// [DataField] public bool NeedsHands = true; + + [DataField] + public ProtoId PullingAlert = "Pulling"; } diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index 81b2fee5695a..2781c4952989 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -223,7 +223,7 @@ private void StopPulling(EntityUid pullableUid, PullableComponent pullableComp) if (TryComp(oldPuller, out var pullerComp)) { var pullerUid = oldPuller.Value; - _alertsSystem.ClearAlert(pullerUid, AlertType.Pulling); + _alertsSystem.ClearAlert(pullerUid, pullerComp.PullingAlert); pullerComp.Pulling = null; Dirty(oldPuller.Value, pullerComp); @@ -237,7 +237,7 @@ private void StopPulling(EntityUid pullableUid, PullableComponent pullableComp) } - _alertsSystem.ClearAlert(pullableUid, AlertType.Pulled); + _alertsSystem.ClearAlert(pullableUid, pullableComp.PulledAlert); } public bool IsPulled(EntityUid uid, PullableComponent? component = null) @@ -460,8 +460,8 @@ public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, // Messaging var message = new PullStartedMessage(pullerUid, pullableUid); - _alertsSystem.ShowAlert(pullerUid, AlertType.Pulling); - _alertsSystem.ShowAlert(pullableUid, AlertType.Pulled); + _alertsSystem.ShowAlert(pullerUid, pullerComp.PullingAlert); + _alertsSystem.ShowAlert(pullableUid, pullableComp.PulledAlert); RaiseLocalEvent(pullerUid, message); RaiseLocalEvent(pullableUid, message); diff --git a/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs b/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs index 0f3bff265cba..91c816df5c93 100644 --- a/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs +++ b/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Alert; using Content.Shared.Ninja.Systems; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -53,4 +54,7 @@ public sealed partial class SpaceNinjaComponent : Component /// [DataField] public EntProtoId SpiderChargeObjective = "SpiderChargeObjective"; + + [DataField] + public ProtoId SuitPowerAlert = "SuitPower"; } diff --git a/Content.Shared/Nutrition/Components/HungerComponent.cs b/Content.Shared/Nutrition/Components/HungerComponent.cs index 9ac82ba283c9..79d895ddae66 100644 --- a/Content.Shared/Nutrition/Components/HungerComponent.cs +++ b/Content.Shared/Nutrition/Components/HungerComponent.cs @@ -2,6 +2,7 @@ using Content.Shared.Damage; using Content.Shared.Nutrition.EntitySystems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; @@ -65,15 +66,18 @@ public sealed partial class HungerComponent : Component /// /// A dictionary relating hunger thresholds to corresponding alerts. /// - [DataField("hungerThresholdAlerts", customTypeSerializer: typeof(DictionarySerializer))] + [DataField("hungerThresholdAlerts")] [AutoNetworkedField] - public Dictionary HungerThresholdAlerts = new() + public Dictionary> HungerThresholdAlerts = new() { - { HungerThreshold.Peckish, AlertType.Peckish }, - { HungerThreshold.Starving, AlertType.Starving }, - { HungerThreshold.Dead, AlertType.Starving } + { HungerThreshold.Peckish, "Peckish" }, + { HungerThreshold.Starving, "Starving" }, + { HungerThreshold.Dead, "Starving" } }; + [DataField] + public ProtoId HungerAlertCategory = "Hunger"; + /// /// A dictionary relating HungerThreshold to how much they modify . /// diff --git a/Content.Shared/Nutrition/Components/ThirstComponent.cs b/Content.Shared/Nutrition/Components/ThirstComponent.cs index 731346401fd9..f3ac881361fc 100644 --- a/Content.Shared/Nutrition/Components/ThirstComponent.cs +++ b/Content.Shared/Nutrition/Components/ThirstComponent.cs @@ -1,6 +1,7 @@ using Content.Shared.Alert; using Content.Shared.Nutrition.EntitySystems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Nutrition.Components; @@ -56,11 +57,14 @@ public sealed partial class ThirstComponent : Component {ThirstThreshold.Dead, 0.0f}, }; - public static readonly Dictionary ThirstThresholdAlertTypes = new() + [DataField] + public ProtoId ThirstyCategory = "Thirst"; + + public static readonly Dictionary> ThirstThresholdAlertTypes = new() { - {ThirstThreshold.Thirsty, AlertType.Thirsty}, - {ThirstThreshold.Parched, AlertType.Parched}, - {ThirstThreshold.Dead, AlertType.Parched}, + {ThirstThreshold.Thirsty, "Thirsty"}, + {ThirstThreshold.Parched, "Parched"}, + {ThirstThreshold.Dead, "Parched"}, }; } diff --git a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs index 4de4e4d5feb0..bff15f06ff9c 100644 --- a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs @@ -61,7 +61,7 @@ private void OnMapInit(EntityUid uid, HungerComponent component, MapInitEvent ar private void OnShutdown(EntityUid uid, HungerComponent component, ComponentShutdown args) { - _alerts.ClearAlertCategory(uid, AlertCategory.Hunger); + _alerts.ClearAlertCategory(uid, component.HungerAlertCategory); } private void OnRefreshMovespeed(EntityUid uid, HungerComponent component, RefreshMovementSpeedModifiersEvent args) @@ -142,7 +142,7 @@ private void DoHungerThresholdEffects(EntityUid uid, HungerComponent? component } else { - _alerts.ClearAlertCategory(uid, AlertCategory.Hunger); + _alerts.ClearAlertCategory(uid, component.HungerAlertCategory); } if (component.HungerThresholdDecayModifiers.TryGetValue(component.CurrentThreshold, out var modifier)) diff --git a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs index 8ea7d9140c36..205d8d6cde32 100644 --- a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs @@ -165,7 +165,7 @@ private void UpdateEffects(EntityUid uid, ThirstComponent component) } else { - _alerts.ClearAlertCategory(uid, AlertCategory.Thirst); + _alerts.ClearAlertCategory(uid, component.ThirstyCategory); } switch (component.CurrentThirstThreshold) diff --git a/Content.Shared/Revenant/Components/RevenantComponent.cs b/Content.Shared/Revenant/Components/RevenantComponent.cs index 947c1a4b3fc6..d7fb28ef1360 100644 --- a/Content.Shared/Revenant/Components/RevenantComponent.cs +++ b/Content.Shared/Revenant/Components/RevenantComponent.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Shared.Alert; using Content.Shared.FixedPoint; using Content.Shared.Store; using Content.Shared.Whitelist; @@ -200,6 +201,9 @@ public sealed partial class RevenantComponent : Component public EntityWhitelist? MalfunctionBlacklist; #endregion + [DataField] + public ProtoId EssenceAlert = "Essence"; + #region Visualizer [DataField("state")] public string State = "idle"; diff --git a/Content.Shared/Shuttles/Components/PilotComponent.cs b/Content.Shared/Shuttles/Components/PilotComponent.cs index 1a6927cf813d..cb42db4436fc 100644 --- a/Content.Shared/Shuttles/Components/PilotComponent.cs +++ b/Content.Shared/Shuttles/Components/PilotComponent.cs @@ -1,7 +1,9 @@ using System.Numerics; +using Content.Shared.Alert; using Content.Shared.Movement.Systems; using Robust.Shared.GameStates; using Robust.Shared.Map; +using Robust.Shared.Prototypes; using Robust.Shared.Timing; namespace Content.Shared.Shuttles.Components @@ -32,6 +34,9 @@ public sealed partial class PilotComponent : Component [ViewVariables] public ShuttleButtons HeldButtons = ShuttleButtons.None; + [DataField] + public ProtoId PilotingAlert = "PilotingShuttle"; + public override bool SendOnlyToOwner => true; } } diff --git a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs index 71d3a7bd166c..e1776873da91 100644 --- a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs +++ b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs @@ -1,6 +1,8 @@ -using Content.Shared.Whitelist; +using Content.Shared.Alert; +using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Silicons.Borgs.Components; @@ -76,6 +78,12 @@ public sealed partial class BorgChassisComponent : Component [DataField("noMindState")] public string NoMindState = string.Empty; #endregion + + [DataField] + public ProtoId BatteryAlert = "BorgBattery"; + + [DataField] + public ProtoId NoBatteryAlert = "BorgBatteryNone"; } [Serializable, NetSerializable] diff --git a/Content.Shared/StatusEffect/StatusEffectPrototype.cs b/Content.Shared/StatusEffect/StatusEffectPrototype.cs index ae9e26879ebb..8b1f84e4d81a 100644 --- a/Content.Shared/StatusEffect/StatusEffectPrototype.cs +++ b/Content.Shared/StatusEffect/StatusEffectPrototype.cs @@ -10,7 +10,7 @@ public sealed partial class StatusEffectPrototype : IPrototype public string ID { get; private set; } = default!; [DataField("alert")] - public AlertType? Alert { get; private set; } + public ProtoId? Alert { get; private set; } /// /// Whether a status effect should be able to apply to any entity, diff --git a/Content.Shared/StatusEffect/StatusEffectsSystem.cs b/Content.Shared/StatusEffect/StatusEffectsSystem.cs index f3e3e12bd8cc..9806077f9bb8 100644 --- a/Content.Shared/StatusEffect/StatusEffectsSystem.cs +++ b/Content.Shared/StatusEffect/StatusEffectsSystem.cs @@ -219,7 +219,7 @@ public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool re /// This is mostly for stuns, since Stun and Knockdown share an alert key. Other times this pretty much /// will not be useful. /// - private (TimeSpan, TimeSpan)? GetAlertCooldown(EntityUid uid, AlertType alert, StatusEffectsComponent status) + private (TimeSpan, TimeSpan)? GetAlertCooldown(EntityUid uid, ProtoId alert, StatusEffectsComponent status) { (TimeSpan, TimeSpan)? maxCooldown = null; foreach (var kvp in status.ActiveEffects) diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 36dbedb4cb16..03ad97edff27 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -42,6 +42,9 @@ public sealed class ReflectSystem : EntitySystem [Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly AlertsSystem _alerts = default!; + [ValidatePrototypeId] + private const string DeflectingAlert = "Deflecting"; + public override void Initialize() { base.Initialize(); @@ -296,11 +299,11 @@ private void RefreshReflectUser(EntityUid user) private void EnableAlert(EntityUid alertee) { - _alerts.ShowAlert(alertee, AlertType.Deflecting); + _alerts.ShowAlert(alertee, DeflectingAlert); } private void DisableAlert(EntityUid alertee) { - _alerts.ClearAlert(alertee, AlertType.Deflecting); + _alerts.ClearAlert(alertee, DeflectingAlert); } } diff --git a/Content.Tests/Shared/Alert/AlertManagerTests.cs b/Content.Tests/Shared/Alert/AlertManagerTests.cs index 2d5f6af5a7f1..c57df63d5b7d 100644 --- a/Content.Tests/Shared/Alert/AlertManagerTests.cs +++ b/Content.Tests/Shared/Alert/AlertManagerTests.cs @@ -1,6 +1,5 @@ using System.IO; using Content.Client.Alerts; -using Content.Server.Alert; using Content.Shared.Alert; using NUnit.Framework; using Robust.Shared.GameObjects; @@ -45,15 +44,15 @@ public void TestAlertManager() prototypeManager.Initialize(); prototypeManager.LoadFromStream(new StringReader(PROTOTYPES)); - Assert.That(alertsSystem.TryGet(AlertType.LowPressure, out var lowPressure)); - Assert.That(lowPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); - Assert.That(alertsSystem.TryGet(AlertType.HighPressure, out var highPressure)); - Assert.That(highPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/highpressure.png")))); + Assert.That(alertsSystem.TryGet("LowPressure", out var lowPressure)); + Assert.That(lowPressure!.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); + Assert.That(alertsSystem.TryGet("HighPressure", out var highPressure)); + Assert.That(highPressure!.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/highpressure.png")))); - Assert.That(alertsSystem.TryGet(AlertType.LowPressure, out lowPressure)); - Assert.That(lowPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); - Assert.That(alertsSystem.TryGet(AlertType.HighPressure, out highPressure)); - Assert.That(highPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/highpressure.png")))); + Assert.That(alertsSystem.TryGet("LowPressure", out lowPressure)); + Assert.That(lowPressure!.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); + Assert.That(alertsSystem.TryGet("HighPressure", out highPressure)); + Assert.That(highPressure!.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/highpressure.png")))); } } } diff --git a/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs b/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs index 56f76d46a927..efcd59acbbb0 100644 --- a/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs +++ b/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs @@ -85,24 +85,24 @@ public void TestAlertOrderPrototype() var alerts = prototypeManager.EnumeratePrototypes(); // ensure they sort according to our expected criteria - var expectedOrder = new List(); - expectedOrder.Add(AlertType.Handcuffed); - expectedOrder.Add(AlertType.Ensnared); - expectedOrder.Add(AlertType.HighPressure); + var expectedOrder = new List(); + expectedOrder.Add("Handcuffed"); + expectedOrder.Add("Ensnared"); + expectedOrder.Add("HighPressure"); // stuff with only category + same category ordered by enum value - expectedOrder.Add(AlertType.Peckish); - expectedOrder.Add(AlertType.Hot); - expectedOrder.Add(AlertType.Stun); - expectedOrder.Add(AlertType.LowPressure); - expectedOrder.Add(AlertType.Cold); - // stuff at end of list ordered by enum value - expectedOrder.Add(AlertType.Weightless); - expectedOrder.Add(AlertType.PilotingShuttle); + expectedOrder.Add("Peckish"); + expectedOrder.Add("Hot"); + expectedOrder.Add("Stun"); + expectedOrder.Add("LowPressure"); + expectedOrder.Add("Cold"); + // stuff at end of list ordered by ID + expectedOrder.Add("PilotingShuttle"); + expectedOrder.Add("Weightless"); var actual = alerts.ToList(); actual.Sort(alertOrder); - Assert.That(actual.Select(a => a.AlertType).ToList(), Is.EqualTo(expectedOrder)); + Assert.That(actual.Select(a => a.ID).ToList(), Is.EqualTo(expectedOrder)); } } } diff --git a/Content.Tests/Shared/Alert/AlertPrototypeTests.cs b/Content.Tests/Shared/Alert/AlertPrototypeTests.cs index d95acb8aff49..43ae98b452b4 100644 --- a/Content.Tests/Shared/Alert/AlertPrototypeTests.cs +++ b/Content.Tests/Shared/Alert/AlertPrototypeTests.cs @@ -39,9 +39,9 @@ public void OneTimeSetUp() [Test] public void TestAlertKey() { - Assert.That(new AlertKey(AlertType.HumanHealth, null), Is.Not.EqualTo(AlertKey.ForCategory(AlertCategory.Health))); - Assert.That((new AlertKey(null, AlertCategory.Health)), Is.EqualTo(AlertKey.ForCategory(AlertCategory.Health))); - Assert.That((new AlertKey(AlertType.Buckled, AlertCategory.Health)), Is.EqualTo(AlertKey.ForCategory(AlertCategory.Health))); + Assert.That(new AlertKey("HumanHealth", null), Is.Not.EqualTo(AlertKey.ForCategory("Health"))); + Assert.That((new AlertKey(null, "Health")), Is.EqualTo(AlertKey.ForCategory("Health"))); + Assert.That((new AlertKey("Buckled", "Health")), Is.EqualTo(AlertKey.ForCategory("Health"))); } [TestCase(0, "/Textures/Interface/Alerts/Human/human.rsi/human0.png")] diff --git a/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs b/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs index 47ae3ef74a68..bcc32e13deed 100644 --- a/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs +++ b/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs @@ -15,6 +15,9 @@ namespace Content.Tests.Shared.Alert public sealed class ServerAlertsComponentTests : ContentUnitTest { const string PROTOTYPES = @" +- type: alertCategory + id: Pressure + - type: alert id: LowPressure category: Pressure @@ -49,10 +52,10 @@ public void ShowAlerts() var alertsComponent = new AlertsComponent(); alertsComponent = IoCManager.InjectDependencies(alertsComponent); - Assert.That(entManager.System().TryGet(AlertType.LowPressure, out var lowpressure)); - Assert.That(entManager.System().TryGet(AlertType.HighPressure, out var highpressure)); + Assert.That(entManager.System().TryGet("LowPressure", out var lowpressure)); + Assert.That(entManager.System().TryGet("HighPressure", out var highpressure)); - entManager.System().ShowAlert(alertsComponent.Owner, AlertType.LowPressure, null, null); + entManager.System().ShowAlert(alertsComponent.Owner, "LowPressure"); var getty = new ComponentGetState(); entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); @@ -60,17 +63,17 @@ public void ShowAlerts() var alertState = (AlertsComponent.AlertsComponent_AutoState) getty.State!; Assert.That(alertState, Is.Not.Null); Assert.That(alertState.Alerts.Count, Is.EqualTo(1)); - Assert.That(alertState.Alerts.ContainsKey(lowpressure.AlertKey)); + Assert.That(alertState.Alerts.ContainsKey(lowpressure!.AlertKey)); - entManager.System().ShowAlert(alertsComponent.Owner, AlertType.HighPressure, null, null); + entManager.System().ShowAlert(alertsComponent.Owner, "HighPressure"); // Lazy entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); alertState = (AlertsComponent.AlertsComponent_AutoState) getty.State!; Assert.That(alertState.Alerts.Count, Is.EqualTo(1)); - Assert.That(alertState.Alerts.ContainsKey(highpressure.AlertKey)); + Assert.That(alertState.Alerts.ContainsKey(highpressure!.AlertKey)); - entManager.System().ClearAlertCategory(alertsComponent.Owner, AlertCategory.Pressure); + entManager.System().ClearAlertCategory(alertsComponent.Owner, "Pressure"); entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); alertState = (AlertsComponent.AlertsComponent_AutoState) getty.State!; diff --git a/Resources/Prototypes/Alerts/categories.yml b/Resources/Prototypes/Alerts/categories.yml new file mode 100644 index 000000000000..2365422ed914 --- /dev/null +++ b/Resources/Prototypes/Alerts/categories.yml @@ -0,0 +1,35 @@ +- type: alertCategory + id: Pressure + +- type: alertCategory + id: Temperature + +- type: alertCategory + id: Breathing + +- type: alertCategory + id: Buckled + +- type: alertCategory + id: Health + +- type: alertCategory + id: Internals + +- type: alertCategory + id: Stamina + +- type: alertCategory + id: Piloting + +- type: alertCategory + id: Hunger + +- type: alertCategory + id: Thirst + +- type: alertCategory + id: Toxins + +- type: alertCategory + id: Battery From 5d5046746688639b95655dcb9cb3bab67484561b Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Fri, 24 May 2024 16:08:23 +1200 Subject: [PATCH 026/568] Content changes for engine delta-state PR (#28134) * Update GasTileOverlayState * Update DecalGridState * Update NavMapState * poke * poke2 * poke3 * Poke dem tests --- .../EntitySystems/GasTileOverlaySystem.cs | 39 ++++++++++------ Content.Client/Decals/DecalSystem.cs | 39 ++++++++++------ Content.Client/Pinpointer/NavMapSystem.cs | 41 +++++++++++------ .../Components/GasTileOverlayComponent.cs | 45 ++++++++---------- .../SharedGasTileOverlaySystem.cs | 2 +- Content.Shared/Decals/DecalGridComponent.cs | 35 ++++++-------- Content.Shared/Decals/SharedDecalSystem.cs | 2 +- .../Pinpointer/SharedNavMapSystem.cs | 46 ++++++++----------- 8 files changed, 129 insertions(+), 120 deletions(-) diff --git a/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs index 78185ce6b0e6..86cf0a9eb824 100644 --- a/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -1,4 +1,5 @@ using Content.Client.Atmos.Overlays; +using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Atmos.EntitySystems; using JetBrains.Annotations; @@ -36,28 +37,38 @@ public override void Shutdown() private void OnHandleState(EntityUid gridUid, GasTileOverlayComponent comp, ref ComponentHandleState args) { - if (args.Current is not GasTileOverlayState state) - return; + Dictionary modifiedChunks; - // is this a delta or full state? - if (!state.FullState) + switch (args.Current) { - foreach (var index in comp.Chunks.Keys) + // is this a delta or full state? + case GasTileOverlayDeltaState delta: { - if (!state.AllChunks!.Contains(index)) - comp.Chunks.Remove(index); + modifiedChunks = delta.ModifiedChunks; + foreach (var index in comp.Chunks.Keys) + { + if (!delta.AllChunks.Contains(index)) + comp.Chunks.Remove(index); + } + + break; } - } - else - { - foreach (var index in comp.Chunks.Keys) + case GasTileOverlayState state: { - if (!state.Chunks.ContainsKey(index)) - comp.Chunks.Remove(index); + modifiedChunks = state.Chunks; + foreach (var index in comp.Chunks.Keys) + { + if (!state.Chunks.ContainsKey(index)) + comp.Chunks.Remove(index); + } + + break; } + default: + return; } - foreach (var (index, data) in state.Chunks) + foreach (var (index, data) in modifiedChunks) { comp.Chunks[index] = data; } diff --git a/Content.Client/Decals/DecalSystem.cs b/Content.Client/Decals/DecalSystem.cs index 901ab270fb59..41e5f39c2867 100644 --- a/Content.Client/Decals/DecalSystem.cs +++ b/Content.Client/Decals/DecalSystem.cs @@ -56,34 +56,43 @@ protected override void OnDecalRemoved(EntityUid gridId, uint decalId, DecalGrid private void OnHandleState(EntityUid gridUid, DecalGridComponent gridComp, ref ComponentHandleState args) { - if (args.Current is not DecalGridState state) - return; - // is this a delta or full state? _removedChunks.Clear(); + Dictionary modifiedChunks; - if (!state.FullState) + switch (args.Current) { - foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys) + case DecalGridDeltaState delta: { - if (!state.AllChunks!.Contains(key)) - _removedChunks.Add(key); + modifiedChunks = delta.ModifiedChunks; + foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys) + { + if (!delta.AllChunks.Contains(key)) + _removedChunks.Add(key); + } + + break; } - } - else - { - foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys) + case DecalGridState state: { - if (!state.Chunks.ContainsKey(key)) - _removedChunks.Add(key); + modifiedChunks = state.Chunks; + foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys) + { + if (!state.Chunks.ContainsKey(key)) + _removedChunks.Add(key); + } + + break; } + default: + return; } if (_removedChunks.Count > 0) RemoveChunks(gridUid, gridComp, _removedChunks); - if (state.Chunks.Count > 0) - UpdateChunks(gridUid, gridComp, state.Chunks); + if (modifiedChunks.Count > 0) + UpdateChunks(gridUid, gridComp, modifiedChunks); } private void OnChunkUpdate(DecalChunkUpdateEvent ev) diff --git a/Content.Client/Pinpointer/NavMapSystem.cs b/Content.Client/Pinpointer/NavMapSystem.cs index e33bc5d32917..9aeb792a429f 100644 --- a/Content.Client/Pinpointer/NavMapSystem.cs +++ b/Content.Client/Pinpointer/NavMapSystem.cs @@ -14,27 +14,40 @@ public override void Initialize() private void OnHandleState(EntityUid uid, NavMapComponent component, ref ComponentHandleState args) { - if (args.Current is not NavMapComponentState state) - return; + Dictionary modifiedChunks; + Dictionary beacons; - if (!state.FullState) + switch (args.Current) { - foreach (var index in component.Chunks.Keys) + case NavMapDeltaState delta: { - if (!state.AllChunks!.Contains(index)) - component.Chunks.Remove(index); + modifiedChunks = delta.ModifiedChunks; + beacons = delta.Beacons; + foreach (var index in component.Chunks.Keys) + { + if (!delta.AllChunks!.Contains(index)) + component.Chunks.Remove(index); + } + + break; } - } - else - { - foreach (var index in component.Chunks.Keys) + case NavMapState state: { - if (!state.Chunks.ContainsKey(index)) - component.Chunks.Remove(index); + modifiedChunks = state.Chunks; + beacons = state.Beacons; + foreach (var index in component.Chunks.Keys) + { + if (!state.Chunks.ContainsKey(index)) + component.Chunks.Remove(index); + } + + break; } + default: + return; } - foreach (var (origin, chunk) in state.Chunks) + foreach (var (origin, chunk) in modifiedChunks) { var newChunk = new NavMapChunk(origin); Array.Copy(chunk, newChunk.TileData, chunk.Length); @@ -42,7 +55,7 @@ private void OnHandleState(EntityUid uid, NavMapComponent component, ref Compone } component.Beacons.Clear(); - foreach (var (nuid, beacon) in state.Beacons) + foreach (var (nuid, beacon) in beacons) { component.Beacons[nuid] = beacon; } diff --git a/Content.Shared/Atmos/Components/GasTileOverlayComponent.cs b/Content.Shared/Atmos/Components/GasTileOverlayComponent.cs index e72a1d675841..2c3149b11a8f 100644 --- a/Content.Shared/Atmos/Components/GasTileOverlayComponent.cs +++ b/Content.Shared/Atmos/Components/GasTileOverlayComponent.cs @@ -1,7 +1,6 @@ using Robust.Shared.GameStates; using Robust.Shared.Serialization; using Robust.Shared.Timing; -using Robust.Shared.Utility; namespace Content.Shared.Atmos.Components; @@ -24,55 +23,47 @@ public sealed partial class GasTileOverlayComponent : Component public GameTick ForceTick { get; set; } } - [Serializable, NetSerializable] -public sealed class GasTileOverlayState : ComponentState, IComponentDeltaState +public sealed class GasTileOverlayState(Dictionary chunks) : ComponentState { - public readonly Dictionary Chunks; - public bool FullState => AllChunks == null; - - // required to infer deleted/missing chunks for delta states - public HashSet? AllChunks; + public readonly Dictionary Chunks = chunks; +} - public GasTileOverlayState(Dictionary chunks) - { - Chunks = chunks; - } +[Serializable, NetSerializable] +public sealed class GasTileOverlayDeltaState( + Dictionary modifiedChunks, + HashSet allChunks) + : ComponentState, IComponentDeltaState +{ + public readonly Dictionary ModifiedChunks = modifiedChunks; + public readonly HashSet AllChunks = allChunks; - public void ApplyToFullState(IComponentState fullState) + public void ApplyToFullState(GasTileOverlayState state) { - DebugTools.Assert(!FullState); - var state = (GasTileOverlayState) fullState; - DebugTools.Assert(state.FullState); - foreach (var key in state.Chunks.Keys) { - if (!AllChunks!.Contains(key)) + if (!AllChunks.Contains(key)) state.Chunks.Remove(key); } - foreach (var (chunk, data) in Chunks) + foreach (var (chunk, data) in ModifiedChunks) { state.Chunks[chunk] = new(data); } } - public IComponentState CreateNewFullState(IComponentState fullState) + public GasTileOverlayState CreateNewFullState(GasTileOverlayState state) { - DebugTools.Assert(!FullState); - var state = (GasTileOverlayState) fullState; - DebugTools.Assert(state.FullState); - - var chunks = new Dictionary(state.Chunks.Count); + var chunks = new Dictionary(AllChunks.Count); - foreach (var (chunk, data) in Chunks) + foreach (var (chunk, data) in ModifiedChunks) { chunks[chunk] = new(data); } foreach (var (chunk, data) in state.Chunks) { - if (AllChunks!.Contains(chunk)) + if (AllChunks.Contains(chunk)) chunks.TryAdd(chunk, new(data)); } diff --git a/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs b/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs index f468724db333..8e7dfdedaf90 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs @@ -55,7 +55,7 @@ private void OnGetState(EntityUid uid, GasTileOverlayComponent component, ref Co data[index] = chunk; } - args.State = new GasTileOverlayState(data) { AllChunks = new(component.Chunks.Keys) }; + args.State = new GasTileOverlayDeltaState(data, new(component.Chunks.Keys)); } public static Vector2i GetGasChunkIndices(Vector2i indices) diff --git a/Content.Shared/Decals/DecalGridComponent.cs b/Content.Shared/Decals/DecalGridComponent.cs index 8ac05cb280fa..67a9c037696d 100644 --- a/Content.Shared/Decals/DecalGridComponent.cs +++ b/Content.Shared/Decals/DecalGridComponent.cs @@ -62,46 +62,37 @@ public record DecalGridChunkCollection(Dictionary ChunkCol } [Serializable, NetSerializable] - public sealed class DecalGridState : ComponentState, IComponentDeltaState + public sealed class DecalGridState(Dictionary chunks) : ComponentState { - public Dictionary Chunks; - public bool FullState => AllChunks == null; - - // required to infer deleted/missing chunks for delta states - public HashSet? AllChunks; + public Dictionary Chunks = chunks; + } - public DecalGridState(Dictionary chunks) - { - Chunks = chunks; - } + [Serializable, NetSerializable] + public sealed class DecalGridDeltaState(Dictionary modifiedChunks, HashSet allChunks) + : ComponentState, IComponentDeltaState + { + public Dictionary ModifiedChunks = modifiedChunks; + public HashSet AllChunks = allChunks; - public void ApplyToFullState(IComponentState fullState) + public void ApplyToFullState(DecalGridState state) { - DebugTools.Assert(!FullState); - var state = (DecalGridState) fullState; - DebugTools.Assert(state.FullState); - foreach (var key in state.Chunks.Keys) { if (!AllChunks!.Contains(key)) state.Chunks.Remove(key); } - foreach (var (chunk, data) in Chunks) + foreach (var (chunk, data) in ModifiedChunks) { state.Chunks[chunk] = new(data); } } - public IComponentState CreateNewFullState(IComponentState fullState) + public DecalGridState CreateNewFullState(DecalGridState state) { - DebugTools.Assert(!FullState); - var state = (DecalGridState) fullState; - DebugTools.Assert(state.FullState); - var chunks = new Dictionary(state.Chunks.Count); - foreach (var (chunk, data) in Chunks) + foreach (var (chunk, data) in ModifiedChunks) { chunks[chunk] = new(data); } diff --git a/Content.Shared/Decals/SharedDecalSystem.cs b/Content.Shared/Decals/SharedDecalSystem.cs index 0665ccbf84b3..0a2349ea299a 100644 --- a/Content.Shared/Decals/SharedDecalSystem.cs +++ b/Content.Shared/Decals/SharedDecalSystem.cs @@ -49,7 +49,7 @@ private void OnGetState(EntityUid uid, DecalGridComponent component, ref Compone data[index] = chunk; } - args.State = new DecalGridState(data) { AllChunks = new(component.ChunkCollection.ChunkCollection.Keys) }; + args.State = new DecalGridDeltaState(data, new(component.ChunkCollection.ChunkCollection.Keys)); } private void OnGridInitialize(GridInitializeEvent msg) diff --git a/Content.Shared/Pinpointer/SharedNavMapSystem.cs b/Content.Shared/Pinpointer/SharedNavMapSystem.cs index ffe81c2d0ea4..7c12321b5db6 100644 --- a/Content.Shared/Pinpointer/SharedNavMapSystem.cs +++ b/Content.Shared/Pinpointer/SharedNavMapSystem.cs @@ -96,7 +96,7 @@ private void OnGetState(EntityUid uid, NavMapComponent component, ref ComponentG chunks.Add(origin, chunk.TileData); } - args.State = new NavMapComponentState(chunks, component.Beacons); + args.State = new NavMapState(chunks, component.Beacons); return; } @@ -109,12 +109,7 @@ private void OnGetState(EntityUid uid, NavMapComponent component, ref ComponentG chunks.Add(origin, chunk.TileData); } - args.State = new NavMapComponentState(chunks, component.Beacons) - { - // TODO NAVMAP cache a single AllChunks hashset in the component. - // Or maybe just only send them if a chunk gets removed. - AllChunks = new(component.Chunks.Keys), - }; + args.State = new NavMapDeltaState(chunks, component.Beacons, new(component.Chunks.Keys)); } #endregion @@ -122,32 +117,35 @@ private void OnGetState(EntityUid uid, NavMapComponent component, ref ComponentG #region: System messages [Serializable, NetSerializable] - protected sealed class NavMapComponentState( + protected sealed class NavMapState( Dictionary chunks, Dictionary beacons) - : ComponentState, IComponentDeltaState + : ComponentState { public Dictionary Chunks = chunks; public Dictionary Beacons = beacons; + } - // Required to infer deleted/missing chunks for delta states - public HashSet? AllChunks; - - public bool FullState => AllChunks == null; + [Serializable, NetSerializable] + protected sealed class NavMapDeltaState( + Dictionary modifiedChunks, + Dictionary beacons, + HashSet allChunks) + : ComponentState, IComponentDeltaState + { + public Dictionary ModifiedChunks = modifiedChunks; + public Dictionary Beacons = beacons; + public HashSet AllChunks = allChunks; - public void ApplyToFullState(IComponentState fullState) + public void ApplyToFullState(NavMapState state) { - DebugTools.Assert(!FullState); - var state = (NavMapComponentState) fullState; - DebugTools.Assert(state.FullState); - foreach (var key in state.Chunks.Keys) { if (!AllChunks!.Contains(key)) state.Chunks.Remove(key); } - foreach (var (index, data) in Chunks) + foreach (var (index, data) in ModifiedChunks) { if (!state.Chunks.TryGetValue(index, out var stateValue)) state.Chunks[index] = stateValue = new int[data.Length]; @@ -162,12 +160,8 @@ public void ApplyToFullState(IComponentState fullState) } } - public IComponentState CreateNewFullState(IComponentState fullState) + public NavMapState CreateNewFullState(NavMapState state) { - DebugTools.Assert(!FullState); - var state = (NavMapComponentState) fullState; - DebugTools.Assert(state.FullState); - var chunks = new Dictionary(state.Chunks.Count); foreach (var (index, data) in state.Chunks) { @@ -176,13 +170,13 @@ public IComponentState CreateNewFullState(IComponentState fullState) var newData = chunks[index] = new int[ArraySize]; - if (Chunks.TryGetValue(index, out var updatedData)) + if (ModifiedChunks.TryGetValue(index, out var updatedData)) Array.Copy(newData, updatedData, ArraySize); else Array.Copy(newData, data, ArraySize); } - return new NavMapComponentState(chunks, new(Beacons)); + return new NavMapState(chunks, new(Beacons)); } } From 01e7c9c37eaa34a4bc388f59eace26ab79684526 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Fri, 24 May 2024 16:11:45 +1200 Subject: [PATCH 027/568] Update engine to v223.0.0 (#28239) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index ec794ce4e469..6a6bfe33ca98 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit ec794ce4e4693069d3b3ebf7a88ead5ff2f860e0 +Subproject commit 6a6bfe33ca98dd3df6035ef4dcb7429f4dd5db66 From 76e13eed3595747135a8e0f4d668a9011d2ea06f Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Fri, 24 May 2024 17:03:03 +1200 Subject: [PATCH 028/568] Improve InteractionSystem range & BUI checks (#27999) * Improve InteractionSystem range & BUI checks * Ghost fixes * AAA * Fix test * fix nullable * revert to broadcast event * Fixes for eengine PR * Ah buckle code * ) --- .../UI/InstrumentBoundUserInterface.cs | 10 +- .../Interactable/InteractionSystem.cs | 22 +- Content.IntegrationTests/Tests/EntityTest.cs | 4 + .../Configurable/ConfigurationSystem.cs | 1 + .../Interaction/InteractionSystem.cs | 29 +- .../StationEvents/Events/ImmovableRodRule.cs | 4 +- .../Weapons/Melee/MeleeWeaponSystem.cs | 9 +- Content.Shared/Actions/SharedActionsSystem.cs | 8 +- .../Buckle/SharedBuckleSystem.Buckle.cs | 2 +- .../Interaction/SharedInteractionSystem.cs | 254 ++++++++++++------ .../Inventory/InventorySystem.Equip.cs | 6 +- .../MagicMirror/SharedMagicMirrorSystem.cs | 10 +- .../Movement/Pulling/Systems/PullingSystem.cs | 13 +- .../EntitySystems/SharedStorageSystem.cs | 2 +- .../UserInterface/ActivatableUIComponent.cs | 2 +- .../UserInterface/ActivatableUISystem.cs | 88 ++---- Content.Shared/Verbs/SharedVerbSystem.cs | 14 +- .../Fun/Instruments/base_instruments.yml | 2 +- 18 files changed, 232 insertions(+), 248 deletions(-) diff --git a/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs b/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs index 2a846ff708a6..0f5729f55b10 100644 --- a/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs +++ b/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs @@ -37,14 +37,8 @@ public InstrumentBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, u protected override void ReceiveMessage(BoundUserInterfaceMessage message) { - switch (message) - { - case InstrumentBandResponseBuiMessage bandRx: - _bandMenu?.Populate(bandRx.Nearby, EntMan); - break; - default: - break; - } + if (message is InstrumentBandResponseBuiMessage bandRx) + _bandMenu?.Populate(bandRx.Nearby, EntMan); } protected override void Open() diff --git a/Content.Client/Interactable/InteractionSystem.cs b/Content.Client/Interactable/InteractionSystem.cs index 0af8830e9acd..ff0a607920f3 100644 --- a/Content.Client/Interactable/InteractionSystem.cs +++ b/Content.Client/Interactable/InteractionSystem.cs @@ -4,24 +4,6 @@ namespace Content.Client.Interactable { - public sealed class InteractionSystem : SharedInteractionSystem - { - [Dependency] private readonly SharedContainerSystem _container = default!; - - public override bool CanAccessViaStorage(EntityUid user, EntityUid target) - { - if (!EntityManager.EntityExists(target)) - return false; - - if (!_container.TryGetContainingContainer(target, out var container)) - return false; - - if (!HasComp(container.Owner)) - return false; - - // we don't check if the user can access the storage entity itself. This should be handed by the UI system. - // Need to return if UI is open or not - return true; - } - } + // TODO Remove Shared prefix + public sealed class InteractionSystem : SharedInteractionSystem; } diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index d3b1fb47221d..54af64122be0 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -350,8 +350,12 @@ public async Task AllComponentsOneToOneDeleteTest() "DebrisFeaturePlacerController", // Above. "LoadedChunk", // Worldgen chunk loading malding. "BiomeSelection", // Whaddya know, requires config. + "ActivatableUI", // Requires enum key }; + // TODO TESTS + // auto ignore any components that have a "required" data field. + await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; var entityManager = server.ResolveDependency(); diff --git a/Content.Server/Configurable/ConfigurationSystem.cs b/Content.Server/Configurable/ConfigurationSystem.cs index 2683bf4e0955..5f5f1ef7d166 100644 --- a/Content.Server/Configurable/ConfigurationSystem.cs +++ b/Content.Server/Configurable/ConfigurationSystem.cs @@ -24,6 +24,7 @@ public override void Initialize() private void OnInteractUsing(EntityUid uid, ConfigurationComponent component, InteractUsingEvent args) { + // TODO use activatable ui system if (args.Handled) return; diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs index 4eac7e9ef1d4..9ac82b21858d 100644 --- a/Content.Server/Interaction/InteractionSystem.cs +++ b/Content.Server/Interaction/InteractionSystem.cs @@ -7,31 +7,6 @@ namespace Content.Server.Interaction { - /// - /// Governs interactions during clicking on entities - /// - [UsedImplicitly] - public sealed partial class InteractionSystem : SharedInteractionSystem - { - [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; - - public override bool CanAccessViaStorage(EntityUid user, EntityUid target) - { - if (Deleted(target)) - return false; - - if (!_container.TryGetContainingContainer(target, out var container)) - return false; - - if (!TryComp(container.Owner, out StorageComponent? storage)) - return false; - - if (storage.Container?.ID != container.ID) - return false; - - // we don't check if the user can access the storage entity itself. This should be handed by the UI system. - return _uiSystem.IsUiOpen(container.Owner, StorageComponent.StorageUiKey.Key, user); - } - } + // TODO Remove Shared prefix + public sealed class InteractionSystem : SharedInteractionSystem; } diff --git a/Content.Server/StationEvents/Events/ImmovableRodRule.cs b/Content.Server/StationEvents/Events/ImmovableRodRule.cs index cacb839cd398..aa193f2f4cbc 100644 --- a/Content.Server/StationEvents/Events/ImmovableRodRule.cs +++ b/Content.Server/StationEvents/Events/ImmovableRodRule.cs @@ -28,7 +28,9 @@ protected override void Started(EntityUid uid, ImmovableRodRuleComponent compone if (proto.TryGetComponent(out var rod) && proto.TryGetComponent(out var despawn)) { - TryFindRandomTile(out _, out _, out _, out var targetCoords); + if (!TryFindRandomTile(out _, out _, out _, out var targetCoords)) + return; + var speed = RobustRandom.NextFloat(rod.MinSpeed, rod.MaxSpeed); var angle = RobustRandom.NextAngle(); var direction = angle.ToVec(); diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index 2612e99ec9af..190a2d0263e8 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -187,15 +187,10 @@ protected override bool InRange(EntityUid user, EntityUid target, float range, I if (session is { } pSession) { (targetCoordinates, targetLocalAngle) = _lag.GetCoordinatesAngle(target, pSession); - } - else - { - var xform = Transform(target); - targetCoordinates = xform.Coordinates; - targetLocalAngle = xform.LocalRotation; + return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range); } - return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range); + return Interaction.InRangeUnobstructed(user, target, range); } protected override void DoDamageEffect(List targets, EntityUid? user, TransformComponent targetXform) diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 30687c932256..c92d67ba8129 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -502,13 +502,7 @@ private bool ValidateEntityTargetBase(EntityUid user, EntityUid target, EntityTa return distance <= action.Range; } - if (_interactionSystem.InRangeUnobstructed(user, target, range: action.Range) - && _containerSystem.IsInSameOrParentContainer(user, target)) - { - return true; - } - - return _interactionSystem.CanAccessViaStorage(user, target); + return _interactionSystem.InRangeAndAccessible(user, target, range: action.Range); } public bool ValidateWorldTarget(EntityUid user, EntityCoordinates coords, Entity action) diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index 4e94c6134b4a..00040211e36c 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -64,7 +64,7 @@ private void OnBuckleMove(EntityUid uid, BuckleComponent component, ref MoveEven return; var strapPosition = Transform(strapUid).Coordinates; - if (ev.NewPosition.InRange(EntityManager, _transform, strapPosition, strapComp.MaxBuckleDistance)) + if (ev.NewPosition.EntityId.IsValid() && ev.NewPosition.InRange(EntityManager, _transform, strapPosition, strapComp.MaxBuckleDistance)) return; TryUnbuckle(uid, uid, true, component); diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index c82a749755d6..3324ce5b9b8d 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -1,11 +1,10 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.ActionBlocker; -using Content.Shared.Administration; using Content.Shared.Administration.Logs; -using Content.Shared.Administration.Managers; using Content.Shared.CombatMode; using Content.Shared.Database; +using Content.Shared.Ghost; using Content.Shared.Hands; using Content.Shared.Hands.Components; using Content.Shared.Input; @@ -15,12 +14,13 @@ using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Movement.Components; -using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Physics; using Content.Shared.Popups; +using Content.Shared.Storage; using Content.Shared.Tag; using Content.Shared.Timing; +using Content.Shared.UserInterface; using Content.Shared.Verbs; using Content.Shared.Wall; using JetBrains.Annotations; @@ -36,6 +36,7 @@ using Robust.Shared.Random; using Robust.Shared.Serialization; using Robust.Shared.Timing; +using Robust.Shared.Utility; #pragma warning disable 618 @@ -50,12 +51,11 @@ public abstract partial class SharedInteractionSystem : EntitySystem [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly ISharedAdminManager _adminManager = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedPhysicsSystem _sharedBroadphaseSystem = default!; + [Dependency] private readonly SharedPhysicsSystem _broadphase = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedVerbSystem _verbSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; @@ -64,6 +64,18 @@ public abstract partial class SharedInteractionSystem : EntitySystem [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + + private EntityQuery _ignoreUiRangeQuery; + private EntityQuery _fixtureQuery; + private EntityQuery _itemQuery; + private EntityQuery _physicsQuery; + private EntityQuery _handsQuery; + private EntityQuery _relayQuery; + private EntityQuery _combatQuery; + private EntityQuery _wallMountQuery; + private EntityQuery _delayQuery; + private EntityQuery _uiQuery; private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable; @@ -76,6 +88,17 @@ public abstract partial class SharedInteractionSystem : EntitySystem public override void Initialize() { + _ignoreUiRangeQuery = GetEntityQuery(); + _fixtureQuery = GetEntityQuery(); + _itemQuery = GetEntityQuery(); + _physicsQuery = GetEntityQuery(); + _handsQuery = GetEntityQuery(); + _relayQuery = GetEntityQuery(); + _combatQuery = GetEntityQuery(); + _wallMountQuery = GetEntityQuery(); + _delayQuery = GetEntityQuery(); + _uiQuery = GetEntityQuery(); + SubscribeLocalEvent(HandleUserInterfaceRangeCheck); SubscribeLocalEvent(OnBoundInterfaceInteractAttempt); @@ -111,29 +134,57 @@ public override void Shutdown() /// private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev) { - var user = ev.Actor; + _uiQuery.TryComp(ev.Target, out var uiComp); + if (!_actionBlockerSystem.CanInteract(ev.Actor, ev.Target)) + { + // We permit ghosts to open uis unless explicitly blocked + if (ev.Message is not OpenBoundInterfaceMessage || !HasComp(ev.Actor) || uiComp?.BlockSpectators == true) + { + ev.Cancel(); + return; + } + } + + var range = _ui.GetUiRange(ev.Target, ev.UiKey); - if (!_actionBlockerSystem.CanInteract(user, ev.Target)) + // As long as range>0, the UI frame updates should have auto-closed the UI if it is out of range. + DebugTools.Assert(range <= 0 || UiRangeCheck(ev.Actor, ev.Target, range)); + + if (range <= 0 && !IsAccessible(ev.Actor, ev.Target)) { ev.Cancel(); return; } - // Check if the bound entity is accessible. Note that we allow admins to ignore this restriction, so that - // they can fiddle with UI's that people can't normally interact with (e.g., placing things directly into - // other people's backpacks). - if (!_containerSystem.IsInSameOrParentContainer(user, ev.Target) - && !CanAccessViaStorage(user, ev.Target) - && !_adminManager.HasAdminFlag(user, AdminFlags.Admin)) + if (uiComp == null) + return; + + if (uiComp.SingleUser && uiComp.CurrentSingleUser != ev.Actor) { ev.Cancel(); return; } - if (!InRangeUnobstructed(user, ev.Target)) - { + if (!uiComp.RequireHands) + return; + + if (!_handsQuery.TryComp(ev.Actor, out var hands) || hands.Hands.Count == 0) ev.Cancel(); - } + } + + private bool UiRangeCheck(Entity user, Entity target, float range) + { + if (!Resolve(target, ref target.Comp)) + return false; + + if (user.Owner == target.Owner) + return true; + + // Fast check: if the user is the parent of the entity (e.g., holding it), we always assume that it is in range + if (target.Comp.ParentUid == user.Owner) + return true; + + return InRangeAndAccessible(user, target, range) || _ignoreUiRangeQuery.HasComp(user); } /// @@ -190,10 +241,7 @@ private bool HandleTryPullObject(ICommonSession? session, EntityCoordinates coor if (!InRangeUnobstructed(userEntity.Value, uid, popup: true)) return false; - if (!TryComp(uid, out PullableComponent? pull)) - return false; - - _pullSystem.TogglePull(uid, userEntity.Value, pull); + _pullSystem.TogglePull(uid, userEntity.Value); return false; } @@ -269,7 +317,7 @@ private bool ShouldCheckAccess(EntityUid user) public bool CombatModeCanHandInteract(EntityUid user, EntityUid? target) { // Always allow attack in these cases - if (target == null || !TryComp(user, out var hands) || hands.ActiveHand?.HeldEntity is not null) + if (target == null || !_handsQuery.TryComp(user, out var hands) || hands.ActiveHand?.HeldEntity is not null) return false; // Only eat input if: @@ -277,7 +325,7 @@ public bool CombatModeCanHandInteract(EntityUid user, EntityUid? target) // - Target doesn't cancel should-interact event // This is intended to allow items to be picked up in combat mode, // but to also allow items to force attacks anyway (like mobs which are items, e.g. mice) - if (!HasComp(target)) + if (!_itemQuery.HasComp(target)) return false; var combatEv = new CombatModeShouldHandInteractEvent(); @@ -307,7 +355,7 @@ public void UserInteraction( bool checkAccess = true, bool checkCanUse = true) { - if (TryComp(user, out var relay) && relay.RelayEntity is not null) + if (_relayQuery.TryComp(user, out var relay) && relay.RelayEntity is not null) { // TODO this needs to be handled better. This probably bypasses many complex can-interact checks in weird roundabout ways. if (_actionBlockerSystem.CanInteract(user, target)) @@ -321,7 +369,7 @@ public void UserInteraction( if (target != null && Deleted(target.Value)) return; - if (!altInteract && TryComp(user, out var combatMode) && combatMode.IsInCombatMode) + if (!altInteract && _combatQuery.TryComp(user, out var combatMode) && combatMode.IsInCombatMode) { if (!CombatModeCanHandInteract(user, target)) return; @@ -343,10 +391,7 @@ public void UserInteraction( // Check if interacted entity is in the same container, the direct child, or direct parent of the user. // Also checks if the item is accessible via some storage UI (e.g., open backpack) - if (checkAccess - && target != null - && !_containerSystem.IsInSameOrParentContainer(user, target.Value) - && !CanAccessViaStorage(user, target.Value)) + if (checkAccess && target != null && !IsAccessible(user, target.Value)) return; var inRangeUnobstructed = target == null @@ -354,7 +399,7 @@ public void UserInteraction( : !checkAccess || InRangeUnobstructed(user, target.Value); // permits interactions with wall mounted entities // Does the user have hands? - if (!TryComp(user, out var hands) || hands.ActiveHand == null) + if (!_handsQuery.TryComp(user, out var hands) || hands.ActiveHand == null) { var ev = new InteractNoHandEvent(user, target, coordinates); RaiseLocalEvent(user, ev); @@ -494,7 +539,7 @@ public float UnobstructedDistance( predicate ??= _ => false; var ray = new CollisionRay(origin.Position, dir.Normalized(), collisionMask); - var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, dir.Length(), predicate.Invoke, false).ToList(); + var rayResults = _broadphase.IntersectRayWithPredicate(origin.MapId, ray, dir.Length(), predicate.Invoke, false).ToList(); if (rayResults.Count == 0) return dir.Length(); @@ -557,23 +602,29 @@ public bool InRangeUnobstructed( } var ray = new CollisionRay(origin.Position, dir.Normalized(), (int) collisionMask); - var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, length, predicate.Invoke, false).ToList(); + var rayResults = _broadphase.IntersectRayWithPredicate(origin.MapId, ray, length, predicate.Invoke, false).ToList(); return rayResults.Count == 0; } public bool InRangeUnobstructed( - EntityUid origin, - EntityUid other, + Entity origin, + Entity other, float range = InteractionRange, CollisionGroup collisionMask = InRangeUnobstructedMask, Ignored? predicate = null, bool popup = false) { - if (!TryComp(other, out TransformComponent? otherXform)) + if (!Resolve(other, ref other.Comp)) return false; - return InRangeUnobstructed(origin, other, otherXform.Coordinates, otherXform.LocalRotation, range, collisionMask, predicate, + return InRangeUnobstructed(origin, + other, + other.Comp.Coordinates, + other.Comp.LocalRotation, + range, + collisionMask, + predicate, popup); } @@ -605,8 +656,8 @@ public bool InRangeUnobstructed( /// True if the two points are within a given range without being obstructed. /// public bool InRangeUnobstructed( - EntityUid origin, - EntityUid other, + Entity origin, + Entity other, EntityCoordinates otherCoordinates, Angle otherAngle, float range = InteractionRange, @@ -614,10 +665,10 @@ public bool InRangeUnobstructed( Ignored? predicate = null, bool popup = false) { - Ignored combinedPredicate = e => e == origin || (predicate?.Invoke(e) ?? false); + Ignored combinedPredicate = e => e == origin.Owner || (predicate?.Invoke(e) ?? false); var inRange = true; MapCoordinates originPos = default; - var targetPos = otherCoordinates.ToMap(EntityManager, _transform); + var targetPos = _transform.ToMapCoordinates(otherCoordinates); Angle targetRot = default; // So essentially: @@ -627,23 +678,30 @@ public bool InRangeUnobstructed( // Alternatively we could check centre distances first though // that means we wouldn't be able to easily check overlap interactions. if (range > 0f && - TryComp(origin, out var fixtureA) && + _fixtureQuery.TryComp(origin, out var fixtureA) && // These fixture counts are stuff that has the component but no fixtures for (e.g. buttons). // At least until they get removed. fixtureA.FixtureCount > 0 && - TryComp(other, out var fixtureB) && + _fixtureQuery.TryComp(other, out var fixtureB) && fixtureB.FixtureCount > 0 && - TryComp(origin, out TransformComponent? xformA)) + Resolve(origin, ref origin.Comp)) { - var (worldPosA, worldRotA) = xformA.GetWorldPositionRotation(); + var (worldPosA, worldRotA) = origin.Comp.GetWorldPositionRotation(); var xfA = new Transform(worldPosA, worldRotA); var parentRotB = _transform.GetWorldRotation(otherCoordinates.EntityId); var xfB = new Transform(targetPos.Position, parentRotB + otherAngle); // Different map or the likes. - if (!_sharedBroadphaseSystem.TryGetNearest(origin, other, - out _, out _, out var distance, - xfA, xfB, fixtureA, fixtureB)) + if (!_broadphase.TryGetNearest( + origin, + other, + out _, + out _, + out var distance, + xfA, + xfB, + fixtureA, + fixtureB)) { inRange = false; } @@ -665,15 +723,15 @@ public bool InRangeUnobstructed( else { // We'll still do the raycast from the centres but we'll bump the range as we know they're in range. - originPos = _transform.GetMapCoordinates(origin, xform: xformA); + originPos = _transform.GetMapCoordinates(origin, xform: origin.Comp); range = (originPos.Position - targetPos.Position).Length(); } } // No fixtures, e.g. wallmounts. else { - originPos = _transform.GetMapCoordinates(origin); - var otherParent = Transform(other).ParentUid; + originPos = _transform.GetMapCoordinates(origin, origin); + var otherParent = (other.Comp ?? Transform(other)).ParentUid; targetRot = otherParent.IsValid() ? Transform(otherParent).LocalRotation + otherAngle : otherAngle; } @@ -724,13 +782,13 @@ private Ignored GetPredicate( { HashSet ignored = new(); - if (HasComp(target) && TryComp(target, out PhysicsComponent? physics) && physics.CanCollide) + if (_itemQuery.HasComp(target) && _physicsQuery.TryComp(target, out var physics) && physics.CanCollide) { // If the target is an item, we ignore any colliding entities. Currently done so that if items get stuck // inside of walls, users can still pick them up. - ignored.UnionWith(_sharedBroadphaseSystem.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics)); + ignored.UnionWith(_broadphase.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics)); } - else if (TryComp(target, out WallMountComponent? wallMount)) + else if (_wallMountQuery.TryComp(target, out var wallMount)) { // wall-mount exemptions may be restricted to a specific angle range.da @@ -748,13 +806,7 @@ private Ignored GetPredicate( ignored.UnionWith(grid.GetAnchoredEntities(targetCoords)); } - Ignored combinedPredicate = e => - { - return e == target - || (predicate?.Invoke(e) ?? false) - || ignored.Contains(e); - }; - + Ignored combinedPredicate = e => e == target || (predicate?.Invoke(e) ?? false) || ignored.Contains(e); return combinedPredicate; } @@ -951,10 +1003,8 @@ public bool InteractionActivate( bool checkUseDelay = true, bool checkAccess = true) { - UseDelayComponent? delayComponent = null; - if (checkUseDelay - && TryComp(used, out delayComponent) - && _useDelay.IsDelayed((used, delayComponent))) + _delayQuery.TryComp(used, out var delayComponent); + if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent))) return false; if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used)) @@ -965,11 +1015,11 @@ public bool InteractionActivate( // Check if interacted entity is in the same container, the direct child, or direct parent of the user. // This is bypassed IF the interaction happened through an item slot (e.g., backpack UI) - if (checkAccess && !_containerSystem.IsInSameOrParentContainer(user, used) && !CanAccessViaStorage(user, used)) + if (checkAccess && !IsAccessible(user, used)) return false; // Does the user have hands? - if (!HasComp(user)) + if (!_handsQuery.HasComp(user)) return false; var activateMsg = new ActivateInWorldEvent(user, used); @@ -979,7 +1029,9 @@ public bool InteractionActivate( DoContactInteraction(user, used, activateMsg); // Still need to call this even without checkUseDelay in case this gets relayed from Activate. - _useDelay.TryResetDelay(used, component: delayComponent); + if (delayComponent != null) + _useDelay.TryResetDelay(used, component: delayComponent); + if (!activateMsg.WasLogged) _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); return true; @@ -1000,11 +1052,8 @@ public bool UseInHandInteraction( bool checkCanInteract = true, bool checkUseDelay = true) { - UseDelayComponent? delayComponent = null; - - if (checkUseDelay - && TryComp(used, out delayComponent) - && _useDelay.IsDelayed((used, delayComponent))) + _delayQuery.TryComp(used, out var delayComponent); + if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent))) return true; // if the item is on cooldown, we consider this handled. if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used)) @@ -1066,11 +1115,60 @@ public void DroppedInteraction(EntityUid user, EntityUid item) } #endregion + /// + /// Check if a user can access a target (stored in the same containers) and is in range without obstructions. + /// + public bool InRangeAndAccessible( + Entity user, + Entity target, + float range = InteractionRange, + CollisionGroup collisionMask = InRangeUnobstructedMask, + Ignored? predicate = null) + { + if (user == target) + return true; + + if (!Resolve(user, ref user.Comp)) + return false; + + if (!Resolve(target, ref target.Comp)) + return false; + + return IsAccessible(user, target) && InRangeUnobstructed(user, target, range, collisionMask, predicate); + } + + /// + /// Check if a user can access a target or if they are stored in different containers. + /// + public bool IsAccessible(Entity user, Entity target) + { + if (_containerSystem.IsInSameOrParentContainer(user, target, out _, out var container)) + return true; + + return container != null && CanAccessViaStorage(user, target, container); + } + /// /// If a target is in range, but not in the same container as the user, it may be inside of a backpack. This /// checks if the user can access the item in these situations. /// - public abstract bool CanAccessViaStorage(EntityUid user, EntityUid target); + public bool CanAccessViaStorage(EntityUid user, EntityUid target) + { + if (!_containerSystem.TryGetContainingContainer(target, out var container)) + return false; + + return CanAccessViaStorage(user, target, container); + } + + /// + public bool CanAccessViaStorage(EntityUid user, EntityUid target, BaseContainer container) + { + if (StorageComponent.ContainerId != container.ID) + return false; + + // we don't check if the user can access the storage entity itself. This should be handed by the UI system. + return _ui.IsUiOpen(target, StorageComponent.StorageUiKey.Key, user); + } /// /// Checks whether an entity currently equipped by another player is accessible to some user. This shouldn't @@ -1151,19 +1249,15 @@ public void DoContactInteraction(EntityUid uidA, EntityUid? uidB, HandledEntityE RaiseLocalEvent(uidB.Value, new ContactInteractionEvent(uidA)); } + private void HandleUserInterfaceRangeCheck(ref BoundUserInterfaceCheckRangeEvent ev) { if (ev.Result == BoundUserInterfaceRangeResult.Fail) return; - if (InRangeUnobstructed(ev.Actor, ev.Target, ev.Data.InteractionRange)) - { - ev.Result = BoundUserInterfaceRangeResult.Pass; - } - else - { - ev.Result = BoundUserInterfaceRangeResult.Fail; - } + ev.Result = UiRangeCheck(ev.Actor!, ev.Target, ev.Data.InteractionRange) + ? BoundUserInterfaceRangeResult.Pass + : BoundUserInterfaceRangeResult.Fail; } } diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index 400dfb0beb3d..7fd156213b45 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -210,11 +210,7 @@ public bool CanAccess(EntityUid actor, EntityUid target, EntityUid itemUid) return false; // Can the actor reach the item? - if (_interactionSystem.InRangeUnobstructed(actor, itemUid) && _containerSystem.IsInSameOrParentContainer(actor, itemUid)) - return true; - - // Is the item in an open storage UI, i.e., is the user quick-equipping from an open backpack? - if (_interactionSystem.CanAccessViaStorage(actor, itemUid)) + if (_interactionSystem.InRangeAndAccessible(actor, itemUid)) return true; // Is the actor currently stripping the target? Here we could check if the actor has the stripping UI open, but diff --git a/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs b/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs index 91059d60bfd3..433ad6b4fc94 100644 --- a/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs +++ b/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Interaction; using Content.Shared.UserInterface; using Robust.Shared.Serialization; +using Robust.Shared.Utility; namespace Content.Shared.MagicMirror; @@ -21,10 +22,13 @@ public override void Initialize() private void OnMirrorRangeCheck(EntityUid uid, MagicMirrorComponent component, ref BoundUserInterfaceCheckRangeEvent args) { - if (!Exists(component.Target) || !_interaction.InRangeUnobstructed(uid, component.Target.Value)) - { + if (args.Result == BoundUserInterfaceRangeResult.Fail) + return; + + DebugTools.Assert(component.Target != null && Exists(component.Target)); + + if (!_interaction.InRangeUnobstructed(uid, component.Target.Value)) args.Result = BoundUserInterfaceRangeResult.Fail; - } } private void OnBeforeUIOpen(Entity ent, ref BeforeActivatableUIOpenEvent args) diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index 2781c4952989..3de71172c72f 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -362,14 +362,17 @@ public bool CanPull(EntityUid puller, EntityUid pullableUid, PullerComponent? pu return !startPull.Cancelled && !getPulled.Cancelled; } - public bool TogglePull(EntityUid pullableUid, EntityUid pullerUid, PullableComponent pullable) + public bool TogglePull(Entity pullable, EntityUid pullerUid) { - if (pullable.Puller == pullerUid) + if (!Resolve(pullable, ref pullable.Comp, false)) + return false; + + if (pullable.Comp.Puller == pullerUid) { - return TryStopPull(pullableUid, pullable); + return TryStopPull(pullable, pullable.Comp); } - return TryStartPull(pullerUid, pullableUid, pullableComp: pullable); + return TryStartPull(pullerUid, pullable, pullableComp: pullable); } public bool TogglePull(EntityUid pullerUid, PullerComponent puller) @@ -377,7 +380,7 @@ public bool TogglePull(EntityUid pullerUid, PullerComponent puller) if (!TryComp(puller.Pulling, out var pullable)) return false; - return TogglePull(puller.Pulling.Value, pullerUid, pullable); + return TogglePull((puller.Pulling.Value, pullable), pullerUid); } public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index ea0c9632f0ec..778e2b2e74e9 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -1078,7 +1078,7 @@ public bool PlayerInsertHeldEntity(EntityUid uid, EntityUid player, StorageCompo /// true if inserted, false otherwise public bool PlayerInsertEntityInWorld(Entity uid, EntityUid player, EntityUid toInsert) { - if (!Resolve(uid, ref uid.Comp) || !_interactionSystem.InRangeUnobstructed(player, uid)) + if (!Resolve(uid, ref uid.Comp) || !_interactionSystem.InRangeUnobstructed(player, uid.Owner)) return false; if (!Insert(uid, toInsert, out _, user: player, uid.Comp)) diff --git a/Content.Shared/UserInterface/ActivatableUIComponent.cs b/Content.Shared/UserInterface/ActivatableUIComponent.cs index 3f83816b7dea..93f05acac053 100644 --- a/Content.Shared/UserInterface/ActivatableUIComponent.cs +++ b/Content.Shared/UserInterface/ActivatableUIComponent.cs @@ -57,7 +57,7 @@ public sealed partial class ActivatableUIComponent : Component /// [ViewVariables(VVAccess.ReadWrite)] [DataField] - public bool AllowSpectator = true; + public bool BlockSpectators; /// /// Whether the item must be in the user's currently selected/active hand. diff --git a/Content.Shared/UserInterface/ActivatableUISystem.cs b/Content.Shared/UserInterface/ActivatableUISystem.cs index a6d27ac5459f..c1822c4ee335 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.cs @@ -8,7 +8,7 @@ using Content.Shared.Interaction.Events; using Content.Shared.Popups; using Content.Shared.Verbs; -using Robust.Shared.Containers; +using Robust.Shared.Utility; namespace Content.Shared.UserInterface; @@ -19,15 +19,12 @@ public sealed partial class ActivatableUISystem : EntitySystem [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly SharedInteractionSystem _interaction = default!; - - private readonly List _toClose = new(); public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(OnInteractUsing); @@ -37,28 +34,24 @@ public override void Initialize() SubscribeLocalEvent>(GetActivationVerb); SubscribeLocalEvent>(GetVerb); - // TODO ActivatableUI - // Add UI-user component, and listen for user container changes. - // I.e., should lose a computer UI if a player gets shut into a locker. - SubscribeLocalEvent(OnGotInserted); - SubscribeLocalEvent(OnGotRemoved); - - SubscribeLocalEvent(OnBoundInterfaceInteractAttempt); SubscribeLocalEvent(OnActionPerform); InitializePower(); } - private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev) + private void OnStartup(Entity ent, ref ComponentStartup args) { - if (!TryComp(ev.Target, out ActivatableUIComponent? comp)) - return; - - if (!comp.RequireHands) + if (ent.Comp.Key == null) + { + Log.Error($"Missing UI Key for entity: {ToPrettyString(ent)}"); return; + } - if (!TryComp(ev.Actor, out HandsComponent? hands) || hands.Hands.Count == 0) - ev.Cancel(); + // TODO BUI + // set interaction range to zero to avoid constant range checks. + // + // if (ent.Comp.InHandsOnly && _uiSystem.TryGetInterfaceData(ent.Owner, ent.Comp.Key, out var data)) + // data.InteractionRange = 0; } private void OnActionPerform(EntityUid uid, UserInterfaceComponent component, OpenUiActionEvent args) @@ -77,9 +70,10 @@ private void GetActivationVerb(EntityUid uid, ActivatableUIComponent component, args.Verbs.Add(new ActivationVerb { - // TODO VERBS add "open UI" icon Act = () => InteractUI(args.User, uid, component), - Text = Loc.GetString(component.VerbText) + Text = Loc.GetString(component.VerbText), + // TODO VERB ICON find a better icon + Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")), }); } @@ -90,9 +84,10 @@ private void GetVerb(EntityUid uid, ActivatableUIComponent component, GetVerbsEv args.Verbs.Add(new Verb { - // TODO VERBS add "open UI" icon Act = () => InteractUI(args.User, uid, component), - Text = Loc.GetString(component.VerbText) + Text = Loc.GetString(component.VerbText), + // TODO VERB ICON find a better icon + Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")), }); } @@ -119,7 +114,7 @@ private bool ShouldAddVerb(EntityUid uid, ActivatableUIComponent component, G } } - return args.CanInteract || component.AllowSpectator && HasComp(args.User); + return args.CanInteract || HasComp(args.User) && !component.BlockSpectators; } private void OnUseInHand(EntityUid uid, ActivatableUIComponent component, UseInHandEvent args) @@ -191,7 +186,7 @@ private bool InteractUI(EntityUid user, EntityUid uiEntity, ActivatableUICompone return true; } - if (!_blockerSystem.CanInteract(user, uiEntity) && (!aui.AllowSpectator || !HasComp(user))) + if (!_blockerSystem.CanInteract(user, uiEntity) && (!HasComp(user) || aui.BlockSpectators)) return false; if (aui.RequireHands) @@ -286,47 +281,4 @@ private void OnHandUnequipped(Entity ent, ref GotUnequip if (ent.Comp.RequireHands && ent.Comp.InHandsOnly) CloseAll(ent, ent); } - - private void OnGotInserted(Entity ent, ref EntGotInsertedIntoContainerMessage args) - { - CheckAccess((ent, ent)); - } - - private void OnGotRemoved(Entity ent, ref EntGotRemovedFromContainerMessage args) - { - CheckAccess((ent, ent)); - } - - public void CheckAccess(Entity ent) - { - if (!Resolve(ent, ref ent.Comp)) - return; - - if (ent.Comp.Key == null) - { - Log.Error($"Encountered null key in activatable ui on entity {ToPrettyString(ent)}"); - return; - } - - foreach (var user in _uiSystem.GetActors(ent.Owner, ent.Comp.Key)) - { - if (!_container.IsInSameOrParentContainer(user, ent) - && !_interaction.CanAccessViaStorage(user, ent)) - { - _toClose.Add(user); - continue; - - } - - if (!_interaction.InRangeUnobstructed(user, ent)) - _toClose.Add(user); - } - - foreach (var user in _toClose) - { - _uiSystem.CloseUi(ent.Owner, ent.Comp.Key, user); - } - - _toClose.Clear(); - } } diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index 60714aea8f38..e78fe98f4c3c 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -72,19 +72,7 @@ public SortedSet GetLocalVerbs(EntityUid target, EntityUid user, List Date: Fri, 24 May 2024 05:04:10 +0000 Subject: [PATCH 029/568] 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 9ae89dad4a3f..d330b043c617 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Plykiya - changes: - - message: Pre-filled syringes start in inject mode now. - type: Tweak - id: 6114 - time: '2024-03-09T10:19:03.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25881 - author: EmoGarbage404 changes: - message: Added a new "colorblind friendly" toggle in the accessibility menu. This @@ -3875,3 +3868,10 @@ id: 6613 time: '2024-05-23T20:27:45.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28226 +- author: ElectroJr + changes: + - message: Ghosts can once again open paper & other UIs + type: Fix + id: 6614 + time: '2024-05-24T05:03:03.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27999 From 926f72ee13ab3bea948d517f34957d0c75ccbe3a Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Fri, 24 May 2024 19:02:04 +1200 Subject: [PATCH 030/568] Update engine to v223.1.1 (#28245) * Update engine to v223.1.0 * Update engine to v223.1.1 --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 6a6bfe33ca98..c250010dada8 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 6a6bfe33ca98dd3df6035ef4dcb7429f4dd5db66 +Subproject commit c250010dada8279a48f6861dc80c0eebafee3421 From b2ca9b6a9cafe1e6f630fc6f085393a367597da1 Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Fri, 24 May 2024 14:44:42 +0000 Subject: [PATCH 031/568] Fix firelock prediction issues with periodic pulses of closing lights (#28227) * Fix firelock prediction issues with periodic pulses of closing lights For some reason this function was setting a time for the next state which was triggering the door system to try to close the firelock. This does not happen serverside because the function only fires from an event called clientside apparently. It appears to be an attempt to stop firelocks from closing instantly that did not function properly, and I cannot discern any other purpose. As such I have removed it. * Remove redundant serverside check This became redundant with commit 439a87f2 --- Content.Server/Doors/Systems/FirelockSystem.cs | 15 --------------- .../Doors/Components/FirelockComponent.cs | 2 -- .../Doors/Systems/SharedFirelockSystem.cs | 15 --------------- 3 files changed, 32 deletions(-) diff --git a/Content.Server/Doors/Systems/FirelockSystem.cs b/Content.Server/Doors/Systems/FirelockSystem.cs index 5ad86fb20aab..e2b8b5829d18 100644 --- a/Content.Server/Doors/Systems/FirelockSystem.cs +++ b/Content.Server/Doors/Systems/FirelockSystem.cs @@ -28,8 +28,6 @@ public override void Initialize() { base.Initialize(); - - SubscribeLocalEvent(OnBeforeDoorAutoclose); SubscribeLocalEvent(OnAtmosAlarm); SubscribeLocalEvent(PowerChanged); @@ -80,19 +78,6 @@ public override void Update(float frameTime) } } - private void OnBeforeDoorAutoclose(EntityUid uid, FirelockComponent component, BeforeDoorAutoCloseEvent args) - { - if (!this.IsPowered(uid, EntityManager)) - args.Cancel(); - - // Make firelocks autoclose, but only if the last alarm type it - // remembers was a danger. This is to prevent people from - // flooding hallways with endless bad air/fire. - if (component.AlarmAutoClose && - (_atmosAlarmable.TryGetHighestAlert(uid, out var alarm) && alarm != AtmosAlarmType.Danger || alarm == null)) - args.Cancel(); - } - private void OnAtmosAlarm(EntityUid uid, FirelockComponent component, AtmosAlarmEvent args) { if (!this.IsPowered(uid, EntityManager)) diff --git a/Content.Shared/Doors/Components/FirelockComponent.cs b/Content.Shared/Doors/Components/FirelockComponent.cs index ca62daaa0fd1..3f7d6c3f7049 100644 --- a/Content.Shared/Doors/Components/FirelockComponent.cs +++ b/Content.Shared/Doors/Components/FirelockComponent.cs @@ -19,8 +19,6 @@ public sealed partial class FirelockComponent : Component [DataField("lockedPryTimeModifier"), ViewVariables(VVAccess.ReadWrite)] public float LockedPryTimeModifier = 1.5f; - [DataField("autocloseDelay")] public TimeSpan AutocloseDelay = TimeSpan.FromSeconds(3f); - /// /// Maximum pressure difference before the firelock will refuse to open, in kPa. /// diff --git a/Content.Shared/Doors/Systems/SharedFirelockSystem.cs b/Content.Shared/Doors/Systems/SharedFirelockSystem.cs index 7d033efdd408..47a29a4ba803 100644 --- a/Content.Shared/Doors/Systems/SharedFirelockSystem.cs +++ b/Content.Shared/Doors/Systems/SharedFirelockSystem.cs @@ -18,8 +18,6 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnUpdateState); - // Access/Prying SubscribeLocalEvent(OnBeforeDoorOpened); SubscribeLocalEvent(OnDoorGetPryTimeModifier); @@ -46,19 +44,6 @@ public bool EmergencyPressureStop(EntityUid uid, FirelockComponent? firelock = n return _doorSystem.OnPartialClose(uid, door); } - private void OnUpdateState(EntityUid uid, FirelockComponent component, DoorStateChangedEvent args) - { - var ev = new BeforeDoorAutoCloseEvent(); - RaiseLocalEvent(uid, ev); - UpdateVisuals(uid, component, args); - if (ev.Cancelled) - { - return; - } - - _doorSystem.SetNextStateChange(uid, component.AutocloseDelay); - } - #region Access/Prying private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args) From aa09741a4143b469b78a7215def4f0fc5e5a068c Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 24 May 2024 14:45:49 +0000 Subject: [PATCH 032/568] 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 d330b043c617..61b8e7ec78f6 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,15 +1,4 @@ Entries: -- author: EmoGarbage404 - changes: - - message: Added a new "colorblind friendly" toggle in the accessibility menu. This - allows you to toggle between a standard and colorblind-friendly palette for - things like progress bars and the medical HUD. - type: Add - - message: The medical HUD is now bright, even in low light levels. - type: Tweak - id: 6115 - time: '2024-03-09T11:43:20.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25318 - author: metalgearsloth changes: - message: Fix NPC mouse movement. @@ -3875,3 +3864,10 @@ id: 6614 time: '2024-05-24T05:03:03.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27999 +- author: nikthechampiongr + changes: + - message: Firelocks will no longer randomly pulse closing lights. + type: Fix + id: 6615 + time: '2024-05-24T14:44:42.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28227 From 98503fc793be26acde787a6ab765f38887d0ae78 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 24 May 2024 15:57:02 -0400 Subject: [PATCH 033/568] Hotfix for crashes from bad item names (#28256) --- Content.Client/Examine/ExamineSystem.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs index 45db4efa53ca..125ec62e4920 100644 --- a/Content.Client/Examine/ExamineSystem.cs +++ b/Content.Client/Examine/ExamineSystem.cs @@ -239,8 +239,10 @@ public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCurso if (knowTarget) { - var itemName = FormattedMessage.RemoveMarkup(Identity.Name(target, EntityManager, player)); - var labelMessage = FormattedMessage.FromMarkup($"[bold]{itemName}[/bold]"); + // TODO: FormattedMessage.RemoveMarkupPermissive + // var itemName = FormattedMessage.RemoveMarkupPermissive(Identity.Name(target, EntityManager, player)); + var itemName = FormattedMessage.FromMarkupPermissive(Identity.Name(target, EntityManager, player)).ToString(); + var labelMessage = FormattedMessage.FromMarkupPermissive($"[bold]{itemName}[/bold]"); var label = new RichTextLabel(); label.SetMessage(labelMessage); hBox.AddChild(label); From d0cf620a85bd14ce8bdc641f6dde1715e771879e Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sat, 25 May 2024 15:13:24 +1200 Subject: [PATCH 034/568] Fix weapon error logs (#28264) --- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 0fb6ac3eff96..7fc440db4794 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -174,49 +174,39 @@ private void OnStopAttack(StopAttackEvent msg, EntitySessionEventArgs args) private void OnLightAttack(LightAttackEvent msg, EntitySessionEventArgs args) { - var user = args.SenderSession.AttachedEntity; - - if (user == null) + if (args.SenderSession.AttachedEntity is not {} user) return; - if (!TryGetWeapon(user.Value, out var weaponUid, out var weapon) || + if (!TryGetWeapon(user, out var weaponUid, out var weapon) || weaponUid != GetEntity(msg.Weapon)) { return; } - AttemptAttack(args.SenderSession.AttachedEntity!.Value, weaponUid, weapon, msg, args.SenderSession); + AttemptAttack(user, weaponUid, weapon, msg, args.SenderSession); } private void OnHeavyAttack(HeavyAttackEvent msg, EntitySessionEventArgs args) { - if (args.SenderSession.AttachedEntity == null) - { + if (args.SenderSession.AttachedEntity is not {} user) return; - } - if (!TryGetWeapon(args.SenderSession.AttachedEntity.Value, out var weaponUid, out var weapon) || + if (!TryGetWeapon(user, out var weaponUid, out var weapon) || weaponUid != GetEntity(msg.Weapon)) { return; } - AttemptAttack(args.SenderSession.AttachedEntity.Value, weaponUid, weapon, msg, args.SenderSession); + AttemptAttack(user, weaponUid, weapon, msg, args.SenderSession); } private void OnDisarmAttack(DisarmAttackEvent msg, EntitySessionEventArgs args) { - if (args.SenderSession.AttachedEntity == null) - { - return; - } - - if (!TryGetWeapon(args.SenderSession.AttachedEntity.Value, out var weaponUid, out var weapon)) - { + if (args.SenderSession.AttachedEntity is not {} user) return; - } - AttemptAttack(args.SenderSession.AttachedEntity.Value, weaponUid, weapon, msg, args.SenderSession); + if (TryGetWeapon(user, out var weaponUid, out var weapon)) + AttemptAttack(user, weaponUid, weapon, msg, args.SenderSession); } /// @@ -343,23 +333,32 @@ private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponCompo if (!CombatMode.IsInCombatMode(user)) return false; + EntityUid? target = null; switch (attack) { case LightAttackEvent light: - var lightTarget = GetEntity(light.Target); + if (light.Target != null && !TryGetEntity(light.Target, out target)) + { + // Target was lightly attacked & deleted. + return false; + } - if (!Blocker.CanAttack(user, lightTarget, (weaponUid, weapon))) + if (!Blocker.CanAttack(user, target, (weaponUid, weapon))) return false; // Can't self-attack if you're the weapon - if (weaponUid == lightTarget) + if (weaponUid == target) return false; break; case DisarmAttackEvent disarm: - var disarmTarget = GetEntity(disarm.Target); + if (disarm.Target != null && !TryGetEntity(disarm.Target, out target)) + { + // Target was lightly attacked & deleted. + return false; + } - if (!Blocker.CanAttack(user, disarmTarget, (weaponUid, weapon), true)) + if (!Blocker.CanAttack(user, target, (weaponUid, weapon), true)) return false; break; default: From a58323cca150af73814be6d4cb70741246289d54 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sat, 25 May 2024 21:49:46 +1200 Subject: [PATCH 035/568] Update engine to v223.1.2 (#28273) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index c250010dada8..796abe123055 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit c250010dada8279a48f6861dc80c0eebafee3421 +Subproject commit 796abe1230554c31daffbfa6559a0a4bcf50b1af From 4735cb7d2df3b54e7213913532a67f64be4ed606 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 26 May 2024 08:03:05 +1200 Subject: [PATCH 036/568] Fix dud modular grenade visuals (#28265) --- .../Objects/Weapons/Throwable/grenades.yml | 3 +- .../Graphs/weapons/modular_grenade.yml | 30 ++++++++++++++++-- .../Weapons/Grenades/modular.rsi/meta.json | 6 +++- .../Grenades/modular.rsi/no-trigger.png | Bin 0 -> 347 bytes 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 Resources/Textures/Objects/Weapons/Grenades/modular.rsi/no-trigger.png diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml index 0261bd8cadd9..b1d260c32761 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml @@ -320,11 +320,12 @@ emptyCase: { state: empty } wiredCase: { state: wired } caseWithTrigger: { state: no-payload } + caseWithPayload: { state: no-trigger } grenade: { state: complete } enum.Trigger.TriggerVisuals.VisualState: enum.ConstructionVisuals.Layer: Primed: { state: primed } - Unprimed: { state: complete } + # Unprimed: - type: StaticPrice price: 25 diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_grenade.yml b/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_grenade.yml index 020be4e09c3d..243a030c9810 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_grenade.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_grenade.yml @@ -12,7 +12,7 @@ doAfter: 1 - node: emptyCase - entity: ModularGrenade + entity: ModularGrenade actions: - !type:AppearanceChange edges: @@ -31,7 +31,7 @@ doAfter: 2 - node: wiredCase - entity: ModularGrenade + entity: ModularGrenade actions: - !type:AppearanceChange - !type:PlaySound @@ -50,6 +50,12 @@ store: payloadTrigger name: Trigger doAfter: 0.5 + - to: caseWithPayload + steps: + - tag: Payload + store: payload + name: Payload + doAfter: 0.5 - node: caseWithTrigger actions: @@ -71,6 +77,26 @@ name: Payload doAfter: 0.5 + - node: caseWithPayload + actions: + - !type:AppearanceChange + - !type:PlaySound + sound: /Audio/Machines/button.ogg + edges: + - to: wiredCase + steps: + - tool: Prying + doAfter: 0.5 + completed: + - !type:EmptyContainer + container: payload + - to: grenade + steps: + - component: PayloadTrigger + store: payloadTrigger + name: Trigger + doAfter: 0.5 + - node: grenade actions: - !type:AppearanceChange diff --git a/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/meta.json b/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/meta.json index f23b6ec168d4..b0b12127c594 100644 --- a/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/meta.json +++ b/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/29c0ed1b000619cb5398ef921000a8d4502ba0b6 and modified by Swept", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/29c0ed1b000619cb5398ef921000a8d4502ba0b6 and modified by Swept & ElectroSR", "size": { "x": 32, "y": 32 @@ -19,6 +19,10 @@ "name": "no-payload", "directions": 1 }, + { + "name": "no-trigger", + "directions": 1 + }, { "name": "complete", "directions": 1 diff --git a/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/no-trigger.png b/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/no-trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..1be2cbc421b2add226c928b7612c9b856a09c276 GIT binary patch literal 347 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSL74G){)!Z!;0I3^$B+p3WC_;A2_iie2RN2`G8GjS{a@(Q z=H4$iF_n#H!Wzl6owH|4&)A=RU9U^#Aj{HDwiDBAYk!IOD7SO0Y^m*=uT?c;?&f zT^!F*VIz`Sv4yW`C8I|f!&b(Vd^Z&T&vMk`01F&gV7>`++5XhtJuoB4;ERwVPtKh^+&!ACD#s Date: Sat, 25 May 2024 20:04:12 +0000 Subject: [PATCH 037/568] 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 61b8e7ec78f6..d3b1dfb40504 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix NPC mouse movement. - type: Fix - id: 6116 - time: '2024-03-10T15:41:42.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25965 - author: DoutorWhite changes: - message: Prevents rendering from crashing in certain scenarios @@ -3871,3 +3864,10 @@ id: 6615 time: '2024-05-24T14:44:42.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28227 +- author: ElectroJr + changes: + - message: Fixed modular grenade visuals getting stuck in an incorrect state. + type: Fix + id: 6616 + time: '2024-05-25T20:03:05.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28265 From 8619f241d4b44ff4e4bb40da83cc12334a966692 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 25 May 2024 13:07:37 -0700 Subject: [PATCH 038/568] Fix not networking whitelist and blacklist in storage component (#28238) --- .../Storage/EntitySystems/SharedStorageSystem.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 778e2b2e74e9..18a8f20afc7e 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -21,6 +21,7 @@ using Content.Shared.Storage.Components; using Content.Shared.Timing; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -151,7 +152,9 @@ private void OnStorageGetState(EntityUid uid, StorageComponent component, ref Co Grid = new List(component.Grid), MaxItemSize = component.MaxItemSize, StoredItems = storedItems, - SavedLocations = component.SavedLocations + SavedLocations = component.SavedLocations, + Whitelist = component.Whitelist, + Blacklist = component.Blacklist }; } @@ -163,6 +166,8 @@ private void OnStorageHandleState(EntityUid uid, StorageComponent component, ref component.Grid.Clear(); component.Grid.AddRange(state.Grid); component.MaxItemSize = state.MaxItemSize; + component.Whitelist = state.Whitelist; + component.Blacklist = state.Blacklist; component.StoredItems.Clear(); @@ -1490,5 +1495,9 @@ protected sealed class StorageComponentState : ComponentState public List Grid = new(); public ProtoId? MaxItemSize; + + public EntityWhitelist? Whitelist; + + public EntityWhitelist? Blacklist; } } From 37ad81c59ca04b10cdc420eb66ae7a9487ecdfaf Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Sat, 25 May 2024 20:08:15 +0000 Subject: [PATCH 039/568] fix id card console not updating records (#28237) * fix id card console not updating records * test --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- Content.Server/Access/Systems/IdCardConsoleSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/Access/Systems/IdCardConsoleSystem.cs b/Content.Server/Access/Systems/IdCardConsoleSystem.cs index e680b0c6f407..4e63c93a837d 100644 --- a/Content.Server/Access/Systems/IdCardConsoleSystem.cs +++ b/Content.Server/Access/Systems/IdCardConsoleSystem.cs @@ -135,6 +135,8 @@ private void TryWriteToTargetId(EntityUid uid, _idCard.TryChangeJobDepartment(targetId, job); } + UpdateStationRecord(uid, targetId, newFullName, newJobTitle, job); + if (!newAccessList.TrueForAll(x => component.AccessLevels.Contains(x))) { _sawmill.Warning($"User {ToPrettyString(uid)} tried to write unknown access tag."); @@ -168,8 +170,6 @@ private void TryWriteToTargetId(EntityUid uid, This current implementation is pretty shit as it logs 27 entries (27 lines) if someone decides to give themselves AA*/ _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(player):player} has modified {ToPrettyString(targetId):entity} with the following accesses: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]"); - - UpdateStationRecord(uid, targetId, newFullName, newJobTitle, job); } /// From cb17fe2845e59c1b55b9594a9ed8a2c2c8169bf4 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 25 May 2024 20:09:22 +0000 Subject: [PATCH 040/568] 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 d3b1dfb40504..22c9a547de79 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: DoutorWhite - changes: - - message: Prevents rendering from crashing in certain scenarios - type: Fix - id: 6117 - time: '2024-03-10T17:07:24.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25960 - author: nikthechampiongr changes: - message: Shields will no longer absorb asphyxiation damage, or any other damage @@ -3871,3 +3864,10 @@ id: 6616 time: '2024-05-25T20:03:05.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28265 +- author: deltanedas + changes: + - message: Fixed ID Cards not updating the manifest when changed. + type: Fix + id: 6617 + time: '2024-05-25T20:08:16.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28237 From f1c70d69f2669cac0bdbca758a9da3e7c3608cbc Mon Sep 17 00:00:00 2001 From: Vasilis Date: Sat, 25 May 2024 23:09:52 +0300 Subject: [PATCH 041/568] Remove the network tab (#28231) It is useless and bloat, if a user needs to change these settings they are free to modify their cvars manually via the clientconfig.toml file or via the cvar command. --- Content.Client/Options/UI/OptionsMenu.xaml | 1 - Content.Client/Options/UI/OptionsMenu.xaml.cs | 1 - .../Options/UI/Tabs/NetworkTab.xaml | 102 -------------- .../Options/UI/Tabs/NetworkTab.xaml.cs | 125 ------------------ 4 files changed, 229 deletions(-) delete mode 100644 Content.Client/Options/UI/Tabs/NetworkTab.xaml delete mode 100644 Content.Client/Options/UI/Tabs/NetworkTab.xaml.cs diff --git a/Content.Client/Options/UI/OptionsMenu.xaml b/Content.Client/Options/UI/OptionsMenu.xaml index ab3b88ca4e65..4f624c1bb69a 100644 --- a/Content.Client/Options/UI/OptionsMenu.xaml +++ b/Content.Client/Options/UI/OptionsMenu.xaml @@ -7,6 +7,5 @@ - diff --git a/Content.Client/Options/UI/OptionsMenu.xaml.cs b/Content.Client/Options/UI/OptionsMenu.xaml.cs index c3a8e664705c..35a3f751bbfe 100644 --- a/Content.Client/Options/UI/OptionsMenu.xaml.cs +++ b/Content.Client/Options/UI/OptionsMenu.xaml.cs @@ -19,7 +19,6 @@ public OptionsMenu() Tabs.SetTabTitle(1, Loc.GetString("ui-options-tab-graphics")); Tabs.SetTabTitle(2, Loc.GetString("ui-options-tab-controls")); Tabs.SetTabTitle(3, Loc.GetString("ui-options-tab-audio")); - Tabs.SetTabTitle(4, Loc.GetString("ui-options-tab-network")); UpdateTabs(); } diff --git a/Content.Client/Options/UI/Tabs/NetworkTab.xaml b/Content.Client/Options/UI/Tabs/NetworkTab.xaml deleted file mode 100644 index d010f0bd3147..000000000000 --- a/Content.Client/Options/UI/Tabs/NetworkTab.xaml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - [RegisterComponent, Access(typeof(ThiefRuleSystem))] -public sealed partial class ThiefRuleComponent : Component -{ - [DataField] - public ProtoId BigObjectiveGroup = "ThiefBigObjectiveGroups"; - - [DataField] - public ProtoId SmallObjectiveGroup = "ThiefObjectiveGroups"; - - [DataField] - public ProtoId EscapeObjectiveGroup = "ThiefEscapeObjectiveGroups"; - - [DataField] - public float BigObjectiveChance = 0.7f; - - [DataField] - public float MaxObjectiveDifficulty = 2.5f; - - [DataField] - public int MaxStealObjectives = 10; -} +public sealed partial class ThiefRuleComponent : Component; diff --git a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs index b85019d8afa6..62f92963aa7b 100644 --- a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs @@ -22,9 +22,6 @@ public sealed partial class TraitorRuleComponent : Component [DataField] public ProtoId SyndicateFaction = "Syndicate"; - [DataField] - public ProtoId ObjectiveGroup = "TraitorObjectiveGroups"; - [DataField] public ProtoId CodewordAdjectives = "adjectives"; @@ -72,7 +69,4 @@ public enum SelectionState /// [DataField] public int StartingBalance = 20; - - [DataField] - public int MaxDifficulty = 5; } diff --git a/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs b/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs index 81bdda706bdf..0367aa1460ce 100644 --- a/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs @@ -1,6 +1,8 @@ using Content.Server.GameTicking.Rules.Components; using Content.Server.Objectives; +using Content.Shared.Mind; using System.Diagnostics.CodeAnalysis; +using System.Linq; namespace Content.Server.GameTicking.Rules; @@ -47,7 +49,8 @@ public bool StartRule(string rule, EntityUid mindId, [NotNullWhen(true)] out Ent private void OnObjectivesTextGetInfo(EntityUid uid, GenericAntagRuleComponent comp, ref ObjectivesTextGetInfoEvent args) { - args.Minds = comp.Minds; + // just temporary until this is deleted + args.Minds = comp.Minds.Select(mindId => (mindId, Comp(mindId).CharacterName ?? "?")).ToList(); args.AgentName = Loc.GetString(comp.AgentName); } } diff --git a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs index 083085fa0d8e..faec4a9e9ca2 100644 --- a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs @@ -24,7 +24,6 @@ public override void Initialize() SubscribeLocalEvent(AfterAntagSelected); SubscribeLocalEvent(OnGetBriefing); - SubscribeLocalEvent(OnObjectivesTextGetInfo); } private void AfterAntagSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) @@ -33,41 +32,9 @@ private void AfterAntagSelected(Entity ent, ref AfterAntagEn return; //Generate objectives - GenerateObjectives(mindId, mind, ent); _antag.SendBriefing(args.EntityUid, MakeBriefing(args.EntityUid), null, null); } - private void GenerateObjectives(EntityUid mindId, MindComponent mind, ThiefRuleComponent thiefRule) - { - // Give thieves their objectives - var difficulty = 0f; - - if (_random.Prob(thiefRule.BigObjectiveChance)) // 70% chance to 1 big objective (structure or animal) - { - var objective = _objectives.GetRandomObjective(mindId, mind, thiefRule.BigObjectiveGroup); - if (objective != null) - { - _mindSystem.AddObjective(mindId, mind, objective.Value); - difficulty += Comp(objective.Value).Difficulty; - } - } - - for (var i = 0; i < thiefRule.MaxStealObjectives && thiefRule.MaxObjectiveDifficulty > difficulty; i++) // Many small objectives - { - var objective = _objectives.GetRandomObjective(mindId, mind, thiefRule.SmallObjectiveGroup); - if (objective == null) - continue; - - _mindSystem.AddObjective(mindId, mind, objective.Value); - difficulty += Comp(objective.Value).Difficulty; - } - - //Escape target - var escapeObjective = _objectives.GetRandomObjective(mindId, mind, thiefRule.EscapeObjectiveGroup); - if (escapeObjective != null) - _mindSystem.AddObjective(mindId, mind, escapeObjective.Value); - } - //Add mind briefing private void OnGetBriefing(Entity thief, ref GetBriefingEvent args) { @@ -87,10 +54,4 @@ private string MakeBriefing(EntityUid thief) briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n"; return briefing; } - - private void OnObjectivesTextGetInfo(Entity ent, ref ObjectivesTextGetInfoEvent args) - { - args.Minds = _antag.GetAntagMindEntityUids(ent.Owner); - args.AgentName = Loc.GetString("thief-round-end-agent-name"); - } } diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 1c894a460cca..29de85a42e50 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -31,15 +31,12 @@ public sealed class TraitorRuleSystem : GameRuleSystem [Dependency] private readonly SharedJobSystem _jobs = default!; [Dependency] private readonly ObjectivesSystem _objectives = default!; - public const int MaxPicks = 20; - public override void Initialize() { base.Initialize(); SubscribeLocalEvent(AfterEntitySelected); - SubscribeLocalEvent(OnObjectivesTextGetInfo); SubscribeLocalEvent(OnObjectivesTextPrepend); } @@ -67,7 +64,7 @@ private void MakeCodewords(TraitorRuleComponent component) } } - public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true) + public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true) { //Grab the mind if it wasnt provided if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind)) @@ -112,37 +109,16 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool _npcFaction.RemoveFaction(traitor, component.NanoTrasenFaction, false); _npcFaction.AddFaction(traitor, component.SyndicateFaction); - // Give traitors their objectives - if (giveObjectives) - { - var difficulty = 0f; - for (var pick = 0; pick < MaxPicks && component.MaxDifficulty > difficulty; pick++) - { - var objective = _objectives.GetRandomObjective(mindId, mind, component.ObjectiveGroup); - if (objective == null) - continue; - - _mindSystem.AddObjective(mindId, mind, objective.Value); - var adding = Comp(objective.Value).Difficulty; - difficulty += adding; - Log.Debug($"Added objective {ToPrettyString(objective):objective} with {adding} difficulty"); - } - } - return true; } - private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args) - { - args.Minds = _antag.GetAntagMindEntityUids(uid); - args.AgentName = Loc.GetString("traitor-round-end-agent-name"); - } - + // TODO: AntagCodewordsComponent private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args) { args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords))); } + // TODO: figure out how to handle this? add priority to briefing event? private string GenerateBriefing(string[] codewords, Note[]? uplinkCode, string? objectiveIssuer = null) { var sb = new StringBuilder(); diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index 47fe4eb5f889..f8ecc22828e5 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -36,14 +36,14 @@ public override void Initialize() private void OnRoundEndText(RoundEndTextAppendEvent ev) { // go through each gamerule getting data for the roundend summary. - var summaries = new Dictionary>>(); + var summaries = new Dictionary>>(); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var gameRule)) { if (!_gameTicker.IsGameRuleAdded(uid, gameRule)) continue; - var info = new ObjectivesTextGetInfoEvent(new List(), string.Empty); + var info = new ObjectivesTextGetInfoEvent(new List<(EntityUid, string)>(), string.Empty); RaiseLocalEvent(uid, ref info); if (info.Minds.Count == 0) continue; @@ -51,7 +51,7 @@ private void OnRoundEndText(RoundEndTextAppendEvent ev) // first group the gamerules by their agents, for example 2 different dragons var agent = info.AgentName; if (!summaries.ContainsKey(agent)) - summaries[agent] = new Dictionary>(); + summaries[agent] = new Dictionary>(); var prepend = new ObjectivesTextPrependEvent(""); RaiseLocalEvent(uid, ref prepend); @@ -79,7 +79,7 @@ private void OnRoundEndText(RoundEndTextAppendEvent ev) foreach (var (_, minds) in summary) { total += minds.Count; - totalInCustody += minds.Where(m => IsInCustody(m)).Count(); + totalInCustody += minds.Where(pair => IsInCustody(pair.Item1)).Count(); } var result = new StringBuilder(); @@ -104,19 +104,16 @@ private void OnRoundEndText(RoundEndTextAppendEvent ev) } } - private void AddSummary(StringBuilder result, string agent, List minds) + private void AddSummary(StringBuilder result, string agent, List<(EntityUid, string)> minds) { var agentSummaries = new List<(string summary, float successRate, int completedObjectives)>(); - foreach (var mindId in minds) + foreach (var (mindId, name) in minds) { - if (!TryComp(mindId, out MindComponent? mind)) - continue; - - var title = GetTitle(mindId, mind); - if (title == null) + if (!TryComp(mindId, out var mind)) continue; + var title = GetTitle((mindId, mind), name); var custody = IsInCustody(mindId, mind) ? Loc.GetString("objectives-in-custody") : string.Empty; var objectives = mind.Objectives; @@ -238,34 +235,18 @@ private bool IsInCustody(EntityUid mindId, MindComponent? mind = null) /// /// Get the title for a player's mind used in round end. + /// Pass in the original entity name which is shown alongside username. /// - public string? GetTitle(EntityUid mindId, MindComponent? mind = null) + public string GetTitle(Entity mind, string name) { - if (!Resolve(mindId, ref mind)) - return null; - - var name = mind.CharacterName; - var username = (string?) null; - - if (mind.OriginalOwnerUserId != null && - _player.TryGetPlayerData(mind.OriginalOwnerUserId.Value, out var sessionData)) + if (Resolve(mind, ref mind.Comp) && + mind.Comp.OriginalOwnerUserId != null && + _player.TryGetPlayerData(mind.Comp.OriginalOwnerUserId.Value, out var sessionData)) { - username = sessionData.UserName; + var username = sessionData.UserName; + return Loc.GetString("objectives-player-user-named", ("user", username), ("name", name)); } - - if (username != null) - { - if (name != null) - return Loc.GetString("objectives-player-user-named", ("user", username), ("name", name)); - - return Loc.GetString("objectives-player-user", ("user", username)); - } - - // nothing to identify the player by, just give up - if (name == null) - return null; - return Loc.GetString("objectives-player-named", ("name", name)); } } @@ -279,7 +260,7 @@ private bool IsInCustody(EntityUid mindId, MindComponent? mind = null) /// The objectives system already checks if the game rule is added so you don't need to check that in this event's handler. /// [ByRefEvent] -public record struct ObjectivesTextGetInfoEvent(List Minds, string AgentName); +public record struct ObjectivesTextGetInfoEvent(List<(EntityUid, string)> Minds, string AgentName); /// /// Raised on the game rule before text for each agent's objectives is added, letting you prepend something. diff --git a/Resources/Locale/en-US/objectives/round-end.ftl b/Resources/Locale/en-US/objectives/round-end.ftl index b4314b2caff9..3da81fc96404 100644 --- a/Resources/Locale/en-US/objectives/round-end.ftl +++ b/Resources/Locale/en-US/objectives/round-end.ftl @@ -6,7 +6,6 @@ objectives-round-end-result = {$count -> objectives-round-end-result-in-custody = {$custody} out of {$count} {MAKEPLURAL($agent)} were in custody. objectives-player-user-named = [color=White]{$name}[/color] ([color=gray]{$user}[/color]) -objectives-player-user = [color=gray]{$user}[/color] objectives-player-named = [color=White]{$name}[/color] objectives-no-objectives = {$custody}{$title} was a {$agent}. diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 3b9ad5aadf2c..593122eee558 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -428,9 +428,9 @@ prototype: Nukeops - type: entity - id: SleeperAgentsRule - parent: BaseGameRule noSpawn: true + parent: BaseTraitorRule + id: SleeperAgentsRule components: - type: StationEvent earliestStart: 30 @@ -441,7 +441,6 @@ startAudio: path: /Audio/Announcements/intercept.ogg - type: AlertLevelInterceptionRule - - type: TraitorRule - type: AntagSelection definitions: - prefRoles: [ Traitor ] diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index 5a4cde31018b..1b58ada64855 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -35,7 +35,19 @@ id: Thief components: - type: ThiefRule + - type: AntagObjectives + objectives: + - EscapeThiefShuttleObjective + - type: AntagRandomObjectives + sets: + - groups: ThiefBigObjectiveGroups + prob: 0.7 + maxPicks: 1 + - groups: ThiefObjectiveGroups + maxPicks: 10 + maxDifficulty: 2.5 - type: AntagSelection + agentName: thief-round-end-agent-name definitions: - prefRoles: [ Thief ] maxRange: @@ -53,14 +65,3 @@ prototype: Thief briefing: sound: "/Audio/Misc/thief_greeting.ogg" - -#- type: entity -# noSpawn: true -# parent: BaseGameRule -# id: Exterminator -# components: -# - type: GenericAntagRule -# agentName: terminator-round-end-agent-name -# objectives: -# - TerminateObjective -# - ShutDownObjective diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index d87e7ccbe76d..be5f617c8dc8 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -134,16 +134,30 @@ prototype: Nukeops - type: entity - id: Traitor + abstract: true parent: BaseGameRule + id: BaseTraitorRule + components: + - type: TraitorRule + # TODO: codewords in yml + # TODO: uplink in yml + - type: AntagRandomObjectives + sets: + - groups: TraitorObjectiveGroups + maxDifficulty: 5 + - type: AntagSelection + agentName: traitor-round-end-agent-name + +- type: entity noSpawn: true + parent: BaseTraitorRule + id: Traitor components: - type: GameRule minPlayers: 5 delay: min: 240 max: 420 - - type: TraitorRule - type: AntagSelection definitions: - prefRoles: [ Traitor ] diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index ff126eb5d162..bb74c92da3b5 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -55,13 +55,6 @@ ThiefObjectiveGroupStructure: 0 #Temporarily disabled until obvious ways to steal structures are added ThiefObjectiveGroupAnimal: 2 -- type: weightedRandom - id: ThiefEscapeObjectiveGroups - weights: - ThiefObjectiveGroupEscape: 1 - - - - type: weightedRandom id: ThiefObjectiveGroupCollection weights: From 739c81c230ad4ae103b9a3b6a886cd7a46830361 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Sat, 25 May 2024 20:15:56 +0000 Subject: [PATCH 043/568] move nukie profile loading into its own rule (#28208) * move profile loading out of nukeops rule * make BaseNukeopsRule and use AntagLoadProfileRule * untroll --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Rules/AntagLoadProfileRuleSystem.cs | 39 +++++++++++++++++++ .../AntagLoadProfileRuleCOmponent.cs | 7 ++++ .../GameTicking/Rules/NukeopsRuleSystem.cs | 28 ------------- Resources/Prototypes/GameRules/events.yml | 4 +- Resources/Prototypes/GameRules/roundstart.yml | 16 ++++++-- 5 files changed, 60 insertions(+), 34 deletions(-) create mode 100644 Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs create mode 100644 Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs diff --git a/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs b/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs new file mode 100644 index 000000000000..fd3fb6cd655e --- /dev/null +++ b/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs @@ -0,0 +1,39 @@ +using Content.Server.Antag; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Humanoid; +using Content.Server.Preferences.Managers; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Preferences; +using Robust.Shared.Prototypes; + +namespace Content.Server.GameTicking.Rules; + +public sealed class AntagLoadProfileRuleSystem : GameRuleSystem +{ + [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IServerPreferencesManager _prefs = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSelectEntity); + } + + private void OnSelectEntity(Entity ent, ref AntagSelectEntityEvent args) + { + if (args.Handled) + return; + + var profile = args.Session != null + ? _prefs.GetPreferences(args.Session.UserId).SelectedCharacter as HumanoidCharacterProfile + : HumanoidCharacterProfile.RandomWithSpecies(); + if (profile?.Species is not {} speciesId || !_proto.TryIndex(speciesId, out var species)) + species = _proto.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); + + args.Entity = Spawn(species.Prototype); + _humanoid.LoadProfile(args.Entity.Value, profile); + } +} diff --git a/Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs b/Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs new file mode 100644 index 000000000000..5e58fd14fc04 --- /dev/null +++ b/Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server.GameTicking.Rules.Components; + +/// +/// Makes this rules antags spawn a humanoid, either from the player's profile or a random one. +/// +[RegisterComponent] +public sealed partial class AntagLoadProfileRuleComponent : Component; diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 232d24004b90..d6f1c3c619ab 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -1,11 +1,9 @@ using Content.Server.Antag; using Content.Server.Communications; using Content.Server.GameTicking.Rules.Components; -using Content.Server.Humanoid; using Content.Server.Nuke; using Content.Server.NukeOps; using Content.Server.Popups; -using Content.Server.Preferences.Managers; using Content.Server.Roles; using Content.Server.RoundEnd; using Content.Server.Shuttles.Events; @@ -13,20 +11,16 @@ using Content.Server.Station.Components; using Content.Server.Store.Components; using Content.Server.Store.Systems; -using Content.Shared.Humanoid; -using Content.Shared.Humanoid.Prototypes; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.NPC.Components; using Content.Shared.NPC.Systems; using Content.Shared.Nuke; using Content.Shared.NukeOps; -using Content.Shared.Preferences; using Content.Shared.Store; using Content.Shared.Tag; using Content.Shared.Zombies; using Robust.Shared.Map; -using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; using System.Linq; @@ -36,10 +30,7 @@ namespace Content.Server.GameTicking.Rules; public sealed class NukeopsRuleSystem : GameRuleSystem { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IServerPreferencesManager _prefs = default!; [Dependency] private readonly EmergencyShuttleSystem _emergency = default!; - [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; @@ -71,7 +62,6 @@ public override void Initialize() SubscribeLocalEvent(OnWarDeclared); SubscribeLocalEvent(OnShuttleCallAttempt); - SubscribeLocalEvent(OnAntagSelectEntity); SubscribeLocalEvent(OnAfterAntagEntSelected); } @@ -471,24 +461,6 @@ private void CheckRoundShouldEnd(Entity ent) nukeops.RoundEndBehavior = RoundEndBehavior.Nothing; } - // this should really go anywhere else but im tired. - private void OnAntagSelectEntity(Entity ent, ref AntagSelectEntityEvent args) - { - if (args.Handled) - return; - - var profile = args.Session != null - ? _prefs.GetPreferences(args.Session.UserId).SelectedCharacter as HumanoidCharacterProfile - : HumanoidCharacterProfile.RandomWithSpecies(); - if (!_prototypeManager.TryIndex(profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies, out SpeciesPrototype? species)) - { - species = _prototypeManager.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); - } - - args.Entity = Spawn(species.Prototype); - _humanoid.LoadProfile(args.Entity.Value, profile); - } - private void OnAfterAntagEntSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) { if (ent.Comp.TargetStation is not { } station) diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 593122eee558..b1b48258bade 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -394,9 +394,9 @@ prototype: InitialInfected - type: entity - id: LoneOpsSpawn - parent: BaseGameRule noSpawn: true + parent: BaseNukeopsRule + id: LoneOpsSpawn components: - type: StationEvent earliestStart: 35 diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index be5f617c8dc8..57e59f10e213 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -64,17 +64,25 @@ roundEndDelay: 10 - type: entity - id: Nukeops + abstract: true parent: BaseGameRule - noSpawn: true + id: BaseNukeopsRule components: - - type: GameRule - minPlayers: 20 - type: RandomMetadata #this generates the random operation name cuz it's cool. nameSegments: - operationPrefix - operationSuffix - type: NukeopsRule + - type: AntagSelection + - type: AntagLoadProfileRule + +- type: entity + noSpawn: true + parent: BaseNukeopsRule + id: Nukeops + components: + - type: GameRule + minPlayers: 20 - type: LoadMapRule gameMap: NukieOutpost - type: AntagSelection From 90d3699c1df2d0de21e887c893b9f17aaebcfced Mon Sep 17 00:00:00 2001 From: Repo <47093363+Titian3@users.noreply.github.com> Date: Sun, 26 May 2024 08:18:05 +1200 Subject: [PATCH 044/568] Fix gamerule display issues (#28178) * A comprehensive rule list for joining admins and mid round command to get rule list added * Fix up for when a rule is added vs started and some logging * fix command help localization, fix admin flags and spam anouncement. * Send admin message only to the joining player not all admins. * Bit better formatting in chat box --- Content.Server/Chat/Managers/ChatManager.cs | 8 ++ Content.Server/Chat/Managers/IChatManager.cs | 1 + .../GameTicking/GameTicker.GameRule.cs | 74 ++++++++++++++++++- .../GameTicking/GameTicker.Player.cs | 11 +++ .../GameTicking/Rules/SecretRuleSystem.cs | 1 - .../game-rules/gamerule-admin.ftl | 6 ++ .../game-ticking/game-rules/rule-secret.ftl | 2 - 7 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 Resources/Locale/en-US/game-ticking/game-rules/gamerule-admin.ftl delete mode 100644 Resources/Locale/en-US/game-ticking/game-rules/rule-secret.ftl diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index 812aed80bd76..79683db64117 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -150,6 +150,14 @@ public void SendAdminAnnouncement(string message, AdminFlags? flagBlacklist, Adm _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Admin announcement: {message}"); } + public void SendAdminAnnouncementMessage(ICommonSession player, string message, bool suppressLog = true) + { + var wrappedMessage = Loc.GetString("chat-manager-send-admin-announcement-wrap-message", + ("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")), + ("message", FormattedMessage.EscapeText(message))); + ChatMessageToOne(ChatChannel.Admin, message, wrappedMessage, default, false, player.Channel); + } + public void SendAdminAlert(string message) { var clients = _adminManager.ActiveAdmins.Select(p => p.Channel); diff --git a/Content.Server/Chat/Managers/IChatManager.cs b/Content.Server/Chat/Managers/IChatManager.cs index 59945bf5ca6f..c8c057a1ad76 100644 --- a/Content.Server/Chat/Managers/IChatManager.cs +++ b/Content.Server/Chat/Managers/IChatManager.cs @@ -23,6 +23,7 @@ public interface IChatManager void SendHookOOC(string sender, string message); void SendAdminAnnouncement(string message, AdminFlags? flagBlacklist = null, AdminFlags? flagWhitelist = null); + void SendAdminAnnouncementMessage(ICommonSession player, string message, bool suppressLog = true); void SendAdminAlert(string message); void SendAdminAlert(EntityUid player, string message); diff --git a/Content.Server/GameTicking/GameTicker.GameRule.cs b/Content.Server/GameTicking/GameTicker.GameRule.cs index f52a3cb296d2..a6d0a4baf0a9 100644 --- a/Content.Server/GameTicking/GameTicker.GameRule.cs +++ b/Content.Server/GameTicking/GameTicker.GameRule.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Server.Administration; using Content.Server.GameTicking.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Shared.Administration; using Content.Shared.Database; using Content.Shared.Prototypes; @@ -42,6 +43,14 @@ private void InitializeGameRules() string.Empty, "cleargamerules", ClearGameRulesCommand); + + // List game rules command. + var localizedHelp = Loc.GetString("listgamerules-command-help"); + + _consoleHost.RegisterCommand("listgamerules", + string.Empty, + $"listgamerules - {localizedHelp}", + ListGameRuleCommand); } private void ShutdownGameRules() @@ -49,6 +58,7 @@ private void ShutdownGameRules() _consoleHost.UnregisterCommand("addgamerule"); _consoleHost.UnregisterCommand("endgamerule"); _consoleHost.UnregisterCommand("cleargamerules"); + _consoleHost.UnregisterCommand("listgamerules"); } /// @@ -64,6 +74,13 @@ public EntityUid AddGameRule(string ruleId) var ev = new GameRuleAddedEvent(ruleEntity, ruleId); RaiseLocalEvent(ruleEntity, ref ev, true); + + var currentTime = RunLevel == GameRunLevel.PreRoundLobby ? TimeSpan.Zero : RoundDuration(); + if (!HasComp(ruleEntity) && !HasComp(ruleEntity)) + { + _allPreviousGameRules.Add((currentTime, ruleId + " (Pending)")); + } + return ruleEntity; } @@ -110,7 +127,8 @@ public bool StartGameRule(EntityUid ruleEntity, GameRuleComponent? ruleData = nu if (delayTime > TimeSpan.Zero) { _sawmill.Info($"Queued start for game rule {ToPrettyString(ruleEntity)} with delay {delayTime}"); - _adminLogger.Add(LogType.EventStarted, $"Queued start for game rule {ToPrettyString(ruleEntity)} with delay {delayTime}"); + _adminLogger.Add(LogType.EventStarted, + $"Queued start for game rule {ToPrettyString(ruleEntity)} with delay {delayTime}"); var delayed = EnsureComp(ruleEntity); delayed.RuleStartTime = _gameTiming.CurTime + (delayTime); @@ -118,7 +136,20 @@ public bool StartGameRule(EntityUid ruleEntity, GameRuleComponent? ruleData = nu } } - _allPreviousGameRules.Add((RoundDuration(), id)); + var currentTime = RunLevel == GameRunLevel.PreRoundLobby ? TimeSpan.Zero : RoundDuration(); + + // Remove the first occurrence of the pending entry before adding the started entry + var pendingRuleIndex = _allPreviousGameRules.FindIndex(rule => rule.Item2 == id + " (Pending)"); + if (pendingRuleIndex >= 0) + { + _allPreviousGameRules.RemoveAt(pendingRuleIndex); + } + + if (!HasComp(ruleEntity) && !HasComp(ruleEntity)) + { + _allPreviousGameRules.Add((currentTime, id)); + } + _sawmill.Info($"Started game rule {ToPrettyString(ruleEntity)}"); _adminLogger.Add(LogType.EventStarted, $"Started game rule {ToPrettyString(ruleEntity)}"); @@ -296,6 +327,7 @@ private void AddGameRuleCommand(IConsoleShell shell, string argstr, string[] arg if (shell.Player != null) { _adminLogger.Add(LogType.EventStarted, $"{shell.Player} tried to add game rule [{rule}] via command"); + _chatManager.SendAdminAnnouncement(Loc.GetString("add-gamerule-admin", ("rule", rule), ("admin", shell.Player))); } else { @@ -306,6 +338,7 @@ private void AddGameRuleCommand(IConsoleShell shell, string argstr, string[] arg // Start rule if we're already in the middle of a round if(RunLevel == GameRunLevel.InRound) StartGameRule(ent); + } } @@ -349,5 +382,42 @@ private void ClearGameRulesCommand(IConsoleShell shell, string argstr, string[] ClearGameRules(); } + [AdminCommand(AdminFlags.Admin)] + private void ListGameRuleCommand(IConsoleShell shell, string argstr, string[] args) + { + _sawmill.Info($"{shell.Player} tried to get list of game rules via command"); + _adminLogger.Add(LogType.Action, $"{shell.Player} tried to get list of game rules via command"); + var message = GetGameRulesListMessage(false); + shell.WriteLine(message); + } + private string GetGameRulesListMessage(bool forChatWindow) + { + if (_allPreviousGameRules.Count > 0) + { + var sortedRules = _allPreviousGameRules.OrderBy(rule => rule.Item1).ToList(); + var message = "\n"; + + if (!forChatWindow) + { + var header = Loc.GetString("list-gamerule-admin-header"); + message += $"\n{header}\n"; + message += "|------------|------------------\n"; + } + + foreach (var (time, rule) in sortedRules) + { + var formattedTime = time.ToString(@"hh\:mm\:ss"); + message += $"| {formattedTime,-10} | {rule,-16} \n"; + } + + return message; + } + else + { + return Loc.GetString("list-gamerule-admin-no-rules"); + + } + } + #endregion } diff --git a/Content.Server/GameTicking/GameTicker.Player.cs b/Content.Server/GameTicking/GameTicker.Player.cs index c1388f629036..61cdf6f8553f 100644 --- a/Content.Server/GameTicking/GameTicker.Player.cs +++ b/Content.Server/GameTicking/GameTicker.Player.cs @@ -1,4 +1,6 @@ +using System.Linq; using Content.Server.Database; +using Content.Shared.Administration; using Content.Shared.CCVar; using Content.Shared.GameTicking; using Content.Shared.GameWindow; @@ -196,6 +198,15 @@ public void PlayerJoinGame(ICommonSession session, bool silent = false) _playerGameStatuses[session.UserId] = PlayerGameStatus.JoinedGame; _db.AddRoundPlayers(RoundId, session.UserId); + if (_adminManager.HasAdminFlag(session, AdminFlags.Admin)) + { + if (_allPreviousGameRules.Count > 0) + { + var rulesMessage = GetGameRulesListMessage(true); + _chatManager.SendAdminAnnouncementMessage(session, Loc.GetString("starting-rule-selected-preset", ("preset", rulesMessage))); + } + } + RaiseNetworkEvent(new TickerJoinGameEvent(), session.Channel); } diff --git a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs index 95bf5986a5a6..d25262b797af 100644 --- a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs @@ -46,7 +46,6 @@ protected override void Added(EntityUid uid, SecretRuleComponent component, Game Log.Info($"Selected {preset.ID} as the secret preset."); _adminLogger.Add(LogType.EventStarted, $"Selected {preset.ID} as the secret preset."); - _chatManager.SendAdminAnnouncement(Loc.GetString("rule-secret-selected-preset", ("preset", preset.ID))); foreach (var rule in preset.Rules) { diff --git a/Resources/Locale/en-US/game-ticking/game-rules/gamerule-admin.ftl b/Resources/Locale/en-US/game-ticking/game-rules/gamerule-admin.ftl new file mode 100644 index 000000000000..3b31fe466307 --- /dev/null +++ b/Resources/Locale/en-US/game-ticking/game-rules/gamerule-admin.ftl @@ -0,0 +1,6 @@ +#When an admin adds a game rule +add-gamerule-admin = Game rule({$rule}) added - {$admin} +list-gamerule-admin-header = | Time | Rule added +list-gamerule-admin-no-rules = No game rules have been added. +starting-rule-selected-preset = Current gamerules in use: {$preset} +listgamerules-command-help = Lists all rules that have been added for the round so far. diff --git a/Resources/Locale/en-US/game-ticking/game-rules/rule-secret.ftl b/Resources/Locale/en-US/game-ticking/game-rules/rule-secret.ftl deleted file mode 100644 index c38220cca1dd..000000000000 --- a/Resources/Locale/en-US/game-ticking/game-rules/rule-secret.ftl +++ /dev/null @@ -1,2 +0,0 @@ -# Sent to admin chat -rule-secret-selected-preset = Selected {$preset} for secret. From 7c9e5a0779e7d44f183575b1fce6ab95dc0c8996 Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Sat, 25 May 2024 23:18:40 +0300 Subject: [PATCH 045/568] Tomato killers don't kill the server anymore. (#28173) * tomato killer auto death * fix * Update miscellaneous.yml --- .../Entities/Mobs/NPCs/miscellaneous.yml | 27 ++++++++++++++++--- .../Entities/Objects/Consumable/Food/meat.yml | 4 +++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml b/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml index f77429d5978d..e09d3917c751 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml @@ -114,13 +114,22 @@ - trigger: !type:DamageTypeTrigger damageType: Blunt - damage: 100 + damage: 40 behaviors: - - !type:GibBehavior { } + - !type:SpawnEntitiesBehavior + spawn: + FoodMeatTomato: + min: 1 + max: 2 + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: gib - type: MobThresholds thresholds: 0: Alive - 24: Dead + 35: Dead - type: Fixtures fixtures: fix1: @@ -136,7 +145,7 @@ hidden: true damage: groups: - Brute: 4 + Brute: 9 animation: WeaponArcBite - type: Climbing - type: NameIdentifier @@ -156,3 +165,13 @@ - type: Appearance - type: Produce seedId: killerTomato + - type: PassiveDamage # Slight passive damage. 35 hp \ 5 min \ 60 sec = 0.08 + allowedStates: + - Alive + - Dead + damageCap: 50 + damage: + types: + Blunt: 0.11 + - type: StaticPrice + price: 400 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml index 81c98750dc26..bb0f05c1814f 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml @@ -599,6 +599,8 @@ - type: SliceableFood count: 3 slice: FoodMeatTomatoCutlet + - type: StaticPrice + price: 100 - type: entity name: salami @@ -1270,6 +1272,8 @@ - type: Sprite state: salami-slice color: red + - type: StaticPrice + price: 30 - type: entity name: salami slice From bc5b92b8154669194f1973f3564a336e5deaa35c Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 25 May 2024 20:19:46 +0000 Subject: [PATCH 046/568] Automatic changelog update --- Resources/Changelog/Admin.yml | 11 +++++++++++ Resources/Changelog/Changelog.yml | 20 ++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index f0bb5671028a..879eaff2329f 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -235,5 +235,16 @@ Entries: id: 29 time: '2024-05-19T23:04:16.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28107 +- author: Repo + changes: + - message: Added listgamerules command, lists all run game modes for the round. + type: Add + - message: Added admin anouncement for addgamerule + type: Add + - message: Gamemode now shows after starting round. + type: Fix + id: 30 + time: '2024-05-25T20:18:05.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28178 Name: Admin Order: 1 diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 22c9a547de79..1a290ea8c399 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: nikthechampiongr - changes: - - message: Shields will no longer absorb asphyxiation damage, or any other damage - they themselves can't take. - type: Fix - id: 6118 - time: '2024-03-11T01:55:19.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25972 - author: metalgearsloth changes: - message: Remove the buttons for generated debris from shuttle maps. @@ -3871,3 +3863,15 @@ id: 6617 time: '2024-05-25T20:08:16.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28237 +- author: TheShuEd + changes: + - message: 'Killer tomatoes got a small health and damage buff: 24 -> 35 hp and + 4->9 brute damage' + type: Tweak + - message: Killer tomatoes now die after 5 minutes of their existence + type: Tweak + - message: Killer tomatoes can now be profitably sold. + type: Add + id: 6618 + time: '2024-05-25T20:18:40.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28173 From de8b6631245cc6a7a3641f43b68c61e077a06bf8 Mon Sep 17 00:00:00 2001 From: MilenVolf <63782763+MilenVolf@users.noreply.github.com> Date: Sat, 25 May 2024 23:20:43 +0300 Subject: [PATCH 047/568] Add direction relative to station for emergency shuttle's docking & nearby announcement (#28164) * Use nav beacon location for emergency shuttle's docking announcement Location of the shuttle relative to the nearest nav beacon in docking announcement message of the emergency shuttle * Add directions relative to station --- .../Shuttles/Systems/EmergencyShuttleSystem.cs | 11 ++++++++--- Resources/Locale/en-US/shuttles/emergency.ftl | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index 2d8ae4b735e5..e2b1ad32cd7a 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -287,7 +287,8 @@ public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleCo if (TryComp(targetGrid.Value, out TransformComponent? targetXform)) { var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery); - _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir())), playDefaultSound: false); + var location = FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((stationShuttle.EmergencyShuttle.Value, xform))); + _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir()), ("location", location)), playDefaultSound: false); } // shuttle timers @@ -313,8 +314,12 @@ public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleCo } else { - var location = FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((stationShuttle.EmergencyShuttle.Value, xform))); - _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("direction", location)), playDefaultSound: false); + if (TryComp(targetGrid.Value, out var targetXform)) + { + var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery); + var location = FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((stationShuttle.EmergencyShuttle.Value, xform))); + _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir()), ("location", location)), playDefaultSound: false); + } _logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to find a valid docking port for {ToPrettyString(stationUid)}"); // TODO: Need filter extensions or something don't blame me. diff --git a/Resources/Locale/en-US/shuttles/emergency.ftl b/Resources/Locale/en-US/shuttles/emergency.ftl index c71629113514..2fa3a7a1240a 100644 --- a/Resources/Locale/en-US/shuttles/emergency.ftl +++ b/Resources/Locale/en-US/shuttles/emergency.ftl @@ -13,9 +13,9 @@ emergency-shuttle-command-launch-desc = Early launches the emergency shuttle if # Emergency shuttle emergency-shuttle-left = The Emergency Shuttle has left the station. Estimate {$transitTime} seconds until the shuttle arrives at CentCom. emergency-shuttle-launch-time = The emergency shuttle will launch in {$consoleAccumulator} seconds. -emergency-shuttle-docked = The Emergency Shuttle has docked with the station on the {$direction} side. It will leave in {$time} seconds. +emergency-shuttle-docked = The Emergency Shuttle has docked {$direction} of the station, {$location}. It will leave in {$time} seconds. emergency-shuttle-good-luck = The Emergency Shuttle is unable to find a station. Good luck. -emergency-shuttle-nearby = The Emergency Shuttle is unable to find a valid docking port. It has warped {$direction}. +emergency-shuttle-nearby = The Emergency Shuttle is unable to find a valid docking port. It has warped in {$direction} of the station, {$location}. # Emergency shuttle console popup / announcement emergency-shuttle-console-no-early-launches = Early launch is disabled From afb1acab10a5abc2ff954d08107c5e7866d3db8e Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Sat, 25 May 2024 23:22:34 +0300 Subject: [PATCH 048/568] Fix candles (firestack fading) (#28139) Update FlammableSystem.cs --- Content.Server/Atmos/EntitySystems/FlammableSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index e8721920dd8c..b6e26435a759 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -447,7 +447,7 @@ public override void Update(float frameTime) _damageableSystem.TryChangeDamage(uid, flammable.Damage * flammable.FireStacks * ev.Multiplier, interruptsDoAfters: false); - AdjustFireStacks(uid, flammable.FirestackFade * (flammable.Resisting ? 10f : 1f), flammable); + AdjustFireStacks(uid, flammable.FirestackFade * (flammable.Resisting ? 10f : 1f), flammable, flammable.OnFire); } else { From 93f289c7dc94e0aa280b1b8544f8b9ba575d5294 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 26 May 2024 06:23:34 +1000 Subject: [PATCH 049/568] Fix water postshader (#28130) --- .../Movement/Systems/FloorOcclusionSystem.cs | 41 +++++++++++-------- .../Components/FloorOcclusionComponent.cs | 9 ++-- .../Systems/SharedFloorOcclusionSystem.cs | 24 +++++------ 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Content.Client/Movement/Systems/FloorOcclusionSystem.cs b/Content.Client/Movement/Systems/FloorOcclusionSystem.cs index 5c75f25ca2d9..44573f8e0844 100644 --- a/Content.Client/Movement/Systems/FloorOcclusionSystem.cs +++ b/Content.Client/Movement/Systems/FloorOcclusionSystem.cs @@ -10,51 +10,56 @@ public sealed class FloorOcclusionSystem : SharedFloorOcclusionSystem { [Dependency] private readonly IPrototypeManager _proto = default!; + private EntityQuery _spriteQuery; + public override void Initialize() { base.Initialize(); + + _spriteQuery = GetEntityQuery(); + SubscribeLocalEvent(OnOcclusionStartup); + SubscribeLocalEvent(OnOcclusionShutdown); SubscribeLocalEvent(OnOcclusionAuto); } - private void OnOcclusionAuto(EntityUid uid, FloorOcclusionComponent component, ref AfterAutoHandleStateEvent args) + private void OnOcclusionAuto(Entity ent, ref AfterAutoHandleStateEvent args) { - SetEnabled(uid, component, component.Enabled); + SetShader(ent.Owner, ent.Comp.Enabled); } - private void OnOcclusionStartup(EntityUid uid, FloorOcclusionComponent component, ComponentStartup args) + private void OnOcclusionStartup(Entity ent, ref ComponentStartup args) { - if (component.Enabled && TryComp(uid, out var sprite)) - SetShader(sprite, true); + SetShader(ent.Owner, ent.Comp.Enabled); } - protected override void SetEnabled(EntityUid uid, FloorOcclusionComponent component, bool enabled) + private void OnOcclusionShutdown(Entity ent, ref ComponentShutdown args) { - if (component.Enabled == enabled) - return; - - base.SetEnabled(uid, component, enabled); - - if (!TryComp(uid, out var sprite)) - return; + SetShader(ent.Owner, false); + } - SetShader(sprite, enabled); + protected override void SetEnabled(Entity entity) + { + SetShader(entity.Owner, entity.Comp.Enabled); } - private void SetShader(SpriteComponent sprite, bool enabled) + private void SetShader(Entity sprite, bool enabled) { + if (!_spriteQuery.Resolve(sprite.Owner, ref sprite.Comp, false)) + return; + var shader = _proto.Index("HorizontalCut").Instance(); - if (sprite.PostShader is not null && sprite.PostShader != shader) + if (sprite.Comp.PostShader is not null && sprite.Comp.PostShader != shader) return; if (enabled) { - sprite.PostShader = shader; + sprite.Comp.PostShader = shader; } else { - sprite.PostShader = null; + sprite.Comp.PostShader = null; } } } diff --git a/Content.Shared/Movement/Components/FloorOcclusionComponent.cs b/Content.Shared/Movement/Components/FloorOcclusionComponent.cs index aa9a1ced5521..5d412f694a0c 100644 --- a/Content.Shared/Movement/Components/FloorOcclusionComponent.cs +++ b/Content.Shared/Movement/Components/FloorOcclusionComponent.cs @@ -8,12 +8,9 @@ namespace Content.Shared.Movement.Components; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] public sealed partial class FloorOcclusionComponent : Component { - /// - /// Is the shader currently enabled. - /// - [ViewVariables(VVAccess.ReadWrite), DataField("enabled"), AutoNetworkedField] - public bool Enabled; + [ViewVariables] + public bool Enabled => Colliding.Count > 0; - [DataField("colliding")] + [DataField, AutoNetworkedField] public List Colliding = new(); } diff --git a/Content.Shared/Movement/Systems/SharedFloorOcclusionSystem.cs b/Content.Shared/Movement/Systems/SharedFloorOcclusionSystem.cs index 9d27ea42c639..6b7023a1c64d 100644 --- a/Content.Shared/Movement/Systems/SharedFloorOcclusionSystem.cs +++ b/Content.Shared/Movement/Systems/SharedFloorOcclusionSystem.cs @@ -15,39 +15,37 @@ public override void Initialize() SubscribeLocalEvent(OnEndCollide); } - private void OnStartCollide(EntityUid uid, FloorOccluderComponent component, ref StartCollideEvent args) + private void OnStartCollide(Entity entity, ref StartCollideEvent args) { var other = args.OtherEntity; if (!TryComp(other, out var occlusion) || - occlusion.Colliding.Contains(uid)) + occlusion.Colliding.Contains(entity.Owner)) { return; } - SetEnabled(other, occlusion, true); - occlusion.Colliding.Add(uid); + occlusion.Colliding.Add(entity.Owner); + Dirty(other, occlusion); + SetEnabled((other, occlusion)); } - private void OnEndCollide(EntityUid uid, FloorOccluderComponent component, ref EndCollideEvent args) + private void OnEndCollide(Entity entity, ref EndCollideEvent args) { var other = args.OtherEntity; if (!TryComp(other, out var occlusion)) return; - occlusion.Colliding.Remove(uid); + if (!occlusion.Colliding.Remove(entity.Owner)) + return; - if (occlusion.Colliding.Count == 0) - SetEnabled(other, occlusion, false); + Dirty(other, occlusion); + SetEnabled((other, occlusion)); } - protected virtual void SetEnabled(EntityUid uid, FloorOcclusionComponent component, bool enabled) + protected virtual void SetEnabled(Entity entity) { - if (component.Enabled == enabled) - return; - component.Enabled = enabled; - Dirty(uid, component); } } From ac08f0068fec2491b6558260a286d4c8213639ad Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 25 May 2024 20:24:40 +0000 Subject: [PATCH 050/568] 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 1a290ea8c399..7880f0a8d130 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Remove the buttons for generated debris from shuttle maps. - type: Tweak - id: 6119 - time: '2024-03-11T02:11:46.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25897 - author: Errant changes: - message: Species info is now available in the Guidebook and in the character editor. @@ -3875,3 +3868,10 @@ id: 6618 time: '2024-05-25T20:18:40.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28173 +- author: metalgearsloth + changes: + - message: Fix water shader getting stuck on sometimes. + type: Fix + id: 6619 + time: '2024-05-25T20:23:34.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28130 From 229f6d697cd1234f1d187bce79f70fe098571285 Mon Sep 17 00:00:00 2001 From: eoineoineoin Date: Sat, 25 May 2024 21:26:48 +0100 Subject: [PATCH 051/568] Objects ordered through cargo system shouldn't start anchored (#28115) * Order normal space heater instead of anchored variant * Make sure ordered objects don't spawn anchored * Order space heater flatpack instead of a regular space heater * Remove obsolete TODO * Remove unnecessary name --------- Co-authored-by: Eoin Mcloughlin --- Content.Server/Cargo/Systems/CargoSystem.Orders.cs | 5 +++++ .../Prototypes/Catalog/Cargo/cargo_engineering.yml | 2 +- .../Prototypes/Catalog/Fills/Crates/engineering.yml | 10 ++++++++++ Resources/Prototypes/Catalog/Fills/Crates/engines.yml | 2 +- .../Prototypes/Entities/Objects/Devices/flatpack.yml | 9 +++++++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs index 63556d2fbd73..c519362945e6 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs @@ -20,6 +20,8 @@ namespace Content.Server.Cargo.Systems { public sealed partial class CargoSystem { + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + /// /// How much time to wait (in seconds) before increasing bank accounts balance. /// @@ -489,6 +491,9 @@ private bool FulfillOrder(CargoOrderData order, EntityCoordinates spawn, string? // Create the item itself var item = Spawn(order.ProductId, spawn); + // Ensure the item doesn't start anchored + _transformSystem.Unanchor(item, Transform(item)); + // Create a sheet of paper to write the order details on var printed = EntityManager.SpawnEntity(paperProto, spawn); if (TryComp(printed, out var paper)) diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml b/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml index 754e30f133a2..7ca6af84518d 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml @@ -133,7 +133,7 @@ icon: sprite: Structures/Piping/Atmospherics/Portable/portable_sheater.rsi state: sheaterOff - product: SpaceHeaterAnchored + product: CrateEngineeringSpaceHeater cost: 300 category: cargoproduct-category-name-engineering group: market diff --git a/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml b/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml index 03c870fa5808..26a8910c735e 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml @@ -194,3 +194,13 @@ contents: - id: WeaponParticleDecelerator amount: 3 + +- type: entity + id: CrateEngineeringSpaceHeater + parent: CrateEngineering + name: space heater crate + description: Contains a space heater for climate control. + components: + - type: StorageFill + contents: + - id: SpaceHeaterFlatpack diff --git a/Resources/Prototypes/Catalog/Fills/Crates/engines.yml b/Resources/Prototypes/Catalog/Fills/Crates/engines.yml index 79698b550a75..c37b7b7535a0 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: EmitterFlatpack # TODO change to flatpack + - id: EmitterFlatpack - type: entity id: CrateEngineeringSingularityCollector diff --git a/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml b/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml index 2aecd1328809..e3e77d5c88ef 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml @@ -195,3 +195,12 @@ - state: overlay color: "#cec8ac" - state: icon-default + +- type: entity + parent: BaseFlatpack + id: SpaceHeaterFlatpack + name: space heater flatpack + description: A flatpack used for constructing a space heater. + components: + - type: Flatpack + entity: SpaceHeaterAnchored From f84e87a010be663d7d3c045c50c4904a00417ca6 Mon Sep 17 00:00:00 2001 From: Ady4ik <141335742+Ady4ik@users.noreply.github.com> Date: Sun, 26 May 2024 00:07:18 +0300 Subject: [PATCH 052/568] Move PendingZombieComponent to Shared (#28143) * Move PendingZombieComponent to Shared * network me boy --------- Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --- .../Chemistry/ReagentEffects/CauseZombieInfection.cs | 1 + .../Chemistry/ReagentEffects/CureZombieInfection.cs | 1 + .../Zombies/PendingZombieComponent.cs | 5 +++-- 3 files changed, 5 insertions(+), 2 deletions(-) rename {Content.Server => Content.Shared}/Zombies/PendingZombieComponent.cs (94%) diff --git a/Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs b/Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs index 029b1495002b..96c57f74653e 100644 --- a/Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs +++ b/Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs @@ -2,6 +2,7 @@ using Content.Shared.Chemistry.Reagent; using Robust.Shared.Configuration; using Robust.Shared.Prototypes; +using Content.Shared.Zombies; namespace Content.Server.Chemistry.ReagentEffects; diff --git a/Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs b/Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs index d56fc1153101..20e2c015c46f 100644 --- a/Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs +++ b/Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs @@ -2,6 +2,7 @@ using Content.Shared.Chemistry.Reagent; using Robust.Shared.Configuration; using Robust.Shared.Prototypes; +using Content.Shared.Zombies; namespace Content.Server.Chemistry.ReagentEffects; diff --git a/Content.Server/Zombies/PendingZombieComponent.cs b/Content.Shared/Zombies/PendingZombieComponent.cs similarity index 94% rename from Content.Server/Zombies/PendingZombieComponent.cs rename to Content.Shared/Zombies/PendingZombieComponent.cs index 811d3f96440c..0fb61c84df11 100644 --- a/Content.Server/Zombies/PendingZombieComponent.cs +++ b/Content.Shared/Zombies/PendingZombieComponent.cs @@ -1,12 +1,13 @@ using Content.Shared.Damage; +using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.Zombies; +namespace Content.Shared.Zombies; /// /// Temporary because diseases suck. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent] public sealed partial class PendingZombieComponent : Component { /// From 436344c36da8af99d70e2d3247f694d639160931 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 25 May 2024 14:07:27 -0700 Subject: [PATCH 053/568] Make it possible to hide full health bars below a total damage threshold (#28127) * Make it possible to hide full health bars below a total damage threshold * Fix not setting state --- .../Overlays/EntityHealthBarOverlay.cs | 20 +++++++------- .../Damage/Components/DamageableComponent.cs | 26 +++++++++++-------- .../Damage/Systems/DamageableSystem.cs | 6 ++--- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/Content.Client/Overlays/EntityHealthBarOverlay.cs b/Content.Client/Overlays/EntityHealthBarOverlay.cs index c1c0ae93ec11..2b2ff14a22b4 100644 --- a/Content.Client/Overlays/EntityHealthBarOverlay.cs +++ b/Content.Client/Overlays/EntityHealthBarOverlay.cs @@ -1,15 +1,14 @@ +using System.Numerics; +using Content.Client.UserInterface.Systems; using Content.Shared.Damage; using Content.Shared.FixedPoint; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.StatusIcon.Components; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Enums; -using System.Numerics; -using Content.Shared.StatusIcon.Components; -using Content.Client.UserInterface.Systems; -using Robust.Shared.Prototypes; using static Robust.Shared.Maths.Color; namespace Content.Client.Overlays; @@ -79,6 +78,10 @@ protected override void Draw(in OverlayDrawArgs args) continue; } + // we are all progressing towards death every day + if (CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent) is not { } deathProgress) + continue; + var worldPosition = _transform.GetWorldPosition(xform); var worldMatrix = Matrix3.CreateTranslation(worldPosition); @@ -91,10 +94,6 @@ protected override void Draw(in OverlayDrawArgs args) var widthOfMob = bounds.Width * EyeManager.PixelsPerMeter; var position = new Vector2(-widthOfMob / EyeManager.PixelsPerMeter / 2, yOffset / EyeManager.PixelsPerMeter); - - // we are all progressing towards death every day - (float ratio, bool inCrit) deathProgress = CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent); - var color = GetProgressColor(deathProgress.ratio, deathProgress.inCrit); // Hardcoded width of the progress bar because it doesn't match the texture. @@ -122,10 +121,13 @@ protected override void Draw(in OverlayDrawArgs args) /// /// Returns a ratio between 0 and 1, and whether the entity is in crit. /// - private (float, bool) CalcProgress(EntityUid uid, MobStateComponent component, DamageableComponent dmg, MobThresholdsComponent thresholds) + private (float ratio, bool inCrit)? CalcProgress(EntityUid uid, MobStateComponent component, DamageableComponent dmg, MobThresholdsComponent thresholds) { if (_mobStateSystem.IsAlive(uid, component)) { + if (dmg.HealthBarThreshold != null && dmg.TotalDamage < dmg.HealthBarThreshold) + return null; + if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var threshold, thresholds) && !_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Dead, out threshold, thresholds)) return (1, false); diff --git a/Content.Shared/Damage/Components/DamageableComponent.cs b/Content.Shared/Damage/Components/DamageableComponent.cs index be66d51e3bcf..f8205568f101 100644 --- a/Content.Shared/Damage/Components/DamageableComponent.cs +++ b/Content.Shared/Damage/Components/DamageableComponent.cs @@ -5,8 +5,6 @@ using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Shared.Damage { @@ -18,7 +16,7 @@ namespace Content.Shared.Damage /// may also have resistances to certain damage types, defined via a . /// [RegisterComponent] - [NetworkedComponent()] + [NetworkedComponent] [Access(typeof(DamageableSystem), Other = AccessPermissions.ReadExecute)] public sealed partial class DamageableComponent : Component { @@ -26,8 +24,8 @@ public sealed partial class DamageableComponent : Component /// This specifies what damage types are supported by this component. /// If null, all damage types will be supported. /// - [DataField("damageContainer", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? DamageContainerID; + [DataField("damageContainer")] + public ProtoId? DamageContainerID; /// /// This will be applied to any damage that is dealt to this container, @@ -37,8 +35,8 @@ public sealed partial class DamageableComponent : Component /// Though DamageModifierSets can be deserialized directly, we only want to use the prototype version here /// to reduce duplication. /// - [DataField("damageModifierSet", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? DamageModifierSetId; + [DataField("damageModifierSet")] + public ProtoId? DamageModifierSetId; /// /// All the damage information is stored in this . @@ -46,7 +44,7 @@ public sealed partial class DamageableComponent : Component /// /// If this data-field is specified, this allows damageable components to be initialized with non-zero damage. /// - [DataField("damage", readOnly: true)] //todo remove this readonly when implementing writing to damagespecifier + [DataField(readOnly: true)] //todo remove this readonly when implementing writing to damagespecifier public DamageSpecifier Damage = new(); /// @@ -64,8 +62,8 @@ public sealed partial class DamageableComponent : Component [ViewVariables] public FixedPoint2 TotalDamage; - [DataField("radiationDamageTypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List RadiationDamageTypeIDs = new() { "Radiation" }; + [DataField("radiationDamageTypes")] + public List> RadiationDamageTypeIDs = new() { "Radiation" }; [DataField] public Dictionary> HealthIcons = new() @@ -77,6 +75,9 @@ public sealed partial class DamageableComponent : Component [DataField] public ProtoId RottingIcon = "HealthIconRotting"; + + [DataField] + public FixedPoint2? HealthBarThreshold; } [Serializable, NetSerializable] @@ -84,13 +85,16 @@ public sealed class DamageableComponentState : ComponentState { public readonly Dictionary DamageDict; public readonly string? ModifierSetId; + public readonly FixedPoint2? HealthBarThreshold; public DamageableComponentState( Dictionary damageDict, - string? modifierSetId) + string? modifierSetId, + FixedPoint2? healthBarThreshold) { DamageDict = damageDict; ModifierSetId = modifierSetId; + HealthBarThreshold = healthBarThreshold; } } } diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 4aaf380c47df..3c3e1b736df3 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Shared.Administration.Logs; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Content.Shared.Inventory; @@ -229,12 +228,12 @@ private void DamageableGetState(EntityUid uid, DamageableComponent component, re { if (_netMan.IsServer) { - args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId); + args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId, component.HealthBarThreshold); } else { // avoid mispredicting damage on newly spawned entities. - args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageModifierSetId); + args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageModifierSetId, component.HealthBarThreshold); } } @@ -268,6 +267,7 @@ private void DamageableHandleState(EntityUid uid, DamageableComponent component, } component.DamageModifierSetId = state.ModifierSetId; + component.HealthBarThreshold = state.HealthBarThreshold; // Has the damage actually changed? DamageSpecifier newDamage = new() { DamageDict = new(state.DamageDict) }; From d99e73ed92f06d22840e38a4e41ac8bfb8e93cdd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 May 2024 20:32:32 -0400 Subject: [PATCH 054/568] Update Credits (#28288) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index 172dd2ad37a1..b7ec22c0b0db 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, bhenrich, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, casperr04, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, DexlerXD, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, DuskyJay, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, Jark255, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, KEEYNy, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, MishaUnity, MisterMecky, Mith-randalf, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, Subversionary, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, waylon531, weaversam8, whateverusername0, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem +0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, bhenrich, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blueDev2, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, DexlerXD, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, DuskyJay, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, Jark255, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, KEEYNy, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, MishaUnity, MisterMecky, Mith-randalf, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, Subversionary, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, waylon531, weaversam8, whateverusername0, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem From 6574c9b7c31dc05831a95ddf121f2c29d9479fa4 Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Sun, 26 May 2024 03:17:01 +0200 Subject: [PATCH 055/568] Changing hands unwields item (#28161) Unhand me, fiend --- Content.Shared/Wieldable/WieldableSystem.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Content.Shared/Wieldable/WieldableSystem.cs b/Content.Shared/Wieldable/WieldableSystem.cs index cee6c65fa11e..c09044f84b43 100644 --- a/Content.Shared/Wieldable/WieldableSystem.cs +++ b/Content.Shared/Wieldable/WieldableSystem.cs @@ -41,6 +41,7 @@ public override void Initialize() SubscribeLocalEvent(OnItemLeaveHand); SubscribeLocalEvent(OnVirtualItemDeleted); SubscribeLocalEvent>(AddToggleWieldVerb); + SubscribeLocalEvent(OnDeselectWieldable); SubscribeLocalEvent(OnMeleeAttempt); SubscribeLocalEvent(OnShootAttempt); @@ -91,6 +92,14 @@ private void OnGunWielded(EntityUid uid, GunWieldBonusComponent component, ref I _gun.RefreshModifiers(uid); } + private void OnDeselectWieldable(EntityUid uid, WieldableComponent component, HandDeselectedEvent args) + { + if (!component.Wielded) + return; + + TryUnwield(uid, component, args.User); + } + private void OnGunRefreshModifiers(Entity bonus, ref GunRefreshModifiersEvent args) { if (TryComp(bonus, out WieldableComponent? wield) && From d5aae7a48b67d1d14322a2b5ae9fff2c4f0b9bd0 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 26 May 2024 01:18:07 +0000 Subject: [PATCH 056/568] 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 7880f0a8d130..4ec38486dac9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Errant - changes: - - message: Species info is now available in the Guidebook and in the character editor. - type: Add - id: 6120 - time: '2024-03-11T03:01:32.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25844 - author: Dygon changes: - message: 'The following criminal record statuses have been added: Suspect, Discharged, @@ -3875,3 +3868,10 @@ id: 6619 time: '2024-05-25T20:23:34.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28130 +- author: Errant + changes: + - message: Changing hands while wielding an item will now immediately unwield it. + type: Tweak + id: 6620 + time: '2024-05-26T01:17:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28161 From 12841da9a1de8e552df1117d945dd2b189ffcc4d Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 26 May 2024 14:11:37 +1200 Subject: [PATCH 057/568] Fix storage UI interactions (#28291) * Fix storage UI interactions * Add VV support --- .../UserInterface/Systems/Storage/Controls/ItemGridPiece.cs | 4 +++- Content.Shared/Interaction/SharedInteractionSystem.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs b/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs index f436cc8c39bb..dd9986e4c6e6 100644 --- a/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs +++ b/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs @@ -9,7 +9,7 @@ namespace Content.Client.UserInterface.Systems.Storage.Controls; -public sealed class ItemGridPiece : Control +public sealed class ItemGridPiece : Control, IEntityControl { private readonly IEntityManager _entityManager; private readonly StorageUIController _storageController; @@ -287,6 +287,8 @@ public static Vector2 GetCenterOffset(Entity entity, ItemStorage var actualSize = new Vector2(boxSize.X + 1, boxSize.Y + 1); return actualSize * new Vector2i(8, 8); } + + public EntityUid? UiEntity => Entity; } public enum ItemGridPieceMarks diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 3324ce5b9b8d..6f6777ee9648 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -1167,7 +1167,7 @@ public bool CanAccessViaStorage(EntityUid user, EntityUid target, BaseContainer return false; // we don't check if the user can access the storage entity itself. This should be handed by the UI system. - return _ui.IsUiOpen(target, StorageComponent.StorageUiKey.Key, user); + return _ui.IsUiOpen(container.Owner, StorageComponent.StorageUiKey.Key, user); } /// From b8020776cc9eb9e8f6b487e5ba34f87d144b3b24 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 26 May 2024 02:12:43 +0000 Subject: [PATCH 058/568] 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 4ec38486dac9..36276ccfcef1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,15 +1,4 @@ Entries: -- author: Dygon - changes: - - message: 'The following criminal record statuses have been added: Suspect, Discharged, - Paroled.' - type: Add - - message: Security huds now show an icon on entities that have a visible name linked - to a criminal record. - type: Add - id: 6121 - time: '2024-03-11T03:12:52.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25192 - author: Lank changes: - message: The detective is no longer independent, and again works under Security. @@ -3875,3 +3864,10 @@ id: 6620 time: '2024-05-26T01:17:01.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28161 +- author: ElectroJr + changes: + - message: Fixed being unable to interact with items via storage UIs + type: Fix + id: 6621 + time: '2024-05-26T02:11:37.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28291 From a38e6475cb5d0505771ec827bcd9b4336a0392ae Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 26 May 2024 15:18:27 +1200 Subject: [PATCH 059/568] Fix stripping not marking interactions as handled (#28292) --- Content.Server/Strip/StrippableSystem.cs | 33 ++----------------- .../Strip/SharedStrippableSystem.cs | 27 +++++++++++++-- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/Content.Server/Strip/StrippableSystem.cs b/Content.Server/Strip/StrippableSystem.cs index 6f0a1ecb328d..397396de5016 100644 --- a/Content.Server/Strip/StrippableSystem.cs +++ b/Content.Server/Strip/StrippableSystem.cs @@ -1,7 +1,6 @@ using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Ensnaring; -using Content.Shared.CombatMode; using Content.Shared.Cuffs; using Content.Shared.Cuffs.Components; using Content.Shared.Database; @@ -10,7 +9,6 @@ using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory; using Content.Shared.Inventory.VirtualItem; @@ -18,7 +16,6 @@ using Content.Shared.Strip; using Content.Shared.Strip.Components; using Content.Shared.Verbs; -using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Utility; @@ -28,7 +25,6 @@ public sealed class StrippableSystem : SharedStrippableSystem { [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly EnsnareableSystem _ensnaringSystem = default!; - [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly SharedCuffableSystem _cuffableSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; @@ -45,7 +41,6 @@ public override void Initialize() SubscribeLocalEvent>(AddStripVerb); SubscribeLocalEvent>(AddStripExamineVerb); - SubscribeLocalEvent(OnActivateInWorld); // BUI SubscribeLocalEvent(OnStripButtonPressed); @@ -68,7 +63,7 @@ private void AddStripVerb(EntityUid uid, StrippableComponent component, GetVerbs { Text = Loc.GetString("strip-verb-get-data-text"), Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")), - Act = () => StartOpeningStripper(args.User, (uid, component), true), + Act = () => TryOpenStrippingUi(args.User, (uid, component), true), }; args.Verbs.Add(verb); @@ -86,37 +81,13 @@ private void AddStripExamineVerb(EntityUid uid, StrippableComponent component, G { Text = Loc.GetString("strip-verb-get-data-text"), Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")), - Act = () => StartOpeningStripper(args.User, (uid, component), true), + Act = () => TryOpenStrippingUi(args.User, (uid, component), true), Category = VerbCategory.Examine, }; args.Verbs.Add(verb); } - private void OnActivateInWorld(EntityUid uid, StrippableComponent component, ActivateInWorldEvent args) - { - if (args.Target == args.User) - return; - - if (!HasComp(args.User)) - return; - - StartOpeningStripper(args.User, (uid, component)); - } - - public override void StartOpeningStripper(EntityUid user, Entity strippable, bool openInCombat = false) - { - base.StartOpeningStripper(user, strippable, openInCombat); - - if (TryComp(user, out var mode) && mode.IsInCombatMode && !openInCombat) - return; - - if (HasComp(user)) - { - _userInterfaceSystem.OpenUi(strippable.Owner, StrippingUiKey.Key, user); - } - } - private void OnStripButtonPressed(Entity strippable, ref StrippingSlotButtonPressed args) { if (args.Actor is not { Valid: true } user || diff --git a/Content.Shared/Strip/SharedStrippableSystem.cs b/Content.Shared/Strip/SharedStrippableSystem.cs index 59b24ec943ef..075cf81a4cb9 100644 --- a/Content.Shared/Strip/SharedStrippableSystem.cs +++ b/Content.Shared/Strip/SharedStrippableSystem.cs @@ -1,17 +1,31 @@ +using Content.Shared.CombatMode; using Content.Shared.DragDrop; using Content.Shared.Hands.Components; +using Content.Shared.Interaction; using Content.Shared.Strip.Components; namespace Content.Shared.Strip; public abstract class SharedStrippableSystem : EntitySystem { + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnCanDropOn); SubscribeLocalEvent(OnCanDrop); SubscribeLocalEvent(OnDragDrop); + SubscribeLocalEvent(OnActivateInWorld); + } + + private void OnActivateInWorld(EntityUid uid, StrippableComponent component, ActivateInWorldEvent args) + { + if (args.Handled || args.Target == args.User) + return; + + if (TryOpenStrippingUi(args.User, (uid, component))) + args.Handled = true; } public (TimeSpan Time, bool Stealth) GetStripTimeModifiers(EntityUid user, EntityUid target, TimeSpan initialTime) @@ -29,13 +43,20 @@ private void OnDragDrop(EntityUid uid, StrippableComponent component, ref DragDr if (args.Handled || args.Target != args.User) return; - StartOpeningStripper(args.User, (uid, component)); - args.Handled = true; + if (TryOpenStrippingUi(args.User, (uid, component))) + args.Handled = true; } - public virtual void StartOpeningStripper(EntityUid user, Entity component, bool openInCombat = false) + public bool TryOpenStrippingUi(EntityUid user, Entity target, bool openInCombat = false) { + if (!openInCombat && TryComp(user, out var mode) && mode.IsInCombatMode) + return false; + + if (!HasComp(user)) + return false; + _ui.OpenUi(target.Owner, StrippingUiKey.Key, user); + return true; } private void OnCanDropOn(EntityUid uid, StrippingComponent component, ref CanDropTargetEvent args) From b177fb8179eaf7a3b1c9fc9dabe36835e582cd06 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 26 May 2024 15:20:29 +1200 Subject: [PATCH 060/568] Make NetworkConfigurator use BoundUserInterfaceCheckRangeEvent (#28293) --- .../Systems/NetworkConfiguratorSystem.cs | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs index 346012415857..402d005dd475 100644 --- a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs @@ -63,9 +63,21 @@ public override void Initialize() SubscribeLocalEvent(OnToggleLinks); SubscribeLocalEvent(OnConfigButtonPressed); + SubscribeLocalEvent(OnUiRangeCheck); + SubscribeLocalEvent(OnComponentRemoved); } + private void OnUiRangeCheck(Entity ent, ref BoundUserInterfaceCheckRangeEvent args) + { + if (ent.Comp.ActiveDeviceList == null || args.Result == BoundUserInterfaceRangeResult.Fail) + return; + + DebugTools.Assert(Exists(ent.Comp.ActiveDeviceList)); + if (!_interactionSystem.InRangeUnobstructed(args.Actor!, ent.Comp.ActiveDeviceList.Value)) + args.Result = BoundUserInterfaceRangeResult.Fail; + } + private void OnShutdown(EntityUid uid, NetworkConfiguratorComponent component, ComponentShutdown args) { ClearDevices(uid, component); @@ -75,23 +87,6 @@ private void OnShutdown(EntityUid uid, NetworkConfiguratorComponent component, C component.ActiveDeviceList = null; } - public override void Update(float frameTime) - { - base.Update(frameTime); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var component)) - { - if (component.ActiveDeviceList != null - && EntityManager.EntityExists(component.ActiveDeviceList.Value) - && _interactionSystem.InRangeUnobstructed(uid, component.ActiveDeviceList.Value)) - continue; - - //The network configurator is a handheld device. There can only ever be an ui session open for the player holding the device. - _uiSystem.CloseUi(uid, NetworkConfiguratorUiKey.Configure); - } - } - private void OnMapInit(EntityUid uid, NetworkConfiguratorComponent component, MapInitEvent args) { UpdateListUiState(uid, component); From d747fa98f8d62344aeab379731c43335341bfc16 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 26 May 2024 00:46:41 -0400 Subject: [PATCH 061/568] actually fix magic mirrors (#28282) --- .../MagicMirror/MagicMirrorSystem.cs | 32 +++++-------------- .../MagicMirror/SharedMagicMirrorSystem.cs | 21 ++++++++++-- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/Content.Server/MagicMirror/MagicMirrorSystem.cs b/Content.Server/MagicMirror/MagicMirrorSystem.cs index fc1bff975660..8d8a6bfa3b4f 100644 --- a/Content.Server/MagicMirror/MagicMirrorSystem.cs +++ b/Content.Server/MagicMirror/MagicMirrorSystem.cs @@ -1,7 +1,6 @@ using System.Linq; using Content.Server.DoAfter; using Content.Server.Humanoid; -using Content.Shared.UserInterface; using Content.Shared.DoAfter; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Markings; @@ -24,7 +23,6 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnOpenUIAttempt); Subs.BuiEvents(MagicMirrorUiKey.Key, subs => @@ -36,7 +34,6 @@ public override void Initialize() subs.Event(OnTryMagicMirrorRemoveSlot); }); - SubscribeLocalEvent(OnMagicMirrorInteract); SubscribeLocalEvent(OnSelectSlotDoAfter); SubscribeLocalEvent(OnChangeColorDoAfter); @@ -44,23 +41,6 @@ public override void Initialize() SubscribeLocalEvent(OnAddSlotDoAfter); } - private void OnMagicMirrorInteract(Entity mirror, ref AfterInteractEvent args) - { - if (!args.CanReach || args.Target == null) - return; - - if (!_uiSystem.TryOpenUi(mirror.Owner, MagicMirrorUiKey.Key, args.User)) - return; - - UpdateInterface(mirror.Owner, args.Target.Value, mirror.Comp); - } - - private void OnOpenUIAttempt(EntityUid uid, MagicMirrorComponent mirror, ActivatableUIOpenAttemptEvent args) - { - if (!HasComp(args.User)) - args.Cancel(); - } - private void OnMagicMirrorSelect(EntityUid uid, MagicMirrorComponent component, MagicMirrorSelectMessage message) { if (component.Target is not { } target) @@ -83,7 +63,8 @@ private void OnMagicMirrorSelect(EntityUid uid, MagicMirrorComponent component, BreakOnMove = true, BreakOnHandChange = false, NeedHand = true - }, out var doAfterId); + }, + out var doAfterId); component.DoAfter = doAfterId; _audio.PlayPvs(component.ChangeHairSound, uid); @@ -137,7 +118,8 @@ private void OnTryMagicMirrorChangeColor(EntityUid uid, MagicMirrorComponent com BreakOnMove = true, BreakOnHandChange = false, NeedHand = true - }, out var doAfterId); + }, + out var doAfterId); component.DoAfter = doAfterId; } @@ -189,7 +171,8 @@ private void OnTryMagicMirrorRemoveSlot(EntityUid uid, MagicMirrorComponent comp BreakOnDamage = true, BreakOnHandChange = false, NeedHand = true - }, out var doAfterId); + }, + out var doAfterId); component.DoAfter = doAfterId; _audio.PlayPvs(component.ChangeHairSound, uid); @@ -241,7 +224,8 @@ private void OnTryMagicMirrorAddSlot(EntityUid uid, MagicMirrorComponent compone BreakOnMove = true, BreakOnHandChange = false, NeedHand = true - }, out var doAfterId); + }, + out var doAfterId); component.DoAfter = doAfterId; _audio.PlayPvs(component.ChangeHairSound, uid); diff --git a/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs b/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs index 433ad6b4fc94..ea96d504c6e6 100644 --- a/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs +++ b/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs @@ -11,15 +11,27 @@ namespace Content.Shared.MagicMirror; public abstract class SharedMagicMirrorSystem : EntitySystem { [Dependency] private readonly SharedInteractionSystem _interaction = default!; - [Dependency] protected readonly SharedUserInterfaceSystem _uiSystem = default!; + [Dependency] protected readonly SharedUserInterfaceSystem UISystem = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnMagicMirrorInteract); SubscribeLocalEvent(OnBeforeUIOpen); SubscribeLocalEvent(OnMirrorRangeCheck); } + private void OnMagicMirrorInteract(Entity mirror, ref AfterInteractEvent args) + { + if (!args.CanReach || args.Target == null) + return; + + if (!UISystem.TryOpenUi(mirror.Owner, MagicMirrorUiKey.Key, args.User)) + return; + + UpdateInterface(mirror, args.Target.Value, mirror); + } + private void OnMirrorRangeCheck(EntityUid uid, MagicMirrorComponent component, ref BoundUserInterfaceCheckRangeEvent args) { if (args.Result == BoundUserInterfaceRangeResult.Fail) @@ -33,7 +45,9 @@ private void OnMirrorRangeCheck(EntityUid uid, MagicMirrorComponent component, r private void OnBeforeUIOpen(Entity ent, ref BeforeActivatableUIOpenEvent args) { - ent.Comp.Target ??= args.User; + if (args.User != ent.Comp.Target && ent.Comp.Target != null) + return; + UpdateInterface(ent, args.User, ent); } @@ -41,6 +55,7 @@ protected void UpdateInterface(EntityUid mirrorUid, EntityUid targetUid, MagicMi { if (!TryComp(targetUid, out var humanoid)) return; + component.Target ??= targetUid; var hair = humanoid.MarkingSet.TryGetCategory(MarkingCategories.Hair, out var hairMarkings) ? new List(hairMarkings) @@ -59,7 +74,7 @@ protected void UpdateInterface(EntityUid mirrorUid, EntityUid targetUid, MagicMi // TODO: Component states component.Target = targetUid; - _uiSystem.SetUiState(mirrorUid, MagicMirrorUiKey.Key, state); + UISystem.SetUiState(mirrorUid, MagicMirrorUiKey.Key, state); Dirty(mirrorUid, component); } } From af10de61d313ae539ce24933b2986ee63a8f6a07 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 26 May 2024 04:47:47 +0000 Subject: [PATCH 062/568] 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 36276ccfcef1..0f5bddbcdbed 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Lank - changes: - - message: The detective is no longer independent, and again works under Security. - type: Tweak - id: 6122 - time: '2024-03-11T03:33:08.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25986 - author: pigeonpeas changes: - message: added new expedition ambience @@ -3871,3 +3864,10 @@ id: 6621 time: '2024-05-26T02:11:37.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28291 +- author: EmoGarbage404 + changes: + - message: Properly fix magic mirrors + type: Fix + id: 6622 + time: '2024-05-26T04:46:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28282 From 492ccc93d01647b586755da27f88f4cbb62e7ccc Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Sun, 26 May 2024 05:14:29 +0000 Subject: [PATCH 063/568] fix antag selection being evil (#28197) * fix antag selection being evil * fix test * untroll the other tests * remove role timer troll * Allow tests to modify antag preferences * Fix antag selection * Misc test fixes * Add AntagPreferenceTest * Fix lazy mistakes * Test cleanup * Try stop players in lobbies from being assigned mid-round antags * ranting * I am going insane --------- Co-authored-by: deltanedas <@deltanedas:kde.org> Co-authored-by: ElectroJr --- .../Pair/TestPair.Helpers.cs | 28 +++++++ Content.IntegrationTests/PoolManager.cs | 4 +- .../Tests/GameRules/AntagPreferenceTest.cs | 76 +++++++++++++++++++ .../Tests/GameRules/NukeOpsTest.cs | 4 + .../Click/InteractionSystemTests.cs | 1 - .../Tests/ResettingEntitySystemTests.cs | 3 - .../Antag/AntagSelectionSystem.API.cs | 9 +++ Content.Server/Antag/AntagSelectionSystem.cs | 72 +++++++++--------- .../GameTicking/GameTicker.RoundFlow.cs | 2 +- .../Managers/IServerPreferencesManager.cs | 2 + .../Managers/ServerPreferencesManager.cs | 30 ++++---- Content.Shared/Antag/AntagAcceptability.cs | 8 ++ Content.Shared/Roles/Jobs/SharedJobSystem.cs | 4 +- 13 files changed, 182 insertions(+), 61 deletions(-) create mode 100644 Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs diff --git a/Content.IntegrationTests/Pair/TestPair.Helpers.cs b/Content.IntegrationTests/Pair/TestPair.Helpers.cs index 0ea6d3e2dcc0..cc83232a0663 100644 --- a/Content.IntegrationTests/Pair/TestPair.Helpers.cs +++ b/Content.IntegrationTests/Pair/TestPair.Helpers.cs @@ -2,6 +2,9 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using Content.Server.Preferences.Managers; +using Content.Shared.Preferences; +using Content.Shared.Roles; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -128,4 +131,29 @@ public List GetPrototypesWithComponent( return list; } + + /// + /// Helper method for enabling or disabling a antag role + /// + public async Task SetAntagPref(ProtoId id, bool value) + { + var prefMan = Server.ResolveDependency(); + + var prefs = prefMan.GetPreferences(Client.User!.Value); + // what even is the point of ICharacterProfile if we always cast it to HumanoidCharacterProfile to make it usable? + var profile = (HumanoidCharacterProfile) prefs.SelectedCharacter; + + Assert.That(profile.AntagPreferences.Contains(id), Is.EqualTo(!value)); + var newProfile = profile.WithAntagPreference(id, value); + + await Server.WaitPost(() => + { + prefMan.SetProfile(Client.User.Value, prefs.SelectedCharacterIndex, newProfile).Wait(); + }); + + // And why the fuck does it always create a new preference and profile object instead of just reusing them? + var newPrefs = prefMan.GetPreferences(Client.User.Value); + var newProf = (HumanoidCharacterProfile) newPrefs.SelectedCharacter; + Assert.That(newProf.AntagPreferences.Contains(id), Is.EqualTo(value)); + } } diff --git a/Content.IntegrationTests/PoolManager.cs b/Content.IntegrationTests/PoolManager.cs index 25e6c7ef26f5..3b49ffcf8477 100644 --- a/Content.IntegrationTests/PoolManager.cs +++ b/Content.IntegrationTests/PoolManager.cs @@ -65,11 +65,11 @@ public static partial class PoolManager options.BeforeStart += () => { + // Server-only systems (i.e., systems that subscribe to events with server-only components) var entSysMan = IoCManager.Resolve(); - entSysMan.LoadExtraSystemType(); - entSysMan.LoadExtraSystemType(); entSysMan.LoadExtraSystemType(); entSysMan.LoadExtraSystemType(); + IoCManager.Resolve().GetSawmill("loc").Level = LogLevel.Error; IoCManager.Resolve() .OnValueChanged(RTCVars.FailureLogLevel, value => logHandler.FailureLevel = value, true); diff --git a/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs new file mode 100644 index 000000000000..662ea3b97470 --- /dev/null +++ b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs @@ -0,0 +1,76 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using Content.Server.Antag; +using Content.Server.Antag.Components; +using Content.Server.GameTicking; +using Content.Shared.GameTicking; +using Robust.Shared.GameObjects; +using Robust.Shared.Player; +using Robust.Shared.Random; + +namespace Content.IntegrationTests.Tests.GameRules; + +// Once upon a time, players in the lobby weren't ever considered eligible for antag roles. +// Lets not let that happen again. +[TestFixture] +public sealed class AntagPreferenceTest +{ + [Test] + public async Task TestLobbyPlayersValid() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings + { + DummyTicker = false, + Connected = true, + InLobby = true + }); + + var server = pair.Server; + var client = pair.Client; + var ticker = server.System(); + var sys = server.System(); + + // 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)); + + EntityUid uid = default; + await server.WaitPost(() => uid = server.EntMan.Spawn("Traitor")); + var rule = new Entity(uid, server.EntMan.GetComponent(uid)); + var def = rule.Comp.Definitions.Single(); + + // IsSessionValid & IsEntityValid are preference agnostic and should always be true for players in the lobby. + // Though maybe that will change in the future, but then GetPlayerPool() needs to be updated to reflect that. + Assert.That(sys.IsSessionValid(rule, pair.Player, def), Is.True); + Assert.That(sys.IsEntityValid(client.AttachedEntity, def), Is.True); + + // By default, traitor/antag preferences are disabled, so the pool should be empty. + var sessions = new List{pair.Player!}; + var pool = sys.GetPlayerPool(rule, sessions, def); + Assert.That(pool.Count, Is.EqualTo(0)); + + // Opt into the traitor role. + await pair.SetAntagPref("Traitor", true); + + Assert.That(sys.IsSessionValid(rule, pair.Player, def), Is.True); + Assert.That(sys.IsEntityValid(client.AttachedEntity, def), Is.True); + pool = sys.GetPlayerPool(rule, sessions, def); + Assert.That(pool.Count, Is.EqualTo(1)); + pool.TryPickAndTake(pair.Server.ResolveDependency(), out var picked); + Assert.That(picked, Is.EqualTo(pair.Player)); + Assert.That(sessions.Count, Is.EqualTo(1)); + + // opt back out + await pair.SetAntagPref("Traitor", false); + + Assert.That(sys.IsSessionValid(rule, pair.Player, def), Is.True); + Assert.That(sys.IsEntityValid(client.AttachedEntity, def), Is.True); + pool = sys.GetPlayerPool(rule, sessions, def); + Assert.That(pool.Count, Is.EqualTo(0)); + + await server.WaitPost(() => server.EntMan.DeleteEntity(uid)); + await pair.CleanReturnAsync(); + } +} diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs index 5bada98a3aa5..3a77834b7259 100644 --- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs @@ -58,6 +58,9 @@ public async Task TryStopNukeOpsFromConstantlyFailing() Assert.That(client.AttachedEntity, Is.Null); Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay)); + // Opt into the nukies role. + await pair.SetAntagPref("NukeopsCommander", true); + // There are no grids or maps Assert.That(entMan.Count(), Is.Zero); Assert.That(entMan.Count(), Is.Zero); @@ -198,6 +201,7 @@ public async Task TryStopNukeOpsFromConstantlyFailing() ticker.SetGamePreset((GamePresetPrototype?)null); server.CfgMan.SetCVar(CCVars.GridFill, false); + await pair.SetAntagPref("NukeopsCommander", false); await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs b/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs index 317aa10400b9..4415eddf376d 100644 --- a/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs +++ b/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs @@ -407,7 +407,6 @@ await server.WaitAssertion(() => await pair.CleanReturnAsync(); } - [Reflect(false)] public sealed class TestInteractionSystem : EntitySystem { public EntityEventHandler? InteractUsingEvent; diff --git a/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs b/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs index d5c2a9124dd9..40457f548835 100644 --- a/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs +++ b/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs @@ -9,7 +9,6 @@ namespace Content.IntegrationTests.Tests [TestOf(typeof(RoundRestartCleanupEvent))] public sealed class ResettingEntitySystemTests { - [Reflect(false)] public sealed class TestRoundRestartCleanupEvent : EntitySystem { public bool HasBeenReset { get; set; } @@ -49,8 +48,6 @@ await server.WaitAssertion(() => system.HasBeenReset = false; - Assert.That(system.HasBeenReset, Is.False); - gameTicker.RestartRound(); Assert.That(system.HasBeenReset); diff --git a/Content.Server/Antag/AntagSelectionSystem.API.cs b/Content.Server/Antag/AntagSelectionSystem.API.cs index d8554e3fb22a..3b5855cdf95c 100644 --- a/Content.Server/Antag/AntagSelectionSystem.API.cs +++ b/Content.Server/Antag/AntagSelectionSystem.API.cs @@ -27,6 +27,11 @@ public bool TryGetNextAvailableDefinition(Entity ent, if (mindCount >= totalTargetCount) return false; + // TODO ANTAG fix this + // If here are two definitions with 1/10 and 10/10 slots filled, this will always return the second definition + // even though it has already met its target + // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA I fucking hate game ticker code. + // It needs to track selected minds for each definition independently. foreach (var def in ent.Comp.Definitions) { var target = GetTargetAntagCount(ent, null, def); @@ -64,6 +69,10 @@ public int GetTargetAntagCount(Entity ent, AntagSelecti /// public int GetTargetAntagCount(Entity ent, AntagSelectionPlayerPool? pool, AntagSelectionDefinition def) { + // TODO ANTAG + // make pool non-nullable + // Review uses and ensure that people are INTENTIONALLY including players in the lobby if this is a mid-round + // antag selection. var poolSize = pool?.Count ?? _playerManager.Sessions .Count(s => s.State.Status is not SessionStatus.Disconnected and not SessionStatus.Zombie); diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index 1d1783ed8756..5b6c891ddfc5 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -14,6 +14,7 @@ using Content.Server.Shuttles.Components; using Content.Server.Station.Systems; using Content.Shared.Antag; +using Content.Shared.GameTicking; using Content.Shared.Ghost; using Content.Shared.Humanoid; using Content.Shared.Players; @@ -25,6 +26,7 @@ using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Server.Antag; @@ -85,10 +87,9 @@ private void OnPlayerSpawning(RulePlayerSpawningEvent args) continue; if (comp.SelectionsComplete) - return; + continue; ChooseAntags((uid, comp), pool); - comp.SelectionsComplete = true; foreach (var session in comp.SelectedSessions) { @@ -106,11 +107,7 @@ private void OnJobsAssigned(RulePlayerJobsAssignedEvent args) if (comp.SelectionTime != AntagSelectionTime.PostPlayerSpawn) continue; - if (comp.SelectionsComplete) - continue; - - ChooseAntags((uid, comp)); - comp.SelectionsComplete = true; + ChooseAntags((uid, comp), args.Players); } } @@ -126,12 +123,18 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent args) var query = QueryActiveRules(); while (query.MoveNext(out var uid, out _, out var antag, out _)) { + // TODO ANTAG + // what why aasdiuhasdopiuasdfhksad + // stop this insanity please + // probability of antag assignment shouldn't depend on the order in which rules are returned by the query. if (!RobustRandom.Prob(LateJoinRandomChance)) continue; if (!antag.Definitions.Any(p => p.LateJoinAdditional)) continue; + DebugTools.AssertEqual(antag.SelectionTime, AntagSelectionTime.PostPlayerSpawn); + if (!TryGetNextAvailableDefinition((uid, antag), out var def)) continue; @@ -164,43 +167,40 @@ protected override void Started(EntityUid uid, AntagSelectionComponent component { base.Started(uid, component, gameRule, args); - if (component.SelectionsComplete) - return; - + // If the round has not yet started, we defer antag selection until roundstart if (GameTicker.RunLevel != GameRunLevel.InRound) return; - if (GameTicker.RunLevel == GameRunLevel.InRound && component.SelectionTime == AntagSelectionTime.PrePlayerSpawn) + if (component.SelectionsComplete) return; - ChooseAntags((uid, component)); - component.SelectionsComplete = true; - } + var players = _playerManager.Sessions + .Where(x => GameTicker.PlayerGameStatuses[x.UserId] == PlayerGameStatus.JoinedGame) + .ToList(); - /// - /// Chooses antagonists from the current selection of players - /// - public void ChooseAntags(Entity ent) - { - var sessions = _playerManager.Sessions.ToList(); - ChooseAntags(ent, sessions); + ChooseAntags((uid, component), players); } /// /// Chooses antagonists from the given selection of players /// - public void ChooseAntags(Entity ent, List pool) + public void ChooseAntags(Entity ent, IList pool) { + if (ent.Comp.SelectionsComplete) + return; + foreach (var def in ent.Comp.Definitions) { ChooseAntags(ent, pool, def); } + + ent.Comp.SelectionsComplete = true; } /// /// Chooses antagonists from the given selection of players for the given antag definition. /// - public void ChooseAntags(Entity ent, List pool, AntagSelectionDefinition def) + public void ChooseAntags(Entity ent, IList pool, AntagSelectionDefinition def) { var playerPool = GetPlayerPool(ent, pool, def); var count = GetTargetAntagCount(ent, playerPool, def); @@ -324,20 +324,15 @@ public void MakeAntag(Entity ent, ICommonSession? sessi /// /// Gets an ordered player pool based on player preferences and the antagonist definition. /// - public AntagSelectionPlayerPool GetPlayerPool(Entity ent, List sessions, AntagSelectionDefinition def) + public AntagSelectionPlayerPool GetPlayerPool(Entity ent, IList sessions, AntagSelectionDefinition def) { var preferredList = new List(); var fallbackList = new List(); - var unwantedList = new List(); - var invalidList = new List(); foreach (var session in sessions) { if (!IsSessionValid(ent, session, def) || !IsEntityValid(session.AttachedEntity, def)) - { - invalidList.Add(session); continue; - } var pref = (HumanoidCharacterProfile) _pref.GetPreferences(session.UserId).SelectedCharacter; if (def.PrefRoles.Count != 0 && pref.AntagPreferences.Any(p => def.PrefRoles.Contains(p))) @@ -348,13 +343,9 @@ public AntagSelectionPlayerPool GetPlayerPool(Entity en { fallbackList.Add(session); } - else - { - unwantedList.Add(session); - } } - return new AntagSelectionPlayerPool(new() { preferredList, fallbackList, unwantedList, invalidList }); + return new AntagSelectionPlayerPool(new() { preferredList, fallbackList }); } /// @@ -365,14 +356,18 @@ public bool IsSessionValid(Entity ent, ICommonSession? if (session == null) return true; - mind ??= session.GetMind(); - if (session.Status is SessionStatus.Disconnected or SessionStatus.Zombie) return false; if (ent.Comp.SelectedSessions.Contains(session)) return false; + mind ??= session.GetMind(); + + // If the player has not spawned in as any entity (e.g., in the lobby), they can be given an antag role/entity. + if (mind == null) + return true; + //todo: we need some way to check that we're not getting the same role twice. (double picking thieves or zombies through midrounds) switch (def.MultiAntagSetting) @@ -401,10 +396,11 @@ public bool IsSessionValid(Entity ent, ICommonSession? /// /// Checks if a given entity (mind/session not included) is valid for a given antagonist. /// - private bool IsEntityValid(EntityUid? entity, AntagSelectionDefinition def) + public bool IsEntityValid(EntityUid? entity, AntagSelectionDefinition def) { + // If the player has not spawned in as any entity (e.g., in the lobby), they can be given an antag role/entity. if (entity == null) - return false; + return true; if (HasComp(entity)) return false; diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index df597e69b2fa..6eb42b65c03a 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -795,7 +795,7 @@ public RulePlayerSpawningEvent(List playerPool, IReadOnlyDiction } /// - /// Event raised after players were assigned jobs by the GameTicker. + /// Event raised after players were assigned jobs by the GameTicker and have been spawned in. /// You can give on-station people special roles by listening to this event. /// public sealed class RulePlayerJobsAssignedEvent diff --git a/Content.Server/Preferences/Managers/IServerPreferencesManager.cs b/Content.Server/Preferences/Managers/IServerPreferencesManager.cs index 63c267f154ac..cdb53bb4be77 100644 --- a/Content.Server/Preferences/Managers/IServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/IServerPreferencesManager.cs @@ -20,5 +20,7 @@ public interface IServerPreferencesManager PlayerPreferences? GetPreferencesOrNull(NetUserId? userId); IEnumerable> GetSelectedProfilesForPlayers(List userIds); bool HavePreferencesLoaded(ICommonSession session); + + Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile); } } diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index 1aad61715bb7..e32af589e958 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -29,11 +29,14 @@ public sealed class ServerPreferencesManager : IServerPreferencesManager [Dependency] private readonly IServerDbManager _db = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IDependencyCollection _dependencies = default!; + [Dependency] private readonly ILogManager _log = default!; // Cache player prefs on the server so we don't need as much async hell related to them. private readonly Dictionary _cachedPlayerPrefs = new(); + private ISawmill _sawmill = default!; + private int MaxCharacterSlots => _cfg.GetCVar(CCVars.GameMaxCharacterSlots); public void Init() @@ -42,6 +45,7 @@ public void Init() _netManager.RegisterNetMessage(HandleSelectCharacterMessage); _netManager.RegisterNetMessage(HandleUpdateCharacterMessage); _netManager.RegisterNetMessage(HandleDeleteCharacterMessage); + _sawmill = _log.GetSawmill("prefs"); } private async void HandleSelectCharacterMessage(MsgSelectCharacter message) @@ -78,27 +82,25 @@ private async void HandleSelectCharacterMessage(MsgSelectCharacter message) private async void HandleUpdateCharacterMessage(MsgUpdateCharacter message) { - var slot = message.Slot; - var profile = message.Profile; var userId = message.MsgChannel.UserId; - if (profile == null) - { - Logger.WarningS("prefs", - $"User {userId} sent a {nameof(MsgUpdateCharacter)} with a null profile in slot {slot}."); - return; - } + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (message.Profile == null) + _sawmill.Error($"User {userId} sent a {nameof(MsgUpdateCharacter)} with a null profile in slot {message.Slot}."); + else + await SetProfile(userId, message.Slot, message.Profile); + } + public async Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile) + { if (!_cachedPlayerPrefs.TryGetValue(userId, out var prefsData) || !prefsData.PrefsLoaded) { - Logger.WarningS("prefs", $"User {userId} tried to modify preferences before they loaded."); + _sawmill.Error($"Tried to modify user {userId} preferences before they loaded."); return; } if (slot < 0 || slot >= MaxCharacterSlots) - { return; - } var curPrefs = prefsData.Prefs!; var session = _playerManager.GetSessionById(userId); @@ -112,10 +114,8 @@ private async void HandleUpdateCharacterMessage(MsgUpdateCharacter message) prefsData.Prefs = new PlayerPreferences(profiles, slot, curPrefs.AdminOOCColor); - if (ShouldStorePrefs(message.MsgChannel.AuthType)) - { - await _db.SaveCharacterSlotAsync(message.MsgChannel.UserId, message.Profile, message.Slot); - } + if (ShouldStorePrefs(session.Channel.AuthType)) + await _db.SaveCharacterSlotAsync(userId, profile, slot); } private async void HandleDeleteCharacterMessage(MsgDeleteCharacter message) diff --git a/Content.Shared/Antag/AntagAcceptability.cs b/Content.Shared/Antag/AntagAcceptability.cs index 02d0b5f58fee..f56be9750335 100644 --- a/Content.Shared/Antag/AntagAcceptability.cs +++ b/Content.Shared/Antag/AntagAcceptability.cs @@ -22,6 +22,14 @@ public enum AntagAcceptability public enum AntagSelectionTime : byte { + /// + /// Antag roles are assigned before players are assigned jobs and spawned in. + /// This prevents antag selection from happening if the round is on-going. + /// PrePlayerSpawn, + + /// + /// Antag roles get assigned after players have been assigned jobs and have spawned in. + /// PostPlayerSpawn } diff --git a/Content.Shared/Roles/Jobs/SharedJobSystem.cs b/Content.Shared/Roles/Jobs/SharedJobSystem.cs index 04ac45c4c5f4..fcf76052785e 100644 --- a/Content.Shared/Roles/Jobs/SharedJobSystem.cs +++ b/Content.Shared/Roles/Jobs/SharedJobSystem.cs @@ -146,8 +146,10 @@ public string MindTryGetJobName([NotNullWhen(true)] EntityUid? mindId) public bool CanBeAntag(ICommonSession player) { + // If the player does not have any mind associated with them (e.g., has not spawned in or is in the lobby), then + // they are eligible to be given an antag role/entity. if (_playerSystem.ContentData(player) is not { Mind: { } mindId }) - return false; + return true; if (!MindTryGetJob(mindId, out _, out var prototype)) return true; From 0be71c400d9e5585a9f3572788479df1bf9c4fcd Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 26 May 2024 05:15:35 +0000 Subject: [PATCH 064/568] 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 0f5bddbcdbed..a26b705a35f0 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: pigeonpeas - changes: - - message: added new expedition ambience - type: Add - id: 6123 - time: '2024-03-11T06:56:01.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25983 - author: Ilya246 changes: - message: The biomass reclaimer may now reclaim plants, and inserting smaller entities @@ -3871,3 +3864,10 @@ id: 6622 time: '2024-05-26T04:46:41.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28282 +- author: deltanedas + changes: + - message: Fixed being picked for antag roles while in the lobby or not opted in. + type: Fix + id: 6623 + time: '2024-05-26T05:14:29.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28197 From 2586427c5211323ae49b387b51edc5a56255b169 Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Sun, 26 May 2024 10:48:34 -0400 Subject: [PATCH 065/568] Fix hypodarts not injecting with people that have ANY outerclothing (#28301) Update darts.yml --- Resources/Prototypes/Entities/Objects/Fun/darts.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Objects/Fun/darts.yml b/Resources/Prototypes/Entities/Objects/Fun/darts.yml index 4c7ae68420b9..36c841995e5c 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/darts.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/darts.yml @@ -126,6 +126,7 @@ maxVol: 7 - type: SolutionInjectOnEmbed transferAmount: 7 + blockSlots: NONE solution: melee - type: SolutionTransfer maxTransferAmount: 7 From fed072ee202512b6010a315cbe1ff260fddf5709 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 26 May 2024 14:49:40 +0000 Subject: [PATCH 066/568] 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 a26b705a35f0..80f1972d5601 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Ilya246 - changes: - - message: The biomass reclaimer may now reclaim plants, and inserting smaller entities - into it is now faster. - type: Tweak - id: 6124 - time: '2024-03-11T21:59:21.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/23731 - author: SlamBamActionman changes: - message: Moths eating lizard plushies will now start to Weh! @@ -3871,3 +3863,10 @@ id: 6623 time: '2024-05-26T05:14:29.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28197 +- author: Dutch-VanDerLinde + changes: + - message: Fix hypodarts not injecting into people with outerclothing equipped. + type: Fix + id: 6624 + time: '2024-05-26T14:48:34.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28301 From cba23487d3df52fbd14aec5a30d3ac90ca942f88 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 26 May 2024 16:07:16 -0400 Subject: [PATCH 067/568] fix borg ui mispredict opening (#28305) move borg ui junk to shared --- .../ActivatableUIRequiresLockSystem.cs | 43 ------------------- Content.Server/Silicons/Borgs/BorgSystem.cs | 9 ---- Content.Server/Wires/WiresSystem.cs | 23 ---------- .../ActivatableUIRequiresLockComponent.cs | 10 +++-- Content.Shared/Lock/LockSystem.cs | 26 +++++++++++ .../Silicons/Borgs/SharedBorgSystem.cs | 11 ++++- .../ActivatableUIRequiresPanelComponent.cs | 8 ++-- Content.Shared/Wires/SharedWiresSystem.cs | 22 ++++++++++ 8 files changed, 69 insertions(+), 83 deletions(-) delete mode 100644 Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs rename {Content.Server/Lock/Components => Content.Shared/Lock}/ActivatableUIRequiresLockComponent.cs (66%) rename {Content.Server => Content.Shared}/Wires/ActivatableUIRequiresPanelComponent.cs (71%) diff --git a/Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs b/Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs deleted file mode 100644 index 04f8e2eb54cf..000000000000 --- a/Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Content.Server.Lock.Components; -using Content.Server.Popups; -using Content.Shared.UserInterface; -using Content.Shared.Lock; -using Content.Server.UserInterface; -using ActivatableUISystem = Content.Shared.UserInterface.ActivatableUISystem; - -namespace Content.Server.Lock.EntitySystems; -public sealed class ActivatableUIRequiresLockSystem : EntitySystem -{ - [Dependency] private readonly ActivatableUISystem _activatableUI = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnUIOpenAttempt); - SubscribeLocalEvent(LockToggled); - } - - private void OnUIOpenAttempt(EntityUid uid, ActivatableUIRequiresLockComponent component, ActivatableUIOpenAttemptEvent args) - { - if (args.Cancelled) - return; - - if (TryComp(uid, out var lockComp) && lockComp.Locked != component.requireLocked) - { - args.Cancel(); - if (lockComp.Locked) - _popupSystem.PopupEntity(Loc.GetString("entity-storage-component-locked-message"), uid, args.User); - } - } - - private void LockToggled(EntityUid uid, ActivatableUIRequiresLockComponent component, LockToggledEvent args) - { - if (!TryComp(uid, out var lockComp) || lockComp.Locked == component.requireLocked) - return; - - _activatableUI.CloseAll(uid); - } -} - diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index 97adfd00eb4c..082e38921a2c 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -5,7 +5,6 @@ using Content.Server.Explosion.EntitySystems; using Content.Server.Hands.Systems; using Content.Server.PowerCell; -using Content.Shared.UserInterface; using Content.Shared.Access.Systems; using Content.Shared.Alert; using Content.Shared.Database; @@ -70,7 +69,6 @@ public override void Initialize() SubscribeLocalEvent(OnMobStateChanged); SubscribeLocalEvent(OnPowerCellChanged); SubscribeLocalEvent(OnPowerCellSlotEmpty); - SubscribeLocalEvent(OnUIOpenAttempt); SubscribeLocalEvent(OnGetDeadIC); SubscribeLocalEvent(OnBrainMindAdded); @@ -214,13 +212,6 @@ private void OnPowerCellSlotEmpty(EntityUid uid, BorgChassisComponent component, UpdateUI(uid, component); } - private void OnUIOpenAttempt(EntityUid uid, BorgChassisComponent component, ActivatableUIOpenAttemptEvent args) - { - // borgs can't view their own ui - if (args.User == uid) - args.Cancel(); - } - private void OnGetDeadIC(EntityUid uid, BorgChassisComponent component, ref GetCharactedDeadIcEvent args) { args.Dead = true; diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index c643759f504b..944b0a0e2500 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -4,27 +4,23 @@ using Content.Server.Construction; using Content.Server.Construction.Components; using Content.Server.Power.Components; -using Content.Server.UserInterface; using Content.Shared.DoAfter; using Content.Shared.GameTicking; using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Tools.Components; -using Content.Shared.UserInterface; using Content.Shared.Wires; using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using ActivatableUISystem = Content.Shared.UserInterface.ActivatableUISystem; namespace Content.Server.Wires; public sealed class WiresSystem : SharedWiresSystem { [Dependency] private readonly IPrototypeManager _protoMan = default!; - [Dependency] private readonly ActivatableUISystem _activatableUI = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; @@ -52,8 +48,6 @@ public override void Initialize() SubscribeLocalEvent(OnTimedWire); SubscribeLocalEvent(OnWiresPowered); SubscribeLocalEvent(OnDoAfter); - SubscribeLocalEvent(OnAttemptOpenActivatableUI); - SubscribeLocalEvent(OnActivatableUIPanelChanged); SubscribeLocalEvent(SetWiresPanelSecurity); } @@ -473,23 +467,6 @@ private void OnPanelChanged(Entity ent, ref PanelChangedEvent ar _uiSystem.CloseUi(ent.Owner, WiresUiKey.Key); } - private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPanelComponent component, ActivatableUIOpenAttemptEvent args) - { - if (args.Cancelled || !TryComp(uid, out var wires)) - return; - - if (component.RequireOpen != wires.Open) - args.Cancel(); - } - - private void OnActivatableUIPanelChanged(EntityUid uid, ActivatableUIRequiresPanelComponent component, ref PanelChangedEvent args) - { - if (args.Open == component.RequireOpen) - return; - - _activatableUI.CloseAll(uid); - } - private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args) { if (!string.IsNullOrEmpty(component.LayoutId)) diff --git a/Content.Server/Lock/Components/ActivatableUIRequiresLockComponent.cs b/Content.Shared/Lock/ActivatableUIRequiresLockComponent.cs similarity index 66% rename from Content.Server/Lock/Components/ActivatableUIRequiresLockComponent.cs rename to Content.Shared/Lock/ActivatableUIRequiresLockComponent.cs index dac677c1c276..7d701ffd8741 100644 --- a/Content.Server/Lock/Components/ActivatableUIRequiresLockComponent.cs +++ b/Content.Shared/Lock/ActivatableUIRequiresLockComponent.cs @@ -1,15 +1,17 @@ -namespace Content.Server.Lock.Components; +using Robust.Shared.GameStates; + +namespace Content.Shared.Lock; /// /// This is used for activatable UIs that require the entity to have a lock in a certain state. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent, Access(typeof(LockSystem))] public sealed partial class ActivatableUIRequiresLockComponent : Component { /// /// TRUE: the lock must be locked to access the UI. /// FALSE: the lock must be unlocked to access the UI. /// - [DataField("requireLocked"), ViewVariables(VVAccess.ReadWrite)] - public bool requireLocked = false; + [DataField] + public bool RequireLocked; } diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index 4115358d9d78..36ac5f025b1a 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Popups; using Content.Shared.Storage; using Content.Shared.Storage.Components; +using Content.Shared.UserInterface; using Content.Shared.Verbs; using Content.Shared.Wires; using JetBrains.Annotations; @@ -25,6 +26,7 @@ namespace Content.Shared.Lock; public sealed class LockSystem : EntitySystem { [Dependency] private readonly AccessReaderSystem _accessReader = default!; + [Dependency] private readonly ActivatableUISystem _activatableUI = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedPopupSystem _sharedPopupSystem = default!; @@ -48,6 +50,9 @@ public override void Initialize() SubscribeLocalEvent(OnLockToggleAttempt); SubscribeLocalEvent(OnAttemptChangePanel); SubscribeLocalEvent(OnUnanchorAttempt); + + SubscribeLocalEvent(OnUIOpenAttempt); + SubscribeLocalEvent(LockToggled); } private void OnStartup(EntityUid uid, LockComponent lockComp, ComponentStartup args) @@ -349,5 +354,26 @@ private void OnUnanchorAttempt(Entity ent, ref Unanch args.User); args.Cancel(); } + + private void OnUIOpenAttempt(EntityUid uid, ActivatableUIRequiresLockComponent component, ActivatableUIOpenAttemptEvent args) + { + if (args.Cancelled) + return; + + if (TryComp(uid, out var lockComp) && lockComp.Locked != component.RequireLocked) + { + args.Cancel(); + if (lockComp.Locked) + _sharedPopupSystem.PopupEntity(Loc.GetString("entity-storage-component-locked-message"), uid, args.User); + } + } + + private void LockToggled(EntityUid uid, ActivatableUIRequiresLockComponent component, LockToggledEvent args) + { + if (!TryComp(uid, out var lockComp) || lockComp.Locked == component.RequireLocked) + return; + + _activatableUI.CloseAll(uid); + } } diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs index 0431d95a42f8..2983c0d642f2 100644 --- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Popups; using Content.Shared.PowerCell.Components; using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.UserInterface; using Content.Shared.Wires; using Robust.Shared.Containers; @@ -30,7 +31,8 @@ public override void Initialize() SubscribeLocalEvent(OnInserted); SubscribeLocalEvent(OnRemoved); SubscribeLocalEvent(OnRefreshMovementSpeedModifiers); - + SubscribeLocalEvent(OnUIOpenAttempt); + InitializeRelay(); } @@ -75,6 +77,13 @@ private void OnStartup(EntityUid uid, BorgChassisComponent component, ComponentS component.ModuleContainer = Container.EnsureContainer(uid, component.ModuleContainerId, containerManager); } + private void OnUIOpenAttempt(EntityUid uid, BorgChassisComponent component, ActivatableUIOpenAttemptEvent args) + { + // borgs can't view their own ui + if (args.User == uid) + args.Cancel(); + } + protected virtual void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args) { diff --git a/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs b/Content.Shared/Wires/ActivatableUIRequiresPanelComponent.cs similarity index 71% rename from Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs rename to Content.Shared/Wires/ActivatableUIRequiresPanelComponent.cs index f92df3d3d59a..6e8fff6e49ca 100644 --- a/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs +++ b/Content.Shared/Wires/ActivatableUIRequiresPanelComponent.cs @@ -1,15 +1,17 @@ -namespace Content.Server.Wires; +using Robust.Shared.GameStates; + +namespace Content.Shared.Wires; /// /// This is used for activatable UIs that require the entity to have a panel in a certain state. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedWiresSystem))] public sealed partial class ActivatableUIRequiresPanelComponent : Component { /// /// TRUE: the panel must be open to access the UI. /// FALSE: the panel must be closed to access the UI. /// - [DataField("requireOpen"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public bool RequireOpen = true; } diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index b4b0768e0f77..d84766a5fc66 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Tools.Systems; +using Content.Shared.UserInterface; using Robust.Shared.Audio.Systems; namespace Content.Shared.Wires; @@ -10,6 +11,7 @@ namespace Content.Shared.Wires; public abstract class SharedWiresSystem : EntitySystem { [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; + [Dependency] private readonly ActivatableUISystem _activatableUI = default!; [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedToolSystem Tool = default!; @@ -21,6 +23,9 @@ public override void Initialize() SubscribeLocalEvent(OnPanelDoAfter); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnExamine); + + SubscribeLocalEvent(OnAttemptOpenActivatableUI); + SubscribeLocalEvent(OnActivatableUIPanelChanged); } private void OnPanelDoAfter(EntityUid uid, WiresPanelComponent panel, WirePanelDoAfterEvent args) @@ -132,4 +137,21 @@ public bool IsPanelOpen(Entity entity) return entity.Comp.Open; } + + private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPanelComponent component, ActivatableUIOpenAttemptEvent args) + { + if (args.Cancelled || !TryComp(uid, out var wires)) + return; + + if (component.RequireOpen != wires.Open) + args.Cancel(); + } + + private void OnActivatableUIPanelChanged(EntityUid uid, ActivatableUIRequiresPanelComponent component, ref PanelChangedEvent args) + { + if (args.Open == component.RequireOpen) + return; + + _activatableUI.CloseAll(uid); + } } From af70b9a3c78046f7f680e5c35a298ebc1747eadd Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Mon, 27 May 2024 10:11:17 +1000 Subject: [PATCH 068/568] Selectively revert PullController (#28126) I am leaving the issues open and have updated #26547 with more info on what we should do long-term. This is just to bandaid the short-term complaining. --- .../Movement/Components/PullMoverComponent.cs | 13 + .../Components/PullMovingComponent.cs | 14 + .../Movement/Systems/PullController.cs | 318 ++++++++++++++++++ .../Pulling/Components/PullerComponent.cs | 2 +- .../Movement/Pulling/Systems/PullingSystem.cs | 44 --- 5 files changed, 346 insertions(+), 45 deletions(-) create mode 100644 Content.Server/Movement/Components/PullMoverComponent.cs create mode 100644 Content.Server/Movement/Components/PullMovingComponent.cs create mode 100644 Content.Server/Movement/Systems/PullController.cs diff --git a/Content.Server/Movement/Components/PullMoverComponent.cs b/Content.Server/Movement/Components/PullMoverComponent.cs new file mode 100644 index 000000000000..19a01c6b17db --- /dev/null +++ b/Content.Server/Movement/Components/PullMoverComponent.cs @@ -0,0 +1,13 @@ +namespace Content.Server.Movement.Components; + +/// +/// Added to an entity that is ctrl-click moving their pulled object. +/// +/// +/// This just exists so we don't have MoveEvent subs going off for every single mob constantly. +/// +[RegisterComponent] +public sealed partial class PullMoverComponent : Component +{ + +} diff --git a/Content.Server/Movement/Components/PullMovingComponent.cs b/Content.Server/Movement/Components/PullMovingComponent.cs new file mode 100644 index 000000000000..32c50d657ade --- /dev/null +++ b/Content.Server/Movement/Components/PullMovingComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.Map; + +namespace Content.Server.Movement.Components; + +/// +/// Added when an entity is being ctrl-click moved when pulled. +/// +[RegisterComponent] +public sealed partial class PullMovingComponent : Component +{ + // Not serialized to indicate THIS CODE SUCKS, fix pullcontroller first + [ViewVariables] + public EntityCoordinates MovingTo; +} diff --git a/Content.Server/Movement/Systems/PullController.cs b/Content.Server/Movement/Systems/PullController.cs new file mode 100644 index 000000000000..72110ff67d24 --- /dev/null +++ b/Content.Server/Movement/Systems/PullController.cs @@ -0,0 +1,318 @@ +using System.Numerics; +using Content.Server.Movement.Components; +using Content.Server.Physics.Controllers; +using Content.Shared.ActionBlocker; +using Content.Shared.Gravity; +using Content.Shared.Input; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Rotatable; +using Robust.Server.Physics; +using Robust.Shared.Containers; +using Robust.Shared.Input.Binding; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Controllers; +using Robust.Shared.Physics.Dynamics.Joints; +using Robust.Shared.Player; +using Robust.Shared.Timing; + +namespace Content.Server.Movement.Systems; + +public sealed class PullController : VirtualController +{ + /* + * This code is awful. If you try to tweak this without refactoring it I'm gonna revert it. + */ + + // Parameterization for pulling: + // Speeds. Note that the speed is mass-independent (multiplied by mass). + // Instead, tuning to mass is done via the mass values below. + // Note that setting the speed too high results in overshoots (stabilized by drag, but bad) + private const float AccelModifierHigh = 15f; + private const float AccelModifierLow = 60.0f; + // High/low-mass marks. Curve is constant-lerp-constant, i.e. if you can even pull an item, + // you'll always get at least AccelModifierLow and no more than AccelModifierHigh. + private const float AccelModifierHighMass = 70.0f; // roundstart saltern emergency closet + private const float AccelModifierLowMass = 5.0f; // roundstart saltern emergency crowbar + // Used to control settling (turns off pulling). + private const float MaximumSettleVelocity = 0.1f; + private const float MaximumSettleDistance = 0.1f; + // Settle shutdown control. + // Mustn't be too massive, as that causes severe mispredicts *and can prevent it ever resolving*. + // Exists to bleed off "I pulled my crowbar" overshoots. + // Minimum velocity for shutdown to be necessary. This prevents stuff getting stuck b/c too much shutdown. + private const float SettleMinimumShutdownVelocity = 0.25f; + // Distance in which settle shutdown multiplier is at 0. It then scales upwards linearly with closer distances. + private const float SettleShutdownDistance = 1.0f; + // Velocity change of -LinearVelocity * frameTime * this + private const float SettleShutdownMultiplier = 20.0f; + + // How much you must move for the puller movement check to actually hit. + private const float MinimumMovementDistance = 0.005f; + + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; + + /// + /// If distance between puller and pulled entity lower that this threshold, + /// pulled entity will not change its rotation. + /// Helps with small distance jittering + /// + private const float ThresholdRotDistance = 1; + + /// + /// If difference between puller and pulled angle lower that this threshold, + /// pulled entity will not change its rotation. + /// Helps with diagonal movement jittering + /// As of further adjustments, should divide cleanly into 90 degrees + /// + private const float ThresholdRotAngle = 22.5f; + + private EntityQuery _physicsQuery; + private EntityQuery _pullableQuery; + private EntityQuery _pullerQuery; + private EntityQuery _xformQuery; + + public override void Initialize() + { + CommandBinds.Builder + .Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(OnRequestMovePulledObject)) + .Register(); + + _physicsQuery = GetEntityQuery(); + _pullableQuery = GetEntityQuery(); + _pullerQuery = GetEntityQuery(); + _xformQuery = GetEntityQuery(); + + UpdatesAfter.Add(typeof(MoverController)); + SubscribeLocalEvent(OnPullStop); + SubscribeLocalEvent(OnPullerMove); + + base.Initialize(); + } + + public override void Shutdown() + { + base.Shutdown(); + CommandBinds.Unregister(); + } + + private void OnPullStop(Entity ent, ref PullStoppedMessage args) + { + RemCompDeferred(ent); + } + + private bool OnRequestMovePulledObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid) + { + if (session?.AttachedEntity is not { } player || + !player.IsValid()) + { + return false; + } + + if (!_pullerQuery.TryComp(player, out var pullerComp)) + return false; + + var pulled = pullerComp.Pulling; + + if (!_pullableQuery.TryComp(pulled, out var pullable)) + return false; + + if (_container.IsEntityInContainer(player)) + return false; + + // Cooldown buddy + if (_timing.CurTime < pullerComp.NextThrow) + return false; + + pullerComp.NextThrow = _timing.CurTime + pullerComp.ThrowCooldown; + + // Cap the distance + var range = 2f; + var fromUserCoords = coords.WithEntityId(player, EntityManager); + var userCoords = new EntityCoordinates(player, Vector2.Zero); + + if (!coords.InRange(EntityManager, TransformSystem, userCoords, range)) + { + var direction = fromUserCoords.Position - userCoords.Position; + + // TODO: Joint API not ass + // with that being said I think throwing is the way to go but. + if (pullable.PullJointId != null && + TryComp(player, out JointComponent? joint) && + joint.GetJoints.TryGetValue(pullable.PullJointId, out var pullJoint) && + pullJoint is DistanceJoint distance) + { + range = MathF.Max(0.01f, distance.MaxLength - 0.01f); + } + + fromUserCoords = new EntityCoordinates(player, direction.Normalized() * (range - 0.01f)); + coords = fromUserCoords.WithEntityId(coords.EntityId); + } + + EnsureComp(player); + var moving = EnsureComp(pulled!.Value); + moving.MovingTo = coords; + return false; + } + + private void OnPullerMove(EntityUid uid, PullMoverComponent component, ref MoveEvent args) + { + if (!_pullerQuery.TryComp(uid, out var puller)) + return; + + if (puller.Pulling is not { } pullable) + return; + + UpdatePulledRotation(uid, pullable); + + // WHY + if (args.NewPosition.EntityId == args.OldPosition.EntityId && + (args.NewPosition.Position - args.OldPosition.Position).LengthSquared() < + MinimumMovementDistance * MinimumMovementDistance) + { + return; + } + + if (_physicsQuery.TryComp(uid, out var physics)) + PhysicsSystem.WakeBody(uid, body: physics); + + StopMove(uid, pullable); + } + + private void StopMove(Entity mover, Entity moving) + { + RemCompDeferred(mover.Owner); + RemCompDeferred(moving.Owner); + } + + private void UpdatePulledRotation(EntityUid puller, EntityUid pulled) + { + // TODO: update once ComponentReference works with directed event bus. + if (!TryComp(pulled, out RotatableComponent? rotatable)) + return; + + if (!rotatable.RotateWhilePulling) + return; + + var pulledXform = _xformQuery.GetComponent(pulled); + var pullerXform = _xformQuery.GetComponent(puller); + + var pullerData = TransformSystem.GetWorldPositionRotation(pullerXform); + var pulledData = TransformSystem.GetWorldPositionRotation(pulledXform); + + var dir = pullerData.WorldPosition - pulledData.WorldPosition; + if (dir.LengthSquared() > ThresholdRotDistance * ThresholdRotDistance) + { + var oldAngle = pulledData.WorldRotation; + var newAngle = Angle.FromWorldVec(dir); + + var diff = newAngle - oldAngle; + if (Math.Abs(diff.Degrees) > ThresholdRotAngle / 2f) + { + // Ok, so this bit is difficult because ideally it would look like it's snapping to sane angles. + // Otherwise PIANO DOOR STUCK! happens. + // But it also needs to work with station rotation / align to the local parent. + // So... + var baseRotation = pulledData.WorldRotation - pulledXform.LocalRotation; + var localRotation = newAngle - baseRotation; + var localRotationSnapped = Angle.FromDegrees(Math.Floor((localRotation.Degrees / ThresholdRotAngle) + 0.5f) * ThresholdRotAngle); + TransformSystem.SetLocalRotation(pulled, localRotationSnapped, pulledXform); + } + } + } + + public override void UpdateBeforeSolve(bool prediction, float frameTime) + { + base.UpdateBeforeSolve(prediction, frameTime); + var movingQuery = EntityQueryEnumerator(); + + while (movingQuery.MoveNext(out var pullableEnt, out var mover, out var pullable, out var pullableXform)) + { + if (!mover.MovingTo.IsValid(EntityManager)) + { + RemCompDeferred(pullableEnt); + continue; + } + + if (pullable.Puller is not {Valid: true} puller) + continue; + + var pullerXform = _xformQuery.Get(puller); + var pullerPosition = TransformSystem.GetMapCoordinates(pullerXform); + + var movingTo = mover.MovingTo.ToMap(EntityManager, TransformSystem); + + if (movingTo.MapId != pullerPosition.MapId) + { + RemCompDeferred(pullableEnt); + continue; + } + + if (!TryComp(pullableEnt, out var physics) || + physics.BodyType == BodyType.Static || + movingTo.MapId != pullableXform.MapID) + { + RemCompDeferred(pullableEnt); + continue; + } + + var movingPosition = movingTo.Position; + var ownerPosition = TransformSystem.GetWorldPosition(pullableXform); + + var diff = movingPosition - ownerPosition; + var diffLength = diff.Length(); + + if (diffLength < MaximumSettleDistance && physics.LinearVelocity.Length() < MaximumSettleVelocity) + { + PhysicsSystem.SetLinearVelocity(pullableEnt, Vector2.Zero, body: physics); + RemCompDeferred(pullableEnt); + continue; + } + + var impulseModifierLerp = Math.Min(1.0f, Math.Max(0.0f, (physics.Mass - AccelModifierLowMass) / (AccelModifierHighMass - AccelModifierLowMass))); + var impulseModifier = MathHelper.Lerp(AccelModifierLow, AccelModifierHigh, impulseModifierLerp); + var multiplier = diffLength < 1 ? impulseModifier * diffLength : impulseModifier; + // Note the implication that the real rules of physics don't apply to pulling control. + var accel = diff.Normalized() * multiplier; + // Now for the part where velocity gets shutdown... + if (diffLength < SettleShutdownDistance && physics.LinearVelocity.Length() >= SettleMinimumShutdownVelocity) + { + // Shutdown velocity increases as we get closer to centre + var scaling = (SettleShutdownDistance - diffLength) / SettleShutdownDistance; + accel -= physics.LinearVelocity * SettleShutdownMultiplier * scaling; + } + + PhysicsSystem.WakeBody(pullableEnt, body: physics); + + var impulse = accel * physics.Mass * frameTime; + PhysicsSystem.ApplyLinearImpulse(pullableEnt, impulse, body: physics); + + // if the puller is weightless or can't move, then we apply the inverse impulse (Newton's third law). + // doing it under gravity produces an unsatisfying wiggling when pulling. + // If player can't move, assume they are on a chair and we need to prevent pull-moving. + if (_gravity.IsWeightless(puller) && pullerXform.Comp.GridUid == null || !_actionBlockerSystem.CanMove(puller)) + { + PhysicsSystem.WakeBody(puller); + PhysicsSystem.ApplyLinearImpulse(puller, -impulse); + } + } + + // Cleanup PullMover + var moverQuery = EntityQueryEnumerator(); + + while (moverQuery.MoveNext(out var uid, out _, out var puller)) + { + if (!HasComp(puller.Pulling)) + { + RemCompDeferred(uid); + continue; + } + } + } +} diff --git a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs index 061ec13ed2ab..f47ae32f90bf 100644 --- a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs +++ b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs @@ -18,7 +18,7 @@ public sealed partial class PullerComponent : Component /// Next time the puller can throw what is being pulled. /// Used to avoid spamming it for infinite spin + velocity. /// - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, Access(Other = AccessPermissions.ReadWriteExecute)] public TimeSpan NextThrow; [DataField] diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index 3de71172c72f..225810daed2f 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -43,8 +43,6 @@ public sealed class PullingSystem : EntitySystem [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SharedTransformSystem _xformSys = default!; - [Dependency] private readonly ThrowingSystem _throwing = default!; public override void Initialize() { @@ -66,7 +64,6 @@ public override void Initialize() SubscribeLocalEvent(OnDropHandItems); CommandBinds.Builder - .Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(OnRequestMovePulledObject)) .Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(OnReleasePulledObject, handle: false)) .Register(); } @@ -245,47 +242,6 @@ public bool IsPulled(EntityUid uid, PullableComponent? component = null) return Resolve(uid, ref component, false) && component.BeingPulled; } - private bool OnRequestMovePulledObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid) - { - if (session?.AttachedEntity is not { } player || - !player.IsValid()) - { - return false; - } - - if (!TryComp(player, out var pullerComp)) - return false; - - var pulled = pullerComp.Pulling; - - if (!HasComp(pulled)) - return false; - - if (_containerSystem.IsEntityInContainer(player)) - return false; - - // Cooldown buddy - if (_timing.CurTime < pullerComp.NextThrow) - return false; - - pullerComp.NextThrow = _timing.CurTime + pullerComp.ThrowCooldown; - - // Cap the distance - const float range = 2f; - var fromUserCoords = coords.WithEntityId(player, EntityManager); - var userCoords = new EntityCoordinates(player, Vector2.Zero); - - if (!userCoords.InRange(EntityManager, _xformSys, fromUserCoords, range)) - { - var userDirection = fromUserCoords.Position - userCoords.Position; - fromUserCoords = userCoords.Offset(userDirection.Normalized() * range); - } - - Dirty(player, pullerComp); - _throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false, doSpin: false); - return false; - } - public bool IsPulling(EntityUid puller, PullerComponent? component = null) { return Resolve(puller, ref component, false) && component.Pulling != null; From 642bcffbaf7a25a95454b2052282cb7980fd0491 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 27 May 2024 00:12:24 +0000 Subject: [PATCH 069/568] 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 80f1972d5601..31c41b9ed5b9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: SlamBamActionman - changes: - - message: Moths eating lizard plushies will now start to Weh! - type: Tweak - id: 6125 - time: '2024-03-11T23:05:25.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26003 - author: Ubaser changes: - message: Unpressurized areas now deal heat damage along with blunt. @@ -3870,3 +3863,10 @@ id: 6624 time: '2024-05-26T14:48:34.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28301 +- author: metalgearsloth + changes: + - message: Selectively revert pulling throwing for the short-term. + type: Tweak + id: 6625 + time: '2024-05-27T00:11:17.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28126 From 5a59c6f262eaaf4fd5da12dbef4f88f89898fe45 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Mon, 27 May 2024 18:04:34 +1000 Subject: [PATCH 070/568] Add loadout group check (#28311) Forgot to add it back in one of the rewrites. --- Content.Shared/Preferences/Loadouts/RoleLoadout.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Content.Shared/Preferences/Loadouts/RoleLoadout.cs b/Content.Shared/Preferences/Loadouts/RoleLoadout.cs index 40e13f0edfa4..479974893ceb 100644 --- a/Content.Shared/Preferences/Loadouts/RoleLoadout.cs +++ b/Content.Shared/Preferences/Loadouts/RoleLoadout.cs @@ -78,12 +78,20 @@ public void EnsureValid(HumanoidCharacterProfile profile, ICommonSession session { var loadout = loadouts[i]; + // Old prototype or otherwise invalid. if (!protoManager.TryIndex(loadout.Prototype, out var loadoutProto)) { loadouts.RemoveAt(i); continue; } + // Malicious client maybe, check the group even has it. + if (!groupProto.Loadouts.Contains(loadout.Prototype)) + { + loadouts.RemoveAt(i); + continue; + } + // Validate the loadout can be applied (e.g. points). if (!IsValid(profile, session, loadout.Prototype, collection, out _)) { From 224af2af0eefce38d2ab1c3e87b01fbe6d913b20 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Mon, 27 May 2024 11:50:05 -0400 Subject: [PATCH 071/568] fix mirror server crashes (#28318) --- .../MagicMirror/SharedMagicMirrorSystem.cs | 25 +++++++++++++------ .../Entities/Structures/Wallmounts/mirror.yml | 1 + 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs b/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs index ea96d504c6e6..789b484cfd3e 100644 --- a/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs +++ b/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs @@ -4,7 +4,6 @@ using Content.Shared.Interaction; using Content.Shared.UserInterface; using Robust.Shared.Serialization; -using Robust.Shared.Utility; namespace Content.Shared.MagicMirror; @@ -18,6 +17,7 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnMagicMirrorInteract); SubscribeLocalEvent(OnBeforeUIOpen); + SubscribeLocalEvent(OnAttemptOpenUI); SubscribeLocalEvent(OnMirrorRangeCheck); } @@ -26,10 +26,8 @@ private void OnMagicMirrorInteract(Entity mirror, ref Afte if (!args.CanReach || args.Target == null) return; - if (!UISystem.TryOpenUi(mirror.Owner, MagicMirrorUiKey.Key, args.User)) - return; - UpdateInterface(mirror, args.Target.Value, mirror); + UISystem.TryOpenUi(mirror.Owner, MagicMirrorUiKey.Key, args.User); } private void OnMirrorRangeCheck(EntityUid uid, MagicMirrorComponent component, ref BoundUserInterfaceCheckRangeEvent args) @@ -37,17 +35,27 @@ private void OnMirrorRangeCheck(EntityUid uid, MagicMirrorComponent component, r if (args.Result == BoundUserInterfaceRangeResult.Fail) return; - DebugTools.Assert(component.Target != null && Exists(component.Target)); + if (component.Target == null || !Exists(component.Target)) + { + component.Target = null; + args.Result = BoundUserInterfaceRangeResult.Fail; + return; + } if (!_interaction.InRangeUnobstructed(uid, component.Target.Value)) args.Result = BoundUserInterfaceRangeResult.Fail; } - private void OnBeforeUIOpen(Entity ent, ref BeforeActivatableUIOpenEvent args) + private void OnAttemptOpenUI(EntityUid uid, MagicMirrorComponent component, ref ActivatableUIOpenAttemptEvent args) { - if (args.User != ent.Comp.Target && ent.Comp.Target != null) - return; + var user = component.Target ?? args.User; + if (!HasComp(user)) + args.Cancel(); + } + + private void OnBeforeUIOpen(Entity ent, ref BeforeActivatableUIOpenEvent args) + { UpdateInterface(ent, args.User, ent); } @@ -55,6 +63,7 @@ protected void UpdateInterface(EntityUid mirrorUid, EntityUid targetUid, MagicMi { if (!TryComp(targetUid, out var humanoid)) return; + component.Target ??= targetUid; var hair = humanoid.MarkingSet.TryGetCategory(MarkingCategories.Hair, out var hairMarkings) diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/mirror.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/mirror.yml index 1fe3318ef558..27b8390add29 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/mirror.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/mirror.yml @@ -19,6 +19,7 @@ changeSlotTime: 0 - type: ActivatableUI key: enum.MagicMirrorUiKey.Key + singleUser: true - type: UserInterface interfaces: enum.MagicMirrorUiKey.Key: From cf8fcb170a185c06c966c1a1dcf7eec7ada52745 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 27 May 2024 23:52:23 +0200 Subject: [PATCH 072/568] Remove bogus C# finalizers (#28315) Begging people to learn how this programming language works before throwing random syntax into a file. None of these finalizers ever worked. I also checked whether they were memory leaks and needed *proper* shutdown logic, but they're all instantiated-once UI controls that last for the entire lifetime of the program so it's probably fine. --- Content.Client/Interaction/DragDropHelper.cs | 5 ----- .../Systems/Actions/Controls/ActionButtonContainer.cs | 5 ----- .../Systems/Inventory/Controls/ItemSlotButtonContainer.cs | 5 ----- 3 files changed, 15 deletions(-) diff --git a/Content.Client/Interaction/DragDropHelper.cs b/Content.Client/Interaction/DragDropHelper.cs index abe35bf6d9a8..e453dfd74bff 100644 --- a/Content.Client/Interaction/DragDropHelper.cs +++ b/Content.Client/Interaction/DragDropHelper.cs @@ -73,11 +73,6 @@ public DragDropHelper(OnBeginDrag onBeginDrag, OnContinueDrag onContinueDrag, On _cfg.OnValueChanged(CCVars.DragDropDeadZone, SetDeadZone, true); } - ~DragDropHelper() - { - _cfg.UnsubValueChanged(CCVars.DragDropDeadZone, SetDeadZone); - } - /// /// Tell the helper that the mouse button was pressed down on /// a target, thus a drag has the possibility to begin for this target. diff --git a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs index c5f8adbdea3b..38c08dc47216 100644 --- a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs +++ b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs @@ -119,9 +119,4 @@ public IEnumerable GetButtons() yield return button; } } - - ~ActionButtonContainer() - { - UserInterfaceManager.GetUIController().RemoveActionContainer(); - } } diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotButtonContainer.cs b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotButtonContainer.cs index 78c3b40adac8..7a027fc4f810 100644 --- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotButtonContainer.cs +++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotButtonContainer.cs @@ -22,9 +22,4 @@ public ItemSlotButtonContainer() { _inventoryController = UserInterfaceManager.GetUIController(); } - - ~ItemSlotButtonContainer() - { - _inventoryController.RemoveSlotGroup(SlotGroup); - } } From 7309704ace2017e701e1b566359d012067c4107a Mon Sep 17 00:00:00 2001 From: Flareguy <78941145+Flareguy@users.noreply.github.com> Date: Mon, 27 May 2024 16:59:10 -0500 Subject: [PATCH 073/568] Cleans up some entity-related spawnmenu stuff (#28234) * cleans up a lot of stuff in the spawnmenu * skibidi dode * spawners update * Revert "spawners update" This reverts commit bc27d9f556b29f6fb1f89cebbe0ac37e28319fd0. --- .../Prototypes/Entities/Effects/mobspawn.yml | 4 + .../Prototypes/Entities/Effects/wallspawn.yml | 9 ++ .../Devices/Circuitboards/Machine/cannons.yml | 92 +++++++++++++++++++ .../Circuitboards/Machine/production.yml | 86 ----------------- .../Entities/Structures/Shuttles/cannons.yml | 9 +- 5 files changed, 113 insertions(+), 87 deletions(-) create mode 100644 Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml diff --git a/Resources/Prototypes/Entities/Effects/mobspawn.yml b/Resources/Prototypes/Entities/Effects/mobspawn.yml index fb59aa3fc466..2fad68d7f2c8 100644 --- a/Resources/Prototypes/Entities/Effects/mobspawn.yml +++ b/Resources/Prototypes/Entities/Effects/mobspawn.yml @@ -1,6 +1,7 @@ - type: entity id: MobSpawnCrabQuartz name: mobspawner quartzcrab + noSpawn: True components: - type: Transform anchored: True @@ -30,6 +31,7 @@ id: MobSpawnCrabIron parent: MobSpawnCrabQuartz name: mobspawner ironcrab + noSpawn: True components: - type: Sprite sprite: /Textures/Effects/mobspawn.rsi @@ -41,6 +43,7 @@ id: MobSpawnCrabSilver parent: MobSpawnCrabQuartz name: mobspawner silvercrab + noSpawn: True components: - type: Sprite sprite: /Textures/Effects/mobspawn.rsi @@ -52,6 +55,7 @@ id: MobSpawnCrabUranium parent: MobSpawnCrabQuartz name: mobspawner uraniumcrab + noSpawn: True components: - type: Sprite sprite: /Textures/Effects/mobspawn.rsi diff --git a/Resources/Prototypes/Entities/Effects/wallspawn.yml b/Resources/Prototypes/Entities/Effects/wallspawn.yml index f1bd236a8abc..7213763c4690 100644 --- a/Resources/Prototypes/Entities/Effects/wallspawn.yml +++ b/Resources/Prototypes/Entities/Effects/wallspawn.yml @@ -1,5 +1,6 @@ - type: entity id: WallSpawnAsteroid + noSpawn: True components: - type: Transform anchored: True @@ -28,6 +29,7 @@ - type: entity id: WallSpawnAsteroidUraniumCrab parent: WallSpawnAsteroid + noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockUraniumCrab @@ -35,6 +37,7 @@ - type: entity id: WallSpawnAsteroidUranium parent: WallSpawnAsteroid + noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockUranium @@ -42,6 +45,7 @@ - type: entity id: WallSpawnAsteroidQuartzCrab parent: WallSpawnAsteroid + noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockQuartzCrab @@ -49,6 +53,7 @@ - type: entity id: WallSpawnAsteroidQuartz parent: WallSpawnAsteroid + noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockQuartz @@ -56,6 +61,7 @@ - type: entity id: WallSpawnAsteroidSilverCrab parent: WallSpawnAsteroid + noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockSilverCrab @@ -63,6 +69,7 @@ - type: entity id: WallSpawnAsteroidSilver parent: WallSpawnAsteroid + noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockSilver @@ -70,6 +77,7 @@ - type: entity id: WallSpawnAsteroidIronCrab parent: WallSpawnAsteroid + noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockTinCrab @@ -77,6 +85,7 @@ - type: entity id: WallSpawnAsteroidIron parent: WallSpawnAsteroid + noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockTin \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml new file mode 100644 index 000000000000..6db7f4b385ed --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml @@ -0,0 +1,92 @@ +# Most of these have DO NOT MAP, since stations are completely unequipped to deal with ship combat + these are basically placeholder. + +- type: entity + id: ShuttleGunSvalinnMachineGunCircuitboard + parent: BaseMachineCircuitboard + name: LSE-400c "Svalinn machine gun" machine board + description: A machine printed circuit board for an LSE-400c "Svalinn machine gun" + suffix: DO NOT MAP, Machine Board + components: + - type: Sprite + state: security + - type: MachineBoard + prototype: ShuttleGunSvalinnMachineGun + requirements: + MatterBin: 2 + Manipulator: 4 + materialRequirements: + Steel: 5 + CableHV: 5 + +- type: entity + id: ShuttleGunPerforatorCircuitboard + parent: BaseMachineCircuitboard + name: LSE-1200c "Perforator" machine board + description: A machine printed circuit board for an LSE-1200c "Perforator" + suffix: DO NOT MAP, Machine Board + components: + - type: Sprite + state: security + - type: MachineBoard + prototype: ShuttleGunPerforator + requirements: + MatterBin: 4 + Manipulator: 6 + materialRequirements: + Steel: 10 + CableHV: 5 + +- type: entity + id: ShuttleGunFriendshipCircuitboard + parent: BaseMachineCircuitboard + name: EXP-320g "Friendship" machine board + description: A machine printed circuit board for an EXP-320g "Friendship" + suffix: DO NOT MAP, Machine Board + components: + - type: Sprite + state: security + - type: MachineBoard + prototype: ShuttleGunFriendship + requirements: + MatterBin: 3 + Manipulator: 2 + materialRequirements: + Steel: 7 + CableHV: 5 + +- type: entity + id: ShuttleGunDusterCircuitboard + parent: BaseMachineCircuitboard + name: EXP-2100g "Duster" machine board + description: A machine printed circuit board for an EXP-2100g "Duster" + suffix: DO NOT MAP, Machine Board + components: + - type: Sprite + state: security + - type: MachineBoard + prototype: ShuttleGunDuster + requirements: + MatterBin: 6 + Manipulator: 4 + materialRequirements: + Steel: 10 + CableHV: 5 + Uranium: 2 + +- type: entity + id: ShuttleGunKineticCircuitboard + parent: BaseMachineCircuitboard + name: PTK-800 "Matter Dematerializer" machine board + description: A machine printed circuit board for an PTK-800 "Matter Dematerializer" + suffix: DO NOT MAP, Machine Board + components: + - type: Sprite + state: security + - type: MachineBoard + prototype: ShuttleGunKinetic + requirements: + MatterBin: 2 + Manipulator: 3 + materialRequirements: + Steel: 5 + CableHV: 2 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 7f25d00a05f4..587e56865b4d 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -1277,92 +1277,6 @@ CableHV: 5 Uranium: 2 -- type: entity - id: ShuttleGunSvalinnMachineGunCircuitboard - parent: BaseMachineCircuitboard - name: LSE-400c "Svalinn machine gun" machine board - description: A machine printed circuit board for an LSE-400c "Svalinn machine gun" - components: - - type: Sprite - state: security - - type: MachineBoard - prototype: ShuttleGunSvalinnMachineGun - requirements: - MatterBin: 2 - Manipulator: 4 - materialRequirements: - Steel: 5 - CableHV: 5 - -- type: entity - id: ShuttleGunPerforatorCircuitboard - parent: BaseMachineCircuitboard - name: LSE-1200c "Perforator" machine board - description: A machine printed circuit board for an LSE-1200c "Perforator" - components: - - type: Sprite - state: security - - type: MachineBoard - prototype: ShuttleGunPerforator - requirements: - MatterBin: 4 - Manipulator: 6 - materialRequirements: - Steel: 10 - CableHV: 5 - -- type: entity - id: ShuttleGunFriendshipCircuitboard - parent: BaseMachineCircuitboard - name: EXP-320g "Friendship" machine board - description: A machine printed circuit board for an EXP-320g "Friendship" - components: - - type: Sprite - state: security - - type: MachineBoard - prototype: ShuttleGunFriendship - requirements: - MatterBin: 3 - Manipulator: 2 - materialRequirements: - Steel: 7 - CableHV: 5 - -- type: entity - id: ShuttleGunDusterCircuitboard - parent: BaseMachineCircuitboard - name: EXP-2100g "Duster" machine board - description: A machine printed circuit board for an EXP-2100g "Duster" - components: - - type: Sprite - state: security - - type: MachineBoard - prototype: ShuttleGunDuster - requirements: - MatterBin: 6 - Manipulator: 4 - materialRequirements: - Steel: 10 - CableHV: 5 - Uranium: 2 - -- type: entity - id: ShuttleGunKineticCircuitboard - parent: BaseMachineCircuitboard - name: PTK-800 "Matter Dematerializer" machine board - description: A machine printed circuit board for an PTK-800 "Matter Dematerializer" - components: - - type: Sprite - state: security - - type: MachineBoard - prototype: ShuttleGunKinetic - requirements: - MatterBin: 2 - Manipulator: 3 - materialRequirements: - Steel: 5 - CableHV: 2 - - type: entity parent: BaseMachineCircuitboard id: ReagentGrinderIndustrialMachineCircuitboard diff --git a/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml b/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml index 3368e2d7ee0b..08eff29c81b8 100644 --- a/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml +++ b/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml @@ -1,3 +1,5 @@ +# Most of these have DO NOT MAP, since stations are completely unequipped to deal with ship combat + these are basically placeholder. + - type: entity id: ShuttleGunBase name: shittle gun @@ -58,7 +60,8 @@ id: ShuttleGunSvalinnMachineGun parent: [ ShuttleGunBase, ConstructibleMachine] name: LSE-400c "Svalinn machine gun" - description: Basic stationary laser unit. Effective against live targets and electronics. Uses regular power cells to fire, and has an extremely high rate of fire + description: Basic stationary laser unit. Effective against live targets and electronics. Uses regular power cells to fire, and has an extremely high rate of fire. + suffix: DO NOT MAP components: - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/laser.rsi @@ -112,6 +115,7 @@ parent: [ ShuttleGunBase, ConstructibleMachine] name: LSE-1200c "Perforator" description: Advanced stationary laser unit. Annihilates electronics and is extremely dangerous to health! Uses the power cage to fire. + suffix: DO NOT MAP components: - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/laser.rsi @@ -167,6 +171,7 @@ parent: [ShuttleGunBase, ConstructibleMachine] name: EXP-320g "Friendship" description: A small stationary grenade launcher that holds 2 grenades. + suffix: DO NOT MAP components: - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/launcher.rsi @@ -220,6 +225,7 @@ parent: [ShuttleGunBase, ConstructibleMachine] name: EXP-2100g "Duster" description: A powerful stationary grenade launcher. A cartridge is required for use. + suffix: DO NOT MAP components: - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/launcher.rsi @@ -317,6 +323,7 @@ parent: [ ShuttleGunBase, ConstructibleMachine] name: PTK-800 "Matter Dematerializer" description: Salvage stationary mining turret. Gradually accumulates charges on its own, extremely effective for asteroid excavation. + suffix: DO NOT MAP components: - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/kinetic.rsi From 984af3584f3c54037c0571db6fe35098884a5b00 Mon Sep 17 00:00:00 2001 From: alex-georgeff <54858069+taurie@users.noreply.github.com> Date: Mon, 27 May 2024 18:00:19 -0400 Subject: [PATCH 074/568] Rewords plant/effect descriptions for clarity (#28156) (#28169) * Rewords plant/effect descriptions for clarity (#28156) * Forgot Robust Harvest. * Update Resources/Locale/en-US/guidebook/chemistry/effects.ftl --------- Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --- Resources/Locale/en-US/guidebook/chemistry/effects.ftl | 6 +++--- Resources/Locale/en-US/reagents/meta/botany.ftl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Resources/Locale/en-US/guidebook/chemistry/effects.ftl b/Resources/Locale/en-US/guidebook/chemistry/effects.ftl index 5579c95e9df6..1dacebd1349e 100644 --- a/Resources/Locale/en-US/guidebook/chemistry/effects.ftl +++ b/Resources/Locale/en-US/guidebook/chemistry/effects.ftl @@ -365,9 +365,9 @@ reagent-effect-guidebook-plant-cryoxadone = reagent-effect-guidebook-plant-phalanximine = { $chance -> - [1] Makes - *[other] make - } a plant not viable due to mutation viable again + [1] Restores + *[other] restore + } viability to a plant rendered nonviable by a mutation reagent-effect-guidebook-plant-diethylamine = { $chance -> diff --git a/Resources/Locale/en-US/reagents/meta/botany.ftl b/Resources/Locale/en-US/reagents/meta/botany.ftl index 912ded5cf292..c7101c232795 100644 --- a/Resources/Locale/en-US/reagents/meta/botany.ftl +++ b/Resources/Locale/en-US/reagents/meta/botany.ftl @@ -11,7 +11,7 @@ reagent-name-plant-b-gone = plant-B-gone reagent-desc-plant-b-gone = A harmful toxic mixture to kill plantlife. Very effective against kudzu. reagent-name-robust-harvest = robust harvest -reagent-desc-robust-harvest = A highly effective fertilizer, with a limited potency-boosting effect on plants. Be careful with it's usage since using too much has a chance to reduce the plant yield. It has a positive effect on dionas. +reagent-desc-robust-harvest = A highly effective fertilizer with a limited potency-boosting effect on plants. Use it cautiously, as excessive application can reduce plant yield. It has a particularly beneficial effect on dionas. reagent-name-weed-killer = weed killer reagent-desc-weed-killer = A mixture that targets weeds. Very effective against kudzu. While useful it slowly poisons plants with toxins, be careful when using it. From 44b93e68ee0d6eb98933b4c8520e7e8e966cce35 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Mon, 27 May 2024 18:37:27 -0400 Subject: [PATCH 075/568] Prevent stacking pipes (#28308) * prevent stacking pipes * access * notafet review * notafet review pt. 2 * not the actual fix --- .../PipeRestrictOverlapComponent.cs | 9 ++ .../PipeRestrictOverlapSystem.cs | 123 ++++++++++++++++++ .../ConstructionSystem.Initial.cs | 39 +++--- .../NodeContainer/Nodes/PipeNode.cs | 16 +-- .../conditions/no-unstackable-in-tile.ftl | 1 + .../Structures/Piping/Atmospherics/binary.yml | 1 + .../Structures/Piping/Atmospherics/pipes.yml | 1 + .../Structures/Piping/Atmospherics/unary.yml | 2 + .../Structures/Power/Generation/teg.yml | 1 + 9 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 Content.Server/Atmos/Components/PipeRestrictOverlapComponent.cs create mode 100644 Content.Server/Atmos/EntitySystems/PipeRestrictOverlapSystem.cs diff --git a/Content.Server/Atmos/Components/PipeRestrictOverlapComponent.cs b/Content.Server/Atmos/Components/PipeRestrictOverlapComponent.cs new file mode 100644 index 000000000000..49e1a8c94d38 --- /dev/null +++ b/Content.Server/Atmos/Components/PipeRestrictOverlapComponent.cs @@ -0,0 +1,9 @@ +using Content.Server.Atmos.EntitySystems; + +namespace Content.Server.Atmos.Components; + +/// +/// This is used for restricting anchoring pipes so that they do not overlap. +/// +[RegisterComponent, Access(typeof(PipeRestrictOverlapSystem))] +public sealed partial class PipeRestrictOverlapComponent : Component; diff --git a/Content.Server/Atmos/EntitySystems/PipeRestrictOverlapSystem.cs b/Content.Server/Atmos/EntitySystems/PipeRestrictOverlapSystem.cs new file mode 100644 index 000000000000..c2ff87ca79c3 --- /dev/null +++ b/Content.Server/Atmos/EntitySystems/PipeRestrictOverlapSystem.cs @@ -0,0 +1,123 @@ +using System.Linq; +using Content.Server.Atmos.Components; +using Content.Server.NodeContainer; +using Content.Server.NodeContainer.Nodes; +using Content.Server.Popups; +using Content.Shared.Atmos; +using Content.Shared.Construction.Components; +using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Shared.Map.Components; + +namespace Content.Server.Atmos.EntitySystems; + +/// +/// This handles restricting pipe-based entities from overlapping outlets/inlets with other entities. +/// +public sealed class PipeRestrictOverlapSystem : EntitySystem +{ + [Dependency] private readonly MapSystem _map = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly TransformSystem _xform = default!; + + private readonly List _anchoredEntities = new(); + private EntityQuery _nodeContainerQuery; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnAnchorStateChanged); + SubscribeLocalEvent(OnAnchorAttempt); + + _nodeContainerQuery = GetEntityQuery(); + } + + private void OnAnchorStateChanged(Entity ent, ref AnchorStateChangedEvent args) + { + if (!args.Anchored) + return; + + if (HasComp(ent) && CheckOverlap(ent)) + { + _popup.PopupEntity(Loc.GetString("pipe-restrict-overlap-popup-blocked", ("pipe", ent.Owner)), ent); + _xform.Unanchor(ent, Transform(ent)); + } + } + + private void OnAnchorAttempt(Entity ent, ref AnchorAttemptEvent args) + { + if (args.Cancelled) + return; + + if (!_nodeContainerQuery.TryComp(ent, out var node)) + return; + + var xform = Transform(ent); + if (CheckOverlap((ent, node, xform))) + { + _popup.PopupEntity(Loc.GetString("pipe-restrict-overlap-popup-blocked", ("pipe", ent.Owner)), ent, args.User); + args.Cancel(); + } + } + + [PublicAPI] + public bool CheckOverlap(EntityUid uid) + { + if (!_nodeContainerQuery.TryComp(uid, out var node)) + return false; + + return CheckOverlap((uid, node, Transform(uid))); + } + + public bool CheckOverlap(Entity ent) + { + if (ent.Comp2.GridUid is not { } grid || !TryComp(grid, out var gridComp)) + return false; + + var indices = _map.TileIndicesFor(grid, gridComp, ent.Comp2.Coordinates); + _anchoredEntities.Clear(); + _map.GetAnchoredEntities((grid, gridComp), indices, _anchoredEntities); + + foreach (var otherEnt in _anchoredEntities) + { + // this should never actually happen but just for safety + if (otherEnt == ent.Owner) + continue; + + if (!_nodeContainerQuery.TryComp(otherEnt, out var otherComp)) + continue; + + if (PipeNodesOverlap(ent, (otherEnt, otherComp, Transform(otherEnt)))) + return true; + } + + return false; + } + + public bool PipeNodesOverlap(Entity ent, Entity other) + { + var entDirs = GetAllDirections(ent).ToList(); + var otherDirs = GetAllDirections(other).ToList(); + + foreach (var dir in entDirs) + { + foreach (var otherDir in otherDirs) + { + if ((dir & otherDir) != 0) + return true; + } + } + + return false; + + IEnumerable GetAllDirections(Entity pipe) + { + foreach (var node in pipe.Comp1.Nodes.Values) + { + // we need to rotate the pipe manually like this because the rotation doesn't update for pipes that are unanchored. + if (node is PipeNode pipeNode) + yield return pipeNode.OriginalPipeDirection.RotatePipeDirection(pipe.Comp2.LocalRotation); + } + } + } +} diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index 17ed5c90f4d4..04d3722c66cb 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -15,6 +15,7 @@ using Content.Shared.Inventory; using Content.Shared.Storage; using Robust.Shared.Containers; +using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Timing; @@ -91,7 +92,14 @@ private IEnumerable EnumerateNearby(EntityUid user) } // LEGACY CODE. See warning at the top of the file! - private async Task Construct(EntityUid user, string materialContainer, ConstructionGraphPrototype graph, ConstructionGraphEdge edge, ConstructionGraphNode targetNode) + private async Task Construct( + EntityUid user, + string materialContainer, + ConstructionGraphPrototype graph, + ConstructionGraphEdge edge, + ConstructionGraphNode targetNode, + EntityCoordinates coords, + Angle angle = default) { // We need a place to hold our construction items! var container = _container.EnsureContainer(user, materialContainer, out var existed); @@ -261,7 +269,7 @@ void ShutdownContainers() } var newEntityProto = graph.Nodes[edge.Target].Entity.GetId(null, user, new(EntityManager)); - var newEntity = EntityManager.SpawnEntity(newEntityProto, EntityManager.GetComponent(user).Coordinates); + var newEntity = Spawn(newEntityProto, _transformSystem.ToMapCoordinates(coords), rotation: angle); if (!TryComp(newEntity, out ConstructionComponent? construction)) { @@ -376,7 +384,13 @@ public async Task TryStartItemConstruction(string prototype, EntityUid use } } - if (await Construct(user, "item_construction", constructionGraph, edge, targetNode) is not { Valid: true } item) + if (await Construct( + user, + "item_construction", + constructionGraph, + edge, + targetNode, + Transform(user).Coordinates) is not { Valid: true } item) return false; // Just in case this is a stack, attempt to merge it. If it isn't a stack, this will just normally pick up @@ -511,23 +525,18 @@ void Cleanup() return; } - if (await Construct(user, (ev.Ack + constructionPrototype.GetHashCode()).ToString(), constructionGraph, - edge, targetNode) is not {Valid: true} structure) + if (await Construct(user, + (ev.Ack + constructionPrototype.GetHashCode()).ToString(), + constructionGraph, + edge, + targetNode, + GetCoordinates(ev.Location), + constructionPrototype.CanRotate ? ev.Angle : Angle.Zero) is not {Valid: true} structure) { Cleanup(); return; } - // We do this to be able to move the construction to its proper position in case it's anchored... - // Oh wow transform anchoring is amazing wow I love it!!!! - // ikr - var xform = Transform(structure); - var wasAnchored = xform.Anchored; - xform.Anchored = false; - xform.Coordinates = GetCoordinates(ev.Location); - xform.LocalRotation = constructionPrototype.CanRotate ? ev.Angle : Angle.Zero; - xform.Anchored = wasAnchored; - RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack, GetNetEntity(structure))); _adminLogger.Add(LogType.Construction, LogImpact.Low, $"{ToPrettyString(user):player} has turned a {ev.PrototypeName} construction ghost into {ToPrettyString(structure)} at {Transform(structure).Coordinates}"); Cleanup(); diff --git a/Content.Server/NodeContainer/Nodes/PipeNode.cs b/Content.Server/NodeContainer/Nodes/PipeNode.cs index 861f3eea98d2..31ee5712493d 100644 --- a/Content.Server/NodeContainer/Nodes/PipeNode.cs +++ b/Content.Server/NodeContainer/Nodes/PipeNode.cs @@ -20,7 +20,7 @@ public partial class PipeNode : Node, IGasMixtureHolder, IRotatableNode /// The directions in which this pipe can connect to other pipes around it. /// [DataField("pipeDirection")] - private PipeDirection _originalPipeDirection; + public PipeDirection OriginalPipeDirection; /// /// The *current* pipe directions (accounting for rotation) @@ -110,26 +110,26 @@ public override void Initialize(EntityUid owner, IEntityManager entMan) return; var xform = entMan.GetComponent(owner); - CurrentPipeDirection = _originalPipeDirection.RotatePipeDirection(xform.LocalRotation); + CurrentPipeDirection = OriginalPipeDirection.RotatePipeDirection(xform.LocalRotation); } bool IRotatableNode.RotateNode(in MoveEvent ev) { - if (_originalPipeDirection == PipeDirection.Fourway) + if (OriginalPipeDirection == PipeDirection.Fourway) return false; // update valid pipe direction if (!RotationsEnabled) { - if (CurrentPipeDirection == _originalPipeDirection) + if (CurrentPipeDirection == OriginalPipeDirection) return false; - CurrentPipeDirection = _originalPipeDirection; + CurrentPipeDirection = OriginalPipeDirection; return true; } var oldDirection = CurrentPipeDirection; - CurrentPipeDirection = _originalPipeDirection.RotatePipeDirection(ev.NewRotation); + CurrentPipeDirection = OriginalPipeDirection.RotatePipeDirection(ev.NewRotation); return oldDirection != CurrentPipeDirection; } @@ -142,12 +142,12 @@ public override void OnAnchorStateChanged(IEntityManager entityManager, bool anc if (!RotationsEnabled) { - CurrentPipeDirection = _originalPipeDirection; + CurrentPipeDirection = OriginalPipeDirection; return; } var xform = entityManager.GetComponent(Owner); - CurrentPipeDirection = _originalPipeDirection.RotatePipeDirection(xform.LocalRotation); + CurrentPipeDirection = OriginalPipeDirection.RotatePipeDirection(xform.LocalRotation); } public override IEnumerable GetReachableNodes(TransformComponent xform, diff --git a/Resources/Locale/en-US/construction/conditions/no-unstackable-in-tile.ftl b/Resources/Locale/en-US/construction/conditions/no-unstackable-in-tile.ftl index 37ce0de9e8e7..715825e801c8 100644 --- a/Resources/Locale/en-US/construction/conditions/no-unstackable-in-tile.ftl +++ b/Resources/Locale/en-US/construction/conditions/no-unstackable-in-tile.ftl @@ -1 +1,2 @@ construction-step-condition-no-unstackable-in-tile = You cannot make a stack of similar devices. +pipe-restrict-overlap-popup-blocked = { CAPITALIZE(THE($pipe))} doesn't fit over the other pipes! diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml index fa5804c64525..213b0b893d94 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml @@ -358,6 +358,7 @@ - type: PipeColorVisuals - type: Rotatable - type: GasRecycler + - type: PipeRestrictOverlap - type: NodeContainer nodes: inlet: diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml index 0025fc5ae1b3..c2fc4e0565b0 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml @@ -51,6 +51,7 @@ - type: Appearance - type: PipeColorVisuals - type: NodeContainer + - type: PipeRestrictOverlap - type: AtmosUnsafeUnanchor - type: AtmosPipeColor - type: Tag diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml index c0664602b498..d301f43c7887 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml @@ -246,6 +246,7 @@ key: enum.ThermomachineUiKey.Key - type: WiresPanel - type: WiresVisuals + - type: PipeRestrictOverlap - type: NodeContainer nodes: pipe: @@ -420,6 +421,7 @@ - type: GasCondenser - type: AtmosPipeColor - type: AtmosDevice + - type: PipeRestrictOverlap - type: ApcPowerReceiver powerLoad: 10000 - type: Machine diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml index 9a378c26a449..78d979ab8eba 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml @@ -176,6 +176,7 @@ nodeGroupID: Teg - type: AtmosUnsafeUnanchor + - type: PipeRestrictOverlap - type: TegCirculator - type: StealTarget stealGroup: Teg From ab3abfde17c22b507f6d28c40206b832b89185db Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 27 May 2024 22:38:34 +0000 Subject: [PATCH 076/568] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 31c41b9ed5b9..e45f9d83ccfe 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Ubaser - changes: - - message: Unpressurized areas now deal heat damage along with blunt. - type: Tweak - id: 6126 - time: '2024-03-12T02:38:40.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25770 - author: nikthechampiongr changes: - message: Recyclers now leave logs when they gib people. @@ -3870,3 +3863,12 @@ id: 6625 time: '2024-05-27T00:11:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28126 +- author: EmoGarbage404 + changes: + - message: Multiple pipes facing the same direction can no longer be placed on the + same tile. You can still have overlapping straight pipes or corners going in + opposite directions. + type: Fix + id: 6626 + time: '2024-05-27T22:37:27.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28308 From 0805637807776872ec39dec47f51b278b55dde0c Mon Sep 17 00:00:00 2001 From: Emisse <99158783+Emisse@users.noreply.github.com> Date: Mon, 27 May 2024 16:40:00 -0600 Subject: [PATCH 077/568] derotate train atm until i redo its atmos (#28328) webedit webedit webedit --- Resources/Prototypes/Maps/Pools/default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Maps/Pools/default.yml b/Resources/Prototypes/Maps/Pools/default.yml index 245ccce4b7bb..5c6b0eee0d70 100644 --- a/Resources/Prototypes/Maps/Pools/default.yml +++ b/Resources/Prototypes/Maps/Pools/default.yml @@ -15,4 +15,4 @@ - Saltern - Packed - Reach - - Train + #- Train From 5f533e8753a81a543fba843b83e96b505f7e9f2e Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 27 May 2024 22:41:06 +0000 Subject: [PATCH 078/568] 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 e45f9d83ccfe..4dbcc6de133d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: nikthechampiongr - changes: - - message: Recyclers now leave logs when they gib people. - type: Add - - message: People sending a broadcast in the communications console now leave logs. - type: Add - id: 6127 - time: '2024-03-12T10:57:05.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26008 - author: ShadowCommander changes: - message: Fixed arrivals shuttle not docking with the station when it was slowly @@ -3872,3 +3863,10 @@ id: 6626 time: '2024-05-27T22:37:27.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28308 +- author: Emisse + changes: + - message: Derotate Train temporarily + type: Tweak + id: 6627 + time: '2024-05-27T22:40:00.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28328 From 217d081b29f7c15dccfa43d43d5f83121b75370b Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Mon, 27 May 2024 18:43:17 -0400 Subject: [PATCH 079/568] Fix under-selecting antags (#28327) Fix under selecting antags --- .../Antag/AntagSelectionSystem.API.cs | 23 +++++++++++++++---- Content.Server/Antag/AntagSelectionSystem.cs | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Content.Server/Antag/AntagSelectionSystem.API.cs b/Content.Server/Antag/AntagSelectionSystem.API.cs index 3b5855cdf95c..16d83c4f3fc4 100644 --- a/Content.Server/Antag/AntagSelectionSystem.API.cs +++ b/Content.Server/Antag/AntagSelectionSystem.API.cs @@ -52,12 +52,26 @@ public bool TryGetNextAvailableDefinition(Entity ent, /// Gets the number of antagonists that should be present for a given rule based on the provided pool. /// A null pool will simply use the player count. /// - public int GetTargetAntagCount(Entity ent, AntagSelectionPlayerPool? pool = null) + public int GetTargetAntagCount(Entity ent, int? playerCount = null) { var count = 0; foreach (var def in ent.Comp.Definitions) { - count += GetTargetAntagCount(ent, pool, def); + count += GetTargetAntagCount(ent, playerCount, def); + } + + return count; + } + + public int GetTotalPlayerCount(IList pool) + { + var count = 0; + foreach (var session in pool) + { + if (session.Status is SessionStatus.Disconnected or SessionStatus.Zombie) + continue; + + count++; } return count; @@ -67,14 +81,13 @@ public int GetTargetAntagCount(Entity ent, AntagSelecti /// Gets the number of antagonists that should be present for a given antag definition based on the provided pool. /// A null pool will simply use the player count. /// - public int GetTargetAntagCount(Entity ent, AntagSelectionPlayerPool? pool, AntagSelectionDefinition def) + public int GetTargetAntagCount(Entity ent, int? playerCount, AntagSelectionDefinition def) { // TODO ANTAG // make pool non-nullable // Review uses and ensure that people are INTENTIONALLY including players in the lobby if this is a mid-round // antag selection. - var poolSize = pool?.Count ?? _playerManager.Sessions - .Count(s => s.State.Status is not SessionStatus.Disconnected and not SessionStatus.Zombie); + var poolSize = playerCount ?? GetTotalPlayerCount(_playerManager.Sessions); // factor in other definitions' affect on the count. var countOffset = 0; diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index 5b6c891ddfc5..a86611bedb39 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -203,7 +203,7 @@ public void ChooseAntags(Entity ent, IList ent, IList pool, AntagSelectionDefinition def) { var playerPool = GetPlayerPool(ent, pool, def); - var count = GetTargetAntagCount(ent, playerPool, def); + var count = GetTargetAntagCount(ent, GetTotalPlayerCount(pool), def); for (var i = 0; i < count; i++) { From 056097e4815001f6f39e18c0de8ec05df3b23d77 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 27 May 2024 22:44:23 +0000 Subject: [PATCH 080/568] 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 4dbcc6de133d..98ec517a5024 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: ShadowCommander - changes: - - message: Fixed arrivals shuttle not docking with the station when it was slowly - spinning. - type: Fix - id: 6128 - time: '2024-03-12T12:57:35.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26033 - author: liltenhead changes: - message: Buffed the zombie virus to do purely poison damage, and more likely to @@ -3870,3 +3862,10 @@ id: 6627 time: '2024-05-27T22:40:00.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28328 +- author: EmoGarbage404 + changes: + - message: Fixed not enough antags being selected for gamemodes. + type: Fix + id: 6628 + time: '2024-05-27T22:43:17.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28327 From 5ac66306711e5e3424f84946482b33e20f9936ae Mon Sep 17 00:00:00 2001 From: Kara Date: Mon, 27 May 2024 16:09:40 -0700 Subject: [PATCH 081/568] Update engine to 223.2.0 (#28329) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 796abe123055..721408bb3731 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 796abe1230554c31daffbfa6559a0a4bcf50b1af +Subproject commit 721408bb37317e7a66cd34655e068369c47912ac From 4e09743d44d2d81d6f34d11cefcbacb39b6aa9d0 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Mon, 27 May 2024 20:04:32 -0400 Subject: [PATCH 082/568] remove firefighting remote and bolting firelocks (#28330) remove firefighting remote --- Resources/Prototypes/Access/engineering.yml | 6 ------ .../Catalog/Fills/Lockers/engineer.yml | 2 -- .../Entities/Objects/Devices/door_remote.yml | 18 ------------------ .../Structures/Doors/Firelocks/firelock.yml | 3 --- Resources/migration.yml | 3 +++ 5 files changed, 3 insertions(+), 29 deletions(-) diff --git a/Resources/Prototypes/Access/engineering.yml b/Resources/Prototypes/Access/engineering.yml index 94901fdf7afe..f2f79fa805f2 100644 --- a/Resources/Prototypes/Access/engineering.yml +++ b/Resources/Prototypes/Access/engineering.yml @@ -16,9 +16,3 @@ - ChiefEngineer - Engineering - Atmospherics - -- type: accessGroup - id: FireFight - tags: - - Engineering - - Atmospherics diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/engineer.yml b/Resources/Prototypes/Catalog/Fills/Lockers/engineer.yml index d9d9071d444a..1bf7a9443b5e 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/engineer.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/engineer.yml @@ -94,7 +94,6 @@ - id: GasAnalyzer - id: MedkitOxygenFilled - id: HolofanProjector - - id: DoorRemoteFirefight - id: RCD - id: RCDAmmo @@ -111,7 +110,6 @@ - id: GasAnalyzer - id: MedkitOxygenFilled - id: HolofanProjector - - id: DoorRemoteFirefight - id: RCD - id: RCDAmmo diff --git a/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml b/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml index 25ac56a6daba..a368ec2b65d3 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml @@ -142,24 +142,6 @@ groups: - Engineering -- type: entity - parent: DoorRemoteDefault - id: DoorRemoteFirefight - name: fire-fighting door remote - description: A gadget which can open and bolt FireDoors remotely. - components: - - type: Sprite - layers: - - state: door_remotebase - - state: door_remotelightscolour - color: "#ff9900" - - state: door_remotescreencolour - color: "#e02020" - - - type: Access - groups: - - FireFight - - type: entity parent: DoorRemoteDefault id: DoorRemoteAll diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml index 860db862aeef..c098409f3aa3 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml @@ -105,9 +105,6 @@ arc: 360 - type: StaticPrice price: 150 - - type: DoorBolt - - type: AccessReader - access: [ [ "Engineering" ] ] - type: PryUnpowered pryModifier: 0.5 diff --git a/Resources/migration.yml b/Resources/migration.yml index c19f1408707c..cf1a0f7a6023 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -342,3 +342,6 @@ chem_master: ChemMaster # 2024-05-21 CrateJanitorExplosive: ClosetJanitorBombFilled + +# 2024-05-27 +DoorRemoteFirefight: null From a57b4ff520bef91365dba5912f5a996c4dd1ac7c Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 28 May 2024 00:05:38 +0000 Subject: [PATCH 083/568] 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 98ec517a5024..7fa7746b62e7 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: liltenhead - changes: - - message: Buffed the zombie virus to do purely poison damage, and more likely to - spread the infection per bite. - type: Tweak - id: 6129 - time: '2024-03-12T18:44:09.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25954 - author: UnicornOnLSD changes: - message: brought back the classic crew cut as an alternative to the current one @@ -3869,3 +3861,10 @@ id: 6628 time: '2024-05-27T22:43:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28327 +- author: EmoGarbage404 + changes: + - message: Removed the fire-fighting remote. + type: Remove + id: 6629 + time: '2024-05-28T00:04:32.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28330 From 0a0c0f37a1d331f26dd2c709ab02da6af3a6c48c Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Tue, 28 May 2024 02:46:54 +0200 Subject: [PATCH 084/568] Add popup for owner when inserting item in hand (#28032) --- Content.Server/Strip/StrippableSystem.cs | 3 +++ Resources/Locale/en-US/strip/strippable-component.ftl | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Content.Server/Strip/StrippableSystem.cs b/Content.Server/Strip/StrippableSystem.cs index 397396de5016..ded9eab3eb28 100644 --- a/Content.Server/Strip/StrippableSystem.cs +++ b/Content.Server/Strip/StrippableSystem.cs @@ -413,6 +413,9 @@ private void StartStripInsertHand( var (time, stealth) = GetStripTimeModifiers(user, target, targetStrippable.HandStripDelay); + if (!stealth) + _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert-hand", ("user", Identity.Entity(user, EntityManager)), ("item", user.Comp.ActiveHandEntity!.Value)), target, target, PopupType.Large); + var prefix = stealth ? "stealthily " : ""; _adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands"); diff --git a/Resources/Locale/en-US/strip/strippable-component.ftl b/Resources/Locale/en-US/strip/strippable-component.ftl index 7654b20b03f3..ee37a5e90c12 100644 --- a/Resources/Locale/en-US/strip/strippable-component.ftl +++ b/Resources/Locale/en-US/strip/strippable-component.ftl @@ -9,6 +9,7 @@ strippable-component-cannot-drop-message = {$owner} cannot drop that! strippable-component-alert-owner = {$user} is removing your {$item}! strippable-component-alert-owner-hidden = You feel someone fumbling in your {$slot}! strippable-component-alert-owner-insert = {$user} is putting {$item} on you! +strippable-component-alert-owner-insert-hand = {$user} is putting {$item} in your hand! # generic warning for when a user interacts with your equipped items. strippable-component-alert-owner-interact = {$user} is fumbling around with your {$item}! @@ -19,4 +20,4 @@ strip-verb-get-data-text = Strip ## UI strippable-bound-user-interface-stripping-menu-title = {$ownerName}'s inventory -strippable-bound-user-interface-stripping-menu-ensnare-button = Remove Leg Restraints \ No newline at end of file +strippable-bound-user-interface-stripping-menu-ensnare-button = Remove Leg Restraints From b95df6530dec1a5b62f340d5717587e9ca8332df Mon Sep 17 00:00:00 2001 From: Ghagliiarghii <68826635+Ghagliiarghii@users.noreply.github.com> Date: Mon, 27 May 2024 20:51:30 -0400 Subject: [PATCH 085/568] Update Antagonists Guidebook Sections (#27931) * Update MinorAntagonists.xml Flesh out the Rat King and Space Dragon sections of MinorAntagonists.xml in the guidebook. * Rope the rest of the Guidebook Antag Entries into this (except Zombies they were fine) - Also fix two typos I discovered during research * oops missed a hyphen * Sydnicate :despair: * AUTOTELL ON -> OPERATIVES --- .../Entities/Objects/Misc/implanters.yml | 2 +- .../Objects/Misc/subdermal_implants.yml | 2 +- Resources/Prototypes/Objectives/ninja.yml | 2 +- .../Guidebook/Antagonist/MinorAntagonists.xml | 28 ++++++---- .../Antagonist/Nuclear Operatives.xml | 41 +++++++------- .../Guidebook/Antagonist/Revolutionaries.xml | 17 +++--- .../Guidebook/Antagonist/SpaceNinja.xml | 29 +++++----- .../Guidebook/Antagonist/Traitors.xml | 54 ++++++++++--------- 8 files changed, 96 insertions(+), 79 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Misc/implanters.yml b/Resources/Prototypes/Entities/Objects/Misc/implanters.yml index c09deb130717..532bcadeb538 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/implanters.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/implanters.yml @@ -246,7 +246,7 @@ - type: entity id: MindShieldImplanter - name: mind-shield implanter + name: mindshield implanter parent: BaseImplantOnlyImplanter components: - type: Implanter diff --git a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml index a0f5e254d5f6..c92985c2cbae 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml @@ -317,7 +317,7 @@ - type: entity parent: BaseSubdermalImplant id: MindShieldImplant - name: mind-shield implant + name: mindshield implant description: This implant will ensure loyalty to Nanotrasen and prevent mind control devices. noSpawn: true components: diff --git a/Resources/Prototypes/Objectives/ninja.yml b/Resources/Prototypes/Objectives/ninja.yml index 0203059c2c45..bc17992e90df 100644 --- a/Resources/Prototypes/Objectives/ninja.yml +++ b/Resources/Prototypes/Objectives/ninja.yml @@ -4,7 +4,7 @@ id: BaseNinjaObjective components: - type: Objective - # difficulty isn't used all since objectives are picked + # difficulty isn't used since all objectives are picked difficulty: 1.5 issuer: spiderclan - type: RoleRequirement diff --git a/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml b/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml index 8d96573a3d0b..1a3486b301bd 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml @@ -27,22 +27,21 @@ # Rat King - - + - A Rat King is a giant rat capable of setting a nest and creating rats to do their bidding (usually to get food). + A Rat King is a gigantic rat capable of eating an enormous amount of food, scampering under doors and tables, and raising an army of rats to defend them and do their bidding. ## Abilities - Abilities come at a cost to the Rat King's hunger. Simply eating replenishes it. + Abilities come at the cost of some of the Rat King's hunger. Eat to replenish it. - - Raise an Army of [color=#a4885c]Rat Servants[/color]. - - Conjure a cloud of ammonia. + - Raise an Army of [color=#a4885c]Rat Servants[/color]. You may command these [color=#a4885c]Rat Servants[/color] to stay in place, follow you, attack a target of your choice, or attack indiscriminately. + - Conjure a cloud of [color=#77b58e]ammonia[/color]. Note that [color=#77b58e]ammonia[/color] is mildly poisonous to others, but heals rats. # Space Dragon @@ -50,11 +49,13 @@ - A Space Dragon is a giant dragon that creates space carp rifts and eats the crew. + A Space Dragon is a giant extradimensional dragon that creates space carp rifts and eats the crew. ## Abilities - - Devour critical or dead victims. + - Devour critical or dead victims to heal slightly, or devour infrastructure such as doors, windows, and walls. + + - Breathe out a powerful flaming sphere which will explode periodically along a straight flight path and explode when it hits something, igniting things along the way. Be careful, your carps are not immune to the Dragon's Breath! @@ -62,6 +63,15 @@ - - Summon a Carp Rift that periodically spawns [color=#a4885c]Space Carp[/color]. + - Summon a Carp Rift that periodically spawns [color=#a4885c]Space Carp[/color]. + + ## Carp Rifts + + - Space Dragons may have up to three Carp Rifts active at any given time. + - Carp Rifts spawn a [color=#a4885c]Space Carp[/color] every thirty seconds while active. + - Rifts charge with extradimensional energy over a period of five minutes and are vulnerable during this time. Protect them! The space dragon will suffer a temporary debilitating feedback effect in the event that its rifts are destroyed before they are done charging. + - You may only charge one rift at any given time. + - After the rifts are done charging, they are invulnerable and will continue to spawn [color=#a4885c]Space Carp[/color] so long as the Dragon lives. + - If a period of five minutes passes since the appearance of the Space Dragon or the last time a Carp Rift was charging and unless the Space Dragon already controls the maximum of three Carp Rifts, the Space Dragon will disappear. diff --git a/Resources/ServerInfo/Guidebook/Antagonist/Nuclear Operatives.xml b/Resources/ServerInfo/Guidebook/Antagonist/Nuclear Operatives.xml index f3ced7eeda37..f6b917457108 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/Nuclear Operatives.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/Nuclear Operatives.xml @@ -1,24 +1,24 @@ # Nuclear Operatives - AUTOTELL ON STANDBY. YOUR OBJECTIVES ARE SIMPLE. DELIVER THE PAYLOAD AND GET OUT BEFORE THE PAYLOAD DETONATES. BEGIN MISSION. + OPERATIVES STANDBY. YOUR OBJECTIVES ARE SIMPLE. DELIVER THE PAYLOAD AND GET OUT BEFORE THE PAYLOAD DETONATES. BEGIN MISSION. If you hear this message then congratulations! You have just been chosen to be a nuclear operative for the syndicate. You have one goal, to blow up the space station with a nuclear fission explosive. ## Operatives Your team may contain varying amounts of three different roles: - - The [color=#a4885c]Nukie Commander[/color] wears the unique commander’s hardsuit, and will create or approve of the battle plan. + - The [color=#a4885c]Syndicate Commander[/color] wears the unique commander’s hardsuit and will create or approve of the battle plan. - - The [color=#a4885c]Nukie Agent[/color] wears the blood-red medic hardsuit, and can act as a combat medic for the mission. + - The [color=#a4885c]Syndicate Agent[/color] wears the blood-red medic hardsuit and can act as a combat medic for the mission. - - Regular [color=#a4885c]Nuclear Operatives[/color], who have the normal blood red hardsuit. + - Regular [color=#a4885c]Nuclear Operatives[/color] who have the normal blood red hardsuit. @@ -27,7 +27,7 @@ ## Preparation - Taking on a whole station in a fight is a difficult job, luckily you are well prepared for the job. Every operative has an [color=#a4885c]Uplink[/color]. It comes with 40 [color=#a4885c]Telecrystals[/color], which you can use to purchase gear. What you get depends on the strategy picked by your commander or voted for by the team. Only your commander and agent will have passenger access, but no external airlock access. This means you will need something to hack or blast your way onto the station. In general you should need some weapons, something to breach doors, and utility/healing. + Taking on a whole station in a fight is a difficult job, luckily you are well prepared for the job. Every operative has an [color=#a4885c]Uplink[/color]. It comes with 40 [color=#a4885c]Telecrystals[/color], which you can use to purchase gear. What you get depends on the strategy picked by your commander or voted for by the team. Only your commander and agent will have passenger access, but no external airlock access. This means you will need something to hack or blast your way onto the station. In general, you will need some weapons to protect yourself and kill the crew, something to breach doors to get where you need to go, utility items to make your job easier, and healing to not die. @@ -35,9 +35,14 @@ ## Getting to the Station - After gearing up and creating a plan, grab a jetpack from your armory and go with your other operatives to the [color=#a4885c]Nukie Shuttle[/color]. Here you will find some extra explosives and tools, as well as your nuke and nuke codes. + After gearing up and creating a plan, grab a jetpack from your armory and go with your other operatives to the [color=#a4885c]Syndicate Shuttle[/color]. Here you will find some extra explosives and tools, as well as your nuke and nuke codes. You will find an [color=#a4885c]IFF Console[/color] on the shuttle, it allows you to hide from other ships and mass scanners. Make sure that [color=#a4885c]Show IFF[/color] and [color=#a4885c]Show Vessel[/color] are toggled off to hide your shuttle from the crew. When everyone is ready, FTL to the station and fly to it with a jetpack. Don't forget the nuclear fission explosive on your ship if you are going to use it, and definitely don't forget the nuke codes or pinpointer. + + + + + ## The Disk You will notice that each operative starts with a [color=#a4885c]Pinpointer[/color]. This device is one of the most important items to the mission, and you should keep it with you at all times. Turn on the pinpointer when you arrive at the station and it will always point to the [color=#a4885c]Nuke Disk[/color], your next objective. @@ -51,7 +56,7 @@ ## The Nuke - On your shuttle you will find one of two [color=#a4885c]Nuclear Fission Explosives[/color] and a paper with the nuke codes. The paper will tell you which explosive it corresponds to. If the ID is the same as the one on your nuke, the codes will only work for it. If it is not, then they will only work for the explosive in the station's vault. + On your shuttle, you will find one of two [color=#a4885c]Nuclear Fission Explosives[/color] and a paper with the nuke codes. The paper will tell you which explosive it corresponds to. If the ID is the same as the one on your nuke, the codes will only work for it. If it is not, then they will only work for the explosive in the station's vault. Once you acquire the nuke disk, put it into the nuke and use the code to arm it. It takes 30 seconds for a crewmember to disarm it, and it will count down from 300. Be prepared to defend the nuke for as long as possible, remember that escaping alive isn't necessary, but recommended. @@ -61,28 +66,26 @@ ## Victories - The “victor” of the round is announced on the round end screen, as well as how much they won by. The scale of the victory depends on circumstances at the end of the round. + The “victor” of the round is announced on the round end screen, as well as by how much they won by. The scale of the victory depends on the circumstances at the end of the round. [color=#a4885c]Syndicate Major Victory[/color] - - The nuke detonates on station - - The crew escapes on the evac shuttle, but the nuke was armed - - The nuke was armed and delivered to central command on the evac shuttle + - The nuke detonates on the station + - The crew escapes on the evac shuttle, but the nuke is still armed + - The nuke is armed and delivered to central command on the evac shuttle [color=#a4885c]Syndicate Minor Victory[/color] - - The crew escapes on evac, but every nukie survives - - The crew escapes and some nukies die, but the crew loses the disk + - The crew escapes on evac, but every nuclear operative survives + - The crew escapes and some nuclear operatives die, but the crew loses the disk [color=#a4885c]Neutral Victory[/color] - - The nuke detonates off station + - The nuke detonates off the station [color=#a4885c]Crew Minor Victory[/color] - - The crew escapes on evac and some nukies die, but the crew keeps the disk + - The crew escapes on evac and some nuclear operatives die, but the crew keeps the disk [color=#a4885c]Crew Major Victory[/color] - All nuclear operatives die - - The crew blow up the nukie outpost with the nuke + - The crew blow up the nuclear operative outpost with the nuke - If you feel that you won't be able to completely win as crew or nukie, consider these options for a compromise. + If you feel that you won't be able to completely win as Crew or Nuclear Operatives, consider these options for a compromise. - - diff --git a/Resources/ServerInfo/Guidebook/Antagonist/Revolutionaries.xml b/Resources/ServerInfo/Guidebook/Antagonist/Revolutionaries.xml index 4b1c2e67d6bb..343a1c37bf5c 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/Revolutionaries.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/Revolutionaries.xml @@ -1,7 +1,7 @@ # Revolutionaries - - Revolutionaries are antagonists that are sponsored by the Syndicate to take over the station. + - Revolutionaries are antagonists who are sponsored by the Syndicate to take over the station. ## Head Revolutionaries @@ -10,7 +10,7 @@ - [color=#5e9cff]Head Revolutionaries[/color] are chosen at the start of the shift and are tasked with taking over the station by killing, exiling or cuffing all of the Command staff. Head Revolutionaries will be given a [color=#a4885c]Flash[/color] and a pair of [color=#a4885c]Sunglasses[/color] to aid them. + [color=#5e9cff]Head Revolutionaries[/color] are chosen at the start of the shift and are tasked with taking over the station by killing, exiling, or cuffing all of the Command staff. Head Revolutionaries will be given a [color=#a4885c]Flash[/color] and a pair of [color=#a4885c]Sunglasses[/color] to aid them. ## Conversion @@ -18,7 +18,7 @@ - You can convert crew members by using a [color=#a4885c]Flash[/color] in [color=#ff0000]harm mode[/color] and attacking someone with it. Any flash can be used for conversion, but remember that flashes have limited charges. + You can convert crew members to [color=#ff0000]Revolutionaries[/color] by using a [color=#a4885c]Flash[/color] in [color=#ff0000]harm mode[/color] and attacking someone with it. Any flash can be used for conversion, but remember that flashes have limited charges. @@ -26,22 +26,23 @@ - However, things such as [color=#a4885c]Sunglasses[/color] and [color=#a4885c]Welding Masks[/color] offer flash protection and people wearing these will not be able to be converted. + However, gear providing flash protection such as [color=#a4885c]Sunglasses[/color] and [color=#a4885c]Welding Masks[/color] will also block your flashes, and crew wearing these will not be able to be converted. - While not flash protection, a [color=#a4885c]MindShield Implant[/color] will prevent the implanted person from being converted into a revolutionary. Assume all of [color=#a4885c]Security[/color] and [color=#a4885c]Command[/color] are implanted already. + While not flash protection, a [color=#a4885c]MindShield Implant[/color] will prevent the implanted person from being converted into a revolutionary. Assume all of [color=#a4885c]Security[/color] and [color=#a4885c]Command[/color] are implanted already. + Additionally, a [color=#a4885c]MindShield Implanter[/color] used on a [color=#ff0000]Revolutionary[/color] will de-convert them and they will no longer be loyal to your cause. If a [color=#a4885c]MindShield Implanter[/color] is used on a [color=#5e9cff]Head Revolutionary[/color], it will be destroyed but this will reveal you so be careful! ## Revolutionary - A [color=#ff0000]Revolutionary[/color] is the result of being converted by a [color=#5e9cff]Head Revolutionary[/color]. Revolutionaries are underlings of the Head Revolutionaries and should follow orders given by them and prioritize their well-being over anything else because if they die you will lose. + A [color=#ff0000]Revolutionary[/color] is the result of being converted by a [color=#5e9cff]Head Revolutionary[/color]. Revolutionaries are underlings of the Head Revolutionaries and should follow orders given by them and prioritize their well-being over anything else because if the [color=#5e9cff]Head Revolutionaries[/color] die the Revolution will fail. Keep in mind that you can't convert others as a regular revolutionary, only your boss can do that. ## Objectives - You must eliminate, exile or arrest all of the following Command staff on station in no particular order. + You must eliminate, exile, or arrest all of the following Command staff on station in no particular order. - Captain - Head of Personnel - Chief Engineer @@ -51,4 +52,4 @@ - Chief Medical Officer Remember, your objective is to take over the station and not to destroy it so try to minimize damage where possible. Viva la revolución! - + \ No newline at end of file diff --git a/Resources/ServerInfo/Guidebook/Antagonist/SpaceNinja.xml b/Resources/ServerInfo/Guidebook/Antagonist/SpaceNinja.xml index f088b9f27b1e..496c00e6c331 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/SpaceNinja.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/SpaceNinja.xml @@ -1,15 +1,15 @@ # Space Ninja - The [color=#66FF00]Space Ninja[/color] is a ghost role randomly available mid-late game. If you pick it you will be given your gear, your objectives and the greeting. + The [color=#66FF00]Space Ninja[/color] is a ghost role randomly available mid-late game. If you pick it you will be given your gear, your objectives, and the greeting. You are a ninja, but in space. [color=#66FF00]The Spider Clan[/color] has sent you to the station to wreak all kinds of havoc, and you are equipped to keep it silent-but-deadly. - Whether you mercilessly pick off the station's crew one by one, or assassinate the clown over and over, your discipline has taught you that [color=#66FF00]your objectives must be at least attempted[/color]. For honor! + Whether you mercilessly pick off the station's crew one by one or assassinate the clown over and over, your discipline has taught you that [color=#66FF00]your objectives must be at least attempted[/color]. For honor! # Equipment - You begin implanted with a [color=#a4885c]death acidifier[/color], so if you are KIA or decide to commit seppuku you will leave behind one final gift to the janitor and all your precious equipment is kept out of enemy hands. + You begin implanted with a [color=#a4885c]death acidifier[/color], so if you are KIA or decide to commit seppuku you will leave behind one final gift to the janitor, and all your precious equipment is kept out of enemy hands. Your bag is full of tools for more subtle sabotage, along with a survival box if you need a snack. @@ -25,7 +25,7 @@ Your suit requires power to function. Its [color=#a4885c]internal battery[/color] can be replaced by clicking on it with another one, and [color=#a4885c]higher capacity batteries[/color] mean a [color=#a4885c]highly effective ninja[/color]. You can see the current charge by examining the suit or in a sweet battery alert at the top right of your screen. - If you run out of power and need to recharge your battery, just use your gloves to drain an APC, substation or a SMES. + If you run out of power and need to recharge your battery, just use your gloves to drain an APC, substation, or SMES. ## Ninja Gloves @@ -33,16 +33,16 @@ [color=#66FF00]These bad boys are your bread and butter.[/color] - They are made from [color=#a4885c]insulated nanomachines[/color] to assist you in gracefully breaking and entering into your destination without leaving behind fingerprints. + They are made from [color=#a4885c]insulated nanomachines[/color] to assist you in gracefully breaking into destinations without leaving behind fingerprints. You have an action to toggle gloves. When the gloves are turned on, they allow you to use [color=#a4885c]special abilities[/color], which are triggered by interacting with things with an empty hand and with combat mode disabled. Your glove abilities include: - Emagging an unlimited number of doors. - - Draining power from transformers such as APCs, substations or SMESes. The higher the voltage, the more efficient the draining is. + - Draining power from transformers such as APCs, substations, or SMESes. The higher the voltage, the more efficient the draining is. - You can shock any mob, stunning and slightly damaging them. - - You can download technologies from a R&D server for one of your objectives. - - You can hack a communications console to call in a threat. + - You can download technologies from an R&D server for one of your objectives. + - You can hack a communications console to call in a [textlink="threat" link="MinorAntagonists"]. ## Energy Katana @@ -52,7 +52,7 @@ Deals a lot of damage and can be recalled at will, costing suit power proportional to the distance teleported. While in hand you can [color=#a4885c]teleport[/color] to anywhere that you can see, meaning most doors and windows, but not past solid walls. - This has a limited number of charges which regenerate slowly, so keep a charge or two spare incase you need a quick getaway. + This has a limited number of charges which regenerate slowly, so keep a charge or two spare in case you need a quick getaway. ## Spider Clan Charge @@ -71,10 +71,11 @@ # Objectives - - Download X research nodes: Use your gloves on an R&D server with a number of unlocked technologies - - Doorjack X doors on the station: Use your gloves to emag a number of doors. - - Detonate the spider clan charge: Plant your spider clan charge at a random location and watch it go boom. - - Call in a threat: Use your gloves on a communications console. + - Download \[9-13\] research nodes: Use your gloves on an R&D server with a number of unlocked technologies + - Doorjack \[15-40\] doors on the station: Use your gloves to emag a number of doors. + - Detonate the spider clan charge: Plant your spider clan charge at the specified location and watch it go boom! + - Call in a [textlink="threat" link="MinorAntagonists"]: Use your gloves on a communications console. + - Set everyone to wanted: Use your gloves on a criminal records console. - Survive: Don't die. - + \ No newline at end of file diff --git a/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml b/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml index 002ee56d115a..a8fe4cef1805 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml @@ -10,7 +10,7 @@ - - The [color=#a4885c]Uplink[/color] is the most important tool as a Traitor, as it can purchase tools and weapons with [color=#a4885c]telecrystals[/color](TC). + - The [color=#a4885c]Uplink[/color] is the most important tool for a Traitor, as it can purchase tools and weapons with [color=#a4885c]telecrystals[/color](TC). @@ -22,16 +22,23 @@ Make sure to relock your [color=#a4885c]PDA[/color] to prevent anyone else from seeing it! - Various gear include: - - - - - + + + + + + + + + + + + ## Objectives - - When becoming a Traitor, you will have a list of objectives, ranging from escape alive, stealing something, and killing someone. Using the [color=#a4885c]Uplink[/color] will help you with most of these tasks. + - When becoming a Traitor, you will have a list of objectives, ranging from escaping alive, stealing something, and killing someone. Using the [color=#a4885c]Uplink[/color] will help you with most of these tasks. ## List of Possible Tasks @@ -40,30 +47,25 @@ - Kill or maroon a randomly selected department head. - Keep a randomly selected traitor alive. - Escape on the evacuation shuttle alive and uncuffed. - - Help a randomly selected traitor finish 2/3 of their objectives. + - Help a randomly selected traitor finish half of their objectives. - Die a glorious death. - - Steal the Captain's [color=#a4885c]ID Card[/color]. + - Steal the Captain's [color=#a4885c]ID Card[/color], [color=#a4885c]Antique Laser Pistol[/color], [color=#a4885c]Jetpack[/color], or [color=#a4885c]Nuke Disk[/color]. - - - Steal the Captain's [color=#a4885c]Antique Laser Pistol[/color]. - - - - - Steal the Captain's [color=#a4885c]Jetpack[/color]. + + - - - Steal the Chief Medical Officer's [color=#a4885c]Hypospray[/color]. + + + - Steal the Chief Medical Officer's [color=#a4885c]Hypospray[/color] or [color=#a4885c]Handheld Crew Monitor[/color]. + - - Steal the Research Director's [color=#a4885c]Hardsuit[/color]. + - Steal the Research Director's [color=#a4885c]Hardsuit[/color] or [color=#a4885c]Hand Teleporter[/color]. - - - Steal the Research Director's [color=#a4885c]Hand Teleporter[/color]. - - Steal the Head of Security's [color=#a4885c]Secret Documents[/color]. @@ -74,14 +76,14 @@ - - Steal the [color=#a4885c]Nuke Disk[/color]. + - Steal the Quartermaster's [color=#a4885c]Requisition Digi-Board[/color]. - + - - Steal [color=#a4885c]Corgi Meat[/color]. + - Steal the Head of Personnel's dog, Ian's [color=#a4885c]Corgi Meat[/color]. - + - + \ No newline at end of file From c740fbc68c0fe8aa8519009fee72b88cb17188eb Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Tue, 28 May 2024 02:51:50 +0200 Subject: [PATCH 086/568] Cargo bounty corrections (#28255) cargo bounty corrections --- .../Cargo/Systems/CargoSystem.Bounty.cs | 4 ++- .../Cargo/Prototypes/CargoBountyPrototype.cs | 8 ++++- .../Prototypes/Catalog/Bounties/bounties.yml | 15 +++++++++ .../Objects/Consumable/Food/Baked/bread.yml | 18 ++++++++--- .../Objects/Consumable/Food/Baked/cake.yml | 32 +++++++++++++++++++ .../Objects/Consumable/Food/Baked/misc.yml | 9 ++++-- .../Objects/Consumable/Food/Baked/pie.yml | 10 ++++++ .../Objects/Consumable/Food/Baked/pizza.yml | 12 ++++++- .../Objects/Consumable/Food/ingredients.yml | 18 +++++++++++ .../Objects/Consumable/Food/produce.yml | 13 ++++++++ Resources/Prototypes/tags.yml | 12 +++++-- 11 files changed, 138 insertions(+), 13 deletions(-) diff --git a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs index e132e4f12a36..0fcfd160bb39 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs @@ -11,6 +11,7 @@ using Content.Shared.Database; using Content.Shared.NameIdentifier; using Content.Shared.Stacks; +using Content.Shared.Whitelist; using JetBrains.Annotations; using Robust.Server.Containers; using Robust.Shared.Containers; @@ -23,6 +24,7 @@ public sealed partial class CargoSystem { [Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly NameIdentifierSystem _nameIdentifier = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSys = default!; [ValidatePrototypeId] private const string BountyNameIdentifierGroup = "Bounty"; @@ -311,7 +313,7 @@ public bool IsBountyComplete(HashSet entities, IEnumerable(); foreach (var entity in entities) { - if (!entry.Whitelist.IsValid(entity, EntityManager)) + if (!_whitelistSys.IsValid(entry.Whitelist, entity) || (entry.Blacklist != null && _whitelistSys.IsValid(entry.Blacklist, entity))) continue; count += _stackQuery.CompOrNull(entity)?.Count ?? 1; diff --git a/Content.Shared/Cargo/Prototypes/CargoBountyPrototype.cs b/Content.Shared/Cargo/Prototypes/CargoBountyPrototype.cs index bf4907b0dd4b..b40b03672eff 100644 --- a/Content.Shared/Cargo/Prototypes/CargoBountyPrototype.cs +++ b/Content.Shared/Cargo/Prototypes/CargoBountyPrototype.cs @@ -31,7 +31,7 @@ public sealed partial class CargoBountyPrototype : IPrototype /// /// The entries that must be satisfied for the cargo bounty to be complete. /// - [DataField( required: true)] + [DataField(required: true)] public List Entries = new(); /// @@ -50,6 +50,12 @@ public readonly partial record struct CargoBountyItemEntry() [DataField(required: true)] public EntityWhitelist Whitelist { get; init; } = default!; + /// + /// A blacklist that can be used to exclude items in the whitelist. + /// + [DataField] + public EntityWhitelist? Blacklist { get; init; } = null; + // todo: implement some kind of simple generic condition system /// diff --git a/Resources/Prototypes/Catalog/Bounties/bounties.yml b/Resources/Prototypes/Catalog/Bounties/bounties.yml index 3c1bd02fcfc4..bedfe442872e 100644 --- a/Resources/Prototypes/Catalog/Bounties/bounties.yml +++ b/Resources/Prototypes/Catalog/Bounties/bounties.yml @@ -309,6 +309,9 @@ whitelist: tags: - Pie + blacklist: + tags: + - Slice - type: cargoBounty id: BountyPrisonUniform @@ -541,6 +544,12 @@ whitelist: tags: - Fruit + blacklist: + tags: + - Slice + - Cake + - Pie + - Bread - type: cargoBounty id: BountyVegetable @@ -552,6 +561,12 @@ whitelist: tags: - Vegetable + blacklist: + tags: + - Slice + - Cake + - Pie + - Bread - type: cargoBounty id: BountyChili diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml index 7600882925a5..12a6e2a246b3 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml @@ -34,7 +34,9 @@ flavors: - bread - type: Tag - tags: [] #override bread + tags: + - Bread + - Slice - type: SolutionContainerManager solutions: food: @@ -116,6 +118,8 @@ - type: Tag tags: - Fruit + - Bread + - Slice - type: entity name: cornbread @@ -274,6 +278,8 @@ - type: Tag tags: - Meat + - Bread + - Slice - type: entity name: mimana bread @@ -418,6 +424,8 @@ - type: Tag tags: - Meat + - Bread + - Slice - type: entity name: spider meat bread @@ -476,6 +484,8 @@ - type: Tag tags: - Meat + - Bread + - Slice - type: entity name: tofu bread @@ -585,6 +595,8 @@ - type: Tag tags: - Meat + - Bread + - Slice # Other than bread/slices @@ -594,9 +606,6 @@ id: FoodBreadBaguette description: Bon appétit! components: - - type: Tag - tags: - - Baguette - type: Sprite state: baguette - type: SliceableFood @@ -824,6 +833,7 @@ tags: - VimPilot - DoorBumpOpener + - Bread - type: CanEscapeInventory baseResistTime: 2 - type: Puller diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml index c939dec52c67..922d49388850 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml @@ -23,6 +23,9 @@ Quantity: 5 - type: Item size: Normal + - type: Tag + tags: + - Cake - type: entity parent: FoodCakeBase @@ -45,6 +48,10 @@ Quantity: 1 - type: Item size: Tiny + - type: Tag + tags: + - Cake + - Slice # Custom Cake Example @@ -63,6 +70,7 @@ slice: FoodCakeBlueberrySlice - type: Tag tags: + - Cake - Fruit - type: entity @@ -78,7 +86,9 @@ color: blue - type: Tag tags: + - Cake - Fruit + - Slice # Cake @@ -203,6 +213,7 @@ slice: FoodCakeOrangeSlice - type: Tag tags: + - Cake - Fruit - type: entity @@ -214,7 +225,9 @@ state: orange-slice - type: Tag tags: + - Cake - Fruit + - Slice # Tastes like sweetness, cake, oranges. - type: entity @@ -229,6 +242,7 @@ slice: FoodCakeLimeSlice - type: Tag tags: + - Cake - Fruit - type: entity @@ -240,7 +254,9 @@ state: lime-slice - type: Tag tags: + - Cake - Fruit + - Slice # Tastes like sweetness, cake, limes. - type: entity @@ -255,6 +271,7 @@ slice: FoodCakeLemonSlice - type: Tag tags: + - Cake - Fruit - type: entity @@ -266,7 +283,9 @@ state: lemon-slice - type: Tag tags: + - Cake - Fruit + - Slice # Tastes like sweetness, cake, lemons. - type: entity @@ -296,6 +315,7 @@ Quantity: 5 - type: Tag tags: + - Cake - Fruit - type: entity @@ -323,7 +343,9 @@ Quantity: 1 - type: Tag tags: + - Cake - Fruit + - Slice - type: entity name: chocolate cake @@ -379,6 +401,7 @@ slice: FoodCakeAppleSlice - type: Tag tags: + - Cake - Fruit - type: entity @@ -391,7 +414,9 @@ state: apple-slice - type: Tag tags: + - Cake - Fruit + - Slice # Tastes like sweetness, cake, slime. - type: entity @@ -436,6 +461,7 @@ Quantity: 11 - type: Tag tags: + - Cake - Fruit - type: entity @@ -457,7 +483,9 @@ Quantity: 2.2 - type: Tag tags: + - Cake - Fruit + - Slice # Tastes like sweetness, cake, pumpkin. - type: entity @@ -686,6 +714,7 @@ tags: - VimPilot - DoorBumpOpener + - Cake - type: CanEscapeInventory baseResistTime: 2 - type: Puller @@ -752,3 +781,6 @@ color: "#FFFF00" radius: 1.4 energy: 1.4 + - type: Tag + tags: + - Slice diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/misc.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/misc.yml index b1bbdfb53055..fde181d8b9b2 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/misc.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/misc.yml @@ -163,9 +163,6 @@ # Nuggets -- type: Tag - id: Nugget - - type: entity name: chicken nugget parent: FoodBakedBase @@ -530,6 +527,9 @@ Quantity: 5 - ReagentId: Theobromine Quantity: 3 + - type: Tag + tags: + - Slice - type: entity name: special brownies @@ -585,6 +585,9 @@ Quantity: 3 - ReagentId: THC Quantity: 25 + - type: Tag + tags: + - Slice - type: entity name: onion rings diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml index 8cd1c5dfab67..f97d87a9c503 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml @@ -52,6 +52,10 @@ Quantity: 1.2 - ReagentId: Vitamin Quantity: 1 + - type: Tag + tags: + - Pie + - Slice # Pie @@ -94,6 +98,7 @@ tags: - Fruit - Pie + - Slice # Tastes like pie, apple. - type: entity @@ -182,6 +187,7 @@ tags: - Fruit - Pie + - Slice # Tastes like pie, cream, banana. - type: entity @@ -224,6 +230,7 @@ tags: - Fruit - Pie + - Slice # Tastes like pie, blackberries. - type: entity @@ -263,6 +270,7 @@ tags: - Fruit - Pie + - Slice # Tastes like pie, cherries. - type: entity @@ -302,6 +310,7 @@ tags: - Meat - Pie + - Slice # Tastes like pie, meat. - type: entity @@ -342,6 +351,7 @@ tags: - Meat - Pie + - Slice # Tastes like pie, meat, acid. - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml index bdce1d440861..154f34063c81 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml @@ -59,6 +59,7 @@ tags: - Pizza - ReptilianFood + - Slice # Pizza @@ -135,6 +136,7 @@ tags: - Meat - Pizza + - Slice # Tastes like crust, tomato, cheese, meat. - type: entity @@ -291,6 +293,7 @@ tags: - Meat - Pizza + - Slice # Tastes like crust, tomato, cheese, meat, laziness. - type: entity @@ -391,6 +394,7 @@ tags: - Meat - Pizza + - Slice # Tastes like crust, tomato, cheese, sausage, sass. - type: entity @@ -411,9 +415,13 @@ - state: pineapple - type: SliceableFood slice: FoodPizzaPineappleSlice + - type: Tag + tags: + - Meat + - Pizza - type: entity - name: slice of pineapple pizza + name: slice of Hawaiian pizza parent: FoodPizzaSliceBase id: FoodPizzaPineappleSlice description: A slice of joy/sin. @@ -432,6 +440,7 @@ tags: - Meat - Pizza + - Slice # Tastes like crust, tomato, cheese, pineapple, ham. #TODO: This is a meme pizza from /tg/. It has specially coded mechanics. @@ -504,6 +513,7 @@ tags: - Meat - Pizza + - Slice # Tastes like crust, tomato, cheese, pepperoni, 9 millimeter bullets. #TODO: Make this do poison damage and make cut pizza slices eventually rot into this. diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml index 1df6615a9fbd..dcf2f3355ce7 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml @@ -302,6 +302,9 @@ - dough - type: Sprite state: dough-slice + - type: Tag + tags: + - Slice - type: entity name: cornmeal dough @@ -331,6 +334,9 @@ - dough - type: Sprite state: cornmealdough-slice + - type: Tag + tags: + - Slice - type: entity name: tortilla dough @@ -363,6 +369,9 @@ - type: Construction graph: Tortilla node: start + - type: Tag + tags: + - Slice - type: entity name: flattened tortilla dough @@ -503,6 +512,9 @@ reagents: - ReagentId: Nutriment Quantity: 5 + - type: Tag + tags: + - Slice - type: entity name: chèvre log @@ -550,6 +562,9 @@ Quantity: 1 - ReagentId: Vitamin Quantity: 0.2 + - type: Tag + tags: + - Slice - type: entity name: tofu @@ -595,6 +610,9 @@ Quantity: 3 - ReagentId: Nutriment Quantity: 2 + - type: Tag + tags: + - Slice - type: entity name: burned mess diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index 888e4e4e3530..a74e3450e940 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -1065,6 +1065,9 @@ state: slice - type: Extractable grindableSolutionName: food + - type: Tag + tags: + - Slice - type: entity name: pineapple slice @@ -1085,6 +1088,7 @@ - type: Tag tags: - Fruit + - Slice - type: entity name: onion slice @@ -1108,6 +1112,10 @@ Quantity: 1 - ReagentId: Vitamin Quantity: 1 + - type: Tag + tags: + - Vegetable + - Slice - type: entity name: red onion slice @@ -1131,6 +1139,10 @@ Quantity: 1 - ReagentId: Vitamin Quantity: 1 + - type: Tag + tags: + - Vegetable + - Slice - type: entity name: chili pepper @@ -1650,6 +1662,7 @@ - type: Tag tags: - Fruit + - Slice - type: entity name: grapes diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 253aa485653e..439ad47c0f80 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -27,9 +27,6 @@ - type: Tag id: ATVKeys -- type: Tag - id: Baguette - - type: Tag id: Balloon @@ -258,6 +255,9 @@ - type: Tag id: CableCoil +- type: Tag + id: Cake + - type: Tag id: CaneBlade @@ -926,6 +926,9 @@ - type: Tag id: NozzleBackTank +- type: Tag + id: Nugget # for chicken nuggets + - type: Tag id: NukeOpsUplink @@ -1122,6 +1125,9 @@ - type: Tag id: Skewer +- type: Tag + id: Slice # sliced fruit, vegetables, pizza etc. + - type: Tag id: SmallAIChip From a269f9bb9e722b3eaad0689fab4ba1600aeb76f0 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Mon, 27 May 2024 20:52:56 -0400 Subject: [PATCH 087/568] Small anomaly behavior fix (#28290) * Small anomaly behavior fix * well put together code --- Content.Server/Anomaly/AnomalySystem.cs | 25 ++++++++----------- .../Effects/ShuffleParticlesAnomalySystem.cs | 20 +++++++-------- .../Anomaly/Components/AnomalyComponent.cs | 9 ++++--- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/Content.Server/Anomaly/AnomalySystem.cs b/Content.Server/Anomaly/AnomalySystem.cs index 73658a0320ee..3e9760a056c0 100644 --- a/Content.Server/Anomaly/AnomalySystem.cs +++ b/Content.Server/Anomaly/AnomalySystem.cs @@ -18,8 +18,6 @@ using Robust.Shared.Physics.Events; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Serialization.Manager; -using System.Linq; namespace Content.Server.Anomaly; @@ -69,20 +67,21 @@ private void OnMapInit(Entity anomaly, ref MapInitEvent args) ChangeAnomalyStability(anomaly, Random.NextFloat(anomaly.Comp.InitialStabilityRange.Item1 , anomaly.Comp.InitialStabilityRange.Item2), anomaly.Comp); ChangeAnomalySeverity(anomaly, Random.NextFloat(anomaly.Comp.InitialSeverityRange.Item1, anomaly.Comp.InitialSeverityRange.Item2), anomaly.Comp); - ShuffleParticlesEffect(anomaly.Comp); + ShuffleParticlesEffect(anomaly); anomaly.Comp.Continuity = _random.NextFloat(anomaly.Comp.MinContituty, anomaly.Comp.MaxContituty); SetBehavior(anomaly, GetRandomBehavior()); } - public void ShuffleParticlesEffect(AnomalyComponent anomaly) + public void ShuffleParticlesEffect(Entity anomaly) { var particles = new List { AnomalousParticleType.Delta, AnomalousParticleType.Epsilon, AnomalousParticleType.Zeta, AnomalousParticleType.Sigma }; - anomaly.SeverityParticleType = Random.PickAndTake(particles); - anomaly.DestabilizingParticleType = Random.PickAndTake(particles); - anomaly.WeakeningParticleType = Random.PickAndTake(particles); - anomaly.TransformationParticleType = Random.PickAndTake(particles); + anomaly.Comp.SeverityParticleType = Random.PickAndTake(particles); + anomaly.Comp.DestabilizingParticleType = Random.PickAndTake(particles); + anomaly.Comp.WeakeningParticleType = Random.PickAndTake(particles); + anomaly.Comp.TransformationParticleType = Random.PickAndTake(particles); + Dirty(anomaly); } private void OnShutdown(Entity anomaly, ref ComponentShutdown args) @@ -198,14 +197,12 @@ private void SetBehavior(Entity anomaly, ProtoId anomaly, ProtoId behaviorProto) @@ -213,7 +210,7 @@ private void RemoveBehavior(Entity anomaly, ProtoId(OnStartCollide); } - private void OnStartCollide(EntityUid uid, ShuffleParticlesAnomalyComponent shuffle, StartCollideEvent args) + private void OnStartCollide(Entity ent, ref StartCollideEvent args) { - if (!TryComp(uid, out var anomaly)) + if (!TryComp(ent, out var anomaly)) return; - if (shuffle.ShuffleOnParticleHit && _random.Prob(shuffle.Prob)) - _anomaly.ShuffleParticlesEffect(anomaly); - - if (!TryComp(args.OtherEntity, out var particle)) + if (!HasComp(args.OtherEntity)) return; + + if (ent.Comp.ShuffleOnParticleHit && _random.Prob(ent.Comp.Prob)) + _anomaly.ShuffleParticlesEffect((ent, anomaly)); } - private void OnPulse(EntityUid uid, ShuffleParticlesAnomalyComponent shuffle, AnomalyPulseEvent args) + private void OnPulse(Entity ent, ref AnomalyPulseEvent args) { - if (!TryComp(uid, out var anomaly)) + if (!TryComp(ent, out var anomaly)) return; - if (shuffle.ShuffleOnPulse && _random.Prob(shuffle.Prob)) + if (ent.Comp.ShuffleOnPulse && _random.Prob(ent.Comp.Prob)) { - _anomaly.ShuffleParticlesEffect(anomaly); + _anomaly.ShuffleParticlesEffect((ent, anomaly)); } } } diff --git a/Content.Shared/Anomaly/Components/AnomalyComponent.cs b/Content.Shared/Anomaly/Components/AnomalyComponent.cs index 3878aeb81cba..88e942ec5079 100644 --- a/Content.Shared/Anomaly/Components/AnomalyComponent.cs +++ b/Content.Shared/Anomaly/Components/AnomalyComponent.cs @@ -152,25 +152,25 @@ public sealed partial class AnomalyComponent : Component /// /// The particle type that increases the severity of the anomaly. /// - [DataField] + [DataField, AutoNetworkedField] public AnomalousParticleType SeverityParticleType; /// /// The particle type that destabilizes the anomaly. /// - [DataField] + [DataField, AutoNetworkedField] public AnomalousParticleType DestabilizingParticleType; /// /// The particle type that weakens the anomalys health. /// - [DataField] + [DataField, AutoNetworkedField] public AnomalousParticleType WeakeningParticleType; /// /// The particle type that change anomaly behaviour. /// - [DataField] + [DataField, AutoNetworkedField] public AnomalousParticleType TransformationParticleType; #region Points and Vessels @@ -317,6 +317,7 @@ public sealed partial class AnomalyComponent : Component /// /// Event broadcast when an anomaly's behavior is changed. +/// This is raised after the relevant components are applied /// [ByRefEvent] public readonly record struct AnomalyBehaviorChangedEvent(EntityUid Anomaly, ProtoId? Old, ProtoId? New); From ac6c9bc47661b88091e7cecfa2c2112efe368975 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 28 May 2024 00:52:57 +0000 Subject: [PATCH 088/568] 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 7fa7746b62e7..4291972e7750 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: UnicornOnLSD - changes: - - message: brought back the classic crew cut as an alternative to the current one - ! - type: Add - id: 6130 - time: '2024-03-12T18:47:29.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25935 - author: FungiFellow changes: - message: Added Improvised Shotgun Shell Recipe @@ -3868,3 +3860,10 @@ id: 6629 time: '2024-05-28T00:04:32.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28330 +- author: slarticodefast + changes: + - message: Corrected some cargo bounties. + type: Fix + id: 6630 + time: '2024-05-28T00:51:50.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28255 From b0afbf6a3e15f1beaac9227e0ea11a571c9db7cb Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Tue, 28 May 2024 07:52:30 +0000 Subject: [PATCH 089/568] saltern cryo untroll (#28333) Co-authored-by: deltanedas <@deltanedas:kde.org> --- Resources/Maps/saltern.yml | 1792 +++++++++++++++++++----------------- 1 file changed, 932 insertions(+), 860 deletions(-) diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index b9690eb37d23..3f2bd76d4fc7 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -241,1071 +241,1144 @@ entities: data: tiles: -4,-4: - 0: 65535 + 0: 61695 + -4,-5: + 0: 62457 + -5,-4: + 0: 61695 -4,-3: - 0: 65535 + 0: 47871 -4,-2: - 0: 16383 + 0: 12731 1: 49152 + -5,-2: + 0: 47935 + -5,-1: + 0: 65528 -4,-1: - 0: 65523 + 0: 61056 1: 12 + -4,0: + 0: 61695 -3,-4: - 0: 65535 + 0: 54527 -3,-3: - 0: 65535 + 0: 3581 -3,-2: - 0: 36863 + 0: 247 1: 28672 -3,-1: 1: 7 - 0: 65528 + 0: 65280 + -3,-5: + 0: 61695 + -3,0: + 0: 61695 -2,-4: - 0: 65535 + 0: 53503 -2,-3: - 0: 65535 + 0: 65529 -2,-2: - 0: 65535 + 0: 65520 -2,-1: 0: 65535 + -2,-5: + 0: 64541 + -2,0: + 0: 63487 -1,-4: - 0: 65535 + 0: 53503 -1,-3: - 0: 65535 + 0: 48124 -1,-2: - 0: 65535 + 0: 65520 -1,-1: 0: 65535 - -4,0: - 0: 65535 + -1,-5: + 0: 65285 + -1,0: + 0: 61695 + 0,-4: + 0: 52991 + 0,-3: + 0: 56573 + 0,-2: + 0: 56796 + 0,-1: + 0: 65533 + -5,0: + 0: 63743 -4,1: - 0: 65535 + 0: 49407 + -5,1: + 0: 255 -4,2: - 0: 65535 + 0: 57245 + -5,2: + 0: 53247 -4,3: - 0: 65535 - -3,0: - 0: 65535 + 0: 61424 + -5,3: + 0: 16241 + -4,4: + 0: 61070 -3,1: - 0: 65535 + 0: 41215 -3,2: - 0: 65535 + 0: 65450 -3,3: - 0: 65535 - -2,0: - 0: 65535 + 0: 64988 + -3,4: + 0: 55757 -2,1: - 0: 65535 + 0: 56575 -2,2: - 0: 65535 + 0: 65485 -2,3: - 0: 65535 - -1,0: - 0: 65535 + 0: 32767 + -2,4: + 0: 4215 -1,1: - 0: 65535 + 0: 58623 -1,2: - 0: 65535 + 0: 16142 -1,3: - 0: 65535 - 0,-4: - 0: 65535 - 0,-3: - 0: 65535 - 0,-2: - 0: 65535 - 0,-1: - 0: 65535 + 0: 3003 + -1,4: + 0: 4095 + 0,0: + 0: 64733 + 0,1: + 0: 56575 + 0,2: + 0: 56797 + 0,3: + 0: 52701 + 0,-5: + 0: 65263 1,-4: - 0: 65535 + 0: 7099 1,-3: - 0: 65535 + 0: 53725 1,-2: - 0: 65535 + 0: 56605 1,-1: - 0: 65535 + 0: 65533 + 1,-5: + 0: 47935 + 1,0: + 0: 61917 2,-4: - 0: 65535 + 0: 10231 2,-3: - 0: 65535 + 0: 65278 2,-2: - 0: 65535 + 0: 65390 2,-1: 0: 65535 + 2,-5: + 0: 30511 + 2,0: + 0: 61695 3,-4: - 0: 65535 + 0: 4095 3,-3: - 0: 65535 + 0: 57309 3,-2: - 0: 65535 + 0: 56781 3,-1: + 0: 64985 + 3,-5: + 0: 65295 + 3,0: + 0: 61661 + 4,-4: + 0: 61007 + 4,-3: 0: 65535 - 0,0: - 0: 65535 - 0,1: - 0: 65535 - 0,2: - 0: 65535 - 0,3: - 0: 65535 - 1,0: + 4,-2: 0: 65535 + 4,-1: + 0: 65523 + 0,4: + 0: 61166 1,1: - 0: 65535 + 0: 55807 1,2: - 0: 65535 + 0: 56797 1,3: - 0: 65535 - 2,0: - 0: 65535 + 0: 7665 + 1,4: + 0: 48123 2,1: - 0: 65535 + 0: 29439 2,2: - 0: 65535 + 0: 30583 2,3: - 0: 65535 - 3,0: - 0: 65535 + 0: 3953 + 2,4: + 0: 63351 3,1: 0: 65535 3,2: - 0: 65535 + 0: 65530 3,3: - 0: 65535 + 0: 65295 + 3,4: + 0: 32767 4,0: - 0: 65535 + 0: 61695 4,1: - 0: 65535 + 0: 53759 4,2: - 0: 65535 + 0: 65500 4,3: - 0: 65535 + 0: 53709 + 4,4: + 0: 4573 + 2: 16384 5,0: - 0: 65535 + 0: 62190 5,1: - 0: 65535 + 0: 61695 5,2: 0: 65535 5,3: - 0: 65535 + 0: 62463 + 5,4: + 0: 30719 + 5,-1: + 0: 61152 6,0: - 0: 65535 + 0: 63726 6,1: - 0: 65535 + 0: 3839 6,2: - 0: 65535 + 0: 7677 6,3: - 0: 65535 + 0: 54495 + 6,4: + 0: 64767 7,0: - 0: 65535 + 0: 63547 7,1: - 0: 65535 + 0: 3067 7,2: - 0: 65535 + 0: 35771 7,3: + 0: 40159 + 7,4: + 0: 55739 + 7,-1: + 0: 36590 + 8,0: + 0: 65327 + 8,1: + 0: 12287 + 8,2: 0: 65535 - 0,4: + 8,3: 0: 65535 0,5: - 0: 65535 + 0: 55535 + -1,5: + 0: 58623 0,6: - 0: 65535 + 0: 51711 + -1,6: + 0: 3822 0,7: - 0: 65535 - 1,4: - 0: 65535 + 0: 65503 + -1,7: + 0: 61166 + 0,8: + 0: 15 + 2: 3840 1,5: - 0: 65535 + 0: 53439 1,6: - 0: 65535 + 0: 7421 1,7: - 0: 65535 - 2,4: - 0: 65535 + 0: 65503 + 1,8: + 0: 15 + 2: 40704 2,5: - 0: 65535 + 0: 53367 2,6: - 0: 65535 + 0: 1405 2,7: - 0: 65535 - 3,4: - 0: 65535 + 0: 4915 + 2: 34944 + 2,8: + 2: 36 3,5: - 0: 32767 + 0: 4181 3,6: - 0: 30583 + 0: 4881 + 4,5: + 0: 273 + 2: 17476 -8,0: - 0: 65535 + 0: 61678 + -9,0: + 0: 64413 -8,1: - 0: 65535 + 0: 53503 + -9,1: + 0: 46079 -8,2: - 0: 65535 + 0: 56796 + -9,2: + 0: 35835 -8,3: - 0: 65535 + 0: 4081 + -9,3: + 0: 16315 + -8,4: + 0: 477 + 2: 49152 -7,0: - 0: 65535 + 0: 64671 -7,1: - 0: 65535 + 0: 40191 -7,2: - 0: 65535 + 0: 49087 -7,3: - 0: 65535 + 0: 20472 + -7,4: + 0: 58983 + -7,-1: + 0: 52732 -6,0: - 0: 65535 + 0: 62363 -6,1: - 0: 65535 + 0: 5119 -6,2: - 0: 65535 + 0: 6007 -6,3: - 0: 65535 - -5,0: - 0: 65535 - -5,1: - 0: 65535 - -5,2: - 0: 65535 - -5,3: - 0: 65535 - 4,-4: - 0: 65535 - 4,-3: - 0: 65535 - 4,-2: - 0: 65535 - 4,-1: - 0: 65535 + 0: 4092 + -6,-1: + 0: 48051 + -6,4: + 0: 61567 + -5,4: + 0: 30527 + 4,-5: + 0: 61152 5,-4: - 0: 65535 + 0: 3777 5,-3: - 0: 65535 + 0: 3823 5,-2: - 0: 65535 - 5,-1: - 0: 65535 + 0: 61182 + 5,-5: + 0: 56774 6,-4: - 0: 65535 + 0: 53230 6,-3: - 0: 65535 + 0: 35769 6,-2: - 0: 65535 + 0: 56797 6,-1: - 0: 65535 + 0: 4092 + 6,-5: + 0: 65524 + 7,-2: + 0: 57599 7,-4: - 0: 13107 + 2: 8738 7,-3: - 0: 65535 - 7,-2: - 0: 65535 - 7,-1: + 0: 3808 + 8,-3: + 0: 4024 + 8,-2: + 0: 62207 + 8,-1: 0: 65535 0,-8: - 0: 65535 + 0: 64977 + -1,-8: + 0: 61166 0,-7: - 0: 65535 + 0: 56829 + -1,-7: + 0: 53247 0,-6: - 0: 65535 - 0,-5: - 0: 65535 + 0: 65532 + 0,-9: + 0: 53192 + 2: 39 1,-8: - 0: 65535 + 0: 56784 1,-7: - 0: 65535 + 0: 57343 1,-6: - 0: 65535 - 1,-5: - 0: 65535 + 0: 45981 + 1,-9: + 0: 5937 2,-8: - 0: 65535 + 0: 15353 2,-7: 0: 65535 2,-6: - 0: 65535 - 2,-5: - 0: 65535 + 0: 61695 + 2,-9: + 0: 13107 + 2: 128 3,-8: - 0: 65535 + 0: 52637 3,-7: - 0: 65535 + 0: 64925 3,-6: - 0: 65535 - 3,-5: - 0: 65535 + 0: 64733 + 4,-8: + 0: 39299 + 2: 17484 4,-7: - 0: 65535 + 0: 65289 + 2: 4 4,-6: - 0: 65535 - 4,-5: - 0: 65535 + 0: 61152 + 4,-9: + 2: 17476 + 0: 34952 + 5,-8: + 2: 21855 + 0: 43680 5,-7: - 0: 65535 + 2: 5 + 0: 63242 + 5,-9: + 2: 21845 + 0: 43690 5,-6: - 0: 65535 - 5,-5: - 0: 65535 - 6,-5: - 0: 65535 + 0: 26214 + 6,-8: + 2: 21855 + 0: 43680 + 6,-7: + 2: 5 + 0: 56586 + 6,-6: + 0: 61919 + 6,-9: + 2: 21845 + 0: 43690 + 7,-8: + 2: 4383 + 0: 8736 + 7,-7: + 2: 8749 + 0: 2 7,-5: - 0: 65535 - -4,4: - 0: 65535 + 0: 3846 + 7,-9: + 2: 4369 + 0: 8738 + 7,-6: + 2: 1570 + 8,-8: + 2: 1 + 8,-7: + 2: 15 + 0: 65280 + 8,-6: + 0: 13119 + 2: 2048 + 8,-5: + 0: 3891 -4,5: - 0: 65535 + 0: 238 -4,6: - 0: 65535 - -3,4: - 0: 65535 + 0: 255 + 2: 61440 + -5,6: + 0: 238 + 2: 61440 + -4,7: + 2: 2289 + -5,7: + 2: 240 -3,5: - 0: 65535 + 0: 3295 -3,6: - 0: 49151 - -2,4: - 0: 65535 + 0: 255 + 2: 12288 + -3,7: + 2: 8816 + -3,8: + 2: 3086 -2,5: - 0: 65535 + 0: 52701 -2,6: - 0: 65535 - -1,4: - 0: 65535 - -1,5: - 0: 65535 - -1,6: - 0: 65535 - -1,7: - 0: 65535 - -5,4: - 0: 65535 - -5,5: - 0: 65535 - -5,6: - 0: 65535 + 0: 63743 + -2,7: + 0: 255 -1,8: - 0: 36847 - 4,4: - 0: 65535 - 0,8: - 0: 4095 - 1,8: - 0: 40959 - 2,8: - 0: 55 - 8,0: - 0: 65535 - 8,1: - 0: 65535 - 8,2: - 0: 65535 - 8,3: - 0: 65535 + 0: 8 + 2: 36640 + -9,4: + 0: 7400 + -8,5: + 2: 8879 + 0: 21840 + -9,5: + 2: 43692 + 0: 21840 + -8,6: + 2: 41519 + 0: 21840 + -9,6: + 2: 43695 + 0: 21840 + -8,7: + 2: 242 + -9,7: + 2: 240 + -7,6: + 2: 62465 + 0: 14 + -7,7: + 2: 16 + -7,5: + 0: 1038 + -6,5: + 0: 65319 + -6,6: + 0: 255 + -6,7: + 2: 240 + -5,5: + 0: 26215 + -2,8: + 2: 7967 + -1,9: + 2: 142 + 0,9: + 2: 15 + 4,6: + 2: 4 + 0: 34816 + 5,5: + 0: 30583 + 5,6: + 0: 65319 + 5,7: + 0: 5 + 6,5: + 0: 255 + 2: 53248 + 6,6: + 2: 35049 + 7,5: + 0: 36063 + 2: 4096 + 7,6: + 2: 53196 + 7,7: + 2: 12 + 8,4: + 0: 4351 + 2: 57344 + 8,5: + 0: 272 + 4: 17472 + 8,6: + 2: 65345 + 8,7: + 2: 61167 + 1,9: + 2: 15 + 2,9: + 2: 1 9,0: - 0: 65535 + 0: 65102 9,1: - 0: 65535 + 0: 3839 9,2: - 0: 65535 + 0: 61917 9,3: 0: 65535 + 9,4: + 0: 255 + 2: 61440 + 9,-1: + 0: 60942 10,0: - 0: 65535 + 0: 65481 10,1: - 0: 65535 + 0: 53247 10,2: - 0: 65535 + 0: 62717 10,3: 0: 65535 + 10,-1: + 0: 48015 + 10,4: + 0: 255 + 2: 61440 11,0: - 0: 65535 + 0: 65520 11,1: - 0: 65535 + 0: 53759 11,2: - 0: 16383 - 2: 49152 + 0: 4319 + 3: 49152 11,3: - 0: 65331 - 2: 204 + 0: 61457 + 3: 204 + 11,-1: + 0: 30583 + 11,4: + 0: 255 + 2: 61440 12,0: - 0: 65535 + 0: 65527 12,1: - 0: 65535 + 0: 28791 12,2: - 0: 4095 - 2: 61440 + 0: 119 + 3: 28672 12,3: - 2: 255 - 0: 65280 - 8,-3: - 0: 65535 - 8,-2: - 0: 65535 - 8,-1: + 3: 119 + 0: 61440 + 12,-1: + 0: 29311 + 12,4: + 0: 255 + 2: 61440 + 13,0: + 0: 49080 + 13,1: + 0: 48058 + 13,2: + 2: 13104 + 0: 34826 + 13,3: + 2: 35059 + 0: 12288 + 13,-1: + 0: 14119 + 13,4: + 0: 51 + 2: 63624 + 14,0: 0: 65535 + 14,1: + 0: 64319 + 14,2: + 0: 15235 + 14,3: + 2: 248 + 15,0: + 0: 7441 + 15,1: + 0: 12545 + 2: 49344 + 15,2: + 0: 272 + 15,3: + 2: 9023 + 15,4: + 2: 8754 + 16,0: + 0: 256 + 2: 40601 + 16,1: + 2: 40857 + 16,3: + 2: 255 + 8,-4: + 2: 8738 + 0: 34952 + 9,-4: + 0: 56789 9,-3: - 0: 65535 + 0: 26485 9,-2: - 0: 65535 - 9,-1: - 0: 65535 - 10,-2: - 0: 65535 - 10,-1: - 0: 65535 + 0: 58999 + 9,-5: + 0: 18176 10,-4: - 0: 65521 + 0: 65024 10,-3: - 0: 65535 + 0: 65520 + 10,-2: + 0: 63743 11,-4: - 0: 63472 + 0: 13056 + 2: 128 11,-3: - 0: 65535 + 0: 43946 11,-2: + 0: 30250 + 12,-4: + 2: 1265 + 12,-3: 0: 65535 - 11,-1: - 0: 65535 + 12,-2: + 0: 65359 + -4,-8: + 0: 4016 + -4,-9: + 2: 28672 + 0: 127 + -5,-8: + 0: 7091 -4,-7: - 0: 65535 + 0: 32631 + -5,-7: + 0: 7647 -4,-6: - 0: 65535 - -4,-5: - 0: 65535 - -4,-8: - 0: 65535 + 0: 14193 + -5,-6: + 0: 56797 + -5,-5: + 0: 64729 -3,-8: 0: 65535 -3,-7: - 0: 65535 + 0: 16383 -3,-6: - 0: 65535 - -3,-5: - 0: 65535 + 0: 65530 + -3,-9: + 0: 50519 -2,-8: - 0: 65535 + 0: 32767 -2,-7: - 0: 65535 + 0: 1919 -2,-6: - 0: 65535 - -2,-5: - 0: 65535 - -1,-8: - 0: 65535 - -1,-7: - 0: 65535 + 0: 57309 + -2,-9: + 0: 61440 + 2: 47 -1,-6: - 0: 65535 - -1,-5: - 0: 65535 + 0: 5489 + -8,-8: + 0: 3 + 2: 8 + -8,-9: + 0: 12288 + 2: 35056 + -9,-8: + 0: 65356 + 2: 1 + -8,-7: + 0: 240 + -9,-7: + 0: 13311 + 2: 32768 + -8,-6: + 2: 1520 + -9,-6: + 2: 29832 + 0: 3 + -8,-5: + 0: 48123 + -9,-5: + 0: 43008 + 2: 4 + -8,-4: + 0: 35771 + -7,-7: + 0: 33008 + 2: 8192 -7,-5: - 0: 65535 + 0: 61951 + -7,-4: + 0: 36317 + -7,-6: + 2: 546 + 0: 2184 + -7,-8: + 2: 68 + -7,-9: + 2: 17408 + 0: 32768 + -6,-8: + 0: 34955 + 2: 8704 + -6,-7: + 0: 47352 + -6,-6: + 0: 35835 -6,-5: - 0: 65535 - -5,-5: - 0: 65535 - -5,-7: - 0: 65535 - -5,-6: - 0: 65535 - -8,-4: - 0: 65535 + 0: 61691 + -6,-9: + 0: 61440 + 2: 34 + -6,-4: + 0: 62463 + -5,-9: + 0: 12799 + 2: 32768 + -9,-4: + 0: 34952 + 2: 800 -8,-3: - 0: 65535 + 0: 65039 + -9,-3: + 0: 56797 -8,-2: - 0: 65535 + 0: 60942 + -9,-2: + 0: 56829 + -9,-1: + 0: 56797 -8,-1: - 0: 65535 - -7,-4: - 0: 65535 + 0: 3808 -7,-3: - 0: 65535 + 0: 64907 -7,-2: - 0: 65535 - -7,-1: - 0: 65535 - -6,-4: - 0: 65535 + 0: 57293 -6,-3: - 0: 65535 + 0: 63291 -6,-2: - 0: 65535 - -6,-1: - 0: 65535 - -5,-4: - 0: 65535 + 0: 30583 -5,-3: - 0: 65535 - -5,-2: - 0: 65535 - -5,-1: - 0: 65535 + 0: 28791 + -12,0: + 0: 2056 -11,0: - 0: 65535 - -11,1: - 0: 65262 + 0: 3855 + -12,1: + 2: 2056 + -12,2: + 0: 2056 -11,2: - 0: 65535 + 0: 3855 + -12,3: + 2: 136 + -11,1: + 2: 546 + 0: 2184 + -11,3: + 2: 58030 + -11,4: + 2: 70 -10,0: 0: 65535 -10,1: 0: 65535 -10,2: - 0: 65535 - -9,0: - 0: 65535 - -9,1: - 0: 65535 - -9,2: - 0: 65535 - -9,3: - 0: 65535 - -10,-2: - 0: 65535 + 0: 36863 + -10,3: + 0: 61166 -10,-1: - 0: 65535 - -10,-3: - 0: 65535 - -9,-3: - 0: 65535 - -9,-2: - 0: 65535 - -9,-1: - 0: 65535 - -9,-4: - 0: 65519 - 12,-4: - 0: 62705 - 12,-3: - 0: 65535 - 12,-2: - 0: 65535 - 12,-1: - 0: 65535 - 13,-4: - 0: 63720 + 0: 61134 + -10,4: + 0: 61154 + -12,-3: + 0: 4095 + -13,-3: + 0: 4095 + -12,-4: + 0: 32768 + -11,-3: + 0: 4095 + -12,-2: + 2: 136 + -11,-2: + 2: 15 + 0: 60928 + -12,-1: + 2: 2184 + -11,-1: + 0: 14 + 2: 3584 + -10,-3: + 0: 53247 + -10,-2: + 2: 1 + 0: 65228 + -10,-4: + 2: 3720 + -10,-5: + 2: 32776 + 12,-5: + 2: 61440 + 0: 127 13,-3: - 0: 65535 + 0: 48059 13,-2: - 0: 65535 - 13,-1: - 0: 65535 - 10,-8: - 0: 34816 + 0: 63243 + 13,-4: + 2: 2280 + 13,-5: + 2: 35304 + 14,-4: + 2: 1039 + 14,-3: + 0: 13107 + 2: 128 + 14,-2: + 0: 65283 + 14,-1: + 0: 4095 + 14,-5: + 2: 17476 + 15,-4: + 2: 8739 + 15,-3: + 2: 12914 + 15,-2: + 0: 4352 + 2: 14 + 15,-1: + 0: 273 + 2: 52224 + 15,-5: + 2: 8704 + 16,-2: + 2: 61455 + 16,-1: + 2: 40857 + 9,-7: + 2: 15 + 0: 65280 + 9,-6: + 0: 15 + 2: 3840 10,-7: - 0: 65535 + 2: 7 + 0: 65280 10,-6: - 0: 36863 - 10,-5: - 0: 8 - 11,-8: - 0: 65532 + 0: 15 + 2: 34560 + 10,-8: + 2: 34816 11,-7: - 0: 65535 + 0: 65534 11,-6: - 0: 65535 + 0: 61183 + 10,-5: + 2: 8 + 11,-8: + 2: 52 + 0: 59392 11,-5: - 0: 36095 + 2: 33840 + 0: 8 12,-8: - 0: 65535 + 0: 65392 12,-7: 0: 65535 12,-6: 0: 65535 - 12,-5: - 0: 65535 + 20,-2: + 2: 8739 + 19,-2: + 2: 28687 + 20,-1: + 2: 26146 + 20,0: + 2: 8230 + 19,0: + 2: 20292 + 20,1: + 2: 9830 + 20,2: + 2: 8738 + 20,3: + 2: 3 + 19,3: + 2: 127 13,-8: - 0: 65393 + 2: 34913 + 0: 12288 13,-7: - 0: 65535 + 0: 65395 13,-6: - 0: 65535 - 13,-5: - 0: 35327 - 14,-8: - 0: 17476 + 0: 13183 + 2: 32768 14,-7: - 0: 30581 + 2: 26213 14,-6: - 0: 17783 - 14,-5: - 0: 17476 + 2: 17766 + 14,-8: + 2: 17476 14,-9: - 0: 17476 - 6,-7: - 0: 65535 - 6,-6: - 0: 65535 - 7,-7: - 0: 48063 - 7,-6: - 0: 65467 - -7,4: - 0: 65535 - -7,5: - 0: 61183 - -6,4: - 0: 65535 - -6,5: - 0: 65535 - 4,5: - 0: 53247 - 4,-8: - 0: 65535 - 5,-8: - 0: 65535 - 6,-8: - 0: 65535 - 7,-8: - 0: 13119 - -8,4: - 0: 65535 - -8,5: - 0: 30719 - -8,6: - 0: 63359 - -7,6: - 0: 64751 - -6,6: - 0: 4095 + 2: 17476 + -11,5: + 2: 35916 -10,5: - 0: 56799 + 2: 39296 + 0: 17488 + -11,6: + 2: 2184 -10,6: - 0: 56799 - -9,5: - 0: 65535 - -9,6: - 0: 65535 - -9,4: - 0: 65535 - 3,-9: - 0: 62836 - 4,-9: - 0: 64716 - 6,4: - 0: 65535 - 7,4: - 0: 65535 - 7,5: - 0: 65535 - 7,6: - 0: 53196 - 8,4: - 0: 65535 - 8,5: - 0: 48063 - 3: 17472 - 8,6: - 0: 65359 - 9,4: - 0: 65535 - 9,5: - 0: 43695 - 4: 4368 - 2: 17472 - 9,6: - 0: 7951 - 10,4: - 0: 65535 - 10,5: - 0: 43695 - 2: 4368 - 5: 17472 - 11,4: - 0: 65535 - 11,5: - 0: 43695 - 2: 21840 - 12,4: - 0: 65535 - 12,5: - 0: 13183 - 13,4: - 0: 65535 - 13,5: - 0: 1 - -8,7: - 0: 242 - 8,-8: - 0: 1 - 8,-7: - 0: 65535 - 8,-10: - 0: 240 - -10,4: - 0: 65535 + 0: 21569 + 2: 35230 -10,7: - 0: 192 - -9,7: - 0: 240 + 2: 192 + 0,-12: + 2: 79 + 0: 12288 + -1,-12: + 2: 79 + 0: 32768 + 0,-11: + 0: 29107 + -1,-11: + 0: 2047 + 0,-10: + 0: 247 + 2: 16384 + -1,-10: + 0: 255 + 2: 36864 + -1,-9: + 2: 313 + 1,-12: + 2: 4375 + 1,-11: + 0: 3536 + 1,-10: + 0: 3831 + 2,-11: + 0: 65520 + 2,-10: + 0: 14196 + 2,-12: + 2: 44800 + 3,-12: + 2: 768 + 3,-10: + 2: 18240 + 3,-9: + 2: 1396 5,-10: - 0: 17648 - 5,-9: - 0: 65535 + 2: 17648 6,-10: - 0: 240 - 6,-9: - 0: 65535 + 2: 240 7,-10: - 0: 240 - 7,-9: - 0: 13107 + 2: 240 + 8,-10: + 2: 240 + 8,8: + 2: 65534 + 9,5: + 5: 4368 + 3: 17472 + 9,6: + 2: 7936 + 10,5: + 3: 4368 + 6: 17472 10,6: - 0: 3855 + 2: 3840 + 11,5: + 3: 21840 11,6: - 0: 3871 + 2: 3856 + 12,5: + 2: 65535 12,6: - 0: 15 - 6,5: - 0: 57343 - 14,-4: - 0: 29711 - 14,-3: - 0: 30711 - 14,-2: - 0: 65527 - 5,4: - 0: 65535 - 5,5: - 0: 65535 - 5,7: - 0: 15 - 5,6: - 0: 65535 - 6,7: - 0: 1 - 6,6: - 0: 39417 - 2,-9: - 0: 63479 - 15,-4: - 0: 8739 - 15,-5: - 0: 8704 - 3,7: - 0: 3 - -4,7: - 0: 2289 - -3,7: - 0: 11000 - -2,7: - 0: 4095 - -7,7: - 0: 16 - -6,7: - 0: 240 - -5,7: - 0: 240 - -1,9: - 0: 142 - 0,9: - 0: 15 - 1,9: - 0: 15 - 13,0: - 0: 65535 - 13,1: - 0: 65535 - 13,2: - 0: 65535 - 13,3: - 0: 65535 - 14,0: - 0: 65535 - 14,2: - 0: 65535 - 14,1: - 0: 65535 - 14,3: - 0: 255 - -8,-5: - 0: 65535 - -5,-8: - 0: 65535 - -11,3: - 0: 58030 - -10,3: - 0: 65535 - 14,-1: - 0: 65535 - 15,-3: - 0: 12914 - -11,4: - 0: 70 - 0,-9: - 0: 65519 - 1,-9: - 0: 65535 + 2: 15 + 13,5: + 2: 39321 13,6: - 0: 15 - 14,6: - 0: 7 + 2: 15 14,4: - 0: 28672 + 2: 28672 + 14,6: + 2: 7 14,5: - 0: 17476 - -1,-9: - 0: 61753 - 15,0: - 0: 16179 - 15,1: - 0: 62451 - 15,2: - 0: 62259 - 8,-4: - 0: 61166 - 9,-4: - 0: 65535 - 15,-2: - 0: 13310 - 15,-1: - 0: 65331 - 8,-6: - 0: 32767 - 8,-5: - 0: 65527 - 9,-7: - 0: 65535 - 9,-6: - 0: 4095 - 9,-5: - 0: 65520 - 20,-2: - 0: 13107 - 20,-1: - 0: 30515 - 20,0: - 0: 12599 - 20,1: - 0: 14199 - 20,2: - 0: 13107 - 2,-10: - 0: 65535 - 3,-10: - 0: 18241 + 2: 17484 + 15,5: + 2: 35 + -4,-10: + 0: 6143 + -5,-10: + 0: 14472 + 2: 2 + -3,-10: + 0: 29949 + -3,-11: + 2: 304 + 0: 34944 -2,-10: - 0: 32767 - -2,-9: - 0: 65327 - -1,-10: - 0: 40959 - 16,-2: - 0: 61695 - 16,-1: - 0: 40857 + 0: 1019 + -2,-11: + 2: 544 + 0: 2176 + 16,-3: + 2: 61440 17,-2: - 0: 63731 + 2: 63491 17,-1: - 0: 36744 + 2: 36744 18,-2: - 0: 61694 + 2: 61454 18,-1: - 0: 36744 - 19,-2: - 0: 28927 + 2: 36744 + 17,0: + 2: 36744 + 18,-3: + 2: 32768 + 19,-3: + 2: 28672 19,-1: - 0: 18244 - 16,0: - 0: 40857 - 16,1: - 0: 40857 + 2: 18244 + 18,0: + 2: 36744 16,2: - 0: 61689 - 17,0: - 0: 36744 + 2: 249 17,1: - 0: 36744 + 2: 36744 17,2: - 0: 63736 - 18,0: - 0: 36744 + 2: 2296 + 17,3: + 2: 3 18,1: - 0: 36744 + 2: 36744 18,2: - 0: 61688 - 19,0: - 0: 20292 + 2: 248 + 18,3: + 2: 142 19,1: - 0: 18244 + 2: 18244 19,2: - 0: 61556 + 2: 116 14,-10: - 0: 17520 + 2: 17520 9,-10: - 0: 16 - -8,-8: - 0: 4479 - -8,-7: - 0: 4095 - -7,-7: - 0: 61439 - -6,-7: - 0: 65535 - -6,-8: - 0: 61183 - -6,-6: - 0: 65535 + 2: 16 -11,-8: - 0: 60074 + 2: 60074 -11,-7: - 0: 2730 + 2: 2730 -10,-8: - 0: 65262 + 2: 12846 + 0: 34816 -10,-7: - 0: 61166 + 2: 8738 + 0: 34952 -10,-6: - 0: 58094 - -9,-8: - 0: 65535 - -9,-7: - 0: 65535 - -9,-6: - 0: 62719 - -9,-10: - 0: 63728 + 2: 57890 + 0: 8 -9,-9: - 0: 65528 + 2: 4600 + 0: 49152 + -9,-10: + 2: 63728 -8,-10: - 0: 28784 - -8,-9: - 0: 65520 - -8,-6: - 0: 62960 - -6,-9: - 0: 65450 - -5,-9: - 0: 65535 - -7,-6: - 0: 65262 - -12,0: - 0: 34952 - -12,1: - 0: 34824 - -12,2: - 0: 34952 - -12,3: - 0: 136 - -12,-1: - 0: 34952 - -11,-1: - 0: 65279 - -11,-4: - 0: 61440 - -11,-3: - 0: 65535 - -11,-2: - 0: 65535 - -10,-4: - 0: 65160 - 0,-10: - 0: 20479 - 1,-10: - 0: 65535 - -5,-10: - 0: 65486 - -7,-8: - 0: 204 - -4,-9: - 0: 65535 - -3,-9: - 0: 65535 - -7,-9: - 0: 52224 - 7,7: - 0: 12 - 8,7: - 0: 61167 - 8,8: - 0: 65534 - 8,9: - 0: 3839 - 4,6: - 0: 52428 - 4,7: - 0: 12 - -3,8: - 0: 3086 - -2,8: - 0: 7967 - -11,5: - 0: 35916 - -11,6: - 0: 2184 - -12,-4: - 0: 61440 - -12,-3: - 0: 65535 - -12,-2: - 0: 136 - -10,-5: - 0: 32776 - -9,-5: - 0: 65484 - 0,-12: - 0: 30543 - 0,-11: - 0: 65535 - 1,-12: - 0: 4375 - 1,-11: - 0: 65535 - 2,-12: - 0: 44800 - 2,-11: - 0: 65535 - 3,-12: - 0: 768 - 3,-11: - 0: 4369 - -4,-11: - 0: 61440 - -4,-10: - 0: 65535 - -3,-11: - 0: 65008 - -3,-10: - 0: 65535 - -2,-12: - 0: 32768 - -2,-11: - 0: 65532 - -1,-12: - 0: 64591 - -1,-11: - 0: 65535 + 2: 28784 -6,-11: - 0: 8192 + 2: 8192 -6,-10: - 0: 60078 - -5,-11: - 0: 49152 + 2: 25262 + 8,9: + 2: 2815 9,8: - 0: 4368 + 2: 4368 9,9: - 0: 17 - 2,9: - 0: 1 + 2: 17 -14,-3: - 0: 61166 - -14,-4: - 0: 49152 + 0: 3272 -13,-4: - 0: 61440 - -13,-3: - 0: 65535 - 15,3: - 0: 31 - 20,3: - 0: 3 - 16,-3: - 0: 61440 - 18,-3: - 0: 32768 - 19,-3: - 0: 28672 - 16,3: - 0: 255 - 17,3: - 0: 3 - 18,3: - 0: 142 - 19,3: - 0: 127 + 0: 4096 uniqueMixes: - volume: 2500 temperature: 293.15 @@ -1337,6 +1410,21 @@ entities: - 0 - 0 - 0 + - volume: 2500 + immutable: True + moles: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 - volume: 2500 temperature: 293.15 moles: @@ -26130,18 +26218,6 @@ entities: - type: Transform pos: -48.183727,-9.500211 parent: 31 -- proto: chem_master - entities: - - uid: 606 - components: - - type: Transform - pos: 19.5,-0.5 - parent: 31 - - uid: 5075 - components: - - type: Transform - pos: 15.5,1.5 - parent: 31 - proto: ChemDispenser entities: - uid: 5076 @@ -26161,6 +26237,18 @@ entities: - type: Transform pos: 18.5,1.5 parent: 31 +- proto: ChemMaster + entities: + - uid: 606 + components: + - type: Transform + pos: 19.5,-0.5 + parent: 31 + - uid: 5075 + components: + - type: Transform + pos: 15.5,1.5 + parent: 31 - proto: ChessBoard entities: - uid: 841 @@ -27734,14 +27822,6 @@ entities: - type: Transform pos: 8.5,30.5 parent: 31 -- proto: ComputerFrame - entities: - - uid: 7085 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 10.5,-16.5 - parent: 31 - proto: ComputerId entities: - uid: 810 @@ -34314,12 +34394,6 @@ entities: parent: 31 - type: AtmosPipeColor color: '#0055CCFF' - - uid: 2227 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 9.5,-17.5 - parent: 31 - uid: 2417 components: - type: Transform @@ -42265,6 +42339,12 @@ entities: parent: 31 - type: AtmosPipeColor color: '#0055CCFF' + - uid: 2227 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 9.5,-17.5 + parent: 31 - uid: 2282 components: - type: Transform @@ -43171,11 +43251,15 @@ entities: - uid: 10912 components: - type: Transform + anchored: False rot: 1.5707963267948966 rad pos: 53.5,-7.5 parent: 31 - type: AtmosPipeColor color: '#990000FF' + - type: Physics + canCollide: True + bodyType: Dynamic - uid: 10931 components: - type: Transform @@ -43473,9 +43557,11 @@ entities: - uid: 2215 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 7.5,-17.5 + rot: -1.5707963267948966 rad + pos: 10.5,-17.5 parent: 31 + - type: GasThermoMachine + targetTemperature: 150 - proto: GasThermoMachineHeater entities: - uid: 8861 @@ -49186,13 +49272,6 @@ entities: - type: Transform pos: 14.5,0.5 parent: 31 -- proto: MachineFrame - entities: - - uid: 7464 - components: - - type: Transform - pos: 10.5,-17.5 - parent: 31 - proto: MachineFrameDestroyed entities: - uid: 9667 @@ -57149,7 +57228,7 @@ entities: - type: Transform pos: -6.517076,-41.294003 parent: 31 -- proto: soda_dispenser +- proto: SodaDispenser entities: - uid: 1418 components: @@ -60963,13 +61042,6 @@ entities: - type: Transform pos: 48.60447,5.4525433 parent: 31 -- proto: UnfinishedMachineFrame - entities: - - uid: 7105 - components: - - type: Transform - pos: 10.5,-15.5 - parent: 31 - proto: UniformPrinter entities: - uid: 8408 From dc895feab3a6f105be81c37a2a7ab9bf4f2d838b Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Tue, 28 May 2024 09:52:59 +0200 Subject: [PATCH 090/568] Fix unlocked locker on Omega (#28247) --- Resources/Maps/omega.yml | 6002 +------------------------------------- 1 file changed, 57 insertions(+), 5945 deletions(-) diff --git a/Resources/Maps/omega.yml b/Resources/Maps/omega.yml index bf3ee069976a..07ebb24b9da8 100644 --- a/Resources/Maps/omega.yml +++ b/Resources/Maps/omega.yml @@ -37,12 +37,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 - uid: 4812 components: - type: MetaData @@ -3786,7 +3787,8 @@ entities: -9,2: 0: 65535 -9,3: - 0: 65535 + 2: 1 + 0: 65534 -8,0: 0: 65535 -8,-2: @@ -4217,8 +4219,6 @@ entities: - 8806 - 5025 - 6554 - - type: AtmosDevice - joinedGrid: 4812 - uid: 4982 components: - type: Transform @@ -4229,16 +4229,12 @@ entities: devices: - 4941 - 4875 - - type: AtmosDevice - joinedGrid: 4812 - uid: 6692 components: - type: Transform rot: -1.5707963267948966 rad pos: -23.5,-26.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 7234 components: - type: Transform @@ -4249,8 +4245,6 @@ entities: devices: - 4890 - 4876 - - type: AtmosDevice - joinedGrid: 4812 - uid: 7236 components: - type: Transform @@ -4261,8 +4255,6 @@ entities: devices: - 8838 - 8836 - - type: AtmosDevice - joinedGrid: 4812 - uid: 7241 components: - type: Transform @@ -4272,16 +4264,12 @@ entities: devices: - 6686 - 7227 - - type: AtmosDevice - joinedGrid: 4812 - uid: 7249 components: - type: Transform rot: 1.5707963267948966 rad pos: -11.5,-27.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 7284 components: - type: Transform @@ -4292,8 +4280,6 @@ entities: - 12226 - 1853 - 1852 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12171 components: - type: Transform @@ -4305,8 +4291,6 @@ entities: - 12170 - 12027 - 12026 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12173 components: - type: Transform @@ -4318,8 +4302,6 @@ entities: - 12028 - 12025 - 12172 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12174 components: - type: Transform @@ -4331,8 +4313,6 @@ entities: - 12175 - 12029 - 12024 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12176 components: - type: Transform @@ -4343,8 +4323,6 @@ entities: - 12177 - 12032 - 12033 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12178 components: - type: Transform @@ -4361,8 +4339,6 @@ entities: - 10754 - 10755 - 10756 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12182 components: - type: Transform @@ -4371,8 +4347,6 @@ entities: - type: DeviceList devices: - 12181 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12183 components: - type: Transform @@ -4389,8 +4363,6 @@ entities: - 10754 - 10755 - 10756 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12186 components: - type: Transform @@ -4406,8 +4378,6 @@ entities: - 12185 - 3708 - 8787 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12190 components: - type: Transform @@ -4420,8 +4390,6 @@ entities: - 8691 - 12034 - 11962 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12193 components: - type: Transform @@ -4441,8 +4409,6 @@ entities: - 12189 - 12197 - 12196 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12198 components: - type: Transform @@ -4456,8 +4422,6 @@ entities: - 12197 - 1685 - 1686 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12202 components: - type: Transform @@ -4475,8 +4439,6 @@ entities: - 12204 - 12039 - 12040 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12206 components: - type: Transform @@ -4489,8 +4451,6 @@ entities: - 12205 - 8562 - 8563 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12209 components: - type: Transform @@ -4515,8 +4475,6 @@ entities: - 7873 - 7887 - 7886 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12213 components: - type: Transform @@ -4527,8 +4485,6 @@ entities: - 7905 - 7907 - 12214 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12215 components: - type: Transform @@ -4541,8 +4497,6 @@ entities: - 12212 - 7906 - 7908 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12219 components: - type: Transform @@ -4553,8 +4507,6 @@ entities: - 12218 - 7858 - 7856 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12220 components: - type: Transform @@ -4569,8 +4521,6 @@ entities: - 12222 - 8307 - 8306 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12223 components: - type: Transform @@ -4585,8 +4535,6 @@ entities: - 12225 - 1817 - 1816 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12228 components: - type: Transform @@ -4600,8 +4548,6 @@ entities: - 1928 - 2474 - 2441 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12232 components: - type: Transform @@ -4615,8 +4561,6 @@ entities: - 2472 - 2442 - 2475 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12234 components: - type: Transform @@ -4635,8 +4579,6 @@ entities: - 2448 - 2446 - 2445 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12240 components: - type: Transform @@ -4651,8 +4593,6 @@ entities: - 2071 - 2072 - 2073 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12245 components: - type: Transform @@ -4669,8 +4609,6 @@ entities: - 2782 - 1770 - 3782 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12249 components: - type: Transform @@ -4688,8 +4626,6 @@ entities: - 2786 - 4439 - 4440 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12252 components: - type: Transform @@ -4702,8 +4638,6 @@ entities: - 12254 - 3021 - 3024 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12256 components: - type: Transform @@ -4717,8 +4651,6 @@ entities: - 3017 - 3019 - 12257 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12258 components: - type: Transform @@ -4730,8 +4662,6 @@ entities: - 12259 - 3001 - 2983 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12260 components: - type: Transform @@ -4742,8 +4672,6 @@ entities: - 3814 - 12261 - 3944 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12262 components: - type: Transform @@ -4755,8 +4683,6 @@ entities: - 3816 - 12263 - 3841 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12265 components: - type: Transform @@ -4768,8 +4694,6 @@ entities: - 3813 - 12264 - 3835 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12266 components: - type: Transform @@ -4780,8 +4704,6 @@ entities: - 3815 - 3838 - 12267 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12272 components: - type: Transform @@ -4797,8 +4719,6 @@ entities: - 4117 - 4442 - 4441 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12275 components: - type: Transform @@ -4812,8 +4732,6 @@ entities: - 4425 - 4380 - 4381 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12278 components: - type: Transform @@ -4826,8 +4744,6 @@ entities: - 4424 - 4444 - 4443 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12282 components: - type: Transform @@ -4838,8 +4754,6 @@ entities: - 4226 - 12281 - 4231 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12283 components: - type: Transform @@ -4851,8 +4765,6 @@ entities: - 4205 - 12284 - 4203 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12285 components: - type: Transform @@ -4867,8 +4779,6 @@ entities: - 6414 - 6417 - 6416 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12289 components: - type: Transform @@ -4888,8 +4798,6 @@ entities: - 12288 - 6420 - 6419 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12293 components: - type: Transform @@ -4904,8 +4812,6 @@ entities: - 12295 - 12038 - 12037 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12296 components: - type: Transform @@ -4916,8 +4822,6 @@ entities: - 1511 - 12297 - 1512 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12299 components: - type: Transform @@ -4930,8 +4834,6 @@ entities: - 858 - 857 - 848 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12302 components: - type: Transform @@ -4958,8 +4860,6 @@ entities: - 817 - 816 - 864 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12304 components: - type: Transform @@ -4970,8 +4870,6 @@ entities: - 877 - 885 - 12303 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12307 components: - type: Transform @@ -4982,8 +4880,6 @@ entities: - 847 - 846 - 12306 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12308 components: - type: Transform @@ -4997,8 +4893,6 @@ entities: - 784 - 835 - 814 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12311 components: - type: Transform @@ -5013,8 +4907,6 @@ entities: - 1938 - 710 - 711 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12316 components: - type: Transform @@ -5044,8 +4936,6 @@ entities: - 328 - 3582 - 2759 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12319 components: - type: Transform @@ -5066,8 +4956,6 @@ entities: - 8591 - 8758 - 8770 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12322 components: - type: Transform @@ -5083,8 +4971,6 @@ entities: - 5222 - 5224 - 5225 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12325 components: - type: Transform @@ -5095,8 +4981,6 @@ entities: - 6842 - 6839 - 12326 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12327 components: - type: Transform @@ -5107,16 +4991,12 @@ entities: - 6841 - 6840 - 12328 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12330 components: - type: Transform rot: 3.141592653589793 rad pos: -9.5,-29.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12341 components: - type: Transform @@ -5129,8 +5009,6 @@ entities: - 12343 - 7380 - 7382 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12344 components: - type: Transform @@ -5141,8 +5019,6 @@ entities: - 7088 - 7087 - 12345 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12346 components: - type: Transform @@ -5153,8 +5029,6 @@ entities: - 5700 - 5697 - 12347 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12350 components: - type: Transform @@ -5165,8 +5039,6 @@ entities: - 5699 - 5698 - 12349 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12352 components: - type: Transform @@ -5177,8 +5049,6 @@ entities: - 5715 - 5716 - 12351 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12353 components: - type: Transform @@ -5191,8 +5061,6 @@ entities: - 12355 - 5718 - 5719 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12356 components: - type: Transform @@ -5202,8 +5070,6 @@ entities: - type: DeviceList devices: - 12357 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12358 components: - type: Transform @@ -5214,8 +5080,6 @@ entities: - 5765 - 5763 - 12359 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12361 components: - type: Transform @@ -5226,8 +5090,6 @@ entities: - 5766 - 5764 - 12360 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12362 components: - type: Transform @@ -5238,8 +5100,6 @@ entities: - 12363 - 9007 - 8991 - - type: AtmosDevice - joinedGrid: 4812 - proto: AirAlarmElectronics entities: - uid: 10719 @@ -5259,77 +5119,55 @@ entities: - type: Transform pos: 15.5,44.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9041 components: - type: Transform pos: -34.5,-37.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9671 components: - type: Transform pos: -31.5,4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9672 components: - type: Transform pos: -31.5,5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12398 components: - type: Transform pos: -34.5,-3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12399 components: - type: Transform pos: -34.5,-2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: Airlock entities: - uid: 1531 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,0.5 parent: 4812 - uid: 1534 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,3.5 parent: 4812 - uid: 4116 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,7.5 parent: 4812 - uid: 6694 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-34.5 parent: 4812 - uid: 7025 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,-34.5 parent: 4812 @@ -5361,22 +5199,16 @@ entities: entities: - uid: 8862 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-36.5 parent: 4812 - uid: 9563 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-0.5 parent: 4812 - uid: 9566 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,2.5 parent: 4812 @@ -5384,8 +5216,6 @@ entities: entities: - uid: 325 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,7.5 parent: 4812 @@ -5427,15 +5257,11 @@ entities: entities: - uid: 2419 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,26.5 parent: 4812 - uid: 2463 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,28.5 parent: 4812 @@ -5471,22 +5297,17 @@ entities: - uid: 329 components: - type: MetaData - flags: PvsPriority name: Chaplain's Room - type: Transform pos: -25.5,-36.5 parent: 4812 - uid: 6792 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-36.5 parent: 4812 - uid: 6793 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-34.5 parent: 4812 @@ -5494,8 +5315,6 @@ entities: entities: - uid: 6617 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-19.5 parent: 4812 @@ -5576,15 +5395,11 @@ entities: entities: - uid: 5174 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,23.5 parent: 4812 - uid: 11928 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,18.5 parent: 4812 @@ -5592,15 +5407,11 @@ entities: entities: - uid: 5358 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-13.5 parent: 4812 - uid: 5359 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-17.5 parent: 4812 @@ -5630,106 +5441,76 @@ entities: entities: - uid: 901 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,9.5 parent: 4812 - uid: 3726 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,2.5 parent: 4812 - uid: 4370 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,0.5 parent: 4812 - uid: 4371 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,3.5 parent: 4812 - uid: 5912 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-32.5 parent: 4812 - uid: 6885 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-32.5 parent: 4812 - uid: 7956 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,7.5 parent: 4812 - uid: 9564 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-4.5 parent: 4812 - uid: 9663 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-8.5 parent: 4812 - uid: 9664 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-8.5 parent: 4812 - uid: 9888 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-12.5 parent: 4812 - uid: 9958 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-20.5 parent: 4812 - uid: 10165 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-23.5 parent: 4812 - uid: 11386 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 33.5,-37.5 parent: 4812 - uid: 11688 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 6.5,4.5 @@ -5919,50 +5700,36 @@ entities: entities: - uid: 2737 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,31.5 parent: 4812 - uid: 2814 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,33.5 parent: 4812 - uid: 3349 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,10.5 parent: 4812 - uid: 3350 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,10.5 parent: 4812 - uid: 3686 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,44.5 parent: 4812 - uid: 3687 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,45.5 parent: 4812 - uid: 10405 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,-22.5 parent: 4812 @@ -5970,8 +5737,6 @@ entities: entities: - uid: 3119 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,35.5 parent: 4812 @@ -5979,8 +5744,6 @@ entities: entities: - uid: 716 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,-4.5 parent: 4812 @@ -6147,15 +5910,11 @@ entities: entities: - uid: 2461 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,27.5 parent: 4812 - uid: 2462 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,28.5 parent: 4812 @@ -6170,22 +5929,16 @@ entities: entities: - uid: 7934 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,25.5 parent: 4812 - uid: 7936 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,21.5 parent: 4812 - uid: 8242 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,27.5 parent: 4812 @@ -6205,8 +5958,6 @@ entities: entities: - uid: 720 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-10.5 parent: 4812 @@ -6221,15 +5972,11 @@ entities: entities: - uid: 7949 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,6.5 parent: 4812 - uid: 8686 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,0.5 parent: 4812 @@ -6237,8 +5984,6 @@ entities: entities: - uid: 324 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,7.5 parent: 4812 @@ -6246,8 +5991,6 @@ entities: entities: - uid: 2418 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,24.5 parent: 4812 @@ -6255,8 +5998,6 @@ entities: entities: - uid: 2741 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,24.5 parent: 4812 @@ -6264,15 +6005,11 @@ entities: entities: - uid: 6790 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-31.5 parent: 4812 - uid: 6791 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-31.5 parent: 4812 @@ -6280,8 +6017,6 @@ entities: entities: - uid: 7932 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,21.5 parent: 4812 @@ -6289,29 +6024,21 @@ entities: entities: - uid: 8683 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,-10.5 parent: 4812 - uid: 8684 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,-10.5 parent: 4812 - uid: 8685 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,-6.5 parent: 4812 - uid: 9830 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -44.5,-15.5 parent: 4812 @@ -6331,8 +6058,6 @@ entities: entities: - uid: 309 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,-4.5 parent: 4812 @@ -6340,8 +6065,6 @@ entities: entities: - uid: 719 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-5.5 parent: 4812 @@ -6349,15 +6072,11 @@ entities: entities: - uid: 717 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-1.5 parent: 4812 - uid: 718 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-6.5 parent: 4812 @@ -6365,246 +6084,176 @@ entities: entities: - uid: 336 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,-8.5 parent: 4812 - uid: 337 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,-3.5 parent: 4812 - uid: 338 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,2.5 parent: 4812 - uid: 339 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -5.5,13.5 parent: 4812 - uid: 340 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,14.5 parent: 4812 - uid: 341 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,10.5 parent: 4812 - uid: 342 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-4.5 parent: 4812 - uid: 343 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-10.5 parent: 4812 - uid: 344 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,3.5 parent: 4812 - uid: 1252 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,2.5 parent: 4812 - uid: 2882 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,21.5 parent: 4812 - uid: 4372 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,3.5 parent: 4812 - uid: 4374 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,10.5 parent: 4812 - uid: 4375 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,0.5 parent: 4812 - uid: 4376 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-7.5 parent: 4812 - uid: 4377 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-7.5 parent: 4812 - uid: 5392 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,-13.5 parent: 4812 - uid: 5399 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,-27.5 parent: 4812 - uid: 5838 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-30.5 parent: 4812 - uid: 6982 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,-33.5 parent: 4812 - uid: 7054 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-21.5 parent: 4812 - uid: 7954 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,8.5 parent: 4812 - uid: 8681 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-11.5 parent: 4812 - uid: 8848 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-31.5 parent: 4812 - uid: 8861 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-26.5 parent: 4812 - uid: 8866 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,-34.5 parent: 4812 - uid: 8867 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-39.5 parent: 4812 - uid: 10157 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-22.5 parent: 4812 - uid: 10487 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-26.5 parent: 4812 - uid: 11300 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-35.5 parent: 4812 - uid: 11326 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,-34.5 parent: 4812 - uid: 11350 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 34.5,-34.5 parent: 4812 - uid: 11434 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 31.5,-35.5 parent: 4812 - uid: 11676 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,-37.5 parent: 4812 - uid: 11802 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,-2.5 parent: 4812 @@ -6612,15 +6261,11 @@ entities: entities: - uid: 6788 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-23.5 parent: 4812 - uid: 7336 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,-29.5 parent: 4812 @@ -6628,29 +6273,21 @@ entities: entities: - uid: 5497 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-15.5 parent: 4812 - uid: 5498 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,-29.5 parent: 4812 - uid: 5499 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-29.5 parent: 4812 - uid: 5787 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 33.5,-29.5 parent: 4812 @@ -6658,8 +6295,6 @@ entities: entities: - uid: 7806 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,9.5 parent: 4812 @@ -6667,8 +6302,6 @@ entities: entities: - uid: 714 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -8.5,10.5 parent: 4812 @@ -6708,29 +6341,21 @@ entities: entities: - uid: 7410 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-26.5 parent: 4812 - uid: 7414 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-17.5 parent: 4812 - uid: 7415 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-19.5 parent: 4812 - uid: 7416 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-13.5 parent: 4812 @@ -6745,8 +6370,6 @@ entities: entities: - uid: 2817 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,28.5 parent: 4812 @@ -6766,8 +6389,6 @@ entities: entities: - uid: 2921 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,14.5 parent: 4812 @@ -6832,36 +6453,26 @@ entities: entities: - uid: 5396 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-20.5 parent: 4812 - uid: 5484 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-21.5 parent: 4812 - uid: 5485 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-21.5 parent: 4812 - uid: 5486 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-20.5 parent: 4812 - uid: 5487 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-24.5 parent: 4812 @@ -6916,8 +6527,6 @@ entities: entities: - uid: 6675 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,-20.5 parent: 4812 @@ -6925,15 +6534,11 @@ entities: entities: - uid: 713 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,-0.5 parent: 4812 - uid: 886 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -8.5,2.5 parent: 4812 @@ -6953,8 +6558,6 @@ entities: entities: - uid: 3771 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-33.5 parent: 4812 @@ -7638,3046 +7241,2176 @@ entities: entities: - uid: 2840 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,34.5 parent: 4812 - uid: 3012 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,37.5 parent: 4812 - uid: 3044 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,38.5 parent: 4812 - uid: 3045 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,38.5 parent: 4812 - uid: 3295 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,36.5 parent: 4812 - uid: 3353 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,34.5 parent: 4812 - uid: 3372 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,33.5 parent: 4812 - uid: 3373 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,31.5 parent: 4812 - uid: 3374 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,37.5 parent: 4812 - uid: 3375 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,35.5 parent: 4812 - uid: 3376 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,33.5 parent: 4812 - uid: 3394 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,32.5 parent: 4812 - uid: 3395 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,32.5 parent: 4812 - uid: 3396 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,33.5 parent: 4812 - uid: 3397 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,35.5 parent: 4812 - uid: 3398 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,38.5 parent: 4812 - uid: 3399 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,36.5 parent: 4812 - uid: 3400 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,34.5 parent: 4812 - uid: 3427 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,32.5 parent: 4812 - uid: 3433 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,36.5 parent: 4812 - uid: 3440 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,32.5 parent: 4812 - uid: 3450 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,32.5 parent: 4812 - uid: 3459 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,37.5 parent: 4812 - uid: 3460 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,38.5 parent: 4812 - uid: 3461 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,35.5 parent: 4812 - uid: 3579 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,37.5 parent: 4812 - uid: 3583 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,42.5 parent: 4812 - uid: 3584 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,42.5 parent: 4812 - uid: 3585 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,42.5 parent: 4812 - uid: 3586 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,41.5 parent: 4812 - uid: 3587 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,42.5 parent: 4812 - uid: 3588 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,41.5 parent: 4812 - uid: 3589 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,42.5 parent: 4812 - uid: 3590 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,41.5 parent: 4812 - uid: 3591 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,41.5 parent: 4812 - uid: 3592 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,41.5 parent: 4812 - uid: 3593 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,41.5 parent: 4812 - uid: 3594 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,41.5 parent: 4812 - uid: 3595 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,40.5 parent: 4812 - uid: 3596 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,40.5 parent: 4812 - uid: 3597 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,40.5 parent: 4812 - uid: 3598 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,43.5 parent: 4812 - uid: 3599 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,44.5 parent: 4812 - uid: 3600 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,45.5 parent: 4812 - uid: 3601 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,47.5 parent: 4812 - uid: 3602 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,47.5 parent: 4812 - uid: 3603 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,48.5 parent: 4812 - uid: 3604 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,49.5 parent: 4812 - uid: 3605 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,49.5 parent: 4812 - uid: 3606 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,48.5 parent: 4812 - uid: 3607 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,48.5 parent: 4812 - uid: 3608 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,48.5 parent: 4812 - uid: 3609 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,49.5 parent: 4812 - uid: 3610 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,49.5 parent: 4812 - uid: 3612 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,49.5 parent: 4812 - uid: 3613 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,50.5 parent: 4812 - uid: 3614 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,50.5 parent: 4812 - uid: 3615 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,51.5 parent: 4812 - uid: 3616 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,50.5 parent: 4812 - uid: 3617 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,51.5 parent: 4812 - uid: 3618 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,51.5 parent: 4812 - uid: 3619 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,51.5 parent: 4812 - uid: 3620 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,53.5 parent: 4812 - uid: 3621 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,52.5 parent: 4812 - uid: 3622 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,52.5 parent: 4812 - uid: 3623 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,50.5 parent: 4812 - uid: 3624 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,53.5 parent: 4812 - uid: 3625 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,53.5 parent: 4812 - uid: 3626 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,54.5 parent: 4812 - uid: 3627 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,54.5 parent: 4812 - uid: 3628 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,55.5 parent: 4812 - uid: 3629 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,56.5 parent: 4812 - uid: 3630 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,57.5 parent: 4812 - uid: 3631 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,57.5 parent: 4812 - uid: 3632 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,56.5 parent: 4812 - uid: 3633 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,57.5 parent: 4812 - uid: 3634 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,58.5 parent: 4812 - uid: 3635 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,58.5 parent: 4812 - uid: 3636 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,59.5 parent: 4812 - uid: 3637 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,59.5 parent: 4812 - uid: 3638 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,60.5 parent: 4812 - uid: 3639 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,59.5 parent: 4812 - uid: 3640 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,60.5 parent: 4812 - uid: 3641 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,59.5 parent: 4812 - uid: 3642 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,60.5 parent: 4812 - uid: 3643 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,59.5 parent: 4812 - uid: 3644 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,60.5 parent: 4812 - uid: 3645 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,59.5 parent: 4812 - uid: 3646 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,58.5 parent: 4812 - uid: 3647 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,59.5 parent: 4812 - uid: 3648 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,58.5 parent: 4812 - uid: 3649 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,58.5 parent: 4812 - uid: 3650 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,57.5 parent: 4812 - uid: 3651 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,57.5 parent: 4812 - uid: 3652 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,56.5 parent: 4812 - uid: 3653 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,56.5 parent: 4812 - uid: 3654 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,55.5 parent: 4812 - uid: 3655 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,54.5 parent: 4812 - uid: 3656 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,54.5 parent: 4812 - uid: 3657 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,53.5 parent: 4812 - uid: 3658 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,52.5 parent: 4812 - uid: 3659 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,52.5 parent: 4812 - uid: 3660 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,51.5 parent: 4812 - uid: 3661 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,50.5 parent: 4812 - uid: 3662 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,50.5 parent: 4812 - uid: 3663 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,49.5 parent: 4812 - uid: 3664 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,49.5 parent: 4812 - uid: 3665 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,48.5 parent: 4812 - uid: 3666 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,47.5 parent: 4812 - uid: 3667 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,47.5 parent: 4812 - uid: 3668 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,46.5 parent: 4812 - uid: 3669 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,48.5 parent: 4812 - uid: 3670 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,48.5 parent: 4812 - uid: 3671 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,48.5 parent: 4812 - uid: 3672 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,49.5 parent: 4812 - uid: 3673 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,51.5 parent: 4812 - uid: 3676 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,49.5 parent: 4812 - uid: 3679 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,52.5 parent: 4812 - uid: 3680 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,53.5 parent: 4812 - uid: 3681 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,54.5 parent: 4812 - uid: 3682 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,55.5 parent: 4812 - uid: 3683 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,55.5 parent: 4812 - uid: 3684 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,56.5 parent: 4812 - uid: 3685 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,57.5 parent: 4812 - uid: 3976 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,27.5 parent: 4812 - uid: 5032 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,32.5 parent: 4812 - uid: 5625 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-18.5 parent: 4812 - uid: 5626 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-20.5 parent: 4812 - uid: 5627 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-19.5 parent: 4812 - uid: 5808 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 31.5,-20.5 parent: 4812 - uid: 5809 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 31.5,-21.5 parent: 4812 - uid: 5810 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-21.5 parent: 4812 - uid: 5811 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-20.5 parent: 4812 - uid: 5812 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-21.5 parent: 4812 - uid: 5813 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-22.5 parent: 4812 - uid: 5814 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-23.5 parent: 4812 - uid: 5815 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 38.5,-23.5 parent: 4812 - uid: 5816 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 38.5,-24.5 parent: 4812 - uid: 5817 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 39.5,-24.5 parent: 4812 - uid: 5818 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-24.5 parent: 4812 - uid: 5819 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-29.5 parent: 4812 - uid: 5820 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-28.5 parent: 4812 - uid: 5821 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-27.5 parent: 4812 - uid: 5822 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-26.5 parent: 4812 - uid: 5823 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-25.5 parent: 4812 - uid: 5824 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 38.5,-28.5 parent: 4812 - uid: 5825 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 38.5,-27.5 parent: 4812 - uid: 5827 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 38.5,-25.5 parent: 4812 - uid: 5828 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 39.5,-25.5 parent: 4812 - uid: 5829 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 39.5,-26.5 parent: 4812 - uid: 5830 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 39.5,-27.5 parent: 4812 - uid: 5831 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 40.5,-27.5 parent: 4812 - uid: 5832 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 40.5,-26.5 parent: 4812 - uid: 5839 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-17.5 parent: 4812 - uid: 6332 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-21.5 parent: 4812 - uid: 8458 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,36.5 parent: 4812 - uid: 8459 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,35.5 parent: 4812 - uid: 8460 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,34.5 parent: 4812 - uid: 8461 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,33.5 parent: 4812 - uid: 8462 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,32.5 parent: 4812 - uid: 8463 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,30.5 parent: 4812 - uid: 8464 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,31.5 parent: 4812 - uid: 8465 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,31.5 parent: 4812 - uid: 8466 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,31.5 parent: 4812 - uid: 8467 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,31.5 parent: 4812 - uid: 8468 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,31.5 parent: 4812 - uid: 8469 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,31.5 parent: 4812 - uid: 8470 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,31.5 parent: 4812 - uid: 8471 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,31.5 parent: 4812 - uid: 8472 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,31.5 parent: 4812 - uid: 8473 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,32.5 parent: 4812 - uid: 8474 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,32.5 parent: 4812 - uid: 8475 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,32.5 parent: 4812 - uid: 8476 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,32.5 parent: 4812 - uid: 8479 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,32.5 parent: 4812 - uid: 8480 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,33.5 parent: 4812 - uid: 8483 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,33.5 parent: 4812 - uid: 8484 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,33.5 parent: 4812 - uid: 8485 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,34.5 parent: 4812 - uid: 8486 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,34.5 parent: 4812 - uid: 8487 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,34.5 parent: 4812 - uid: 8488 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,34.5 parent: 4812 - uid: 8489 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,34.5 parent: 4812 - uid: 8490 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,35.5 parent: 4812 - uid: 8491 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,35.5 parent: 4812 - uid: 8492 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,35.5 parent: 4812 - uid: 8493 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,36.5 parent: 4812 - uid: 8494 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,36.5 parent: 4812 - uid: 8495 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,37.5 parent: 4812 - uid: 8496 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,38.5 parent: 4812 - uid: 8497 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,38.5 parent: 4812 - uid: 8498 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,30.5 parent: 4812 - uid: 8499 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,33.5 parent: 4812 - uid: 8500 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,34.5 parent: 4812 - uid: 8501 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,34.5 parent: 4812 - uid: 8502 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,34.5 parent: 4812 - uid: 8503 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,34.5 parent: 4812 - uid: 8504 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,35.5 parent: 4812 - uid: 8505 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,35.5 parent: 4812 - uid: 8506 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,37.5 parent: 4812 - uid: 8507 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,37.5 parent: 4812 - uid: 8508 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,38.5 parent: 4812 - uid: 8509 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,38.5 parent: 4812 - uid: 8510 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,38.5 parent: 4812 - uid: 8511 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,38.5 parent: 4812 - uid: 8512 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,39.5 parent: 4812 - uid: 8513 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,39.5 parent: 4812 - uid: 8514 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,39.5 parent: 4812 - uid: 8515 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,39.5 parent: 4812 - uid: 8516 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,34.5 parent: 4812 - uid: 8517 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,34.5 parent: 4812 - uid: 8518 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,34.5 parent: 4812 - uid: 8519 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,34.5 parent: 4812 - uid: 8520 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,34.5 parent: 4812 - uid: 8521 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,35.5 parent: 4812 - uid: 8522 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,36.5 parent: 4812 - uid: 8523 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,36.5 parent: 4812 - uid: 8524 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,35.5 parent: 4812 - uid: 8525 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,35.5 parent: 4812 - uid: 9621 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-34.5 parent: 4812 - uid: 9622 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-33.5 parent: 4812 - uid: 9623 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-33.5 parent: 4812 - uid: 9624 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-32.5 parent: 4812 - uid: 9630 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-29.5 parent: 4812 - uid: 9631 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-30.5 parent: 4812 - uid: 9632 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-29.5 parent: 4812 - uid: 9633 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-28.5 parent: 4812 - uid: 9637 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-26.5 parent: 4812 - uid: 9638 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-38.5 parent: 4812 - uid: 9639 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-39.5 parent: 4812 - uid: 9640 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-39.5 parent: 4812 - uid: 9641 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-39.5 parent: 4812 - uid: 9642 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-39.5 parent: 4812 - uid: 9643 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-40.5 parent: 4812 - uid: 10417 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,-20.5 parent: 4812 - uid: 10418 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,-20.5 parent: 4812 - uid: 10419 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-20.5 parent: 4812 - uid: 10420 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-20.5 parent: 4812 - uid: 10421 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,-19.5 parent: 4812 - uid: 10422 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,-19.5 parent: 4812 - uid: 10423 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-19.5 parent: 4812 - uid: 10424 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,-18.5 parent: 4812 - uid: 10425 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-18.5 parent: 4812 - uid: 10426 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-18.5 parent: 4812 - uid: 10427 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-17.5 parent: 4812 - uid: 10428 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-16.5 parent: 4812 - uid: 10429 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-15.5 parent: 4812 - uid: 10430 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-15.5 parent: 4812 - uid: 10431 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,-15.5 parent: 4812 - uid: 10432 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,-15.5 parent: 4812 - uid: 10433 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,-15.5 parent: 4812 - uid: 10434 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-15.5 parent: 4812 - uid: 10435 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-15.5 parent: 4812 - uid: 10436 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-15.5 parent: 4812 - uid: 10437 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -48.5,-15.5 parent: 4812 - uid: 10438 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-15.5 parent: 4812 - uid: 10439 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-16.5 parent: 4812 - uid: 10443 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-16.5 parent: 4812 - uid: 10444 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,-16.5 parent: 4812 - uid: 10447 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-16.5 parent: 4812 - uid: 10448 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-17.5 parent: 4812 - uid: 10449 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,-17.5 parent: 4812 - uid: 10452 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-17.5 parent: 4812 - uid: 10453 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-17.5 parent: 4812 - uid: 10454 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-17.5 parent: 4812 - uid: 10455 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -48.5,-17.5 parent: 4812 - uid: 10456 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-17.5 parent: 4812 - uid: 10457 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-18.5 parent: 4812 - uid: 10458 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-18.5 parent: 4812 - uid: 10460 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,29.5 parent: 4812 - uid: 10461 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-19.5 parent: 4812 - uid: 10462 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-19.5 parent: 4812 - uid: 10463 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,28.5 parent: 4812 - uid: 10464 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-14.5 parent: 4812 - uid: 10465 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-13.5 parent: 4812 - uid: 10466 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-12.5 parent: 4812 - uid: 10467 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-11.5 parent: 4812 - uid: 10468 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-10.5 parent: 4812 - uid: 10469 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-9.5 parent: 4812 - uid: 10470 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-8.5 parent: 4812 - uid: 10472 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -57.5,-10.5 parent: 4812 - uid: 10473 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -57.5,-11.5 parent: 4812 - uid: 10474 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,28.5 parent: 4812 - uid: 10475 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -57.5,-13.5 parent: 4812 - uid: 10476 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -57.5,-14.5 parent: 4812 - uid: 10477 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -57.5,-15.5 parent: 4812 - uid: 10478 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -58.5,-13.5 parent: 4812 - uid: 10479 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -58.5,-12.5 parent: 4812 - uid: 10480 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -58.5,-11.5 parent: 4812 - uid: 10481 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -58.5,-10.5 parent: 4812 - uid: 10482 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -58.5,-9.5 parent: 4812 - uid: 10483 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-7.5 parent: 4812 - uid: 10484 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -57.5,-8.5 parent: 4812 - uid: 10485 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -58.5,-8.5 parent: 4812 - uid: 10486 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-2.5 parent: 4812 - uid: 10926 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,14.5 parent: 4812 - uid: 10927 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,15.5 parent: 4812 - uid: 10928 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,16.5 parent: 4812 - uid: 10929 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,17.5 parent: 4812 - uid: 10941 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,24.5 parent: 4812 - uid: 10942 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,25.5 parent: 4812 - uid: 10943 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,26.5 parent: 4812 - uid: 10944 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,27.5 parent: 4812 - uid: 10945 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,24.5 parent: 4812 - uid: 10947 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,26.5 parent: 4812 - uid: 10948 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,27.5 parent: 4812 - uid: 10949 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,28.5 parent: 4812 - uid: 10950 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,29.5 parent: 4812 - uid: 10951 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,30.5 parent: 4812 - uid: 10952 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,31.5 parent: 4812 - uid: 10953 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,32.5 parent: 4812 - uid: 10954 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,33.5 parent: 4812 - uid: 10955 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,32.5 parent: 4812 - uid: 10956 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,31.5 parent: 4812 - uid: 10957 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,30.5 parent: 4812 - uid: 10963 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,24.5 parent: 4812 - uid: 10965 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,15.5 parent: 4812 - uid: 10966 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,16.5 parent: 4812 - uid: 10967 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,17.5 parent: 4812 - uid: 10969 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,19.5 parent: 4812 - uid: 10970 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,20.5 parent: 4812 - uid: 10971 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,21.5 parent: 4812 - uid: 10972 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,22.5 parent: 4812 - uid: 10973 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,23.5 parent: 4812 - uid: 10974 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,24.5 parent: 4812 - uid: 10975 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,24.5 parent: 4812 - uid: 10977 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,24.5 parent: 4812 - uid: 10982 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,23.5 parent: 4812 - uid: 10983 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,18.5 parent: 4812 - uid: 10984 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,31.5 parent: 4812 - uid: 10986 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,33.5 parent: 4812 - uid: 10987 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,33.5 parent: 4812 - uid: 10988 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,33.5 parent: 4812 - uid: 10990 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,32.5 parent: 4812 - uid: 10991 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,31.5 parent: 4812 - uid: 10992 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,30.5 parent: 4812 - uid: 10993 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,29.5 parent: 4812 - uid: 10994 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,28.5 parent: 4812 - uid: 10995 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,27.5 parent: 4812 - uid: 10999 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,26.5 parent: 4812 - uid: 11000 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,27.5 parent: 4812 - uid: 11001 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,28.5 parent: 4812 - uid: 11002 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,29.5 parent: 4812 - uid: 11003 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,30.5 parent: 4812 - uid: 11004 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,31.5 parent: 4812 - uid: 11005 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,32.5 parent: 4812 - uid: 11009 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,17.5 parent: 4812 - uid: 11010 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,18.5 parent: 4812 - uid: 11011 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,19.5 parent: 4812 - uid: 11012 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,20.5 parent: 4812 - uid: 11013 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,21.5 parent: 4812 - uid: 11014 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,24.5 parent: 4812 - uid: 11016 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,24.5 parent: 4812 - uid: 11017 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,25.5 parent: 4812 - uid: 11018 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,26.5 parent: 4812 - uid: 11019 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,21.5 parent: 4812 - uid: 11020 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,22.5 parent: 4812 - uid: 11021 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,23.5 parent: 4812 - uid: 11022 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,24.5 parent: 4812 - uid: 11023 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,25.5 parent: 4812 - uid: 11024 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,26.5 parent: 4812 - uid: 11025 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,25.5 parent: 4812 - uid: 11026 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,26.5 parent: 4812 - uid: 11027 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,27.5 parent: 4812 - uid: 11028 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,28.5 parent: 4812 - uid: 11029 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,29.5 parent: 4812 - uid: 11616 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-30.5 parent: 4812 - uid: 11621 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-39.5 parent: 4812 - uid: 11622 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 34.5,-41.5 parent: 4812 - uid: 11623 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 33.5,-41.5 parent: 4812 - uid: 11624 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 32.5,-41.5 parent: 4812 - uid: 11625 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 31.5,-41.5 parent: 4812 - uid: 11626 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-39.5 parent: 4812 - uid: 11627 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,-39.5 parent: 4812 - uid: 11628 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-40.5 parent: 4812 - uid: 11629 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-40.5 parent: 4812 - uid: 11630 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-41.5 parent: 4812 - uid: 11631 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-42.5 parent: 4812 - uid: 11632 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-40.5 parent: 4812 - uid: 11633 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-41.5 parent: 4812 - uid: 11634 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-40.5 parent: 4812 - uid: 11635 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-42.5 parent: 4812 - uid: 11636 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-42.5 parent: 4812 - uid: 11637 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-43.5 parent: 4812 - uid: 11638 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-43.5 parent: 4812 - uid: 11639 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,-43.5 parent: 4812 - uid: 11640 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,-44.5 parent: 4812 - uid: 11641 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-44.5 parent: 4812 - uid: 11643 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-44.5 parent: 4812 - uid: 11644 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-44.5 parent: 4812 - uid: 11645 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-44.5 parent: 4812 - uid: 11647 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,-45.5 parent: 4812 - uid: 11648 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-45.5 parent: 4812 - uid: 11649 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-45.5 parent: 4812 - uid: 11650 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-45.5 parent: 4812 - uid: 11651 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-45.5 parent: 4812 - uid: 11652 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-45.5 parent: 4812 - uid: 11653 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,-46.5 parent: 4812 - uid: 11654 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-46.5 parent: 4812 - uid: 11655 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-46.5 parent: 4812 - uid: 11656 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,-46.5 parent: 4812 - uid: 11657 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-46.5 parent: 4812 - uid: 11658 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-46.5 parent: 4812 - uid: 11659 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,-44.5 parent: 4812 - uid: 11660 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-43.5 parent: 4812 - uid: 11663 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-38.5 parent: 4812 - uid: 11664 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-41.5 parent: 4812 - uid: 11665 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-42.5 parent: 4812 - uid: 11666 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-43.5 parent: 4812 - uid: 11667 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-44.5 parent: 4812 - uid: 11668 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,-44.5 parent: 4812 - uid: 11669 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,-43.5 parent: 4812 - uid: 11670 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,-42.5 parent: 4812 - uid: 11679 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,-38.5 parent: 4812 - uid: 11680 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-43.5 parent: 4812 - uid: 11681 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-44.5 parent: 4812 - uid: 11682 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 27.5,-44.5 parent: 4812 - uid: 11683 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 27.5,-43.5 parent: 4812 - uid: 11684 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-38.5 parent: 4812 - uid: 11686 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 28.5,-38.5 parent: 4812 - uid: 11689 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 28.5,-43.5 parent: 4812 - uid: 11690 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 28.5,-42.5 parent: 4812 - uid: 11691 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-40.5 parent: 4812 - uid: 11695 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-42.5 parent: 4812 - uid: 11696 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-42.5 parent: 4812 - uid: 11697 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-43.5 parent: 4812 - uid: 11698 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-43.5 parent: 4812 - uid: 11699 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 31.5,-42.5 parent: 4812 - uid: 11700 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 32.5,-42.5 parent: 4812 - uid: 11701 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 33.5,-42.5 parent: 4812 - uid: 11702 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-40.5 parent: 4812 - uid: 11703 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-39.5 parent: 4812 - uid: 11704 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 38.5,-38.5 parent: 4812 - uid: 11705 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-41.5 parent: 4812 - uid: 12144 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -48.5,-25.5 parent: 4812 @@ -10685,8 +9418,6 @@ entities: entities: - uid: 12146 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,-3.5 parent: 4812 @@ -11056,8 +9787,6 @@ entities: rot: 3.141592653589793 rad pos: -29.5,1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: Beaker entities: - uid: 9030 @@ -11293,22 +10022,16 @@ entities: entities: - uid: 729 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,18.5 parent: 4812 - uid: 771 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,18.5 parent: 4812 - uid: 2864 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,24.5 parent: 4812 @@ -11317,8 +10040,6 @@ entities: - 2938 - uid: 2866 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,28.5 parent: 4812 @@ -11327,8 +10048,6 @@ entities: - 2938 - uid: 5573 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-30.5 parent: 4812 @@ -11337,8 +10056,6 @@ entities: - 6690 - uid: 5574 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-29.5 parent: 4812 @@ -11347,8 +10064,6 @@ entities: - 6690 - uid: 5575 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-28.5 parent: 4812 @@ -11357,8 +10072,6 @@ entities: - 6690 - uid: 5794 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-27.5 parent: 4812 @@ -11367,8 +10080,6 @@ entities: - 6218 - uid: 5795 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-26.5 parent: 4812 @@ -11377,8 +10088,6 @@ entities: - 6218 - uid: 5796 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-25.5 parent: 4812 @@ -11387,8 +10096,6 @@ entities: - 6218 - uid: 9496 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -44.5,18.5 parent: 4812 @@ -11402,22 +10109,30 @@ entities: - type: Transform pos: 27.5,-0.5 parent: 4812 + - type: SpamEmitSound + enabled: False - uid: 5026 components: - type: Transform rot: -1.5707963267948966 rad pos: -9.5,-47.5 parent: 4812 + - type: SpamEmitSound + enabled: False - uid: 8399 components: - type: Transform pos: -26.5,32.5 parent: 4812 + - type: SpamEmitSound + enabled: False - uid: 10853 components: - type: Transform pos: -22.5,5.5 parent: 4812 + - type: SpamEmitSound + enabled: False - proto: BookAtmosAirAlarms entities: - uid: 865 @@ -11446,33 +10161,29 @@ entities: - type: Transform pos: -25.436514,-18.48842 parent: 4812 -- proto: BookDetective +- proto: BookRandom entities: - - uid: 5387 + - uid: 7161 components: - type: Transform - pos: 17.568647,-11.417019 + pos: -30.557493,-18.387486 parent: 4812 -- proto: BookEscalation +- proto: BookRandomStory entities: - - uid: 12421 + - uid: 5387 components: - type: Transform - pos: -30.59653,-19.308746 + pos: 17.568647,-11.417019 parent: 4812 -- proto: BookEscalationSecurity - entities: - uid: 10446 components: - type: Transform pos: -30.424656,-19.480621 parent: 4812 -- proto: BookRandom - entities: - - uid: 7161 + - uid: 12421 components: - type: Transform - pos: -30.557493,-18.387486 + pos: -30.59653,-19.308746 parent: 4812 - proto: BooksBag entities: @@ -11485,78 +10196,56 @@ entities: entities: - uid: 1952 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,25.5 parent: 4812 - uid: 5374 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-18.5 parent: 4812 - uid: 7000 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-32.5 parent: 4812 - uid: 7001 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-36.5 parent: 4812 - uid: 7005 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-38.5 parent: 4812 - uid: 7130 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-22.5 parent: 4812 - uid: 7131 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-21.5 parent: 4812 - uid: 7172 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-39.5 parent: 4812 - uid: 8391 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,32.5 parent: 4812 - uid: 8392 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,29.5 parent: 4812 - uid: 11352 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-32.5 parent: 4812 @@ -11564,113 +10253,81 @@ entities: entities: - uid: 4816 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-20.5 parent: 4812 - uid: 4817 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-18.5 parent: 4812 - uid: 4818 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-18.5 parent: 4812 - uid: 4819 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-20.5 parent: 4812 - uid: 4820 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-20.5 parent: 4812 - uid: 4821 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-20.5 parent: 4812 - uid: 4822 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-18.5 parent: 4812 - uid: 4823 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,-20.5 parent: 4812 - uid: 4824 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,-18.5 parent: 4812 - uid: 4825 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-18.5 parent: 4812 - uid: 4835 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-14.5 parent: 4812 - uid: 4836 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-14.5 parent: 4812 - uid: 4837 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-16.5 parent: 4812 - uid: 4839 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-19.5 parent: 4812 - uid: 4840 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-18.5 parent: 4812 - uid: 4841 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-17.5 parent: 4812 @@ -26916,8 +25573,6 @@ entities: - type: Transform pos: -53.5,5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: Carpet entities: - uid: 1739 @@ -29683,18 +28338,6 @@ entities: - type: Transform pos: -17.5785,-25.512428 parent: 4812 -- proto: chem_master - entities: - - uid: 6606 - components: - - type: Transform - pos: -8.5,-15.5 - parent: 4812 - - uid: 6607 - components: - - type: Transform - pos: -12.5,-17.5 - parent: 4812 - proto: ChemDispenser entities: - uid: 6604 @@ -29726,6 +28369,18 @@ entities: - type: Transform pos: -10.5,-18.5 parent: 4812 +- proto: ChemMaster + entities: + - uid: 6606 + components: + - type: Transform + pos: -8.5,-15.5 + parent: 4812 + - uid: 6607 + components: + - type: Transform + pos: -12.5,-17.5 + parent: 4812 - proto: ChessBoard entities: - uid: 11880 @@ -31862,25 +30517,13 @@ entities: - type: Transform pos: -44.4085,-23.673323 parent: 4812 -- proto: ClothingHeadHatFlowerCrown +- proto: ClothingHeadHatFlowerWreath entities: - uid: 7254 components: - type: Transform pos: -24.594517,-34.532703 parent: 4812 -- proto: ClothingHeadHatHairflower - entities: - - uid: 2716 - components: - - type: Transform - pos: -5.180887,24.379204 - parent: 4812 - - uid: 12475 - components: - - type: Transform - pos: -24.401733,-34.40146 - parent: 4812 - proto: ClothingHeadHatHardhatOrange entities: - uid: 12119 @@ -31920,8 +30563,6 @@ entities: entities: - uid: 10924 components: - - type: MetaData - flags: InContainer - type: Transform parent: 10921 - type: Physics @@ -32159,8 +30800,6 @@ entities: entities: - uid: 10922 components: - - type: MetaData - flags: InContainer - type: Transform parent: 10921 - type: Physics @@ -32283,8 +30922,6 @@ entities: entities: - uid: 6765 components: - - type: MetaData - flags: InContainer - type: Transform parent: 3441 - type: Physics @@ -32481,8 +31118,6 @@ entities: entities: - uid: 10923 components: - - type: MetaData - flags: InContainer - type: Transform parent: 10921 - type: Physics @@ -33904,6 +32539,9 @@ entities: - type: Transform pos: -3.5,26.5 parent: 4812 + - type: SingletonDeviceNetServer + active: False + available: False - proto: Crowbar entities: - uid: 3469 @@ -33966,15 +32604,11 @@ entities: - type: Transform pos: -15.5,-25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 7219 components: - type: Transform pos: -13.5,-25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: CryoxadoneBeakerSmall entities: - uid: 3269 @@ -37321,8 +35955,6 @@ entities: entities: - uid: 4992 components: - - type: MetaData - flags: InContainer - type: Transform parent: 4991 - type: Physics @@ -37331,8 +35963,6 @@ entities: entities: - uid: 6663 components: - - type: MetaData - flags: InContainer - type: Transform parent: 6662 - type: Physics @@ -37341,8 +35971,6 @@ entities: entities: - uid: 7268 components: - - type: MetaData - flags: InContainer - type: Transform parent: 7267 - type: Physics @@ -37351,8 +35979,6 @@ entities: entities: - uid: 7291 components: - - type: MetaData - flags: InContainer - type: Transform parent: 7290 - type: Physics @@ -37361,8 +35987,6 @@ entities: entities: - uid: 331 components: - - type: MetaData - flags: InContainer - type: Transform parent: 330 - type: Physics @@ -37371,8 +35995,6 @@ entities: entities: - uid: 7248 components: - - type: MetaData - flags: InContainer - type: Transform parent: 7247 - type: Physics @@ -37381,8 +36003,6 @@ entities: entities: - uid: 7269 components: - - type: MetaData - flags: InContainer - type: Transform parent: 7267 - type: Physics @@ -37613,8 +36233,6 @@ entities: - type: DeviceList devices: - 5039 - - type: AtmosDevice - joinedGrid: 4812 - uid: 4979 components: - type: Transform @@ -37627,16 +36245,12 @@ entities: - 5047 - 6553 - 8806 - - type: AtmosDevice - joinedGrid: 4812 - uid: 6781 components: - type: Transform rot: -1.5707963267948966 rad pos: -7.5,-28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 8804 components: - type: Transform @@ -37650,8 +36264,6 @@ entities: - 4920 - 8838 - 8836 - - type: AtmosDevice - joinedGrid: 4812 - uid: 10750 components: - type: Transform @@ -37666,8 +36278,6 @@ entities: - 10754 - 10755 - 10756 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12180 components: - type: Transform @@ -37682,8 +36292,6 @@ entities: - 10754 - 10755 - 10756 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12187 components: - type: Transform @@ -37697,8 +36305,6 @@ entities: - 12189 - 12188 - 12185 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12191 components: - type: Transform @@ -37709,8 +36315,6 @@ entities: devices: - 12192 - 8691 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12194 components: - type: Transform @@ -37728,8 +36332,6 @@ entities: - 12189 - 12197 - 12196 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12199 components: - type: Transform @@ -37741,8 +36343,6 @@ entities: - 12200 - 12196 - 12197 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12203 components: - type: Transform @@ -37759,8 +36359,6 @@ entities: - 8155 - 12205 - 12204 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12207 components: - type: Transform @@ -37771,8 +36369,6 @@ entities: - 12208 - 12204 - 12205 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12210 components: - type: Transform @@ -37785,8 +36381,6 @@ entities: - 12212 - 7782 - 7783 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12217 components: - type: Transform @@ -37797,8 +36391,6 @@ entities: devices: - 12216 - 12212 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12221 components: - type: Transform @@ -37811,8 +36403,6 @@ entities: - 2693 - 2695 - 12222 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12224 components: - type: Transform @@ -37825,8 +36415,6 @@ entities: - 2782 - 2783 - 12225 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12229 components: - type: Transform @@ -37838,8 +36426,6 @@ entities: - 12230 - 2231 - 1928 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12233 components: - type: Transform @@ -37851,8 +36437,6 @@ entities: - 12231 - 2473 - 2472 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12235 components: - type: Transform @@ -37866,8 +36450,6 @@ entities: - 2472 - 2231 - 1928 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12236 components: - type: Transform @@ -37881,8 +36463,6 @@ entities: - 2472 - 2231 - 1928 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12238 components: - type: Transform @@ -37894,8 +36474,6 @@ entities: - 2476 - 963 - 12239 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12241 components: - type: Transform @@ -37905,8 +36483,6 @@ entities: devices: - 12242 - 1927 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12244 components: - type: Transform @@ -37921,8 +36497,6 @@ entities: - 963 - 2783 - 2782 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12246 components: - type: Transform @@ -37935,8 +36509,6 @@ entities: - 2853 - 12247 - 3257 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12250 components: - type: Transform @@ -37952,8 +36524,6 @@ entities: - 2853 - 2787 - 2786 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12253 components: - type: Transform @@ -37964,8 +36534,6 @@ entities: devices: - 3257 - 12254 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12273 components: - type: Transform @@ -37979,8 +36547,6 @@ entities: - 12274 - 4122 - 4117 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12276 components: - type: Transform @@ -37991,8 +36557,6 @@ entities: - 12277 - 4424 - 4425 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12280 components: - type: Transform @@ -38004,8 +36568,6 @@ entities: - 12279 - 4425 - 4424 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12286 components: - type: Transform @@ -38019,8 +36581,6 @@ entities: - 4426 - 6415 - 6414 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12290 components: - type: Transform @@ -38038,8 +36598,6 @@ entities: - 6414 - 6415 - 12288 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12291 components: - type: Transform @@ -38052,8 +36610,6 @@ entities: - 12292 - 6494 - 6495 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12294 components: - type: Transform @@ -38067,8 +36623,6 @@ entities: - 6500 - 6499 - 12295 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12301 components: - type: Transform @@ -38090,8 +36644,6 @@ entities: - 353 - 352 - 350 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12305 components: - type: Transform @@ -38108,8 +36660,6 @@ entities: - 352 - 350 - 12306 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12310 components: - type: Transform @@ -38121,8 +36671,6 @@ entities: - 782 - 783 - 784 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12312 components: - type: Transform @@ -38135,8 +36683,6 @@ entities: - 1940 - 1939 - 1938 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12314 components: - type: Transform @@ -38168,8 +36714,6 @@ entities: - 6655 - 4980 - 7198 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12317 components: - type: Transform @@ -38188,8 +36732,6 @@ entities: - 8591 - 8758 - 8770 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12320 components: - type: Transform @@ -38201,8 +36743,6 @@ entities: - 12321 - 5072 - 5071 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12342 components: - type: Transform @@ -38214,8 +36754,6 @@ entities: - 6585 - 6584 - 12343 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12348 components: - type: Transform @@ -38227,8 +36765,6 @@ entities: - 12347 - 5791 - 5792 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12354 components: - type: Transform @@ -38240,8 +36776,6 @@ entities: - 5600 - 5599 - 12355 - - type: AtmosDevice - joinedGrid: 4812 - proto: FireAxeCabinetFilled entities: - uid: 6784 @@ -39705,6 +38239,18 @@ entities: - type: Transform pos: 9.469537,-39.368214 parent: 4812 +- proto: FoodPoppy + entities: + - uid: 2716 + components: + - type: Transform + pos: -5.180887,24.379204 + parent: 4812 + - uid: 12475 + components: + - type: Transform + pos: -24.401733,-34.40146 + parent: 4812 - proto: FoodSnackCheesie entities: - uid: 1775 @@ -39763,8 +38309,6 @@ entities: rot: 1.5707963267948966 rad pos: -13.5,-27.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - proto: GasFilterFlipped @@ -39774,8 +38318,6 @@ entities: - type: Transform pos: -47.5,1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 9170 @@ -39783,36 +38325,26 @@ entities: - type: Transform pos: -47.5,3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9171 components: - type: Transform pos: -47.5,5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9172 components: - type: Transform pos: -47.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9173 components: - type: Transform pos: -47.5,9.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9174 components: - type: Transform pos: -47.5,11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: GasMinerCarbonDioxide entities: - uid: 9452 @@ -39820,8 +38352,6 @@ entities: - type: Transform pos: -52.5,5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: GasMinerNitrogen entities: - uid: 9449 @@ -39829,8 +38359,6 @@ entities: - type: Transform pos: -52.5,1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: GasMinerOxygen entities: - uid: 9451 @@ -39838,8 +38366,6 @@ entities: - type: Transform pos: -52.5,3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: GasMinerWaterVapor entities: - uid: 9454 @@ -39847,8 +38373,6 @@ entities: - type: Transform pos: -52.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: GasMixerFlipped entities: - uid: 8550 @@ -39856,8 +38380,6 @@ entities: - type: Transform pos: -45.5,5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 8552 components: - type: Transform @@ -39867,8 +38389,6 @@ entities: - type: GasMixer inletTwoConcentration: 0.22000003 inletOneConcentration: 0.78 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#03FDC3FF' - uid: 9236 @@ -39876,29 +38396,21 @@ entities: - type: Transform pos: -45.5,8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9237 components: - type: Transform pos: -45.5,10.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9247 components: - type: Transform pos: -45.5,6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9261 components: - type: Transform pos: -45.5,12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: GasOutletInjector entities: - uid: 4002 @@ -39907,56 +38419,42 @@ entities: rot: 1.5707963267948966 rad pos: 23.5,-30.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 4445 components: - type: Transform rot: 1.5707963267948966 rad pos: -51.5,1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 4446 components: - type: Transform rot: 1.5707963267948966 rad pos: -51.5,3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 4478 components: - type: Transform rot: 1.5707963267948966 rad pos: -51.5,5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 4479 components: - type: Transform rot: 1.5707963267948966 rad pos: -51.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 4497 components: - type: Transform rot: 1.5707963267948966 rad pos: -51.5,9.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 4826 components: - type: Transform rot: 1.5707963267948966 rad pos: -51.5,11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: GasPassiveGate entities: - uid: 5801 @@ -39965,8 +38463,6 @@ entities: rot: 3.141592653589793 rad pos: 12.5,-19.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8979 @@ -39975,8 +38471,6 @@ entities: rot: -1.5707963267948966 rad pos: -31.5,-33.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - proto: GasPassiveVent @@ -39987,47 +38481,35 @@ entities: rot: 3.141592653589793 rad pos: 6.5,-7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 863 components: - type: Transform rot: 3.141592653589793 rad pos: 7.5,-7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 3817 components: - type: Transform rot: -1.5707963267948966 rad pos: 17.5,46.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 5596 components: - type: Transform rot: 1.5707963267948966 rad pos: 23.5,-28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9005 components: - type: Transform rot: 3.141592653589793 rad pos: -40.5,-37.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9168 components: - type: Transform pos: -49.5,-0.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 9199 @@ -40036,69 +38518,51 @@ entities: rot: 3.141592653589793 rad pos: -53.5,1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9200 components: - type: Transform rot: 3.141592653589793 rad pos: -53.5,3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9201 components: - type: Transform rot: 3.141592653589793 rad pos: -53.5,5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9202 components: - type: Transform rot: 3.141592653589793 rad pos: -53.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9203 components: - type: Transform rot: 3.141592653589793 rad pos: -53.5,9.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9204 components: - type: Transform rot: 3.141592653589793 rad pos: -53.5,11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9264 components: - type: Transform pos: -47.5,14.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9494 components: - type: Transform pos: -45.5,16.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9495 components: - type: Transform pos: -43.5,16.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: GasPipeBend entities: - uid: 560 @@ -52101,16 +50565,12 @@ entities: - type: Transform pos: -29.5,2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 3467 components: - type: Transform rot: 1.5707963267948966 rad pos: -34.5,-2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3710 @@ -52119,8 +50579,6 @@ entities: rot: 1.5707963267948966 rad pos: -34.5,-0.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3711 @@ -52129,8 +50587,6 @@ entities: rot: 1.5707963267948966 rad pos: -34.5,-1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3712 @@ -52139,8 +50595,6 @@ entities: rot: 1.5707963267948966 rad pos: -34.5,-3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3839 @@ -52149,8 +50603,6 @@ entities: rot: -1.5707963267948966 rad pos: 15.5,44.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5593 @@ -52159,40 +50611,30 @@ entities: rot: -1.5707963267948966 rad pos: 29.5,-28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 5594 components: - type: Transform rot: -1.5707963267948966 rad pos: 29.5,-30.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 8839 components: - type: Transform rot: 3.141592653589793 rad pos: -12.5,-28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9037 components: - type: Transform rot: 3.141592653589793 rad pos: -33.5,-37.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9548 components: - type: Transform rot: -1.5707963267948966 rad pos: -41.5,6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#947507FF' - uid: 9549 @@ -52201,8 +50643,6 @@ entities: rot: -1.5707963267948966 rad pos: -41.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#947507FF' - uid: 9550 @@ -52211,8 +50651,6 @@ entities: rot: -1.5707963267948966 rad pos: -41.5,8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#947507FF' - proto: GasPressurePump @@ -52223,8 +50661,6 @@ entities: rot: 1.5707963267948966 rad pos: 15.5,46.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4929 @@ -52234,8 +50670,6 @@ entities: - type: Transform pos: -12.5,-25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5583 @@ -52244,24 +50678,18 @@ entities: rot: -1.5707963267948966 rad pos: 25.5,-30.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 5584 components: - type: Transform rot: 1.5707963267948966 rad pos: 25.5,-28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 8773 components: - type: Transform rot: -1.5707963267948966 rad pos: -39.5,1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 9115 @@ -52270,16 +50698,12 @@ entities: rot: 1.5707963267948966 rad pos: -47.5,2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9131 components: - type: Transform rot: 1.5707963267948966 rad pos: -39.5,2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 9206 @@ -52288,47 +50712,35 @@ entities: rot: 1.5707963267948966 rad pos: -47.5,4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9212 components: - type: Transform rot: 1.5707963267948966 rad pos: -47.5,6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9218 components: - type: Transform rot: 1.5707963267948966 rad pos: -47.5,8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9224 components: - type: Transform rot: 1.5707963267948966 rad pos: -47.5,10.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9230 components: - type: Transform rot: 1.5707963267948966 rad pos: -47.5,12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9513 components: - type: Transform pos: -43.5,12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#947507FF' - uid: 9543 @@ -52336,8 +50748,6 @@ entities: - type: Transform pos: -42.5,4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - proto: GasThermoMachineFreezer @@ -52347,30 +50757,22 @@ entities: - type: Transform pos: 7.5,-5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 5803 components: - type: Transform pos: 12.5,-17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 7245 components: - type: Transform rot: 3.141592653589793 rad pos: -14.5,-28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9551 components: - type: Transform pos: -41.5,12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: GasThermoMachineHeater entities: - uid: 9552 @@ -52378,8 +50780,6 @@ entities: - type: Transform pos: -41.5,11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: GasValve entities: - uid: 5591 @@ -52388,16 +50788,12 @@ entities: rot: 1.5707963267948966 rad pos: 28.5,-30.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 5592 components: - type: Transform rot: 1.5707963267948966 rad pos: 28.5,-28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9039 components: - type: Transform @@ -52406,8 +50802,6 @@ entities: parent: 4812 - type: GasValve open: False - - type: AtmosDevice - joinedGrid: 4812 - uid: 9164 components: - type: Transform @@ -52416,8 +50810,6 @@ entities: parent: 4812 - type: GasValve open: False - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 9544 @@ -52428,8 +50820,6 @@ entities: parent: 4812 - type: GasValve open: False - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#947507FF' - proto: GasVentPump @@ -52440,8 +50830,6 @@ entities: rot: 1.5707963267948966 rad pos: -27.5,-12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 710 @@ -52450,8 +50838,6 @@ entities: rot: -1.5707963267948966 rad pos: -2.5,13.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 817 @@ -52460,8 +50846,6 @@ entities: rot: -1.5707963267948966 rad pos: -2.5,10.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 818 @@ -52470,8 +50854,6 @@ entities: rot: -1.5707963267948966 rad pos: -2.5,-7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 835 @@ -52479,8 +50861,6 @@ entities: - type: Transform pos: 5.5,-1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 838 @@ -52489,8 +50869,6 @@ entities: rot: 3.141592653589793 rad pos: 4.5,-6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 846 @@ -52499,8 +50877,6 @@ entities: rot: 1.5707963267948966 rad pos: 2.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 847 @@ -52509,8 +50885,6 @@ entities: rot: -1.5707963267948966 rad pos: 6.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 848 @@ -52518,8 +50892,6 @@ entities: - type: Transform pos: -17.5,-6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 849 @@ -52527,8 +50899,6 @@ entities: - type: Transform pos: -10.5,-6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 864 @@ -52537,8 +50907,6 @@ entities: rot: 3.141592653589793 rad pos: -2.5,1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 871 @@ -52547,8 +50915,6 @@ entities: rot: 1.5707963267948966 rad pos: -9.5,-0.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 877 @@ -52557,8 +50923,6 @@ entities: rot: 1.5707963267948966 rad pos: -9.5,4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 1511 @@ -52566,8 +50930,6 @@ entities: - type: Transform pos: -22.5,-8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 1685 @@ -52575,8 +50937,6 @@ entities: - type: Transform pos: -21.5,3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 1770 @@ -52585,8 +50945,6 @@ entities: rot: 1.5707963267948966 rad pos: 11.5,19.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 1814 @@ -52595,8 +50953,6 @@ entities: rot: 3.141592653589793 rad pos: 8.5,16.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 1817 @@ -52605,8 +50961,6 @@ entities: rot: 3.141592653589793 rad pos: -0.5,18.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 1852 @@ -52614,8 +50968,6 @@ entities: - type: Transform pos: -3.5,23.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2071 @@ -52624,8 +50976,6 @@ entities: rot: 3.141592653589793 rad pos: 8.5,24.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2072 @@ -52633,8 +50983,6 @@ entities: - type: Transform pos: 8.5,29.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2429 @@ -52643,8 +50991,6 @@ entities: rot: 1.5707963267948966 rad pos: -14.5,23.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2430 @@ -52652,8 +50998,6 @@ entities: - type: Transform pos: -13.5,28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2443 @@ -52662,8 +51006,6 @@ entities: rot: 1.5707963267948966 rad pos: 2.5,29.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2444 @@ -52672,8 +51014,6 @@ entities: rot: -1.5707963267948966 rad pos: -7.5,29.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2445 @@ -52681,8 +51021,6 @@ entities: - type: Transform pos: -5.5,34.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2446 @@ -52690,8 +51028,6 @@ entities: - type: Transform pos: 0.5,34.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2474 @@ -52700,8 +51036,6 @@ entities: rot: -1.5707963267948966 rad pos: -7.5,22.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2475 @@ -52710,8 +51044,6 @@ entities: rot: 1.5707963267948966 rad pos: 2.5,22.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2842 @@ -52720,8 +51052,6 @@ entities: rot: -1.5707963267948966 rad pos: 22.5,27.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2966 @@ -52730,8 +51060,6 @@ entities: rot: 1.5707963267948966 rad pos: 23.5,14.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2969 @@ -52740,8 +51068,6 @@ entities: rot: -1.5707963267948966 rad pos: 26.5,14.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 2983 @@ -52749,8 +51075,6 @@ entities: - type: Transform pos: 15.5,29.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3019 @@ -52759,8 +51083,6 @@ entities: rot: 3.141592653589793 rad pos: 14.5,25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3020 @@ -52769,8 +51091,6 @@ entities: rot: 3.141592653589793 rad pos: 19.5,25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3023 @@ -52779,8 +51099,6 @@ entities: rot: 1.5707963267948966 rad pos: 14.5,18.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3024 @@ -52789,8 +51107,6 @@ entities: rot: 1.5707963267948966 rad pos: 14.5,20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3026 @@ -52799,8 +51115,6 @@ entities: rot: 3.141592653589793 rad pos: 15.5,13.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3206 @@ -52809,8 +51123,6 @@ entities: rot: -1.5707963267948966 rad pos: 22.5,25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3708 @@ -52819,8 +51131,6 @@ entities: rot: -1.5707963267948966 rad pos: -31.5,-3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3781 @@ -52828,8 +51138,6 @@ entities: - type: Transform pos: -1.5,-34.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3834 @@ -52838,8 +51146,6 @@ entities: rot: 1.5707963267948966 rad pos: 3.5,45.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3835 @@ -52848,8 +51154,6 @@ entities: rot: 3.141592653589793 rad pos: 5.5,44.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3838 @@ -52857,8 +51161,6 @@ entities: - type: Transform pos: 8.5,51.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3841 @@ -52867,8 +51169,6 @@ entities: rot: -1.5707963267948966 rad pos: 15.5,45.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 3944 @@ -52877,8 +51177,6 @@ entities: rot: 3.141592653589793 rad pos: 9.5,43.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4196 @@ -52886,8 +51184,6 @@ entities: - type: Transform pos: 30.5,3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4197 @@ -52896,8 +51192,6 @@ entities: rot: -1.5707963267948966 rad pos: 33.5,2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4198 @@ -52906,8 +51200,6 @@ entities: rot: -1.5707963267948966 rad pos: 33.5,0.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4199 @@ -52916,8 +51208,6 @@ entities: rot: -1.5707963267948966 rad pos: 33.5,-5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4200 @@ -52926,8 +51216,6 @@ entities: rot: -1.5707963267948966 rad pos: 33.5,-7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4201 @@ -52936,8 +51224,6 @@ entities: rot: 3.141592653589793 rad pos: 30.5,-8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4203 @@ -52946,8 +51232,6 @@ entities: rot: 1.5707963267948966 rad pos: 28.5,-0.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4231 @@ -52956,8 +51240,6 @@ entities: rot: 1.5707963267948966 rad pos: 22.5,4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4380 @@ -52966,8 +51248,6 @@ entities: rot: 3.141592653589793 rad pos: 19.5,-3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4440 @@ -52976,8 +51256,6 @@ entities: rot: 1.5707963267948966 rad pos: 11.5,2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4441 @@ -52986,8 +51264,6 @@ entities: rot: 1.5707963267948966 rad pos: 11.5,-8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4444 @@ -52996,8 +51272,6 @@ entities: rot: 1.5707963267948966 rad pos: 24.5,-1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4464 @@ -53006,8 +51280,6 @@ entities: rot: -1.5707963267948966 rad pos: 15.5,8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4890 @@ -53016,8 +51288,6 @@ entities: rot: 3.141592653589793 rad pos: -18.5,-18.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 4941 @@ -53026,8 +51296,6 @@ entities: rot: -1.5707963267948966 rad pos: -15.5,-17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5033 @@ -53036,8 +51304,6 @@ entities: rot: 3.141592653589793 rad pos: -24.5,-27.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5222 @@ -53046,8 +51312,6 @@ entities: rot: 3.141592653589793 rad pos: -10.5,-52.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5225 @@ -53056,8 +51320,6 @@ entities: rot: 1.5707963267948966 rad pos: -11.5,-41.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5697 @@ -53065,8 +51327,6 @@ entities: - type: Transform pos: 4.5,-17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5698 @@ -53075,8 +51335,6 @@ entities: rot: 3.141592653589793 rad pos: 5.5,-24.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5714 @@ -53085,8 +51343,6 @@ entities: rot: 3.141592653589793 rad pos: 9.5,-27.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5715 @@ -53095,8 +51351,6 @@ entities: rot: -1.5707963267948966 rad pos: 12.5,-25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5718 @@ -53105,8 +51359,6 @@ entities: rot: -1.5707963267948966 rad pos: 10.5,-19.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5734 @@ -53115,8 +51367,6 @@ entities: rot: -1.5707963267948966 rad pos: 25.5,-20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5763 @@ -53124,8 +51374,6 @@ entities: - type: Transform pos: 27.5,-24.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5764 @@ -53134,8 +51382,6 @@ entities: rot: -1.5707963267948966 rad pos: 33.5,-25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 5802 @@ -53144,15 +51390,11 @@ entities: rot: -1.5707963267948966 rad pos: 13.5,-18.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 6416 components: - type: Transform pos: 8.5,-11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 6419 @@ -53161,8 +51403,6 @@ entities: rot: 3.141592653589793 rad pos: -3.5,-13.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 6461 @@ -53170,8 +51410,6 @@ entities: - type: Transform pos: -1.5,-31.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 6471 @@ -53179,8 +51417,6 @@ entities: - type: Transform pos: -2.5,-28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 6554 @@ -53188,8 +51424,6 @@ entities: - type: Transform pos: -16.5,-20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 6686 @@ -53198,8 +51432,6 @@ entities: rot: 1.5707963267948966 rad pos: -21.5,-27.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 6821 @@ -53208,8 +51440,6 @@ entities: rot: 1.5707963267948966 rad pos: -26.5,-35.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 6840 @@ -53218,8 +51448,6 @@ entities: rot: 3.141592653589793 rad pos: -23.5,-36.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 6842 @@ -53227,8 +51455,6 @@ entities: - type: Transform pos: -19.5,-34.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7087 @@ -53237,8 +51463,6 @@ entities: rot: 3.141592653589793 rad pos: -25.5,-17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7323 @@ -53247,8 +51471,6 @@ entities: rot: 3.141592653589793 rad pos: -10.5,-26.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7379 @@ -53257,8 +51479,6 @@ entities: rot: 1.5707963267948966 rad pos: -10.5,-20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7380 @@ -53266,8 +51486,6 @@ entities: - type: Transform pos: -9.5,-17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7856 @@ -53275,8 +51493,6 @@ entities: - type: Transform pos: -26.5,30.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7857 @@ -53285,8 +51501,6 @@ entities: rot: 3.141592653589793 rad pos: -26.5,23.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7871 @@ -53295,8 +51509,6 @@ entities: rot: -1.5707963267948966 rad pos: -26.5,17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7872 @@ -53305,8 +51517,6 @@ entities: rot: -1.5707963267948966 rad pos: -26.5,20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7886 @@ -53315,8 +51525,6 @@ entities: rot: -1.5707963267948966 rad pos: -26.5,10.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7905 @@ -53325,8 +51533,6 @@ entities: rot: 1.5707963267948966 rad pos: -36.5,10.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7906 @@ -53334,8 +51540,6 @@ entities: - type: Transform pos: -35.5,15.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7910 @@ -53344,8 +51548,6 @@ entities: rot: -1.5707963267948966 rad pos: -28.5,15.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7911 @@ -53354,8 +51556,6 @@ entities: rot: -1.5707963267948966 rad pos: -28.5,12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7912 @@ -53364,8 +51564,6 @@ entities: rot: -1.5707963267948966 rad pos: -28.5,18.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8238 @@ -53373,8 +51571,6 @@ entities: - type: Transform pos: -23.5,27.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8240 @@ -53383,8 +51579,6 @@ entities: rot: -1.5707963267948966 rad pos: -22.5,25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8300 @@ -53393,8 +51587,6 @@ entities: rot: -1.5707963267948966 rad pos: -19.5,16.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8306 @@ -53403,8 +51595,6 @@ entities: rot: -1.5707963267948966 rad pos: -22.5,17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8318 @@ -53413,8 +51603,6 @@ entities: rot: 3.141592653589793 rad pos: -13.5,16.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8562 @@ -53422,8 +51610,6 @@ entities: - type: Transform pos: -18.5,11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8838 @@ -53432,8 +51618,6 @@ entities: rot: 3.141592653589793 rad pos: -16.5,-28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8991 @@ -53442,31 +51626,23 @@ entities: rot: 3.141592653589793 rad pos: -39.5,-34.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 8992 components: - type: Transform rot: 3.141592653589793 rad pos: -36.5,-37.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 8993 components: - type: Transform rot: 3.141592653589793 rad pos: -34.5,-34.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 10757 components: - type: Transform pos: -38.5,3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 11962 @@ -53475,8 +51651,6 @@ entities: rot: -1.5707963267948966 rad pos: -32.5,-8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 12027 @@ -53485,8 +51659,6 @@ entities: rot: -1.5707963267948966 rad pos: -42.5,-12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 12028 @@ -53495,8 +51667,6 @@ entities: rot: 3.141592653589793 rad pos: -49.5,-13.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 12029 @@ -53505,8 +51675,6 @@ entities: rot: 1.5707963267948966 rad pos: -53.5,-12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 12030 @@ -53514,8 +51682,6 @@ entities: - type: Transform pos: -49.5,-9.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 12032 @@ -53523,8 +51689,6 @@ entities: - type: Transform pos: -44.5,-6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 12035 @@ -53533,8 +51697,6 @@ entities: rot: -1.5707963267948966 rad pos: -32.5,2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 12037 @@ -53542,8 +51704,6 @@ entities: - type: Transform pos: -16.5,-11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 12039 @@ -53552,8 +51712,6 @@ entities: rot: 3.141592653589793 rad pos: -23.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - uid: 12041 @@ -53562,8 +51720,6 @@ entities: rot: -1.5707963267948966 rad pos: -25.5,0.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#0335FCFF' - proto: GasVentScrubber @@ -53574,8 +51730,6 @@ entities: rot: 1.5707963267948966 rad pos: -2.5,14.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 814 @@ -53584,8 +51738,6 @@ entities: rot: -1.5707963267948966 rad pos: 4.5,-1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 815 @@ -53594,8 +51746,6 @@ entities: rot: 1.5707963267948966 rad pos: -2.5,-6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 816 @@ -53604,8 +51754,6 @@ entities: rot: 1.5707963267948966 rad pos: -2.5,9.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 857 @@ -53614,8 +51762,6 @@ entities: rot: 1.5707963267948966 rad pos: -17.5,-8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 858 @@ -53623,8 +51769,6 @@ entities: - type: Transform pos: -10.5,-8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 885 @@ -53633,8 +51777,6 @@ entities: rot: 1.5707963267948966 rad pos: -9.5,8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 1512 @@ -53643,8 +51785,6 @@ entities: rot: 3.141592653589793 rad pos: -21.5,-7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 1686 @@ -53653,8 +51793,6 @@ entities: rot: 3.141592653589793 rad pos: -21.5,0.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 1813 @@ -53663,8 +51801,6 @@ entities: rot: 3.141592653589793 rad pos: 6.5,16.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 1816 @@ -53673,8 +51809,6 @@ entities: rot: 3.141592653589793 rad pos: -4.5,18.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 1853 @@ -53682,8 +51816,6 @@ entities: - type: Transform pos: -1.5,23.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 2073 @@ -53691,8 +51823,6 @@ entities: - type: Transform pos: 6.5,29.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 2074 @@ -53701,8 +51831,6 @@ entities: rot: -1.5707963267948966 rad pos: 7.5,24.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 2439 @@ -53711,8 +51839,6 @@ entities: rot: 3.141592653589793 rad pos: -12.5,23.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 2440 @@ -53720,8 +51846,6 @@ entities: - type: Transform pos: -12.5,28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 2441 @@ -53730,8 +51854,6 @@ entities: rot: 1.5707963267948966 rad pos: -8.5,26.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 2442 @@ -53740,8 +51862,6 @@ entities: rot: -1.5707963267948966 rad pos: 3.5,26.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 2447 @@ -53750,8 +51870,6 @@ entities: rot: 3.141592653589793 rad pos: -5.5,31.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 2448 @@ -53760,8 +51878,6 @@ entities: rot: 3.141592653589793 rad pos: 0.5,31.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 2963 @@ -53770,8 +51886,6 @@ entities: rot: -1.5707963267948966 rad pos: 22.5,14.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3001 @@ -53779,8 +51893,6 @@ entities: - type: Transform pos: 16.5,29.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3016 @@ -53789,8 +51901,6 @@ entities: rot: -1.5707963267948966 rad pos: 20.5,25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3017 @@ -53799,8 +51909,6 @@ entities: rot: 1.5707963267948966 rad pos: 15.5,25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3021 @@ -53809,8 +51917,6 @@ entities: rot: -1.5707963267948966 rad pos: 20.5,20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3022 @@ -53819,8 +51925,6 @@ entities: rot: -1.5707963267948966 rad pos: 20.5,18.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3027 @@ -53829,8 +51933,6 @@ entities: rot: 3.141592653589793 rad pos: 19.5,13.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3782 @@ -53838,8 +51940,6 @@ entities: - type: Transform pos: 10.5,20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3813 @@ -53848,8 +51948,6 @@ entities: rot: 3.141592653589793 rad pos: 7.5,44.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3814 @@ -53858,8 +51956,6 @@ entities: rot: 3.141592653589793 rad pos: 11.5,43.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3815 @@ -53867,8 +51963,6 @@ entities: - type: Transform pos: 12.5,51.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3816 @@ -53877,8 +51971,6 @@ entities: rot: 3.141592653589793 rad pos: 13.5,45.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4205 @@ -53887,8 +51979,6 @@ entities: rot: 3.141592653589793 rad pos: 28.5,-4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4214 @@ -53897,8 +51987,6 @@ entities: rot: 3.141592653589793 rad pos: 28.5,-8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4226 @@ -53906,8 +51994,6 @@ entities: - type: Transform pos: 24.5,3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4381 @@ -53915,8 +52001,6 @@ entities: - type: Transform pos: 17.5,-2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4439 @@ -53925,8 +52009,6 @@ entities: rot: -1.5707963267948966 rad pos: 12.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4442 @@ -53935,8 +52017,6 @@ entities: rot: -1.5707963267948966 rad pos: 12.5,-1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4443 @@ -53945,8 +52025,6 @@ entities: rot: -1.5707963267948966 rad pos: 25.5,-4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4465 @@ -53955,8 +52033,6 @@ entities: rot: -1.5707963267948966 rad pos: 15.5,6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4875 @@ -53964,8 +52040,6 @@ entities: - type: Transform pos: -15.5,-15.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 4876 @@ -53974,8 +52048,6 @@ entities: rot: 1.5707963267948966 rad pos: -19.5,-16.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5025 @@ -53983,8 +52055,6 @@ entities: - type: Transform pos: -18.5,-20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5223 @@ -53993,8 +52063,6 @@ entities: rot: 3.141592653589793 rad pos: -11.5,-51.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5224 @@ -54003,8 +52071,6 @@ entities: rot: -1.5707963267948966 rad pos: -10.5,-42.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5699 @@ -54013,8 +52079,6 @@ entities: rot: 3.141592653589793 rad pos: 6.5,-24.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5700 @@ -54022,8 +52086,6 @@ entities: - type: Transform pos: 3.5,-17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5716 @@ -54032,8 +52094,6 @@ entities: rot: -1.5707963267948966 rad pos: 12.5,-26.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5717 @@ -54042,8 +52102,6 @@ entities: rot: 3.141592653589793 rad pos: 8.5,-27.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5719 @@ -54052,8 +52110,6 @@ entities: rot: 1.5707963267948966 rad pos: 9.5,-18.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5735 @@ -54061,8 +52117,6 @@ entities: - type: Transform pos: 27.5,-20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5765 @@ -54071,8 +52125,6 @@ entities: rot: 3.141592653589793 rad pos: 27.5,-27.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5766 @@ -54081,8 +52133,6 @@ entities: rot: -1.5707963267948966 rad pos: 33.5,-26.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 5805 @@ -54091,8 +52141,6 @@ entities: rot: 1.5707963267948966 rad pos: 13.5,-17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 6253 @@ -54101,8 +52149,6 @@ entities: rot: 3.141592653589793 rad pos: -26.5,-26.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 6417 @@ -54111,8 +52157,6 @@ entities: rot: 3.141592653589793 rad pos: 11.5,-12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 6420 @@ -54121,8 +52165,6 @@ entities: rot: 3.141592653589793 rad pos: -1.5,-13.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 6455 @@ -54131,8 +52173,6 @@ entities: rot: 3.141592653589793 rad pos: -3.5,-35.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 6472 @@ -54141,8 +52181,6 @@ entities: rot: -1.5707963267948966 rad pos: -2.5,-25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 6687 @@ -54151,8 +52189,6 @@ entities: rot: 3.141592653589793 rad pos: -3.5,-32.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 6838 @@ -54161,8 +52197,6 @@ entities: rot: 1.5707963267948966 rad pos: -26.5,-36.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 6839 @@ -54170,8 +52204,6 @@ entities: - type: Transform pos: -18.5,-33.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 6841 @@ -54180,8 +52212,6 @@ entities: rot: 3.141592653589793 rad pos: -22.5,-37.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7088 @@ -54190,8 +52220,6 @@ entities: rot: 3.141592653589793 rad pos: -24.5,-17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7227 @@ -54200,8 +52228,6 @@ entities: rot: 1.5707963267948966 rad pos: -20.5,-26.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7325 @@ -54210,8 +52236,6 @@ entities: rot: 3.141592653589793 rad pos: -8.5,-26.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7381 @@ -54220,8 +52244,6 @@ entities: rot: 1.5707963267948966 rad pos: -12.5,-20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7382 @@ -54229,8 +52251,6 @@ entities: - type: Transform pos: -11.5,-17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7858 @@ -54238,8 +52258,6 @@ entities: - type: Transform pos: -30.5,30.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7873 @@ -54248,8 +52266,6 @@ entities: rot: -1.5707963267948966 rad pos: -26.5,16.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7874 @@ -54258,8 +52274,6 @@ entities: rot: -1.5707963267948966 rad pos: -26.5,19.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7887 @@ -54268,8 +52282,6 @@ entities: rot: -1.5707963267948966 rad pos: -26.5,11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7907 @@ -54278,8 +52290,6 @@ entities: rot: 1.5707963267948966 rad pos: -36.5,11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7908 @@ -54287,8 +52297,6 @@ entities: - type: Transform pos: -34.5,15.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7909 @@ -54297,8 +52305,6 @@ entities: rot: 1.5707963267948966 rad pos: -31.5,15.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7913 @@ -54307,8 +52313,6 @@ entities: rot: 1.5707963267948966 rad pos: -31.5,18.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7914 @@ -54317,8 +52321,6 @@ entities: rot: 1.5707963267948966 rad pos: -31.5,12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 8239 @@ -54326,8 +52328,6 @@ entities: - type: Transform pos: -22.5,27.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 8241 @@ -54336,8 +52336,6 @@ entities: rot: 1.5707963267948966 rad pos: -23.5,22.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 8307 @@ -54345,8 +52343,6 @@ entities: - type: Transform pos: -21.5,20.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 8317 @@ -54355,8 +52351,6 @@ entities: rot: 3.141592653589793 rad pos: -11.5,16.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 8563 @@ -54365,8 +52359,6 @@ entities: rot: 3.141592653589793 rad pos: -18.5,8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 8787 @@ -54375,8 +52367,6 @@ entities: rot: 1.5707963267948966 rad pos: -31.5,-2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 8836 @@ -54385,8 +52375,6 @@ entities: rot: -1.5707963267948966 rad pos: -16.5,-25.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 8994 @@ -54395,32 +52383,24 @@ entities: rot: -1.5707963267948966 rad pos: -33.5,-34.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9006 components: - type: Transform rot: 3.141592653589793 rad pos: -37.5,-37.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9007 components: - type: Transform rot: 1.5707963267948966 rad pos: -38.5,-34.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 10758 components: - type: Transform rot: 3.141592653589793 rad pos: -38.5,0.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12023 @@ -54428,8 +52408,6 @@ entities: - type: Transform pos: -48.5,-9.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12024 @@ -54438,8 +52416,6 @@ entities: rot: 1.5707963267948966 rad pos: -53.5,-11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12025 @@ -54448,8 +52424,6 @@ entities: rot: 3.141592653589793 rad pos: -48.5,-13.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12026 @@ -54458,8 +52432,6 @@ entities: rot: -1.5707963267948966 rad pos: -42.5,-11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12033 @@ -54467,8 +52439,6 @@ entities: - type: Transform pos: -40.5,-6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12034 @@ -54477,8 +52447,6 @@ entities: rot: 3.141592653589793 rad pos: -30.5,-6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12036 @@ -54486,8 +52454,6 @@ entities: - type: Transform pos: -30.5,2.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12038 @@ -54496,8 +52462,6 @@ entities: rot: 3.141592653589793 rad pos: -14.5,-12.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12040 @@ -54505,8 +52469,6 @@ entities: - type: Transform pos: -24.5,8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12042 @@ -54515,8 +52477,6 @@ entities: rot: 1.5707963267948966 rad pos: -26.5,-4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12044 @@ -54525,8 +52485,6 @@ entities: rot: 1.5707963267948966 rad pos: -26.5,-11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - uid: 12418 @@ -54535,8 +52493,6 @@ entities: rot: 1.5707963267948966 rad pos: -2.5,4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - type: AtmosPipeColor color: '#FF1212FF' - proto: GeneratorBasic15kW @@ -57741,43 +55697,31 @@ entities: entities: - uid: 1846 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -2.5,21.5 parent: 4812 - uid: 3287 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,6.5 parent: 4812 - uid: 3688 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,45.5 parent: 4812 - uid: 3689 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,45.5 parent: 4812 - uid: 3690 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,47.5 parent: 4812 - uid: 9886 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-12.5 parent: 4812 @@ -59707,16 +57651,14 @@ entities: - type: Transform pos: -35.5,12.5 parent: 4812 - - type: Lock - locked: False - type: EntityStorage air: volume: 200 immutable: False temperature: 293.14957 moles: - - 1.6033952 - - 6.031821 + - 1.8742619 + - 7.050795 - 0 - 0 - 0 @@ -60156,8 +58098,6 @@ entities: entities: - uid: 12381 components: - - type: MetaData - flags: InContainer - type: Transform parent: 9099 - type: Physics @@ -60288,43 +58228,31 @@ entities: - type: Transform pos: 15.5,11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 6327 components: - type: Transform pos: 25.5,-22.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9448 components: - type: Transform pos: -53.5,1.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9667 components: - type: Transform pos: -33.5,4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9668 components: - type: Transform pos: -33.5,5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 11786 components: - type: Transform pos: 21.5,-32.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: NuclearBomb entities: - uid: 1854 @@ -60390,80 +58318,63 @@ entities: - type: Transform pos: 9.5,17.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 4603 components: - type: Transform pos: 14.5,11.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 6328 components: - type: Transform pos: 26.5,-22.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 8849 components: - type: Transform pos: -13.5,-28.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9450 components: - type: Transform pos: -53.5,3.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9669 components: - type: Transform pos: -32.5,4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9670 components: - type: Transform pos: -32.5,5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 10740 components: - type: Transform pos: -46.5,-9.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 10878 components: - type: Transform pos: -34.5,8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 10917 components: - type: Transform pos: -47.5,-23.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 11787 components: - type: Transform pos: 20.5,-32.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: OxygenTankFilled entities: + - uid: 11791 + components: + - type: Transform + pos: 19.452671,-32.41012 + parent: 4812 - uid: 11792 components: - type: Transform @@ -60905,43 +58816,31 @@ entities: entities: - uid: 1767 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -2.5035148,7.4145155 parent: 4812 - uid: 3963 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 9.496426,48.542255 parent: 4812 - uid: 6307 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 26.432638,-19.383717 parent: 4812 - uid: 7142 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -28.490486,-14.614019 parent: 4812 - uid: 10864 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -22.475502,3.5391276 parent: 4812 - uid: 10865 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 6.426591,-28.501112 parent: 4812 @@ -61037,8 +58936,6 @@ entities: - type: Transform pos: -53.5,9.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: PlasticFlapsAirtightClear entities: - uid: 2779 @@ -61932,8 +59829,6 @@ entities: entities: - uid: 1515 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,-5.5 parent: 4812 @@ -61941,8 +59836,6 @@ entities: powerLoad: 0 - uid: 1516 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-5.5 parent: 4812 @@ -61950,8 +59843,6 @@ entities: powerLoad: 0 - uid: 1517 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -7.5,-8.5 @@ -61960,8 +59851,6 @@ entities: powerLoad: 0 - uid: 1518 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -18.5,-8.5 @@ -61970,8 +59859,6 @@ entities: powerLoad: 0 - uid: 1665 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 0.5,10.5 @@ -61980,8 +59867,6 @@ entities: powerLoad: 0 - uid: 1666 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -5.5,10.5 @@ -61990,8 +59875,6 @@ entities: powerLoad: 0 - uid: 1667 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 0.5,1.5 @@ -62000,8 +59883,6 @@ entities: powerLoad: 0 - uid: 1668 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -5.5,1.5 @@ -62010,8 +59891,6 @@ entities: powerLoad: 0 - uid: 1669 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -5.5,-4.5 @@ -62020,8 +59899,6 @@ entities: powerLoad: 0 - uid: 1670 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 0.5,-4.5 @@ -62030,8 +59907,6 @@ entities: powerLoad: 0 - uid: 1671 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,9.5 parent: 4812 @@ -62039,8 +59914,6 @@ entities: powerLoad: 0 - uid: 1672 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 2.5,-3.5 @@ -62049,8 +59922,6 @@ entities: powerLoad: 0 - uid: 1673 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 7.5,0.5 @@ -62059,8 +59930,6 @@ entities: powerLoad: 0 - uid: 1975 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 5.5,15.5 @@ -62069,8 +59938,6 @@ entities: powerLoad: 0 - uid: 1983 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 9.5,15.5 @@ -62079,8 +59946,6 @@ entities: powerLoad: 0 - uid: 2541 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 10.5,29.5 @@ -62089,8 +59954,6 @@ entities: powerLoad: 0 - uid: 2542 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 8.5,29.5 @@ -62099,8 +59962,6 @@ entities: powerLoad: 0 - uid: 2543 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -15.5,27.5 @@ -62109,8 +59970,6 @@ entities: powerLoad: 0 - uid: 2544 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,22.5 parent: 4812 @@ -62118,8 +59977,6 @@ entities: powerLoad: 0 - uid: 2545 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,26.5 parent: 4812 @@ -62127,8 +59984,6 @@ entities: powerLoad: 0 - uid: 2622 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -10.5,28.5 @@ -62137,8 +59992,6 @@ entities: powerLoad: 0 - uid: 2623 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,24.5 parent: 4812 @@ -62146,8 +59999,6 @@ entities: powerLoad: 0 - uid: 2624 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 3.5,28.5 @@ -62156,8 +60007,6 @@ entities: powerLoad: 0 - uid: 2625 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -8.5,28.5 @@ -62166,8 +60015,6 @@ entities: powerLoad: 0 - uid: 2626 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -7.5,23.5 @@ -62176,8 +60023,6 @@ entities: powerLoad: 0 - uid: 2627 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 2.5,23.5 @@ -62186,8 +60031,6 @@ entities: powerLoad: 0 - uid: 2628 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,34.5 parent: 4812 @@ -62195,8 +60038,6 @@ entities: powerLoad: 0 - uid: 2629 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,34.5 parent: 4812 @@ -62204,8 +60045,6 @@ entities: powerLoad: 0 - uid: 2630 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,35.5 parent: 4812 @@ -62213,8 +60052,6 @@ entities: powerLoad: 0 - uid: 2631 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,35.5 parent: 4812 @@ -62222,8 +60059,6 @@ entities: powerLoad: 0 - uid: 2687 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -5.5,31.5 @@ -62232,8 +60067,6 @@ entities: powerLoad: 0 - uid: 2688 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 0.5,31.5 @@ -62242,8 +60075,6 @@ entities: powerLoad: 0 - uid: 2689 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -5.5,18.5 @@ -62252,8 +60083,6 @@ entities: powerLoad: 0 - uid: 2690 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 0.5,18.5 @@ -62262,8 +60091,6 @@ entities: powerLoad: 0 - uid: 2691 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -0.5,13.5 @@ -62272,8 +60099,6 @@ entities: powerLoad: 0 - uid: 2692 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -4.5,14.5 @@ -62282,8 +60107,6 @@ entities: powerLoad: 0 - uid: 2694 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,20.5 parent: 4812 @@ -62291,8 +60114,6 @@ entities: powerLoad: 0 - uid: 2696 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,20.5 parent: 4812 @@ -62300,8 +60121,6 @@ entities: powerLoad: 0 - uid: 2733 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 0.5,24.5 @@ -62310,8 +60129,6 @@ entities: powerLoad: 0 - uid: 2734 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -5.5,24.5 @@ -62320,8 +60137,6 @@ entities: powerLoad: 0 - uid: 2776 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 15.5,31.5 @@ -62330,8 +60145,6 @@ entities: powerLoad: 0 - uid: 2784 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,20.5 parent: 4812 @@ -62339,8 +60152,6 @@ entities: powerLoad: 0 - uid: 2785 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,20.5 parent: 4812 @@ -62348,8 +60159,6 @@ entities: powerLoad: 0 - uid: 3216 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: 17.5,17.5 @@ -62358,8 +60167,6 @@ entities: powerLoad: 0 - uid: 3217 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: 17.5,13.5 @@ -62368,8 +60175,6 @@ entities: powerLoad: 0 - uid: 3218 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: 23.5,12.5 @@ -62378,8 +60183,6 @@ entities: powerLoad: 0 - uid: 3219 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,21.5 parent: 4812 @@ -62387,8 +60190,6 @@ entities: powerLoad: 0 - uid: 3220 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,27.5 parent: 4812 @@ -62396,8 +60197,6 @@ entities: powerLoad: 0 - uid: 3221 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: 17.5,23.5 @@ -62406,8 +60205,6 @@ entities: powerLoad: 0 - uid: 3965 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,56.5 parent: 4812 @@ -62415,8 +60212,6 @@ entities: powerLoad: 0 - uid: 3966 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,50.5 parent: 4812 @@ -62424,8 +60219,6 @@ entities: powerLoad: 0 - uid: 3967 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 13.5,54.5 @@ -62434,8 +60227,6 @@ entities: powerLoad: 0 - uid: 3968 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 13.5,50.5 @@ -62444,8 +60235,6 @@ entities: powerLoad: 0 - uid: 3969 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 7.5,50.5 @@ -62454,8 +60243,6 @@ entities: powerLoad: 0 - uid: 3970 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 7.5,54.5 @@ -62464,8 +60251,6 @@ entities: powerLoad: 0 - uid: 3971 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: 10.5,43.5 @@ -62474,8 +60259,6 @@ entities: powerLoad: 0 - uid: 3972 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,47.5 parent: 4812 @@ -62483,8 +60266,6 @@ entities: powerLoad: 0 - uid: 3973 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,47.5 parent: 4812 @@ -62492,8 +60273,6 @@ entities: powerLoad: 0 - uid: 4468 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 11.5,12.5 @@ -62502,8 +60281,6 @@ entities: powerLoad: 0 - uid: 4469 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 11.5,4.5 @@ -62512,8 +60289,6 @@ entities: powerLoad: 0 - uid: 4470 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 11.5,-5.5 @@ -62522,8 +60297,6 @@ entities: powerLoad: 0 - uid: 4471 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-2.5 parent: 4812 @@ -62531,8 +60304,6 @@ entities: powerLoad: 0 - uid: 4472 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: 29.5,-9.5 @@ -62541,8 +60312,6 @@ entities: powerLoad: 0 - uid: 4473 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,4.5 parent: 4812 @@ -62550,8 +60319,6 @@ entities: powerLoad: 0 - uid: 4474 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 27.5,-5.5 @@ -62560,8 +60327,6 @@ entities: powerLoad: 0 - uid: 4475 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 27.5,0.5 @@ -62570,8 +60335,6 @@ entities: powerLoad: 0 - uid: 4476 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: 24.5,-7.5 @@ -62580,39 +60343,29 @@ entities: powerLoad: 0 - uid: 4952 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-25.5 parent: 4812 - uid: 4953 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -19.5,-23.5 parent: 4812 - uid: 4954 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -10.5,-27.5 parent: 4812 - uid: 4986 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -12.5,-27.5 parent: 4812 - uid: 5067 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -10.5,-53.5 @@ -62621,8 +60374,6 @@ entities: powerLoad: 0 - uid: 5068 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -10.5,-40.5 @@ -62631,8 +60382,6 @@ entities: powerLoad: 0 - uid: 5069 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-45.5 parent: 4812 @@ -62640,8 +60389,6 @@ entities: powerLoad: 0 - uid: 5070 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-45.5 parent: 4812 @@ -62649,8 +60396,6 @@ entities: powerLoad: 0 - uid: 5283 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,-34.5 parent: 4812 @@ -62658,8 +60403,6 @@ entities: powerLoad: 0 - uid: 5284 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-34.5 parent: 4812 @@ -62667,8 +60410,6 @@ entities: powerLoad: 0 - uid: 6258 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 7.5,-14.5 @@ -62677,8 +60418,6 @@ entities: powerLoad: 0 - uid: 6259 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 7.5,-18.5 @@ -62687,8 +60426,6 @@ entities: powerLoad: 0 - uid: 6260 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-17.5 parent: 4812 @@ -62696,8 +60433,6 @@ entities: powerLoad: 0 - uid: 6261 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: 6.5,-28.5 @@ -62706,8 +60441,6 @@ entities: powerLoad: 0 - uid: 6262 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-23.5 parent: 4812 @@ -62715,8 +60448,6 @@ entities: powerLoad: 0 - uid: 6263 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 11.5,-27.5 @@ -62725,8 +60456,6 @@ entities: powerLoad: 0 - uid: 6264 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 16.5,-25.5 @@ -62735,8 +60464,6 @@ entities: powerLoad: 0 - uid: 6265 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 9.5,-27.5 @@ -62745,8 +60472,6 @@ entities: powerLoad: 0 - uid: 6266 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-20.5 parent: 4812 @@ -62754,8 +60479,6 @@ entities: powerLoad: 0 - uid: 6267 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-20.5 parent: 4812 @@ -62763,8 +60486,6 @@ entities: powerLoad: 0 - uid: 6268 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 9.5,-16.5 @@ -62773,8 +60494,6 @@ entities: powerLoad: 0 - uid: 6269 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 23.5,-28.5 @@ -62783,8 +60502,6 @@ entities: powerLoad: 0 - uid: 6270 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: 28.5,-30.5 @@ -62793,8 +60510,6 @@ entities: powerLoad: 0 - uid: 6271 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 28.5,-22.5 parent: 4812 @@ -62802,8 +60517,6 @@ entities: powerLoad: 0 - uid: 6272 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 21.5,-26.5 @@ -62812,8 +60525,6 @@ entities: powerLoad: 0 - uid: 6273 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 21.5,-22.5 @@ -62822,8 +60533,6 @@ entities: powerLoad: 0 - uid: 6274 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 24.5,-19.5 @@ -62832,8 +60541,6 @@ entities: powerLoad: 0 - uid: 6275 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 28.5,-19.5 @@ -62842,8 +60549,6 @@ entities: powerLoad: 0 - uid: 6276 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-23.5 parent: 4812 @@ -62851,8 +60556,6 @@ entities: powerLoad: 0 - uid: 6277 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: 31.5,-28.5 @@ -62861,8 +60564,6 @@ entities: powerLoad: 0 - uid: 6478 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -1.5,-26.5 @@ -62871,8 +60572,6 @@ entities: powerLoad: 0 - uid: 7019 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -19.5,-34.5 @@ -62881,8 +60580,6 @@ entities: powerLoad: 0 - uid: 7024 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -26.5,-32.5 @@ -62891,45 +60588,33 @@ entities: powerLoad: 0 - uid: 7028 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,-15.5 parent: 4812 - uid: 7053 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -22.5,-28.5 parent: 4812 - uid: 7288 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-20.5 parent: 4812 - uid: 7367 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -17.5,-25.5 parent: 4812 - uid: 7397 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-22.5 parent: 4812 - uid: 7582 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,-20.5 parent: 4812 @@ -62937,8 +60622,6 @@ entities: powerLoad: 0 - uid: 7583 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -10.5,-18.5 @@ -62947,8 +60630,6 @@ entities: powerLoad: 0 - uid: 7621 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -2.5,-21.5 parent: 4812 @@ -62956,8 +60637,6 @@ entities: powerLoad: 0 - uid: 7622 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -2.5,-19.5 @@ -62966,8 +60645,6 @@ entities: powerLoad: 0 - uid: 7623 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -5.5,-11.5 parent: 4812 @@ -62975,8 +60652,6 @@ entities: powerLoad: 0 - uid: 7624 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,-11.5 parent: 4812 @@ -62984,8 +60659,6 @@ entities: powerLoad: 0 - uid: 7625 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 0.5,-30.5 @@ -62994,8 +60667,6 @@ entities: powerLoad: 0 - uid: 7626 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -5.5,-30.5 @@ -63004,8 +60675,6 @@ entities: powerLoad: 0 - uid: 7627 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 0.5,-23.5 @@ -63014,8 +60683,6 @@ entities: powerLoad: 0 - uid: 7628 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -5.5,-23.5 @@ -63024,8 +60691,6 @@ entities: powerLoad: 0 - uid: 7629 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-11.5 parent: 4812 @@ -63033,8 +60698,6 @@ entities: powerLoad: 0 - uid: 7630 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-11.5 parent: 4812 @@ -63042,8 +60705,6 @@ entities: powerLoad: 0 - uid: 7631 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,-11.5 parent: 4812 @@ -63051,8 +60712,6 @@ entities: powerLoad: 0 - uid: 7632 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-11.5 parent: 4812 @@ -63060,8 +60719,6 @@ entities: powerLoad: 0 - uid: 8141 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,12.5 parent: 4812 @@ -63069,8 +60726,6 @@ entities: powerLoad: 0 - uid: 8142 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -31.5,16.5 @@ -63079,8 +60734,6 @@ entities: powerLoad: 0 - uid: 8143 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -33.5,16.5 @@ -63089,8 +60742,6 @@ entities: powerLoad: 0 - uid: 8144 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -28.5,12.5 @@ -63099,8 +60750,6 @@ entities: powerLoad: 0 - uid: 8145 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -36.5,19.5 @@ -63109,8 +60758,6 @@ entities: powerLoad: 0 - uid: 8247 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -19.5,28.5 @@ -63119,16 +60766,12 @@ entities: powerLoad: 0 - uid: 8251 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -24.5,-27.5 parent: 4812 - uid: 8269 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,25.5 parent: 4812 @@ -63136,8 +60779,6 @@ entities: powerLoad: 0 - uid: 8304 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -22.5,18.5 @@ -63146,8 +60787,6 @@ entities: powerLoad: 0 - uid: 8305 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -16.5,15.5 @@ -63156,8 +60795,6 @@ entities: powerLoad: 0 - uid: 8603 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,12.5 parent: 4812 @@ -63165,8 +60802,6 @@ entities: powerLoad: 0 - uid: 8604 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -18.5,7.5 @@ -63175,8 +60810,6 @@ entities: powerLoad: 0 - uid: 9017 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-33.5 parent: 4812 @@ -63184,8 +60817,6 @@ entities: powerLoad: 0 - uid: 9311 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -53.5,1.5 @@ -63194,8 +60825,6 @@ entities: powerLoad: 0 - uid: 9312 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -53.5,3.5 @@ -63204,8 +60833,6 @@ entities: powerLoad: 0 - uid: 9313 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -53.5,5.5 @@ -63214,8 +60841,6 @@ entities: powerLoad: 0 - uid: 9314 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -53.5,7.5 @@ -63224,8 +60849,6 @@ entities: powerLoad: 0 - uid: 9315 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -53.5,9.5 @@ -63234,8 +60857,6 @@ entities: powerLoad: 0 - uid: 9316 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -53.5,11.5 @@ -63244,8 +60865,6 @@ entities: powerLoad: 0 - uid: 9317 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -49.5,0.5 @@ -63254,8 +60873,6 @@ entities: powerLoad: 0 - uid: 9318 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -49.5,4.5 @@ -63264,8 +60881,6 @@ entities: powerLoad: 0 - uid: 9319 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -49.5,8.5 @@ -63274,8 +60889,6 @@ entities: powerLoad: 0 - uid: 9320 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -49.5,12.5 @@ -63284,8 +60897,6 @@ entities: powerLoad: 0 - uid: 9706 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,14.5 parent: 4812 @@ -63293,8 +60904,6 @@ entities: powerLoad: 0 - uid: 9707 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,14.5 parent: 4812 @@ -63302,8 +60911,6 @@ entities: powerLoad: 0 - uid: 9819 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,5.5 parent: 4812 @@ -63311,8 +60918,6 @@ entities: powerLoad: 0 - uid: 9820 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -41.5,10.5 @@ -63321,8 +60926,6 @@ entities: powerLoad: 0 - uid: 9821 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -41.5,0.5 @@ -63331,8 +60934,6 @@ entities: powerLoad: 0 - uid: 9822 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -28.5,1.5 @@ -63341,8 +60942,6 @@ entities: powerLoad: 0 - uid: 9823 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -34.5,1.5 @@ -63351,8 +60950,6 @@ entities: powerLoad: 0 - uid: 10660 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -52.5,-10.5 @@ -63361,8 +60958,6 @@ entities: powerLoad: 0 - uid: 10661 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -54.5,-10.5 @@ -63371,8 +60966,6 @@ entities: powerLoad: 0 - uid: 10680 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-6.5 parent: 4812 @@ -63380,8 +60973,6 @@ entities: powerLoad: 0 - uid: 10681 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-11.5 parent: 4812 @@ -63389,8 +60980,6 @@ entities: powerLoad: 0 - uid: 10682 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-10.5 parent: 4812 @@ -63398,8 +60987,6 @@ entities: powerLoad: 0 - uid: 10684 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -42.5,-18.5 @@ -63408,8 +60995,6 @@ entities: powerLoad: 0 - uid: 10686 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -28.5,-5.5 @@ -63418,8 +61003,6 @@ entities: powerLoad: 0 - uid: 10687 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -28.5,-9.5 @@ -63428,8 +61011,6 @@ entities: powerLoad: 0 - uid: 10688 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -25.5,-2.5 @@ -63438,8 +61019,6 @@ entities: powerLoad: 0 - uid: 10689 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-0.5 parent: 4812 @@ -63447,8 +61026,6 @@ entities: powerLoad: 0 - uid: 10690 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,8.5 parent: 4812 @@ -63456,8 +61033,6 @@ entities: powerLoad: 0 - uid: 10691 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -22.5,13.5 @@ -63466,8 +61041,6 @@ entities: powerLoad: 0 - uid: 10692 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-1.5 parent: 4812 @@ -63475,8 +61048,6 @@ entities: powerLoad: 0 - uid: 10693 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-5.5 parent: 4812 @@ -63484,8 +61055,6 @@ entities: powerLoad: 0 - uid: 10694 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-6.5 parent: 4812 @@ -63493,8 +61062,6 @@ entities: powerLoad: 0 - uid: 10695 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -45.5,-14.5 @@ -63503,8 +61070,6 @@ entities: powerLoad: 0 - uid: 10920 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -25.5,-9.5 @@ -63513,8 +61078,6 @@ entities: powerLoad: 0 - uid: 11246 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 4.5,-37.5 @@ -63523,8 +61086,6 @@ entities: powerLoad: 0 - uid: 11247 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 4.5,-41.5 @@ -63533,8 +61094,6 @@ entities: powerLoad: 0 - uid: 11248 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 14.5,-37.5 @@ -63543,8 +61102,6 @@ entities: powerLoad: 0 - uid: 11916 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -17.5,1.5 @@ -63553,8 +61110,6 @@ entities: powerLoad: 0 - uid: 11917 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,5.5 parent: 4812 @@ -63562,8 +61117,6 @@ entities: powerLoad: 0 - uid: 11918 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -21.5,-1.5 @@ -63572,8 +61125,6 @@ entities: powerLoad: 0 - uid: 11920 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 3.141592653589793 rad pos: -9.5,-35.5 @@ -63582,8 +61133,6 @@ entities: powerLoad: 0 - uid: 11921 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-14.5 parent: 4812 @@ -63591,8 +61140,6 @@ entities: powerLoad: 0 - uid: 11923 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,5.5 parent: 4812 @@ -63600,8 +61147,6 @@ entities: powerLoad: 0 - uid: 11924 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: -33.5,-7.5 @@ -63610,8 +61155,6 @@ entities: powerLoad: 0 - uid: 11926 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: -38.5,-7.5 @@ -63620,8 +61163,6 @@ entities: powerLoad: 0 - uid: 11961 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,17.5 parent: 4812 @@ -67594,8 +65135,6 @@ entities: parent: 4812 - uid: 11812 components: - - type: MetaData - flags: InContainer - type: Transform parent: 11811 - type: Physics @@ -69099,29 +66638,21 @@ entities: entities: - uid: 933 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -6.5,-7.5 parent: 4812 - uid: 7186 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -18.5,-26.5 parent: 4812 - uid: 9020 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -40.5,-33.5 parent: 4812 - uid: 11100 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 10.5,-43.5 parent: 4812 @@ -69190,7 +66721,7 @@ entities: - type: Transform pos: -15.503267,29.527489 parent: 4812 -- proto: soda_dispenser +- proto: SodaDispenser entities: - uid: 364 components: @@ -69739,23 +67270,6 @@ entities: - type: Transform pos: -43.5,6.5 parent: 4812 -- proto: SpawnMobDrone - entities: - - uid: 11933 - components: - - type: Transform - pos: -13.5,16.5 - parent: 4812 - - uid: 11934 - components: - - type: Transform - pos: -12.5,16.5 - parent: 4812 - - uid: 11935 - components: - - type: Transform - pos: -11.5,16.5 - parent: 4812 - proto: SpawnMobFoxRenault entities: - uid: 2621 @@ -70208,20 +67722,6 @@ entities: - type: Transform pos: -34.5,15.5 parent: 4812 -- proto: SpawnVehicleJanicart - entities: - - uid: 1481 - components: - - type: Transform - pos: -22.5,-6.5 - parent: 4812 -- proto: SpawnVehicleSecway - entities: - - uid: 8348 - components: - - type: Transform - pos: -31.5,19.5 - parent: 4812 - proto: Spoon entities: - uid: 1774 @@ -70413,57 +67913,41 @@ entities: - type: Transform pos: 23.5,-22.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 6326 components: - type: Transform pos: 24.5,-22.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9553 components: - type: Transform pos: -41.5,6.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9554 components: - type: Transform pos: -41.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9555 components: - type: Transform pos: -41.5,8.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9665 components: - type: Transform pos: -34.5,4.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 9666 components: - type: Transform pos: -34.5,5.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - uid: 12462 components: - type: Transform pos: -36.5,26.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: SubstationBasic entities: - uid: 897 @@ -73157,57 +70641,41 @@ entities: entities: - uid: 4859 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-13.5 parent: 4812 - uid: 5015 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-13.5 parent: 4812 - uid: 6679 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-16.5 parent: 4812 - uid: 6693 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-35.5 parent: 4812 - uid: 6757 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-37.5 parent: 4812 - uid: 6758 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-35.5 parent: 4812 - uid: 7192 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-17.5 parent: 4812 - uid: 7193 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-15.5 parent: 4812 @@ -73543,26 +71011,10 @@ entities: - type: Transform pos: -38.5,-33.5 parent: 4812 -- proto: VehicleKeyJanicart - entities: - - uid: 1482 - components: - - type: Transform - pos: -22.528929,-6.6977267 - parent: 4812 -- proto: VehicleKeySecway - entities: - - uid: 8349 - components: - - type: Transform - pos: -31.5,20.5 - parent: 4812 - proto: VendingBarDrobe entities: - uid: 1647 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 5.5,6.5 parent: 4812 @@ -73570,8 +71022,6 @@ entities: entities: - uid: 9572 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -28.5,2.5 parent: 4812 @@ -73579,22 +71029,16 @@ entities: entities: - uid: 12 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 4.5,6.5 parent: 4812 - uid: 2606 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -15.5,25.5 parent: 4812 - uid: 10494 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -38.5,-22.5 parent: 4812 @@ -73602,8 +71046,6 @@ entities: entities: - uid: 3230 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 14.5,19.5 parent: 4812 @@ -73611,8 +71053,6 @@ entities: entities: - uid: 2510 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 10.5,26.5 parent: 4812 @@ -73620,22 +71060,16 @@ entities: entities: - uid: 923 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 0.5,-6.5 parent: 4812 - uid: 4538 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 23.5,-6.5 parent: 4812 - uid: 6422 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 2.5,-32.5 parent: 4812 @@ -73643,8 +71077,6 @@ entities: entities: - uid: 7036 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -27.5,-35.5 parent: 4812 @@ -73652,8 +71084,6 @@ entities: entities: - uid: 1539 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 7.5,-3.5 parent: 4812 @@ -73661,8 +71091,6 @@ entities: entities: - uid: 1124 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 3.5,-3.5 parent: 4812 @@ -73670,8 +71098,6 @@ entities: entities: - uid: 6608 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -12.5,-15.5 parent: 4812 @@ -73679,8 +71105,6 @@ entities: entities: - uid: 741 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -10.5,-14.5 parent: 4812 @@ -73689,7 +71113,6 @@ entities: - uid: 917 components: - type: MetaData - flags: SessionSpecific name: cigarette machine - type: Transform pos: -6.5,9.5 @@ -73697,7 +71120,6 @@ entities: - uid: 2672 components: - type: MetaData - flags: SessionSpecific name: cigarette machine - type: Transform pos: 0.5,28.5 @@ -73705,7 +71127,6 @@ entities: - uid: 4536 components: - type: MetaData - flags: SessionSpecific name: cigarette machine - type: Transform pos: 21.5,-4.5 @@ -73713,7 +71134,6 @@ entities: - uid: 5372 components: - type: MetaData - flags: SessionSpecific name: cigarette machine - type: Transform pos: 18.5,-18.5 @@ -73721,7 +71141,6 @@ entities: - uid: 8268 components: - type: MetaData - flags: SessionSpecific name: cigarette machine - type: Transform pos: -23.5,25.5 @@ -73729,7 +71148,6 @@ entities: - uid: 9077 components: - type: MetaData - flags: SessionSpecific name: cigarette machine - type: Transform pos: -30.5,-28.5 @@ -73737,7 +71155,6 @@ entities: - uid: 10873 components: - type: MetaData - flags: SessionSpecific name: cigarette machine - type: Transform pos: -27.5,7.5 @@ -73746,8 +71163,6 @@ entities: entities: - uid: 10850 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -23.5,-1.5 parent: 4812 @@ -73756,7 +71171,6 @@ entities: - uid: 918 components: - type: MetaData - flags: SessionSpecific name: Hot drinks machine - type: Transform pos: 0.5,10.5 @@ -73764,7 +71178,6 @@ entities: - uid: 1797 components: - type: MetaData - flags: SessionSpecific name: Hot drinks machine - type: Transform pos: -5.5,16.5 @@ -73772,7 +71185,6 @@ entities: - uid: 2671 components: - type: MetaData - flags: SessionSpecific name: Hot drinks machine - type: Transform pos: -5.5,28.5 @@ -73780,7 +71192,6 @@ entities: - uid: 4537 components: - type: MetaData - flags: SessionSpecific name: Hot drinks machine - type: Transform pos: 23.5,-5.5 @@ -73788,7 +71199,6 @@ entities: - uid: 6356 components: - type: MetaData - flags: SessionSpecific name: Hot drinks machine - type: Transform pos: 9.5,-28.5 @@ -73796,7 +71206,6 @@ entities: - uid: 10741 components: - type: MetaData - flags: SessionSpecific name: Hot drinks machine - type: Transform pos: -45.5,-5.5 @@ -73804,7 +71213,6 @@ entities: - uid: 12150 components: - type: MetaData - flags: SessionSpecific name: Hot drinks machine - type: Transform pos: -27.5,-12.5 @@ -73813,15 +71221,11 @@ entities: entities: - uid: 4533 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 13.5,2.5 parent: 4812 - uid: 5061 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -9.5,-46.5 parent: 4812 @@ -73829,8 +71233,6 @@ entities: entities: - uid: 9584 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 0.5,-3.5 parent: 4812 @@ -73845,8 +71247,6 @@ entities: entities: - uid: 5361 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 20.5,-14.5 parent: 4812 @@ -73855,7 +71255,6 @@ entities: - uid: 1552 components: - type: MetaData - flags: SessionSpecific name: Dinnerware - type: Transform pos: 2.5,-3.5 @@ -73864,8 +71263,6 @@ entities: entities: - uid: 919 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 0.5,11.5 parent: 4812 @@ -73873,8 +71270,6 @@ entities: entities: - uid: 12516 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -38.5,-7.5 parent: 4812 @@ -73882,8 +71277,6 @@ entities: entities: - uid: 9722 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -31.5,-5.5 parent: 4812 @@ -73891,8 +71284,6 @@ entities: entities: - uid: 7070 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -30.5,-14.5 parent: 4812 @@ -73900,8 +71291,6 @@ entities: entities: - uid: 2607 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -14.5,-18.5 parent: 4812 @@ -73909,8 +71298,6 @@ entities: entities: - uid: 1565 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 6.5,-3.5 parent: 4812 @@ -73918,8 +71305,6 @@ entities: entities: - uid: 1094 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -18.5,-7.5 parent: 4812 @@ -73927,8 +71312,6 @@ entities: entities: - uid: 1480 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -20.5,-9.5 parent: 4812 @@ -73936,8 +71319,6 @@ entities: entities: - uid: 8275 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -16.5,17.5 parent: 4812 @@ -73945,8 +71326,6 @@ entities: entities: - uid: 7188 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -8.5,-28.5 parent: 4812 @@ -73954,8 +71333,6 @@ entities: entities: - uid: 7399 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -8.5,-25.5 parent: 4812 @@ -73963,15 +71340,11 @@ entities: entities: - uid: 1092 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -7.5,-5.5 parent: 4812 - uid: 11785 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 25.5,-32.5 parent: 4812 @@ -73979,8 +71352,6 @@ entities: entities: - uid: 6223 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 11.5,-26.5 parent: 4812 @@ -73988,8 +71359,6 @@ entities: entities: - uid: 737 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 4.5,-23.5 parent: 4812 @@ -73998,7 +71367,6 @@ entities: - uid: 3434 components: - type: MetaData - flags: SessionSpecific name: Salvage Equipment - type: Transform pos: 23.5,16.5 @@ -74007,8 +71375,6 @@ entities: entities: - uid: 5513 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 25.5,-26.5 parent: 4812 @@ -74016,15 +71382,11 @@ entities: entities: - uid: 8342 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -31.5,13.5 parent: 4812 - uid: 8420 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -36.5,12.5 parent: 4812 @@ -74032,15 +71394,11 @@ entities: entities: - uid: 6439 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -3.5,-29.5 parent: 4812 - uid: 12506 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -34.5,10.5 parent: 4812 @@ -74048,8 +71406,6 @@ entities: entities: - uid: 1091 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -8.5,-5.5 parent: 4812 @@ -74057,8 +71413,6 @@ entities: entities: - uid: 6240 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -29.5,32.5 parent: 4812 @@ -74066,22 +71420,16 @@ entities: entities: - uid: 920 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 0.5,-7.5 parent: 4812 - uid: 4530 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 13.5,1.5 parent: 4812 - uid: 5060 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -9.5,-45.5 parent: 4812 @@ -74089,8 +71437,6 @@ entities: entities: - uid: 11780 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 29.5,-32.5 parent: 4812 @@ -74098,8 +71444,6 @@ entities: entities: - uid: 6165 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -28.5,32.5 parent: 4812 @@ -74108,7 +71452,6 @@ entities: - uid: 739 components: - type: MetaData - flags: SessionSpecific name: tank dispenser - type: Transform pos: 7.5,15.5 @@ -74116,7 +71459,6 @@ entities: - uid: 9577 components: - type: MetaData - flags: SessionSpecific name: tank dispenser - type: Transform pos: -31.5,1.5 @@ -74125,15 +71467,12 @@ entities: entities: - uid: 732 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: 9.5,14.5 parent: 4812 - uid: 6312 components: - type: MetaData - flags: SessionSpecific name: tank dispenser - type: Transform pos: 26.5,-26.5 @@ -74141,7 +71480,6 @@ entities: - uid: 10708 components: - type: MetaData - flags: SessionSpecific name: tank dispenser - type: Transform pos: -35.5,-9.5 @@ -74149,7 +71487,6 @@ entities: - uid: 12113 components: - type: MetaData - flags: SessionSpecific name: tank dispenser - type: Transform pos: -42.5,-3.5 @@ -74158,15 +71495,11 @@ entities: entities: - uid: 1692 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -7.5,1.5 parent: 4812 - uid: 9045 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -26.5,-39.5 parent: 4812 @@ -74174,8 +71507,6 @@ entities: entities: - uid: 8578 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -18.5,7.5 parent: 4812 @@ -74183,8 +71514,6 @@ entities: entities: - uid: 8871 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -37.5,-33.5 parent: 4812 @@ -74192,15 +71521,11 @@ entities: entities: - uid: 8577 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -19.5,7.5 parent: 4812 - uid: 9723 components: - - type: MetaData - flags: SessionSpecific - type: Transform pos: -32.5,-5.5 parent: 4812 @@ -74243,6848 +71568,4894 @@ entities: entities: - uid: 32 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,15.5 parent: 4812 - uid: 35 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,15.5 parent: 4812 - uid: 122 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,18.5 parent: 4812 - uid: 123 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,18.5 parent: 4812 - uid: 124 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,15.5 parent: 4812 - uid: 125 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,18.5 parent: 4812 - uid: 126 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,18.5 parent: 4812 - uid: 127 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,15.5 parent: 4812 - uid: 138 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,18.5 parent: 4812 - uid: 160 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,18.5 parent: 4812 - uid: 170 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,17.5 parent: 4812 - uid: 171 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,16.5 parent: 4812 - uid: 172 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,15.5 parent: 4812 - uid: 173 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,14.5 parent: 4812 - uid: 174 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,13.5 parent: 4812 - uid: 176 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,18.5 parent: 4812 - uid: 297 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,17.5 parent: 4812 - uid: 298 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,16.5 parent: 4812 - uid: 299 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,15.5 parent: 4812 - uid: 300 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,14.5 parent: 4812 - uid: 301 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,14.5 parent: 4812 - uid: 302 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,14.5 parent: 4812 - uid: 303 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,14.5 parent: 4812 - uid: 304 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,14.5 parent: 4812 - uid: 305 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,14.5 parent: 4812 - uid: 306 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,14.5 parent: 4812 - uid: 721 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,14.5 parent: 4812 - uid: 722 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,13.5 parent: 4812 - uid: 724 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,13.5 parent: 4812 - uid: 725 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,13.5 parent: 4812 - uid: 726 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,13.5 parent: 4812 - uid: 727 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,13.5 parent: 4812 - uid: 728 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,13.5 parent: 4812 - uid: 772 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,18.5 parent: 4812 - uid: 773 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,18.5 parent: 4812 - uid: 1819 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -3.5,21.5 parent: 4812 - uid: 1820 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,21.5 parent: 4812 - uid: 1821 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -5.5,21.5 parent: 4812 - uid: 1822 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,21.5 parent: 4812 - uid: 1823 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,22.5 parent: 4812 - uid: 1824 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,23.5 parent: 4812 - uid: 1825 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,24.5 parent: 4812 - uid: 1826 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,25.5 parent: 4812 - uid: 1827 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,26.5 parent: 4812 - uid: 1828 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,27.5 parent: 4812 - uid: 1829 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -1.5,21.5 parent: 4812 - uid: 1830 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,21.5 parent: 4812 - uid: 1831 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,21.5 parent: 4812 - uid: 1832 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,21.5 parent: 4812 - uid: 1833 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,22.5 parent: 4812 - uid: 1834 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,23.5 parent: 4812 - uid: 1836 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,25.5 parent: 4812 - uid: 1837 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,26.5 parent: 4812 - uid: 1838 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,27.5 parent: 4812 - uid: 1839 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,27.5 parent: 4812 - uid: 1840 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -1.5,27.5 parent: 4812 - uid: 1841 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -3.5,27.5 parent: 4812 - uid: 1842 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -5.5,27.5 parent: 4812 - uid: 1857 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,31.5 parent: 4812 - uid: 1859 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,31.5 parent: 4812 - uid: 1861 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,31.5 parent: 4812 - uid: 1862 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,31.5 parent: 4812 - uid: 1863 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,33.5 parent: 4812 - uid: 1864 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,33.5 parent: 4812 - uid: 1865 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,35.5 parent: 4812 - uid: 1866 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,35.5 parent: 4812 - uid: 1867 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,36.5 parent: 4812 - uid: 1868 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,36.5 parent: 4812 - uid: 1885 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,33.5 parent: 4812 - uid: 1886 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,33.5 parent: 4812 - uid: 1887 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,32.5 parent: 4812 - uid: 1888 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,31.5 parent: 4812 - uid: 1889 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,30.5 parent: 4812 - uid: 1890 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,30.5 parent: 4812 - uid: 1891 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,30.5 parent: 4812 - uid: 1892 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,30.5 parent: 4812 - uid: 1893 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,21.5 parent: 4812 - uid: 1894 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,22.5 parent: 4812 - uid: 1895 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,23.5 parent: 4812 - uid: 1896 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,21.5 parent: 4812 - uid: 1899 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,27.5 parent: 4812 - uid: 1900 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,28.5 parent: 4812 - uid: 1901 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,29.5 parent: 4812 - uid: 1903 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,22.5 parent: 4812 - uid: 1904 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,23.5 parent: 4812 - uid: 1905 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,24.5 parent: 4812 - uid: 1906 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,25.5 parent: 4812 - uid: 1907 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,26.5 parent: 4812 - uid: 1908 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,27.5 parent: 4812 - uid: 1909 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,28.5 parent: 4812 - uid: 1910 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,29.5 parent: 4812 - uid: 1911 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,30.5 parent: 4812 - uid: 1912 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,31.5 parent: 4812 - uid: 1913 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,31.5 parent: 4812 - uid: 1914 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,31.5 parent: 4812 - uid: 1915 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,30.5 parent: 4812 - uid: 1916 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,30.5 parent: 4812 - uid: 1920 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,23.5 parent: 4812 - uid: 1921 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,23.5 parent: 4812 - uid: 1922 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,23.5 parent: 4812 - uid: 1923 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,21.5 parent: 4812 - uid: 1946 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,25.5 parent: 4812 - uid: 1947 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,26.5 parent: 4812 - uid: 1948 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,21.5 parent: 4812 - uid: 1949 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,21.5 parent: 4812 - uid: 1950 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,21.5 parent: 4812 - uid: 1951 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,21.5 parent: 4812 - uid: 1954 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,21.5 parent: 4812 - uid: 1955 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,21.5 parent: 4812 - uid: 1956 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,30.5 parent: 4812 - uid: 1957 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,30.5 parent: 4812 - uid: 1958 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,30.5 parent: 4812 - uid: 1959 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,30.5 parent: 4812 - uid: 1960 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,30.5 parent: 4812 - uid: 1961 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,30.5 parent: 4812 - uid: 1967 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,27.5 parent: 4812 - uid: 1968 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,28.5 parent: 4812 - uid: 1969 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,29.5 parent: 4812 - uid: 1970 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,29.5 parent: 4812 - uid: 1971 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,28.5 parent: 4812 - uid: 1972 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,27.5 parent: 4812 - uid: 1973 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,26.5 parent: 4812 - uid: 1974 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,25.5 parent: 4812 - uid: 1976 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,23.5 parent: 4812 - uid: 1977 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,22.5 parent: 4812 - uid: 2075 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,24.5 parent: 4812 - uid: 2609 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,21.5 parent: 4812 - uid: 2612 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,21.5 parent: 4812 - uid: 2746 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,36.5 parent: 4812 - uid: 2747 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,28.5 parent: 4812 - uid: 2748 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,29.5 parent: 4812 - uid: 2749 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,30.5 parent: 4812 - uid: 2750 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,31.5 parent: 4812 - uid: 2753 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,33.5 parent: 4812 - uid: 2755 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,23.5 parent: 4812 - uid: 2756 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,36.5 parent: 4812 - uid: 2757 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,32.5 parent: 4812 - uid: 2758 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,33.5 parent: 4812 - uid: 2766 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,32.5 parent: 4812 - uid: 2767 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,35.5 parent: 4812 - uid: 2769 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,32.5 parent: 4812 - uid: 2774 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,29.5 parent: 4812 - uid: 2778 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,34.5 parent: 4812 - uid: 2820 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,33.5 parent: 4812 - uid: 2822 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,22.5 parent: 4812 - uid: 2824 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,28.5 parent: 4812 - uid: 2825 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,29.5 parent: 4812 - uid: 2828 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,28.5 parent: 4812 - uid: 2829 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,20.5 parent: 4812 - uid: 2830 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,18.5 parent: 4812 - uid: 2831 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,17.5 parent: 4812 - uid: 2841 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,35.5 parent: 4812 - uid: 2896 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,12.5 parent: 4812 - uid: 2897 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,11.5 parent: 4812 - uid: 2898 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,11.5 parent: 4812 - uid: 2899 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,11.5 parent: 4812 - uid: 2900 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,11.5 parent: 4812 - uid: 2901 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,11.5 parent: 4812 - uid: 2903 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,16.5 parent: 4812 - uid: 2904 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,17.5 parent: 4812 - uid: 2905 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,17.5 parent: 4812 - uid: 2906 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,17.5 parent: 4812 - uid: 2942 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,29.5 parent: 4812 - uid: 3006 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,22.5 parent: 4812 - uid: 3042 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,23.5 parent: 4812 - uid: 3118 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,34.5 parent: 4812 - uid: 3150 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,33.5 parent: 4812 - uid: 3189 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,36.5 parent: 4812 - uid: 3193 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,33.5 parent: 4812 - uid: 3212 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,9.5 parent: 4812 - uid: 3288 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,6.5 parent: 4812 - uid: 3352 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,9.5 parent: 4812 - uid: 3355 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,9.5 parent: 4812 - uid: 3356 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,9.5 parent: 4812 - uid: 3357 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,9.5 parent: 4812 - uid: 3391 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,6.5 parent: 4812 - uid: 3428 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,37.5 parent: 4812 - uid: 3429 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,37.5 parent: 4812 - uid: 3430 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,36.5 parent: 4812 - uid: 3431 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,36.5 parent: 4812 - uid: 3432 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,37.5 parent: 4812 - uid: 3436 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,33.5 parent: 4812 - uid: 3437 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,31.5 parent: 4812 - uid: 3486 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,43.5 parent: 4812 - uid: 3487 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,44.5 parent: 4812 - uid: 3488 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,45.5 parent: 4812 - uid: 3489 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,46.5 parent: 4812 - uid: 3490 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,47.5 parent: 4812 - uid: 3491 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,48.5 parent: 4812 - uid: 3492 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,43.5 parent: 4812 - uid: 3493 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,43.5 parent: 4812 - uid: 3494 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,43.5 parent: 4812 - uid: 3495 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,43.5 parent: 4812 - uid: 3496 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,42.5 parent: 4812 - uid: 3497 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,42.5 parent: 4812 - uid: 3498 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,42.5 parent: 4812 - uid: 3499 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,42.5 parent: 4812 - uid: 3500 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,42.5 parent: 4812 - uid: 3501 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,43.5 parent: 4812 - uid: 3502 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,43.5 parent: 4812 - uid: 3504 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,43.5 parent: 4812 - uid: 3505 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,43.5 parent: 4812 - uid: 3506 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,44.5 parent: 4812 - uid: 3507 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,46.5 parent: 4812 - uid: 3508 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,44.5 parent: 4812 - uid: 3509 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,45.5 parent: 4812 - uid: 3510 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,46.5 parent: 4812 - uid: 3511 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,47.5 parent: 4812 - uid: 3512 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,47.5 parent: 4812 - uid: 3513 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,47.5 parent: 4812 - uid: 3514 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,48.5 parent: 4812 - uid: 3515 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,48.5 parent: 4812 - uid: 3516 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,49.5 parent: 4812 - uid: 3517 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,50.5 parent: 4812 - uid: 3518 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,51.5 parent: 4812 - uid: 3519 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,52.5 parent: 4812 - uid: 3520 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,53.5 parent: 4812 - uid: 3521 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,54.5 parent: 4812 - uid: 3522 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,55.5 parent: 4812 - uid: 3523 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,55.5 parent: 4812 - uid: 3524 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,56.5 parent: 4812 - uid: 3525 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,56.5 parent: 4812 - uid: 3526 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,57.5 parent: 4812 - uid: 3527 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,57.5 parent: 4812 - uid: 3528 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,58.5 parent: 4812 - uid: 3529 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,58.5 parent: 4812 - uid: 3530 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,58.5 parent: 4812 - uid: 3531 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,58.5 parent: 4812 - uid: 3532 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,58.5 parent: 4812 - uid: 3533 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,57.5 parent: 4812 - uid: 3534 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,57.5 parent: 4812 - uid: 3535 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,56.5 parent: 4812 - uid: 3536 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,56.5 parent: 4812 - uid: 3537 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,55.5 parent: 4812 - uid: 3538 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,55.5 parent: 4812 - uid: 3539 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,55.5 parent: 4812 - uid: 3540 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,54.5 parent: 4812 - uid: 3541 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,53.5 parent: 4812 - uid: 3542 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,52.5 parent: 4812 - uid: 3543 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,51.5 parent: 4812 - uid: 3544 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,50.5 parent: 4812 - uid: 3545 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,49.5 parent: 4812 - uid: 3546 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,48.5 parent: 4812 - uid: 3547 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,57.5 parent: 4812 - uid: 3548 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,57.5 parent: 4812 - uid: 3549 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,57.5 parent: 4812 - uid: 3550 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,56.5 parent: 4812 - uid: 3551 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,56.5 parent: 4812 - uid: 3552 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,48.5 parent: 4812 - uid: 3553 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,48.5 parent: 4812 - uid: 3554 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,48.5 parent: 4812 - uid: 3555 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,48.5 parent: 4812 - uid: 3556 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,48.5 parent: 4812 - uid: 3557 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,48.5 parent: 4812 - uid: 3558 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,47.5 parent: 4812 - uid: 3559 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,47.5 parent: 4812 - uid: 3560 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,44.5 parent: 4812 - uid: 3561 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,46.5 parent: 4812 - uid: 3562 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,47.5 parent: 4812 - uid: 3563 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,46.5 parent: 4812 - uid: 3564 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,44.5 parent: 4812 - uid: 3565 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,47.5 parent: 4812 - uid: 3566 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,49.5 parent: 4812 - uid: 3567 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,50.5 parent: 4812 - uid: 3568 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,51.5 parent: 4812 - uid: 3569 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,52.5 parent: 4812 - uid: 3570 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,53.5 parent: 4812 - uid: 3571 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,54.5 parent: 4812 - uid: 3572 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,54.5 parent: 4812 - uid: 3573 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,53.5 parent: 4812 - uid: 3574 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,52.5 parent: 4812 - uid: 3575 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,51.5 parent: 4812 - uid: 3576 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,50.5 parent: 4812 - uid: 3577 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,49.5 parent: 4812 - uid: 3691 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,54.5 parent: 4812 - uid: 3692 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,54.5 parent: 4812 - uid: 3693 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,52.5 parent: 4812 - uid: 3694 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,51.5 parent: 4812 - uid: 3695 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,51.5 parent: 4812 - uid: 3696 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,51.5 parent: 4812 - uid: 3697 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,52.5 parent: 4812 - uid: 3717 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,3.5 parent: 4812 - uid: 3718 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,3.5 parent: 4812 - uid: 3719 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,4.5 parent: 4812 - uid: 3720 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,5.5 parent: 4812 - uid: 3721 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,2.5 parent: 4812 - uid: 3722 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,7.5 parent: 4812 - uid: 3723 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,2.5 parent: 4812 - uid: 3724 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,3.5 parent: 4812 - uid: 3725 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,4.5 parent: 4812 - uid: 3727 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,2.5 parent: 4812 - uid: 3728 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,8.5 parent: 4812 - uid: 3729 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,6.5 parent: 4812 - uid: 3730 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,2.5 parent: 4812 - uid: 3731 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,2.5 parent: 4812 - uid: 3990 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,8.5 parent: 4812 - uid: 3991 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,7.5 parent: 4812 - uid: 3992 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,6.5 parent: 4812 - uid: 3993 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,5.5 parent: 4812 - uid: 3994 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,5.5 parent: 4812 - uid: 3995 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,5.5 parent: 4812 - uid: 3996 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 32.5,5.5 parent: 4812 - uid: 3997 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 31.5,5.5 parent: 4812 - uid: 3998 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,5.5 parent: 4812 - uid: 3999 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,5.5 parent: 4812 - uid: 4000 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 28.5,5.5 parent: 4812 - uid: 4001 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 27.5,5.5 parent: 4812 - uid: 4081 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-4.5 parent: 4812 - uid: 4082 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-4.5 parent: 4812 - uid: 4083 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-4.5 parent: 4812 - uid: 4084 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-5.5 parent: 4812 - uid: 4085 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-5.5 parent: 4812 - uid: 4086 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-4.5 parent: 4812 - uid: 4087 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 32.5,-10.5 parent: 4812 - uid: 4088 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 31.5,-10.5 parent: 4812 - uid: 4089 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-10.5 parent: 4812 - uid: 4090 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-10.5 parent: 4812 - uid: 4091 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 28.5,-10.5 parent: 4812 - uid: 4092 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-10.5 parent: 4812 - uid: 4093 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-9.5 parent: 4812 - uid: 4094 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-8.5 parent: 4812 - uid: 4095 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,-8.5 parent: 4812 - uid: 4096 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-8.5 parent: 4812 - uid: 4097 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-8.5 parent: 4812 - uid: 4098 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-8.5 parent: 4812 - uid: 4101 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-8.5 parent: 4812 - uid: 4102 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-8.5 parent: 4812 - uid: 4103 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-8.5 parent: 4812 - uid: 4104 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-8.5 parent: 4812 - uid: 4105 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-8.5 parent: 4812 - uid: 4106 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-9.5 parent: 4812 - uid: 4107 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-10.5 parent: 4812 - uid: 4108 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-6.5 parent: 4812 - uid: 4109 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-6.5 parent: 4812 - uid: 4638 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-36.5 parent: 4812 - uid: 4639 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,-36.5 parent: 4812 - uid: 4640 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,-36.5 parent: 4812 - uid: 4641 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-36.5 parent: 4812 - uid: 4642 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-36.5 parent: 4812 - uid: 4643 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-36.5 parent: 4812 - uid: 4681 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-40.5 parent: 4812 - uid: 4685 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-53.5 parent: 4812 - uid: 4686 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-53.5 parent: 4812 - uid: 4693 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-39.5 parent: 4812 - uid: 4694 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-37.5 parent: 4812 - uid: 4696 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-40.5 parent: 4812 - uid: 4789 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-29.5 parent: 4812 - uid: 4790 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -8.5,-29.5 parent: 4812 - uid: 4791 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-30.5 parent: 4812 - uid: 4792 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-31.5 parent: 4812 - uid: 4828 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,23.5 parent: 4812 - uid: 4829 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,23.5 parent: 4812 - uid: 4830 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,23.5 parent: 4812 - uid: 4831 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,23.5 parent: 4812 - uid: 4832 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,23.5 parent: 4812 - uid: 4886 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,23.5 parent: 4812 - uid: 4887 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,22.5 parent: 4812 - uid: 4898 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,21.5 parent: 4812 - uid: 4917 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-29.5 parent: 4812 - uid: 4936 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-27.5 parent: 4812 - uid: 5016 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-24.5 parent: 4812 - uid: 5027 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,19.5 parent: 4812 - uid: 5042 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-59.5 parent: 4812 - uid: 5126 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,20.5 parent: 4812 - uid: 5285 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,-24.5 parent: 4812 - uid: 5286 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,-23.5 parent: 4812 - uid: 5287 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -3.5,-23.5 parent: 4812 - uid: 5288 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -1.5,-23.5 parent: 4812 - uid: 5289 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-23.5 parent: 4812 - uid: 5290 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-24.5 parent: 4812 - uid: 5291 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-25.5 parent: 4812 - uid: 5292 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-26.5 parent: 4812 - uid: 5293 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -3.5,-30.5 parent: 4812 - uid: 5294 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,-30.5 parent: 4812 - uid: 5295 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,-29.5 parent: 4812 - uid: 5296 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -1.5,-30.5 parent: 4812 - uid: 5297 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-30.5 parent: 4812 - uid: 5298 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-29.5 parent: 4812 - uid: 5299 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-28.5 parent: 4812 - uid: 5312 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-13.5 parent: 4812 - uid: 5314 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-13.5 parent: 4812 - uid: 5316 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-13.5 parent: 4812 - uid: 5330 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-13.5 parent: 4812 - uid: 5331 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-19.5 parent: 4812 - uid: 5332 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-19.5 parent: 4812 - uid: 5336 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-19.5 parent: 4812 - uid: 5337 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-19.5 parent: 4812 - uid: 5395 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-22.5 parent: 4812 - uid: 5397 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-22.5 parent: 4812 - uid: 5398 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-18.5 parent: 4812 - uid: 5400 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-19.5 parent: 4812 - uid: 5401 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-17.5 parent: 4812 - uid: 5402 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-16.5 parent: 4812 - uid: 5403 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-16.5 parent: 4812 - uid: 5404 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-16.5 parent: 4812 - uid: 5405 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-16.5 parent: 4812 - uid: 5406 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,-16.5 parent: 4812 - uid: 5407 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-13.5 parent: 4812 - uid: 5408 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-14.5 parent: 4812 - uid: 5409 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-13.5 parent: 4812 - uid: 5410 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-13.5 parent: 4812 - uid: 5411 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-13.5 parent: 4812 - uid: 5412 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-17.5 parent: 4812 - uid: 5413 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-19.5 parent: 4812 - uid: 5414 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-22.5 parent: 4812 - uid: 5432 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-24.5 parent: 4812 - uid: 5433 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-28.5 parent: 4812 - uid: 5434 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-29.5 parent: 4812 - uid: 5435 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-29.5 parent: 4812 - uid: 5436 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-29.5 parent: 4812 - uid: 5437 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-29.5 parent: 4812 - uid: 5438 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,-29.5 parent: 4812 - uid: 5439 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,-29.5 parent: 4812 - uid: 5440 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-29.5 parent: 4812 - uid: 5441 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-29.5 parent: 4812 - uid: 5442 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-29.5 parent: 4812 - uid: 5443 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-29.5 parent: 4812 - uid: 5444 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-29.5 parent: 4812 - uid: 5445 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-29.5 parent: 4812 - uid: 5446 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-29.5 parent: 4812 - uid: 5447 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-28.5 parent: 4812 - uid: 5448 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-27.5 parent: 4812 - uid: 5449 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-26.5 parent: 4812 - uid: 5450 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-25.5 parent: 4812 - uid: 5451 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-26.5 parent: 4812 - uid: 5452 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-23.5 parent: 4812 - uid: 5453 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-22.5 parent: 4812 - uid: 5454 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-22.5 parent: 4812 - uid: 5455 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,-22.5 parent: 4812 - uid: 5456 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-22.5 parent: 4812 - uid: 5469 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-17.5 parent: 4812 - uid: 5470 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-18.5 parent: 4812 - uid: 5471 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-19.5 parent: 4812 - uid: 5473 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-22.5 parent: 4812 - uid: 5479 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-27.5 parent: 4812 - uid: 5480 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-27.5 parent: 4812 - uid: 5500 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,-29.5 parent: 4812 - uid: 5501 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-22.5 parent: 4812 - uid: 5502 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-22.5 parent: 4812 - uid: 5505 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-21.5 parent: 4812 - uid: 5506 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-21.5 parent: 4812 - uid: 5510 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 28.5,-21.5 parent: 4812 - uid: 5511 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-19.5 parent: 4812 - uid: 5512 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-27.5 parent: 4812 - uid: 5515 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-27.5 parent: 4812 - uid: 5516 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,-27.5 parent: 4812 - uid: 5517 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-27.5 parent: 4812 - uid: 5518 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-28.5 parent: 4812 - uid: 5519 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-28.5 parent: 4812 - uid: 5520 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-30.5 parent: 4812 - uid: 5521 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-30.5 parent: 4812 - uid: 5522 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-31.5 parent: 4812 - uid: 5523 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 25.5,-31.5 parent: 4812 - uid: 5524 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-31.5 parent: 4812 - uid: 5525 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-31.5 parent: 4812 - uid: 5526 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-31.5 parent: 4812 - uid: 5527 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-31.5 parent: 4812 - uid: 5528 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-31.5 parent: 4812 - uid: 5529 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-31.5 parent: 4812 - uid: 5530 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-21.5 parent: 4812 - uid: 5531 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-20.5 parent: 4812 - uid: 5532 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-19.5 parent: 4812 - uid: 5556 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 27.5,-31.5 parent: 4812 - uid: 5557 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 28.5,-31.5 parent: 4812 - uid: 5558 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-31.5 parent: 4812 - uid: 5559 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-31.5 parent: 4812 - uid: 5560 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-30.5 parent: 4812 - uid: 5561 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-29.5 parent: 4812 - uid: 5568 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-22.5 parent: 4812 - uid: 5569 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-21.5 parent: 4812 - uid: 5595 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,18.5 parent: 4812 - uid: 5769 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 31.5,-29.5 parent: 4812 - uid: 5770 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 32.5,-29.5 parent: 4812 - uid: 5772 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 34.5,-29.5 parent: 4812 - uid: 5773 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-29.5 parent: 4812 - uid: 5774 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-29.5 parent: 4812 - uid: 5775 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-28.5 parent: 4812 - uid: 5776 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-27.5 parent: 4812 - uid: 5777 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-26.5 parent: 4812 - uid: 5778 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-25.5 parent: 4812 - uid: 5779 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-24.5 parent: 4812 - uid: 5780 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-23.5 parent: 4812 - uid: 5781 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-22.5 parent: 4812 - uid: 5785 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 31.5,-22.5 parent: 4812 - uid: 5786 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-22.5 parent: 4812 - uid: 5826 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-59.5 parent: 4812 - uid: 5904 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 13.5,-30.5 parent: 4812 - uid: 5905 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 13.5,-31.5 parent: 4812 - uid: 5906 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 17.5,-30.5 parent: 4812 - uid: 5907 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 17.5,-31.5 parent: 4812 - uid: 5908 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 17.5,-32.5 parent: 4812 - uid: 5909 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 16.5,-32.5 parent: 4812 - uid: 5910 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 13.5,-32.5 parent: 4812 - uid: 5911 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: 1.5707963267948966 rad pos: 14.5,-32.5 parent: 4812 - uid: 6546 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-26.5 parent: 4812 - uid: 6547 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-25.5 parent: 4812 - uid: 6559 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-13.5 parent: 4812 - uid: 6560 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-18.5 parent: 4812 - uid: 6561 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-16.5 parent: 4812 - uid: 6562 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-14.5 parent: 4812 - uid: 6563 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-13.5 parent: 4812 - uid: 6564 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-19.5 parent: 4812 - uid: 6565 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,-19.5 parent: 4812 - uid: 6566 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-19.5 parent: 4812 - uid: 6567 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-17.5 parent: 4812 - uid: 6568 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-15.5 parent: 4812 - uid: 6569 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-13.5 parent: 4812 - uid: 6696 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-37.5 parent: 4812 - uid: 6697 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,-37.5 parent: 4812 - uid: 6698 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-37.5 parent: 4812 - uid: 6699 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-38.5 parent: 4812 - uid: 6700 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-39.5 parent: 4812 - uid: 6701 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-40.5 parent: 4812 - uid: 6702 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-40.5 parent: 4812 - uid: 6704 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-40.5 parent: 4812 - uid: 6705 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-40.5 parent: 4812 - uid: 6706 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-40.5 parent: 4812 - uid: 6707 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-40.5 parent: 4812 - uid: 6759 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-29.5 parent: 4812 - uid: 6760 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-30.5 parent: 4812 - uid: 6761 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-31.5 parent: 4812 - uid: 6762 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-32.5 parent: 4812 - uid: 6763 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-33.5 parent: 4812 - uid: 6900 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-33.5 parent: 4812 - uid: 6901 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -8.5,-33.5 parent: 4812 - uid: 7281 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-28.5 parent: 4812 - uid: 7289 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-29.5 parent: 4812 - uid: 7292 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-29.5 parent: 4812 - uid: 7293 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,-29.5 parent: 4812 - uid: 7294 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-29.5 parent: 4812 - uid: 7337 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-24.5 parent: 4812 - uid: 7635 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,30.5 parent: 4812 - uid: 7636 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,29.5 parent: 4812 - uid: 7637 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,30.5 parent: 4812 - uid: 7638 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,30.5 parent: 4812 - uid: 7639 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,30.5 parent: 4812 - uid: 7640 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,29.5 parent: 4812 - uid: 7641 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,21.5 parent: 4812 - uid: 7642 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,22.5 parent: 4812 - uid: 7643 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,23.5 parent: 4812 - uid: 7644 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,24.5 parent: 4812 - uid: 7645 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,26.5 parent: 4812 - uid: 7646 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,27.5 parent: 4812 - uid: 7647 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,28.5 parent: 4812 - uid: 7648 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,30.5 parent: 4812 - uid: 7649 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,29.5 parent: 4812 - uid: 7650 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,29.5 parent: 4812 - uid: 7651 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,29.5 parent: 4812 - uid: 7652 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,21.5 parent: 4812 - uid: 7653 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,21.5 parent: 4812 - uid: 7654 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,22.5 parent: 4812 - uid: 7656 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,24.5 parent: 4812 - uid: 7657 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,25.5 parent: 4812 - uid: 7658 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,26.5 parent: 4812 - uid: 7659 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,27.5 parent: 4812 - uid: 7660 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,28.5 parent: 4812 - uid: 7661 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,32.5 parent: 4812 - uid: 7662 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,33.5 parent: 4812 - uid: 7663 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,33.5 parent: 4812 - uid: 7664 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,33.5 parent: 4812 - uid: 7670 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,33.5 parent: 4812 - uid: 7671 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,33.5 parent: 4812 - uid: 7672 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,33.5 parent: 4812 - uid: 7673 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,33.5 parent: 4812 - uid: 7674 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,28.5 parent: 4812 - uid: 7675 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,29.5 parent: 4812 - uid: 7676 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,30.5 parent: 4812 - uid: 7677 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,31.5 parent: 4812 - uid: 7678 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,32.5 parent: 4812 - uid: 7679 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,28.5 parent: 4812 - uid: 7680 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,21.5 parent: 4812 - uid: 7681 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,21.5 parent: 4812 - uid: 7686 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,21.5 parent: 4812 - uid: 7687 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,21.5 parent: 4812 - uid: 7688 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,21.5 parent: 4812 - uid: 7689 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,23.5 parent: 4812 - uid: 7690 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,25.5 parent: 4812 - uid: 7691 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,25.5 parent: 4812 - uid: 7692 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,22.5 parent: 4812 - uid: 7693 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,25.5 parent: 4812 - uid: 7694 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,25.5 parent: 4812 - uid: 7695 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,21.5 parent: 4812 - uid: 7696 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,27.5 parent: 4812 - uid: 7697 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,26.5 parent: 4812 - uid: 7698 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,25.5 parent: 4812 - uid: 7699 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,24.5 parent: 4812 - uid: 7700 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,25.5 parent: 4812 - uid: 7701 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,26.5 parent: 4812 - uid: 7702 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,27.5 parent: 4812 - uid: 7703 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,28.5 parent: 4812 - uid: 7704 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,28.5 parent: 4812 - uid: 7705 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,28.5 parent: 4812 - uid: 7706 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,28.5 parent: 4812 - uid: 7707 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,28.5 parent: 4812 - uid: 7712 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,9.5 parent: 4812 - uid: 7713 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,9.5 parent: 4812 - uid: 7714 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,9.5 parent: 4812 - uid: 7715 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,9.5 parent: 4812 - uid: 7718 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,18.5 parent: 4812 - uid: 7719 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,15.5 parent: 4812 - uid: 7720 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,15.5 parent: 4812 - uid: 7721 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,15.5 parent: 4812 - uid: 7722 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,15.5 parent: 4812 - uid: 7723 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,12.5 parent: 4812 - uid: 7724 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,12.5 parent: 4812 - uid: 7732 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,18.5 parent: 4812 - uid: 7733 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,22.5 parent: 4812 - uid: 7734 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,22.5 parent: 4812 - uid: 7735 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,22.5 parent: 4812 - uid: 7736 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,22.5 parent: 4812 - uid: 7737 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,18.5 parent: 4812 - uid: 7738 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,19.5 parent: 4812 - uid: 7739 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,20.5 parent: 4812 - uid: 7740 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,18.5 parent: 4812 - uid: 7743 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,16.5 parent: 4812 - uid: 7744 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,13.5 parent: 4812 - uid: 7749 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,9.5 parent: 4812 - uid: 7750 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,9.5 parent: 4812 - uid: 7751 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,9.5 parent: 4812 - uid: 7753 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,9.5 parent: 4812 - uid: 7754 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,9.5 parent: 4812 - uid: 7755 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,9.5 parent: 4812 - uid: 7756 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,21.5 parent: 4812 - uid: 7757 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,22.5 parent: 4812 - uid: 7758 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,17.5 parent: 4812 - uid: 7759 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,16.5 parent: 4812 - uid: 7760 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,15.5 parent: 4812 - uid: 7761 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,14.5 parent: 4812 - uid: 7762 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,13.5 parent: 4812 - uid: 7763 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,13.5 parent: 4812 - uid: 7765 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,13.5 parent: 4812 - uid: 7766 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,13.5 parent: 4812 - uid: 7767 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,13.5 parent: 4812 - uid: 7768 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,9.5 parent: 4812 - uid: 7769 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,9.5 parent: 4812 - uid: 7770 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,9.5 parent: 4812 - uid: 7771 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,9.5 parent: 4812 - uid: 7772 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,9.5 parent: 4812 - uid: 7773 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,9.5 parent: 4812 - uid: 7774 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,10.5 parent: 4812 - uid: 7775 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,11.5 parent: 4812 - uid: 7776 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,12.5 parent: 4812 - uid: 7937 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,6.5 parent: 4812 - uid: 7938 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,6.5 parent: 4812 - uid: 7939 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,6.5 parent: 4812 - uid: 7940 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,6.5 parent: 4812 - uid: 7941 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,6.5 parent: 4812 - uid: 7942 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,6.5 parent: 4812 - uid: 7943 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,6.5 parent: 4812 - uid: 7944 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,6.5 parent: 4812 - uid: 7945 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,6.5 parent: 4812 - uid: 7946 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,6.5 parent: 4812 - uid: 7947 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,6.5 parent: 4812 - uid: 7948 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,6.5 parent: 4812 - uid: 7950 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,6.5 parent: 4812 - uid: 7951 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,7.5 parent: 4812 - uid: 7952 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,8.5 parent: 4812 - uid: 7955 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,8.5 parent: 4812 - uid: 8621 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,0.5 parent: 4812 - uid: 8623 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,0.5 parent: 4812 - uid: 8624 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,0.5 parent: 4812 - uid: 8625 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,1.5 parent: 4812 - uid: 8626 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,5.5 parent: 4812 - uid: 8635 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,0.5 parent: 4812 - uid: 8639 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,4.5 parent: 4812 - uid: 8641 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-0.5 parent: 4812 - uid: 8642 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-1.5 parent: 4812 - uid: 8643 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-2.5 parent: 4812 - uid: 8644 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-3.5 parent: 4812 - uid: 8645 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-4.5 parent: 4812 - uid: 8646 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-5.5 parent: 4812 - uid: 8647 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-6.5 parent: 4812 - uid: 8648 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-4.5 parent: 4812 - uid: 8649 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-4.5 parent: 4812 - uid: 8650 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-4.5 parent: 4812 - uid: 8651 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-4.5 parent: 4812 - uid: 8652 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-5.5 parent: 4812 - uid: 8653 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-9.5 parent: 4812 - uid: 8654 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-10.5 parent: 4812 - uid: 8655 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-10.5 parent: 4812 - uid: 8656 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-10.5 parent: 4812 - uid: 8657 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,-10.5 parent: 4812 - uid: 8658 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-10.5 parent: 4812 - uid: 8659 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-10.5 parent: 4812 - uid: 8660 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-10.5 parent: 4812 - uid: 8661 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-9.5 parent: 4812 - uid: 8662 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-7.5 parent: 4812 - uid: 8663 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-6.5 parent: 4812 - uid: 8664 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-10.5 parent: 4812 - uid: 8665 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-10.5 parent: 4812 - uid: 8666 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-9.5 parent: 4812 - uid: 8667 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-7.5 parent: 4812 - uid: 8668 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-6.5 parent: 4812 - uid: 8669 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-5.5 parent: 4812 - uid: 8670 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-4.5 parent: 4812 - uid: 8671 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-3.5 parent: 4812 - uid: 8672 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-2.5 parent: 4812 - uid: 8673 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-1.5 parent: 4812 - uid: 8674 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-0.5 parent: 4812 - uid: 8675 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,0.5 parent: 4812 - uid: 8712 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-0.5 parent: 4812 - uid: 8713 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,-0.5 parent: 4812 - uid: 8714 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-0.5 parent: 4812 - uid: 8715 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-1.5 parent: 4812 - uid: 8716 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-2.5 parent: 4812 - uid: 8717 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-3.5 parent: 4812 - uid: 8718 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,-4.5 parent: 4812 - uid: 8719 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-4.5 parent: 4812 - uid: 8720 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-4.5 parent: 4812 - uid: 8724 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-4.5 parent: 4812 - uid: 8726 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-2.5 parent: 4812 - uid: 8727 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-1.5 parent: 4812 - uid: 8728 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-0.5 parent: 4812 - uid: 8729 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -45.5,-0.5 parent: 4812 - uid: 8730 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -44.5,-0.5 parent: 4812 - uid: 8731 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-0.5 parent: 4812 - uid: 8732 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-0.5 parent: 4812 - uid: 8733 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -45.5,-4.5 parent: 4812 - uid: 8751 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-3.5 parent: 4812 - uid: 8789 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-41.5 parent: 4812 - uid: 8790 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,-41.5 parent: 4812 - uid: 8791 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-41.5 parent: 4812 - uid: 8793 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-41.5 parent: 4812 - uid: 8794 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,-41.5 parent: 4812 - uid: 8795 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,-41.5 parent: 4812 - uid: 8796 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-41.5 parent: 4812 - uid: 8797 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-40.5 parent: 4812 - uid: 8798 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-39.5 parent: 4812 - uid: 8799 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-38.5 parent: 4812 - uid: 8800 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-38.5 parent: 4812 - uid: 8801 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-38.5 parent: 4812 - uid: 8802 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-38.5 parent: 4812 - uid: 8809 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-38.5 parent: 4812 - uid: 8811 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-36.5 parent: 4812 - uid: 8812 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-34.5 parent: 4812 - uid: 8813 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-33.5 parent: 4812 - uid: 8814 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-32.5 parent: 4812 - uid: 8815 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-35.5 parent: 4812 - uid: 8816 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-37.5 parent: 4812 - uid: 8817 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-36.5 parent: 4812 - uid: 8818 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-35.5 parent: 4812 - uid: 8819 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-34.5 parent: 4812 - uid: 8820 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-35.5 parent: 4812 - uid: 8821 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-35.5 parent: 4812 - uid: 8822 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-34.5 parent: 4812 - uid: 8823 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-32.5 parent: 4812 - uid: 8824 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-32.5 parent: 4812 - uid: 8825 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-32.5 parent: 4812 - uid: 8826 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-32.5 parent: 4812 - uid: 8827 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,-32.5 parent: 4812 - uid: 8828 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-32.5 parent: 4812 - uid: 8829 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-32.5 parent: 4812 - uid: 8830 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-32.5 parent: 4812 - uid: 8831 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,-32.5 parent: 4812 - uid: 8873 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-11.5 parent: 4812 - uid: 8874 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-12.5 parent: 4812 - uid: 8875 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-13.5 parent: 4812 - uid: 8876 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-14.5 parent: 4812 - uid: 8877 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-15.5 parent: 4812 - uid: 9022 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,2.5 parent: 4812 - uid: 9091 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,12.5 parent: 4812 - uid: 9092 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,11.5 parent: 4812 - uid: 9093 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,10.5 parent: 4812 - uid: 9094 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,9.5 parent: 4812 - uid: 9095 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,8.5 parent: 4812 - uid: 9096 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,7.5 parent: 4812 - uid: 9097 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,6.5 parent: 4812 - uid: 9098 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,5.5 parent: 4812 - uid: 9100 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,3.5 parent: 4812 - uid: 9101 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,2.5 parent: 4812 - uid: 9102 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,1.5 parent: 4812 - uid: 9103 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,12.5 parent: 4812 - uid: 9104 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,11.5 parent: 4812 - uid: 9105 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,10.5 parent: 4812 - uid: 9106 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,9.5 parent: 4812 - uid: 9107 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,8.5 parent: 4812 - uid: 9108 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,7.5 parent: 4812 - uid: 9109 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,6.5 parent: 4812 - uid: 9110 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,5.5 parent: 4812 - uid: 9111 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,4.5 parent: 4812 - uid: 9112 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,3.5 parent: 4812 - uid: 9113 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,2.5 parent: 4812 - uid: 9114 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,1.5 parent: 4812 - uid: 9139 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,0.5 parent: 4812 - uid: 9140 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,0.5 parent: 4812 - uid: 9141 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,0.5 parent: 4812 - uid: 9142 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,0.5 parent: 4812 - uid: 9143 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,0.5 parent: 4812 - uid: 9144 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,0.5 parent: 4812 - uid: 9239 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,6.5 parent: 4812 - uid: 9240 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,4.5 parent: 4812 - uid: 9242 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,2.5 parent: 4812 - uid: 9243 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,2.5 parent: 4812 - uid: 9244 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,6.5 parent: 4812 - uid: 9245 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,2.5 parent: 4812 - uid: 9248 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,6.5 parent: 4812 - uid: 9256 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,4.5 parent: 4812 - uid: 9258 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,4.5 parent: 4812 - uid: 9260 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,4.5 parent: 4812 - uid: 9265 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,6.5 parent: 4812 - uid: 9266 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,8.5 parent: 4812 - uid: 9267 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,8.5 parent: 4812 - uid: 9268 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,8.5 parent: 4812 - uid: 9269 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,8.5 parent: 4812 - uid: 9270 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,10.5 parent: 4812 - uid: 9271 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,10.5 parent: 4812 - uid: 9272 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,10.5 parent: 4812 - uid: 9273 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,10.5 parent: 4812 - uid: 9274 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,12.5 parent: 4812 - uid: 9275 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,12.5 parent: 4812 - uid: 9276 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,12.5 parent: 4812 - uid: 9277 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,12.5 parent: 4812 - uid: 9284 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-5.5 parent: 4812 - uid: 9285 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -48.5,-3.5 parent: 4812 - uid: 9286 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -48.5,-2.5 parent: 4812 - uid: 9469 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,18.5 parent: 4812 - uid: 9470 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -45.5,18.5 parent: 4812 - uid: 9471 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,18.5 parent: 4812 - uid: 9472 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,18.5 parent: 4812 - uid: 9473 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,15.5 parent: 4812 - uid: 9474 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,15.5 parent: 4812 - uid: 9511 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,13.5 parent: 4812 - uid: 9517 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-5.5 parent: 4812 - uid: 9518 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -48.5,-5.5 parent: 4812 - uid: 9519 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-5.5 parent: 4812 - uid: 9520 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-3.5 parent: 4812 - uid: 9521 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-5.5 parent: 4812 - uid: 9522 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-3.5 parent: 4812 - uid: 9579 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-32.5 parent: 4812 - uid: 9590 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-25.5 parent: 4812 - uid: 9591 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-24.5 parent: 4812 - uid: 9592 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -48.5,-24.5 parent: 4812 - uid: 9594 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-24.5 parent: 4812 - uid: 9595 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-25.5 parent: 4812 - uid: 9596 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-26.5 parent: 4812 - uid: 9597 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-26.5 parent: 4812 - uid: 9598 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-26.5 parent: 4812 - uid: 9604 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,-24.5 parent: 4812 - uid: 9605 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-24.5 parent: 4812 - uid: 9606 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-24.5 parent: 4812 - uid: 9607 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -56.5,-21.5 parent: 4812 - uid: 9644 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-56.5 parent: 4812 - uid: 9645 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-56.5 parent: 4812 - uid: 9824 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-16.5 parent: 4812 - uid: 9825 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-17.5 parent: 4812 - uid: 9826 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-18.5 parent: 4812 - uid: 9827 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-19.5 parent: 4812 - uid: 9828 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-19.5 parent: 4812 - uid: 9829 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-15.5 parent: 4812 - uid: 9831 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -45.5,-15.5 parent: 4812 - uid: 9836 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-10.5 parent: 4812 - uid: 9837 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-5.5 parent: 4812 - uid: 9840 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-14.5 parent: 4812 - uid: 9841 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-15.5 parent: 4812 - uid: 9842 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-5.5 parent: 4812 - uid: 9844 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-6.5 parent: 4812 - uid: 9845 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-7.5 parent: 4812 - uid: 9846 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-14.5 parent: 4812 - uid: 9847 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-13.5 parent: 4812 - uid: 9848 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-12.5 parent: 4812 - uid: 9849 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-11.5 parent: 4812 - uid: 9850 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-10.5 parent: 4812 - uid: 9851 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-9.5 parent: 4812 - uid: 9852 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-8.5 parent: 4812 - uid: 9853 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,-14.5 parent: 4812 - uid: 9854 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,-14.5 parent: 4812 - uid: 9855 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,-14.5 parent: 4812 - uid: 9856 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-14.5 parent: 4812 - uid: 9857 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-14.5 parent: 4812 - uid: 9858 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-14.5 parent: 4812 - uid: 9859 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -48.5,-14.5 parent: 4812 - uid: 9860 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-14.5 parent: 4812 - uid: 9864 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-10.5 parent: 4812 - uid: 9865 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-9.5 parent: 4812 - uid: 9866 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-8.5 parent: 4812 - uid: 9867 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-7.5 parent: 4812 - uid: 9868 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-6.5 parent: 4812 - uid: 9873 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-9.5 parent: 4812 - uid: 9876 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-10.5 parent: 4812 - uid: 9890 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-9.5 parent: 4812 - uid: 9891 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-10.5 parent: 4812 - uid: 9895 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-14.5 parent: 4812 - uid: 9896 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-9.5 parent: 4812 - uid: 9900 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-9.5 parent: 4812 - uid: 9901 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-19.5 parent: 4812 - uid: 9902 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -40.5,-19.5 parent: 4812 - uid: 9903 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-19.5 parent: 4812 - uid: 9904 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-19.5 parent: 4812 - uid: 9905 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-19.5 parent: 4812 - uid: 9906 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-18.5 parent: 4812 - uid: 9907 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-17.5 parent: 4812 - uid: 9908 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-16.5 parent: 4812 - uid: 9939 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -55.5,-21.5 parent: 4812 - uid: 9940 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,-21.5 parent: 4812 - uid: 9941 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -53.5,-21.5 parent: 4812 - uid: 9942 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -52.5,-21.5 parent: 4812 - uid: 9943 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -51.5,-21.5 parent: 4812 - uid: 9944 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-21.5 parent: 4812 - uid: 9945 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-21.5 parent: 4812 - uid: 9946 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -48.5,-21.5 parent: 4812 - uid: 9947 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-21.5 parent: 4812 - uid: 9948 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-21.5 parent: 4812 - uid: 9949 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-16.5 parent: 4812 - uid: 9950 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-17.5 parent: 4812 - uid: 9951 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-18.5 parent: 4812 - uid: 9952 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-18.5 parent: 4812 - uid: 9953 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -48.5,-18.5 parent: 4812 - uid: 9954 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-18.5 parent: 4812 - uid: 9955 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-19.5 parent: 4812 - uid: 9956 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-20.5 parent: 4812 - uid: 9957 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -46.5,-19.5 parent: 4812 - uid: 10635 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-10.5 parent: 4812 - uid: 10964 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-37.5 parent: 4812 - uid: 10989 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-41.5 parent: 4812 - uid: 11006 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-40.5 parent: 4812 - uid: 11007 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-39.5 parent: 4812 - uid: 11008 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-38.5 parent: 4812 - uid: 11030 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-42.5 parent: 4812 - uid: 11031 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-43.5 parent: 4812 - uid: 11032 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,-43.5 parent: 4812 - uid: 11033 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,-43.5 parent: 4812 - uid: 11034 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-43.5 parent: 4812 - uid: 11035 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-44.5 parent: 4812 - uid: 11036 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-45.5 parent: 4812 - uid: 11037 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-45.5 parent: 4812 - uid: 11038 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-45.5 parent: 4812 - uid: 11039 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,-45.5 parent: 4812 - uid: 11040 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-45.5 parent: 4812 - uid: 11041 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-45.5 parent: 4812 - uid: 11042 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,-43.5 parent: 4812 - uid: 11043 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-44.5 parent: 4812 - uid: 11044 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-43.5 parent: 4812 - uid: 11045 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-43.5 parent: 4812 - uid: 11046 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-43.5 parent: 4812 - uid: 11110 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-43.5 parent: 4812 - uid: 11111 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-42.5 parent: 4812 - uid: 11112 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-41.5 parent: 4812 - uid: 11113 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-40.5 parent: 4812 - uid: 11114 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-39.5 parent: 4812 - uid: 11115 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-38.5 parent: 4812 - uid: 11116 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-37.5 parent: 4812 - uid: 11117 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-36.5 parent: 4812 - uid: 11118 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-35.5 parent: 4812 - uid: 11119 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 34.5,-30.5 parent: 4812 - uid: 11120 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 34.5,-31.5 parent: 4812 - uid: 11125 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-39.5 parent: 4812 - uid: 11128 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-39.5 parent: 4812 - uid: 11131 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-39.5 parent: 4812 - uid: 11132 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-37.5 parent: 4812 - uid: 11133 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-37.5 parent: 4812 - uid: 11134 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-38.5 parent: 4812 - uid: 11135 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 27.5,-35.5 parent: 4812 - uid: 11136 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-38.5 parent: 4812 - uid: 11137 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-38.5 parent: 4812 - uid: 11138 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-37.5 parent: 4812 - uid: 11140 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 38.5,-37.5 parent: 4812 - uid: 11142 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-39.5 parent: 4812 - uid: 11263 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-37.5 parent: 4812 - uid: 11264 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 27.5,-37.5 parent: 4812 - uid: 11265 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 27.5,-36.5 parent: 4812 - uid: 11340 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 38.5,-35.5 parent: 4812 - uid: 11341 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 38.5,-34.5 parent: 4812 - uid: 11342 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 38.5,-33.5 parent: 4812 - uid: 11344 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-31.5 parent: 4812 - uid: 11345 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-31.5 parent: 4812 - uid: 11371 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-39.5 parent: 4812 - uid: 11372 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-40.5 parent: 4812 - uid: 11373 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 34.5,-40.5 parent: 4812 - uid: 11374 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 33.5,-40.5 parent: 4812 - uid: 11375 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 32.5,-40.5 parent: 4812 - uid: 11376 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 31.5,-40.5 parent: 4812 - uid: 11377 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-40.5 parent: 4812 - uid: 11378 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-40.5 parent: 4812 - uid: 11379 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-39.5 parent: 4812 - uid: 11380 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-38.5 parent: 4812 - uid: 11381 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-37.5 parent: 4812 - uid: 11382 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 28.5,-37.5 parent: 4812 @@ -81092,4217 +76463,3013 @@ entities: entities: - uid: 1 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,-9.5 parent: 4812 - uid: 2 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,-10.5 parent: 4812 - uid: 3 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,-4.5 parent: 4812 - uid: 4 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,-3.5 parent: 4812 - uid: 5 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,0.5 parent: 4812 - uid: 6 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,1.5 parent: 4812 - uid: 7 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,1.5 parent: 4812 - uid: 8 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,3.5 parent: 4812 - uid: 9 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,9.5 parent: 4812 - uid: 10 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,8.5 parent: 4812 - uid: 11 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,4.5 parent: 4812 - uid: 13 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,5.5 parent: 4812 - uid: 14 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,5.5 parent: 4812 - uid: 15 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,5.5 parent: 4812 - uid: 16 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,5.5 parent: 4812 - uid: 17 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,5.5 parent: 4812 - uid: 18 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,6.5 parent: 4812 - uid: 19 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,8.5 parent: 4812 - uid: 20 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,9.5 parent: 4812 - uid: 21 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,10.5 parent: 4812 - uid: 22 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,10.5 parent: 4812 - uid: 23 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,10.5 parent: 4812 - uid: 24 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,10.5 parent: 4812 - uid: 25 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,10.5 parent: 4812 - uid: 26 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,10.5 parent: 4812 - uid: 27 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,10.5 parent: 4812 - uid: 28 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,10.5 parent: 4812 - uid: 29 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,11.5 parent: 4812 - uid: 30 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,12.5 parent: 4812 - uid: 31 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -5.5,12.5 parent: 4812 - uid: 33 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -5.5,15.5 parent: 4812 - uid: 34 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -5.5,14.5 parent: 4812 - uid: 36 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,15.5 parent: 4812 - uid: 37 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,13.5 parent: 4812 - uid: 38 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 0.5,12.5 parent: 4812 - uid: 39 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,12.5 parent: 4812 - uid: 40 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,11.5 parent: 4812 - uid: 41 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,10.5 parent: 4812 - uid: 42 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,-9.5 parent: 4812 - uid: 43 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-9.5 parent: 4812 - uid: 44 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-10.5 parent: 4812 - uid: 45 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,-7.5 parent: 4812 - uid: 46 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,-6.5 parent: 4812 - uid: 47 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,-5.5 parent: 4812 - uid: 48 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -5.5,-10.5 parent: 4812 - uid: 49 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -5.5,-9.5 parent: 4812 - uid: 50 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -5.5,-8.5 parent: 4812 - uid: 51 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,-8.5 parent: 4812 - uid: 52 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,-4.5 parent: 4812 - uid: 53 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,-2.5 parent: 4812 - uid: 54 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,-1.5 parent: 4812 - uid: 55 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,0.5 parent: 4812 - uid: 56 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,1.5 parent: 4812 - uid: 57 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -6.5,2.5 parent: 4812 - uid: 58 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,2.5 parent: 4812 - uid: 59 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,10.5 parent: 4812 - uid: 60 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,10.5 parent: 4812 - uid: 61 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,10.5 parent: 4812 - uid: 62 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,10.5 parent: 4812 - uid: 63 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,9.5 parent: 4812 - uid: 64 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,8.5 parent: 4812 - uid: 65 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,7.5 parent: 4812 - uid: 66 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,6.5 parent: 4812 - uid: 67 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,5.5 parent: 4812 - uid: 68 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,4.5 parent: 4812 - uid: 69 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,3.5 parent: 4812 - uid: 70 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,2.5 parent: 4812 - uid: 71 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,1.5 parent: 4812 - uid: 72 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,0.5 parent: 4812 - uid: 73 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-0.5 parent: 4812 - uid: 74 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-1.5 parent: 4812 - uid: 75 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-2.5 parent: 4812 - uid: 76 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-2.5 parent: 4812 - uid: 77 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -8.5,-2.5 parent: 4812 - uid: 78 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-2.5 parent: 4812 - uid: 79 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,-2.5 parent: 4812 - uid: 80 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,2.5 parent: 4812 - uid: 81 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -10.5,2.5 parent: 4812 - uid: 82 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-0.5 parent: 4812 - uid: 83 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,0.5 parent: 4812 - uid: 84 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,1.5 parent: 4812 - uid: 85 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,1.5 parent: 4812 - uid: 86 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,1.5 parent: 4812 - uid: 87 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,1.5 parent: 4812 - uid: 88 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-2.5 parent: 4812 - uid: 89 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-3.5 parent: 4812 - uid: 90 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-4.5 parent: 4812 - uid: 91 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-4.5 parent: 4812 - uid: 92 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-4.5 parent: 4812 - uid: 93 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,-4.5 parent: 4812 - uid: 94 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-4.5 parent: 4812 - uid: 95 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-4.5 parent: 4812 - uid: 96 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-7.5 parent: 4812 - uid: 97 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-7.5 parent: 4812 - uid: 98 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,-7.5 parent: 4812 - uid: 99 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,-8.5 parent: 4812 - uid: 100 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,-8.5 parent: 4812 - uid: 101 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-8.5 parent: 4812 - uid: 102 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-8.5 parent: 4812 - uid: 103 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-8.5 parent: 4812 - uid: 104 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-7.5 parent: 4812 - uid: 105 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-5.5 parent: 4812 - uid: 139 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-10.5 parent: 4812 - uid: 140 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,-10.5 parent: 4812 - uid: 141 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,-10.5 parent: 4812 - uid: 142 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-10.5 parent: 4812 - uid: 143 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-10.5 parent: 4812 - uid: 145 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,-10.5 parent: 4812 - uid: 146 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-10.5 parent: 4812 - uid: 147 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-9.5 parent: 4812 - uid: 148 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-8.5 parent: 4812 - uid: 149 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-7.5 parent: 4812 - uid: 150 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-6.5 parent: 4812 - uid: 151 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-5.5 parent: 4812 - uid: 152 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-4.5 parent: 4812 - uid: 153 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-3.5 parent: 4812 - uid: 154 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-2.5 parent: 4812 - uid: 155 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-1.5 parent: 4812 - uid: 156 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-0.5 parent: 4812 - uid: 157 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,0.5 parent: 4812 - uid: 158 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,1.5 parent: 4812 - uid: 159 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,2.5 parent: 4812 - uid: 161 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,4.5 parent: 4812 - uid: 162 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,5.5 parent: 4812 - uid: 163 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,6.5 parent: 4812 - uid: 164 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,7.5 parent: 4812 - uid: 165 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,8.5 parent: 4812 - uid: 166 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,9.5 parent: 4812 - uid: 167 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,10.5 parent: 4812 - uid: 168 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,11.5 parent: 4812 - uid: 169 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,12.5 parent: 4812 - uid: 177 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,13.5 parent: 4812 - uid: 180 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,15.5 parent: 4812 - uid: 181 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,18.5 parent: 4812 - uid: 182 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,17.5 parent: 4812 - uid: 183 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,18.5 parent: 4812 - uid: 185 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,18.5 parent: 4812 - uid: 186 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,12.5 parent: 4812 - uid: 189 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,7.5 parent: 4812 - uid: 190 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,6.5 parent: 4812 - uid: 191 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,6.5 parent: 4812 - uid: 192 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,6.5 parent: 4812 - uid: 193 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,6.5 parent: 4812 - uid: 194 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,5.5 parent: 4812 - uid: 195 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-1.5 parent: 4812 - uid: 196 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-2.5 parent: 4812 - uid: 197 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-2.5 parent: 4812 - uid: 198 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-3.5 parent: 4812 - uid: 199 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-5.5 parent: 4812 - uid: 200 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-6.5 parent: 4812 - uid: 201 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-5.5 parent: 4812 - uid: 202 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-7.5 parent: 4812 - uid: 203 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-8.5 parent: 4812 - uid: 204 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-9.5 parent: 4812 - uid: 205 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-10.5 parent: 4812 - uid: 206 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-10.5 parent: 4812 - uid: 207 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-10.5 parent: 4812 - uid: 208 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-10.5 parent: 4812 - uid: 209 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-10.5 parent: 4812 - uid: 210 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-10.5 parent: 4812 - uid: 211 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-10.5 parent: 4812 - uid: 212 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-10.5 parent: 4812 - uid: 213 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-10.5 parent: 4812 - uid: 214 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,-10.5 parent: 4812 - uid: 225 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-9.5 parent: 4812 - uid: 226 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-8.5 parent: 4812 - uid: 227 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-7.5 parent: 4812 - uid: 228 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-6.5 parent: 4812 - uid: 229 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-5.5 parent: 4812 - uid: 230 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-5.5 parent: 4812 - uid: 231 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-5.5 parent: 4812 - uid: 232 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-4.5 parent: 4812 - uid: 233 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -8.5,-4.5 parent: 4812 - uid: 234 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -9.5,-4.5 parent: 4812 - uid: 235 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-4.5 parent: 4812 - uid: 236 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-4.5 parent: 4812 - uid: 237 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-4.5 parent: 4812 - uid: 238 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,-4.5 parent: 4812 - uid: 239 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,-4.5 parent: 4812 - uid: 240 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,-4.5 parent: 4812 - uid: 241 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-4.5 parent: 4812 - uid: 242 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-4.5 parent: 4812 - uid: 243 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-4.5 parent: 4812 - uid: 244 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-2.5 parent: 4812 - uid: 245 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-2.5 parent: 4812 - uid: 246 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-2.5 parent: 4812 - uid: 247 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-2.5 parent: 4812 - uid: 248 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-2.5 parent: 4812 - uid: 249 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-2.5 parent: 4812 - uid: 250 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,-2.5 parent: 4812 - uid: 262 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,6.5 parent: 4812 - uid: 263 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,6.5 parent: 4812 - uid: 264 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,6.5 parent: 4812 - uid: 265 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,6.5 parent: 4812 - uid: 266 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,6.5 parent: 4812 - uid: 267 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,6.5 parent: 4812 - uid: 268 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,6.5 parent: 4812 - uid: 269 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,6.5 parent: 4812 - uid: 270 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,3.5 parent: 4812 - uid: 271 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,4.5 parent: 4812 - uid: 272 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,5.5 parent: 4812 - uid: 273 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,3.5 parent: 4812 - uid: 274 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,3.5 parent: 4812 - uid: 275 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,4.5 parent: 4812 - uid: 276 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,5.5 parent: 4812 - uid: 279 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,-1.5 parent: 4812 - uid: 280 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,-0.5 parent: 4812 - uid: 281 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,0.5 parent: 4812 - uid: 282 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,0.5 parent: 4812 - uid: 283 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-1.5 parent: 4812 - uid: 284 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-0.5 parent: 4812 - uid: 285 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,0.5 parent: 4812 - uid: 286 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,7.5 parent: 4812 - uid: 287 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,8.5 parent: 4812 - uid: 288 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,9.5 parent: 4812 - uid: 289 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,11.5 parent: 4812 - uid: 290 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,12.5 parent: 4812 - uid: 291 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,13.5 parent: 4812 - uid: 292 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,13.5 parent: 4812 - uid: 293 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,13.5 parent: 4812 - uid: 294 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,13.5 parent: 4812 - uid: 295 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,13.5 parent: 4812 - uid: 296 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,13.5 parent: 4812 - uid: 318 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 1.5,4.5 parent: 4812 - uid: 429 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,9.5 parent: 4812 - uid: 898 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,7.5 parent: 4812 - uid: 899 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,8.5 parent: 4812 - uid: 900 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,9.5 parent: 4812 - uid: 1253 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,1.5 parent: 4812 - uid: 1917 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,29.5 parent: 4812 - uid: 1918 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 9.5,27.5 parent: 4812 - uid: 1919 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,27.5 parent: 4812 - uid: 1978 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,26.5 parent: 4812 - uid: 1979 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,26.5 parent: 4812 - uid: 1980 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,25.5 parent: 4812 - uid: 1981 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,27.5 parent: 4812 - uid: 1982 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,29.5 parent: 4812 - uid: 2738 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,21.5 parent: 4812 - uid: 2739 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,22.5 parent: 4812 - uid: 2740 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,23.5 parent: 4812 - uid: 2742 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,25.5 parent: 4812 - uid: 2743 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,26.5 parent: 4812 - uid: 2744 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,27.5 parent: 4812 - uid: 2745 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,28.5 parent: 4812 - uid: 2788 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,16.5 parent: 4812 - uid: 2789 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,18.5 parent: 4812 - uid: 2790 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,16.5 parent: 4812 - uid: 2791 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,16.5 parent: 4812 - uid: 2792 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,12.5 parent: 4812 - uid: 2793 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,12.5 parent: 4812 - uid: 2794 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,12.5 parent: 4812 - uid: 2795 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,12.5 parent: 4812 - uid: 2796 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,12.5 parent: 4812 - uid: 2797 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,12.5 parent: 4812 - uid: 2798 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,12.5 parent: 4812 - uid: 2799 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,12.5 parent: 4812 - uid: 2800 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,12.5 parent: 4812 - uid: 2851 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,22.5 parent: 4812 - uid: 3348 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,11.5 parent: 4812 - uid: 3351 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,11.5 parent: 4812 - uid: 3354 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,9.5 parent: 4812 - uid: 3435 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,33.5 parent: 4812 - uid: 3462 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,34.5 parent: 4812 - uid: 3463 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,33.5 parent: 4812 - uid: 3474 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,44.5 parent: 4812 - uid: 3475 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,41.5 parent: 4812 - uid: 3476 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,40.5 parent: 4812 - uid: 3980 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,37.5 parent: 4812 - uid: 3981 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,33.5 parent: 4812 - uid: 3983 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,8.5 parent: 4812 - uid: 3984 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,6.5 parent: 4812 - uid: 3985 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,5.5 parent: 4812 - uid: 3986 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,9.5 parent: 4812 - uid: 3987 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,9.5 parent: 4812 - uid: 3988 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,9.5 parent: 4812 - uid: 3989 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,9.5 parent: 4812 - uid: 4006 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,5.5 parent: 4812 - uid: 4013 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -44.5,-31.5 parent: 4812 - uid: 4014 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -43.5,-31.5 parent: 4812 - uid: 4015 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,3.5 parent: 4812 - uid: 4016 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,1.5 parent: 4812 - uid: 4017 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,1.5 parent: 4812 - uid: 4018 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,0.5 parent: 4812 - uid: 4019 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,0.5 parent: 4812 - uid: 4020 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,0.5 parent: 4812 - uid: 4021 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,0.5 parent: 4812 - uid: 4022 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,1.5 parent: 4812 - uid: 4023 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,2.5 parent: 4812 - uid: 4024 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,3.5 parent: 4812 - uid: 4025 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,4.5 parent: 4812 - uid: 4026 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,5.5 parent: 4812 - uid: 4027 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,5.5 parent: 4812 - uid: 4028 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,5.5 parent: 4812 - uid: 4029 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,5.5 parent: 4812 - uid: 4030 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,6.5 parent: 4812 - uid: 4031 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,7.5 parent: 4812 - uid: 4032 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,8.5 parent: 4812 - uid: 4033 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,2.5 parent: 4812 - uid: 4034 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,1.5 parent: 4812 - uid: 4035 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,0.5 parent: 4812 - uid: 4036 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-0.5 parent: 4812 - uid: 4037 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-1.5 parent: 4812 - uid: 4038 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-0.5 parent: 4812 - uid: 4039 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-1.5 parent: 4812 - uid: 4040 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-1.5 parent: 4812 - uid: 4041 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-1.5 parent: 4812 - uid: 4042 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-1.5 parent: 4812 - uid: 4043 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 19.5,-1.5 parent: 4812 - uid: 4044 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,-1.5 parent: 4812 - uid: 4045 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-1.5 parent: 4812 - uid: 4046 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-1.5 parent: 4812 - uid: 4047 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-1.5 parent: 4812 - uid: 4049 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,0.5 parent: 4812 - uid: 4118 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-6.5 parent: 4812 - uid: 4119 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-5.5 parent: 4812 - uid: 4120 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,0.5 parent: 4812 - uid: 4121 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,1.5 parent: 4812 - uid: 4123 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-4.5 parent: 4812 - uid: 4124 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-4.5 parent: 4812 - uid: 4125 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-5.5 parent: 4812 - uid: 4126 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-5.5 parent: 4812 - uid: 4127 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-6.5 parent: 4812 - uid: 4128 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-5.5 parent: 4812 - uid: 4129 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-5.5 parent: 4812 - uid: 4130 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-4.5 parent: 4812 - uid: 4131 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 15.5,-4.5 parent: 4812 - uid: 4132 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-6.5 parent: 4812 - uid: 4133 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-10.5 parent: 4812 - uid: 4649 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,-14.5 parent: 4812 - uid: 4650 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-14.5 parent: 4812 - uid: 4651 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-20.5 parent: 4812 - uid: 4652 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -1.5,-20.5 parent: 4812 - uid: 4653 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -2.5,-20.5 parent: 4812 - uid: 4654 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -3.5,-20.5 parent: 4812 - uid: 4655 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,-20.5 parent: 4812 - uid: 4717 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -45.5,-26.5 parent: 4812 - uid: 4756 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -45.5,-28.5 parent: 4812 - uid: 4757 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -45.5,-29.5 parent: 4812 - uid: 4783 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-35.5 parent: 4812 - uid: 4784 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-34.5 parent: 4812 - uid: 4785 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-33.5 parent: 4812 - uid: 4786 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 2.5,-33.5 parent: 4812 - uid: 4787 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -0.5,-33.5 parent: 4812 - uid: 4788 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -4.5,-33.5 parent: 4812 - uid: 4793 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-33.5 parent: 4812 - uid: 4794 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-33.5 parent: 4812 - uid: 4795 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-36.5 parent: 4812 - uid: 4797 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -44.5,-29.5 parent: 4812 - uid: 4814 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-28.5 parent: 4812 - uid: 4916 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-24.5 parent: 4812 - uid: 4918 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-25.5 parent: 4812 - uid: 4919 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-26.5 parent: 4812 - uid: 4922 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-28.5 parent: 4812 - uid: 4937 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-29.5 parent: 4812 - uid: 5024 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-19.5 parent: 4812 - uid: 5050 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-25.5 parent: 4812 - uid: 5333 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-13.5 parent: 4812 - uid: 5334 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-14.5 parent: 4812 - uid: 5335 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,-13.5 parent: 4812 - uid: 5338 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,-19.5 parent: 4812 - uid: 5339 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-19.5 parent: 4812 - uid: 5340 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-18.5 parent: 4812 - uid: 5341 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-16.5 parent: 4812 - uid: 5342 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-15.5 parent: 4812 - uid: 5390 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-13.5 parent: 4812 - uid: 5425 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-19.5 parent: 4812 - uid: 5426 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-19.5 parent: 4812 - uid: 5427 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-19.5 parent: 4812 - uid: 5430 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-14.5 parent: 4812 - uid: 5457 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-27.5 parent: 4812 - uid: 5465 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-22.5 parent: 4812 - uid: 5466 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-22.5 parent: 4812 - uid: 5467 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-22.5 parent: 4812 - uid: 5468 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-27.5 parent: 4812 - uid: 5562 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-23.5 parent: 4812 - uid: 5563 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-27.5 parent: 4812 - uid: 5567 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-28.5 parent: 4812 - uid: 5619 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-16.5 parent: 4812 - uid: 5620 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 8.5,-18.5 parent: 4812 - uid: 5836 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-32.5 parent: 4812 - uid: 5837 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 3.5,-31.5 parent: 4812 - uid: 6498 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-12.5 parent: 4812 - uid: 6501 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-13.5 parent: 4812 - uid: 6502 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-13.5 parent: 4812 - uid: 6503 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-13.5 parent: 4812 - uid: 6504 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-13.5 parent: 4812 - uid: 6505 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-14.5 parent: 4812 - uid: 6506 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-15.5 parent: 4812 - uid: 6507 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-16.5 parent: 4812 - uid: 6508 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-17.5 parent: 4812 - uid: 6509 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-18.5 parent: 4812 - uid: 6510 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-19.5 parent: 4812 - uid: 6511 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-20.5 parent: 4812 - uid: 6512 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-21.5 parent: 4812 - uid: 6513 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-21.5 parent: 4812 - uid: 6514 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-21.5 parent: 4812 - uid: 6515 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-21.5 parent: 4812 - uid: 6516 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-21.5 parent: 4812 - uid: 6517 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,-21.5 parent: 4812 - uid: 6518 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-21.5 parent: 4812 - uid: 6520 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-21.5 parent: 4812 - uid: 6521 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-20.5 parent: 4812 - uid: 6522 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,-20.5 parent: 4812 - uid: 6523 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-22.5 parent: 4812 - uid: 6524 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-23.5 parent: 4812 - uid: 6525 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-24.5 parent: 4812 - uid: 6526 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,-24.5 parent: 4812 - uid: 6527 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,-24.5 parent: 4812 - uid: 6528 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-24.5 parent: 4812 - uid: 6529 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-24.5 parent: 4812 - uid: 6530 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-23.5 parent: 4812 - uid: 6531 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-22.5 parent: 4812 - uid: 6532 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-21.5 parent: 4812 - uid: 6533 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-20.5 parent: 4812 - uid: 6534 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-19.5 parent: 4812 - uid: 6535 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-18.5 parent: 4812 - uid: 6536 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-17.5 parent: 4812 - uid: 6537 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-16.5 parent: 4812 - uid: 6538 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-15.5 parent: 4812 - uid: 6539 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-14.5 parent: 4812 - uid: 6540 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-13.5 parent: 4812 - uid: 6541 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-13.5 parent: 4812 - uid: 6542 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,-13.5 parent: 4812 - uid: 6543 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -30.5,-13.5 parent: 4812 - uid: 6544 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-13.5 parent: 4812 - uid: 6545 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-20.5 parent: 4812 - uid: 6549 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-13.5 parent: 4812 - uid: 6654 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-24.5 parent: 4812 - uid: 6683 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-24.5 parent: 4812 - uid: 6711 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-37.5 parent: 4812 - uid: 6712 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-37.5 parent: 4812 - uid: 6713 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-38.5 parent: 4812 - uid: 6714 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-39.5 parent: 4812 - uid: 6715 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-38.5 parent: 4812 - uid: 6716 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,-38.5 parent: 4812 - uid: 6717 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-38.5 parent: 4812 - uid: 6718 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-38.5 parent: 4812 - uid: 6719 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-37.5 parent: 4812 - uid: 6720 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-36.5 parent: 4812 - uid: 6721 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-35.5 parent: 4812 - uid: 6722 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-34.5 parent: 4812 - uid: 6723 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-35.5 parent: 4812 - uid: 6724 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-34.5 parent: 4812 - uid: 6725 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-33.5 parent: 4812 - uid: 6726 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-32.5 parent: 4812 - uid: 6727 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-34.5 parent: 4812 - uid: 6728 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-34.5 parent: 4812 - uid: 6729 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -24.5,-33.5 parent: 4812 - uid: 6730 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-34.5 parent: 4812 - uid: 6731 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-34.5 parent: 4812 - uid: 6732 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-33.5 parent: 4812 - uid: 6733 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-32.5 parent: 4812 - uid: 6734 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-31.5 parent: 4812 - uid: 6739 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-32.5 parent: 4812 - uid: 6740 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-33.5 parent: 4812 - uid: 6741 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-33.5 parent: 4812 - uid: 6742 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-32.5 parent: 4812 - uid: 6743 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-31.5 parent: 4812 - uid: 6744 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-31.5 parent: 4812 - uid: 6745 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-31.5 parent: 4812 - uid: 6746 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-31.5 parent: 4812 - uid: 6747 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-32.5 parent: 4812 - uid: 6748 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-31.5 parent: 4812 - uid: 6749 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-31.5 parent: 4812 - uid: 6750 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,-31.5 parent: 4812 - uid: 6751 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,-31.5 parent: 4812 - uid: 6752 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,-31.5 parent: 4812 - uid: 6753 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-31.5 parent: 4812 - uid: 6766 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -12.5,-29.5 parent: 4812 - uid: 6767 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-29.5 parent: 4812 - uid: 6768 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,-29.5 parent: 4812 - uid: 6769 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,-29.5 parent: 4812 - uid: 6770 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,-29.5 parent: 4812 - uid: 6771 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-29.5 parent: 4812 - uid: 6772 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-29.5 parent: 4812 - uid: 6773 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-29.5 parent: 4812 - uid: 6774 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-29.5 parent: 4812 - uid: 6775 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-29.5 parent: 4812 - uid: 6776 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -22.5,-29.5 parent: 4812 - uid: 6789 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,-22.5 parent: 4812 - uid: 7060 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,-19.5 parent: 4812 - uid: 7063 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-15.5 parent: 4812 - uid: 7184 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-23.5 parent: 4812 - uid: 7185 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-24.5 parent: 4812 - uid: 7187 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,-31.5 parent: 4812 - uid: 7194 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -23.5,-27.5 parent: 4812 - uid: 7195 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -7.5,-28.5 parent: 4812 - uid: 7212 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-24.5 parent: 4812 - uid: 7213 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-24.5 parent: 4812 - uid: 7215 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-25.5 parent: 4812 - uid: 7217 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-27.5 parent: 4812 - uid: 7218 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -11.5,-28.5 parent: 4812 - uid: 7235 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -18.5,-19.5 parent: 4812 - uid: 7246 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-18.5 parent: 4812 - uid: 7314 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-25.5 parent: 4812 - uid: 7315 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -44.5,-25.5 parent: 4812 - uid: 7317 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-29.5 parent: 4812 - uid: 7318 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-31.5 parent: 4812 - uid: 7319 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -17.5,-14.5 parent: 4812 - uid: 7320 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -16.5,-14.5 parent: 4812 - uid: 7321 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,-14.5 parent: 4812 - uid: 7322 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,-14.5 parent: 4812 - uid: 7727 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,18.5 parent: 4812 - uid: 7728 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,18.5 parent: 4812 - uid: 7729 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -27.5,18.5 parent: 4812 - uid: 7953 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,7.5 parent: 4812 - uid: 8162 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,26.5 parent: 4812 - uid: 8163 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,26.5 parent: 4812 - uid: 8164 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -20.5,28.5 parent: 4812 - uid: 8456 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,37.5 parent: 4812 - uid: 8457 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -26.5,37.5 parent: 4812 - uid: 8833 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-37.5 parent: 4812 - uid: 8834 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,-34.5 parent: 4812 - uid: 8835 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-34.5 parent: 4812 - uid: 8837 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-32.5 parent: 4812 - uid: 8840 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-40.5 parent: 4812 - uid: 8852 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,-28.5 parent: 4812 - uid: 8854 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,-26.5 parent: 4812 - uid: 8855 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-26.5 parent: 4812 - uid: 8856 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-26.5 parent: 4812 - uid: 8857 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-26.5 parent: 4812 - uid: 8903 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-26.5 parent: 4812 - uid: 8904 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-27.5 parent: 4812 - uid: 8905 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-25.5 parent: 4812 - uid: 8907 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-23.5 parent: 4812 - uid: 9968 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-29.5 parent: 4812 - uid: 9969 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-30.5 parent: 4812 - uid: 9970 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-30.5 parent: 4812 - uid: 9973 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-30.5 parent: 4812 - uid: 9985 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-22.5 parent: 4812 - uid: 9986 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-21.5 parent: 4812 - uid: 9987 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-25.5 parent: 4812 - uid: 9988 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-24.5 parent: 4812 - uid: 9989 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-23.5 parent: 4812 - uid: 9994 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -38.5,-21.5 parent: 4812 - uid: 10153 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -54.5,-23.5 parent: 4812 - uid: 10155 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -49.5,-23.5 parent: 4812 - uid: 10156 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -50.5,-23.5 parent: 4812 - uid: 10224 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-22.5 parent: 4812 - uid: 10404 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -47.5,-28.5 parent: 4812 - uid: 10613 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-24.5 parent: 4812 - uid: 10614 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-23.5 parent: 4812 - uid: 11049 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,-39.5 parent: 4812 - uid: 11050 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,-39.5 parent: 4812 - uid: 11051 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-39.5 parent: 4812 - uid: 11052 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-39.5 parent: 4812 - uid: 11053 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-35.5 parent: 4812 - uid: 11054 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-35.5 parent: 4812 - uid: 11055 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,-35.5 parent: 4812 - uid: 11056 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,-35.5 parent: 4812 - uid: 11057 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 7.5,-31.5 parent: 4812 - uid: 11058 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 6.5,-31.5 parent: 4812 - uid: 11059 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 5.5,-31.5 parent: 4812 - uid: 11060 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 4.5,-31.5 parent: 4812 - uid: 11061 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-35.5 parent: 4812 - uid: 11062 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-35.5 parent: 4812 - uid: 11063 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,-35.5 parent: 4812 - uid: 11064 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-35.5 parent: 4812 - uid: 11066 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 10.5,-39.5 parent: 4812 - uid: 11067 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 11.5,-39.5 parent: 4812 - uid: 11068 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 12.5,-39.5 parent: 4812 - uid: 11069 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 13.5,-39.5 parent: 4812 - uid: 11070 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 14.5,-39.5 parent: 4812 - uid: 11121 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-35.5 parent: 4812 - uid: 11122 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 36.5,-35.5 parent: 4812 - uid: 11123 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-35.5 parent: 4812 - uid: 11124 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 34.5,-35.5 parent: 4812 - uid: 11266 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-36.5 parent: 4812 - uid: 11267 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-35.5 parent: 4812 - uid: 11268 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 22.5,-35.5 parent: 4812 - uid: 11269 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 21.5,-35.5 parent: 4812 - uid: 11270 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 20.5,-35.5 parent: 4812 - uid: 11271 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 18.5,-35.5 parent: 4812 - uid: 11272 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 17.5,-35.5 parent: 4812 - uid: 11273 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 16.5,-35.5 parent: 4812 - uid: 11302 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 27.5,-34.5 parent: 4812 - uid: 11303 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 26.5,-34.5 parent: 4812 - uid: 11304 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 23.5,-34.5 parent: 4812 - uid: 11305 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 24.5,-34.5 parent: 4812 - uid: 11348 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 34.5,-33.5 parent: 4812 - uid: 11349 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 34.5,-32.5 parent: 4812 - uid: 11366 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-38.5 parent: 4812 - uid: 11367 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 35.5,-37.5 parent: 4812 - uid: 11368 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 33.5,-35.5 parent: 4812 - uid: 11369 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 32.5,-35.5 parent: 4812 - uid: 11370 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-36.5 parent: 4812 - uid: 11383 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 29.5,-35.5 parent: 4812 - uid: 11384 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-35.5 parent: 4812 - uid: 11385 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 34.5,-37.5 parent: 4812 - uid: 11389 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 30.5,-37.5 parent: 4812 - uid: 11475 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 5.5,3.5 parent: 4812 - uid: 11646 components: - - type: MetaData - flags: PvsPriority - type: Transform rot: -1.5707963267948966 rad pos: 6.5,3.5 parent: 4812 - uid: 11706 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: 37.5,-42.5 parent: 4812 - uid: 11794 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -15.5,1.5 parent: 4812 - uid: 11795 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,1.5 parent: 4812 - uid: 11796 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,1.5 parent: 4812 - uid: 11797 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,0.5 parent: 4812 - uid: 11798 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-0.5 parent: 4812 - uid: 11799 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-1.5 parent: 4812 - uid: 11800 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,-2.5 parent: 4812 - uid: 11801 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -14.5,-2.5 parent: 4812 - uid: 11848 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -13.5,12.5 parent: 4812 @@ -85310,148 +79477,106 @@ entities: entities: - uid: 7302 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-29.5 parent: 4812 - uid: 7305 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -41.5,-26.5 parent: 4812 - uid: 7312 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-25.5 parent: 4812 - uid: 7313 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -42.5,-30.5 parent: 4812 - uid: 7316 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -45.5,-25.5 parent: 4812 - uid: 7335 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -25.5,-31.5 parent: 4812 - uid: 7350 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -29.5,-33.5 parent: 4812 - uid: 7351 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -28.5,-32.5 parent: 4812 - uid: 7353 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -33.5,-30.5 parent: 4812 - uid: 7354 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -32.5,-30.5 parent: 4812 - uid: 7355 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,-29.5 parent: 4812 - uid: 7356 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -31.5,-27.5 parent: 4812 - uid: 7357 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-30.5 parent: 4812 - uid: 7358 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-28.5 parent: 4812 - uid: 7359 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -39.5,-24.5 parent: 4812 - uid: 7360 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-30.5 parent: 4812 - uid: 7361 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,-30.5 parent: 4812 - uid: 7362 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -35.5,-22.5 parent: 4812 - uid: 7363 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -36.5,-21.5 parent: 4812 - uid: 7364 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -37.5,-21.5 parent: 4812 - uid: 7365 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -34.5,-13.5 parent: 4812 @@ -85880,8 +80005,6 @@ entities: - type: Transform pos: -53.5,7.5 parent: 4812 - - type: AtmosDevice - joinedGrid: 4812 - proto: WeaponCapacitorRecharger entities: - uid: 2515 @@ -86820,15 +80943,11 @@ entities: entities: - uid: 6795 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -19.5,-37.5 parent: 4812 - uid: 6796 components: - - type: MetaData - flags: PvsPriority - type: Transform pos: -21.5,-39.5 parent: 4812 @@ -86874,11 +80993,4 @@ entities: - type: Transform pos: 26.494213,-35.426273 parent: 4812 -- proto: YellowOxygenTankFilled - entities: - - uid: 11791 - components: - - type: Transform - pos: 19.452671,-32.41012 - parent: 4812 ... From 4cec8110b0112b37971d6bac2da3593dd014b61c Mon Sep 17 00:00:00 2001 From: Repo <47093363+Titian3@users.noreply.github.com> Date: Wed, 29 May 2024 02:02:15 +1200 Subject: [PATCH 091/568] Fix wall vending machines spawning items in walls. (#28279) * Find spawning of wall vending machines. * Review fixes --- .../VendingMachines/VendingMachineSystem.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Content.Server/VendingMachines/VendingMachineSystem.cs b/Content.Server/VendingMachines/VendingMachineSystem.cs index 723b9de6267c..63ec8f2c24bb 100644 --- a/Content.Server/VendingMachines/VendingMachineSystem.cs +++ b/Content.Server/VendingMachines/VendingMachineSystem.cs @@ -19,6 +19,7 @@ using Content.Shared.Throwing; using Content.Shared.UserInterface; using Content.Shared.VendingMachines; +using Content.Shared.Wall; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Prototypes; @@ -39,6 +40,8 @@ public sealed class VendingMachineSystem : SharedVendingMachineSystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!; + private const float WallVendEjectDistanceFromWall = 1f; + public override void Initialize() { base.Initialize(); @@ -384,7 +387,20 @@ private void EjectItem(EntityUid uid, VendingMachineComponent? vendComponent = n return; } - var ent = Spawn(vendComponent.NextItemToEject, Transform(uid).Coordinates); + // Default spawn coordinates + var spawnCoordinates = Transform(uid).Coordinates; + + //Make sure the wallvends spawn outside of the wall. + + if (TryComp(uid, out var wallMountComponent)) + { + + var offset = wallMountComponent.Direction.ToWorldVec() * WallVendEjectDistanceFromWall; + spawnCoordinates = spawnCoordinates.Offset(offset); + } + + var ent = Spawn(vendComponent.NextItemToEject, spawnCoordinates); + if (vendComponent.ThrowNextItem) { var range = vendComponent.NonLimitedEjectRange; From 691450974d16d4a723a1640dfcc2b33ac21d0bdb Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 28 May 2024 14:03:22 +0000 Subject: [PATCH 092/568] 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 4291972e7750..1dcd23155758 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: FungiFellow - changes: - - message: Added Improvised Shotgun Shell Recipe - type: Add - id: 6131 - time: '2024-03-12T23:52:32.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25545 - author: NakataRin changes: - message: Skeletons can only spawn with 10 people on the server now. @@ -3867,3 +3860,10 @@ id: 6630 time: '2024-05-28T00:51:50.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28255 +- author: Repo + changes: + - message: NanoMed, wall vends will spawn items the correct direction. + type: Fix + id: 6631 + time: '2024-05-28T14:02:15.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28279 From 5172114dbbe4ca95ad54c57a2d7be3fa3700838c Mon Sep 17 00:00:00 2001 From: MilenVolf <63782763+MilenVolf@users.noreply.github.com> Date: Tue, 28 May 2024 17:23:05 +0300 Subject: [PATCH 093/568] Localize emergency shuttle's direction for announcements (#28340) Localize emergency shuttle direction --- Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index e2b1ad32cd7a..45397ede088e 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -22,6 +22,7 @@ using Content.Shared.Database; using Content.Shared.DeviceNetwork; using Content.Shared.GameTicking; +using Content.Shared.Localizations; using Content.Shared.Shuttles.Components; using Content.Shared.Shuttles.Events; using Content.Shared.Tag; @@ -287,8 +288,9 @@ public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleCo if (TryComp(targetGrid.Value, out TransformComponent? targetXform)) { var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery); + var direction = ContentLocalizationManager.FormatDirection(angle.GetDir()); var location = FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((stationShuttle.EmergencyShuttle.Value, xform))); - _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir()), ("location", location)), playDefaultSound: false); + _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", direction), ("location", location)), playDefaultSound: false); } // shuttle timers @@ -317,8 +319,9 @@ public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleCo if (TryComp(targetGrid.Value, out var targetXform)) { var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery); + var direction = ContentLocalizationManager.FormatDirection(angle.GetDir()); var location = FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((stationShuttle.EmergencyShuttle.Value, xform))); - _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir()), ("location", location)), playDefaultSound: false); + _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("time", $"{_consoleAccumulator:0}"), ("direction", direction), ("location", location)), playDefaultSound: false); } _logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to find a valid docking port for {ToPrettyString(stationUid)}"); From 5141a95ed2ba53a0e91131dd1654f5f6cb3b8515 Mon Sep 17 00:00:00 2001 From: S1rFl0 <49481675+S1rFl0@users.noreply.github.com> Date: Tue, 28 May 2024 16:43:12 +0200 Subject: [PATCH 094/568] Fix some grammar and spelling mistakes (#28350) Fix some wacky grammar and spelling mistakes --- .../Locale/en-US/interaction/interaction-popup-component.ftl | 2 +- .../Prototypes/Entities/Clothing/OuterClothing/softsuits.yml | 2 +- .../Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl index 4929b11b1cd7..7db99c3d0aea 100644 --- a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl +++ b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl @@ -51,7 +51,7 @@ petting-failure-dragon = You raise your hand, but as {THE($target)} roars, you d petting-failure-hamster = You reach out to pet {THE($target)}, but {SUBJECT($target)} attempts to bite your finger and only your quick reflexes save you from an almost fatal injury. petting-failure-bear = You reach out to pet {THE($target)}, but {SUBJECT($target)} growls, making you think twice. petting-failure-monkey = You reach out to pet {THE($target)}, but {SUBJECT($target)} almost bites your fingers! -petting-failure-nymph = You reach out to pet {THE($target)}, but {POSS-ADJ($target)} moves their branches away. +petting-failure-nymph = You reach out to pet {THE($target)}, but {SUBJECT($target)} move their branches away. petting-failure-shadow = You're trying to pet {THE($target)}, but your hand passes through the cold darkness of his body. ## Petting silicons diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/softsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/softsuits.yml index 58faf2684709..9db1ea2216c1 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/softsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/softsuits.yml @@ -37,7 +37,7 @@ parent: ClothingOuterEVASuitBase id: ClothingOuterSuitEmergency name: emergency EVA suit - description: An emergency EVA suit with a built-in helmet. It's horribly slow and lacking in temperature protection, but enough to bide you time from the harsh vaccuum of space. + description: An emergency EVA suit with a built-in helmet. It's horribly slow and lacking in temperature protection, but enough to buy you time from the harsh vaccuum of space. components: - type: Sprite sprite: Clothing/OuterClothing/Suits/eva_emergency.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml index a19ed1951e43..b8cd6b8b25e6 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml @@ -201,7 +201,7 @@ deconstructionTarget: null - type: entity - name: sawn-off shogun + name: sawn-off shotgun parent: WeaponShotgunSawn id: WeaponShotgunSawnEmpty description: Groovy! Uses .50 shotgun shells. From e4f1201b4c7869611f09e8e98b3fb94112b6c685 Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Tue, 28 May 2024 07:59:13 -0700 Subject: [PATCH 095/568] Add pressure and temperature warning text to firelocks (#28341) --- Content.Server/Doors/Systems/FirelockSystem.cs | 2 +- .../Doors/Systems/SharedFirelockSystem.cs | 14 ++++++++++++++ .../Locale/en-US/atmos/firelock-component.ftl | 4 +++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Content.Server/Doors/Systems/FirelockSystem.cs b/Content.Server/Doors/Systems/FirelockSystem.cs index e2b8b5829d18..c7da404fe880 100644 --- a/Content.Server/Doors/Systems/FirelockSystem.cs +++ b/Content.Server/Doors/Systems/FirelockSystem.cs @@ -69,7 +69,7 @@ public override void Update(float frameTime) && xformQuery.TryGetComponent(uid, out var xform) && appearanceQuery.TryGetComponent(uid, out var appearance)) { - var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery); + var (pressure, fire) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery); _appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance); firelock.Temperature = fire; firelock.Pressure = pressure; diff --git a/Content.Shared/Doors/Systems/SharedFirelockSystem.cs b/Content.Shared/Doors/Systems/SharedFirelockSystem.cs index 47a29a4ba803..4afe26039ba4 100644 --- a/Content.Shared/Doors/Systems/SharedFirelockSystem.cs +++ b/Content.Shared/Doors/Systems/SharedFirelockSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Access.Systems; using Content.Shared.Doors.Components; +using Content.Shared.Examine; using Content.Shared.Popups; using Content.Shared.Prying.Components; using Robust.Shared.Timing; @@ -26,6 +27,8 @@ public override void Initialize() // Visuals SubscribeLocalEvent(UpdateVisuals); SubscribeLocalEvent(UpdateVisuals); + + SubscribeLocalEvent(OnExamined); } public bool EmergencyPressureStop(EntityUid uid, FirelockComponent? firelock = null, DoorComponent? door = null) @@ -107,4 +110,15 @@ private void UpdateVisuals(EntityUid uid, } #endregion + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + using (args.PushGroup(nameof(FirelockComponent))) + { + if (ent.Comp.Pressure) + args.PushMarkup(Loc.GetString("firelock-component-examine-pressure-warning")); + if (ent.Comp.Temperature) + args.PushMarkup(Loc.GetString("firelock-component-examine-temperature-warning")); + } + } } diff --git a/Resources/Locale/en-US/atmos/firelock-component.ftl b/Resources/Locale/en-US/atmos/firelock-component.ftl index fc375183e975..81f7e58462aa 100644 --- a/Resources/Locale/en-US/atmos/firelock-component.ftl +++ b/Resources/Locale/en-US/atmos/firelock-component.ftl @@ -1,2 +1,4 @@ firelock-component-is-holding-pressure-message = A gush of air blows in your face... Maybe you should reconsider. -firelock-component-is-holding-fire-message = A gush of warm air blows in your face... Maybe you should reconsider. \ No newline at end of file +firelock-component-is-holding-fire-message = A gush of warm air blows in your face... Maybe you should reconsider. +firelock-component-examine-pressure-warning = The [color=red]extreme pressure[/color] differential warning is active. +firelock-component-examine-temperature-warning = The [color=red]extreme temperature[/color] warning is active. From 4f14d9698b815c0e0b4816558d9f78057a46a04c Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 28 May 2024 15:00:19 +0000 Subject: [PATCH 096/568] 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 1dcd23155758..60412578e47b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: NakataRin - changes: - - message: Skeletons can only spawn with 10 people on the server now. - type: Tweak - id: 6132 - time: '2024-03-13T01:00:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26050 - author: SlamBamActionman changes: - message: Syndicate implanters can no longer be recycled. @@ -3867,3 +3860,10 @@ id: 6631 time: '2024-05-28T14:02:15.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28279 +- author: ShadowCommander + changes: + - message: Added pressure and temperature warnings to firelock examine text. + type: Add + id: 6632 + time: '2024-05-28T14:59:13.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28341 From bec21d443ffd6737fecf8612391466a7c38a0b4f Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Tue, 28 May 2024 17:01:19 +0200 Subject: [PATCH 097/568] Fix paper scrap layers + cleanup paper.yml (#28299) Fix paper scrap + cleanup paper.yml --- .../Entities/Objects/Misc/paper.yml | 46 ++++--------------- 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/Entities/Objects/Misc/paper.yml index 05a0b9d34551..1c8d8754884f 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/paper.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/paper.yml @@ -79,9 +79,14 @@ description: 'A crumpled up piece of white paper.' components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: scrap + - state: paper_words + map: ["enum.PaperVisualLayers.Writing"] + visible: false + - state: paper_stamp-generic + map: ["enum.PaperVisualLayers.Stamp"] + visible: false - type: entity name: office paper @@ -102,7 +107,6 @@ description: 'The readout of a device forgotten to time' components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: paper_dotmatrix - state: paper_dotmatrix_words @@ -133,7 +137,6 @@ description: "A page of the captain's journal. In luxurious lavender." components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: paper color: "#e6e6fa" @@ -160,7 +163,6 @@ description: 'A single unit of bureaucracy.' components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: paper color: "#9ef5ff" @@ -190,7 +192,6 @@ description: A paper label designating a crate as containing a bounty. Selling a crate with this label will fulfill the bounty. components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: paper color: "#f7e574" @@ -230,7 +231,6 @@ escapeFormatting: false content: book-cnc-sheet - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: paper color: "#cccccc" @@ -254,18 +254,11 @@ id: PaperWritten noSpawn: true components: - - type: Paper - type: Sprite layers: # Changing it here is fine - if the PaperStatus key is actually added, # something happened, so that ought to override this either way. - state: paper_words - - type: ActivatableUI - key: enum.PaperUiKey.Key - - type: UserInterface - interfaces: - enum.PaperUiKey.Key: - type: PaperBoundUserInterface - type: entity parent: Paper @@ -274,7 +267,6 @@ components: - type: NukeCodePaper allNukesAvailable: true - - type: Paper - type: entity parent: NukeCodePaper @@ -346,7 +338,6 @@ suffix: Red components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: folder-colormap color: "#cc2323" @@ -358,7 +349,6 @@ suffix: Blue components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: folder-colormap color: "#355d99" @@ -370,7 +360,6 @@ suffix: Yellow components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: folder-colormap color: "#b38e3c" @@ -382,7 +371,6 @@ suffix: White components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: folder-white - state: folder-base @@ -393,7 +381,6 @@ suffix: Grey components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: folder-colormap color: "#999999" @@ -405,7 +392,6 @@ suffix: Black components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: folder-colormap color: "#3f3f3f" @@ -417,7 +403,6 @@ suffix: Green components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: folder-colormap color: "#43bc38" @@ -431,7 +416,6 @@ description: CentCom's miserable little pile of secrets! components: - type: Sprite - sprite: Objects/Misc/bureaucracy.rsi layers: - state: folder-centcom - state: folder-base @@ -514,15 +498,12 @@ - state: clipboard_over - type: Item sprite: Objects/Misc/cc-clipboard.rsi - size: Small - type: Clothing - slots: [belt] - quickEquip: false sprite: Objects/Misc/cc-clipboard.rsi - type: entity id: BoxFolderQmClipboard - parent: BoxFolderBase + parent: BoxFolderClipboard name: requisition digi-board description: A bulky electric clipboard, filled with shipping orders and financing details. With so many compromising documents, you ought to keep this safe. components: @@ -537,11 +518,6 @@ map: ["qm_clipboard_pen"] visible: false - state: qm_clipboard_over - - type: ContainerContainer - containers: - storagebase: !type:Container - ents: [] - pen_slot: !type:ContainerSlot {} - type: ItemSlots slots: pen_slot: @@ -554,18 +530,13 @@ sprite: Objects/Misc/qm_clipboard.rsi size: Normal - type: Clothing - slots: [belt] - quickEquip: false sprite: Objects/Misc/qm_clipboard.rsi - type: Storage grid: - 0,0,4,3 quickInsert: true - whitelist: - tags: - - Document - type: StorageFill - contents: [] #to override base folder fill + contents: [] #to override base clipboard fill - type: ItemMapper mapLayers: qm_clipboard_paper: @@ -587,7 +558,6 @@ enum.StorageUiKey.Key: type: StorageBoundUserInterface - type: MeleeWeapon - wideAnimationRotation: 180 damage: types: Blunt: 10 From 229c3cd729e1e928961d6130f54e139f12a50b75 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Tue, 28 May 2024 15:35:08 +0000 Subject: [PATCH 098/568] missing nukies can be filled in by ghost roles (#28316) --- Content.Server/Antag/AntagSelectionSystem.cs | 12 +++++++-- .../Components/AntagSelectionComponent.cs | 1 + .../Entities/Markers/Spawners/ghost_roles.yml | 27 +++++++++++++++++++ Resources/Prototypes/GameRules/roundstart.yml | 8 +++--- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index a86611bedb39..b42831cbde81 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -205,16 +205,24 @@ public void ChooseAntags(Entity ent, IList /// Whether or not players should be picked to inhabit this antag or not. + /// If no players are left and is set, it will make a ghost role. /// [DataField] public bool PickPlayer = true; diff --git a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml index 25d1330d06b9..7942d26f3212 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml @@ -102,6 +102,33 @@ - sprite: Structures/Wallmounts/signs.rsi state: radiation +- type: entity + noSpawn: true + parent: SpawnPointLoneNukeOperative + id: SpawnPointNukeopsCommander + components: + - type: GhostRole + name: roles-antag-nuclear-operative-commander-name + description: roles-antag-nuclear-operative-commander-objective + +- type: entity + noSpawn: true + parent: SpawnPointLoneNukeOperative + id: SpawnPointNukeopsMedic + components: + - type: GhostRole + name: roles-antag-nuclear-operative-agent-name + description: roles-antag-nuclear-operative-agent-objective + +- type: entity + noSpawn: true + parent: SpawnPointLoneNukeOperative + id: SpawnPointNukeopsOperative + components: + - type: GhostRole + name: roles-antag-nuclear-operative-name + description: roles-antag-nuclear-operative-objective + - type: entity parent: MarkerBase id: SpawnPointGhostDragon diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 57e59f10e213..93350163f68f 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -90,8 +90,7 @@ definitions: - prefRoles: [ NukeopsCommander ] fallbackRoles: [ Nukeops, NukeopsMedic ] - max: 1 - playerRatio: 10 + spawnerPrototype: SpawnPointNukeopsCommander startingGear: SyndicateCommanderGearFull components: - type: NukeOperative @@ -107,8 +106,7 @@ prototype: NukeopsCommander - prefRoles: [ NukeopsMedic ] fallbackRoles: [ Nukeops, NukeopsCommander ] - max: 1 - playerRatio: 10 + spawnerPrototype: SpawnPointNukeopsMedic startingGear: SyndicateOperativeMedicFull components: - type: NukeOperative @@ -124,7 +122,7 @@ prototype: NukeopsMedic - prefRoles: [ Nukeops ] fallbackRoles: [ NukeopsCommander, NukeopsMedic ] - min: 0 + spawnerPrototype: SpawnPointNukeopsOperative max: 3 playerRatio: 10 startingGear: SyndicateOperativeGearFull From 1777eea9a42a8bdbc4a865327bb9ef3e10fe57de Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 28 May 2024 15:36:14 +0000 Subject: [PATCH 099/568] 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 60412578e47b..91fd512c01cb 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: SlamBamActionman - changes: - - message: Syndicate implanters can no longer be recycled. - type: Tweak - id: 6133 - time: '2024-03-13T02:02:36.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26047 - author: Gyrandola changes: - message: Adding Sky Blue carpets to tables no longer results in red carpet ones. @@ -3867,3 +3860,10 @@ id: 6632 time: '2024-05-28T14:59:13.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28341 +- author: deltanedas + changes: + - message: Nukies will have ghost roles opened if not enough people can be picked. + type: Tweak + id: 6633 + time: '2024-05-28T15:35:08.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28316 From eb3f27526bc8748a45c3f56de97a982747e51842 Mon Sep 17 00:00:00 2001 From: Repo <47093363+Titian3@users.noreply.github.com> Date: Wed, 29 May 2024 06:00:42 +1200 Subject: [PATCH 100/568] Fix late join & observe to de-admin admins. (#28319) --- Content.Client/Lobby/UI/ObserveWarningWindow.xaml | 5 +++-- .../Lobby/UI/ObserveWarningWindow.xaml.cs | 13 +++++++++++++ .../GameTicking/Commands/JoinGameCommand.cs | 11 +++++++++++ .../GameTicking/Commands/ObserveCommand.cs | 9 +++++++++ .../en-US/lobby/ui/observe-warning-window.ftl | 2 ++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Content.Client/Lobby/UI/ObserveWarningWindow.xaml b/Content.Client/Lobby/UI/ObserveWarningWindow.xaml index 3fe8e83f57df..2feac5792a1e 100644 --- a/Content.Client/Lobby/UI/ObserveWarningWindow.xaml +++ b/Content.Client/Lobby/UI/ObserveWarningWindow.xaml @@ -4,10 +4,11 @@ Pii = 1 << 18, + /// + /// Lets you take moderator actions on the game server. + /// + Moderator = 1 << 19, + + /// + /// Lets you check currently online admins. + /// + AdminWho = 1 << 20, + + /// + /// Lets you set the color of your OOC name. + /// + NameColor = 1 << 21, + /// /// Dangerous host permissions like scsi. /// diff --git a/Resources/engineCommandPerms.yml b/Resources/engineCommandPerms.yml index 42cc4668a937..b68a2b22dfb0 100644 --- a/Resources/engineCommandPerms.yml +++ b/Resources/engineCommandPerms.yml @@ -90,12 +90,12 @@ - Flags: ADMIN Commands: - - delete - - kick - listplayers - tp - tpto - - respawn + +- Flags: FUN + Commands: - tippy - tip @@ -111,6 +111,12 @@ Commands: - spawn - cspawn + - delete + +- Flags: MODERATOR + Commands: + - kick + - respawn - Flags: HOST Commands: From 727a176ca80f65221d62dd0f4906120e24f9a4d6 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 1 Jun 2024 08:15:50 +0000 Subject: [PATCH 195/568] Automatic changelog update --- Resources/Changelog/Admin.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 24fc59b3eb0b..2964e8c11b1e 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -272,5 +272,14 @@ Entries: id: 33 time: '2024-06-01T07:23:54.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27776 +- author: ShadowCommander, EmoGarbage + changes: + - message: 'Added new permissions: moderator, adminwho, and namecolor' + type: Add + - message: Changed various command perms from admin to moderator & fun. + type: Tweak + id: 34 + time: '2024-06-01T08:14:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28451 Name: Admin Order: 1 From e3a66136bfad05d09028fe3196ce7221ba3fb2f1 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 1 Jun 2024 02:35:14 -0700 Subject: [PATCH 196/568] Fix the client thinking it cannot shoot after mispredicting when it actually can (#28464) --- .../Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 6242312b0709..784dd0793a89 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -186,6 +186,7 @@ private void ManualCycle(EntityUid uid, BallisticAmmoProviderComponent component !Paused(uid)) { gunComp.NextFire = Timing.CurTime + TimeSpan.FromSeconds(1 / gunComp.FireRateModified); + Dirty(uid, gunComp); } Dirty(uid, component); From 19be94c9ea184710218aaddff8c9b91237735042 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 1 Jun 2024 05:08:31 -0700 Subject: [PATCH 197/568] Add job whitelist system (#28085) * Add job whitelist system * Address reviews * Fix name * Apply suggestions from code review Co-authored-by: Pieter-Jan Briers * cancinium --------- Co-authored-by: Pieter-Jan Briers --- Content.Client/Lobby/LobbyUIController.cs | 17 +- .../JobRequirementsManager.cs | 28 + Content.IntegrationTests/PoolManager.Cvars.cs | 1 + .../20240531011555_RoleWhitelist.Designer.cs | 1913 +++++++++++++++++ .../Postgres/20240531011555_RoleWhitelist.cs | 40 + .../PostgresServerDbContextModelSnapshot.cs | 31 + .../20240531011549_RoleWhitelist.Designer.cs | 1838 ++++++++++++++++ .../Sqlite/20240531011549_RoleWhitelist.cs | 40 + .../SqliteServerDbContextModelSnapshot.cs | 31 + Content.Server.Database/Model.cs | 20 + .../Commands/JobWhitelistCommands.cs | 214 ++ .../Administration/Managers/BanManager.cs | 8 +- .../Administration/Managers/IBanManager.cs | 4 +- Content.Server/Database/ServerDbBase.cs | 61 + Content.Server/Database/ServerDbManager.cs | 38 + Content.Server/Database/UserDbDataManager.cs | 50 +- Content.Server/Entry/EntryPoint.cs | 2 + .../Events/GetDisallowedJobsEvent.cs | 8 + .../GameTicking/Events/IsJobAllowedEvent.cs | 13 + .../GameTicking/GameTicker.Spawning.cs | 19 +- Content.Server/GameTicking/GameTicker.cs | 1 - Content.Server/IoC/ServerContentIoC.cs | 2 + .../JobWhitelist/JobWhitelistManager.cs | 114 + .../JobWhitelist/JobWhitelistSystem.cs | 83 + .../PlayTimeTrackingManager.cs | 9 +- .../PlayTimeTrackingSystem.cs | 30 +- .../Managers/ServerPreferencesManager.cs | 15 +- .../Events/StationJobsGetCandidatesEvent.cs | 8 + .../Systems/StationJobsSystem.Roundstart.cs | 8 +- .../Station/Systems/StationJobsSystem.cs | 2 +- Content.Shared/CCVar/CCVars.cs | 6 + .../Players/JobWhitelist/MsgJobWhitelist.cs | 33 + Content.Shared/Roles/JobPrototype.cs | 5 +- .../en-US/commands/job-whitelist-command.ftl | 20 + Resources/Locale/en-US/job/role-whitelist.ftl | 1 + 35 files changed, 4666 insertions(+), 47 deletions(-) create mode 100644 Content.Server.Database/Migrations/Postgres/20240531011555_RoleWhitelist.Designer.cs create mode 100644 Content.Server.Database/Migrations/Postgres/20240531011555_RoleWhitelist.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20240531011549_RoleWhitelist.Designer.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20240531011549_RoleWhitelist.cs create mode 100644 Content.Server/Administration/Commands/JobWhitelistCommands.cs create mode 100644 Content.Server/GameTicking/Events/GetDisallowedJobsEvent.cs create mode 100644 Content.Server/GameTicking/Events/IsJobAllowedEvent.cs create mode 100644 Content.Server/Players/JobWhitelist/JobWhitelistManager.cs create mode 100644 Content.Server/Players/JobWhitelist/JobWhitelistSystem.cs create mode 100644 Content.Server/Station/Events/StationJobsGetCandidatesEvent.cs create mode 100644 Content.Shared/Players/JobWhitelist/MsgJobWhitelist.cs create mode 100644 Resources/Locale/en-US/commands/job-whitelist-command.ftl create mode 100644 Resources/Locale/en-US/job/role-whitelist.ftl diff --git a/Content.Client/Lobby/LobbyUIController.cs b/Content.Client/Lobby/LobbyUIController.cs index ae9196c1100b..f6a3eed962c1 100644 --- a/Content.Client/Lobby/LobbyUIController.cs +++ b/Content.Client/Lobby/LobbyUIController.cs @@ -22,7 +22,6 @@ using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; using Robust.Shared.Utility; namespace Content.Client.Lobby; @@ -70,12 +69,9 @@ public override void Initialize() _profileEditor?.RefreshFlavorText(); }); - _configurationManager.OnValueChanged(CCVars.GameRoleTimers, args => - { - _profileEditor?.RefreshAntags(); - _profileEditor?.RefreshJobs(); - _profileEditor?.RefreshLoadouts(); - }); + _configurationManager.OnValueChanged(CCVars.GameRoleTimers, _ => RefreshProfileEditor()); + + _configurationManager.OnValueChanged(CCVars.GameRoleWhitelist, _ => RefreshProfileEditor()); } private LobbyCharacterPreviewPanel? GetLobbyPreview() @@ -193,6 +189,13 @@ private void RefreshLobbyPreview() PreviewPanel.SetSummaryText(humanoid.Summary); } + private void RefreshProfileEditor() + { + _profileEditor?.RefreshAntags(); + _profileEditor?.RefreshJobs(); + _profileEditor?.RefreshLoadouts(); + } + private void SaveProfile() { DebugTools.Assert(EditedProfile != null); diff --git a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs index 8193f795696b..cb155cadbf43 100644 --- a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs +++ b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.CCVar; using Content.Shared.Players; +using Content.Shared.Players.JobWhitelist; using Content.Shared.Players.PlayTimeTracking; using Content.Shared.Roles; using Robust.Client; @@ -24,6 +25,7 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager private readonly Dictionary _roles = new(); private readonly List _roleBans = new(); + private readonly List _jobWhitelists = new(); private ISawmill _sawmill = default!; @@ -36,6 +38,7 @@ public void Initialize() // Yeah the client manager handles role bans and playtime but the server ones are separate DEAL. _net.RegisterNetMessage(RxRoleBans); _net.RegisterNetMessage(RxPlayTime); + _net.RegisterNetMessage(RxJobWhitelist); _client.RunLevelChanged += ClientOnRunLevelChanged; } @@ -79,6 +82,13 @@ private void RxPlayTime(MsgPlayTime message) Updated?.Invoke(); } + private void RxJobWhitelist(MsgJobWhitelist message) + { + _jobWhitelists.Clear(); + _jobWhitelists.AddRange(message.Whitelist); + Updated?.Invoke(); + } + public bool IsAllowed(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason) { reason = null; @@ -89,6 +99,9 @@ public bool IsAllowed(JobPrototype job, [NotNullWhen(false)] out FormattedMessag return false; } + if (!CheckWhitelist(job, out reason)) + return false; + var player = _playerManager.LocalSession; if (player == null) return true; @@ -116,6 +129,21 @@ public bool CheckRoleTime(HashSet? requirements, [NotNullWhen(fa return reason == null; } + public bool CheckWhitelist(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason) + { + reason = default; + if (!_cfg.GetCVar(CCVars.GameRoleWhitelist)) + return true; + + if (job.Whitelisted && !_jobWhitelists.Contains(job.ID)) + { + reason = FormattedMessage.FromUnformatted(Loc.GetString("role-not-whitelisted")); + return false; + } + + return true; + } + public TimeSpan FetchOverallPlaytime() { return _roles.TryGetValue("Overall", out var overallPlaytime) ? overallPlaytime : TimeSpan.Zero; diff --git a/Content.IntegrationTests/PoolManager.Cvars.cs b/Content.IntegrationTests/PoolManager.Cvars.cs index aa6b4dffdcce..5acd9d502c1b 100644 --- a/Content.IntegrationTests/PoolManager.Cvars.cs +++ b/Content.IntegrationTests/PoolManager.Cvars.cs @@ -21,6 +21,7 @@ private static readonly (string cvar, string value)[] TestCvars = (CCVars.NPCMaxUpdates.Name, "999999"), (CVars.ThreadParallelCount.Name, "1"), (CCVars.GameRoleTimers.Name, "false"), + (CCVars.GameRoleWhitelist.Name, "false"), (CCVars.GridFill.Name, "false"), (CCVars.PreloadGrids.Name, "false"), (CCVars.ArrivalsShuttles.Name, "false"), diff --git a/Content.Server.Database/Migrations/Postgres/20240531011555_RoleWhitelist.Designer.cs b/Content.Server.Database/Migrations/Postgres/20240531011555_RoleWhitelist.Designer.cs new file mode 100644 index 000000000000..5032281dfece --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20240531011555_RoleWhitelist.Designer.cs @@ -0,0 +1,1913 @@ +// +using System; +using System.Net; +using System.Text.Json; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + [DbContext(typeof(PostgresServerDbContext))] + [Migration("20240531011555_RoleWhitelist")] + partial class RoleWhitelist + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Title") + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminId") + .HasColumnType("uuid") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("boolean") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("admin_log_id"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("smallint") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("RoundId", "Id") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Message") + .HasAnnotation("Npgsql:TsVectorConfig", "english"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Message"), "GIN"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("LogId") + .HasColumnType("integer") + .HasColumnName("log_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.HasKey("RoundId", "LogId", "PlayerUserId") + .HasName("PK_admin_log_player"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_log_player_player_user_id"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_messages_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("Dismissed") + .HasColumnType("boolean") + .HasColumnName("dismissed"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("boolean") + .HasColumnName("seen"); + + b.HasKey("Id") + .HasName("PK_admin_messages"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_messages_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_messages_round_id"); + + b.ToTable("admin_messages", null, t => + { + t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_notes_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("boolean") + .HasColumnName("secret"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_watchlists_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_admin_watchlists"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_watchlists_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_watchlists_round_id"); + + b.ToTable("admin_watchlists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("antag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("assigned_user_id_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("connection_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("smallint") + .HasColumnName("denied"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("timestamp with time zone") + .HasColumnName("time"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_connection_log_server_id"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("job_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("JobName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("play_time_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PlayerId") + .HasColumnType("uuid") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("interval") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("text") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("player_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FirstSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenHWId") + .HasColumnType("bytea") + .HasColumnName("last_seen_hwid"); + + b.Property("LastSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", null, t => + { + t.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("preference_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("integer") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("integer") + .HasColumnName("age"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("char_name"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("text") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_name"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("PreferenceId") + .HasColumnType("integer") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("integer") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("text") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("integer") + .HasColumnName("slot"); + + b.Property("SpawnPriority") + .HasColumnType("integer") + .HasColumnName("spawn_priority"); + + b.Property("Species") + .IsRequired() + .HasColumnType("text") + .HasColumnName("species"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_loadout_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LoadoutName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("loadout_name"); + + b.Property("ProfileLoadoutGroupId") + .HasColumnType("integer") + .HasColumnName("profile_loadout_group_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout"); + + b.HasIndex("ProfileLoadoutGroupId"); + + b.ToTable("profile_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_loadout_group_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("group_name"); + + b.Property("ProfileRoleLoadoutId") + .HasColumnType("integer") + .HasColumnName("profile_role_loadout_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout_group"); + + b.HasIndex("ProfileRoleLoadoutId"); + + b.ToTable("profile_loadout_group", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_role_loadout_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_name"); + + b.HasKey("Id") + .HasName("PK_profile_role_loadout"); + + b.HasIndex("ProfileId"); + + b.ToTable("profile_role_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("text") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("round_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ServerId") + .HasColumnType("integer") + .HasColumnName("server_id"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_date"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.HasIndex("StartDate"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_ban_round_id"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("integer") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_hit_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("integer") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_role_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_role_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_role_ban_round_id"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("role_unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("trait_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("uploaded_resource_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("integer") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("integer") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("RoundId", "LogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminMessagesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminMessagesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminMessagesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminMessagesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_messages_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_messages_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminWatchlistsCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminWatchlistsDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminWatchlistsLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminWatchlistsReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_watchlists_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_watchlists_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("ConnectionLogs") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired() + .HasConstraintName("FK_connection_log_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.HasOne("Content.Server.Database.ProfileLoadoutGroup", "ProfileLoadoutGroup") + .WithMany("Loadouts") + .HasForeignKey("ProfileLoadoutGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_profile_loadout_group_profile_loadout_group~"); + + b.Navigation("ProfileLoadoutGroup"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.HasOne("Content.Server.Database.ProfileRoleLoadout", "ProfileRoleLoadout") + .WithMany("Groups") + .HasForeignKey("ProfileRoleLoadoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_group_profile_role_loadout_profile_role_loa~"); + + b.Navigation("ProfileRoleLoadout"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Loadouts") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_role_loadout_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerRoleBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerRoleBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_role_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminServerRoleBansCreated"); + + b.Navigation("AdminServerRoleBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Navigation("Loadouts"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/20240531011555_RoleWhitelist.cs b/Content.Server.Database/Migrations/Postgres/20240531011555_RoleWhitelist.cs new file mode 100644 index 000000000000..1cd0d8df17dc --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20240531011555_RoleWhitelist.cs @@ -0,0 +1,40 @@ +#nullable disable + +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Content.Server.Database.Migrations.Postgres +{ + /// + public partial class RoleWhitelist : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "role_whitelists", + columns: table => new + { + player_user_id = table.Column(type: "uuid", nullable: false), + role_id = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_role_whitelists", x => new { x.player_user_id, x.role_id }); + table.ForeignKey( + name: "FK_role_whitelists_player_player_user_id", + column: x => x.player_user_id, + principalTable: "player", + principalColumn: "user_id", + onDelete: ReferentialAction.Cascade); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "role_whitelists"); + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs index bbbd64d874a1..32a655f7f65e 100644 --- a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs @@ -900,6 +900,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("profile_role_loadout", (string)null); }); + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("text") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.Round", b => { b.Property("Id") @@ -1623,6 +1639,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Profile"); }); + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + modelBuilder.Entity("Content.Server.Database.Round", b => { b.HasOne("Content.Server.Database.Server", "Server") @@ -1822,6 +1851,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("AdminWatchlistsLastEdited"); b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); }); modelBuilder.Entity("Content.Server.Database.Preference", b => diff --git a/Content.Server.Database/Migrations/Sqlite/20240531011549_RoleWhitelist.Designer.cs b/Content.Server.Database/Migrations/Sqlite/20240531011549_RoleWhitelist.Designer.cs new file mode 100644 index 000000000000..531f013604f5 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20240531011549_RoleWhitelist.Designer.cs @@ -0,0 +1,1838 @@ +// +using System; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + [DbContext(typeof(SqliteServerDbContext))] + [Migration("20240531011549_RoleWhitelist")] + partial class RoleWhitelist + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Title") + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_flag_id"); + + b.Property("AdminId") + .HasColumnType("TEXT") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("INTEGER") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Id") + .HasColumnType("INTEGER") + .HasColumnName("admin_log_id"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("INTEGER") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("INTEGER") + .HasColumnName("type"); + + b.HasKey("RoundId", "Id") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("LogId") + .HasColumnType("INTEGER") + .HasColumnName("log_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.HasKey("RoundId", "LogId", "PlayerUserId") + .HasName("PK_admin_log_player"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_log_player_player_user_id"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_messages_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("Dismissed") + .HasColumnType("INTEGER") + .HasColumnName("dismissed"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("INTEGER") + .HasColumnName("seen"); + + b.HasKey("Id") + .HasName("PK_admin_messages"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_messages_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_messages_round_id"); + + b.ToTable("admin_messages", null, t => + { + t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_notes_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("INTEGER") + .HasColumnName("secret"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_flag_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_watchlists_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_admin_watchlists"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_watchlists_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_watchlists_round_id"); + + b.ToTable("admin_watchlists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("antag_id"); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("assigned_user_id_id"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("connection_log_id"); + + b.Property("Address") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("INTEGER") + .HasColumnName("denied"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("TEXT") + .HasColumnName("time"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_connection_log_server_id"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("job_id"); + + b.Property("JobName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("play_time_id"); + + b.Property("PlayerId") + .HasColumnType("TEXT") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("TEXT") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("player_id"); + + b.Property("FirstSeenTime") + .HasColumnType("TEXT") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("TEXT") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenHWId") + .HasColumnType("BLOB") + .HasColumnName("last_seen_hwid"); + + b.Property("LastSeenTime") + .HasColumnType("TEXT") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("INTEGER") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("Age") + .HasColumnType("INTEGER") + .HasColumnName("age"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("char_name"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_name"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("PreferenceId") + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("INTEGER") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("INTEGER") + .HasColumnName("slot"); + + b.Property("SpawnPriority") + .HasColumnType("INTEGER") + .HasColumnName("spawn_priority"); + + b.Property("Species") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("species"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_loadout_id"); + + b.Property("LoadoutName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("loadout_name"); + + b.Property("ProfileLoadoutGroupId") + .HasColumnType("INTEGER") + .HasColumnName("profile_loadout_group_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout"); + + b.HasIndex("ProfileLoadoutGroupId"); + + b.ToTable("profile_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_loadout_group_id"); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("group_name"); + + b.Property("ProfileRoleLoadoutId") + .HasColumnType("INTEGER") + .HasColumnName("profile_role_loadout_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout_group"); + + b.HasIndex("ProfileRoleLoadoutId"); + + b.ToTable("profile_loadout_group", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_role_loadout_id"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_name"); + + b.HasKey("Id") + .HasName("PK_profile_role_loadout"); + + b.HasIndex("ProfileId"); + + b.ToTable("profile_role_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("ServerId") + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("StartDate") + .HasColumnType("TEXT") + .HasColumnName("start_date"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.HasIndex("StartDate"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_ban_round_id"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("INTEGER") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_hit_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("INTEGER") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_role_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_role_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_role_ban_round_id"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("role_unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("trait_id"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("uploaded_resource_log_id"); + + b.Property("Data") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("INTEGER") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("INTEGER") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("RoundId", "LogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminMessagesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminMessagesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminMessagesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminMessagesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_messages_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_messages_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminWatchlistsCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminWatchlistsDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminWatchlistsLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminWatchlistsReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_watchlists_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_watchlists_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("ConnectionLogs") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired() + .HasConstraintName("FK_connection_log_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.HasOne("Content.Server.Database.ProfileLoadoutGroup", "ProfileLoadoutGroup") + .WithMany("Loadouts") + .HasForeignKey("ProfileLoadoutGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_profile_loadout_group_profile_loadout_group_id"); + + b.Navigation("ProfileLoadoutGroup"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.HasOne("Content.Server.Database.ProfileRoleLoadout", "ProfileRoleLoadout") + .WithMany("Groups") + .HasForeignKey("ProfileRoleLoadoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_group_profile_role_loadout_profile_role_loadout_id"); + + b.Navigation("ProfileRoleLoadout"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Loadouts") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_role_loadout_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerRoleBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerRoleBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_role_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminServerRoleBansCreated"); + + b.Navigation("AdminServerRoleBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Navigation("Loadouts"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/20240531011549_RoleWhitelist.cs b/Content.Server.Database/Migrations/Sqlite/20240531011549_RoleWhitelist.cs new file mode 100644 index 000000000000..9d192fc6853e --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20240531011549_RoleWhitelist.cs @@ -0,0 +1,40 @@ +#nullable disable + +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Content.Server.Database.Migrations.Sqlite +{ + /// + public partial class RoleWhitelist : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "role_whitelists", + columns: table => new + { + player_user_id = table.Column(type: "TEXT", nullable: false), + role_id = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_role_whitelists", x => new { x.player_user_id, x.role_id }); + table.ForeignKey( + name: "FK_role_whitelists_player_player_user_id", + column: x => x.player_user_id, + principalTable: "player", + principalColumn: "user_id", + onDelete: ReferentialAction.Cascade); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "role_whitelists"); + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs index d343f49f416d..2c444c83eb36 100644 --- a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs @@ -847,6 +847,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("profile_role_loadout", (string)null); }); + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.Round", b => { b.Property("Id") @@ -1548,6 +1564,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Profile"); }); + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + modelBuilder.Entity("Content.Server.Database.Round", b => { b.HasOne("Content.Server.Database.Server", "Server") @@ -1747,6 +1776,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("AdminWatchlistsLastEdited"); b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); }); modelBuilder.Entity("Content.Server.Database.Preference", b => diff --git a/Content.Server.Database/Model.cs b/Content.Server.Database/Model.cs index 57dcc3fc6dce..60935e8f06ad 100644 --- a/Content.Server.Database/Model.cs +++ b/Content.Server.Database/Model.cs @@ -40,6 +40,7 @@ protected ServerDbContext(DbContextOptions options) : base(options) public DbSet AdminNotes { get; set; } = null!; public DbSet AdminWatchlists { get; set; } = null!; public DbSet AdminMessages { get; set; } = null!; + public DbSet RoleWhitelists { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -314,6 +315,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(ban => ban.LastEditedById) .HasPrincipalKey(author => author.UserId) .OnDelete(DeleteBehavior.SetNull); + + modelBuilder.Entity() + .HasOne(w => w.Player) + .WithMany(p => p.JobWhitelists) + .HasForeignKey(w => w.PlayerUserId) + .HasPrincipalKey(p => p.UserId) + .OnDelete(DeleteBehavior.Cascade); } public virtual IQueryable SearchLogs(IQueryable query, string searchText) @@ -530,6 +538,7 @@ public class Player public List AdminServerBansLastEdited { get; set; } = null!; public List AdminServerRoleBansCreated { get; set; } = null!; public List AdminServerRoleBansLastEdited { get; set; } = null!; + public List JobWhitelists { get; set; } = null!; } [Table("whitelist")] @@ -1099,4 +1108,15 @@ public class AdminMessage : IAdminRemarksCommon /// public bool Dismissed { get; set; } } + + [PrimaryKey(nameof(PlayerUserId), nameof(RoleId))] + public class RoleWhitelist + { + [Required, ForeignKey("Player")] + public Guid PlayerUserId { get; set; } + public Player Player { get; set; } = default!; + + [Required] + public string RoleId { get; set; } = default!; + } } diff --git a/Content.Server/Administration/Commands/JobWhitelistCommands.cs b/Content.Server/Administration/Commands/JobWhitelistCommands.cs new file mode 100644 index 000000000000..a84a11ef848e --- /dev/null +++ b/Content.Server/Administration/Commands/JobWhitelistCommands.cs @@ -0,0 +1,214 @@ +using System.Linq; +using Content.Server.Database; +using Content.Server.Players.JobWhitelist; +using Content.Shared.Administration; +using Content.Shared.Roles; +using Robust.Server.Player; +using Robust.Shared.Console; +using Robust.Shared.Prototypes; + +namespace Content.Server.Administration.Commands; + +[AdminCommand(AdminFlags.Ban)] +public sealed class JobWhitelistAddCommand : LocalizedCommands +{ + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly JobWhitelistManager _jobWhitelist = default!; + [Dependency] private readonly IPlayerLocator _playerLocator = default!; + [Dependency] private readonly IPlayerManager _players = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + + public override string Command => "jobwhitelistadd"; + + public override async void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 2) + { + shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", + ("properAmount", 2), + ("currentAmount", args.Length))); + shell.WriteLine(Help); + return; + } + + var player = args[0].Trim(); + var job = new ProtoId(args[1].Trim()); + if (!_prototypes.TryIndex(job, out var jobPrototype)) + { + shell.WriteError(Loc.GetString("cmd-jobwhitelist-job-does-not-exist", ("job", job.Id))); + shell.WriteLine(Help); + return; + } + + var data = await _playerLocator.LookupIdByNameAsync(player); + if (data != null) + { + var guid = data.UserId; + var isWhitelisted = await _db.IsJobWhitelisted(guid, job); + if (isWhitelisted) + { + shell.WriteLine(Loc.GetString("cmd-jobwhitelist-already-whitelisted", + ("player", player), + ("jobId", job.Id), + ("jobName", jobPrototype.LocalizedName))); + return; + } + + _jobWhitelist.AddWhitelist(guid, job); + shell.WriteLine(Loc.GetString("cmd-jobwhitelistadd-added", + ("player", player), + ("jobId", job.Id), + ("jobName", jobPrototype.LocalizedName))); + return; + } + + shell.WriteError(Loc.GetString("cmd-jobwhitelist-player-not-found", ("player", player))); + } + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + if (args.Length == 1) + { + return CompletionResult.FromHintOptions( + _players.Sessions.Select(s => s.Name), + Loc.GetString("cmd-jobwhitelist-hint-player")); + } + + if (args.Length == 2) + { + return CompletionResult.FromHintOptions( + _prototypes.EnumeratePrototypes().Select(p => p.ID), + Loc.GetString("cmd-jobwhitelist-hint-job")); + } + + return CompletionResult.Empty; + } +} + +[AdminCommand(AdminFlags.Ban)] +public sealed class GetJobWhitelistCommand : LocalizedCommands +{ + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly IPlayerLocator _playerLocator = default!; + [Dependency] private readonly IPlayerManager _players = default!; + + public override string Command => "jobwhitelistget"; + + public override async void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length == 0) + { + shell.WriteError("This command needs at least one argument."); + shell.WriteLine(Help); + return; + } + + var player = string.Join(' ', args).Trim(); + var data = await _playerLocator.LookupIdByNameAsync(player); + if (data != null) + { + var guid = data.UserId; + var whitelists = await _db.GetJobWhitelists(guid); + if (whitelists.Count == 0) + { + shell.WriteLine(Loc.GetString("cmd-jobwhitelistget-whitelisted-none", ("player", player))); + return; + } + + shell.WriteLine(Loc.GetString("cmd-jobwhitelistget-whitelisted-for", + ("player", player), + ("jobs", string.Join(", ", whitelists)))); + return; + } + + shell.WriteError(Loc.GetString("cmd-jobwhitelist-player-not-found", ("player", player))); + } + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + if (args.Length == 1) + { + return CompletionResult.FromHintOptions( + _players.Sessions.Select(s => s.Name), + Loc.GetString("cmd-jobwhitelist-hint-player")); + } + + return CompletionResult.Empty; + } +} + +[AdminCommand(AdminFlags.Ban)] +public sealed class RemoveJobWhitelistCommand : LocalizedCommands +{ + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly JobWhitelistManager _jobWhitelist = default!; + [Dependency] private readonly IPlayerLocator _playerLocator = default!; + [Dependency] private readonly IPlayerManager _players = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + + public override string Command => "jobwhitelistremove"; + + public override async void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 2) + { + shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", + ("properAmount", 2), + ("currentAmount", args.Length))); + shell.WriteLine(Help); + return; + } + + var player = args[0].Trim(); + var job = new ProtoId(args[1].Trim()); + if (!_prototypes.TryIndex(job, out var jobPrototype)) + { + shell.WriteError(Loc.GetString("cmd-jobwhitelist-job-does-not-exist", ("job", job))); + shell.WriteLine(Help); + return; + } + + var data = await _playerLocator.LookupIdByNameAsync(player); + if (data != null) + { + var guid = data.UserId; + var isWhitelisted = await _db.IsJobWhitelisted(guid, job); + if (!isWhitelisted) + { + shell.WriteError(Loc.GetString("cmd-jobwhitelistremove-was-not-whitelisted", + ("player", player), + ("jobId", job.Id), + ("jobName", jobPrototype.LocalizedName))); + return; + } + + _jobWhitelist.RemoveWhitelist(guid, job); + shell.WriteLine(Loc.GetString("cmd-jobwhitelistremove-removed", + ("player", player), + ("jobId", job.Id), + ("jobName", jobPrototype.LocalizedName))); + return; + } + + shell.WriteError(Loc.GetString("cmd-jobwhitelist-player-not-found", ("player", player))); + } + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + if (args.Length == 1) + { + return CompletionResult.FromHintOptions( + _players.Sessions.Select(s => s.Name), + Loc.GetString("cmd-jobwhitelist-hint-player")); + } + + if (args.Length == 2) + { + return CompletionResult.FromHintOptions( + _prototypes.EnumeratePrototypes().Select(p => p.ID), + Loc.GetString("cmd-jobwhitelist-hint-job")); + } + + return CompletionResult.Empty; + } +} diff --git a/Content.Server/Administration/Managers/BanManager.cs b/Content.Server/Administration/Managers/BanManager.cs index 3a05b934b242..68bd81702653 100644 --- a/Content.Server/Administration/Managers/BanManager.cs +++ b/Content.Server/Administration/Managers/BanManager.cs @@ -73,7 +73,9 @@ private async Task AddRoleBan(ServerRoleBanDef banDef) public HashSet? GetRoleBans(NetUserId playerUserId) { - return _cachedRoleBans.TryGetValue(playerUserId, out var roleBans) ? roleBans.Select(banDef => banDef.Role).ToHashSet() : null; + return _cachedRoleBans.TryGetValue(playerUserId, out var roleBans) + ? roleBans.Select(banDef => banDef.Role).ToHashSet() + : null; } private async Task CacheDbRoleBans(NetUserId userId, IPAddress? address = null, ImmutableArray? hwId = null) @@ -263,13 +265,13 @@ public async Task PardonRoleBan(int banId, NetUserId? unbanningAdmin, Da return $"Pardoned ban with id {banId}"; } - public HashSet? GetJobBans(NetUserId playerUserId) + public HashSet>? GetJobBans(NetUserId playerUserId) { if (!_cachedRoleBans.TryGetValue(playerUserId, out var roleBans)) return null; return roleBans .Where(ban => ban.Role.StartsWith(JobPrefix, StringComparison.Ordinal)) - .Select(ban => ban.Role[JobPrefix.Length..]) + .Select(ban => new ProtoId(ban.Role[JobPrefix.Length..])) .ToHashSet(); } #endregion diff --git a/Content.Server/Administration/Managers/IBanManager.cs b/Content.Server/Administration/Managers/IBanManager.cs index dafe3d35bdd4..b60e0a25351d 100644 --- a/Content.Server/Administration/Managers/IBanManager.cs +++ b/Content.Server/Administration/Managers/IBanManager.cs @@ -2,8 +2,10 @@ using System.Net; using System.Threading.Tasks; using Content.Shared.Database; +using Content.Shared.Roles; using Robust.Shared.Network; using Robust.Shared.Player; +using Robust.Shared.Prototypes; namespace Content.Server.Administration.Managers; @@ -24,7 +26,7 @@ public interface IBanManager /// Reason for the ban public void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray? hwid, uint? minutes, NoteSeverity severity, string reason); public HashSet? GetRoleBans(NetUserId playerUserId); - public HashSet? GetJobBans(NetUserId playerUserId); + public HashSet>? GetJobBans(NetUserId playerUserId); /// /// Creates a job ban for the specified target, username or GUID diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index 8743a4b5db1c..be6c7196d5f9 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -14,9 +14,11 @@ using Content.Shared.Humanoid.Markings; using Content.Shared.Preferences; using Content.Shared.Preferences.Loadouts; +using Content.Shared.Roles; using Microsoft.EntityFrameworkCore; using Robust.Shared.Enums; using Robust.Shared.Network; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Database @@ -1579,6 +1581,65 @@ protected async Task> GetGroupedServerRoleBansAsNo #endregion + #region Job Whitelists + + public async Task AddJobWhitelist(Guid player, ProtoId job) + { + await using var db = await GetDb(); + var exists = await db.DbContext.RoleWhitelists + .Where(w => w.PlayerUserId == player) + .Where(w => w.RoleId == job.Id) + .AnyAsync(); + + if (exists) + return false; + + var whitelist = new RoleWhitelist + { + PlayerUserId = player, + RoleId = job + }; + db.DbContext.RoleWhitelists.Add(whitelist); + await db.DbContext.SaveChangesAsync(); + return true; + } + + public async Task> GetJobWhitelists(Guid player, CancellationToken cancel) + { + await using var db = await GetDb(cancel); + return await db.DbContext.RoleWhitelists + .Where(w => w.PlayerUserId == player) + .Select(w => w.RoleId) + .ToListAsync(cancellationToken: cancel); + } + + public async Task IsJobWhitelisted(Guid player, ProtoId job) + { + await using var db = await GetDb(); + return await db.DbContext.RoleWhitelists + .Where(w => w.PlayerUserId == player) + .Where(w => w.RoleId == job.Id) + .AnyAsync(); + } + + public async Task RemoveJobWhitelist(Guid player, ProtoId job) + { + await using var db = await GetDb(); + var entry = await db.DbContext.RoleWhitelists + .Where(w => w.PlayerUserId == player) + .Where(w => w.RoleId == job.Id) + .SingleOrDefaultAsync(); + + if (entry == null) + return false; + + db.DbContext.RoleWhitelists.Remove(entry); + await db.DbContext.SaveChangesAsync(); + return true; + } + + #endregion + // SQLite returns DateTime as Kind=Unspecified, Npgsql actually knows for sure it's Kind=Utc. // Normalize DateTimes here so they're always Utc. Thanks. protected abstract DateTime NormalizeDatabaseTime(DateTime time); diff --git a/Content.Server/Database/ServerDbManager.cs b/Content.Server/Database/ServerDbManager.cs index 01d15267274e..1983fe43d20b 100644 --- a/Content.Server/Database/ServerDbManager.cs +++ b/Content.Server/Database/ServerDbManager.cs @@ -9,6 +9,7 @@ using Content.Shared.CCVar; using Content.Shared.Database; using Content.Shared.Preferences; +using Content.Shared.Roles; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -17,6 +18,7 @@ using Robust.Shared.Configuration; using Robust.Shared.ContentPack; using Robust.Shared.Network; +using Robust.Shared.Prototypes; using LogLevel = Robust.Shared.Log.LogLevel; using MSLogLevel = Microsoft.Extensions.Logging.LogLevel; @@ -290,6 +292,18 @@ Task AddConnectionLogAsync( Task MarkMessageAsSeen(int id, bool dismissedToo); #endregion + + #region Job Whitelists + + Task AddJobWhitelist(Guid player, ProtoId job); + + + Task> GetJobWhitelists(Guid player, CancellationToken cancel = default); + Task IsJobWhitelisted(Guid player, ProtoId job); + + Task RemoveJobWhitelist(Guid player, ProtoId job); + + #endregion } public sealed class ServerDbManager : IServerDbManager @@ -869,6 +883,30 @@ public Task MarkMessageAsSeen(int id, bool dismissedToo) return RunDbCommand(() => _db.MarkMessageAsSeen(id, dismissedToo)); } + public Task AddJobWhitelist(Guid player, ProtoId job) + { + DbWriteOpsMetric.Inc(); + return RunDbCommand(() => _db.AddJobWhitelist(player, job)); + } + + public Task> GetJobWhitelists(Guid player, CancellationToken cancel = default) + { + DbReadOpsMetric.Inc(); + return RunDbCommand(() => _db.GetJobWhitelists(player, cancel)); + } + + public Task IsJobWhitelisted(Guid player, ProtoId job) + { + DbReadOpsMetric.Inc(); + return RunDbCommand(() => _db.IsJobWhitelisted(player, job)); + } + + public Task RemoveJobWhitelist(Guid player, ProtoId job) + { + DbWriteOpsMetric.Inc(); + return RunDbCommand(() => _db.RemoveJobWhitelist(player, job)); + } + // Wrapper functions to run DB commands from the thread pool. // This will avoid SynchronizationContext capturing and avoid running CPU work on the main thread. // For SQLite, this will also enable read parallelization (within limits). diff --git a/Content.Server/Database/UserDbDataManager.cs b/Content.Server/Database/UserDbDataManager.cs index c58c594dbad0..1aac86c12916 100644 --- a/Content.Server/Database/UserDbDataManager.cs +++ b/Content.Server/Database/UserDbDataManager.cs @@ -1,8 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Content.Server.Players.PlayTimeTracking; using Content.Server.Preferences.Managers; -using Robust.Server.Player; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Utility; @@ -19,11 +17,12 @@ namespace Content.Server.Database; /// public sealed class UserDbDataManager : IPostInjectInit { - [Dependency] private readonly IServerPreferencesManager _prefs = default!; [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly PlayTimeTrackingManager _playTimeTracking = default!; private readonly Dictionary _users = new(); + private readonly List _onLoadPlayer = []; + private readonly List _onFinishLoad = []; + private readonly List _onPlayerDisconnect = []; private ISawmill _sawmill = default!; @@ -51,8 +50,10 @@ public void ClientDisconnected(ICommonSession session) data.Cancel.Cancel(); data.Cancel.Dispose(); - _prefs.OnClientDisconnected(session); - _playTimeTracking.ClientDisconnected(session); + foreach (var onDisconnect in _onPlayerDisconnect) + { + onDisconnect(session); + } } private async Task Load(ICommonSession session, CancellationToken cancel) @@ -62,12 +63,20 @@ private async Task Load(ICommonSession session, CancellationToken cancel) // As such, this task must NOT throw a non-cancellation error! try { - await Task.WhenAll( - _prefs.LoadData(session, cancel), - _playTimeTracking.LoadData(session, cancel)); + var tasks = new List(); + foreach (var action in _onLoadPlayer) + { + tasks.Add(action(session, cancel)); + } + + await Task.WhenAll(tasks); cancel.ThrowIfCancellationRequested(); - _prefs.FinishLoad(session); + + foreach (var action in _onFinishLoad) + { + action(session); + } _sawmill.Verbose($"Load complete for user {session}"); } @@ -118,10 +127,31 @@ public Task GetLoadTask(ICommonSession session) return _users[session.UserId].Task; } + public void AddOnLoadPlayer(OnLoadPlayer action) + { + _onLoadPlayer.Add(action); + } + + public void AddOnFinishLoad(OnFinishLoad action) + { + _onFinishLoad.Add(action); + } + + public void AddOnPlayerDisconnect(OnPlayerDisconnect action) + { + _onPlayerDisconnect.Add(action); + } + void IPostInjectInit.PostInject() { _sawmill = _logManager.GetSawmill("userdb"); } private sealed record UserData(CancellationTokenSource Cancel, Task Task); + + public delegate Task OnLoadPlayer(ICommonSession player, CancellationToken cancel); + + public delegate void OnFinishLoad(ICommonSession player); + + public delegate void OnPlayerDisconnect(ICommonSession player); } diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs index 3cdf3bfe8e2e..234a2e465869 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -14,6 +14,7 @@ using Content.Server.IoC; using Content.Server.Maps; using Content.Server.NodeContainer.NodeGroups; +using Content.Server.Players.JobWhitelist; using Content.Server.Players.PlayTimeTracking; using Content.Server.Preferences.Managers; using Content.Server.ServerInfo; @@ -107,6 +108,7 @@ public override void Init() _voteManager.Initialize(); _updateManager.Initialize(); _playTimeTracking.Initialize(); + IoCManager.Resolve().Initialize(); } } diff --git a/Content.Server/GameTicking/Events/GetDisallowedJobsEvent.cs b/Content.Server/GameTicking/Events/GetDisallowedJobsEvent.cs new file mode 100644 index 000000000000..cd15cfb8f8d0 --- /dev/null +++ b/Content.Server/GameTicking/Events/GetDisallowedJobsEvent.cs @@ -0,0 +1,8 @@ +using Content.Shared.Roles; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.GameTicking.Events; + +[ByRefEvent] +public readonly record struct GetDisallowedJobsEvent(ICommonSession Player, HashSet> Jobs); diff --git a/Content.Server/GameTicking/Events/IsJobAllowedEvent.cs b/Content.Server/GameTicking/Events/IsJobAllowedEvent.cs new file mode 100644 index 000000000000..51969d61ea0a --- /dev/null +++ b/Content.Server/GameTicking/Events/IsJobAllowedEvent.cs @@ -0,0 +1,13 @@ +using Content.Shared.Roles; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.GameTicking.Events; + +[ByRefEvent] +public struct IsJobAllowedEvent(ICommonSession player, ProtoId jobId, bool cancelled = false) +{ + public readonly ICommonSession Player = player; + public readonly ProtoId JobId = jobId; + public bool Cancelled = cancelled; +} diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 843122e0feaa..22d368e57bde 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -2,11 +2,11 @@ using System.Linq; using System.Numerics; using Content.Server.Administration.Managers; +using Content.Server.GameTicking.Events; using Content.Server.Ghost; using Content.Server.Spawners.Components; using Content.Server.Speech.Components; using Content.Server.Station.Components; -using Content.Shared.CCVar; using Content.Shared.Database; using Content.Shared.Mind; using Content.Shared.Players; @@ -137,8 +137,14 @@ private void SpawnPlayer(ICommonSession player, if (jobBans == null || jobId != null && jobBans.Contains(jobId)) return; - if (jobId != null && !_playTimeTrackings.IsAllowed(player, jobId)) - return; + if (jobId != null) + { + var ev = new IsJobAllowedEvent(player, new ProtoId(jobId)); + RaiseLocalEvent(ref ev); + if (ev.Cancelled) + return; + } + SpawnPlayer(player, character, station, jobId, lateJoin, silent); } @@ -181,10 +187,9 @@ private void SpawnPlayer(ICommonSession player, } // Figure out job restrictions - var restrictedRoles = new HashSet(); - - var getDisallowed = _playTimeTrackings.GetDisallowedJobs(player); - restrictedRoles.UnionWith(getDisallowed); + var restrictedRoles = new HashSet>(); + var ev = new GetDisallowedJobsEvent(player, restrictedRoles); + RaiseLocalEvent(ref ev); var jobBans = _banManager.GetJobBans(player.UserId); if (jobBans != null) diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index fa23312268f5..d9511309b9dc 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -19,7 +19,6 @@ using Robust.Server; using Robust.Server.GameObjects; using Robust.Server.GameStates; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; using Robust.Shared.Console; diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index f6800f72890d..c6dfcadd3826 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -13,6 +13,7 @@ using Content.Server.Maps; using Content.Server.MoMMI; using Content.Server.NodeContainer.NodeGroups; +using Content.Server.Players.JobWhitelist; using Content.Server.Players.PlayTimeTracking; using Content.Server.Preferences.Managers; using Content.Server.ServerInfo; @@ -61,6 +62,7 @@ public static void Register() IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); } } } diff --git a/Content.Server/Players/JobWhitelist/JobWhitelistManager.cs b/Content.Server/Players/JobWhitelist/JobWhitelistManager.cs new file mode 100644 index 000000000000..04289a40980d --- /dev/null +++ b/Content.Server/Players/JobWhitelist/JobWhitelistManager.cs @@ -0,0 +1,114 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Content.Server.Database; +using Content.Shared.CCVar; +using Content.Shared.Players.JobWhitelist; +using Content.Shared.Roles; +using Robust.Server.Player; +using Robust.Shared.Configuration; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Serilog; + +namespace Content.Server.Players.JobWhitelist; + +public sealed class JobWhitelistManager : IPostInjectInit +{ + [Dependency] private readonly IConfigurationManager _config = default!; + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + [Dependency] private readonly UserDbDataManager _userDb = default!; + + private readonly Dictionary> _whitelists = new(); + + public void Initialize() + { + _net.RegisterNetMessage(); + } + + private async Task LoadData(ICommonSession session, CancellationToken cancel) + { + var whitelists = await _db.GetJobWhitelists(session.UserId, cancel); + cancel.ThrowIfCancellationRequested(); + _whitelists[session.UserId] = whitelists.ToHashSet(); + } + + private void FinishLoad(ICommonSession session) + { + SendJobWhitelist(session); + } + + private void ClientDisconnected(ICommonSession session) + { + _whitelists.Remove(session.UserId); + } + + public async void AddWhitelist(NetUserId player, ProtoId job) + { + if (_whitelists.TryGetValue(player, out var whitelists)) + whitelists.Add(job); + + await _db.AddJobWhitelist(player, job); + + if (_player.TryGetSessionById(player, out var session)) + SendJobWhitelist(session); + } + + public bool IsAllowed(ICommonSession session, ProtoId job) + { + if (!_config.GetCVar(CCVars.GameRoleWhitelist)) + return true; + + if (!_prototypes.TryIndex(job, out var jobPrototype) || + !jobPrototype.Whitelisted) + { + return true; + } + + return IsWhitelisted(session.UserId, job); + } + + public bool IsWhitelisted(NetUserId player, ProtoId job) + { + if (!_whitelists.TryGetValue(player, out var whitelists)) + { + Log.Error("Unable to check if player {Player} is whitelisted for {Job}. Stack trace:\\n{StackTrace}", + player, + job, + Environment.StackTrace); + return false; + } + + return whitelists.Contains(job); + } + + public async void RemoveWhitelist(NetUserId player, ProtoId job) + { + _whitelists.GetValueOrDefault(player)?.Remove(job); + await _db.RemoveJobWhitelist(player, job); + + if (_player.TryGetSessionById(new NetUserId(player), out var session)) + SendJobWhitelist(session); + } + + public void SendJobWhitelist(ICommonSession player) + { + var msg = new MsgJobWhitelist + { + Whitelist = _whitelists.GetValueOrDefault(player.UserId) ?? new HashSet() + }; + + _net.ServerSendMessage(msg, player.Channel); + } + + void IPostInjectInit.PostInject() + { + _userDb.AddOnLoadPlayer(LoadData); + _userDb.AddOnFinishLoad(FinishLoad); + _userDb.AddOnPlayerDisconnect(ClientDisconnected); + } +} diff --git a/Content.Server/Players/JobWhitelist/JobWhitelistSystem.cs b/Content.Server/Players/JobWhitelist/JobWhitelistSystem.cs new file mode 100644 index 000000000000..aaada99dea94 --- /dev/null +++ b/Content.Server/Players/JobWhitelist/JobWhitelistSystem.cs @@ -0,0 +1,83 @@ +using System.Collections.Immutable; +using Content.Server.GameTicking.Events; +using Content.Server.Station.Events; +using Content.Shared.CCVar; +using Content.Shared.Roles; +using Robust.Server.Player; +using Robust.Shared.Configuration; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Server.Players.JobWhitelist; + +public sealed class JobWhitelistSystem : EntitySystem +{ + [Dependency] private readonly IConfigurationManager _config = default!; + [Dependency] private readonly JobWhitelistManager _manager = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + + private ImmutableArray> _whitelistedJobs = []; + + public override void Initialize() + { + SubscribeLocalEvent(OnPrototypesReloaded); + SubscribeLocalEvent(OnStationJobsGetCandidates); + SubscribeLocalEvent(OnIsJobAllowed); + SubscribeLocalEvent(OnGetDisallowedJobs); + + CacheJobs(); + } + + private void OnPrototypesReloaded(PrototypesReloadedEventArgs ev) + { + if (ev.WasModified()) + CacheJobs(); + } + + private void OnStationJobsGetCandidates(ref StationJobsGetCandidatesEvent ev) + { + if (!_config.GetCVar(CCVars.GameRoleWhitelist)) + return; + + for (var i = ev.Jobs.Count - 1; i >= 0; i--) + { + var jobId = ev.Jobs[i]; + if (_player.TryGetSessionById(ev.Player, out var player) && + !_manager.IsAllowed(player, jobId)) + { + ev.Jobs.RemoveSwap(i); + } + } + } + + private void OnIsJobAllowed(ref IsJobAllowedEvent ev) + { + if (!_manager.IsAllowed(ev.Player, ev.JobId)) + ev.Cancelled = true; + } + + private void OnGetDisallowedJobs(ref GetDisallowedJobsEvent ev) + { + if (!_config.GetCVar(CCVars.GameRoleWhitelist)) + return; + + foreach (var job in _whitelistedJobs) + { + if (!_manager.IsAllowed(ev.Player, job)) + ev.Jobs.Add(job); + } + } + + private void CacheJobs() + { + var builder = ImmutableArray.CreateBuilder>(); + foreach (var job in _prototypes.EnumeratePrototypes()) + { + if (job.Whitelisted) + builder.Add(job.ID); + } + + _whitelistedJobs = builder.ToImmutable(); + } +} diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs index c638c2f24094..bfd6172f4c0d 100644 --- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs +++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs @@ -54,7 +54,7 @@ namespace Content.Server.Players.PlayTimeTracking; /// Operations like refreshing and sending play time info to clients are deferred until the next frame (note: not tick). /// /// -public sealed class PlayTimeTrackingManager : ISharedPlaytimeManager +public sealed class PlayTimeTrackingManager : ISharedPlaytimeManager, IPostInjectInit { [Dependency] private readonly IServerDbManager _db = default!; [Dependency] private readonly IServerNetManager _net = default!; @@ -62,6 +62,7 @@ public sealed class PlayTimeTrackingManager : ISharedPlaytimeManager [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ITaskManager _task = default!; [Dependency] private readonly IRuntimeLog _runtimeLog = default!; + [Dependency] private readonly UserDbDataManager _userDb = default!; private ISawmill _sawmill = default!; @@ -445,4 +446,10 @@ private sealed class PlayTimeData /// public readonly HashSet DbTrackersDirty = new(); } + + void IPostInjectInit.PostInject() + { + _userDb.AddOnLoadPlayer(LoadData); + _userDb.AddOnPlayerDisconnect(ClientDisconnected); + } } diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs index e594e4de2696..c3142a709a76 100644 --- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs +++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs @@ -4,7 +4,9 @@ using Content.Server.Afk; using Content.Server.Afk.Events; using Content.Server.GameTicking; +using Content.Server.GameTicking.Events; using Content.Server.Mind; +using Content.Server.Station.Events; using Content.Shared.CCVar; using Content.Shared.GameTicking; using Content.Shared.Mobs; @@ -12,7 +14,6 @@ using Content.Shared.Players; using Content.Shared.Players.PlayTimeTracking; using Content.Shared.Roles; -using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Network; @@ -50,6 +51,9 @@ public override void Initialize() SubscribeLocalEvent(OnUnAFK); SubscribeLocalEvent(OnMobStateChanged); SubscribeLocalEvent(OnPlayerJoinedLobby); + SubscribeLocalEvent(OnStationJobsGetCandidates); + SubscribeLocalEvent(OnIsJobAllowed); + SubscribeLocalEvent(OnGetDisallowedJobs); _adminManager.OnPermsChanged += AdminPermsChanged; } @@ -174,6 +178,22 @@ private void OnPlayerJoinedLobby(PlayerJoinedLobbyEvent ev) _tracking.QueueSendTimers(ev.PlayerSession); } + private void OnStationJobsGetCandidates(ref StationJobsGetCandidatesEvent ev) + { + RemoveDisallowedJobs(ev.Player, ev.Jobs); + } + + private void OnIsJobAllowed(ref IsJobAllowedEvent ev) + { + if (!IsAllowed(ev.Player, ev.JobId)) + ev.Cancelled = true; + } + + private void OnGetDisallowedJobs(ref GetDisallowedJobsEvent ev) + { + ev.Jobs.UnionWith(GetDisallowedJobs(ev.Player)); + } + public bool IsAllowed(ICommonSession player, string role) { if (!_prototypes.TryIndex(role, out var job) || @@ -190,9 +210,9 @@ public bool IsAllowed(ICommonSession player, string role) return JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes); } - public HashSet GetDisallowedJobs(ICommonSession player) + public HashSet> GetDisallowedJobs(ICommonSession player) { - var roles = new HashSet(); + var roles = new HashSet>(); if (!_cfg.GetCVar(CCVars.GameRoleTimers)) return roles; @@ -222,7 +242,7 @@ public HashSet GetDisallowedJobs(ICommonSession player) return roles; } - public void RemoveDisallowedJobs(NetUserId userId, ref List jobs) + public void RemoveDisallowedJobs(NetUserId userId, List> jobs) { if (!_cfg.GetCVar(CCVars.GameRoleTimers)) return; @@ -239,7 +259,7 @@ public void RemoveDisallowedJobs(NetUserId userId, ref List jobs) { var job = jobs[i]; - if (!_prototypes.TryIndex(job, out var jobber) || + if (!_prototypes.TryIndex(job, out var jobber) || jobber.Requirements == null || jobber.Requirements.Count == 0) continue; diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index e32af589e958..f7c15a234058 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -3,26 +3,21 @@ using System.Threading; using System.Threading.Tasks; using Content.Server.Database; -using Content.Server.Humanoid; using Content.Shared.CCVar; -using Content.Shared.Humanoid.Prototypes; using Content.Shared.Preferences; -using Content.Shared.Roles; using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Network; using Robust.Shared.Player; -using Robust.Shared.Prototypes; using Robust.Shared.Utility; - namespace Content.Server.Preferences.Managers { /// /// Sends before the client joins the lobby. /// Receives and at any time. /// - public sealed class ServerPreferencesManager : IServerPreferencesManager + public sealed class ServerPreferencesManager : IServerPreferencesManager, IPostInjectInit { [Dependency] private readonly IServerNetManager _netManager = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; @@ -30,6 +25,7 @@ public sealed class ServerPreferencesManager : IServerPreferencesManager [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IDependencyCollection _dependencies = default!; [Dependency] private readonly ILogManager _log = default!; + [Dependency] private readonly UserDbDataManager _userDb = default!; // Cache player prefs on the server so we don't need as much async hell related to them. private readonly Dictionary _cachedPlayerPrefs = @@ -326,5 +322,12 @@ private sealed class PlayerPrefData public bool PrefsLoaded; public PlayerPreferences? Prefs; } + + void IPostInjectInit.PostInject() + { + _userDb.AddOnLoadPlayer(LoadData); + _userDb.AddOnFinishLoad(FinishLoad); + _userDb.AddOnPlayerDisconnect(OnClientDisconnected); + } } } diff --git a/Content.Server/Station/Events/StationJobsGetCandidatesEvent.cs b/Content.Server/Station/Events/StationJobsGetCandidatesEvent.cs new file mode 100644 index 000000000000..58d860b1e121 --- /dev/null +++ b/Content.Server/Station/Events/StationJobsGetCandidatesEvent.cs @@ -0,0 +1,8 @@ +using Content.Shared.Roles; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; + +namespace Content.Server.Station.Events; + +[ByRefEvent] +public readonly record struct StationJobsGetCandidatesEvent(NetUserId Player, List> Jobs); diff --git a/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs b/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs index 4b7cd64961b1..c3c3865c7bfa 100644 --- a/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs +++ b/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs @@ -2,6 +2,7 @@ using Content.Server.Administration.Managers; using Content.Server.Players.PlayTimeTracking; using Content.Server.Station.Components; +using Content.Server.Station.Events; using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Shared.Network; @@ -342,8 +343,9 @@ private Dictionary> GetPlayersJobCandidates(int? weight, foreach (var (player, profile) in profiles) { var roleBans = _banManager.GetJobBans(player); - var profileJobs = profile.JobPriorities.Keys.ToList(); - _playTime.RemoveDisallowedJobs(player, ref profileJobs); + var profileJobs = profile.JobPriorities.Keys.Select(k => new ProtoId(k)).ToList(); + var ev = new StationJobsGetCandidatesEvent(player, profileJobs); + RaiseLocalEvent(ref ev); List? availableJobs = null; @@ -354,7 +356,7 @@ private Dictionary> GetPlayersJobCandidates(int? weight, if (!(priority == selectedPriority || selectedPriority is null)) continue; - if (!_prototypeManager.TryIndex(jobId, out JobPrototype? job)) + if (!_prototypeManager.TryIndex(jobId, out var job)) continue; if (weight is not null && job.Weight != weight.Value) diff --git a/Content.Server/Station/Systems/StationJobsSystem.cs b/Content.Server/Station/Systems/StationJobsSystem.cs index debac8902e2c..3bfa815af1ef 100644 --- a/Content.Server/Station/Systems/StationJobsSystem.cs +++ b/Content.Server/Station/Systems/StationJobsSystem.cs @@ -428,7 +428,7 @@ public IReadOnlySet GetOverflowJobs(EntityUid station, StationJobsCompon /// Whether or not to pick from the overflow list. /// A set of disallowed jobs, if any. /// The selected job, if any. - public string? PickBestAvailableJobWithPriority(EntityUid station, IReadOnlyDictionary jobPriorities, bool pickOverflows, IReadOnlySet? disallowedJobs = null) + public string? PickBestAvailableJobWithPriority(EntityUid station, IReadOnlyDictionary jobPriorities, bool pickOverflows, IReadOnlySet>? disallowedJobs = null) { if (station == EntityUid.Invalid) return null; diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 5ca34945d88a..f41e0a1e6f10 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -225,6 +225,12 @@ public static readonly CVarDef public static readonly CVarDef GameRoleTimers = CVarDef.Create("game.role_timers", true, CVar.SERVER | CVar.REPLICATED); + /// + /// If roles should be restricted based on whether or not they are whitelisted. + /// + public static readonly CVarDef + GameRoleWhitelist = CVarDef.Create("game.role_whitelist", true, CVar.SERVER | CVar.REPLICATED); + /// /// Whether or not disconnecting inside of a cryopod should remove the character or just store them until they reconnect. /// diff --git a/Content.Shared/Players/JobWhitelist/MsgJobWhitelist.cs b/Content.Shared/Players/JobWhitelist/MsgJobWhitelist.cs new file mode 100644 index 000000000000..8347e2d706a2 --- /dev/null +++ b/Content.Shared/Players/JobWhitelist/MsgJobWhitelist.cs @@ -0,0 +1,33 @@ +using Lidgren.Network; +using Robust.Shared.Network; +using Robust.Shared.Serialization; + +namespace Content.Shared.Players.JobWhitelist; + +public sealed class MsgJobWhitelist : NetMessage +{ + public override MsgGroups MsgGroup => MsgGroups.EntityEvent; + + public HashSet Whitelist = new(); + + public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) + { + var count = buffer.ReadVariableInt32(); + Whitelist.EnsureCapacity(count); + + for (var i = 0; i < count; i++) + { + Whitelist.Add(buffer.ReadString()); + } + } + + public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) + { + buffer.WriteVariableInt32(Whitelist.Count); + + foreach (var ban in Whitelist) + { + buffer.Write(ban); + } + } +} diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index 34a8ce64bffb..2959b564972a 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -1,10 +1,8 @@ using Content.Shared.Access; using Content.Shared.Players.PlayTimeTracking; -using Content.Shared.Roles; using Content.Shared.StatusIcon; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Shared.Roles { @@ -116,6 +114,9 @@ public sealed partial class JobPrototype : IPrototype [DataField("extendedAccessGroups")] public IReadOnlyCollection> ExtendedAccessGroups { get; private set; } = Array.Empty>(); + + [DataField] + public bool Whitelisted; } /// diff --git a/Resources/Locale/en-US/commands/job-whitelist-command.ftl b/Resources/Locale/en-US/commands/job-whitelist-command.ftl new file mode 100644 index 000000000000..7fa20f03dec5 --- /dev/null +++ b/Resources/Locale/en-US/commands/job-whitelist-command.ftl @@ -0,0 +1,20 @@ +cmd-jobwhitelist-job-does-not-exist = Job {$job} does not exist. +cmd-jobwhitelist-player-not-found = Player {$player} not found. +cmd-jobwhitelist-hint-player = [player] +cmd-jobwhitelist-hint-job = [job] + +cmd-jobwhitelistadd-desc = Lets a player play a whitelisted job. +cmd-jobwhitelistadd-help = Usage: jobwhitelistadd +cmd-jobwhitelistadd-already-whitelisted = {$player} is already whitelisted to play as {$jobId} .({$jobName}). +cmd-jobwhitelistadd-added = Added {$player} to the {$jobId} ({$jobName}) whitelist. + +cmd-jobwhitelistget-desc = Gets all the jobs that a player has been whitelisted for. +cmd-jobwhitelistget-help = Usage: jobwhitelistadd +cmd-jobwhitelistget-whitelisted-none = Player {$player} is not whitelisted for any jobs. +cmd-jobwhitelistget-whitelisted-for = "Player {$player} is whitelisted for: +{$jobs}" + +cmd-jobwhitelistremove-desc = Removes a player's ability to play a whitelisted job. +cmd-jobwhitelistremove-help = Usage: jobwhitelistadd +cmd-jobwhitelistremove-was-not-whitelisted = {$player} was not whitelisted to play as {$jobId} ({$jobName}). +cmd-jobwhitelistremove-removed = Removed {$player} from the whitelist for {$jobId} ({$jobName}). diff --git a/Resources/Locale/en-US/job/role-whitelist.ftl b/Resources/Locale/en-US/job/role-whitelist.ftl new file mode 100644 index 000000000000..3149f182b6a6 --- /dev/null +++ b/Resources/Locale/en-US/job/role-whitelist.ftl @@ -0,0 +1 @@ +role-not-whitelisted = You are not whitelisted to play this role. From be6f55a09013ce88d7b57518ff283df0977aded9 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sat, 1 Jun 2024 11:29:17 -0400 Subject: [PATCH 198/568] Clean up store system (#28463) --- .../Store/Ui/StoreBoundUserInterface.cs | 24 +++----- Content.Client/Store/Ui/StoreMenu.xaml.cs | 5 +- .../GameTicking/Rules/NukeopsRuleSystem.cs | 1 + .../Implants/SubdermalImplantSystem.cs | 1 + Content.Server/PDA/PdaSystem.cs | 8 ++- Content.Server/PDA/Ringer/RingerSystem.cs | 1 + .../Revenant/EntitySystems/RevenantSystem.cs | 1 + .../Store/Conditions/BuyBeforeCondition.cs | 1 + .../Store/Systems/StoreSystem.Command.cs | 4 +- .../Store/Systems/StoreSystem.Listings.cs | 11 +++- .../Store/Systems/StoreSystem.Refund.cs | 1 + .../Store/Systems/StoreSystem.Ui.cs | 9 +-- Content.Server/Store/Systems/StoreSystem.cs | 42 +------------- .../SurplusBundle/SurplusBundleComponent.cs | 13 +---- .../SurplusBundle/SurplusBundleSystem.cs | 57 ++++++++---------- .../Traitor/Uplink/UplinkComponent.cs | 7 +++ Content.Server/Traitor/Uplink/UplinkSystem.cs | 18 +----- .../Store/Components/StoreComponent.cs | 34 +++++------ Content.Shared/Store/StoreUi.cs | 14 ----- Resources/Locale/en-US/store/store.ftl | 3 + .../Catalog/Fills/Crates/syndicate.yml | 4 +- .../Entities/Objects/Devices/pda.yml | 2 +- .../Entities/Objects/Magic/books.yml | 6 +- .../Objects/Misc/subdermal_implants.yml | 3 +- .../Entities/Objects/Specific/syndicate.yml | 8 +-- Resources/Prototypes/Store/presets.yml | 58 +++++++++++-------- 26 files changed, 127 insertions(+), 209 deletions(-) create mode 100644 Content.Server/Traitor/Uplink/UplinkComponent.cs rename {Content.Server => Content.Shared}/Store/Components/StoreComponent.cs (72%) diff --git a/Content.Client/Store/Ui/StoreBoundUserInterface.cs b/Content.Client/Store/Ui/StoreBoundUserInterface.cs index 88ad0e3de8b5..0010aedd9647 100644 --- a/Content.Client/Store/Ui/StoreBoundUserInterface.cs +++ b/Content.Client/Store/Ui/StoreBoundUserInterface.cs @@ -1,6 +1,7 @@ using Content.Shared.Store; using JetBrains.Annotations; using System.Linq; +using Content.Shared.Store.Components; using Robust.Shared.Prototypes; namespace Content.Client.Store.Ui; @@ -13,9 +14,6 @@ public sealed class StoreBoundUserInterface : BoundUserInterface [ViewVariables] private StoreMenu? _menu; - [ViewVariables] - private string _windowName = Loc.GetString("store-ui-default-title"); - [ViewVariables] private string _search = string.Empty; @@ -28,7 +26,9 @@ public StoreBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) protected override void Open() { - _menu = new StoreMenu(_windowName); + _menu = new StoreMenu(); + if (EntMan.TryGetComponent(Owner, out var store)) + _menu.Title = Loc.GetString(store.Name); _menu.OpenCentered(); _menu.OnClose += Close; @@ -64,25 +64,15 @@ protected override void UpdateState(BoundUserInterfaceState state) { base.UpdateState(state); - if (_menu == null) - return; - switch (state) { case StoreUpdateState msg: _listings = msg.Listings; - _menu.UpdateBalance(msg.Balance); + _menu?.UpdateBalance(msg.Balance); UpdateListingsWithSearchFilter(); - _menu.SetFooterVisibility(msg.ShowFooter); - _menu.UpdateRefund(msg.AllowRefund); - break; - case StoreInitializeState msg: - _windowName = msg.Name; - if (_menu != null && _menu.Window != null) - { - _menu.Window.Title = msg.Name; - } + _menu?.SetFooterVisibility(msg.ShowFooter); + _menu?.UpdateRefund(msg.AllowRefund); break; } } diff --git a/Content.Client/Store/Ui/StoreMenu.xaml.cs b/Content.Client/Store/Ui/StoreMenu.xaml.cs index b7a2c285fe5d..388b31291c19 100644 --- a/Content.Client/Store/Ui/StoreMenu.xaml.cs +++ b/Content.Client/Store/Ui/StoreMenu.xaml.cs @@ -32,7 +32,7 @@ public sealed partial class StoreMenu : DefaultWindow private List _cachedListings = new(); - public StoreMenu(string name) + public StoreMenu() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); @@ -40,9 +40,6 @@ public StoreMenu(string name) WithdrawButton.OnButtonDown += OnWithdrawButtonDown; RefundButton.OnButtonDown += OnRefundButtonDown; SearchBar.OnTextChanged += _ => SearchTextUpdated?.Invoke(this, SearchBar.Text); - - if (Window != null) - Window.Title = name; } public void UpdateBalance(Dictionary, FixedPoint2> balance) diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index d6f1c3c619ab..1b62778d7585 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -25,6 +25,7 @@ using Robust.Shared.Utility; using System.Linq; using Content.Server.GameTicking.Components; +using Content.Shared.Store.Components; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index e8af08b2ebbb..88c5fb945925 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -20,6 +20,7 @@ using System.Numerics; using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Store.Components; using Robust.Shared.Collections; using Robust.Shared.Map.Components; diff --git a/Content.Server/PDA/PdaSystem.cs b/Content.Server/PDA/PdaSystem.cs index d4934ee24e5f..43bf571eb456 100644 --- a/Content.Server/PDA/PdaSystem.cs +++ b/Content.Server/PDA/PdaSystem.cs @@ -8,6 +8,7 @@ using Content.Server.Station.Systems; using Content.Server.Store.Components; using Content.Server.Store.Systems; +using Content.Server.Traitor.Uplink; using Content.Shared.Access.Components; using Content.Shared.CartridgeLoader; using Content.Shared.Chat; @@ -15,6 +16,7 @@ using Content.Shared.Light.Components; using Content.Shared.Light.EntitySystems; using Content.Shared.PDA; +using Content.Shared.Store.Components; using Robust.Server.Containers; using Robust.Server.GameObjects; using Robust.Shared.Containers; @@ -152,7 +154,7 @@ public void UpdatePdaUi(EntityUid uid, PdaComponent? pda = null) var address = GetDeviceNetAddress(uid); var hasInstrument = HasComp(uid); - var showUplink = HasComp(uid) && IsUnlocked(uid); + var showUplink = HasComp(uid) && IsUnlocked(uid); UpdateStationName(uid, pda); UpdateAlertLevel(uid, pda); @@ -237,8 +239,8 @@ private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaShowUplinkMessage m return; // check if its locked again to prevent malicious clients opening locked uplinks - if (TryComp(uid, out var store) && IsUnlocked(uid)) - _store.ToggleUi(msg.Actor, uid, store); + if (HasComp(uid) && IsUnlocked(uid)) + _store.ToggleUi(msg.Actor, uid); } private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaLockUplinkMessage msg) diff --git a/Content.Server/PDA/Ringer/RingerSystem.cs b/Content.Server/PDA/Ringer/RingerSystem.cs index 47ae41896e22..e15dcfaa2bc7 100644 --- a/Content.Server/PDA/Ringer/RingerSystem.cs +++ b/Content.Server/PDA/Ringer/RingerSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.PDA.Ringer; using Content.Shared.Popups; using Content.Shared.Store; +using Content.Shared.Store.Components; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Network; diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index c390432f3a1b..a05105662d4d 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -17,6 +17,7 @@ using Content.Shared.Revenant; using Content.Shared.Revenant.Components; using Content.Shared.StatusEffect; +using Content.Shared.Store.Components; using Content.Shared.Stunnable; using Content.Shared.Tag; using Robust.Server.GameObjects; diff --git a/Content.Server/Store/Conditions/BuyBeforeCondition.cs b/Content.Server/Store/Conditions/BuyBeforeCondition.cs index 132f3534391f..3f0c2de2e18d 100644 --- a/Content.Server/Store/Conditions/BuyBeforeCondition.cs +++ b/Content.Server/Store/Conditions/BuyBeforeCondition.cs @@ -1,6 +1,7 @@ using Content.Server.Store.Components; using Content.Server.Store.Systems; using Content.Shared.Store; +using Content.Shared.Store.Components; using Robust.Shared.Prototypes; namespace Content.Server.Store.Conditions; diff --git a/Content.Server/Store/Systems/StoreSystem.Command.cs b/Content.Server/Store/Systems/StoreSystem.Command.cs index d259da2c95e8..5ad361eb4279 100644 --- a/Content.Server/Store/Systems/StoreSystem.Command.cs +++ b/Content.Server/Store/Systems/StoreSystem.Command.cs @@ -1,7 +1,9 @@ +using System.Linq; using Content.Server.Store.Components; using Content.Shared.FixedPoint; using Content.Server.Administration; using Content.Shared.Administration; +using Content.Shared.Store.Components; using Robust.Shared.Console; namespace Content.Server.Store.Systems; @@ -58,7 +60,7 @@ private CompletionResult AddCurrencyCommandCompletions(IConsoleShell shell, stri if (args.Length == 2 && NetEntity.TryParse(args[0], out var uidNet) && TryGetEntity(uidNet, out var uid)) { if (TryComp(uid, out var store)) - return CompletionResult.FromHintOptions(store.CurrencyWhitelist, ""); + return CompletionResult.FromHintOptions(store.CurrencyWhitelist.Select(p => p.ToString()), ""); } return CompletionResult.Empty; diff --git a/Content.Server/Store/Systems/StoreSystem.Listings.cs b/Content.Server/Store/Systems/StoreSystem.Listings.cs index a56d9640d371..10b53a7c94b8 100644 --- a/Content.Server/Store/Systems/StoreSystem.Listings.cs +++ b/Content.Server/Store/Systems/StoreSystem.Listings.cs @@ -1,5 +1,6 @@ -using Content.Server.Store.Components; using Content.Shared.Store; +using Content.Shared.Store.Components; +using Robust.Shared.Prototypes; namespace Content.Server.Store.Systems; @@ -80,7 +81,11 @@ public IEnumerable GetAvailableListings(EntityUid buyer, EntityUid /// What categories to filter by. /// The physial entity of the store. Can be null. /// The available listings. - public IEnumerable GetAvailableListings(EntityUid buyer, HashSet? listings, HashSet categories, EntityUid? storeEntity = null) + public IEnumerable GetAvailableListings( + EntityUid buyer, + HashSet? listings, + HashSet> categories, + EntityUid? storeEntity = null) { listings ??= GetAllListings(); @@ -117,7 +122,7 @@ public IEnumerable GetAvailableListings(EntityUid buyer, HashSet
  • The listing itself. /// The categories to check through. /// If the listing was present in one of the categories. - public bool ListingHasCategory(ListingData listing, HashSet categories) + public bool ListingHasCategory(ListingData listing, HashSet> categories) { foreach (var cat in categories) { diff --git a/Content.Server/Store/Systems/StoreSystem.Refund.cs b/Content.Server/Store/Systems/StoreSystem.Refund.cs index 5a8be4be2bbd..4e823582e6e8 100644 --- a/Content.Server/Store/Systems/StoreSystem.Refund.cs +++ b/Content.Server/Store/Systems/StoreSystem.Refund.cs @@ -1,4 +1,5 @@ using Content.Server.Store.Components; +using Content.Shared.Store.Components; using Robust.Shared.Containers; namespace Content.Server.Store.Systems; diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index 0a1a8d19f318..983d68d0af73 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -10,6 +10,7 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Mind; using Content.Shared.Store; +using Content.Shared.Store.Components; using Content.Shared.UserInterface; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; @@ -82,16 +83,11 @@ public void CloseUi(EntityUid uid, StoreComponent? component = null) /// The person who if opening the store ui. Listings are filtered based on this. /// The store entity itself /// The store component being refreshed. - /// public void UpdateUserInterface(EntityUid? user, EntityUid store, StoreComponent? component = null) { if (!Resolve(store, ref component)) return; - // TODO: Why is the state not being set unless this? - if (!_ui.HasUi(store, StoreUiKey.Key)) - return; - //this is the person who will be passed into logic for all listing filtering. if (user != null) //if we have no "buyer" for this update, then don't update the listings { @@ -259,7 +255,8 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi } //log dat shit. - _admin.Add(LogType.StorePurchase, LogImpact.Low, + _admin.Add(LogType.StorePurchase, + LogImpact.Low, $"{ToPrettyString(buyer):player} purchased listing \"{ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, _prototypeManager)}\" from {ToPrettyString(uid)}"); listing.PurchaseAmount++; //track how many times something has been purchased diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index e310194778b0..0fd92cfb9651 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -5,11 +5,10 @@ using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Stacks; -using Content.Shared.Store; using JetBrains.Annotations; -using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using System.Linq; +using Content.Shared.Store.Components; using Robust.Shared.Utility; namespace Content.Server.Store.Systems; @@ -44,7 +43,6 @@ public override void Initialize() private void OnMapInit(EntityUid uid, StoreComponent component, MapInitEvent args) { RefreshAllListings(component); - InitializeFromPreset(component.Preset, uid, component); component.StartingMap = Transform(uid).MapUid; } @@ -54,7 +52,6 @@ private void OnStartup(EntityUid uid, StoreComponent component, ComponentStartup if (MetaData(uid).EntityLifeStage == EntityLifeStage.MapInitialized) { RefreshAllListings(component); - InitializeFromPreset(component.Preset, uid, component); } var ev = new StoreAddedEvent(); @@ -167,43 +164,6 @@ public bool TryAddCurrency(Dictionary currency, EntityUid u UpdateUserInterface(null, uid, store); return true; } - - /// - /// Initializes a store based on a preset ID - /// - /// The ID of a store preset prototype - /// - /// The store being initialized - public void InitializeFromPreset(string? preset, EntityUid uid, StoreComponent component) - { - if (preset == null) - return; - - if (!_proto.TryIndex(preset, out var proto)) - return; - - InitializeFromPreset(proto, uid, component); - } - - /// - /// Initializes a store based on a given preset - /// - /// The StorePresetPrototype - /// - /// The store being initialized - public void InitializeFromPreset(StorePresetPrototype preset, EntityUid uid, StoreComponent component) - { - component.Preset = preset.ID; - component.CurrencyWhitelist.UnionWith(preset.CurrencyWhitelist); - component.Categories.UnionWith(preset.Categories); - if (component.Balance == new Dictionary() && preset.InitialBalance != null) //if we don't have a value stored, use the preset - TryAddCurrency(preset.InitialBalance, uid, component); - - if (_ui.HasUi(uid, StoreUiKey.Key)) - { - _ui.SetUiState(uid, StoreUiKey.Key, new StoreInitializeState(preset.StoreName)); - } - } } public sealed class CurrencyInsertAttemptEvent : CancellableEntityEventArgs diff --git a/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleComponent.cs b/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleComponent.cs index 47ce68625a19..120581cf82d6 100644 --- a/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleComponent.cs +++ b/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleComponent.cs @@ -1,6 +1,3 @@ -using Content.Shared.Store; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - namespace Content.Server.Traitor.Uplink.SurplusBundle; /// @@ -12,14 +9,6 @@ public sealed partial class SurplusBundleComponent : Component /// /// Total price of all content inside bundle. /// - [ViewVariables(VVAccess.ReadOnly)] - [DataField("totalPrice")] + [DataField] public int TotalPrice = 20; - - /// - /// The preset that will be used to get all the listings. - /// Currently just defaults to the basic uplink. - /// - [DataField("storePreset", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string StorePreset = "StorePresetUplink"; } diff --git a/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs b/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs index 5c0a56d346c4..759cad5dedae 100644 --- a/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs +++ b/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs @@ -3,76 +3,67 @@ using Content.Server.Store.Systems; using Content.Shared.FixedPoint; using Content.Shared.Store; -using Robust.Shared.Prototypes; +using Content.Shared.Store.Components; using Robust.Shared.Random; namespace Content.Server.Traitor.Uplink.SurplusBundle; public sealed class SurplusBundleSystem : EntitySystem { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly StoreSystem _store = default!; - private ListingData[] _listings = default!; - public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnMapInit); - - SubscribeLocalEvent(OnInit); - } - private void OnInit(EntityUid uid, SurplusBundleComponent component, ComponentInit args) - { - var storePreset = _prototypeManager.Index(component.StorePreset); - - _listings = _store.GetAvailableListings(uid, null, storePreset.Categories).ToArray(); - - Array.Sort(_listings, (a, b) => (int) (b.Cost.Values.Sum() - a.Cost.Values.Sum())); //this might get weird with multicurrency but don't think about it + SubscribeLocalEvent(OnMapInit); } private void OnMapInit(EntityUid uid, SurplusBundleComponent component, MapInitEvent args) { - FillStorage(uid, component); - } - - private void FillStorage(EntityUid uid, SurplusBundleComponent? component = null) - { - if (!Resolve(uid, ref component)) + if (!TryComp(uid, out var store)) return; - var cords = Transform(uid).Coordinates; + FillStorage((uid, component, store)); + } - var content = GetRandomContent(component.TotalPrice); + private void FillStorage(Entity ent) + { + var cords = Transform(ent).Coordinates; + var content = GetRandomContent(ent); foreach (var item in content) { - var ent = EntityManager.SpawnEntity(item.ProductEntity, cords); - _entityStorage.Insert(ent, uid); + var dode = Spawn(item.ProductEntity, cords); + _entityStorage.Insert(dode, ent); } } // wow, is this leetcode reference? - private List GetRandomContent(FixedPoint2 targetCost) + private List GetRandomContent(Entity ent) { var ret = new List(); - if (_listings.Length == 0) + + var listings = _store.GetAvailableListings(ent, null, ent.Comp2.Categories) + .OrderBy(p => p.Cost.Values.Sum()) + .ToList(); + + if (listings.Count == 0) return ret; var totalCost = FixedPoint2.Zero; var index = 0; - while (totalCost < targetCost) + while (totalCost < ent.Comp1.TotalPrice) { // All data is sorted in price descending order // Find new item with the lowest acceptable price // All expansive items will be before index, all acceptable after - var remainingBudget = targetCost - totalCost; - while (_listings[index].Cost.Values.Sum() > remainingBudget) + var remainingBudget = ent.Comp1.TotalPrice - totalCost; + while (listings[index].Cost.Values.Sum() > remainingBudget) { index++; - if (index >= _listings.Length) + if (index >= listings.Count) { // Looks like no cheap items left // It shouldn't be case for ss14 content @@ -82,8 +73,8 @@ private List GetRandomContent(FixedPoint2 targetCost) } // Select random listing and add into crate - var randomIndex = _random.Next(index, _listings.Length); - var randomItem = _listings[randomIndex]; + var randomIndex = _random.Next(index, listings.Count); + var randomItem = listings[randomIndex]; ret.Add(randomItem); totalCost += randomItem.Cost.Values.Sum(); } diff --git a/Content.Server/Traitor/Uplink/UplinkComponent.cs b/Content.Server/Traitor/Uplink/UplinkComponent.cs new file mode 100644 index 000000000000..35f11ce9ef76 --- /dev/null +++ b/Content.Server/Traitor/Uplink/UplinkComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server.Traitor.Uplink; + +/// +/// This is used for identifying something as a hidden uplink and showing the UI. +/// +[RegisterComponent] +public sealed partial class UplinkComponent : Component; diff --git a/Content.Server/Traitor/Uplink/UplinkSystem.cs b/Content.Server/Traitor/Uplink/UplinkSystem.cs index 5670e28ec999..7c39f1ed666a 100644 --- a/Content.Server/Traitor/Uplink/UplinkSystem.cs +++ b/Content.Server/Traitor/Uplink/UplinkSystem.cs @@ -5,6 +5,7 @@ using Content.Server.Store.Components; using Content.Shared.FixedPoint; using Content.Shared.Store; +using Content.Shared.Store.Components; namespace Content.Server.Traitor.Uplink { @@ -17,18 +18,6 @@ public sealed class UplinkSystem : EntitySystem [ValidatePrototypeId] public const string TelecrystalCurrencyPrototype = "Telecrystal"; - /// - /// Gets the amount of TC on an "uplink" - /// Mostly just here for legacy systems based on uplink. - /// - /// - /// the amount of TC - public int GetTCBalance(StoreComponent component) - { - FixedPoint2? tcBalance = component.Balance.GetValueOrDefault(TelecrystalCurrencyPrototype); - return tcBalance?.Int() ?? 0; - } - /// /// Adds an uplink to the target /// @@ -37,7 +26,7 @@ public int GetTCBalance(StoreComponent component) /// The id of the storepreset /// The entity that will actually have the uplink functionality. Defaults to the PDA if null. /// Whether or not the uplink was added successfully - public bool AddUplink(EntityUid user, FixedPoint2? balance, string uplinkPresetId = "StorePresetUplink", EntityUid? uplinkEntity = null) + public bool AddUplink(EntityUid user, FixedPoint2? balance, EntityUid? uplinkEntity = null) { // Try to find target item if (uplinkEntity == null) @@ -47,11 +36,10 @@ public bool AddUplink(EntityUid user, FixedPoint2? balance, string uplinkPresetI return false; } + EnsureComp(uplinkEntity.Value); var store = EnsureComp(uplinkEntity.Value); - _store.InitializeFromPreset(uplinkPresetId, uplinkEntity.Value, store); store.AccountOwner = user; store.Balance.Clear(); - if (balance != null) { store.Balance.Clear(); diff --git a/Content.Server/Store/Components/StoreComponent.cs b/Content.Shared/Store/Components/StoreComponent.cs similarity index 72% rename from Content.Server/Store/Components/StoreComponent.cs rename to Content.Shared/Store/Components/StoreComponent.cs index 0b7dbbea0942..223f50797171 100644 --- a/Content.Server/Store/Components/StoreComponent.cs +++ b/Content.Shared/Store/Components/StoreComponent.cs @@ -1,46 +1,41 @@ using Content.Shared.FixedPoint; -using Content.Shared.Store; using Robust.Shared.Audio; -using Robust.Shared.Map; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; -namespace Content.Server.Store.Components; +namespace Content.Shared.Store.Components; /// /// This component manages a store which players can use to purchase different listings /// through the ui. The currency, listings, and categories are defined in yaml. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent] public sealed partial class StoreComponent : Component { - /// - /// The default preset for the store. Is overriden by default values specified on the component. - /// - [DataField("preset", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? Preset; + [DataField] + public LocId Name = "store-ui-default-title"; /// /// All the listing categories that are available on this store. /// The available listings are partially based on the categories. /// - [DataField("categories", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet Categories = new(); + [DataField] + public HashSet> Categories = new(); /// /// The total amount of currency that can be used in the store. /// The string represents the ID of te currency prototype, where the /// float is that amount. /// - [ViewVariables(VVAccess.ReadWrite), DataField("balance", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public Dictionary Balance = new(); + [DataField] + public Dictionary, FixedPoint2> Balance = new(); /// /// The list of currencies that can be inserted into this store. /// - [ViewVariables(VVAccess.ReadOnly), DataField("currencyWhitelist", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet CurrencyWhitelist = new(); + [DataField] + public HashSet> CurrencyWhitelist = new(); /// /// The person who "owns" the store/account. Used if you want the listings to be fixed @@ -52,6 +47,7 @@ public sealed partial class StoreComponent : Component /// /// All listings, including those that aren't available to the buyer /// + [DataField] public HashSet Listings = new(); /// @@ -70,7 +66,7 @@ public sealed partial class StoreComponent : Component /// The total balance spent in this store. Used for refunds. /// [ViewVariables, DataField] - public Dictionary BalanceSpent = new(); + public Dictionary, FixedPoint2> BalanceSpent = new(); /// /// Controls if the store allows refunds @@ -95,7 +91,7 @@ public sealed partial class StoreComponent : Component /// /// The sound played to the buyer when a purchase is succesfully made. /// - [DataField("buySuccessSound")] + [DataField] public SoundSpecifier BuySuccessSound = new SoundPathSpecifier("/Audio/Effects/kaching.ogg"); #endregion } diff --git a/Content.Shared/Store/StoreUi.cs b/Content.Shared/Store/StoreUi.cs index ee4da6991f6c..59cf1bbbc818 100644 --- a/Content.Shared/Store/StoreUi.cs +++ b/Content.Shared/Store/StoreUi.cs @@ -30,20 +30,6 @@ public StoreUpdateState(HashSet listings, Dictionary -/// initializes miscellaneous data about the store. -/// -[Serializable, NetSerializable] -public sealed class StoreInitializeState : BoundUserInterfaceState -{ - public readonly string Name; - - public StoreInitializeState(string name) - { - Name = name; - } -} - [Serializable, NetSerializable] public sealed class StoreRequestUpdateInterfaceMessage : BoundUserInterfaceMessage { diff --git a/Resources/Locale/en-US/store/store.ftl b/Resources/Locale/en-US/store/store.ftl index 997afedfc063..5c1a46339e72 100644 --- a/Resources/Locale/en-US/store/store.ftl +++ b/Resources/Locale/en-US/store/store.ftl @@ -8,3 +8,6 @@ store-ui-traitor-warning = Operatives must lock their uplinks after use to avoid store-withdraw-button-ui = Withdraw {$currency} store-ui-button-out-of-stock = {""} (Out of Stock) store-not-account-owner = This {$store} is not bound to you! + +store-preset-name-uplink = Uplink +store-preset-name-spellbook = Spellbook diff --git a/Resources/Prototypes/Catalog/Fills/Crates/syndicate.yml b/Resources/Prototypes/Catalog/Fills/Crates/syndicate.yml index 3f9e909c809e..ba97af392507 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/syndicate.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/syndicate.yml @@ -1,6 +1,6 @@ - type: entity id: CrateSyndicateSurplusBundle - parent: CrateSyndicate + parent: [ CrateSyndicate, StorePresetUplink ] name: Syndicate surplus crate description: Contains 50 telecrystals worth of completely random Syndicate items. It can be useless junk or really good. components: @@ -24,7 +24,7 @@ - type: entity id: CrateSyndicateSuperSurplusBundle - parent: CrateSyndicate + parent: [ CrateSyndicate, StorePresetUplink ] name: Syndicate super surplus crate description: Contains 125 telecrystals worth of completely random Syndicate items. components: diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index b76ca6a14ad1..b1a6ab0b8fa6 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -1,6 +1,6 @@ - type: entity abstract: true - parent: BaseItem + parent: [ BaseItem, StorePresetUplink ] #PDA's have uplinks so they have to inherit the data. id: BasePDA name: PDA description: Personal Data Assistant. diff --git a/Resources/Prototypes/Entities/Objects/Magic/books.yml b/Resources/Prototypes/Entities/Objects/Magic/books.yml index 554c5214c199..e47fa00c45bf 100644 --- a/Resources/Prototypes/Entities/Objects/Magic/books.yml +++ b/Resources/Prototypes/Entities/Objects/Magic/books.yml @@ -25,7 +25,7 @@ id: WizardsGrimoire name: wizards grimoire suffix: Wizard - parent: BaseItem + parent: [ BaseItem, StorePresetSpellbook ] components: - type: Sprite sprite: Objects/Misc/books.rsi @@ -46,7 +46,6 @@ - type: Store refundAllowed: true ownerOnly: true # get your own tome! - preset: StorePresetSpellbook balance: WizCoin: 10 # prices are balanced around this 10 point maximum and how strong the spells are @@ -55,12 +54,11 @@ id: WizardsGrimoireNoRefund name: wizards grimoire suffix: Wizard, No Refund - parent: WizardsGrimoire + parent: [ WizardsGrimoire, StorePresetSpellbook ] components: - type: Store refundAllowed: false ownerOnly: true # get your own tome! - preset: StorePresetSpellbook balance: WizCoin: 10 # prices are balanced around this 10 point maximum and how strong the spells are diff --git a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml index c92985c2cbae..9690d0bdfe83 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml @@ -143,7 +143,7 @@ - Cuffable # useless if you cant be cuffed - type: entity - parent: BaseSubdermalImplant + parent: [ BaseSubdermalImplant, StorePresetUplink ] id: UplinkImplant name: uplink implant description: This implant lets the user access a hidden Syndicate uplink at will. @@ -155,7 +155,6 @@ components: - Hands # prevent mouse buying grenade penguin since its not telepathic - type: Store - preset: StorePresetUplink balance: Telecrystal: 0 - type: UserInterface diff --git a/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml b/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml index 459beeef1880..53d4f7953b17 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml @@ -48,7 +48,7 @@ # Uplinks - type: entity - parent: BaseItem + parent: [ BaseItem, StorePresetUplink ] id: BaseUplinkRadio name: syndicate uplink description: Suspiciously looking old radio... @@ -68,7 +68,6 @@ - type: ActivatableUI key: enum.StoreUiKey.Key - type: Store - preset: StorePresetUplink balance: Telecrystal: 0 @@ -78,7 +77,6 @@ suffix: 20 TC components: - type: Store - preset: StorePresetUplink balance: Telecrystal: 20 @@ -88,7 +86,6 @@ suffix: 25 TC components: - type: Store - preset: StorePresetUplink balance: Telecrystal: 25 @@ -99,7 +96,6 @@ suffix: 40 TC, NukeOps components: - type: Store - preset: StorePresetUplink balance: Telecrystal: 40 - type: Tag @@ -112,7 +108,6 @@ suffix: 60 TC, LoneOps components: - type: Store - preset: StorePresetUplink balance: Telecrystal: 60 - type: Tag @@ -125,6 +120,5 @@ suffix: DEBUG components: - type: Store - preset: StorePresetUplink balance: Telecrystal: 99999 diff --git a/Resources/Prototypes/Store/presets.yml b/Resources/Prototypes/Store/presets.yml index 166c29fe4165..762ed68921aa 100644 --- a/Resources/Prototypes/Store/presets.yml +++ b/Resources/Prototypes/Store/presets.yml @@ -1,29 +1,37 @@ -- type: storePreset +- type: entity id: StorePresetUplink - storeName: Uplink - categories: - - UplinkWeaponry - - UplinkAmmo - - UplinkExplosives - - UplinkChemicals - - UplinkDeception - - UplinkDisruption - - UplinkImplants - - UplinkAllies - - UplinkWearables - - UplinkJob - - UplinkPointless - currencyWhitelist: - - Telecrystal + abstract: true + components: + - type: Store + name: store-preset-name-uplink + categories: + - UplinkWeaponry + - UplinkAmmo + - UplinkExplosives + - UplinkChemicals + - UplinkDeception + - UplinkDisruption + - UplinkImplants + - UplinkAllies + - UplinkWearables + - UplinkJob + - UplinkPointless + currencyWhitelist: + - Telecrystal + balance: + Telecrystal: 0 -- type: storePreset +- type: entity id: StorePresetSpellbook - storeName: Spellbook - categories: - - SpellbookOffensive #Fireball, Rod Form - - SpellbookDefensive #Magic Missile, Wall of Force - - SpellbookUtility #Body Swap, Lich, Teleport, Knock, Polymorph - - SpellbookEquipment #Battlemage Robes, Staff of Locker - - SpellbookEvents #Summon Weapons, Summon Ghosts - currencyWhitelist: + abstract: true + components: + - type: Store + name: store-preset-name-spellbook + categories: + - SpellbookOffensive #Fireball, Rod Form + - SpellbookDefensive #Magic Missile, Wall of Force + - SpellbookUtility #Body Swap, Lich, Teleport, Knock, Polymorph + - SpellbookEquipment #Battlemage Robes, Staff of Locker + - SpellbookEvents #Summon Weapons, Summon Ghosts + currencyWhitelist: - WizCoin From 5bb0c4d6a23232103d9da97e7df9c4bbfce467cc Mon Sep 17 00:00:00 2001 From: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Date: Sat, 1 Jun 2024 10:29:46 -0700 Subject: [PATCH 199/568] Fixed bug where ID card computer defaulted to the atmos as the job icon. (#28462) Fixed ID atmos bug! --- Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs index 298912e7d536..82f6ebd8b59c 100644 --- a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs +++ b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs @@ -27,6 +27,9 @@ public sealed partial class IdCardConsoleWindow : DefaultWindow private string? _lastJobTitle; private string? _lastJobProto; + // The job that will be picked if the ID doesn't have a job on the station. + private static ProtoId _defaultJob = "Passenger"; + public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager, List> accessLevels) { @@ -65,7 +68,6 @@ public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeMana } JobPresetOptionButton.OnItemSelected += SelectJobPreset; - _accessButtons.Populate(accessLevels, prototypeManager); AccessLevelControlContainer.AddChild(_accessButtons); @@ -172,11 +174,15 @@ public void UpdateState(IdCardConsoleBoundUserInterfaceState state) new List>()); var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype); - if (jobIndex >= 0) + // If the job index is < 0 that means they don't have a job registered in the station records. + // For example, a new ID from a box would have no job index. + if (jobIndex < 0) { - JobPresetOptionButton.SelectId(jobIndex); + jobIndex = _jobPrototypeIds.IndexOf(_defaultJob); } + JobPresetOptionButton.SelectId(jobIndex); + _lastFullName = state.TargetIdFullName; _lastJobTitle = state.TargetIdJobTitle; _lastJobProto = state.TargetIdJobPrototype; From 81a328c543d9d7194570f2d176354aea9554e2a5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 1 Jun 2024 17:30:52 +0000 Subject: [PATCH 200/568] 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 2a808f32825b..449f9bf43b5c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Tayrtahn - changes: - - message: Reflected tranquilizer rounds no longer inject the character who reflected - them. - type: Fix - id: 6160 - time: '2024-03-15T13:57:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26141 - author: lzk228 changes: - message: Refill light replacer from box popup now shows properly. @@ -3853,3 +3845,11 @@ id: 6659 time: '2024-06-01T05:51:16.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28424 +- author: Beck Thompson + changes: + - message: ID computer will now select passenger as the default id icon instead + of atmospheric technician. + type: Fix + id: 6660 + time: '2024-06-01T17:29:46.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28462 From a29b6a6894f8197be392bfd1c90085b8ea80f86c Mon Sep 17 00:00:00 2001 From: Tornado Tech <54727692+Tornado-Technology@users.noreply.github.com> Date: Sun, 2 Jun 2024 03:46:35 +1000 Subject: [PATCH 201/568] Clean up new HTNs tasks (#28469) * Clean up new HTNs tasks * Added docs to math operations --- .../NPC/HTN/Preconditions/KeyExistsPrecondition.cs | 7 ++++++- .../NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs | 6 +++++- .../HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs | 7 ++++--- .../HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs | 8 ++++++-- .../Preconditions/Math/KeyFloatGreaterPrecondition.cs | 8 ++++++-- .../HTN/Preconditions/Math/KeyFloatLessPrecondition.cs | 8 ++++++-- .../PrimitiveTasks/Operators/Math/AddFloatOperator.cs | 5 +++-- .../HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs | 5 +++-- .../PrimitiveTasks/Operators/Math/SetFloatOperator.cs | 5 +++-- .../Operators/Math/SetRandomFloatOperator.cs | 8 +++++--- .../NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs | 9 +++++++-- .../NPC/HTN/PrimitiveTasks/Operators/SpeakOperator.cs | 7 ++++--- 12 files changed, 58 insertions(+), 25 deletions(-) diff --git a/Content.Server/NPC/HTN/Preconditions/KeyExistsPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/KeyExistsPrecondition.cs index 72c4e6367fef..69e265f2763d 100644 --- a/Content.Server/NPC/HTN/Preconditions/KeyExistsPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/KeyExistsPrecondition.cs @@ -1,8 +1,13 @@ namespace Content.Server.NPC.HTN.Preconditions; +/// +/// Checks for the presence of the value by the specified in the . +/// Returns true if there is a value. +/// public sealed partial class KeyExistsPrecondition : HTNPrecondition { - [DataField("key", required: true)] public string Key = string.Empty; + [DataField(required: true), ViewVariables] + public string Key = string.Empty; public override bool IsMet(NPCBlackboard blackboard) { diff --git a/Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs index c12663901c74..8dc38e442ab3 100644 --- a/Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs @@ -1,8 +1,12 @@ namespace Content.Server.NPC.HTN.Preconditions; +/// +/// Checks if there is no value at the specified in the . +/// Returns true if there is no value. +/// public sealed partial class KeyNotExistsPrecondition : HTNPrecondition { - [DataField(required: true)] + [DataField(required: true), ViewVariables] public string Key = string.Empty; public override bool IsMet(NPCBlackboard blackboard) diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs index 8c7920e8be5c..2abb351272ff 100644 --- a/Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs @@ -1,16 +1,17 @@ namespace Content.Server.NPC.HTN.Preconditions.Math; /// -/// Checks for the presence of data in the blackboard and makes a comparison with the specified boolean +/// Checks if there is a bool value for the specified +/// in the and the specified value is equal to the . /// public sealed partial class KeyBoolEqualsPrecondition : HTNPrecondition { [Dependency] private readonly IEntityManager _entManager = default!; - [DataField(required: true)] + [DataField(required: true), ViewVariables] public string Key = string.Empty; - [DataField(required: true)] + [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] public bool Value; public override bool IsMet(NPCBlackboard blackboard) diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs index 802fdaf2b9c3..0f7e2cca2af0 100644 --- a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs @@ -1,13 +1,17 @@ namespace Content.Server.NPC.HTN.Preconditions.Math; +/// +/// Checks if there is a float value for the specified +/// in the and the specified value is equal to the . +/// public sealed partial class KeyFloatEqualsPrecondition : HTNPrecondition { [Dependency] private readonly IEntityManager _entManager = default!; - [DataField(required: true)] + [DataField(required: true), ViewVariables] public string Key = string.Empty; - [DataField(required: true)] + [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] public float Value; public override bool IsMet(NPCBlackboard blackboard) diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs index 3a9ac366980a..b3a27a7d5deb 100644 --- a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs @@ -1,13 +1,17 @@ namespace Content.Server.NPC.HTN.Preconditions.Math; +/// +/// Checks if there is a float value for the specified +/// in the and the specified value is greater then . +/// public sealed partial class KeyFloatGreaterPrecondition : HTNPrecondition { [Dependency] private readonly IEntityManager _entManager = default!; - [DataField(required: true)] + [DataField(required: true), ViewVariables] public string Key = string.Empty; - [DataField(required: true)] + [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] public float Value; public override bool IsMet(NPCBlackboard blackboard) diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs index 5cd51d7a7c57..c5eb35eebc43 100644 --- a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs @@ -1,13 +1,17 @@ namespace Content.Server.NPC.HTN.Preconditions.Math; +/// +/// Checks if there is a float value for the specified +/// in the and the specified value is less then . +/// public sealed partial class KeyFloatLessPrecondition : HTNPrecondition { [Dependency] private readonly IEntityManager _entManager = default!; - [DataField(required: true)] + [DataField(required: true), ViewVariables] public string Key = string.Empty; - [DataField(required: true)] + [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] public float Value; public override bool IsMet(NPCBlackboard blackboard) diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs index 00404517c9e0..f8ed20544ecc 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs @@ -4,13 +4,14 @@ namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; /// -/// Gets the key, and adds the value to that float +/// Added to float value for the +/// specified in the . /// public sealed partial class AddFloatOperator : HTNOperator { [Dependency] private readonly IEntityManager _entManager = default!; - [DataField(required: true)] + [DataField(required: true), ViewVariables] public string TargetKey = string.Empty; [DataField, ViewVariables(VVAccess.ReadWrite)] diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs index a40b96798d4a..f168326af7b0 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs @@ -4,11 +4,12 @@ namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; /// -/// Just sets a blackboard key to a bool +/// Set to bool value for the +/// specified in the . /// public sealed partial class SetBoolOperator : HTNOperator { - [DataField(required: true)] + [DataField(required: true), ViewVariables] public string TargetKey = string.Empty; [DataField, ViewVariables(VVAccess.ReadWrite)] diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetFloatOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetFloatOperator.cs index 76842b431f70..f9b5e54fe3ee 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetFloatOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetFloatOperator.cs @@ -4,11 +4,12 @@ namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; /// -/// Just sets a blackboard key to a float +/// Set to float value for the +/// specified in the . /// public sealed partial class SetFloatOperator : HTNOperator { - [DataField(required: true)] + [DataField(required: true), ViewVariables] public string TargetKey = string.Empty; [DataField, ViewVariables(VVAccess.ReadWrite)] diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs index 999756f1f74c..edcab6baff57 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs @@ -5,20 +5,22 @@ namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; /// -/// Sets a random float from MinAmount to MaxAmount to blackboard +/// Set random float value between and +/// specified +/// in the . /// public sealed partial class SetRandomFloatOperator : HTNOperator { [Dependency] private readonly IRobustRandom _random = default!; - [DataField(required: true)] + [DataField(required: true), ViewVariables] public string TargetKey = string.Empty; [DataField, ViewVariables(VVAccess.ReadWrite)] public float MaxAmount = 1f; [DataField, ViewVariables(VVAccess.ReadWrite)] - public float MinAmount = 0f; + public float MinAmount; public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, CancellationToken cancelToken) diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs index d1c7d6191502..558b1fc04dc6 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs @@ -20,7 +20,8 @@ public sealed partial class SayKeyOperator : HTNOperator public override void Initialize(IEntitySystemManager sysManager) { base.Initialize(sysManager); - _chat = IoCManager.Resolve().GetEntitySystem(); + + _chat = sysManager.GetEntitySystem(); } public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) @@ -28,8 +29,12 @@ public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTi if (!blackboard.TryGetValue(Key, out var value, _entManager)) return HTNOperatorStatus.Failed; + var @string = value.ToString(); + if (@string is not { }) + return HTNOperatorStatus.Failed; + var speaker = blackboard.GetValue(NPCBlackboard.Owner); - _chat.TrySendInGameICMessage(speaker, value.ToString() ?? "Oh no...", InGameICChatType.Speak, hideChat: Hidden, hideLog: Hidden); + _chat.TrySendInGameICMessage(speaker, @string, InGameICChatType.Speak, hideChat: Hidden, hideLog: Hidden); return base.Update(blackboard, frameTime); } diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SpeakOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SpeakOperator.cs index cf07831959bd..8a4c655a39b5 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SpeakOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SpeakOperator.cs @@ -6,7 +6,7 @@ public sealed partial class SpeakOperator : HTNOperator { private ChatSystem _chat = default!; - [DataField("speech", required: true)] + [DataField(required: true)] public string Speech = string.Empty; /// @@ -18,14 +18,15 @@ public sealed partial class SpeakOperator : HTNOperator public override void Initialize(IEntitySystemManager sysManager) { base.Initialize(sysManager); - _chat = IoCManager.Resolve().GetEntitySystem(); + + _chat = sysManager.GetEntitySystem(); } public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) { var speaker = blackboard.GetValue(NPCBlackboard.Owner); - _chat.TrySendInGameICMessage(speaker, Loc.GetString(Speech), InGameICChatType.Speak, hideChat: Hidden, hideLog: Hidden); + return base.Update(blackboard, frameTime); } } From 09256cfaa572d326840edaea1dc0268cd9def5e3 Mon Sep 17 00:00:00 2001 From: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Date: Sat, 1 Jun 2024 13:49:28 -0400 Subject: [PATCH 202/568] Makes machine parts stackable, removes unused field in stack prototypes (#28434) * Makes machine parts stacks, removes unused field in stack prototypes * forgor * Fix tests * Fixes lathe construction. Yes. This sucks but there's no better way that doesnt involve refactoring machine parts completely * detail * a --- .../Tests/MaterialArbitrageTest.cs | 2 +- .../Construction/MachineFrameSystem.cs | 59 +++++++++++--- Content.Server/Stack/StackSystem.cs | 2 +- .../Construction/MachinePartSystem.cs | 4 +- Content.Shared/Materials/MaterialPrototype.cs | 2 +- Content.Shared/Stacks/StackPrototype.cs | 19 ++--- .../Entities/Objects/Misc/machine_parts.yml | 8 ++ .../Entities/Objects/Tools/fulton.yml | 1 - .../Stacks/Materials/Sheets/glass.yml | 7 -- .../Stacks/Materials/Sheets/metal.yml | 3 - .../Stacks/Materials/Sheets/other.yml | 4 - .../Prototypes/Stacks/Materials/crystals.yml | 1 - .../Prototypes/Stacks/Materials/ingots.yml | 2 - .../Prototypes/Stacks/Materials/materials.yml | 13 ---- Resources/Prototypes/Stacks/Materials/ore.yml | 9 --- .../Prototypes/Stacks/Materials/parts.yml | 1 - .../Prototypes/Stacks/consumable_stacks.yml | 11 --- .../Prototypes/Stacks/engineering_stacks.yml | 2 - .../Prototypes/Stacks/floor_tile_stacks.yml | 76 +------------------ .../Prototypes/Stacks/medical_stacks.yml | 8 -- Resources/Prototypes/Stacks/power_stacks.yml | 3 - .../Prototypes/Stacks/science_stacks.yml | 19 ++++- 22 files changed, 88 insertions(+), 168 deletions(-) diff --git a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs index 7f9c02fc13bc..ed1e5549438f 100644 --- a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs +++ b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs @@ -103,7 +103,7 @@ public async Task NoMaterialArbitrage() continue; var stackProto = protoManager.Index(materialStep.MaterialPrototypeId); - var spawnProto = protoManager.Index(stackProto.Spawn); + var spawnProto = protoManager.Index(stackProto.Spawn); if (!spawnProto.Components.ContainsKey(materialName) || !spawnProto.Components.TryGetValue(compositionName, out var compositionReg)) diff --git a/Content.Server/Construction/MachineFrameSystem.cs b/Content.Server/Construction/MachineFrameSystem.cs index 09d8d413ecd7..e20c36d8498c 100644 --- a/Content.Server/Construction/MachineFrameSystem.cs +++ b/Content.Server/Construction/MachineFrameSystem.cs @@ -59,23 +59,27 @@ private void OnInteractUsing(EntityUid uid, MachineFrameComponent component, Int return; } - // Machine parts cannot currently satisfy stack/component/tag restrictions. Similarly stacks cannot satisfy - // component/tag restrictions. However, there is no reason this cannot be supported in the future. If this - // changes, then RegenerateProgress() also needs to be updated. - // + // If this changes in the future, then RegenerateProgress() also needs to be updated. // Note that one entity is ALLOWED to satisfy more than one kind of component or tag requirements. This is // necessary in order to avoid weird entity-ordering shenanigans in RegenerateProgress(). + var stack = CompOrNull(args.Used); + var machinePart = CompOrNull(args.Used); + if (stack != null && machinePart != null) + { + if (TryInsertPartStack(uid, args.Used, component, machinePart, stack)) + args.Handled = true; + return; + } // Handle parts - if (TryComp(args.Used, out var machinePart)) + if (machinePart != null) { if (TryInsertPart(uid, args.Used, component, machinePart)) args.Handled = true; return; } - // Handle stacks - if (TryComp(args.Used, out var stack)) + if (stack != null) { if (TryInsertStack(uid, args.Used, component, stack)) args.Handled = true; @@ -191,6 +195,44 @@ private bool TryInsertPart(EntityUid uid, EntityUid used, MachineFrameComponent return true; } + /// Whether or not the function had any effect. Does not indicate success. + private bool TryInsertPartStack(EntityUid uid, EntityUid used, MachineFrameComponent component, MachinePartComponent machinePart, StackComponent stack) + { + if (!component.Requirements.ContainsKey(machinePart.PartType)) + return false; + + var progress = component.Progress[machinePart.PartType]; + var requirement = component.Requirements[machinePart.PartType]; + + var needed = requirement - progress; + if (needed <= 0) + return false; + + var count = stack.Count; + if (count < needed) + { + if (!_container.Insert(used, component.PartContainer)) + return true; + + component.Progress[machinePart.PartType] += count; + return true; + } + + var splitStack = _stack.Split(used, needed, Transform(uid).Coordinates, stack); + + if (splitStack == null) + return false; + + if (!_container.Insert(splitStack.Value, component.PartContainer)) + return true; + + component.Progress[machinePart.PartType] += needed; + if (IsComplete(component)) + _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid); + + return true; + } + /// Whether or not the function had any effect. Does not indicate success. private bool TryInsertStack(EntityUid uid, EntityUid used, MachineFrameComponent component, StackComponent stack) { @@ -328,8 +370,6 @@ public void RegenerateProgress(MachineFrameComponent component) { if (TryComp(part, out var machinePart)) { - DebugTools.Assert(!HasComp(part)); - // Check this is part of the requirements... if (!component.Requirements.ContainsKey(machinePart.PartType)) continue; @@ -338,7 +378,6 @@ public void RegenerateProgress(MachineFrameComponent component) component.Progress[machinePart.PartType] = 1; else component.Progress[machinePart.PartType]++; - continue; } diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index 001093a8dd4f..e34592b45f9c 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -51,7 +51,7 @@ public override void SetCount(EntityUid uid, int amount, StackComponent? compone // Get a prototype ID to spawn the new entity. Null is also valid, although it should rarely be picked... var prototype = _prototypeManager.TryIndex(stack.StackTypeId, out var stackType) - ? stackType.Spawn + ? stackType.Spawn.ToString() : Prototype(uid)?.ID; // Set the output parameter in the event instance to the newly split stack. diff --git a/Content.Shared/Construction/MachinePartSystem.cs b/Content.Shared/Construction/MachinePartSystem.cs index 1a19040b4106..359b58c8816f 100644 --- a/Content.Shared/Construction/MachinePartSystem.cs +++ b/Content.Shared/Construction/MachinePartSystem.cs @@ -87,9 +87,9 @@ public Dictionary GetMachineBoardMaterialCost(Entity(stackId); + var defaultProto = _prototype.Index(stackProto.Spawn); - if (_prototype.TryIndex(stackProto.Spawn, out var defaultProto) && - defaultProto.TryGetComponent(out var physComp)) + if (defaultProto.TryGetComponent(out var physComp)) { foreach (var (mat, matAmount) in physComp.MaterialComposition) { diff --git a/Content.Shared/Materials/MaterialPrototype.cs b/Content.Shared/Materials/MaterialPrototype.cs index 905a2359d38b..5adf13213ebe 100644 --- a/Content.Shared/Materials/MaterialPrototype.cs +++ b/Content.Shared/Materials/MaterialPrototype.cs @@ -29,7 +29,7 @@ public sealed partial class MaterialPrototype : IPrototype, IInheritingPrototype /// include which stack we should spawn by default. /// [DataField] - public ProtoId? StackEntity; + public EntProtoId? StackEntity; [DataField] public string Name = string.Empty; diff --git a/Content.Shared/Stacks/StackPrototype.cs b/Content.Shared/Stacks/StackPrototype.cs index 28b7da8f2ae8..f108419a9efd 100644 --- a/Content.Shared/Stacks/StackPrototype.cs +++ b/Content.Shared/Stacks/StackPrototype.cs @@ -4,7 +4,7 @@ namespace Content.Shared.Stacks; -[Prototype("stack")] +[Prototype] public sealed partial class StackPrototype : IPrototype { [ViewVariables] @@ -15,33 +15,26 @@ public sealed partial class StackPrototype : IPrototype /// Human-readable name for this stack type e.g. "Steel" /// /// This is a localization string ID. - [DataField("name")] + [DataField] public string Name { get; private set; } = string.Empty; /// /// An icon that will be used to represent this stack type. /// - [DataField("icon")] + [DataField] public SpriteSpecifier? Icon { get; private set; } /// /// The entity id that will be spawned by default from this stack. /// - [DataField("spawn", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] - public string Spawn { get; private set; } = string.Empty; + [DataField(required: true)] + public EntProtoId Spawn { get; private set; } = string.Empty; /// /// The maximum amount of things that can be in a stack. /// Can be overriden on /// if null, simply has unlimited max count. /// - [DataField("maxCount")] + [DataField] public int? MaxCount { get; private set; } - - /// - /// The size of an individual unit of this stack. - /// - [DataField("itemSize")] - public int? ItemSize; } - diff --git a/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml b/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml index 62a63c80c31a..37de294cce89 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml @@ -9,6 +9,8 @@ sprite: Objects/Misc/stock_parts.rsi - type: Item size: Tiny + - type: Stack + count: 1 - type: entity id: CapacitorStockPart @@ -25,6 +27,8 @@ - type: Tag tags: - CapacitorStockPart + - type: Stack + stackType: Capacitor - type: entity id: MicroManipulatorStockPart @@ -38,6 +42,8 @@ - type: MachinePart part: Manipulator rating: 1 + - type: Stack + stackType: MicroManipulator - type: entity id: MatterBinStockPart @@ -51,3 +57,5 @@ - type: MachinePart part: MatterBin rating: 1 + - type: Stack + stackType: MatterBin diff --git a/Resources/Prototypes/Entities/Objects/Tools/fulton.yml b/Resources/Prototypes/Entities/Objects/Tools/fulton.yml index 5255e5f303b8..cfd0b8f77009 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/fulton.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/fulton.yml @@ -7,7 +7,6 @@ state: extraction_pack spawn: Fulton1 maxCount: 10 - itemSize: 2 # Entities - type: entity diff --git a/Resources/Prototypes/Stacks/Materials/Sheets/glass.yml b/Resources/Prototypes/Stacks/Materials/Sheets/glass.yml index 0caffb301f50..cd6aed7cdfcb 100644 --- a/Resources/Prototypes/Stacks/Materials/Sheets/glass.yml +++ b/Resources/Prototypes/Stacks/Materials/Sheets/glass.yml @@ -4,7 +4,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: glass } spawn: SheetGlass1 maxCount: 30 - itemSize: 1 - type: stack id: ReinforcedGlass @@ -12,7 +11,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: rglass } spawn: SheetRGlass1 maxCount: 30 - itemSize: 1 - type: stack id: PlasmaGlass @@ -20,7 +18,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: pglass } spawn: SheetPGlass1 maxCount: 30 - itemSize: 1 - type: stack id: ReinforcedPlasmaGlass @@ -28,7 +25,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: rpglass } spawn: SheetRPGlass1 maxCount: 30 - itemSize: 1 - type: stack id: UraniumGlass @@ -36,7 +32,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: uglass } spawn: SheetUGlass1 maxCount: 30 - itemSize: 1 - type: stack id: ReinforcedUraniumGlass @@ -44,7 +39,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: ruglass } spawn: SheetRUGlass1 maxCount: 30 - itemSize: 1 - type: stack id: ClockworkGlass @@ -52,4 +46,3 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: cglass } spawn: SheetClockworkGlass1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/Sheets/metal.yml b/Resources/Prototypes/Stacks/Materials/Sheets/metal.yml index 77f750c205b9..7520130b9be1 100644 --- a/Resources/Prototypes/Stacks/Materials/Sheets/metal.yml +++ b/Resources/Prototypes/Stacks/Materials/Sheets/metal.yml @@ -4,7 +4,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/metal.rsi, state: steel } spawn: SheetSteel1 maxCount: 30 - itemSize: 1 - type: stack id: Plasteel @@ -12,7 +11,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/metal.rsi, state: plasteel } spawn: SheetPlasteel1 maxCount: 30 - itemSize: 1 - type: stack id: Brass @@ -20,4 +18,3 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/metal.rsi, state: brass } spawn: SheetBrass1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/Sheets/other.yml b/Resources/Prototypes/Stacks/Materials/Sheets/other.yml index 96f22ae656c9..565b1fc1aeec 100644 --- a/Resources/Prototypes/Stacks/Materials/Sheets/other.yml +++ b/Resources/Prototypes/Stacks/Materials/Sheets/other.yml @@ -4,7 +4,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/other.rsi, state: paper } spawn: SheetPaper1 maxCount: 30 - itemSize: 1 - type: stack id: Plasma @@ -12,7 +11,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/other.rsi, state: plasma } spawn: SheetPlasma1 maxCount: 30 - itemSize: 1 - type: stack id: Plastic @@ -20,7 +18,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/other.rsi, state: plastic } spawn: SheetPlastic1 maxCount: 30 - itemSize: 1 - type: stack id: Uranium @@ -28,4 +25,3 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/other.rsi, state: uranium } spawn: SheetUranium1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/crystals.yml b/Resources/Prototypes/Stacks/Materials/crystals.yml index 274f9c10eab1..28f4ed70ca02 100644 --- a/Resources/Prototypes/Stacks/Materials/crystals.yml +++ b/Resources/Prototypes/Stacks/Materials/crystals.yml @@ -3,4 +3,3 @@ name: telecrystal icon: Objects/Specific/Syndicate/telecrystal.rsi spawn: Telecrystal1 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/ingots.yml b/Resources/Prototypes/Stacks/Materials/ingots.yml index 956523c92ba0..1fd67a096dc7 100644 --- a/Resources/Prototypes/Stacks/Materials/ingots.yml +++ b/Resources/Prototypes/Stacks/Materials/ingots.yml @@ -4,7 +4,6 @@ icon: { sprite: "/Textures/Objects/Materials/ingots.rsi", state: gold } spawn: IngotGold1 maxCount: 30 - itemSize: 1 - type: stack id: Silver @@ -12,4 +11,3 @@ icon: { sprite: "/Textures/Objects/Materials/ingots.rsi", state: silver } spawn: IngotSilver1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/materials.yml b/Resources/Prototypes/Stacks/Materials/materials.yml index cc963dde59a4..1157dc3f004c 100644 --- a/Resources/Prototypes/Stacks/Materials/materials.yml +++ b/Resources/Prototypes/Stacks/Materials/materials.yml @@ -4,7 +4,6 @@ icon: { sprite: /Textures/Objects/Misc/monkeycube.rsi, state: cube } spawn: MaterialBiomass1 maxCount: 100 - itemSize: 1 - type: stack id: WoodPlank @@ -12,7 +11,6 @@ icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: wood } spawn: MaterialWoodPlank1 maxCount: 30 - itemSize: 1 - type: stack id: Cardboard @@ -20,7 +18,6 @@ icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: cardboard } spawn: MaterialCardboard1 maxCount: 30 - itemSize: 1 - type: stack id: Cloth @@ -28,7 +25,6 @@ icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: cloth } spawn: MaterialCloth1 maxCount: 30 - itemSize: 1 - type: stack id: Durathread @@ -36,7 +32,6 @@ icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: durathread } spawn: MaterialDurathread1 maxCount: 30 - itemSize: 1 - type: stack id: Diamond @@ -44,7 +39,6 @@ icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: diamond } spawn: MaterialDiamond1 maxCount: 30 - itemSize: 2 - type: stack id: Cotton @@ -52,7 +46,6 @@ icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: cotton } spawn: MaterialCotton1 maxCount: 30 - itemSize: 1 - type: stack id: Pyrotton @@ -60,7 +53,6 @@ icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: pyrotton } spawn: MaterialPyrotton1 maxCount: 30 - itemSize: 1 - type: stack id: Bananium @@ -68,7 +60,6 @@ icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: bananium } spawn: MaterialBananium1 maxCount: 10 - itemSize: 2 - type: stack id: MeatSheets @@ -76,7 +67,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/meaterial.rsi, state: meat } spawn: MaterialSheetMeat1 maxCount: 30 - itemSize: 1 - type: stack id: WebSilk @@ -84,7 +74,6 @@ icon: { sprite: /Textures/Objects/Materials/silk.rsi, state: icon } spawn: MaterialWebSilk1 maxCount: 50 - itemSize: 1 - type: stack id: Bones @@ -92,7 +81,6 @@ icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: bones} spawn: MaterialBones1 maxCount: 30 - itemSize: 1 - type: stack id: Gunpowder @@ -100,4 +88,3 @@ icon: { sprite: /Textures/Objects/Misc/reagent_fillings.rsi, state: powderpile } spawn: MaterialGunpowder maxCount: 60 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/ore.yml b/Resources/Prototypes/Stacks/Materials/ore.yml index 2a95393c27bb..51254b5a7a68 100644 --- a/Resources/Prototypes/Stacks/Materials/ore.yml +++ b/Resources/Prototypes/Stacks/Materials/ore.yml @@ -4,7 +4,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: gold } spawn: GoldOre1 maxCount: 30 - itemSize: 2 - type: stack id: SteelOre @@ -12,7 +11,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: iron } spawn: SteelOre1 maxCount: 30 - itemSize: 2 - type: stack id: PlasmaOre @@ -20,7 +18,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: plasma } spawn: PlasmaOre1 maxCount: 30 - itemSize: 2 - type: stack id: SilverOre @@ -28,7 +25,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: silver } spawn: SilverOre1 maxCount: 30 - itemSize: 2 - type: stack id: SpaceQuartz @@ -36,7 +32,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: spacequartz } spawn: SpaceQuartz1 maxCount: 30 - itemSize: 2 - type: stack id: UraniumOre @@ -44,7 +39,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: uranium } spawn: UraniumOre1 maxCount: 30 - itemSize: 2 - type: stack @@ -53,7 +47,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: bananium } spawn: BananiumOre1 maxCount: 30 - itemSize: 2 - type: stack id: Coal @@ -61,7 +54,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: coal } spawn: Coal1 maxCount: 30 - itemSize: 2 - type: stack id: SaltOre @@ -69,4 +61,3 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: salt } spawn: Salt1 maxCount: 30 - itemSize: 2 diff --git a/Resources/Prototypes/Stacks/Materials/parts.yml b/Resources/Prototypes/Stacks/Materials/parts.yml index 947bbb1bf25a..50ceb28757a7 100644 --- a/Resources/Prototypes/Stacks/Materials/parts.yml +++ b/Resources/Prototypes/Stacks/Materials/parts.yml @@ -4,4 +4,3 @@ icon: { sprite: /Textures/Objects/Materials/parts.rsi, state: rods } spawn: PartRodMetal1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/consumable_stacks.yml b/Resources/Prototypes/Stacks/consumable_stacks.yml index 2936772f080c..e7feab7b5202 100644 --- a/Resources/Prototypes/Stacks/consumable_stacks.yml +++ b/Resources/Prototypes/Stacks/consumable_stacks.yml @@ -5,7 +5,6 @@ name: pancake spawn: FoodBakedPancake maxCount: 3 - itemSize: 1 # Food Containers @@ -15,7 +14,6 @@ icon: { sprite: Objects/Consumable/Food/Baked/pizza.rsi, state: box } spawn: FoodBoxPizza maxCount: 30 - itemSize: 10 # Smokeables @@ -25,7 +23,6 @@ icon: { sprite: /Textures/Objects/Consumable/Smokeables/Cigarettes/paper.rsi, state: cigpaper } spawn: PaperRolling maxCount: 5 - itemSize: 1 - type: stack id: CigaretteFilter @@ -33,7 +30,6 @@ icon: { sprite: /Textures/Objects/Consumable/Smokeables/Cigarettes/paper.rsi, state: cigfilter } spawn: CigaretteFilter maxCount: 5 - itemSize: 2 - type: stack id: GroundTobacco @@ -41,7 +37,6 @@ icon: { sprite: /Textures/Objects/Misc/reagent_fillings.rsi, state: powderpile } spawn: GroundTobacco maxCount: 5 - itemSize: 1 - type: stack id: GroundCannabis @@ -49,7 +44,6 @@ icon: { sprite: /Textures/Objects/Misc/reagent_fillings.rsi, state: powderpile } spawn: GroundCannabis maxCount: - itemSize: 1 - type: stack id: GroundCannabisRainbow @@ -57,7 +51,6 @@ icon: { sprite: /Textures/Objects/Specific/Hydroponics/rainbow_cannabis.rsi, state: powderpile_rainbow } spawn: GroundCannabisRainbow maxCount: - itemSize: 1 - type: stack id: LeavesTobaccoDried @@ -65,7 +58,6 @@ icon: { sprite: /Textures/Objects/Specific/Hydroponics/tobacco.rsi, state: dried } spawn: LeavesTobaccoDried maxCount: 5 - itemSize: 5 - type: stack id: LeavesCannabisDried @@ -73,12 +65,9 @@ icon: { sprite: /Textures/Objects/Specific/Hydroponics/tobacco.rsi, state: dried } spawn: LeavesCannabisDried maxCount: 5 - itemSize: 5 - type: stack id: LeavesCannabisRainbowDried name: dried rainbow cannabis leaves icon: { sprite: /Textures/Objects/Specific/Hydroponics/rainbow_cannabis.rsi, state: dried } spawn: LeavesCannabisRainbowDried - maxCount: 5 - itemSize: 5 diff --git a/Resources/Prototypes/Stacks/engineering_stacks.yml b/Resources/Prototypes/Stacks/engineering_stacks.yml index 77cc62040230..b4550015dc45 100644 --- a/Resources/Prototypes/Stacks/engineering_stacks.yml +++ b/Resources/Prototypes/Stacks/engineering_stacks.yml @@ -4,11 +4,9 @@ name: inflatable wall spawn: InflatableWallStack1 maxCount: 10 - itemSize: 1 - type: stack id: InflatableDoor name: inflatable door spawn: InflatableDoorStack1 maxCount: 4 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/floor_tile_stacks.yml b/Resources/Prototypes/Stacks/floor_tile_stacks.yml index c5e37013b870..c88786f0dc81 100644 --- a/Resources/Prototypes/Stacks/floor_tile_stacks.yml +++ b/Resources/Prototypes/Stacks/floor_tile_stacks.yml @@ -3,469 +3,402 @@ name: steel tile spawn: FloorTileItemSteel maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMetalDiamond name: steel tile spawn: FloorTileItemMetalDiamond maxCount: 30 - itemSize: 5 - type: stack id: FloorTileWood name: wood floor spawn: FloorTileItemWood maxCount: 30 - itemSize: 5 - type: stack id: FloorTileWhite name: white tile spawn: FloorTileItemWhite maxCount: 30 - itemSize: 5 - type: stack id: FloorTileDark name: dark tile spawn: FloorTileItemDark maxCount: 30 - itemSize: 5 - type: stack id: FloorTileTechmaint name: techmaint floor spawn: FloorTileItemTechmaint maxCount: 30 - itemSize: 5 - type: stack id: FloorTileFreezer name: freezer tile spawn: FloorTileItemFreezer maxCount: 30 - itemSize: 5 - type: stack id: FloorTileShowroom name: showroom tile spawn: FloorTileItemShowroom maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGCircuit name: green-circuit floor spawn: FloorTileItemGCircuit maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGold name: gold floor spawn: FloorTileItemGold maxCount: 30 - itemSize: 5 - type: stack id: FloorTileReinforced name: reinforced tile spawn: FloorTileItemReinforced maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMono name: mono tile spawn: FloorTileItemMono maxCount: 30 - itemSize: 5 - type: stack id: FloorTileBrassFilled name: filled brass plate spawn: FloorTileItemBrassFilled maxCount: 30 - itemSize: 5 - + - type: stack id: FloorTileBrassReebe name: smooth brass plate spawn: FloorTileItemBrassReebe maxCount: 30 - itemSize: 5 - type: stack id: FloorTileLino name: linoleum floor spawn: FloorTileItemLino maxCount: 30 - itemSize: 5 - type: stack id: FloorTileHydro name: hydro tile spawn: FloorTileItemHydro maxCount: 30 - itemSize: 5 - type: stack id: FloorTileLime name: lime tile spawn: FloorTileItemLime maxCount: 30 - itemSize: 5 - type: stack id: FloorTileDirty name: dirty tile spawn: FloorTileItemDirty maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleWhite name: white shuttle tile spawn: FloorTileItemShuttleWhite maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleBlue name: blue shuttle tile spawn: FloorTileItemShuttleBlue maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleOrange name: orange shuttle tile spawn: FloorTileItemShuttleOrange maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttlePurple name: purple shuttle tile spawn: FloorTileItemShuttlePurple maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleRed name: red shuttle tile spawn: FloorTileItemShuttleRed maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleGrey name: grey shuttle tile spawn: FloorTileItemShuttleGrey maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleBlack name: black shuttle tile spawn: FloorTileItemShuttleBlack maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackEighties name: eighties floor tile spawn: FloorTileItemEighties maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackArcadeBlue name: blue arcade tile spawn: FloorTileItemArcadeBlue maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackArcadeBlue2 name: blue arcade tile spawn: FloorTileItemArcadeBlue2 maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackArcadeRed name: red arcade tile spawn: FloorTileItemArcadeRed maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetRed name: red carpet tile spawn: FloorCarpetItemRed maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetBlack name: block carpet tile spawn: FloorCarpetItemBlack maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetBlue name: blue carpet tile spawn: FloorCarpetItemBlue maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetGreen name: green carpet tile spawn: FloorCarpetItemGreen maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetOrange name: orange carpet tile spawn: FloorCarpetItemOrange maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetSkyBlue name: skyblue carpet tile spawn: FloorCarpetItemSkyBlue maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetPurple name: purple carpet tile spawn: FloorCarpetItemPurple maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetPink name: pink carpet tile spawn: FloorCarpetItemPink maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetCyan name: cyan carpet tile spawn: FloorCarpetItemCyan maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetWhite name: white carpet tile spawn: FloorCarpetItemWhite maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackCarpetClown name: clown carpet tile spawn: FloorTileItemCarpetClown maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackCarpetOffice name: office carpet tile spawn: FloorTileItemCarpetOffice maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackBoxing name: boxing ring tile spawn: FloorTileItemBoxing maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackGym name: gym floor tile spawn: FloorTileItemGym maxCount: 30 - itemSize: 5 - type: stack id: FloorTileElevatorShaft name: elevator shaft tile spawn: FloorTileItemElevatorShaft maxCount: 30 - itemSize: 5 - type: stack id: FloorTileRockVault name: rock vault tile spawn: FloorTileItemRockVault maxCount: 30 - itemSize: 5 - type: stack id: FloorTileBlue name: blue floor tile spawn: FloorTileItemBlue maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMining name: mining floor tile spawn: FloorTileItemMining maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMiningDark name: dark mining floor tile spawn: FloorTileItemMiningDark maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMiningLight name: light mining floor tile spawn: FloorTileItemMiningLight maxCount: 30 - itemSize: 5 - type: stack id: FloorTileBar name: item bar floor tile spawn: FloorTileItemBar maxCount: 30 - itemSize: 5 - type: stack id: FloorTileClown name: clown floor tile spawn: FloorTileItemClown maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMime name: mime floor tile spawn: FloorTileItemMime maxCount: 30 - itemSize: 5 - type: stack id: FloorTileKitchen name: kitchen floor tile spawn: FloorTileItemKitchen maxCount: 30 - itemSize: 5 - type: stack id: FloorTileLaundry name: laundry floor tile spawn: FloorTileItemLaundry maxCount: 30 - itemSize: 5 - type: stack id: FloorTileConcrete name: concrete tile spawn: FloorTileItemGrayConcrete maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGrayConcrete name: gray concrete tile spawn: FloorTileItemLaundry maxCount: 30 - itemSize: 5 - type: stack id: FloorTileOldConcrete name: old concrete tile spawn: FloorTileItemOldConcrete maxCount: 30 - itemSize: 5 - type: stack id: FloorTileSilver name: silver floor tile spawn: FloorTileItemSilver maxCount: 30 - itemSize: 5 - type: stack id: FloorTileBCircuit name: bcircuit floor tile spawn: FloorTileItemBCircuit maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGrass name: grass floor tile spawn: FloorTileItemGrass maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGrassJungle name: grass jungle floor tile spawn: FloorTileItemGrassJungle maxCount: 30 - itemSize: 5 - type: stack id: FloorTileSnow name: snow floor tile spawn: FloorTileItemSnow maxCount: 30 - itemSize: 5 - type: stack id: FloorTileWoodPattern name: wood pattern floor spawn: FloorTileItemWoodPattern maxCount: 30 - itemSize: 5 - type: stack id: FloorTileFlesh name: flesh floor spawn: FloorTileItemFlesh maxCount: 30 - itemSize: 5 - type: stack id: FloorTileSteelMaint name: steel maint floor spawn: FloorTileItemSteelMaint maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGratingMaint name: grating maint floor spawn: FloorTileItemGratingMaint maxCount: 30 - itemSize: 5 - type: stack id: FloorTileWeb name: web tile spawn: FloorTileItemWeb maxCount: 30 - itemSize: 5 # Faux science tiles - type: stack @@ -473,38 +406,33 @@ name: astro-grass floor spawn: FloorTileItemAstroGrass maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMowedAstroGrass name: mowed astro-grass floor spawn: FloorTileItemMowedAstroGrass maxCount: 30 - itemSize: 5 - type: stack id: FloorTileJungleAstroGrass name: jungle astro-grass floor spawn: FloorTileItemJungleAstroGrass maxCount: 30 - itemSize: 5 - type: stack id: FloorTileAstroIce name: astro-ice floor spawn: FloorTileItemAstroIce maxCount: 30 - itemSize: 5 - type: stack id: FloorTileAstroSnow name: astro-snow floor spawn: FloorTileItemAstroSnow maxCount: 30 - itemSize: 5 - type: stack id: FloorTileWoodLarge name: large wood floor spawn: FloorTileItemWoodLarge - maxCount: 30 \ No newline at end of file + maxCount: 30 diff --git a/Resources/Prototypes/Stacks/medical_stacks.yml b/Resources/Prototypes/Stacks/medical_stacks.yml index 9d2b77ec933f..7ad3c21634c7 100644 --- a/Resources/Prototypes/Stacks/medical_stacks.yml +++ b/Resources/Prototypes/Stacks/medical_stacks.yml @@ -4,7 +4,6 @@ icon: { sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: ointment } spawn: Ointment maxCount: 10 - itemSize: 1 - type: stack id: AloeCream @@ -12,7 +11,6 @@ icon: { sprite: "/Textures/Objects/Specific/Hydroponics/aloe.rsi", state: cream } spawn: AloeCream maxCount: 10 - itemSize: 1 - type: stack id: Gauze @@ -20,7 +18,6 @@ icon: { sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: gauze } spawn: Gauze maxCount: 10 - itemSize: 1 - type: stack id: Brutepack @@ -28,7 +25,6 @@ icon: { sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: gauze } spawn: Brutepack maxCount: 10 - itemSize: 1 - type: stack id: Bloodpack @@ -36,7 +32,6 @@ icon: { sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: bloodpack } spawn: Bloodpack maxCount: 10 - itemSize: 1 - type: stack id: MedicatedSuture @@ -44,7 +39,6 @@ icon: {sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: medicated-suture } spawn: MedicatedSuture maxCount: 10 - itemSize: 1 - type: stack id: RegenerativeMesh @@ -52,6 +46,4 @@ icon: {sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: regenerative-mesh} spawn: RegenerativeMesh maxCount: 10 - itemSize: 1 - diff --git a/Resources/Prototypes/Stacks/power_stacks.yml b/Resources/Prototypes/Stacks/power_stacks.yml index 5acf4819dec3..7f015659e92e 100644 --- a/Resources/Prototypes/Stacks/power_stacks.yml +++ b/Resources/Prototypes/Stacks/power_stacks.yml @@ -4,7 +4,6 @@ icon: { sprite: "/Textures/Objects/Tools/cable-coils.rsi", state: coil-30 } spawn: CableApcStack1 maxCount: 30 - itemSize: 1 - type: stack id: CableMV @@ -12,7 +11,6 @@ icon: { sprite: "/Textures/Objects/Tools/cable-coils.rsi", state: coilmv-30 } spawn: CableMVStack1 maxCount: 30 - itemSize: 1 - type: stack id: CableHV @@ -20,4 +18,3 @@ icon: { sprite: "/Textures/Objects/Tools/cable-coils.rsi", state: coilhv-30 } spawn: CableHVStack1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/science_stacks.yml b/Resources/Prototypes/Stacks/science_stacks.yml index 010a5dc659a4..bf58fad91543 100644 --- a/Resources/Prototypes/Stacks/science_stacks.yml +++ b/Resources/Prototypes/Stacks/science_stacks.yml @@ -3,4 +3,21 @@ name: artifact fragment spawn: ArtifactFragment1 maxCount: 30 - itemSize: 5 \ No newline at end of file + +- type: stack + id: Capacitor + name: capacitor + spawn: CapacitorStockPart + maxCount: 10 + +- type: stack + id: MicroManipulator + name: micro manipulator + spawn: MicroManipulatorStockPart + maxCount: 10 + +- type: stack + id: MatterBin + name: matter bin + spawn: MatterBinStockPart + maxCount: 10 From 6501a5b180f61b4e5e5f05f7238a8f7a093dfe2d Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 1 Jun 2024 17:50:34 +0000 Subject: [PATCH 203/568] 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 449f9bf43b5c..535a93e87e31 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: lzk228 - changes: - - message: Refill light replacer from box popup now shows properly. - type: Fix - id: 6161 - time: '2024-03-15T15:23:58.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26136 - author: FairlySadPanda changes: - message: Food and drink stocks in vending machines has been reduced to encourage @@ -3853,3 +3846,10 @@ id: 6660 time: '2024-06-01T17:29:46.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28462 +- author: AJCM-git + changes: + - message: Machine parts are now stackable + type: Tweak + id: 6661 + time: '2024-06-01T17:49:28.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28434 From 8c32072f67cbab2f69647ff1e78c3f62e705639a Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 1 Jun 2024 12:00:31 -0700 Subject: [PATCH 204/568] Fix error when removing chasm falling component on a terminating entity (#28471) --- Content.Client/Chasm/ChasmFallingVisualsSystem.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Content.Client/Chasm/ChasmFallingVisualsSystem.cs b/Content.Client/Chasm/ChasmFallingVisualsSystem.cs index 4b04aa9dd713..ddcd509cb336 100644 --- a/Content.Client/Chasm/ChasmFallingVisualsSystem.cs +++ b/Content.Client/Chasm/ChasmFallingVisualsSystem.cs @@ -24,8 +24,11 @@ public override void Initialize() private void OnComponentInit(EntityUid uid, ChasmFallingComponent component, ComponentInit args) { - if (!TryComp(uid, out var sprite)) + if (!TryComp(uid, out var sprite) || + TerminatingOrDeleted(uid)) + { return; + } component.OriginalScale = sprite.Scale; From 5d970b0861d5c8327ba8e62fd3dd81333dc125af Mon Sep 17 00:00:00 2001 From: MilenVolf <63782763+MilenVolf@users.noreply.github.com> Date: Sat, 1 Jun 2024 23:34:58 +0300 Subject: [PATCH 205/568] Station event component and system tweaks (#28331) * Make anomaly, artifact and gifts events announcement sound optional * Requested changes + Added new "GameRuleAfterAddedEvent" for StationEventSystem We need to call "Add" in "StationEventSystem" after others GameRule's in case if we need to change StationEventComponent variables. * Fix margins * Makes use of GameRuleComponent.Delay and remove station system handling of delays plus small cleanup * Fix merge --------- Co-authored-by: AJCM --- Content.Server/Chat/Systems/ChatSystem.cs | 4 +- .../Components/StationEventComponent.cs | 19 +- .../StationEvents/Events/AnomalySpawnRule.cs | 8 +- .../Events/BluespaceArtifactRule.cs | 8 +- .../StationEvents/Events/BreakerFlipRule.cs | 9 +- .../StationEvents/Events/CargoGiftsRule.cs | 8 +- .../StationEvents/Events/FalseAlarmRule.cs | 15 +- .../StationEvents/Events/GasLeakRule.cs | 4 +- .../Events/StationEventSystem.cs | 20 +- .../SurveillanceCameraMicrophoneSystem.cs | 4 +- .../station-events/events/anomaly-spawn.ftl | 2 +- .../Prototypes/GameRules/cargo_gifts.yml | 294 +++++++++--------- Resources/Prototypes/GameRules/events.yml | 79 +++-- 13 files changed, 244 insertions(+), 230 deletions(-) diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index 531c4259efea..b79e16a8df97 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -823,7 +823,7 @@ private Dictionary GetRecipients(EntityUid recipients.Add(player, new ICChatRecipientData(-1, true)); } - RaiseLocalEvent(new ExpandICChatRecipientstEvent(source, voiceGetRange, recipients)); + RaiseLocalEvent(new ExpandICChatRecipientsEvent(source, voiceGetRange, recipients)); return recipients; } @@ -868,7 +868,7 @@ public string BuildGibberishString(IReadOnlyList charOptions, int length) /// This event is raised before chat messages are sent out to clients. This enables some systems to send the chat /// messages to otherwise out-of view entities (e.g. for multiple viewports from cameras). /// -public record ExpandICChatRecipientstEvent(EntityUid Source, float VoiceRange, Dictionary Recipients) +public record ExpandICChatRecipientsEvent(EntityUid Source, float VoiceRange, Dictionary Recipients) { } diff --git a/Content.Server/StationEvents/Components/StationEventComponent.cs b/Content.Server/StationEvents/Components/StationEventComponent.cs index cc17007d90e5..fdd1d3962e74 100644 --- a/Content.Server/StationEvents/Components/StationEventComponent.cs +++ b/Content.Server/StationEvents/Components/StationEventComponent.cs @@ -24,6 +24,12 @@ public sealed partial class StationEventComponent : Component [DataField] public string? EndAnnouncement; + [DataField] + public Color StartAnnouncementColor = Color.Gold; + + [DataField] + public Color EndAnnouncementColor = Color.Gold; + [DataField] public SoundSpecifier? StartAudio; @@ -42,12 +48,6 @@ public sealed partial class StationEventComponent : Component [DataField] public int ReoccurrenceDelay = 30; - /// - /// How long after being added does the event start - /// - [DataField] - public TimeSpan StartDelay = TimeSpan.Zero; - /// /// How long the event lasts. /// @@ -75,13 +75,6 @@ public sealed partial class StationEventComponent : Component [DataField] public int? MaxOccurrences; - /// - /// When the station event starts. - /// - [DataField("startTime", customTypeSerializer: typeof(TimeOffsetSerializer))] - [AutoPausedField] - public TimeSpan StartTime; - /// /// When the station event ends. /// diff --git a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs index 96633834ee10..cf66a7c052e4 100644 --- a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs +++ b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs @@ -1,6 +1,5 @@ using Content.Server.Anomaly; using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; @@ -12,11 +11,14 @@ public sealed class AnomalySpawnRule : StationEventSystem(uid, out var stationEvent)) + return; var str = Loc.GetString("anomaly-spawn-event-announcement", ("sighting", Loc.GetString($"anomaly-spawn-sighting-{RobustRandom.Next(1, 6)}"))); - ChatSystem.DispatchGlobalAnnouncement(str, colorOverride: Color.FromHex("#18abf5")); + stationEvent.StartAnnouncement = str; + + base.Added(uid, component, gameRule, args); } protected override void Started(EntityUid uid, AnomalySpawnRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs index b3ed10999ed3..aaa0f0cffc32 100644 --- a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs +++ b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs @@ -1,5 +1,4 @@ using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Random; @@ -9,11 +8,14 @@ public sealed class BluespaceArtifactRule : StationEventSystem(uid, out var stationEvent)) + return; var str = Loc.GetString("bluespace-artifact-event-announcement", ("sighting", Loc.GetString(RobustRandom.Pick(component.PossibleSighting)))); - ChatSystem.DispatchGlobalAnnouncement(str, colorOverride: Color.FromHex("#18abf5")); + stationEvent.StartAnnouncement = str; + + base.Added(uid, component, gameRule, args); } protected override void Started(EntityUid uid, BluespaceArtifactRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/StationEvents/Events/BreakerFlipRule.cs b/Content.Server/StationEvents/Events/BreakerFlipRule.cs index 16d3fd8c95df..ef2825938974 100644 --- a/Content.Server/StationEvents/Events/BreakerFlipRule.cs +++ b/Content.Server/StationEvents/Events/BreakerFlipRule.cs @@ -1,5 +1,4 @@ using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Station.Components; @@ -15,10 +14,14 @@ public sealed class BreakerFlipRule : StationEventSystem(uid, out var stationEvent)) + return; var str = Loc.GetString("station-event-breaker-flip-announcement", ("data", Loc.GetString(Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}")))); - ChatSystem.DispatchGlobalAnnouncement(str, playSound: false, colorOverride: Color.Gold); + stationEvent.StartAnnouncement = str; + + base.Added(uid, component, gameRule, args); + } protected override void Started(EntityUid uid, BreakerFlipRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/StationEvents/Events/CargoGiftsRule.cs b/Content.Server/StationEvents/Events/CargoGiftsRule.cs index c27cd302784a..4cddf4674b30 100644 --- a/Content.Server/StationEvents/Events/CargoGiftsRule.cs +++ b/Content.Server/StationEvents/Events/CargoGiftsRule.cs @@ -3,7 +3,6 @@ using Content.Server.Cargo.Systems; using Content.Server.GameTicking; using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Prototypes; @@ -18,11 +17,14 @@ public sealed class CargoGiftsRule : StationEventSystem protected override void Added(EntityUid uid, CargoGiftsRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { - base.Added(uid, component, gameRule, args); + if (!TryComp(uid, out var stationEvent)) + return; var str = Loc.GetString(component.Announce, ("sender", Loc.GetString(component.Sender)), ("description", Loc.GetString(component.Description)), ("dest", Loc.GetString(component.Dest))); - ChatSystem.DispatchGlobalAnnouncement(str, colorOverride: Color.FromHex("#18abf5")); + stationEvent.StartAnnouncement = str; + + base.Added(uid, component, gameRule, args); } /// diff --git a/Content.Server/StationEvents/Events/FalseAlarmRule.cs b/Content.Server/StationEvents/Events/FalseAlarmRule.cs index e5317a5449f1..115b6d905eae 100644 --- a/Content.Server/StationEvents/Events/FalseAlarmRule.cs +++ b/Content.Server/StationEvents/Events/FalseAlarmRule.cs @@ -1,9 +1,7 @@ using System.Linq; using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using JetBrains.Annotations; -using Robust.Shared.Player; using Robust.Shared.Random; namespace Content.Server.StationEvents.Events; @@ -15,15 +13,16 @@ public sealed class FalseAlarmRule : StationEventSystem protected override void Started(EntityUid uid, FalseAlarmRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { - base.Started(uid, component, gameRule, args); + if (!TryComp(uid, out var stationEvent)) + return; var allEv = _event.AllEvents().Select(p => p.Value).ToList(); var picked = RobustRandom.Pick(allEv); - if (picked.StartAnnouncement != null) - { - ChatSystem.DispatchGlobalAnnouncement(Loc.GetString(picked.StartAnnouncement), playSound: false, colorOverride: Color.Gold); - } - Audio.PlayGlobal(picked.StartAudio, Filter.Broadcast(), true); + stationEvent.StartAnnouncement = picked.StartAnnouncement; + stationEvent.StartAudio = picked.StartAudio; + stationEvent.StartAnnouncementColor = picked.StartAnnouncementColor; + + base.Started(uid, component, gameRule, args); } } diff --git a/Content.Server/StationEvents/Events/GasLeakRule.cs b/Content.Server/StationEvents/Events/GasLeakRule.cs index 1221612171d9..c17f17a8279d 100644 --- a/Content.Server/StationEvents/Events/GasLeakRule.cs +++ b/Content.Server/StationEvents/Events/GasLeakRule.cs @@ -29,10 +29,10 @@ protected override void Started(EntityUid uid, GasLeakRuleComponent component, G component.LeakGas = RobustRandom.Pick(component.LeakableGases); // Was 50-50 on using normal distribution. var totalGas = RobustRandom.Next(component.MinimumGas, component.MaximumGas); - var startAfter = stationEvent.StartDelay; component.MolesPerSecond = RobustRandom.Next(component.MinimumMolesPerSecond, component.MaximumMolesPerSecond); - stationEvent.EndTime = _timing.CurTime + TimeSpan.FromSeconds(totalGas / component.MolesPerSecond + startAfter.TotalSeconds); + if (gameRule.Delay is {} startAfter) + stationEvent.EndTime = _timing.CurTime + TimeSpan.FromSeconds(totalGas / component.MolesPerSecond + startAfter.Next(RobustRandom)); } // Look technically if you wanted to guarantee a leak you'd do this in announcement but having the announcement diff --git a/Content.Server/StationEvents/Events/StationEventSystem.cs b/Content.Server/StationEvents/Events/StationEventSystem.cs index bd377ec16c83..38390cedb1a9 100644 --- a/Content.Server/StationEvents/Events/StationEventSystem.cs +++ b/Content.Server/StationEvents/Events/StationEventSystem.cs @@ -1,14 +1,11 @@ -using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Chat.Systems; using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; -using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Systems; using Content.Server.StationEvents.Components; using Content.Shared.Database; using Robust.Shared.Audio.Systems; -using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Prototypes; @@ -20,7 +17,6 @@ namespace Content.Server.StationEvents.Events; public abstract class StationEventSystem : GameRuleSystem where T : IComponent { [Dependency] protected readonly IAdminLogManager AdminLogManager = default!; - [Dependency] protected readonly IMapManager MapManager = default!; [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] protected readonly ChatSystem ChatSystem = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; @@ -43,16 +39,12 @@ protected override void Added(EntityUid uid, T component, GameRuleComponent game if (!TryComp(uid, out var stationEvent)) return; - AdminLogManager.Add(LogType.EventAnnounced, $"Event added / announced: {ToPrettyString(uid)}"); if (stationEvent.StartAnnouncement != null) - { - ChatSystem.DispatchGlobalAnnouncement(Loc.GetString(stationEvent.StartAnnouncement), playSound: false, colorOverride: Color.Gold); - } + ChatSystem.DispatchGlobalAnnouncement(Loc.GetString(stationEvent.StartAnnouncement), playSound: false, colorOverride: stationEvent.StartAnnouncementColor); Audio.PlayGlobal(stationEvent.StartAudio, Filter.Broadcast(), true); - stationEvent.StartTime = Timing.CurTime + stationEvent.StartDelay; } /// @@ -86,9 +78,7 @@ protected override void Ended(EntityUid uid, T component, GameRuleComponent game AdminLogManager.Add(LogType.EventStopped, $"Event ended: {ToPrettyString(uid)}"); if (stationEvent.EndAnnouncement != null) - { - ChatSystem.DispatchGlobalAnnouncement(Loc.GetString(stationEvent.EndAnnouncement), playSound: false, colorOverride: Color.Gold); - } + ChatSystem.DispatchGlobalAnnouncement(Loc.GetString(stationEvent.EndAnnouncement), playSound: false, colorOverride: stationEvent.EndAnnouncementColor); Audio.PlayGlobal(stationEvent.EndAudio, Filter.Broadcast(), true); } @@ -108,11 +98,7 @@ public override void Update(float frameTime) if (!GameTicker.IsGameRuleAdded(uid, ruleData)) continue; - if (!GameTicker.IsGameRuleActive(uid, ruleData) && Timing.CurTime >= stationEvent.StartTime) - { - GameTicker.StartGameRule(uid, ruleData); - } - else if (stationEvent.EndTime != null && Timing.CurTime >= stationEvent.EndTime && GameTicker.IsGameRuleActive(uid, ruleData)) + if (stationEvent.EndTime != null && Timing.CurTime >= stationEvent.EndTime && GameTicker.IsGameRuleActive(uid, ruleData)) { GameTicker.EndGameRule(uid, ruleData); } diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs index f411001bd3a3..1adab9930d91 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs @@ -16,10 +16,10 @@ public override void Initialize() SubscribeLocalEvent(OnInit); SubscribeLocalEvent(RelayEntityMessage); SubscribeLocalEvent(CanListen); - SubscribeLocalEvent(OnExpandRecipients); + SubscribeLocalEvent(OnExpandRecipients); } - private void OnExpandRecipients(ExpandICChatRecipientstEvent ev) + private void OnExpandRecipients(ExpandICChatRecipientsEvent ev) { var xformQuery = GetEntityQuery(); var sourceXform = Transform(ev.Source); diff --git a/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl b/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl index 0e4b5f6e39c2..907c14ce5621 100644 --- a/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl +++ b/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl @@ -1,4 +1,4 @@ -anomaly-spawn-event-announcement = Our readings have detected a dangerous interspacial anomaly. Please inform the research team of { $sighting }. +anomaly-spawn-event-announcement = Our readings have detected a dangerous interspacial anomaly. Please inform the research team about { $sighting }. anomaly-spawn-sighting-1 = low pulsating sounds heard throughout the station anomaly-spawn-sighting-2 = strange sources of light diff --git a/Resources/Prototypes/GameRules/cargo_gifts.yml b/Resources/Prototypes/GameRules/cargo_gifts.yml index 62ec96d6a6df..c5a525129707 100644 --- a/Resources/Prototypes/GameRules/cargo_gifts.yml +++ b/Resources/Prototypes/GameRules/cargo_gifts.yml @@ -1,16 +1,30 @@ - type: entity - id: GiftsPizzaPartySmall + id: CargoGiftsBase parent: BaseGameRule + abstract: true + components: + - type: GameRule + delay: + min: 10 + max: 10 + - type: StationEvent + startColor: "#18abf5" + startAudio: + path: /Audio/Announcements/announce.ogg + - type: CargoGiftsRule + sender: cargo-gift-default-sender + +- type: entity + id: GiftsPizzaPartySmall + parent: CargoGiftsBase noSpawn: true components: - type: StationEvent weight: 5 - startDelay: 10 duration: 120 earliestStart: 20 - type: CargoGiftsRule description: cargo-gift-pizza-small - sender: cargo-gift-default-sender dest: cargo-gift-dest-bar gifts: FoodPizza: 1 # 4 pizzas @@ -20,191 +34,173 @@ - type: entity id: GiftsPizzaPartyLarge - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 2 - startDelay: 10 - duration: 240 - earliestStart: 20 - minimumPlayers: 40 - - type: CargoGiftsRule - description: cargo-gift-pizza-large - sender: cargo-gift-default-sender - dest: cargo-gift-dest-bar - gifts: - FoodPizzaLarge: 1 # 16 pizzas - FoodBarSupply: 1 - FoodSoftdrinksLarge: 1 + - type: StationEvent + weight: 2 + duration: 240 + earliestStart: 20 + minimumPlayers: 40 + - type: CargoGiftsRule + description: cargo-gift-pizza-large + dest: cargo-gift-dest-bar + gifts: + FoodPizzaLarge: 1 # 16 pizzas + FoodBarSupply: 1 + FoodSoftdrinksLarge: 1 - type: entity id: GiftsEngineering - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 5 - startDelay: 10 - duration: 240 - earliestStart: 30 - minimumPlayers: 10 - - type: CargoGiftsRule - description: cargo-gift-eng - sender: cargo-gift-default-sender - dest: cargo-gift-dest-eng - gifts: - EngineeringCableBulk: 1 - AirlockKit: 1 - MaterialSteel: 1 - MaterialPlasteel: 1 - MaterialGlass: 1 - CrateVendingMachineRestockEngineering: 1 + - type: StationEvent + weight: 5 + duration: 240 + earliestStart: 30 + minimumPlayers: 10 + - type: CargoGiftsRule + description: cargo-gift-eng + dest: cargo-gift-dest-eng + gifts: + EngineeringCableBulk: 1 + AirlockKit: 1 + MaterialSteel: 1 + MaterialPlasteel: 1 + MaterialGlass: 1 + CrateVendingMachineRestockEngineering: 1 - type: entity id: GiftsVendingRestock - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 4 - startDelay: 10 - duration: 120 - minimumPlayers: 40 - earliestStart: 30 - - type: CargoGiftsRule - description: cargo-gift-vending - sender: cargo-gift-default-sender - dest: cargo-gift-dest-supp - gifts: - CrateVendingMachineRestockHotDrinks: 3 - CrateVendingMachineRestockBooze: 1 - CrateVendingMachineRestockNutriMax: 1 - CrateVendingMachineRestockRobustSoftdrinks: 2 - CrateVendingMachineRestockVendomat: 1 - CrateVendingMachineRestockGetmoreChocolateCorp: 1 + - type: StationEvent + weight: 4 + duration: 120 + minimumPlayers: 40 + earliestStart: 30 + - type: CargoGiftsRule + description: cargo-gift-vending + dest: cargo-gift-dest-supp + gifts: + CrateVendingMachineRestockHotDrinks: 3 + CrateVendingMachineRestockBooze: 1 + CrateVendingMachineRestockNutriMax: 1 + CrateVendingMachineRestockRobustSoftdrinks: 2 + CrateVendingMachineRestockVendomat: 1 + CrateVendingMachineRestockGetmoreChocolateCorp: 1 - type: entity id: GiftsJanitor - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 6 - startDelay: 10 - duration: 120 - minimumPlayers: 30 - earliestStart: 20 - - type: CargoGiftsRule - description: cargo-gift-cleaning - sender: cargo-gift-default-sender - dest: cargo-gift-dest-janitor - gifts: - ServiceJanitorial: 2 - ServiceLightsReplacement: 2 - ServiceJanitorBiosuit: 1 + - type: StationEvent + weight: 6 + duration: 120 + minimumPlayers: 30 + earliestStart: 20 + - type: CargoGiftsRule + description: cargo-gift-cleaning + dest: cargo-gift-dest-janitor + gifts: + ServiceJanitorial: 2 + ServiceLightsReplacement: 2 + ServiceJanitorBiosuit: 1 - type: entity id: GiftsMedical - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 8 - startDelay: 10 - duration: 120 - earliestStart: 20 - minimumPlayers: 30 - - type: CargoGiftsRule - description: cargo-gift-medical-supply - sender: cargo-gift-default-sender - dest: cargo-gift-dest-med - gifts: - MedicalSupplies: 1 - MedicalChemistrySupplies: 1 - EmergencyBruteKit: 1 - EmergencyAdvancedKit: 1 - MedicalBiosuit: 1 + - type: StationEvent + weight: 8 + duration: 120 + earliestStart: 20 + minimumPlayers: 30 + - type: CargoGiftsRule + description: cargo-gift-medical-supply + dest: cargo-gift-dest-med + gifts: + MedicalSupplies: 1 + MedicalChemistrySupplies: 1 + EmergencyBruteKit: 1 + EmergencyAdvancedKit: 1 + MedicalBiosuit: 1 - type: entity id: GiftsSpacingSupplies - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 4 - startDelay: 10 - duration: 120 - earliestStart: 10 - minimumPlayers: 40 - - type: CargoGiftsRule - description: cargo-gift-space-protection - sender: cargo-gift-default-sender - dest: cargo-gift-dest-supp - gifts: - EmergencyInternalsLarge: 2 - EmergencyInflatablewall: 1 - EmergencyAdvancedKit: 1 - MedicalBiosuit: 1 - EmergencyO2Kit: 1 + - type: StationEvent + weight: 4 + duration: 120 + earliestStart: 10 + minimumPlayers: 40 + - type: CargoGiftsRule + description: cargo-gift-space-protection + dest: cargo-gift-dest-supp + gifts: + EmergencyInternalsLarge: 2 + EmergencyInflatablewall: 1 + EmergencyAdvancedKit: 1 + MedicalBiosuit: 1 + EmergencyO2Kit: 1 - type: entity id: GiftsFireProtection - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 4 - startDelay: 10 - duration: 120 - earliestStart: 20 - minimumPlayers: 40 - - type: CargoGiftsRule - description: cargo-gift-fire-protection - sender: cargo-gift-default-sender - dest: cargo-gift-dest-supp - gifts: - EmergencyFire: 2 - EmergencyBurnKit: 1 - EmergencyBruteKit: 1 + - type: StationEvent + weight: 4 + duration: 120 + earliestStart: 20 + minimumPlayers: 40 + - type: CargoGiftsRule + description: cargo-gift-fire-protection + dest: cargo-gift-dest-supp + gifts: + EmergencyFire: 2 + EmergencyBurnKit: 1 + EmergencyBruteKit: 1 - type: entity id: GiftsSecurityGuns - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 3 - startDelay: 10 - duration: 120 - earliestStart: 20 - minimumPlayers: 50 - - type: CargoGiftsRule - description: cargo-gift-security-guns - sender: cargo-gift-default-sender - dest: cargo-gift-dest-sec - gifts: - SecurityArmor: 3 - ArmorySmg: 1 - ArmoryShotgun: 1 - ArmoryLaser: 1 + - type: StationEvent + weight: 3 + duration: 120 + earliestStart: 20 + minimumPlayers: 50 + - type: CargoGiftsRule + description: cargo-gift-security-guns + dest: cargo-gift-dest-sec + gifts: + SecurityArmor: 3 + ArmorySmg: 1 + ArmoryShotgun: 1 + ArmoryLaser: 1 - type: entity id: GiftsSecurityRiot - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 4 - startDelay: 10 - duration: 120 - earliestStart: 20 - minimumPlayers: 50 - - type: CargoGiftsRule - description: cargo-gift-security-riot - sender: cargo-gift-default-sender - dest: cargo-gift-dest-sec - gifts: - SecurityRiot: 2 - SecurityRestraints: 2 - SecurityNonLethal: 2 + - type: StationEvent + weight: 4 + duration: 120 + earliestStart: 20 + minimumPlayers: 50 + - type: CargoGiftsRule + description: cargo-gift-security-riot + dest: cargo-gift-dest-sec + gifts: + SecurityRiot: 2 + SecurityRestraints: 2 + SecurityNonLethal: 2 diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 9dabc14a344e..9f6e6a812574 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -1,22 +1,63 @@ - type: entity - id: AnomalySpawn + id: BaseStationEvent + parent: BaseGameRule + abstract: true + noSpawn: true + components: + - type: GameRule + delay: + min: 10 + max: 20 + +- type: entity + id: BaseStationEventShortDelay parent: BaseGameRule + abstract: true + noSpawn: true + components: + - type: GameRule + delay: + min: 10 + max: 20 + +- type: entity + id: BaseStationEventLongDelay + parent: BaseGameRule + abstract: true + noSpawn: true + components: + - type: GameRule + delay: + min: 40 + max: 60 + +- type: entity + id: AnomalySpawn + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent + startAnnouncementColor: "#18abf5" + startAudio: + path: /Audio/Announcements/announce.ogg weight: 8 - startDelay: 30 duration: 35 - type: AnomalySpawnRule - type: entity id: BluespaceArtifact - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: + - type: GameRule + delay: + min: 30 + max: 30 - type: StationEvent + startAnnouncementColor: "#18abf5" + startAudio: + path: /Audio/Announcements/announce.ogg weight: 8 - startDelay: 30 duration: 35 - type: BluespaceArtifactRule @@ -148,7 +189,7 @@ - type: entity id: GasLeak - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent @@ -157,25 +198,23 @@ path: /Audio/Announcements/attention.ogg endAnnouncement: station-event-gas-leak-end-announcement weight: 8 - startDelay: 20 - type: GasLeakRule - type: entity id: KudzuGrowth - parent: BaseGameRule + parent: BaseStationEventLongDelay noSpawn: true components: - type: StationEvent earliestStart: 15 minimumPlayers: 15 weight: 7 - startDelay: 50 duration: 240 - type: KudzuGrowthRule - type: entity id: MeteorSwarm - parent: BaseGameRule + parent: BaseStationEventLongDelay noSpawn: true components: - type: StationEvent @@ -189,19 +228,17 @@ params: volume: -4 duration: null #ending is handled by MeteorSwarmRule - startDelay: 30 - type: MeteorSwarmRule - type: entity id: MouseMigration - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-creatures-start-announcement startAudio: path: /Audio/Announcements/attention.ogg - startDelay: 10 earliestStart: 15 weight: 6 duration: 50 @@ -221,14 +258,13 @@ - type: entity id: CockroachMigration - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-creatures-start-announcement startAudio: path: /Audio/Announcements/attention.ogg - startDelay: 10 weight: 6 duration: 50 - type: VentCrittersRule @@ -240,7 +276,7 @@ - type: entity id: PowerGridCheck - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent @@ -251,7 +287,6 @@ path: /Audio/Announcements/power_off.ogg params: volume: -4 - startDelay: 24 duration: 60 maxDuration: 120 - type: PowerGridCheckRule @@ -300,7 +335,7 @@ - type: entity id: VentClog - parent: BaseGameRule + parent: BaseStationEventLongDelay noSpawn: true components: - type: StationEvent @@ -310,20 +345,18 @@ earliestStart: 15 minimumPlayers: 15 weight: 5 - startDelay: 50 duration: 60 - type: VentClogRule - type: entity id: SlimesSpawn - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-creatures-start-announcement startAudio: path: /Audio/Announcements/attention.ogg - startDelay: 10 earliestStart: 20 minimumPlayers: 15 weight: 5 @@ -339,14 +372,13 @@ - type: entity id: SpiderSpawn - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-creatures-start-announcement startAudio: path: /Audio/Announcements/attention.ogg - startDelay: 10 earliestStart: 20 minimumPlayers: 15 weight: 5 @@ -358,14 +390,13 @@ - type: entity id: SpiderClownSpawn - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-creatures-start-announcement startAudio: path: /Audio/Announcements/attention.ogg - startDelay: 10 earliestStart: 20 minimumPlayers: 20 weight: 1.5 From ad1fc0cdba0f07f45ff18cee46b28738c226a8b4 Mon Sep 17 00:00:00 2001 From: Kevin Zheng Date: Sat, 1 Jun 2024 12:56:57 -0800 Subject: [PATCH 206/568] Adjust power monitor power display (#28487) --- Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs | 4 +++- .../Locale/en-US/components/power-monitoring-component.ftl | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs b/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs index 25a586a75de8..1427df051534 100644 --- a/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs +++ b/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs @@ -102,7 +102,8 @@ public void UpdateWindowEntryButton(NetEntity netEntity, PowerMonitoringButton b button.ToolTip = Loc.GetString(name); // Update power value - button.PowerValue.Text = Loc.GetString("power-monitoring-window-value", ("value", entry.PowerValue)); + // Don't use SI prefixes, just give the number in W, so that it is readily apparent which consumer is using a lot of power. + button.PowerValue.Text = Loc.GetString("power-monitoring-window-button-value", ("value", Math.Round(entry.PowerValue).ToString("N0"))); } private void UpdateEntrySourcesOrLoads(BoxContainer masterContainer, BoxContainer currentContainer, PowerMonitoringConsoleEntry[]? entries, SpriteSpecifier.Texture icon) @@ -480,6 +481,7 @@ public PowerMonitoringButton() PowerValue = new Label() { HorizontalAlignment = HAlignment.Right, + Align = Label.AlignMode.Right, SetWidth = 72f, Margin = new Thickness(10, 0, 0, 0), ClipText = true, diff --git a/Resources/Locale/en-US/components/power-monitoring-component.ftl b/Resources/Locale/en-US/components/power-monitoring-component.ftl index e84c09e60d5b..9433c9ea9ef9 100644 --- a/Resources/Locale/en-US/components/power-monitoring-component.ftl +++ b/Resources/Locale/en-US/components/power-monitoring-component.ftl @@ -14,6 +14,7 @@ power-monitoring-window-total-sources = Total generator output power-monitoring-window-total-battery-usage = Total battery usage power-monitoring-window-total-loads = Total network loads power-monitoring-window-value = { POWERWATTS($value) } +power-monitoring-window-button-value = {$value} W power-monitoring-window-show-inactive-consumers = Show Inactive Consumers power-monitoring-window-show-cable-networks = Toggle cable networks From 07dfefc4a29927aa74cfd5c7e75c6cb344d713f6 Mon Sep 17 00:00:00 2001 From: Tornado Tech <54727692+Tornado-Technology@users.noreply.github.com> Date: Sun, 2 Jun 2024 09:49:34 +1000 Subject: [PATCH 207/568] Hiding and clearing department prototype code (#28114) --- .../UI/BanPanel/BanPanel.xaml.cs | 2 +- .../Lobby/UI/HumanoidProfileEditor.xaml.cs | 34 ++++++++++++----- .../Conditions/BuyerDepartmentCondition.cs | 4 +- Content.Shared/Roles/DepartmentPrototype.cs | 37 +++++++++++-------- 4 files changed, 49 insertions(+), 28 deletions(-) diff --git a/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs b/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs index dc263d6055ce..588d62e56036 100644 --- a/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs +++ b/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs @@ -147,7 +147,7 @@ public BanPanel() var prototypeManager = IoCManager.Resolve(); foreach (var proto in prototypeManager.EnumeratePrototypes()) { - CreateRoleGroup(proto.ID, proto.Roles, proto.Color); + CreateRoleGroup(proto.ID, proto.Roles.Select(p => p.Id), proto.Color); } CreateRoleGroup("Antagonist", prototypeManager.EnumeratePrototypes().Select(p => p.ID), Color.Red); diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index a0e32a3a4997..eb182c83ee11 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -719,8 +719,17 @@ public void RefreshJobs() _jobPriorities.Clear(); var firstCategory = true; - var departments = _prototypeManager.EnumeratePrototypes().ToArray(); - Array.Sort(departments, DepartmentUIComparer.Instance); + // Get all displayed departments + var departments = new List(); + foreach (var department in _prototypeManager.EnumeratePrototypes()) + { + if (department.EditorHidden) + continue; + + departments.Add(department); + } + + departments.Sort(DepartmentUIComparer.Instance); var items = new[] { @@ -774,7 +783,7 @@ public void RefreshJobs() JobList.AddChild(category); } - var jobs = department.Roles.Select(jobId => _prototypeManager.Index(jobId)) + var jobs = department.Roles.Select(jobId => _prototypeManager.Index(jobId)) .Where(job => job.SetPreference) .ToArray(); @@ -821,13 +830,15 @@ public void RefreshJobs() if (jobId == job.ID) { other.Select(selectedPrio); + continue; } - else if (selectedJobPrio == JobPriority.High && (JobPriority) other.Selected == JobPriority.High) - { - // Lower any other high priorities to medium. - other.Select((int) JobPriority.Medium); - Profile = Profile?.WithJobPriority(jobId, JobPriority.Medium); - } + + if (selectedJobPrio != JobPriority.High || (JobPriority) other.Selected != JobPriority.High) + continue; + + // Lower any other high priorities to medium. + other.Select((int)JobPriority.Medium); + Profile = Profile?.WithJobPriority(jobId, JobPriority.Medium); } // TODO: Only reload on high change (either to or from). @@ -932,6 +943,11 @@ private void OpenLoadout(JobPrototype? jobProto, RoleLoadout roleLoadout, RoleLo SetDirty(); ReloadPreview(); }; + + if (Profile is null) + return; + + UpdateJobPriorities(); } private void OnFlavorTextChange(string content) diff --git a/Content.Server/Store/Conditions/BuyerDepartmentCondition.cs b/Content.Server/Store/Conditions/BuyerDepartmentCondition.cs index 4e5e504aec4b..ea8de4a9ccd6 100644 --- a/Content.Server/Store/Conditions/BuyerDepartmentCondition.cs +++ b/Content.Server/Store/Conditions/BuyerDepartmentCondition.cs @@ -43,7 +43,7 @@ public override bool Condition(ListingConditionArgs args) { foreach (var department in prototypeManager.EnumeratePrototypes()) { - if (department.Roles.Contains(job.Prototype) && Blacklist.Contains(department.ID)) + if (department.Roles.Contains(job.Prototype.Value) && Blacklist.Contains(department.ID)) return false; } } @@ -56,7 +56,7 @@ public override bool Condition(ListingConditionArgs args) { foreach (var department in prototypeManager.EnumeratePrototypes()) { - if (department.Roles.Contains(job.Prototype) && Whitelist.Contains(department.ID)) + if (department.Roles.Contains(job.Prototype.Value) && Whitelist.Contains(department.ID)) { found = true; break; diff --git a/Content.Shared/Roles/DepartmentPrototype.cs b/Content.Shared/Roles/DepartmentPrototype.cs index 024eca37fa2d..d6288bec9098 100644 --- a/Content.Shared/Roles/DepartmentPrototype.cs +++ b/Content.Shared/Roles/DepartmentPrototype.cs @@ -1,28 +1,27 @@ using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Shared.Roles; [Prototype("department")] public sealed partial class DepartmentPrototype : IPrototype { - [IdDataField] public string ID { get; } = default!; + [IdDataField] + public string ID { get; } = string.Empty; /// - /// A description string to display in the character menu as an explanation of the department's function. + /// A description string to display in the character menu as an explanation of the department's function. /// - [DataField("description", required: true)] - public string Description = default!; + [DataField(required: true)] + public string Description = string.Empty; /// - /// A color representing this department to use for text. + /// A color representing this department to use for text. /// - [DataField("color", required: true)] - public Color Color = default!; + [DataField(required: true)] + public Color Color; - [ViewVariables(VVAccess.ReadWrite), - DataField("roles", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List Roles = new(); + [DataField, ViewVariables(VVAccess.ReadWrite)] + public List> Roles = new(); /// /// Whether this is a primary department or not. @@ -34,8 +33,14 @@ public sealed partial class DepartmentPrototype : IPrototype /// /// Departments with a higher weight sorted before other departments in UI. /// - [DataField("weight")] - public int Weight { get; private set; } = 0; + [DataField] + public int Weight { get; private set; } + + /// + /// Toggles the display of the department in the priority setting menu in the character editor. + /// + [DataField] + public bool EditorHidden; } /// @@ -50,14 +55,14 @@ public int Compare(DepartmentPrototype? x, DepartmentPrototype? y) { if (ReferenceEquals(x, y)) return 0; + if (ReferenceEquals(null, y)) return 1; + if (ReferenceEquals(null, x)) return -1; var cmp = -x.Weight.CompareTo(y.Weight); - if (cmp != 0) - return cmp; - return string.Compare(x.ID, y.ID, StringComparison.Ordinal); + return cmp != 0 ? cmp : string.Compare(x.ID, y.ID, StringComparison.Ordinal); } } From da6a1bbdd4a0d69b101d2c85e858b0b3cbf7d552 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 03:03:51 +0200 Subject: [PATCH 208/568] Update Credits (#28493) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index b7ec22c0b0db..b3627580b1b8 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, bhenrich, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blueDev2, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, DexlerXD, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, DuskyJay, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, Jark255, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, KEEYNy, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, MishaUnity, MisterMecky, Mith-randalf, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, Subversionary, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, waylon531, weaversam8, whateverusername0, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem +0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, bhenrich, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blueDev2, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, DexlerXD, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, DuskyJay, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, Jark255, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, waylon531, weaversam8, whateverusername0, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem From 21bc3bfa22b6e234a8ae96c3332bffee6840a66f Mon Sep 17 00:00:00 2001 From: Tornado Tech <54727692+Tornado-Technology@users.noreply.github.com> Date: Sun, 2 Jun 2024 11:11:19 +1000 Subject: [PATCH 209/568] Cleans up tag system (#28272) * Updated tag system * Added params methods * Fixed tag integration tests * Fixed params methods recursion * Revert has All/Any tag one argument realisation * Updated tag integration tests * Shit happens * Added individual List/HashSet methods, docs, tests --- Content.Client/Verbs/VerbSystem.cs | 3 +- Content.IntegrationTests/Tests/Tag/TagTest.cs | 161 ++-- .../Administration/Toolshed/TagCommand.cs | 11 +- .../Mech/Systems/MechAssemblySystem.cs | 3 +- .../Procedural/DungeonJob.PostGen.cs | 5 +- Content.Server/Procedural/DungeonJob.cs | 5 +- Content.Server/Procedural/DungeonSystem.cs | 4 + .../EntitySystems/RevenantSystem.Abilities.cs | 2 +- Content.Server/Spreader/SpreaderSystem.cs | 10 +- .../SharedChameleonClothingSystem.cs | 5 +- .../Conditions/NoWindowsInTile.cs | 3 +- .../EntitySystems/AnchorableSystem.cs | 11 +- .../MultipleTagsConstructionGraphStep.cs | 13 +- .../Pinpointer/SharedNavMapSystem.cs | 5 +- Content.Shared/Tag/TagComponent.cs | 16 +- Content.Shared/Tag/TagComponentState.cs | 15 - Content.Shared/Tag/TagPrototype.cs | 24 +- Content.Shared/Tag/TagSystem.cs | 768 +++++++++--------- 18 files changed, 559 insertions(+), 505 deletions(-) delete mode 100644 Content.Shared/Tag/TagComponentState.cs diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 2e6c5f58099e..5f1f49e5fd08 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -123,7 +123,6 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true if ((visibility & MenuVisibility.Invisible) == 0) { var spriteQuery = GetEntityQuery(); - var tagQuery = GetEntityQuery(); for (var i = entities.Count - 1; i >= 0; i--) { @@ -131,7 +130,7 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true if (!spriteQuery.TryGetComponent(entity, out var spriteComponent) || !spriteComponent.Visible || - _tagSystem.HasTag(entity, "HideContextMenu", tagQuery)) + _tagSystem.HasTag(entity, "HideContextMenu")) { entities.RemoveSwap(i); } diff --git a/Content.IntegrationTests/Tests/Tag/TagTest.cs b/Content.IntegrationTests/Tests/Tag/TagTest.cs index cbcdd1c6c623..e6cd2accaf56 100644 --- a/Content.IntegrationTests/Tests/Tag/TagTest.cs +++ b/Content.IntegrationTests/Tests/Tag/TagTest.cs @@ -53,11 +53,13 @@ public async Task TagComponentTest() EntityUid sTagDummy = default; TagComponent sTagComponent = null!; + Entity sTagEntity = default; await server.WaitPost(() => { sTagDummy = sEntityManager.SpawnEntity(TagEntityId, MapCoordinates.Nullspace); sTagComponent = sEntityManager.GetComponent(sTagDummy); + sTagEntity = new Entity(sTagDummy, sTagComponent); }); await server.WaitAssertion(() => @@ -130,49 +132,64 @@ await server.WaitAssertion(() => Assert.Multiple(() => { // Cannot add the starting tag again - Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, StartingTag), Is.False); - Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, StartingTag), Is.False); - Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List { StartingTag, StartingTag }), Is.False); + Assert.That(tagSystem.AddTag(sTagEntity, StartingTag), Is.False); + + Assert.That(tagSystem.AddTags(sTagEntity, StartingTag, StartingTag), Is.False); + Assert.That(tagSystem.AddTags(sTagEntity, new List> { StartingTag, StartingTag }), Is.False); + Assert.That(tagSystem.AddTags(sTagEntity, new HashSet> { StartingTag, StartingTag }), Is.False); // Has the starting tag Assert.That(tagSystem.HasTag(sTagComponent, StartingTag), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, StartingTag), Is.True); - Assert.That(tagSystem.HasAllTags(sTagComponent, new List { StartingTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List> { StartingTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet> { StartingTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, StartingTag), Is.True); - Assert.That(tagSystem.HasAnyTag(sTagComponent, new List { StartingTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List> { StartingTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet> { StartingTag, StartingTag }), Is.True); // Does not have the added tag yet Assert.That(tagSystem.HasTag(sTagComponent, AddedTag), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, AddedTag, AddedTag), Is.False); - Assert.That(tagSystem.HasAllTags(sTagComponent, new List { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List> { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet> { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAnyTag(sTagComponent, AddedTag, AddedTag), Is.False); - Assert.That(tagSystem.HasAnyTag(sTagComponent, new List { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List> { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet> { AddedTag, AddedTag }), Is.False); // Has a combination of the two tags Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, AddedTag), Is.True); - Assert.That(tagSystem.HasAnyTag(sTagComponent, new List { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List> { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet> { StartingTag, AddedTag }), Is.True); // Does not have both tags Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, AddedTag), Is.False); - Assert.That(tagSystem.HasAllTags(sTagComponent, new List { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List> { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet> { StartingTag, AddedTag }), Is.False); // Cannot remove a tag that does not exist - Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, AddedTag), Is.False); - Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.False); - Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.RemoveTag(sTagEntity, AddedTag), Is.False); + + Assert.That(tagSystem.RemoveTags(sTagEntity, AddedTag, AddedTag), Is.False); + Assert.That(tagSystem.RemoveTags(sTagEntity, new List> { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.RemoveTags(sTagEntity, new HashSet> { AddedTag, AddedTag }), Is.False); }); // Can add the new tag - Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.True); + Assert.That(tagSystem.AddTag(sTagEntity, AddedTag), Is.True); Assert.Multiple(() => { // Cannot add it twice - Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.False); + Assert.That(tagSystem.AddTag(sTagEntity, AddedTag), Is.False); // Cannot add existing tags - Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, AddedTag), Is.False); - Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.AddTags(sTagEntity, StartingTag, AddedTag), Is.False); + Assert.That(tagSystem.AddTags(sTagEntity, new List> { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.AddTags(sTagEntity, new HashSet> { StartingTag, AddedTag }), Is.False); // Now has two tags Assert.That(sTagComponent.Tags, Has.Count.EqualTo(2)); @@ -180,65 +197,103 @@ await server.WaitAssertion(() => // Has both tags Assert.That(tagSystem.HasTag(sTagComponent, StartingTag), Is.True); Assert.That(tagSystem.HasTag(sTagComponent, AddedTag), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, StartingTag), Is.True); Assert.That(tagSystem.HasAllTags(sTagComponent, AddedTag, StartingTag), Is.True); - Assert.That(tagSystem.HasAllTags(sTagComponent, new List { StartingTag, AddedTag }), Is.True); - Assert.That(tagSystem.HasAllTags(sTagComponent, new List { AddedTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List> { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List> { AddedTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet> { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet> { AddedTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, AddedTag), Is.True); Assert.That(tagSystem.HasAnyTag(sTagComponent, AddedTag, StartingTag), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List> { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List> { AddedTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet> { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet> { AddedTag, StartingTag }), Is.True); }); Assert.Multiple(() => { // Remove the existing starting tag - Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, StartingTag), Is.True); + Assert.That(tagSystem.RemoveTag(sTagEntity, StartingTag), Is.True); // Remove the existing added tag - Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.True); + Assert.That(tagSystem.RemoveTags(sTagEntity, AddedTag, AddedTag), Is.True); }); Assert.Multiple(() => { // No tags left to remove - Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.RemoveTags(sTagEntity, new List> { StartingTag, AddedTag }), Is.False); // No tags left in the component Assert.That(sTagComponent.Tags, Is.Empty); }); -#if !DEBUG - return; + // It is run only in DEBUG build, + // as the checks are performed only in DEBUG build. +#if DEBUG + // Has single + Assert.Throws(() => { tagSystem.HasTag(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasTag(sTagComponent, UnregisteredTag); }); + + // HasAny entityUid methods + Assert.Throws(() => { tagSystem.HasAnyTag(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagDummy, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagDummy, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagDummy, new HashSet> { UnregisteredTag }); }); + + // HasAny component methods + Assert.Throws(() => { tagSystem.HasAnyTag(sTagComponent, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagComponent, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagComponent, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagComponent, new HashSet> { UnregisteredTag }); }); + + // HasAll entityUid methods + Assert.Throws(() => { tagSystem.HasAllTags(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagDummy, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagDummy, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagDummy, new HashSet> { UnregisteredTag }); }); + + // HasAll component methods + Assert.Throws(() => { tagSystem.HasAllTags(sTagComponent, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagComponent, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagComponent, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagComponent, new HashSet> { UnregisteredTag }); }); + + // RemoveTag single + Assert.Throws(() => { tagSystem.RemoveTag(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.RemoveTag(sTagEntity, UnregisteredTag); }); + + // RemoveTags entityUid methods + Assert.Throws(() => { tagSystem.RemoveTags(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagDummy, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagDummy, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagDummy, new HashSet> { UnregisteredTag }); }); + + // RemoveTags entity methods + Assert.Throws(() => { tagSystem.RemoveTags(sTagEntity, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagEntity, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagEntity, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagEntity, new HashSet> { UnregisteredTag }); }); + + // AddTag single + Assert.Throws(() => { tagSystem.AddTag(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.AddTag(sTagEntity, UnregisteredTag); }); + + // AddTags entityUid methods + Assert.Throws(() => { tagSystem.AddTags(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.AddTags(sTagDummy, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.AddTags(sTagDummy, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.AddTags(sTagDummy, new HashSet> { UnregisteredTag }); }); + + // AddTags entity methods + Assert.Throws(() => { tagSystem.AddTags(sTagEntity, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.AddTags(sTagEntity, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.AddTags(sTagEntity, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.AddTags(sTagEntity, new HashSet> { UnregisteredTag }); }); #endif - - // Single - Assert.Throws(() => - { - tagSystem.HasTag(sTagDummy, UnregisteredTag); - }); - Assert.Throws(() => - { - tagSystem.HasTag(sTagComponent, UnregisteredTag); - }); - - // Any - Assert.Throws(() => - { - tagSystem.HasAnyTag(sTagDummy, UnregisteredTag); - }); - Assert.Throws(() => - { - tagSystem.HasAnyTag(sTagComponent, UnregisteredTag); - }); - - // All - Assert.Throws(() => - { - tagSystem.HasAllTags(sTagDummy, UnregisteredTag); - }); - Assert.Throws(() => - { - tagSystem.HasAllTags(sTagComponent, UnregisteredTag); - }); }); await pair.CleanReturnAsync(); } diff --git a/Content.Server/Administration/Toolshed/TagCommand.cs b/Content.Server/Administration/Toolshed/TagCommand.cs index 1af27797660d..e1cf53e1b1ab 100644 --- a/Content.Server/Administration/Toolshed/TagCommand.cs +++ b/Content.Server/Administration/Toolshed/TagCommand.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Shared.Administration; using Content.Shared.Tag; +using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; using Robust.Shared.Toolshed.Syntax; using Robust.Shared.Toolshed.TypeParsers; @@ -13,14 +14,14 @@ public sealed class TagCommand : ToolshedCommand private TagSystem? _tag; [CommandImplementation("list")] - public IEnumerable List([PipedArgument] IEnumerable ent) + public IEnumerable> List([PipedArgument] IEnumerable ent) { return ent.SelectMany(x => { if (TryComp(x, out var tags)) // Note: Cast is required for C# to figure out the type signature. - return (IEnumerable)tags.Tags; - return Array.Empty(); + return (IEnumerable>)tags.Tags; + return Array.Empty>(); }); } @@ -72,7 +73,7 @@ [CommandArgument] ValueRef, IEnumerable> @ref ) { _tag ??= GetSys(); - _tag.AddTags(input, @ref.Evaluate(ctx)!); + _tag.AddTags(input, (IEnumerable>)@ref.Evaluate(ctx)!); return input; } @@ -92,7 +93,7 @@ [CommandArgument] ValueRef, IEnumerable> @ref ) { _tag ??= GetSys(); - _tag.RemoveTags(input, @ref.Evaluate(ctx)!); + _tag.RemoveTags(input, (IEnumerable>)@ref.Evaluate(ctx)!); return input; } diff --git a/Content.Server/Mech/Systems/MechAssemblySystem.cs b/Content.Server/Mech/Systems/MechAssemblySystem.cs index e5b7bfaac3c8..c1fff819b4c9 100644 --- a/Content.Server/Mech/Systems/MechAssemblySystem.cs +++ b/Content.Server/Mech/Systems/MechAssemblySystem.cs @@ -14,6 +14,7 @@ namespace Content.Server.Mech.Systems; public sealed class MechAssemblySystem : EntitySystem { [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly TagSystem _tag = default!; /// public override void Initialize() @@ -44,7 +45,7 @@ private void OnInteractUsing(EntityUid uid, MechAssemblyComponent component, Int foreach (var (tag, val) in component.RequiredParts) { - if (!val && tagComp.Tags.Contains(tag)) + if (!val && _tag.HasTag(tagComp, tag)) { component.RequiredParts[tag] = true; _container.Insert(args.Used, component.PartsContainer); diff --git a/Content.Server/Procedural/DungeonJob.PostGen.cs b/Content.Server/Procedural/DungeonJob.PostGen.cs index b326bcc378f6..cb9e64f04e25 100644 --- a/Content.Server/Procedural/DungeonJob.PostGen.cs +++ b/Content.Server/Procedural/DungeonJob.PostGen.cs @@ -13,6 +13,7 @@ using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -24,13 +25,15 @@ public sealed partial class DungeonJob * Run after the main dungeon generation */ + private static readonly ProtoId WallTag = "Wall"; + private bool HasWall(MapGridComponent grid, Vector2i tile) { var anchored = _maps.GetAnchoredEntitiesEnumerator(_gridUid, _grid, tile); while (anchored.MoveNext(out var uid)) { - if (_tagQuery.TryGetComponent(uid, out var tagComp) && tagComp.Tags.Contains("Wall")) + if (_tag.HasTag(uid.Value, WallTag)) return true; } diff --git a/Content.Server/Procedural/DungeonJob.cs b/Content.Server/Procedural/DungeonJob.cs index 8fecf1c9e8e5..bf2822ff4237 100644 --- a/Content.Server/Procedural/DungeonJob.cs +++ b/Content.Server/Procedural/DungeonJob.cs @@ -28,10 +28,10 @@ public sealed partial class DungeonJob : Job private readonly DecalSystem _decals; private readonly DungeonSystem _dungeon; private readonly EntityLookupSystem _lookup; + private readonly TagSystem _tag; private readonly TileSystem _tile; private readonly SharedMapSystem _maps; private readonly SharedTransformSystem _transform; - private EntityQuery _tagQuery; private readonly DungeonConfigPrototype _gen; private readonly int _seed; @@ -53,6 +53,7 @@ public DungeonJob( DecalSystem decals, DungeonSystem dungeon, EntityLookupSystem lookup, + TagSystem tag, TileSystem tile, SharedTransformSystem transform, DungeonConfigPrototype gen, @@ -72,10 +73,10 @@ public DungeonJob( _decals = decals; _dungeon = dungeon; _lookup = lookup; + _tag = tag; _tile = tile; _maps = _entManager.System(); _transform = transform; - _tagQuery = _entManager.GetEntityQuery(); _gen = gen; _grid = grid; diff --git a/Content.Server/Procedural/DungeonSystem.cs b/Content.Server/Procedural/DungeonSystem.cs index 069508bcbbba..36009896a2cb 100644 --- a/Content.Server/Procedural/DungeonSystem.cs +++ b/Content.Server/Procedural/DungeonSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Maps; using Content.Shared.Physics; using Content.Shared.Procedural; +using Content.Shared.Tag; using Robust.Server.GameObjects; using Robust.Shared.Configuration; using Robust.Shared.Console; @@ -31,6 +32,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem [Dependency] private readonly AnchorableSystem _anchorable = default!; [Dependency] private readonly DecalSystem _decals = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly TileSystem _tile = default!; [Dependency] private readonly MapLoaderSystem _loader = default!; [Dependency] private readonly SharedMapSystem _maps = default!; @@ -199,6 +201,7 @@ public void GenerateDungeon(DungeonConfigPrototype gen, _decals, this, _lookup, + _tag, _tile, _transform, gen, @@ -231,6 +234,7 @@ public async Task GenerateDungeonAsync( _decals, this, _lookup, + _tag, _tile, _transform, gen, diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index fd7d15ea5dce..670c64577a48 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -246,7 +246,7 @@ private void OnDefileAction(EntityUid uid, RevenantComponent component, Revenant foreach (var ent in lookup) { //break windows - if (tags.HasComponent(ent) && _tag.HasAnyTag(ent, "Window")) + if (tags.HasComponent(ent) && _tag.HasTag(ent, "Window")) { //hardcoded damage specifiers til i die. var dspec = new DamageSpecifier(); diff --git a/Content.Server/Spreader/SpreaderSystem.cs b/Content.Server/Spreader/SpreaderSystem.cs index fe14d86aa1da..7de8a43d354f 100644 --- a/Content.Server/Spreader/SpreaderSystem.cs +++ b/Content.Server/Spreader/SpreaderSystem.cs @@ -21,6 +21,7 @@ public sealed class SpreaderSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly TagSystem _tag = default!; /// /// Cached maximum number of updates per spreader prototype. This is applied per-grid. @@ -37,8 +38,7 @@ public sealed class SpreaderSystem : EntitySystem public const float SpreadCooldownSeconds = 1; - [ValidatePrototypeId] - private const string IgnoredTag = "SpreaderIgnore"; + private static readonly ProtoId IgnoredTag = "SpreaderIgnore"; /// public override void Initialize() @@ -189,7 +189,6 @@ public void GetNeighbors(EntityUid uid, TransformComponent comp, ProtoId(); var dockQuery = GetEntityQuery(); var xformQuery = GetEntityQuery(); - var tagQuery = GetEntityQuery(); var blockedAtmosDirs = AtmosDirection.Invalid; // Due to docking ports they may not necessarily be opposite directions. @@ -212,7 +211,7 @@ public void GetNeighbors(EntityUid uid, TransformComponent comp, ProtoId(); - var tagQuery = entManager.GetEntityQuery(); var sysMan = entManager.EntitySysManager; var tagSystem = sysMan.GetEntitySystem(); foreach (var entity in location.GetEntitiesInTile(LookupFlags.Static)) { - if (tagSystem.HasTag(entity, "Window", tagQuery)) + if (tagSystem.HasTag(entity, "Window")) return false; } diff --git a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs index c041cf1ba069..e9ef053f629f 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -15,6 +15,7 @@ using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Content.Shared.Tag; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem; @@ -32,16 +33,14 @@ public sealed partial class AnchorableSystem : EntitySystem [Dependency] private readonly TagSystem _tagSystem = default!; private EntityQuery _physicsQuery; - private EntityQuery _tagQuery; - public const string Unstackable = "Unstackable"; + public readonly ProtoId Unstackable = "Unstackable"; public override void Initialize() { base.Initialize(); _physicsQuery = GetEntityQuery(); - _tagQuery = GetEntityQuery(); SubscribeLocalEvent(OnInteractUsing, before: new[] { typeof(ItemSlotsSystem) }, after: new[] { typeof(SharedConstructionSystem) }); @@ -312,7 +311,7 @@ public bool AnyUnstackable(EntityUid uid, EntityCoordinates location) DebugTools.Assert(!Transform(uid).Anchored); // If we are unstackable, iterate through any other entities anchored on the current square - return _tagSystem.HasTag(uid, Unstackable, _tagQuery) && AnyUnstackablesAnchoredAt(location); + return _tagSystem.HasTag(uid, Unstackable) && AnyUnstackablesAnchoredAt(location); } public bool AnyUnstackablesAnchoredAt(EntityCoordinates location) @@ -327,10 +326,8 @@ public bool AnyUnstackablesAnchoredAt(EntityCoordinates location) while (enumerator.MoveNext(out var entity)) { // If we find another unstackable here, return true. - if (_tagSystem.HasTag(entity.Value, Unstackable, _tagQuery)) - { + if (_tagSystem.HasTag(entity.Value, Unstackable)) return true; - } } return false; diff --git a/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs b/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs index 668952dac23d..07ba46946a61 100644 --- a/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs +++ b/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs @@ -1,14 +1,15 @@ using Content.Shared.Tag; +using Robust.Shared.Prototypes; namespace Content.Shared.Construction.Steps { public sealed partial class MultipleTagsConstructionGraphStep : ArbitraryInsertConstructionGraphStep { [DataField("allTags")] - private List? _allTags; + private List>? _allTags; [DataField("anyTags")] - private List? _anyTags; + private List>? _anyTags; private static bool IsNullOrEmpty(ICollection? list) { @@ -21,16 +22,12 @@ public override bool EntityValid(EntityUid uid, IEntityManager entityManager, IC if (IsNullOrEmpty(_allTags) && IsNullOrEmpty(_anyTags)) return false; // Step is somehow invalid, we return. - // No tags at all. - if (!entityManager.TryGetComponent(uid, out TagComponent? tags)) - return false; - var tagSystem = entityManager.EntitySysManager.GetEntitySystem(); - if (_allTags != null && !tagSystem.HasAllTags(tags, _allTags)) + if (_allTags != null && !tagSystem.HasAllTags(uid, _allTags)) return false; // We don't have all the tags needed. - if (_anyTags != null && !tagSystem.HasAnyTag(tags, _anyTags)) + if (_anyTags != null && !tagSystem.HasAnyTag(uid, _anyTags)) return false; // We don't have any of the tags needed. // This entity is valid! diff --git a/Content.Shared/Pinpointer/SharedNavMapSystem.cs b/Content.Shared/Pinpointer/SharedNavMapSystem.cs index 7c12321b5db6..3ced5f3c9ed5 100644 --- a/Content.Shared/Pinpointer/SharedNavMapSystem.cs +++ b/Content.Shared/Pinpointer/SharedNavMapSystem.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using Content.Shared.Tag; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -24,7 +25,7 @@ public abstract class SharedNavMapSystem : EntitySystem [Robust.Shared.IoC.Dependency] private readonly TagSystem _tagSystem = default!; - private readonly string[] _wallTags = ["Wall", "Window"]; + private static readonly ProtoId[] WallTags = {"Wall", "Window"}; private EntityQuery _doorQuery; public override void Initialize() @@ -58,7 +59,7 @@ public NavMapChunkType GetEntityType(EntityUid uid) if (_doorQuery.HasComp(uid)) return NavMapChunkType.Airlock; - if (_tagSystem.HasAnyTag(uid, _wallTags)) + if (_tagSystem.HasAnyTag(uid, WallTags)) return NavMapChunkType.Wall; return NavMapChunkType.Invalid; diff --git a/Content.Shared/Tag/TagComponent.cs b/Content.Shared/Tag/TagComponent.cs index b5b8a48a4415..ad4240ba06c7 100644 --- a/Content.Shared/Tag/TagComponent.cs +++ b/Content.Shared/Tag/TagComponent.cs @@ -1,13 +1,11 @@ using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; +using Robust.Shared.Prototypes; -namespace Content.Shared.Tag +namespace Content.Shared.Tag; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(TagSystem))] +public sealed partial class TagComponent : Component { - [RegisterComponent, NetworkedComponent, Access(typeof(TagSystem))] - public sealed partial class TagComponent : Component - { - [DataField("tags", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - [Access(typeof(TagSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends - public HashSet Tags = new(); - } + [DataField, ViewVariables, AutoNetworkedField] + public HashSet> Tags = new(); } diff --git a/Content.Shared/Tag/TagComponentState.cs b/Content.Shared/Tag/TagComponentState.cs deleted file mode 100644 index 9919aba108da..000000000000 --- a/Content.Shared/Tag/TagComponentState.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Tag -{ - [Serializable, NetSerializable] - public sealed class TagComponentState : ComponentState - { - public TagComponentState(string[] tags) - { - Tags = tags; - } - - public string[] Tags { get; } - } -} diff --git a/Content.Shared/Tag/TagPrototype.cs b/Content.Shared/Tag/TagPrototype.cs index 2a06e22cf907..97f8c1af7d44 100644 --- a/Content.Shared/Tag/TagPrototype.cs +++ b/Content.Shared/Tag/TagPrototype.cs @@ -1,17 +1,15 @@ using Robust.Shared.Prototypes; -namespace Content.Shared.Tag +namespace Content.Shared.Tag; + +/// +/// Prototype representing a tag in YAML. +/// Meant to only have an ID property, as that is the only thing that +/// gets saved in TagComponent. +/// +[Prototype("Tag")] +public sealed partial class TagPrototype : IPrototype { - /// - /// Prototype representing a tag in YAML. - /// Meant to only have an ID property, as that is the only thing that - /// gets saved in TagComponent. - /// - [Prototype("Tag")] - public sealed partial class TagPrototype : IPrototype - { - [ViewVariables] - [IdDataField] - public string ID { get; private set; } = default!; - } + [IdDataField, ViewVariables] + public string ID { get; } = string.Empty; } diff --git a/Content.Shared/Tag/TagSystem.cs b/Content.Shared/Tag/TagSystem.cs index 7bcb887a410a..f1f620a69494 100644 --- a/Content.Shared/Tag/TagSystem.cs +++ b/Content.Shared/Tag/TagSystem.cs @@ -1,11 +1,19 @@ -using System.Diagnostics; -using System.Linq; -using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Shared.Tag; +/// +/// The system that is responsible for working with tags. +/// Checking the existence of the only happens in DEBUG builds, +/// to improve performance, so don't forget to check it. +/// +/// +/// The methods to add or remove a list of tags have only an implementation with the type, +/// it's not much, but it takes away performance, +/// if you need to use them often, it's better to make a proper implementation, +/// you can read more HERE. +/// public sealed class TagSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _proto = default!; @@ -15,541 +23,514 @@ public sealed class TagSystem : EntitySystem public override void Initialize() { base.Initialize(); + _tagQuery = GetEntityQuery(); - SubscribeLocalEvent(OnTagGetState); - SubscribeLocalEvent(OnTagHandleState); #if DEBUG SubscribeLocalEvent(OnTagInit); +#endif } +#if DEBUG private void OnTagInit(EntityUid uid, TagComponent component, ComponentInit args) { foreach (var tag in component.Tags) { AssertValidTag(tag); } -#endif - } - - - private void OnTagHandleState(EntityUid uid, TagComponent component, ref ComponentHandleState args) - { - if (args.Current is not TagComponentState state) - return; - - component.Tags.Clear(); - - foreach (var tag in state.Tags) - { - AssertValidTag(tag); - component.Tags.Add(tag); - } - } - - private static void OnTagGetState(EntityUid uid, TagComponent component, ref ComponentGetState args) - { - var tags = new string[component.Tags.Count]; - var i = 0; - - foreach (var tag in component.Tags) - { - tags[i] = tag; - i++; - } - - args.State = new TagComponentState(tags); - } - - private void AssertValidTag(string id) - { - DebugTools.Assert(_proto.HasIndex(id), $"Unknown tag: {id}"); } +#endif /// - /// Tries to add a tag to an entity if the tag doesn't already exist. + /// Tries to add a tag to an entity if the tag doesn't already exist. /// - /// The entity to add the tag to. - /// The tag to add. /// - /// true if it was added, false otherwise even if it already existed. + /// true if it was added, false otherwise even if it already existed. /// /// - /// Thrown if no exists with the given id. + /// Thrown if no exists with the given id. /// - public bool AddTag(EntityUid entity, string id) + public bool AddTag(EntityUid entityUid, ProtoId tag) { - return AddTag(entity, EnsureComp(entity), id); + return AddTag((entityUid, EnsureComp(entityUid)), tag); } /// - /// Tries to add the given tags to an entity if the tags don't already exist. + /// Tries to add the given tags to an entity if the tags don't already exist. /// - /// The entity to add the tag to. - /// The tags to add. /// - /// true if any tags were added, false otherwise even if they all already existed. + /// true if any tags were added, false otherwise even if they all already existed. /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool AddTags(EntityUid entity, params string[] ids) + public bool AddTags(EntityUid entityUid, params ProtoId[] tags) { - return AddTags(entity, EnsureComp(entity), ids); + return AddTags(entityUid, (IEnumerable>)tags); } /// - /// Tries to add the given tags to an entity if the tags don't already exist. + /// Tries to add the given tags to an entity if the tags don't already exist. /// - /// The entity to add the tag to. - /// The tags to add. /// - /// true if any tags were added, false otherwise even if they all already existed. + /// true if any tags were added, false otherwise even if they all already existed. /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool AddTags(EntityUid entity, IEnumerable ids) + public bool AddTags(EntityUid entityUid, IEnumerable> tags) { - return AddTags(entity, EnsureComp(entity), ids); + return AddTags((entityUid, EnsureComp(entityUid)), tags); } /// - /// Tries to add a tag to an entity if it has a - /// and the tag doesn't already exist. + /// Tries to add a tag to an entity if it has a + /// and the tag doesn't already exist. /// - /// The entity to add the tag to. - /// The tag to add. /// - /// true if it was added, false otherwise even if it already existed. + /// true if it was added, false otherwise even if it already existed. /// /// - /// Thrown if no exists with the given id. + /// Thrown if no exists with the given id. /// - public bool TryAddTag(EntityUid entity, string id) + public bool TryAddTag(EntityUid entityUid, ProtoId tag) { - return _tagQuery.TryComp(entity, out var component) && - AddTag(entity, component, id); + return _tagQuery.TryComp(entityUid, out var component) && + AddTag((entityUid, component), tag); } /// - /// Tries to add the given tags to an entity if it has a - /// and the tags don't already exist. + /// Tries to add the given tags to an entity if it has a + /// and the tags don't already exist. /// - /// The entity to add the tag to. - /// The tags to add. /// - /// true if any tags were added, false otherwise even if they all already existed. + /// true if any tags were added, false otherwise even if they all already existed. /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool TryAddTags(EntityUid entity, params string[] ids) + public bool TryAddTags(EntityUid entityUid, params ProtoId[] tags) { - return _tagQuery.TryComp(entity, out var component) && - AddTags(entity, component, ids); + return TryAddTags(entityUid, (IEnumerable>)tags); } /// - /// Tries to add the given tags to an entity if it has a - /// and the tags don't already exist. + /// Tries to add the given tags to an entity if it has a + /// and the tags don't already exist. /// - /// The entity to add the tag to. - /// The tags to add. /// - /// true if any tags were added, false otherwise even if they all already existed. + /// true if any tags were added, false otherwise even if they all already existed. /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool TryAddTags(EntityUid entity, IEnumerable ids) + public bool TryAddTags(EntityUid entityUid, IEnumerable> tags) { - return _tagQuery.TryComp(entity, out var component) && - AddTags(entity, component, ids); + return _tagQuery.TryComp(entityUid, out var component) && + AddTags((entityUid, component), tags); } /// - /// Checks if a tag has been added to an entity. + /// Checks if a tag has been added to an entity. /// - /// The entity to check. - /// The tag to check for. - /// true if it exists, false otherwise. + /// + /// true if it exists, false otherwise. + /// /// - /// Thrown if no exists with the given id. + /// Thrown if no exists with the given id. /// - public bool HasTag(EntityUid entity, string id) - { - return _tagQuery.TryComp(entity, out var component) && - HasTag(component, id); - } - - /// - /// Checks if a tag has been added to an entity. - /// - [Obsolete] - public bool HasTag(EntityUid entity, string id, EntityQuery tagQuery) + public bool HasTag(EntityUid entityUid, ProtoId tag) { - return tagQuery.TryGetComponent(entity, out var component) && - HasTag(component, id); + return _tagQuery.TryComp(entityUid, out var component) && + HasTag(component, tag); } /// - /// Checks if all of the given tags have been added to an entity. + /// Checks if a tag has been added to an entity. /// - /// The entity to check. - /// The tags to check for. - /// true if they all exist, false otherwise. + /// + /// true if it exists, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if no exists with the given id. /// - public bool HasAllTags(EntityUid entity, string id) => HasTag(entity, id); + public bool HasAllTags(EntityUid entityUid, ProtoId tag) => + HasTag(entityUid, tag); /// - /// Checks if all of the given tags have been added to an entity. + /// Checks if all of the given tags have been added to an entity. /// - /// The entity to check. - /// The tags to check for. - /// true if they all exist, false otherwise. + /// + /// true if they all exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAllTags(EntityUid entity, List ids) + public bool HasAllTags(EntityUid entityUid, params ProtoId[] tags) { - return _tagQuery.TryComp(entity, out var component) && - HasAllTags(component, ids); + return _tagQuery.TryComp(entityUid, out var component) && + HasAllTags(component, tags); } /// - /// Checks if all of the given tags have been added to an entity. + /// Checks if all of the given tags have been added to an entity. /// - /// The entity to check. - /// The tags to check for. - /// true if they all exist, false otherwise. + /// + /// true if they all exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAllTags(EntityUid entity, IEnumerable ids) + public bool HasAllTags(EntityUid entityUid, HashSet> tags) { - return _tagQuery.TryComp(entity, out var component) && - HasAllTags(component, ids); + return _tagQuery.TryComp(entityUid, out var component) && + HasAllTags(component, tags); } /// - /// Checks if all of the given tags have been added to an entity. + /// Checks if all of the given tags have been added to an entity. /// - /// The entity to check. - /// The tags to check for. - /// true if they all exist, false otherwise. + /// + /// true if they all exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAllTags(EntityUid entity, List> ids) + public bool HasAllTags(EntityUid entityUid, List> tags) { - return _tagQuery.TryComp(entity, out var component) && - HasAllTags(component, ids); + return _tagQuery.TryComp(entityUid, out var component) && + HasAllTags(component, tags); } /// - /// Checks if any of the given tags have been added to an entity. + /// Checks if all of the given tags have been added to an entity. /// - /// The entity to check. - /// The tags to check for. - /// true if any of them exist, false otherwise. + /// + /// true if they all exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAnyTag(EntityUid entity, params string[] ids) + public bool HasAllTags(EntityUid entityUid, IEnumerable> tags) { - return _tagQuery.TryComp(entity, out var component) && - HasAnyTag(component, ids); + return _tagQuery.TryComp(entityUid, out var component) && + HasAllTags(component, tags); } /// - /// Checks if any of the given tags have been added to an entity. + /// Checks if a tag has been added to an entity. /// - /// The entity to check. - /// The tag to check for. - /// true if any of them exist, false otherwise. + /// + /// true if it exists, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if no exists with the given id. /// - public bool HasAnyTag(EntityUid entity, string id) => HasTag(entity, id); + public bool HasAnyTag(EntityUid entityUid, ProtoId tag) => + HasTag(entityUid, tag); /// - /// Checks if any of the given tags have been added to an entity. + /// Checks if any of the given tags have been added to an entity. /// - /// The entity to check. - /// The tags to check for. - /// true if any of them exist, false otherwise. + /// + /// true if any of them exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAnyTag(EntityUid entity, List ids) + public bool HasAnyTag(EntityUid entityUid, params ProtoId[] tags) { - return _tagQuery.TryComp(entity, out var component) && - HasAnyTag(component, ids); + return _tagQuery.TryComp(entityUid, out var component) && + HasAnyTag(component, tags); } /// - /// Checks if any of the given tags have been added to an entity. + /// Checks if any of the given tags have been added to an entity. /// - /// The entity to check. - /// The tags to check for. - /// true if any of them exist, false otherwise. + /// + /// true if any of them exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAnyTag(EntityUid entity, List> ids) + public bool HasAnyTag(EntityUid entityUid, HashSet> tags) { - return TryComp(entity, out var component) && - HasAnyTag(component, ids); + return _tagQuery.TryComp(entityUid, out var component) && + HasAnyTag(component, tags); } /// - /// Checks if any of the given tags have been added to an entity. + /// Checks if any of the given tags have been added to an entity. /// - /// The entity to check. - /// The tags to check for. - /// true if any of them exist, false otherwise. + /// + /// true if any of them exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAnyTag(EntityUid entity, IEnumerable ids) + public bool HasAnyTag(EntityUid entityUid, List> tags) { - return _tagQuery.TryComp(entity, out var component) && - HasAnyTag(component, ids); + return _tagQuery.TryComp(entityUid, out var component) && + HasAnyTag(component, tags); } /// - /// Tries to remove a tag from an entity if it exists. + /// Checks if any of the given tags have been added to an entity. /// - /// The entity to remove the tag from. - /// The tag to remove. /// - /// true if it was removed, false otherwise even if it didn't exist. + /// true if any of them exist, false otherwise. /// /// - /// Thrown if no exists with the given id. + /// Thrown if one of the ids represents an unregistered . /// - public bool RemoveTag(EntityUid entity, string id) + public bool HasAnyTag(EntityUid entityUid, IEnumerable> tags) { - return _tagQuery.TryComp(entity, out var component) && - RemoveTag(entity, component, id); + return _tagQuery.TryComp(entityUid, out var component) && + HasAnyTag(component, tags); } /// - /// Tries to remove a tag from an entity if it exists. + /// Checks if a tag has been added to an component. /// - /// The entity to remove the tag from. - /// The tag to remove. /// - /// true if it was removed, false otherwise even if it didn't exist. + /// true if it exists, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if no exists with the given id. /// - /// - public bool RemoveTags(EntityUid entity, params string[] ids) + public bool HasTag(TagComponent component, ProtoId tag) { - return _tagQuery.TryComp(entity, out var component) && - RemoveTags(entity, component, ids); +#if DEBUG + AssertValidTag(tag); +#endif + return component.Tags.Contains(tag); } /// - /// Tries to remove a tag from an entity if it exists. + /// Checks if a tag has been added to an component. /// - /// The entity to remove the tag from. - /// The tag to remove. /// - /// true if it was removed, false otherwise even if it didn't exist. + /// true if it exists, false otherwise. /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if no exists with the given id. /// - public bool RemoveTags(EntityUid entity, IEnumerable ids) - { - return _tagQuery.TryComp(entity, out var component) && - RemoveTags(entity, component, ids); - } + public bool HasAllTags(TagComponent component, ProtoId tag) => + HasTag(component, tag); /// - /// Tries to add a tag if it doesn't already exist. + /// Checks if all of the given tags have been added to an component. /// - /// The tag to add. - /// true if it was added, false if it already existed. + /// + /// true if they all exist, false otherwise. + /// /// - /// Thrown if no exists with the given id. + /// Thrown if one of the ids represents an unregistered . /// - public bool AddTag(EntityUid uid, TagComponent component, string id) + public bool HasAllTags(TagComponent component, params ProtoId[] tags) { - AssertValidTag(id); - var added = component.Tags.Add(id); - - if (added) + foreach (var tag in tags) { - Dirty(uid, component); - return true; +#if DEBUG + AssertValidTag(tag); +#endif + if (!component.Tags.Contains(tag)) + return false; } - return false; + return true; } /// - /// Tries to add the given tags if they don't already exist. + /// Checks if all of the given tags have been added to an component. /// - /// The tags to add. - /// true if any tags were added, false if they all already existed. + /// + /// true if they all exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool AddTags(EntityUid uid, TagComponent component, params string[] ids) + public bool HasAllTagsArray(TagComponent component, ProtoId[] tags) { - return AddTags(uid, component, ids.AsEnumerable()); + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (!component.Tags.Contains(tag)) + return false; + } + + return true; } /// - /// Tries to add the given tags if they don't already exist. + /// Checks if all of the given tags have been added to an component. /// - /// The tags to add. - /// true if any tags were added, false if they all already existed. + /// + /// true if they all exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool AddTags(EntityUid uid, TagComponent component, IEnumerable ids) + public bool HasAllTags(TagComponent component, List> tags) { - var count = component.Tags.Count; - - foreach (var id in ids) - { - AssertValidTag(id); - component.Tags.Add(id); - } - - if (component.Tags.Count > count) + foreach (var tag in tags) { - Dirty(uid, component); - return true; +#if DEBUG + AssertValidTag(tag); +#endif + if (!component.Tags.Contains(tag)) + return false; } - return false; + return true; } /// - /// Checks if a tag has been added. + /// Checks if all of the given tags have been added to an component. /// - /// The tag to check for. - /// true if it exists, false otherwise. + /// + /// true if they all exist, false otherwise. + /// /// - /// Thrown if no exists with the given id. + /// Thrown if one of the ids represents an unregistered . /// - public bool HasTag(TagComponent component, string id) + public bool HasAllTags(TagComponent component, HashSet> tags) { - AssertValidTag(id); - return component.Tags.Contains(id); + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (!component.Tags.Contains(tag)) + return false; + } + + return true; } /// - /// Checks if all of the given tags have been added. + /// Checks if all of the given tags have been added to an component. /// - /// The tags to check for. - /// true if they all exist, false otherwise. + /// + /// true if they all exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAllTags(TagComponent component, params string[] ids) + public bool HasAllTags(TagComponent component, IEnumerable> tags) { - return HasAllTags(component, ids.AsEnumerable()); + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (!component.Tags.Contains(tag)) + return false; + } + + return true; } /// - /// Checks if all of the given tags have been added. + /// Checks if a tag has been added to an component. /// - /// The tag to check for. - /// true if they all exist, false otherwise. + /// + /// true if it exists, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if no exists with the given id. /// - public bool HasAllTags(TagComponent component, string id) => HasTag(component, id); + public bool HasAnyTag(TagComponent component, ProtoId tag) => + HasTag(component, tag); /// - /// Checks if all of the given tags have been added. + /// Checks if any of the given tags have been added to an component. /// - /// The tags to check for. - /// true if they all exist, false otherwise. + /// + /// true if any of them exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAllTags(TagComponent component, List ids) + public bool HasAnyTag(TagComponent component, params ProtoId[] tags) { - foreach (var id in ids) + foreach (var tag in tags) { - AssertValidTag(id); - - if (!component.Tags.Contains(id)) - return false; +#if DEBUG + AssertValidTag(tag); +#endif + if (component.Tags.Contains(tag)) + return true; } - return true; + return false; } /// - /// Checks if all of the given tags have been added. + /// Checks if any of the given tags have been added to an component. /// - /// The tags to check for. - /// true if they all exist, false otherwise. + /// + /// true if any of them exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAllTags(TagComponent component, IEnumerable ids) + public bool HasAnyTag(TagComponent component, HashSet> tags) { - foreach (var id in ids) + foreach (var tag in tags) { - AssertValidTag(id); - - if (!component.Tags.Contains(id)) - return false; - +#if DEBUG + AssertValidTag(tag); +#endif + if (component.Tags.Contains(tag)) + return true; } - return true; + return false; } /// - /// Checks if all of the given tags have been added. + /// Checks if any of the given tags have been added to an component. /// - /// The tags to check for. - /// true if they all exist, false otherwise. + /// + /// true if any of them exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAllTags(TagComponent component, List> ids) + public bool HasAnyTag(TagComponent component, List> tags) { - foreach (var id in ids) + foreach (var tag in tags) { - AssertValidTag(id); - - if (!component.Tags.Contains(id)) - return false; - +#if DEBUG + AssertValidTag(tag); +#endif + if (component.Tags.Contains(tag)) + return true; } - return true; + return false; } /// - /// Checks if any of the given tags have been added. + /// Checks if any of the given tags have been added to an component. /// - /// The tags to check for. - /// true if any of them exist, false otherwise. + /// + /// true if any of them exist, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAnyTag(TagComponent component, params string[] ids) + public bool HasAnyTag(TagComponent component, IEnumerable> tags) { - foreach (var id in ids) + foreach (var tag in tags) { - AssertValidTag(id); - - if (component.Tags.Contains(id)) +#if DEBUG + AssertValidTag(tag); +#endif + if (component.Tags.Contains(tag)) return true; } @@ -557,143 +538,178 @@ public bool HasAnyTag(TagComponent component, params string[] ids) } /// - /// Checks if any of the given tags have been added. + /// Tries to remove a tag from an entity if it exists. /// - /// The tag to check for. - /// true if any of them exist, false otherwise. + /// + /// true if it was removed, false otherwise even if it didn't exist. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if no exists with the given id. /// - public bool HasAnyTag(TagComponent component, string id) => HasTag(component, id); + public bool RemoveTag(EntityUid entityUid, ProtoId tag) + { + return _tagQuery.TryComp(entityUid, out var component) && + RemoveTag((entityUid, component), tag); + } /// - /// Checks if any of the given tags have been added. + /// Tries to remove a tag from an entity if it exists. /// - /// The tags to check for. - /// true if any of them exist, false otherwise. + /// + /// true if it was removed, false otherwise even if it didn't exist. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAnyTag(TagComponent component, List ids) + public bool RemoveTags(EntityUid entityUid, params ProtoId[] tags) { - foreach (var id in ids) - { - AssertValidTag(id); - - if (component.Tags.Contains(id)) - { - return true; - } - } + return RemoveTags(entityUid, (IEnumerable>)tags); + } - return false; + /// + /// Tries to remove a tag from an entity if it exists. + /// + /// + /// true if it was removed, false otherwise even if it didn't exist. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool RemoveTags(EntityUid entityUid, IEnumerable> tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + RemoveTags((entityUid, component), tags); } /// - /// Checks if any of the given tags have been added. + /// Tries to add a tag if it doesn't already exist. /// - /// The tags to check for. - /// true if any of them exist, false otherwise. + /// + /// true if it was added, false if it already existed. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if no exists with the given id. /// - public bool HasAnyTag(TagComponent component, IEnumerable ids) + public bool AddTag(Entity entity, ProtoId tag) { - foreach (var id in ids) - { - AssertValidTag(id); +#if DEBUG + AssertValidTag(tag); +#endif + if (!entity.Comp.Tags.Add(tag)) + return false; - if (component.Tags.Contains(id)) - { - return true; - } - } + Dirty(entity); + return true; + } - return false; + /// + /// Tries to add the given tags if they don't already exist. + /// + /// + /// true if any tags were added, false if they all already existed. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool AddTags(Entity entity, params ProtoId[] tags) + { + return AddTags(entity, (IEnumerable>)tags); } /// - /// Checks if any of the given tags have been added. + /// Tries to add the given tags if they don't already exist. /// - /// The tags to check for. - /// true if any of them exist, false otherwise. + /// + /// true if any tags were added, false if they all already existed. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool HasAnyTag(TagComponent comp, List> ids) + public bool AddTags(Entity entity, IEnumerable> tags) { - foreach (var id in ids) + var update = false; + foreach (var tag in tags) { - AssertValidTag(id); - - if (comp.Tags.Contains(id)) - return true; +#if DEBUG + AssertValidTag(tag); +#endif + if (entity.Comp.Tags.Add(tag) && !update) + update = true; } - return false; + if (!update) + return false; + + Dirty(entity); + return true; } /// - /// Tries to remove a tag if it exists. + /// Tries to remove a tag if it exists. /// /// - /// true if it was removed, false otherwise even if it didn't exist. + /// true if it was removed, false otherwise even if it didn't exist. /// /// - /// Thrown if no exists with the given id. + /// Thrown if no exists with the given id. /// - public bool RemoveTag(EntityUid uid, TagComponent component, string id) + public bool RemoveTag(Entity entity, ProtoId tag) { - AssertValidTag(id); +#if DEBUG + AssertValidTag(tag); +#endif - if (component.Tags.Remove(id)) - { - Dirty(uid, component); - return true; - } + if (!entity.Comp.Tags.Remove(tag)) + return false; - return false; + Dirty(entity); + return true; } /// - /// Tries to remove all of the given tags if they exist. + /// Tries to remove all of the given tags if they exist. /// - /// The tags to remove. /// - /// true if it was removed, false otherwise even if they didn't exist. + /// true if any tag was removed, false otherwise. /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool RemoveTags(EntityUid uid, TagComponent component, params string[] ids) + public bool RemoveTags(Entity entity, params ProtoId[] tags) { - return RemoveTags(uid, component, ids.AsEnumerable()); + return RemoveTags(entity, (IEnumerable>)tags); } /// - /// Tries to remove all of the given tags if they exist. + /// Tries to remove all of the given tags if they exist. /// - /// The tags to remove. - /// true if any tag was removed, false otherwise. + /// + /// true if any tag was removed, false otherwise. + /// /// - /// Thrown if one of the ids represents an unregistered . + /// Thrown if one of the ids represents an unregistered . /// - public bool RemoveTags(EntityUid uid, TagComponent component, IEnumerable ids) + public bool RemoveTags(Entity entity, IEnumerable> tags) { - var count = component.Tags.Count; - - foreach (var id in ids) + var update = false; + foreach (var tag in tags) { - AssertValidTag(id); - component.Tags.Remove(id); +#if DEBUG + AssertValidTag(tag); +#endif + if (entity.Comp.Tags.Remove(tag) && !update) + update = true; } - if (component.Tags.Count < count) - { - Dirty(uid, component); - return true; - } + if (!update) + return false; - return false; + Dirty(entity); + return true; + } + + private void AssertValidTag(string id) + { + DebugTools.Assert(_proto.HasIndex(id), $"Unknown tag: {id}"); } } From 29e34cae350acde79412f3781c9fcfddbda8b949 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:41:06 -0700 Subject: [PATCH 210/568] Disable rainbow overlay when reduced motion is enabled (#28496) Disable rainbow ovelray when reduced motion is enabled --- Content.Client/Drugs/RainbowOverlay.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Content.Client/Drugs/RainbowOverlay.cs b/Content.Client/Drugs/RainbowOverlay.cs index e62b0dfa66cd..fb48c9101096 100644 --- a/Content.Client/Drugs/RainbowOverlay.cs +++ b/Content.Client/Drugs/RainbowOverlay.cs @@ -1,7 +1,9 @@ +using Content.Shared.CCVar; using Content.Shared.Drugs; using Content.Shared.StatusEffect; using Robust.Client.Graphics; using Robust.Client.Player; +using Robust.Shared.Configuration; using Robust.Shared.Enums; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -10,6 +12,7 @@ namespace Content.Client.Drugs; public sealed class RainbowOverlay : Overlay { + [Dependency] private readonly IConfigurationManager _config = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; @@ -75,6 +78,10 @@ protected override bool BeforeDraw(in OverlayDrawArgs args) protected override void Draw(in OverlayDrawArgs args) { + // TODO disable only the motion part or ike's idea (single static frame of the overlay) + if (_config.GetCVar(CCVars.ReducedMotion)) + return; + if (ScreenTexture == null) return; From dce68e48e83d22d76c08bd33c27cd4e8597fa43d Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 2 Jun 2024 01:42:12 +0000 Subject: [PATCH 211/568] 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 535a93e87e31..0c671f49eec1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: FairlySadPanda - changes: - - message: Food and drink stocks in vending machines has been reduced to encourage - people to use the kitchen and bar. - type: Tweak - id: 6162 - time: '2024-03-15T23:28:01.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25999 - author: Plykiya changes: - message: Ion storms now have a chance of happening from the start of shift. @@ -3853,3 +3845,11 @@ id: 6661 time: '2024-06-01T17:49:28.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28434 +- author: DrSmugleaf + changes: + - message: Disabled the seeing rainbows screen effect when reduced motion is enabled + in the options menu. + type: Tweak + id: 6662 + time: '2024-06-02T01:41:06.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28496 From d6ba166d3bb57f6f6438adee89ffdd7b20fb9635 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Sat, 1 Jun 2024 20:10:24 -0700 Subject: [PATCH 212/568] Replace obsolete EntityWhitelist IsValid usages (#28465) * Replace obsolete whitelist is valid with whitelist system * Consistency * Fix logic * Bork * I figured out how to get whitelists on the client lol * test fail * woops * HELP ME FUNCTIONS * Fix errors * simplify --------- Co-authored-by: plykiya --- Content.Client/Chat/UI/EmotesMenu.xaml.cs | 9 +- .../UI/ConstructionMenuPresenter.cs | 6 +- .../Chat/Systems/ChatSystem.Emote.cs | 4 +- Content.Server/Chat/Systems/ChatSystem.cs | 2 + Content.Server/Gatherable/GatherableSystem.cs | 6 +- .../NPCImprintingOnSpawnBehaviourSystem.cs | 4 +- .../NPC/Systems/NPCUtilitySystem.cs | 4 +- .../Power/EntitySystems/ChargerSystem.cs | 4 +- .../EntitySystems/RevenantSystem.Abilities.cs | 8 +- .../Silicons/Borgs/BorgSystem.Modules.cs | 2 +- Content.Server/Silicons/Borgs/BorgSystem.cs | 8 +- .../Storage/EntitySystems/PickRandomSystem.cs | 6 +- .../XenoArtifacts/ArtifactSystem.Nodes.cs | 11 ++- .../Containers/ItemSlot/ItemSlotsSystem.cs | 5 +- .../Damage/Systems/DamageContactsSystem.cs | 4 +- Content.Shared/Devour/SharedDevourSystem.cs | 4 +- .../Disposal/SharedDisposalUnitSystem.cs | 8 +- .../Implants/SharedImplanterSystem.cs | 5 +- .../Interaction/SmartEquipSystem.cs | 6 +- .../Materials/SharedMaterialStorageSystem.cs | 6 +- .../Systems/SharedChameleonProjectorSystem.cs | 6 +- .../Salvage/Fulton/SharedFultonSystem.cs | 4 +- .../Shuttles/Systems/SharedShuttleSystem.cs | 4 +- .../EntitySystems/MagnetPickupSystem.cs | 5 +- .../SharedEntityStorageSystem.cs | 4 +- .../EntitySystems/SharedStorageSystem.cs | 10 +-- .../Marker/SharedDamageMarkerSystem.cs | 4 +- .../Systems/SharedGunSystem.Ballistic.cs | 5 +- .../Systems/SharedGunSystem.Revolver.cs | 2 +- .../Weapons/Ranged/Systems/SharedGunSystem.cs | 2 + .../Whitelist/EntityWhitelistSystem.cs | 84 +++++++++++++++++++ 31 files changed, 186 insertions(+), 56 deletions(-) diff --git a/Content.Client/Chat/UI/EmotesMenu.xaml.cs b/Content.Client/Chat/UI/EmotesMenu.xaml.cs index a26d319920bf..334075534389 100644 --- a/Content.Client/Chat/UI/EmotesMenu.xaml.cs +++ b/Content.Client/Chat/UI/EmotesMenu.xaml.cs @@ -1,7 +1,8 @@ -using System.Numerics; +using System.Numerics; using Content.Client.UserInterface.Controls; using Content.Shared.Chat.Prototypes; using Content.Shared.Speech; +using Content.Shared.Whitelist; using Robust.Client.AutoGenerated; using Robust.Client.GameObjects; using Robust.Client.UserInterface.Controls; @@ -19,6 +20,7 @@ public sealed partial class EmotesMenu : RadialMenu [Dependency] private readonly ISharedPlayerManager _playerManager = default!; private readonly SpriteSystem _spriteSystem; + private readonly EntityWhitelistSystem _whitelistSystem; public event Action>? OnPlayEmote; @@ -28,6 +30,7 @@ public EmotesMenu() RobustXamlLoader.Load(this); _spriteSystem = _entManager.System(); + _whitelistSystem = _entManager.System(); var main = FindControl("Main"); @@ -37,8 +40,8 @@ public EmotesMenu() var player = _playerManager.LocalSession?.AttachedEntity; if (emote.Category == EmoteCategory.Invalid || emote.ChatTriggers.Count == 0 || - !(player.HasValue && (emote.Whitelist?.IsValid(player.Value, _entManager) ?? true)) || - (emote.Blacklist?.IsValid(player.Value, _entManager) ?? false)) + !(player.HasValue && _whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) || + _whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value)) continue; if (!emote.Available && diff --git a/Content.Client/Construction/UI/ConstructionMenuPresenter.cs b/Content.Client/Construction/UI/ConstructionMenuPresenter.cs index 9a0943617665..0c7912e0bcd5 100644 --- a/Content.Client/Construction/UI/ConstructionMenuPresenter.cs +++ b/Content.Client/Construction/UI/ConstructionMenuPresenter.cs @@ -2,6 +2,7 @@ using Content.Client.UserInterface.Systems.MenuBar.Widgets; using Content.Shared.Construction.Prototypes; using Content.Shared.Tag; +using Content.Shared.Whitelist; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Placement; @@ -23,6 +24,7 @@ namespace Content.Client.Construction.UI /// internal sealed class ConstructionMenuPresenter : IDisposable { + [Dependency] private readonly EntityManager _entManager = default!; [Dependency] private readonly IEntitySystemManager _systemManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPlacementManager _placementManager = default!; @@ -30,6 +32,7 @@ internal sealed class ConstructionMenuPresenter : IDisposable [Dependency] private readonly IPlayerManager _playerManager = default!; private readonly IConstructionMenuView _constructionView; + private readonly EntityWhitelistSystem _whitelistSystem; private ConstructionSystem? _constructionSystem; private ConstructionPrototype? _selected; @@ -78,6 +81,7 @@ public ConstructionMenuPresenter() // This is a lot easier than a factory IoCManager.InjectDependencies(this); _constructionView = new ConstructionMenu(); + _whitelistSystem = _entManager.System(); // This is required so that if we load after the system is initialized, we can bind to it immediately if (_systemManager.TryGetEntitySystem(out var constructionSystem)) @@ -157,7 +161,7 @@ private void OnViewPopulateRecipes(object? sender, (string search, string catago if (_playerManager.LocalSession == null || _playerManager.LocalEntity == null - || (recipe.EntityWhitelist != null && !recipe.EntityWhitelist.IsValid(_playerManager.LocalEntity.Value))) + || _whitelistSystem.IsWhitelistFail(recipe.EntityWhitelist, _playerManager.LocalEntity.Value)) continue; if (!string.IsNullOrEmpty(search)) diff --git a/Content.Server/Chat/Systems/ChatSystem.Emote.cs b/Content.Server/Chat/Systems/ChatSystem.Emote.cs index d120812b8801..23e1517d7548 100644 --- a/Content.Server/Chat/Systems/ChatSystem.Emote.cs +++ b/Content.Server/Chat/Systems/ChatSystem.Emote.cs @@ -81,9 +81,7 @@ public void TryEmoteWithChat( bool ignoreActionBlocker = false ) { - if (!(emote.Whitelist?.IsValid(source, EntityManager) ?? true)) - return; - if (emote.Blacklist?.IsValid(source, EntityManager) ?? false) + if (_whitelistSystem.IsWhitelistFailOrNull(emote.Whitelist, source) || _whitelistSystem.IsBlacklistPass(emote.Blacklist, source)) return; if (!emote.Available && diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index b79e16a8df97..0fe9dcbc4d85 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -22,6 +22,7 @@ using Content.Shared.Players; using Content.Shared.Radio; using Content.Shared.Speech; +using Content.Shared.Whitelist; using Robust.Server.Player; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -58,6 +59,7 @@ public sealed partial class ChatSystem : SharedChatSystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly ReplacementAccentSystem _wordreplacement = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public const int VoiceRange = 10; // how far voice goes in world units public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units diff --git a/Content.Server/Gatherable/GatherableSystem.cs b/Content.Server/Gatherable/GatherableSystem.cs index e24b0da59394..d6a3be451b6a 100644 --- a/Content.Server/Gatherable/GatherableSystem.cs +++ b/Content.Server/Gatherable/GatherableSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Interaction; using Content.Shared.Tag; using Content.Shared.Weapons.Melee.Events; +using Content.Shared.Whitelist; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; @@ -18,6 +19,7 @@ public sealed partial class GatherableSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -30,7 +32,7 @@ public override void Initialize() private void OnAttacked(Entity gatherable, ref AttackedEvent args) { - if (gatherable.Comp.ToolWhitelist?.IsValid(args.Used, EntityManager) != true) + if (_whitelistSystem.IsWhitelistFailOrNull(gatherable.Comp.ToolWhitelist, args.Used)) return; Gather(gatherable, args.User); @@ -41,7 +43,7 @@ private void OnActivate(Entity gatherable, ref ActivateInWo if (args.Handled || !args.Complex) return; - if (gatherable.Comp.ToolWhitelist?.IsValid(args.User, EntityManager) != true) + if (_whitelistSystem.IsWhitelistFailOrNull(gatherable.Comp.ToolWhitelist, args.User)) return; Gather(gatherable, args.User); diff --git a/Content.Server/NPC/Systems/NPCImprintingOnSpawnBehaviourSystem.cs b/Content.Server/NPC/Systems/NPCImprintingOnSpawnBehaviourSystem.cs index cfd3b08c61a6..c3e0328037d3 100644 --- a/Content.Server/NPC/Systems/NPCImprintingOnSpawnBehaviourSystem.cs +++ b/Content.Server/NPC/Systems/NPCImprintingOnSpawnBehaviourSystem.cs @@ -1,6 +1,7 @@ using System.Numerics; using Content.Shared.NPC.Components; using Content.Shared.NPC.Systems; +using Content.Shared.Whitelist; using Robust.Shared.Collections; using Robust.Shared.Map; using Robust.Shared.Random; @@ -13,6 +14,7 @@ public sealed partial class NPCImprintingOnSpawnBehaviourSystem : SharedNPCImpri [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly NPCSystem _npc = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -27,7 +29,7 @@ private void OnMapInit(Entity imprinting foreach (var friend in friends) { - if (imprinting.Comp.Whitelist?.IsValid(friend) != false) + if (_whitelistSystem.IsWhitelistPassOrNull(imprinting.Comp.Whitelist, friend)) { AddImprintingTarget(imprinting, friend, imprinting.Comp); } diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 2e8c628b5030..ca74d7133574 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -19,6 +19,7 @@ using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.Whitelist; using Microsoft.Extensions.ObjectPool; using Robust.Server.Containers; using Robust.Shared.Prototypes; @@ -46,6 +47,7 @@ public sealed class NPCUtilitySystem : EntitySystem [Dependency] private readonly SolutionContainerSystem _solutions = default!; [Dependency] private readonly WeldableSystem _weldable = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; private EntityQuery _puddleQuery; private EntityQuery _xformQuery; @@ -249,7 +251,7 @@ private float GetScore(NPCBlackboard blackboard, EntityUid targetUid, UtilityCon return 0f; } - if (heldGun.Whitelist?.IsValid(targetUid, EntityManager) != true) + if (_whitelistSystem.IsWhitelistFailOrNull(heldGun.Whitelist, targetUid)) { return 0f; } diff --git a/Content.Server/Power/EntitySystems/ChargerSystem.cs b/Content.Server/Power/EntitySystems/ChargerSystem.cs index 1ff13a24f2c3..038295eac115 100644 --- a/Content.Server/Power/EntitySystems/ChargerSystem.cs +++ b/Content.Server/Power/EntitySystems/ChargerSystem.cs @@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.Storage.Components; using Robust.Server.Containers; +using Content.Shared.Whitelist; namespace Content.Server.Power.EntitySystems; @@ -20,6 +21,7 @@ internal sealed class ChargerSystem : EntitySystem [Dependency] private readonly PowerCellSystem _powerCell = default!; [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -208,7 +210,7 @@ private void TransferPower(EntityUid uid, EntityUid targetEntity, ChargerCompone if (!receiverComponent.Powered) return; - if (component.Whitelist?.IsValid(targetEntity, EntityManager) == false) + if (_whitelistSystem.IsWhitelistFail(component.Whitelist, targetEntity)) return; if (!SearchForBattery(targetEntity, out var batteryUid, out var heldBattery)) diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index 670c64577a48..c889d59f1556 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -28,6 +28,7 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Utility; using Robust.Shared.Map.Components; +using Content.Shared.Whitelist; namespace Content.Server.Revenant.EntitySystems; @@ -40,6 +41,7 @@ public sealed partial class RevenantSystem [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!; [Dependency] private readonly GhostSystem _ghost = default!; [Dependency] private readonly TileSystem _tile = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; private void InitializeAbilities() { @@ -331,10 +333,8 @@ private void OnMalfunctionAction(EntityUid uid, RevenantComponent component, Rev foreach (var ent in _lookup.GetEntitiesInRange(uid, component.MalfunctionRadius)) { - if (component.MalfunctionWhitelist?.IsValid(ent, EntityManager) == false) - continue; - - if (component.MalfunctionBlacklist?.IsValid(ent, EntityManager) == true) + if (_whitelistSystem.IsWhitelistFail(component.MalfunctionWhitelist, ent) || + _whitelistSystem.IsBlacklistPass(component.MalfunctionBlacklist, ent)) continue; _emag.DoEmagEffect(uid, ent); //it does not emag itself. adorable. diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs index cc57c34c4754..5c600be3f640 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs @@ -267,7 +267,7 @@ public bool CanInsertModule(EntityUid uid, EntityUid module, BorgChassisComponen return false; } - if (component.ModuleWhitelist?.IsValid(module, EntityManager) == false) + if (_whitelistSystem.IsWhitelistFail(component.ModuleWhitelist, module)) { if (user != null) Popup.PopupEntity(Loc.GetString("borg-module-whitelist-deny"), uid, user.Value); diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index 082e38921a2c..1ab7f5387f31 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -22,6 +22,7 @@ using Content.Shared.Silicons.Borgs; using Content.Shared.Silicons.Borgs.Components; using Content.Shared.Throwing; +using Content.Shared.Whitelist; using Content.Shared.Wires; using Robust.Server.GameObjects; using Robust.Shared.Containers; @@ -53,6 +54,8 @@ public sealed partial class BorgSystem : SharedBorgSystem [Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [ValidatePrototypeId] public const string BorgJobId = "Borg"; @@ -104,9 +107,8 @@ private void OnChassisInteractUsing(EntityUid uid, BorgChassisComponent componen return; } - if (component.BrainEntity == null && - brain != null && - component.BrainWhitelist?.IsValid(used) != false) + if (component.BrainEntity == null && brain != null && + _whitelistSystem.IsWhitelistPassOrNull(component.BrainWhitelist, used)) { if (_mind.TryGetMind(used, out _, out var mind) && mind.Session != null) { diff --git a/Content.Server/Storage/EntitySystems/PickRandomSystem.cs b/Content.Server/Storage/EntitySystems/PickRandomSystem.cs index dbbe1dd7785f..f0e986e199bf 100644 --- a/Content.Server/Storage/EntitySystems/PickRandomSystem.cs +++ b/Content.Server/Storage/EntitySystems/PickRandomSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Storage; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.Random; @@ -15,6 +16,7 @@ public sealed class PickRandomSystem : EntitySystem [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -30,7 +32,7 @@ private void OnGetAlternativeVerbs(EntityUid uid, PickRandomComponent comp, GetV var user = args.User; - var enabled = storage.Container.ContainedEntities.Any(item => comp.Whitelist?.IsValid(item, EntityManager) ?? true); + var enabled = storage.Container.ContainedEntities.Any(item => _whitelistSystem.IsWhitelistPassOrNull(comp.Whitelist, item)); // alt-click / alt-z to pick an item args.Verbs.Add(new AlternativeVerb @@ -48,7 +50,7 @@ private void OnGetAlternativeVerbs(EntityUid uid, PickRandomComponent comp, GetV private void TryPick(EntityUid uid, PickRandomComponent comp, StorageComponent storage, EntityUid user) { - var entities = storage.Container.ContainedEntities.Where(item => comp.Whitelist?.IsValid(item, EntityManager) ?? true).ToArray(); + var entities = storage.Container.ContainedEntities.Where(item => _whitelistSystem.IsWhitelistPassOrNull(comp.Whitelist, item)).ToArray(); if (!entities.Any()) return; diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Nodes.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Nodes.cs index 895bb0217b3b..65aaabdf0e95 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Nodes.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Nodes.cs @@ -1,15 +1,16 @@ using System.Linq; using Content.Server.Xenoarchaeology.XenoArtifacts.Events; +using Content.Shared.Whitelist; using Content.Shared.Xenoarchaeology.XenoArtifacts; using JetBrains.Annotations; -using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Serialization.Manager; namespace Content.Server.Xenoarchaeology.XenoArtifacts; public sealed partial class ArtifactSystem { + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + private const int MaxEdgesPerNode = 4; private readonly HashSet _usedNodeIds = new(); @@ -81,7 +82,8 @@ private int GetValidNodeId() private string GetRandomTrigger(EntityUid artifact, ref ArtifactNode node) { var allTriggers = _prototype.EnumeratePrototypes() - .Where(x => (x.Whitelist?.IsValid(artifact, EntityManager) ?? true) && (!x.Blacklist?.IsValid(artifact, EntityManager) ?? true)).ToList(); + .Where(x => _whitelistSystem.IsWhitelistPassOrNull(x.Whitelist, artifact) && + _whitelistSystem.IsBlacklistFailOrNull(x.Blacklist, artifact)).ToList(); var validDepth = allTriggers.Select(x => x.TargetDepth).Distinct().ToList(); var weights = GetDepthWeights(validDepth, node.Depth); @@ -95,7 +97,8 @@ private string GetRandomTrigger(EntityUid artifact, ref ArtifactNode node) private string GetRandomEffect(EntityUid artifact, ref ArtifactNode node) { var allEffects = _prototype.EnumeratePrototypes() - .Where(x => (x.Whitelist?.IsValid(artifact, EntityManager) ?? true) && (!x.Blacklist?.IsValid(artifact, EntityManager) ?? true)).ToList(); + .Where(x => _whitelistSystem.IsWhitelistPassOrNull(x.Whitelist, artifact) && + _whitelistSystem.IsBlacklistFailOrNull(x.Blacklist, artifact)).ToList(); var validDepth = allEffects.Select(x => x.TargetDepth).Distinct().ToList(); var weights = GetDepthWeights(validDepth, node.Depth); diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 2e3f9ed461a3..48f4f07cbe1a 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Interaction.Events; using Content.Shared.Popups; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -31,6 +32,7 @@ public sealed class ItemSlotsSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -266,8 +268,7 @@ public bool CanInsert(EntityUid uid, EntityUid usedUid, EntityUid? user, ItemSlo if (slot.ContainerSlot == null) return false; - if ((!slot.Whitelist?.IsValid(usedUid) ?? false) || - (slot.Blacklist?.IsValid(usedUid) ?? false)) + if (_whitelistSystem.IsWhitelistFail(slot.Whitelist, usedUid) || _whitelistSystem.IsBlacklistPass(slot.Blacklist, usedUid)) { if (popup.HasValue && slot.WhitelistFailPopup.HasValue) _popupSystem.PopupClient(Loc.GetString(slot.WhitelistFailPopup), uid, popup.Value); diff --git a/Content.Shared/Damage/Systems/DamageContactsSystem.cs b/Content.Shared/Damage/Systems/DamageContactsSystem.cs index aec3d0766ae9..b08ef77fed5e 100644 --- a/Content.Shared/Damage/Systems/DamageContactsSystem.cs +++ b/Content.Shared/Damage/Systems/DamageContactsSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Damage.Components; +using Content.Shared.Whitelist; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; @@ -11,6 +12,7 @@ public sealed class DamageContactsSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -63,7 +65,7 @@ private void OnEntityEnter(EntityUid uid, DamageContactsComponent component, ref if (HasComp(otherUid)) return; - if (component.IgnoreWhitelist?.IsValid(otherUid) ?? false) + if (_whitelistSystem.IsWhitelistFail(component.IgnoreWhitelist, otherUid)) return; var damagedByContact = EnsureComp(otherUid); diff --git a/Content.Shared/Devour/SharedDevourSystem.cs b/Content.Shared/Devour/SharedDevourSystem.cs index 3d73b14dd356..14047fba7dd2 100644 --- a/Content.Shared/Devour/SharedDevourSystem.cs +++ b/Content.Shared/Devour/SharedDevourSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Popups; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -18,6 +19,7 @@ public abstract class SharedDevourSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -41,7 +43,7 @@ protected void OnInit(EntityUid uid, DevourerComponent component, MapInitEvent a /// protected void OnDevourAction(EntityUid uid, DevourerComponent component, DevourActionEvent args) { - if (args.Handled || component.Whitelist?.IsValid(args.Target, EntityManager) != true) + if (args.Handled || _whitelistSystem.IsWhitelistFailOrNull(component.Whitelist, args.Target)) return; args.Handled = true; diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs index c39139f9a57d..9fdb4a6a804d 100644 --- a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs +++ b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Emag.Systems; using Content.Shared.Item; using Content.Shared.Throwing; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; @@ -25,6 +26,7 @@ public abstract class SharedDisposalUnitSystem : EntitySystem [Dependency] protected readonly IGameTiming GameTiming = default!; [Dependency] protected readonly MetaDataSystem Metadata = default!; [Dependency] protected readonly SharedJointSystem Joints = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; protected static TimeSpan ExitAttemptDelay = TimeSpan.FromSeconds(0.5); @@ -113,10 +115,8 @@ public virtual bool CanInsert(EntityUid uid, SharedDisposalUnitComponent compone if (!storable && !HasComp(entity)) return false; - if (component.Blacklist?.IsValid(entity, EntityManager) == true) - return false; - - if (component.Whitelist != null && component.Whitelist?.IsValid(entity, EntityManager) != true) + if (_whitelistSystem.IsBlacklistPass(component.Blacklist, entity) || + _whitelistSystem.IsWhitelistFail(component.Whitelist, entity)) return false; if (TryComp(entity, out var physics) && (physics.CanCollide) || storable) diff --git a/Content.Shared/Implants/SharedImplanterSystem.cs b/Content.Shared/Implants/SharedImplanterSystem.cs index d78522b56ccf..44803e721c0d 100644 --- a/Content.Shared/Implants/SharedImplanterSystem.cs +++ b/Content.Shared/Implants/SharedImplanterSystem.cs @@ -20,6 +20,7 @@ public abstract class SharedImplanterSystem : EntitySystem [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -105,8 +106,8 @@ public bool CanImplant( protected bool CheckTarget(EntityUid target, EntityWhitelist? whitelist, EntityWhitelist? blacklist) { - return whitelist?.IsValid(target, EntityManager) != false && - blacklist?.IsValid(target, EntityManager) != true; + return _whitelistSystem.IsWhitelistPassOrNull(whitelist, target) && + _whitelistSystem.IsBlacklistFailOrNull(blacklist, target); } //Draw the implant out of the target diff --git a/Content.Shared/Interaction/SmartEquipSystem.cs b/Content.Shared/Interaction/SmartEquipSystem.cs index fb2bc3c46097..bba294db28d2 100644 --- a/Content.Shared/Interaction/SmartEquipSystem.cs +++ b/Content.Shared/Interaction/SmartEquipSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.ActionBlocker; +using Content.Shared.ActionBlocker; using Content.Shared.Containers.ItemSlots; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; @@ -7,6 +7,7 @@ using Content.Shared.Popups; using Content.Shared.Storage; using Content.Shared.Storage.EntitySystems; +using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.Input.Binding; using Robust.Shared.Player; @@ -25,6 +26,7 @@ public sealed class SmartEquipSystem : EntitySystem [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; /// public override void Initialize() @@ -182,7 +184,7 @@ private void HandleSmartEquip(ICommonSession? session, string equipmentSlot) foreach (var slot in slots.Slots.Values) { if (!slot.HasItem - && (slot.Whitelist?.IsValid(handItem.Value, EntityManager) ?? true) + && _whitelistSystem.IsWhitelistPassOrNull(slot.Whitelist, handItem.Value) && slot.Priority > (toInsertTo?.Priority ?? int.MinValue)) { toInsertTo = slot; diff --git a/Content.Shared/Materials/SharedMaterialStorageSystem.cs b/Content.Shared/Materials/SharedMaterialStorageSystem.cs index b1de77d971a0..af815bdb2b7f 100644 --- a/Content.Shared/Materials/SharedMaterialStorageSystem.cs +++ b/Content.Shared/Materials/SharedMaterialStorageSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Interaction; using Content.Shared.Interaction.Components; using Content.Shared.Stacks; +using Content.Shared.Whitelist; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -17,6 +18,7 @@ public abstract class SharedMaterialStorageSystem : EntitySystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; /// /// Default volume for a sheet if the material's entity prototype has no material composition. @@ -121,7 +123,7 @@ public bool CanChangeMaterialAmount(EntityUid uid, string materialId, int volume if (!CanTakeVolume(uid, volume, component)) return false; - if (component.MaterialWhiteList != null && !component.MaterialWhiteList.Contains(materialId)) + if (component.MaterialWhiteList == null ? false : component.MaterialWhiteList.Contains(materialId)) return false; var amount = component.Storage.GetValueOrDefault(materialId); @@ -239,7 +241,7 @@ public virtual bool TryInsertMaterialEntity(EntityUid user, if (!Resolve(toInsert, ref material, ref composition, false)) return false; - if (storage.Whitelist?.IsValid(toInsert) == false) + if (_whitelistSystem.IsWhitelistFail(storage.Whitelist, toInsert)) return false; if (HasComp(toInsert)) diff --git a/Content.Shared/Polymorph/Systems/SharedChameleonProjectorSystem.cs b/Content.Shared/Polymorph/Systems/SharedChameleonProjectorSystem.cs index c1abfc526f5d..00096b7d4094 100644 --- a/Content.Shared/Polymorph/Systems/SharedChameleonProjectorSystem.cs +++ b/Content.Shared/Polymorph/Systems/SharedChameleonProjectorSystem.cs @@ -6,6 +6,7 @@ using Robust.Shared.Serialization.Manager; using Robust.Shared.Prototypes; using System.Diagnostics.CodeAnalysis; +using Content.Shared.Whitelist; namespace Content.Shared.Polymorph.Systems; @@ -18,6 +19,7 @@ public abstract class SharedChameleonProjectorSystem : EntitySystem [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly ISerializationManager _serMan = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -49,8 +51,8 @@ private void OnInteract(Entity ent, ref AfterIntera /// public bool IsInvalid(ChameleonProjectorComponent comp, EntityUid target) { - return (comp.Whitelist?.IsValid(target, EntityManager) == false) - || (comp.Blacklist?.IsValid(target, EntityManager) == true); + return _whitelistSystem.IsWhitelistFail(comp.Whitelist, target) + || _whitelistSystem.IsBlacklistPass(comp.Blacklist, target); } /// diff --git a/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs b/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs index b355ae587303..f94558b0b30f 100644 --- a/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs +++ b/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Popups; using Content.Shared.Stacks; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -30,6 +31,7 @@ public abstract partial class SharedFultonSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [ValidatePrototypeId] public const string EffectProto = "FultonEffect"; protected static readonly Vector2 EffectOffset = Vector2.Zero; @@ -176,7 +178,7 @@ protected bool CanApplyFulton(EntityUid targetUid, FultonComponent component) if (!CanFulton(targetUid)) return false; - if (component.Whitelist?.IsValid(targetUid, EntityManager) != true) + if (_whitelistSystem.IsWhitelistFailOrNull(component.Whitelist, targetUid)) return false; return true; diff --git a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs index d859d9f48590..a382e943ff94 100644 --- a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs +++ b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Shuttles.BUIStates; using Content.Shared.Shuttles.Components; using Content.Shared.Shuttles.UI.MapObjects; +using Content.Shared.Whitelist; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Collision.Shapes; @@ -15,6 +16,7 @@ public abstract partial class SharedShuttleSystem : EntitySystem [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] protected readonly SharedMapSystem Maps = default!; [Dependency] protected readonly SharedTransformSystem XformSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public const float FTLRange = 512f; public const float FTLBufferRange = 8f; @@ -83,7 +85,7 @@ public bool CanFTLTo(EntityUid shuttleUid, MapId targetMap, EntityUid consoleUid if (HasComp(mapUid)) return false; - return destination.Whitelist?.IsValid(shuttleUid, EntityManager) != false; + return _whitelistSystem.IsWhitelistPassOrNull(destination.Whitelist, shuttleUid); } /// diff --git a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs index 03da2d09b0e1..7a8961485d6e 100644 --- a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs +++ b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Storage.Components; using Content.Shared.Inventory; +using Content.Shared.Whitelist; using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Timing; @@ -16,6 +17,8 @@ public sealed class MagnetPickupSystem : EntitySystem [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedStorageSystem _storage = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + private static readonly TimeSpan ScanDelay = TimeSpan.FromSeconds(1); @@ -63,7 +66,7 @@ public override void Update(float frameTime) foreach (var near in _lookup.GetEntitiesInRange(uid, comp.Range, LookupFlags.Dynamic | LookupFlags.Sundries)) { - if (storage.Whitelist?.IsValid(near, EntityManager) == false) + if (_whitelistSystem.IsWhitelistFail(storage.Whitelist, near)) continue; if (!_physicsQuery.TryGetComponent(near, out var physics) || physics.BodyStatus != BodyStatus.OnGround) diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs index 0576e46df4a2..bb49725e0471 100644 --- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Tools.Systems; using Content.Shared.Verbs; using Content.Shared.Wall; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -45,6 +46,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; [Dependency] private readonly WeldableSystem _weldable = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public const string ContainerName = "entity_storage"; @@ -432,7 +434,7 @@ private bool CanFit(EntityUid toInsert, EntityUid container, SharedEntityStorage var targetIsMob = HasComp(toInsert); var storageIsItem = HasComp(container); - var allowedToEat = component.Whitelist?.IsValid(toInsert) ?? HasComp(toInsert); + var allowedToEat = component.Whitelist == null ? HasComp(toInsert) : _whitelistSystem.IsValid(component.Whitelist, toInsert); // BEFORE REPLACING THIS WITH, I.E. A PROPERTY: // Make absolutely 100% sure you have worked out how to stop people ending up in backpacks. diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index ee087901f3ba..d87b57bfc8dd 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -55,6 +55,7 @@ public abstract class SharedStorageSystem : EntitySystem [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; [Dependency] protected readonly UseDelaySystem UseDelay = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; private EntityQuery _itemQuery; private EntityQuery _stackQuery; @@ -860,13 +861,8 @@ public bool CanInsert( return false; } - if (storageComp.Whitelist?.IsValid(insertEnt, EntityManager) == false) - { - reason = "comp-storage-invalid-container"; - return false; - } - - if (storageComp.Blacklist?.IsValid(insertEnt, EntityManager) == true) + if (_whitelistSystem.IsWhitelistFail(storageComp.Whitelist, insertEnt) || + _whitelistSystem.IsBlacklistPass(storageComp.Blacklist, insertEnt)) { reason = "comp-storage-invalid-container"; return false; diff --git a/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs b/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs index d1814020e6e3..eab638a9fc2a 100644 --- a/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs +++ b/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs @@ -1,6 +1,7 @@ using Content.Shared.Damage; using Content.Shared.Projectiles; using Content.Shared.Weapons.Melee.Events; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; @@ -15,6 +16,7 @@ public abstract class SharedDamageMarkerSystem : EntitySystem [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -58,7 +60,7 @@ private void OnMarkerCollide(EntityUid uid, DamageMarkerOnCollideComponent compo if (!args.OtherFixture.Hard || args.OurFixtureId != SharedProjectileSystem.ProjectileFixture || component.Amount <= 0 || - component.Whitelist?.IsValid(args.OtherEntity, EntityManager) == false || + _whitelistSystem.IsWhitelistFail(component.Whitelist, args.OtherEntity) || !TryComp(uid, out var projectile) || projectile.Weapon == null) { diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 784dd0793a89..1f9e6cee76aa 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -41,7 +41,10 @@ private void OnBallisticUse(EntityUid uid, BallisticAmmoProviderComponent compon private void OnBallisticInteractUsing(EntityUid uid, BallisticAmmoProviderComponent component, InteractUsingEvent args) { - if (args.Handled || component.Whitelist?.IsValid(args.Used, EntityManager) != true) + if (args.Handled) + return; + + if (_whitelistSystem.IsWhitelistFailOrNull(component.Whitelist, args.Used)) return; if (GetBallisticShots(component) >= component.Capacity) diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs index b8b00799c1b0..14aaff5bf70d 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs @@ -89,7 +89,7 @@ private void OnRevolverHandleState(EntityUid uid, RevolverAmmoProviderComponent public bool TryRevolverInsert(EntityUid revolverUid, RevolverAmmoProviderComponent component, EntityUid uid, EntityUid? user) { - if (component.Whitelist?.IsValid(uid, EntityManager) == false) + if (_whitelistSystem.IsWhitelistFail(component.Whitelist, uid)) return false; // If it's a speedloader try to get ammo from it. diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 4debc289be1f..47648a07ad44 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -21,6 +21,7 @@ using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -63,6 +64,7 @@ public abstract partial class SharedGunSystem : EntitySystem [Dependency] protected readonly TagSystem TagSystem = default!; [Dependency] protected readonly ThrowingSystem ThrowingSystem = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; private const float InteractNextFire = 0.3f; private const double SafetyNextFire = 0.5; diff --git a/Content.Shared/Whitelist/EntityWhitelistSystem.cs b/Content.Shared/Whitelist/EntityWhitelistSystem.cs index d73646b7e99e..f311946cf987 100644 --- a/Content.Shared/Whitelist/EntityWhitelistSystem.cs +++ b/Content.Shared/Whitelist/EntityWhitelistSystem.cs @@ -60,6 +60,90 @@ public bool IsValid(EntityWhitelist list, EntityUid uid) return list.RequireAll; } + /// The following are a list of "helper functions" that are basically the same as each other + /// to help make code that uses EntityWhitelist a bit more readable because at the moment + /// it is quite clunky having to write out component.Whitelist == null ? true : _whitelist.IsValid(component.Whitelist, uid) + /// several times in a row and makes comparisons easier to read + + /// + /// Helper function to determine if Whitelist is not null and entity is on list + /// + public bool IsWhitelistPass(EntityWhitelist? whitelist, EntityUid uid) + { + if (whitelist == null) + return false; + + return IsValid(whitelist, uid); + } + + /// + /// Helper function to determine if Whitelist is not null and entity is not on the list + /// + public bool IsWhitelistFail(EntityWhitelist? whitelist, EntityUid uid) + { + if (whitelist == null) + return false; + + return !IsValid(whitelist, uid); + } + + /// + /// Helper function to determine if Whitelist is either null or the entity is on the list + /// + public bool IsWhitelistPassOrNull(EntityWhitelist? whitelist, EntityUid uid) + { + if (whitelist == null) + return true; + + return IsValid(whitelist, uid); + } + + /// + /// Helper function to determine if Whitelist is either null or the entity is not on the list + /// + public bool IsWhitelistFailOrNull(EntityWhitelist? whitelist, EntityUid uid) + { + if (whitelist == null) + return true; + + return !IsValid(whitelist, uid); + } + + /// + /// Helper function to determine if Blacklist is not null and entity is on list + /// Duplicate of equivalent Whitelist function + /// + public bool IsBlacklistPass(EntityWhitelist? blacklist, EntityUid uid) + { + return IsWhitelistPass(blacklist, uid); + } + + /// + /// Helper function to determine if Blacklist is not null and entity is not on the list + /// Duplicate of equivalent Whitelist function + /// + public bool IsBlacklistFail(EntityWhitelist? blacklist, EntityUid uid) + { + return IsWhitelistFail(blacklist, uid); + } + + /// + /// Helper function to determine if Blacklist is either null or the entity is on the list + /// Duplicate of equivalent Whitelist function + /// + public bool IsBlacklistPassOrNull(EntityWhitelist? blacklist, EntityUid uid) + { + return IsWhitelistPassOrNull(blacklist, uid); + } + + /// + /// Helper function to determine if Blacklist is either null or the entity is not on the list + /// Duplicate of equivalent Whitelist function + /// + public bool IsBlacklistFailOrNull(EntityWhitelist? blacklist, EntityUid uid) + { + return IsWhitelistFailOrNull(blacklist, uid); + } private void EnsureRegistrations(EntityWhitelist list) { From 21d0b1fd558f417c25e0587cf8d0186ec83a294e Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sat, 1 Jun 2024 23:58:33 -0400 Subject: [PATCH 213/568] Guidebook Tables (#28484) * PJB's cool table control (it probably doesn't work) * ok wait wrong file * Guidebook Tables --- Content.Client/Guidebook/Richtext/Box.cs | 3 + Content.Client/Guidebook/Richtext/ColorBox.cs | 49 +++ Content.Client/Guidebook/Richtext/Table.cs | 27 ++ .../UserInterface/Controls/TableContainer.cs | 285 ++++++++++++++++++ 4 files changed, 364 insertions(+) create mode 100644 Content.Client/Guidebook/Richtext/ColorBox.cs create mode 100644 Content.Client/Guidebook/Richtext/Table.cs create mode 100644 Content.Client/UserInterface/Controls/TableContainer.cs diff --git a/Content.Client/Guidebook/Richtext/Box.cs b/Content.Client/Guidebook/Richtext/Box.cs index ecf6cb21f70a..6e18ad9c5754 100644 --- a/Content.Client/Guidebook/Richtext/Box.cs +++ b/Content.Client/Guidebook/Richtext/Box.cs @@ -11,6 +11,9 @@ public bool TryParseTag(Dictionary args, [NotNullWhen(true)] out HorizontalExpand = true; control = this; + if (args.TryGetValue("Margin", out var margin)) + Margin = new Thickness(float.Parse(margin)); + if (args.TryGetValue("Orientation", out var orientation)) Orientation = Enum.Parse(orientation); else diff --git a/Content.Client/Guidebook/Richtext/ColorBox.cs b/Content.Client/Guidebook/Richtext/ColorBox.cs new file mode 100644 index 000000000000..84de300d6e01 --- /dev/null +++ b/Content.Client/Guidebook/Richtext/ColorBox.cs @@ -0,0 +1,49 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.Guidebook.Richtext; + +[UsedImplicitly] +public sealed class ColorBox : PanelContainer, IDocumentTag +{ + public bool TryParseTag(Dictionary args, [NotNullWhen(true)] out Control? control) + { + HorizontalExpand = true; + VerticalExpand = true; + control = this; + + if (args.TryGetValue("Margin", out var margin)) + Margin = new Thickness(float.Parse(margin)); + + if (args.TryGetValue("HorizontalAlignment", out var halign)) + HorizontalAlignment = Enum.Parse(halign); + else + HorizontalAlignment = HAlignment.Stretch; + + if (args.TryGetValue("VerticalAlignment", out var valign)) + VerticalAlignment = Enum.Parse(valign); + else + VerticalAlignment = VAlignment.Stretch; + + var styleBox = new StyleBoxFlat(); + if (args.TryGetValue("Color", out var color)) + styleBox.BackgroundColor = Color.FromHex(color); + + if (args.TryGetValue("OutlineThickness", out var outlineThickness)) + styleBox.BorderThickness = new Thickness(float.Parse(outlineThickness)); + else + styleBox.BorderThickness = new Thickness(1); + + if (args.TryGetValue("OutlineColor", out var outlineColor)) + styleBox.BorderColor = Color.FromHex(outlineColor); + else + styleBox.BorderColor = Color.White; + + PanelOverride = styleBox; + + return true; + } +} diff --git a/Content.Client/Guidebook/Richtext/Table.cs b/Content.Client/Guidebook/Richtext/Table.cs new file mode 100644 index 000000000000..b6923c3698ea --- /dev/null +++ b/Content.Client/Guidebook/Richtext/Table.cs @@ -0,0 +1,27 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Client.UserInterface.Controls; +using JetBrains.Annotations; +using Robust.Client.UserInterface; + +namespace Content.Client.Guidebook.Richtext; + +[UsedImplicitly] +public sealed class Table : TableContainer, IDocumentTag +{ + public bool TryParseTag(Dictionary args, [NotNullWhen(true)] out Control? control) + { + HorizontalExpand = true; + control = this; + + if (!args.TryGetValue("Columns", out var columns) || !int.TryParse(columns, out var columnsCount)) + { + Logger.Error("Guidebook tag \"Table\" does not specify required property \"Columns.\""); + control = null; + return false; + } + + Columns = columnsCount; + + return true; + } +} diff --git a/Content.Client/UserInterface/Controls/TableContainer.cs b/Content.Client/UserInterface/Controls/TableContainer.cs new file mode 100644 index 000000000000..3e8d4760015a --- /dev/null +++ b/Content.Client/UserInterface/Controls/TableContainer.cs @@ -0,0 +1,285 @@ +using System.Numerics; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.UserInterface.Controls; + +// This control is not part of engine because I quickly wrote it in 2 hours at 2 AM and don't want to deal with +// API stabilization and/or figuring out relation to GridContainer. +// Grid layout is a complicated problem and I don't want to commit another half-baked thing into the engine. +// It's probably sufficient for its use case (RichTextLabel tables for rules/guidebook). +// Despite that, it's still better comment the shit half of you write on a regular basis. +// +// EMO: thank you PJB i was going to kill myself. + +/// +/// Displays children in a tabular grid. Unlike , +/// properly handles layout constraints so putting word-wrapping in it should work. +/// +/// +/// All children are automatically laid out in columns. +/// The first control is in the top left, laid out per row from there. +/// +[Virtual] +public class TableContainer : Container +{ + private int _columns = 1; + + /// + /// The absolute minimum width a column can be forced to. + /// + /// + /// + /// If a column *asks* for less width than this (small contents), it can still be smaller. + /// But if it asks for more it cannot go below this width. + /// + /// + public float MinForcedColumnWidth { get; set; } = 50; + + // Scratch space used while calculating layout, cached to avoid regular allocations during layout pass. + private ColumnData[] _columnDataCache = []; + private RowData[] _rowDataCache = []; + + /// + /// How many columns should be displayed. + /// + public int Columns + { + get => _columns; + set + { + ArgumentOutOfRangeException.ThrowIfLessThan(value, 1, nameof(value)); + + _columns = value; + } + } + + protected override Vector2 MeasureOverride(Vector2 availableSize) + { + ResetCachedArrays(); + + // Do a first pass measuring all child controls as if they're given infinite space. + // This gives us a maximum width the columns want, which we use to proportion them later. + var columnIdx = 0; + foreach (var child in Children) + { + ref var column = ref _columnDataCache[columnIdx]; + + child.Measure(new Vector2(float.PositiveInfinity, float.PositiveInfinity)); + column.MaxWidth = Math.Max(column.MaxWidth, child.DesiredSize.X); + + columnIdx += 1; + if (columnIdx == _columns) + columnIdx = 0; + } + + // Calculate Slack and MinWidth for all columns. Also calculate sums for all columns. + var totalMinWidth = 0f; + var totalMaxWidth = 0f; + var totalSlack = 0f; + + for (var c = 0; c < _columns; c++) + { + ref var column = ref _columnDataCache[c]; + column.MinWidth = Math.Min(column.MaxWidth, MinForcedColumnWidth); + column.Slack = column.MaxWidth - column.MinWidth; + + totalMinWidth += column.MinWidth; + totalMaxWidth += column.MaxWidth; + totalSlack += column.Slack; + } + + if (totalMaxWidth <= availableSize.X) + { + // We want less horizontal space than we're given. Huh, that's convenient. + // Just set assigned width to be however much they asked for. + // We could probably skip the second measure pass in this scenario, + // but that's just an optimization, so I don't care right now. + // + // There's probably a very clever way to make this behavior work with the else block of logic, + // just by fiddling with the math. + // I'm dumb, it's 4:30 AM. Yeah, I *started* at 2 AM. + for (var c = 0; c < _columns; c++) + { + ref var column = ref _columnDataCache[c]; + + column.AssignedWidth = column.MaxWidth; + } + } + else + { + // We don't have enough horizontal space, + // at least without causing *some* sort of word wrapping (assuming text contents). + // + // Assign horizontal space proportional to the wanted maximum size of the columns. + var assignableWidth = Math.Max(0, availableSize.X - totalMinWidth); + for (var c = 0; c < _columns; c++) + { + ref var column = ref _columnDataCache[c]; + + var slackRatio = column.Slack / totalSlack; + column.AssignedWidth = column.MinWidth + slackRatio * assignableWidth; + } + } + + // Go over controls for a second measuring pass, this time giving them their assigned measure width. + // This will give us a height to slot into per-row data. + // We still measure assuming infinite vertical space. + // This control can't properly handle being constrained on the Y axis. + columnIdx = 0; + var rowIdx = 0; + foreach (var child in Children) + { + ref var column = ref _columnDataCache[columnIdx]; + ref var row = ref _rowDataCache[rowIdx]; + + child.Measure(new Vector2(column.AssignedWidth, float.PositiveInfinity)); + row.MeasuredHeight = Math.Max(row.MeasuredHeight, child.DesiredSize.Y); + + columnIdx += 1; + if (columnIdx == _columns) + { + columnIdx = 0; + rowIdx += 1; + } + } + + // Sum up height of all rows to get final measured table height. + var totalHeight = 0f; + for (var r = 0; r < _rowDataCache.Length; r++) + { + ref var row = ref _rowDataCache[r]; + totalHeight += row.MeasuredHeight; + } + + return new Vector2(Math.Min(availableSize.X, totalMaxWidth), totalHeight); + } + + protected override Vector2 ArrangeOverride(Vector2 finalSize) + { + // TODO: Expand to fit given vertical space. + + // Calculate MinWidth and Slack sums again from column data. + // We could've cached these from measure but whatever. + var totalMinWidth = 0f; + var totalSlack = 0f; + + for (var c = 0; c < _columns; c++) + { + ref var column = ref _columnDataCache[c]; + totalMinWidth += column.MinWidth; + totalSlack += column.Slack; + } + + // Calculate new width based on final given size, also assign horizontal positions of all columns. + var assignableWidth = Math.Max(0, finalSize.X - totalMinWidth); + var xPos = 0f; + for (var c = 0; c < _columns; c++) + { + ref var column = ref _columnDataCache[c]; + + var slackRatio = column.Slack / totalSlack; + column.ArrangedWidth = column.MinWidth + slackRatio * assignableWidth; + column.ArrangedX = xPos; + + xPos += column.ArrangedWidth; + } + + // Do actual arrangement row-by-row. + var arrangeY = 0f; + for (var r = 0; r < _rowDataCache.Length; r++) + { + ref var row = ref _rowDataCache[r]; + + for (var c = 0; c < _columns; c++) + { + ref var column = ref _columnDataCache[c]; + var index = c + r * _columns; + + if (index >= ChildCount) // Quit early if we don't actually fill out the row. + break; + var child = GetChild(c + r * _columns); + + child.Arrange(UIBox2.FromDimensions(column.ArrangedX, arrangeY, column.ArrangedWidth, row.MeasuredHeight)); + } + + arrangeY += row.MeasuredHeight; + } + + return finalSize with { Y = arrangeY }; + } + + /// + /// Ensure cached array space is allocated to correct size and is reset to a clean slate. + /// + private void ResetCachedArrays() + { + // 1-argument Array.Clear() is not currently available in sandbox (added in .NET 6). + + if (_columnDataCache.Length != _columns) + _columnDataCache = new ColumnData[_columns]; + + Array.Clear(_columnDataCache, 0, _columnDataCache.Length); + + var rowCount = ChildCount / _columns; + if (ChildCount % _columns != 0) + rowCount += 1; + + if (rowCount != _rowDataCache.Length) + _rowDataCache = new RowData[rowCount]; + + Array.Clear(_rowDataCache, 0, _rowDataCache.Length); + } + + /// + /// Per-column data used during layout. + /// + private struct ColumnData + { + // Measure data. + + /// + /// The maximum width any control in this column wants, if given infinite space. + /// Maximum of all controls on the column. + /// + public float MaxWidth; + + /// + /// The minimum width this column may be given. + /// This is either or . + /// + public float MinWidth; + + /// + /// Difference between max and min width; how much this column can expand from its minimum. + /// + public float Slack; + + /// + /// How much horizontal space this column was assigned at measure time. + /// + public float AssignedWidth; + + // Arrange data. + + /// + /// How much horizontal space this column was assigned at arrange time. + /// + public float ArrangedWidth; + + /// + /// The horizontal position this column was assigned at arrange time. + /// + public float ArrangedX; + } + + private struct RowData + { + // Measure data. + + /// + /// How much height the tallest control on this row was measured at, + /// measuring for infinite vertical space but assigned column width. + /// + public float MeasuredHeight; + } +} From b44b159431630a918356e9625420e8d7fd8d468e Mon Sep 17 00:00:00 2001 From: eoineoineoin Date: Sun, 2 Jun 2024 05:07:41 +0100 Subject: [PATCH 214/568] Replace Matrix3 with System.Numerics.Matrix3x2 (#27443) Replace Matrix3 with Matrix3x2 --- .../SpawnExplosion/ExplosionDebugOverlay.cs | 16 ++++--- .../Atmos/Overlays/AtmosDebugOverlay.cs | 2 +- .../Atmos/Overlays/GasTileOverlay.cs | 4 +- .../Clickable/ClickableComponent.cs | 12 ++--- .../Decals/Overlays/DecalOverlay.cs | 3 +- .../Decals/Overlays/DecalPlacementOverlay.cs | 4 +- Content.Client/DoAfter/DoAfterOverlay.cs | 12 ++--- Content.Client/Explosion/ExplosionOverlay.cs | 5 +- Content.Client/Fluids/PuddleOverlay.cs | 7 +-- Content.Client/Maps/GridDraggingSystem.cs | 4 +- Content.Client/NPC/PathfindingSystem.cs | 8 ++-- .../NodeContainer/NodeVisualizationOverlay.cs | 2 +- .../Overlays/EntityHealthBarOverlay.cs | 12 ++--- .../StencilOverlay.RestrictedRange.cs | 6 +-- .../Overlays/StencilOverlay.Weather.cs | 8 ++-- Content.Client/Overlays/StencilOverlay.cs | 3 +- Content.Client/Paper/UI/StampLabel.xaml.cs | 2 +- Content.Client/Paper/UI/StampWidget.xaml.cs | 2 +- Content.Client/Pinpointer/UI/NavMapControl.cs | 6 +-- Content.Client/Popups/PopupOverlay.cs | 5 +- .../Systems/ShuttleSystem.EmergencyConsole.cs | 3 +- .../Shuttles/UI/BaseShuttleControl.xaml.cs | 7 +-- Content.Client/Shuttles/UI/NavScreen.xaml.cs | 3 +- .../Shuttles/UI/ShuttleDockControl.xaml.cs | 46 +++++++++---------- .../Shuttles/UI/ShuttleMapControl.xaml.cs | 26 +++++------ .../Shuttles/UI/ShuttleNavControl.xaml.cs | 24 +++++----- .../StatusIcon/StatusIconOverlay.cs | 10 ++-- .../Storage/Systems/StorageSystem.cs | 5 +- .../UserInterface/Controls/DirectionIcon.cs | 2 +- .../Controls/MapGridControl.xaml.cs | 2 +- Content.Client/Viewport/ScalingViewport.cs | 18 ++++---- .../Melee/MeleeWeaponSystem.Effects.cs | 2 +- .../Weapons/Ranged/ItemStatus/BulletRender.cs | 4 +- .../EntitySystems/ExplosionGridTileFlood.cs | 13 +++--- .../EntitySystems/ExplosionSystem.GridMap.cs | 12 ++--- .../ExplosionSystem.Processing.cs | 20 ++++---- .../EntitySystems/ExplosionSystem.TileFill.cs | 4 +- .../EntitySystems/ExplosionSystem.Visuals.cs | 3 +- .../Fluids/EntitySystems/AbsorbentSystem.cs | 3 +- .../GameTicking/GameTicker.Spawning.cs | 2 +- .../Medical/SuitSensors/SuitSensorSystem.cs | 5 +- .../Pathfinding/PathfindingSystem.Distance.cs | 2 +- .../NPC/Pathfinding/PathfindingSystem.Grid.cs | 2 +- .../NPC/Pathfinding/PathfindingSystem.cs | 2 +- .../Procedural/DungeonJob.PrefabDunGen.cs | 24 +++++----- .../Procedural/DungeonSystem.Rooms.cs | 16 +++---- .../Systems/RadiationSystem.GridCast.cs | 4 +- Content.Server/Salvage/FultonSystem.cs | 6 ++- .../Shuttles/Events/FTLStartedEvent.cs | 3 +- .../Shuttles/Systems/ArrivalsSystem.cs | 3 +- .../Shuttles/Systems/DockingSystem.Shuttle.cs | 16 +++---- .../Shuttles/Systems/ShuttleSystem.Impact.cs | 5 +- .../EntitySystems/GravityWellSystem.cs | 16 +++---- .../Weapons/Ranged/Systems/GunSystem.cs | 2 +- .../Components/ExplosionVisualsComponent.cs | 7 +-- Content.Shared/Item/SharedItemSystem.cs | 2 +- Content.Shared/Maps/TurfSystem.cs | 2 +- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 2 +- 58 files changed, 236 insertions(+), 215 deletions(-) diff --git a/Content.Client/Administration/UI/SpawnExplosion/ExplosionDebugOverlay.cs b/Content.Client/Administration/UI/SpawnExplosion/ExplosionDebugOverlay.cs index eede3a6217f0..d60094ad8978 100644 --- a/Content.Client/Administration/UI/SpawnExplosion/ExplosionDebugOverlay.cs +++ b/Content.Client/Administration/UI/SpawnExplosion/ExplosionDebugOverlay.cs @@ -25,7 +25,7 @@ public sealed class ExplosionDebugOverlay : Overlay public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace; - public Matrix3 SpaceMatrix; + public Matrix3x2 SpaceMatrix; public MapId Map; private readonly Font _font; @@ -78,7 +78,8 @@ private void DrawScreen(OverlayDrawArgs args) if (SpaceTiles == null) return; - gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds); + Matrix3x2.Invert(SpaceMatrix, out var invSpace); + gridBounds = invSpace.TransformBox(args.WorldBounds); DrawText(handle, gridBounds, SpaceMatrix, SpaceTiles, SpaceTileSize); } @@ -86,7 +87,7 @@ private void DrawScreen(OverlayDrawArgs args) private void DrawText( DrawingHandleScreen handle, Box2 gridBounds, - Matrix3 transform, + Matrix3x2 transform, Dictionary> tileSets, ushort tileSize) { @@ -103,7 +104,7 @@ private void DrawText( if (!gridBounds.Contains(centre)) continue; - var worldCenter = transform.Transform(centre); + var worldCenter = Vector2.Transform(centre, transform); var screenCenter = _eyeManager.WorldToScreen(worldCenter); @@ -119,7 +120,7 @@ private void DrawText( if (tileSets.TryGetValue(0, out var set)) { var epicenter = set.First(); - var worldCenter = transform.Transform((epicenter + Vector2Helpers.Half) * tileSize); + var worldCenter = Vector2.Transform((epicenter + Vector2Helpers.Half) * tileSize, transform); var screenCenter = _eyeManager.WorldToScreen(worldCenter) + new Vector2(-24, -24); var text = $"{Intensity[0]:F2}\nΣ={TotalIntensity:F1}\nΔ={Slope:F1}"; handle.DrawString(_font, screenCenter, text); @@ -148,11 +149,12 @@ private void DrawWorld(in OverlayDrawArgs args) if (SpaceTiles == null) return; - gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds).Enlarged(2); + Matrix3x2.Invert(SpaceMatrix, out var invSpace); + gridBounds = invSpace.TransformBox(args.WorldBounds).Enlarged(2); handle.SetTransform(SpaceMatrix); DrawTiles(handle, gridBounds, SpaceTiles, SpaceTileSize); - handle.SetTransform(Matrix3.Identity); + handle.SetTransform(Matrix3x2.Identity); } private void DrawTiles( diff --git a/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs b/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs index 6dfbc326ecbf..c85dbd20512c 100644 --- a/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs +++ b/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs @@ -66,7 +66,7 @@ protected override void Draw(in OverlayDrawArgs args) DrawData(msg, handle); } - handle.SetTransform(Matrix3.Identity); + handle.SetTransform(Matrix3x2.Identity); } private void DrawData(DebugMessage msg, diff --git a/Content.Client/Atmos/Overlays/GasTileOverlay.cs b/Content.Client/Atmos/Overlays/GasTileOverlay.cs index f4dc274a4e54..17027525e548 100644 --- a/Content.Client/Atmos/Overlays/GasTileOverlay.cs +++ b/Content.Client/Atmos/Overlays/GasTileOverlay.cs @@ -190,7 +190,7 @@ protected override void Draw(in OverlayDrawArgs args) var (_, _, worldMatrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); state.drawHandle.SetTransform(worldMatrix); - var floatBounds = invMatrix.TransformBox(in state.WorldBounds).Enlarged(grid.TileSize); + var floatBounds = invMatrix.TransformBox(state.WorldBounds).Enlarged(grid.TileSize); var localBounds = new Box2i( (int) MathF.Floor(floatBounds.Left), (int) MathF.Floor(floatBounds.Bottom), @@ -249,7 +249,7 @@ protected override void Draw(in OverlayDrawArgs args) }); drawHandle.UseShader(null); - drawHandle.SetTransform(Matrix3.Identity); + drawHandle.SetTransform(Matrix3x2.Identity); } private void DrawMapOverlay( diff --git a/Content.Client/Clickable/ClickableComponent.cs b/Content.Client/Clickable/ClickableComponent.cs index cfbd1a99d69b..6f75df46830e 100644 --- a/Content.Client/Clickable/ClickableComponent.cs +++ b/Content.Client/Clickable/ClickableComponent.cs @@ -38,9 +38,9 @@ public bool CheckClick(SpriteComponent sprite, TransformComponent transform, Ent renderOrder = sprite.RenderOrder; var (spritePos, spriteRot) = transform.GetWorldPositionRotation(xformQuery); var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye.Rotation); - bottom = Matrix3.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom; + bottom = Matrix3Helpers.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom; - var invSpriteMatrix = Matrix3.Invert(sprite.GetLocalMatrix()); + Matrix3x2.Invert(sprite.GetLocalMatrix(), out var invSpriteMatrix); // This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites. var relativeRotation = (spriteRot + eye.Rotation).Reduced().FlipPositive(); @@ -48,8 +48,8 @@ public bool CheckClick(SpriteComponent sprite, TransformComponent transform, Ent Angle cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero; // First we get `localPos`, the clicked location in the sprite-coordinate frame. - var entityXform = Matrix3.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping); - var localPos = invSpriteMatrix.Transform(entityXform.Transform(worldPos)); + var entityXform = Matrix3Helpers.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping); + var localPos = Vector2.Transform(Vector2.Transform(worldPos, entityXform), invSpriteMatrix); // Check explicitly defined click-able bounds if (CheckDirBound(sprite, relativeRotation, localPos)) @@ -79,8 +79,8 @@ public bool CheckClick(SpriteComponent sprite, TransformComponent transform, Ent // convert to layer-local coordinates layer.GetLayerDrawMatrix(dir, out var matrix); - var inverseMatrix = Matrix3.Invert(matrix); - var layerLocal = inverseMatrix.Transform(localPos); + Matrix3x2.Invert(matrix, out var inverseMatrix); + var layerLocal = Vector2.Transform(localPos, inverseMatrix); // Convert to image coordinates var layerImagePos = (Vector2i) (layerLocal * EyeManager.PixelsPerMeter * new Vector2(1, -1) + rsiState.Size / 2f); diff --git a/Content.Client/Decals/Overlays/DecalOverlay.cs b/Content.Client/Decals/Overlays/DecalOverlay.cs index d9904ae80b5e..0de3301e5891 100644 --- a/Content.Client/Decals/Overlays/DecalOverlay.cs +++ b/Content.Client/Decals/Overlays/DecalOverlay.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Shared.Decals; using Robust.Client.GameObjects; using Robust.Client.Graphics; @@ -113,7 +114,7 @@ protected override void Draw(in OverlayDrawArgs args) handle.DrawTexture(cache.Texture, decal.Coordinates, angle, decal.Color); } - handle.SetTransform(Matrix3.Identity); + handle.SetTransform(Matrix3x2.Identity); } } } diff --git a/Content.Client/Decals/Overlays/DecalPlacementOverlay.cs b/Content.Client/Decals/Overlays/DecalPlacementOverlay.cs index be277448eda2..845bd7c03d27 100644 --- a/Content.Client/Decals/Overlays/DecalPlacementOverlay.cs +++ b/Content.Client/Decals/Overlays/DecalPlacementOverlay.cs @@ -51,7 +51,7 @@ protected override void Draw(in OverlayDrawArgs args) var handle = args.WorldHandle; handle.SetTransform(worldMatrix); - var localPos = invMatrix.Transform(mousePos.Position); + var localPos = Vector2.Transform(mousePos.Position, invMatrix); if (snap) { @@ -63,6 +63,6 @@ protected override void Draw(in OverlayDrawArgs args) var box = new Box2Rotated(aabb, rotation, localPos); handle.DrawTextureRect(_sprite.Frame0(decal.Sprite), box, color); - handle.SetTransform(Matrix3.Identity); + handle.SetTransform(Matrix3x2.Identity); } } diff --git a/Content.Client/DoAfter/DoAfterOverlay.cs b/Content.Client/DoAfter/DoAfterOverlay.cs index 45981159f069..dfbbf1089172 100644 --- a/Content.Client/DoAfter/DoAfterOverlay.cs +++ b/Content.Client/DoAfter/DoAfterOverlay.cs @@ -56,8 +56,8 @@ protected override void Draw(in OverlayDrawArgs args) // If you use the display UI scale then need to set max(1f, displayscale) because 0 is valid. const float scale = 1f; - var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale)); - var rotationMatrix = Matrix3.CreateRotation(-rotation); + var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale)); + var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation); var curTime = _timing.CurTime; @@ -91,9 +91,9 @@ protected override void Draw(in OverlayDrawArgs args) ? curTime - _meta.GetPauseTime(uid, meta) : curTime; - var worldMatrix = Matrix3.CreateTranslation(worldPosition); - Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld); - Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty); + var worldMatrix = Matrix3Helpers.CreateTranslation(worldPosition); + var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix); + var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld); handle.SetTransform(matty); var offset = 0f; @@ -151,7 +151,7 @@ protected override void Draw(in OverlayDrawArgs args) } handle.UseShader(null); - handle.SetTransform(Matrix3.Identity); + handle.SetTransform(Matrix3x2.Identity); } public Color GetProgressColor(float progress, float alpha = 1f) diff --git a/Content.Client/Explosion/ExplosionOverlay.cs b/Content.Client/Explosion/ExplosionOverlay.cs index 2d8c15f1b9f9..8cf7447a5d8e 100644 --- a/Content.Client/Explosion/ExplosionOverlay.cs +++ b/Content.Client/Explosion/ExplosionOverlay.cs @@ -48,7 +48,7 @@ protected override void Draw(in OverlayDrawArgs args) DrawExplosion(drawHandle, args.WorldBounds, visuals, index, xforms, textures); } - drawHandle.SetTransform(Matrix3.Identity); + drawHandle.SetTransform(Matrix3x2.Identity); drawHandle.UseShader(null); } @@ -78,7 +78,8 @@ private void DrawExplosion( if (visuals.SpaceTiles == null) return; - gridBounds = Matrix3.Invert(visuals.SpaceMatrix).TransformBox(worldBounds).Enlarged(2); + Matrix3x2.Invert(visuals.SpaceMatrix, out var invSpace); + gridBounds = invSpace.TransformBox(worldBounds).Enlarged(2); drawHandle.SetTransform(visuals.SpaceMatrix); DrawTiles(drawHandle, gridBounds, index, visuals.SpaceTiles, visuals, visuals.SpaceTileSize, textures); diff --git a/Content.Client/Fluids/PuddleOverlay.cs b/Content.Client/Fluids/PuddleOverlay.cs index ac6661cfdd83..a8c1d355105b 100644 --- a/Content.Client/Fluids/PuddleOverlay.cs +++ b/Content.Client/Fluids/PuddleOverlay.cs @@ -1,4 +1,5 @@ -using Content.Shared.FixedPoint; +using System.Numerics; +using Content.Shared.FixedPoint; using Robust.Client.Graphics; using Robust.Client.ResourceManagement; using Robust.Shared.Enums; @@ -73,7 +74,7 @@ private void DrawWorld(in OverlayDrawArgs args) } } - drawHandle.SetTransform(Matrix3.Identity); + drawHandle.SetTransform(Matrix3x2.Identity); } private void DrawScreen(in OverlayDrawArgs args) @@ -99,7 +100,7 @@ private void DrawScreen(in OverlayDrawArgs args) if (!gridBounds.Contains(centre)) continue; - var screenCenter = _eyeManager.WorldToScreen(matrix.Transform(centre)); + var screenCenter = _eyeManager.WorldToScreen(Vector2.Transform(centre, matrix)); drawHandle.DrawString(_font, screenCenter, debugOverlayData.CurrentVolume.ToString(), Color.White); } diff --git a/Content.Client/Maps/GridDraggingSystem.cs b/Content.Client/Maps/GridDraggingSystem.cs index e82786847e31..5e9250f0ce96 100644 --- a/Content.Client/Maps/GridDraggingSystem.cs +++ b/Content.Client/Maps/GridDraggingSystem.cs @@ -101,7 +101,7 @@ public override void Update(float frameTime) if (!_mapManager.TryFindGridAt(mousePos, out var gridUid, out var grid)) return; - StartDragging(gridUid, Transform(gridUid).InvWorldMatrix.Transform(mousePos.Position)); + StartDragging(gridUid, Vector2.Transform(mousePos.Position, Transform(gridUid).InvWorldMatrix)); } if (!TryComp(_dragging, out TransformComponent? xform)) @@ -116,7 +116,7 @@ public override void Update(float frameTime) return; } - var localToWorld = xform.WorldMatrix.Transform(_localPosition); + var localToWorld = Vector2.Transform(_localPosition, xform.WorldMatrix); if (localToWorld.EqualsApprox(mousePos.Position, 0.01f)) return; diff --git a/Content.Client/NPC/PathfindingSystem.cs b/Content.Client/NPC/PathfindingSystem.cs index 709601a57b6e..d3ae5091528a 100644 --- a/Content.Client/NPC/PathfindingSystem.cs +++ b/Content.Client/NPC/PathfindingSystem.cs @@ -223,7 +223,7 @@ private void DrawScreen(OverlayDrawArgs args, DrawingHandleScreen screenHandle) foreach (var crumb in chunk.Value) { - var crumbMapPos = worldMatrix.Transform(_system.GetCoordinate(chunk.Key, crumb.Coordinates)); + var crumbMapPos = Vector2.Transform(_system.GetCoordinate(chunk.Key, crumb.Coordinates), worldMatrix); var distance = (crumbMapPos - mouseWorldPos.Position).Length(); if (distance < nearestDistance) @@ -292,7 +292,7 @@ private void DrawScreen(OverlayDrawArgs args, DrawingHandleScreen screenHandle) foreach (var poly in tile) { - if (poly.Box.Contains(invGridMatrix.Transform(mouseWorldPos.Position))) + if (poly.Box.Contains(Vector2.Transform(mouseWorldPos.Position, invGridMatrix))) { nearest = poly; break; @@ -488,7 +488,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle) if (neighborMap.MapId != args.MapId) continue; - neighborPos = invMatrix.Transform(neighborMap.Position); + neighborPos = Vector2.Transform(neighborMap.Position, invMatrix); } else { @@ -576,7 +576,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle) } } - worldHandle.SetTransform(Matrix3.Identity); + worldHandle.SetTransform(Matrix3x2.Identity); } } } diff --git a/Content.Client/NodeContainer/NodeVisualizationOverlay.cs b/Content.Client/NodeContainer/NodeVisualizationOverlay.cs index 691bcb41dbd2..4e8a4a10ca6d 100644 --- a/Content.Client/NodeContainer/NodeVisualizationOverlay.cs +++ b/Content.Client/NodeContainer/NodeVisualizationOverlay.cs @@ -199,7 +199,7 @@ private void DrawWorld(in OverlayDrawArgs overlayDrawArgs) } - handle.SetTransform(Matrix3.Identity); + handle.SetTransform(Matrix3x2.Identity); _gridIndex.Clear(); } diff --git a/Content.Client/Overlays/EntityHealthBarOverlay.cs b/Content.Client/Overlays/EntityHealthBarOverlay.cs index 2b2ff14a22b4..758bb562f92a 100644 --- a/Content.Client/Overlays/EntityHealthBarOverlay.cs +++ b/Content.Client/Overlays/EntityHealthBarOverlay.cs @@ -42,8 +42,8 @@ protected override void Draw(in OverlayDrawArgs args) var xformQuery = _entManager.GetEntityQuery(); const float scale = 1f; - var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale)); - var rotationMatrix = Matrix3.CreateRotation(-rotation); + var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale)); + var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation); var query = _entManager.AllEntityQueryEnumerator(); while (query.MoveNext(out var uid, @@ -83,10 +83,10 @@ protected override void Draw(in OverlayDrawArgs args) continue; var worldPosition = _transform.GetWorldPosition(xform); - var worldMatrix = Matrix3.CreateTranslation(worldPosition); + var worldMatrix = Matrix3Helpers.CreateTranslation(worldPosition); - Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld); - Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty); + var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix); + var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld); handle.SetTransform(matty); @@ -115,7 +115,7 @@ protected override void Draw(in OverlayDrawArgs args) handle.DrawRect(pixelDarken, Black.WithAlpha(128)); } - handle.SetTransform(Matrix3.Identity); + handle.SetTransform(Matrix3x2.Identity); } /// diff --git a/Content.Client/Overlays/StencilOverlay.RestrictedRange.cs b/Content.Client/Overlays/StencilOverlay.RestrictedRange.cs index 9581fec37b74..d29564caa932 100644 --- a/Content.Client/Overlays/StencilOverlay.RestrictedRange.cs +++ b/Content.Client/Overlays/StencilOverlay.RestrictedRange.cs @@ -7,7 +7,7 @@ namespace Content.Client.Overlays; public sealed partial class StencilOverlay { - private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeComponent rangeComp, Matrix3 invMatrix) + private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeComponent rangeComp, Matrix3x2 invMatrix) { var worldHandle = args.WorldHandle; var renderScale = args.Viewport.RenderScale.X; @@ -16,7 +16,7 @@ private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeCompone var length = zoom.X; var bufferRange = MathF.Min(10f, rangeComp.Range); - var pixelCenter = invMatrix.Transform(rangeComp.Origin); + var pixelCenter = Vector2.Transform(rangeComp.Origin, invMatrix); // Something something offset? var vertical = args.Viewport.Size.Y; @@ -44,7 +44,7 @@ private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeCompone worldHandle.DrawRect(localAABB, Color.White); }, Color.Transparent); - worldHandle.SetTransform(Matrix3.Identity); + worldHandle.SetTransform(Matrix3x2.Identity); worldHandle.UseShader(_protoManager.Index("StencilMask").Instance()); worldHandle.DrawTextureRect(_blep!.Texture, worldBounds); var curTime = _timing.RealTime; diff --git a/Content.Client/Overlays/StencilOverlay.Weather.cs b/Content.Client/Overlays/StencilOverlay.Weather.cs index 31bc88af45d1..29ed157a7912 100644 --- a/Content.Client/Overlays/StencilOverlay.Weather.cs +++ b/Content.Client/Overlays/StencilOverlay.Weather.cs @@ -10,7 +10,7 @@ public sealed partial class StencilOverlay { private List> _grids = new(); - private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha, Matrix3 invMatrix) + private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha, Matrix3x2 invMatrix) { var worldHandle = args.WorldHandle; var mapId = args.MapId; @@ -32,7 +32,7 @@ private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, foreach (var grid in _grids) { var matrix = _transform.GetWorldMatrix(grid, xformQuery); - Matrix3.Multiply(in matrix, in invMatrix, out var matty); + var matty = Matrix3x2.Multiply(matrix, invMatrix); worldHandle.SetTransform(matty); foreach (var tile in grid.Comp.GetTilesIntersecting(worldAABB)) @@ -52,7 +52,7 @@ private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, }, Color.Transparent); - worldHandle.SetTransform(Matrix3.Identity); + worldHandle.SetTransform(Matrix3x2.Identity); worldHandle.UseShader(_protoManager.Index("StencilMask").Instance()); worldHandle.DrawTextureRect(_blep!.Texture, worldBounds); var curTime = _timing.RealTime; @@ -62,7 +62,7 @@ private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, worldHandle.UseShader(_protoManager.Index("StencilDraw").Instance()); _parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, Vector2.Zero, modulate: (weatherProto.Color ?? Color.White).WithAlpha(alpha)); - worldHandle.SetTransform(Matrix3.Identity); + worldHandle.SetTransform(Matrix3x2.Identity); worldHandle.UseShader(null); } } diff --git a/Content.Client/Overlays/StencilOverlay.cs b/Content.Client/Overlays/StencilOverlay.cs index e475dca75947..78b1c4d2b15c 100644 --- a/Content.Client/Overlays/StencilOverlay.cs +++ b/Content.Client/Overlays/StencilOverlay.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Client.Parallax; using Content.Client.Weather; using Content.Shared.Salvage; @@ -72,6 +73,6 @@ protected override void Draw(in OverlayDrawArgs args) } args.WorldHandle.UseShader(null); - args.WorldHandle.SetTransform(Matrix3.Identity); + args.WorldHandle.SetTransform(Matrix3x2.Identity); } } diff --git a/Content.Client/Paper/UI/StampLabel.xaml.cs b/Content.Client/Paper/UI/StampLabel.xaml.cs index 6a8eb5f98f94..be6d52baeaf9 100644 --- a/Content.Client/Paper/UI/StampLabel.xaml.cs +++ b/Content.Client/Paper/UI/StampLabel.xaml.cs @@ -50,7 +50,7 @@ protected override void Draw(DrawingHandleScreen handle) base.Draw(handle); // Restore a sane transform+shader - handle.SetTransform(Matrix3.Identity); + handle.SetTransform(Matrix3x2.Identity); handle.UseShader(null); } } diff --git a/Content.Client/Paper/UI/StampWidget.xaml.cs b/Content.Client/Paper/UI/StampWidget.xaml.cs index a04508aeba34..487e0732b46a 100644 --- a/Content.Client/Paper/UI/StampWidget.xaml.cs +++ b/Content.Client/Paper/UI/StampWidget.xaml.cs @@ -53,7 +53,7 @@ protected override void Draw(DrawingHandleScreen handle) base.Draw(handle); // Restore a sane transform+shader - handle.SetTransform(Matrix3.Identity); + handle.SetTransform(Matrix3x2.Identity); handle.UseShader(null); } } diff --git a/Content.Client/Pinpointer/UI/NavMapControl.cs b/Content.Client/Pinpointer/UI/NavMapControl.cs index f4d2f8e2fb8c..3c99a1881817 100644 --- a/Content.Client/Pinpointer/UI/NavMapControl.cs +++ b/Content.Client/Pinpointer/UI/NavMapControl.cs @@ -218,7 +218,7 @@ protected override void KeyBindUp(GUIBoundKeyEventArgs args) // Convert to a world position var unscaledPosition = (localPosition - MidPointVector) / MinimapScale; - var worldPosition = _transformSystem.GetWorldMatrix(_xform).Transform(new Vector2(unscaledPosition.X, -unscaledPosition.Y) + offset); + var worldPosition = Vector2.Transform(new Vector2(unscaledPosition.X, -unscaledPosition.Y) + offset, _transformSystem.GetWorldMatrix(_xform)); // Find closest tracked entity in range var closestEntity = NetEntity.Invalid; @@ -401,7 +401,7 @@ protected override void Draw(DrawingHandleScreen handle) if (mapPos.MapId != MapId.Nullspace) { - var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset; + var position = Vector2.Transform(mapPos.Position, _transformSystem.GetInvWorldMatrix(_xform)) - offset; position = ScalePosition(new Vector2(position.X, -position.Y)); handle.DrawCircle(position, float.Sqrt(MinimapScale) * 2f, value.Color); @@ -422,7 +422,7 @@ protected override void Draw(DrawingHandleScreen handle) if (mapPos.MapId != MapId.Nullspace) { - var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset; + var position = Vector2.Transform(mapPos.Position, _transformSystem.GetInvWorldMatrix(_xform)) - offset; position = ScalePosition(new Vector2(position.X, -position.Y)); var scalingCoefficient = MinmapScaleModifier * float.Sqrt(MinimapScale); diff --git a/Content.Client/Popups/PopupOverlay.cs b/Content.Client/Popups/PopupOverlay.cs index fb6bb3bf565e..77eeb611f517 100644 --- a/Content.Client/Popups/PopupOverlay.cs +++ b/Content.Client/Popups/PopupOverlay.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Shared.Examine; using Robust.Client.Graphics; using Robust.Client.Player; @@ -55,7 +56,7 @@ protected override void Draw(in OverlayDrawArgs args) if (args.ViewportControl == null) return; - args.DrawingHandle.SetTransform(Matrix3.Identity); + args.DrawingHandle.SetTransform(Matrix3x2.Identity); args.DrawingHandle.UseShader(_shader); var scale = _configManager.GetCVar(CVars.DisplayUIScale); @@ -90,7 +91,7 @@ private void DrawWorld(DrawingHandleScreen worldHandle, OverlayDrawArgs args, fl e => e == popup.InitialPos.EntityId || e == ourEntity, entMan: _entManager)) continue; - var pos = matrix.Transform(mapPos.Position); + var pos = Vector2.Transform(mapPos.Position, matrix); _controller.DrawPopup(popup, worldHandle, pos, scale); } } diff --git a/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs b/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs index 7086fd05411f..d5154a87befe 100644 --- a/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs +++ b/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Shared.Shuttles.Events; using Content.Shared.Shuttles.Systems; using Robust.Client.Graphics; @@ -75,6 +76,6 @@ protected override void Draw(in OverlayDrawArgs args) args.WorldHandle.SetTransform(xform.WorldMatrix); args.WorldHandle.DrawRect(Position.Value, Color.Red.WithAlpha(100)); - args.WorldHandle.SetTransform(Matrix3.Identity); + args.WorldHandle.SetTransform(Matrix3x2.Identity); } } diff --git a/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs b/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs index 8774c1dfb5ce..b50d8fa6b218 100644 --- a/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs +++ b/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Client.UserInterface.Controls; using Content.Shared.Shuttles.Components; using Robust.Client.AutoGenerated; @@ -115,7 +116,7 @@ protected void DrawCircles(DrawingHandleScreen handle) } } - protected void DrawGrid(DrawingHandleScreen handle, Matrix3 matrix, Entity grid, Color color, float alpha = 0.01f) + protected void DrawGrid(DrawingHandleScreen handle, Matrix3x2 matrix, Entity grid, Color color, float alpha = 0.01f) { var rator = Maps.GetAllTilesEnumerator(grid.Owner, grid.Comp); var minimapScale = MinimapScale; @@ -289,7 +290,7 @@ private record struct GridDrawJob : IParallelRobustJob public float MinimapScale; public Vector2 MidPoint; - public Matrix3 Matrix; + public Matrix3x2 Matrix; public List Vertices; public Vector2[] ScaledVertices; @@ -297,7 +298,7 @@ private record struct GridDrawJob : IParallelRobustJob public void Execute(int index) { var vert = Vertices[index]; - var adjustedVert = Matrix.Transform(vert); + var adjustedVert = Vector2.Transform(vert, Matrix); adjustedVert = adjustedVert with { Y = -adjustedVert.Y }; var scaledVert = ScalePosition(adjustedVert, MinimapScale, MidPoint); diff --git a/Content.Client/Shuttles/UI/NavScreen.xaml.cs b/Content.Client/Shuttles/UI/NavScreen.xaml.cs index b7b757ea4834..91d95aaa0427 100644 --- a/Content.Client/Shuttles/UI/NavScreen.xaml.cs +++ b/Content.Client/Shuttles/UI/NavScreen.xaml.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Shared.Shuttles.BUIStates; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; @@ -68,7 +69,7 @@ protected override void Draw(DrawingHandleScreen handle) } var (_, worldRot, worldMatrix) = _xformSystem.GetWorldPositionRotationMatrix(gridXform); - var worldPos = worldMatrix.Transform(gridBody.LocalCenter); + var worldPos = Vector2.Transform(gridBody.LocalCenter, worldMatrix); // Get the positive reduced angle. var displayRot = -worldRot.Reduced(); diff --git a/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs index f03c44029525..31f0eecad795 100644 --- a/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs +++ b/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs @@ -108,10 +108,10 @@ protected override void Draw(DrawingHandleScreen handle) var gridNent = EntManager.GetNetEntity(GridEntity); var mapPos = _xformSystem.ToMapCoordinates(_coordinates.Value); var ourGridMatrix = _xformSystem.GetWorldMatrix(gridXform.Owner); - var dockMatrix = Matrix3.CreateTransform(_coordinates.Value.Position, Angle.Zero); - Matrix3.Multiply(dockMatrix, ourGridMatrix, out var offsetMatrix); + var dockMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, Angle.Zero); + var worldFromDock = Matrix3x2.Multiply(dockMatrix, ourGridMatrix); - offsetMatrix = offsetMatrix.Invert(); + Matrix3x2.Invert(worldFromDock, out var offsetMatrix); // Draw nearby grids var controlBounds = PixelSizeBox; @@ -137,7 +137,7 @@ protected override void Draw(DrawingHandleScreen handle) continue; var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner); - Matrix3.Multiply(in gridMatrix, in offsetMatrix, out var matty); + var matty = Matrix3x2.Multiply(gridMatrix, offsetMatrix); var color = _shuttles.GetIFFColor(grid.Owner, grid.Owner == GridEntity, component: iffComp); DrawGrid(handle, matty, grid, color); @@ -151,23 +151,23 @@ protected override void Draw(DrawingHandleScreen handle) if (ViewedDock == dock.Entity) continue; - var position = matty.Transform(dock.Coordinates.Position); + var position = Vector2.Transform(dock.Coordinates.Position, matty); - var otherDockRotation = Matrix3.CreateRotation(dock.Angle); + var otherDockRotation = Matrix3Helpers.CreateRotation(dock.Angle); var scaledPos = ScalePosition(position with {Y = -position.Y}); if (!controlBounds.Contains(scaledPos.Floored())) continue; // Draw the dock's collision - var collisionBL = matty.Transform(dock.Coordinates.Position + - otherDockRotation.Transform(new Vector2(-0.2f, -0.7f))); - var collisionBR = matty.Transform(dock.Coordinates.Position + - otherDockRotation.Transform(new Vector2(0.2f, -0.7f))); - var collisionTR = matty.Transform(dock.Coordinates.Position + - otherDockRotation.Transform(new Vector2(0.2f, -0.5f))); - var collisionTL = matty.Transform(dock.Coordinates.Position + - otherDockRotation.Transform(new Vector2(-0.2f, -0.5f))); + var collisionBL = Vector2.Transform(dock.Coordinates.Position + + Vector2.Transform(new Vector2(-0.2f, -0.7f), otherDockRotation), matty); + var collisionBR = Vector2.Transform(dock.Coordinates.Position + + Vector2.Transform(new Vector2(0.2f, -0.7f), otherDockRotation), matty); + var collisionTR = Vector2.Transform(dock.Coordinates.Position + + Vector2.Transform(new Vector2(0.2f, -0.5f), otherDockRotation), matty); + var collisionTL = Vector2.Transform(dock.Coordinates.Position + + Vector2.Transform(new Vector2(-0.2f, -0.5f), otherDockRotation), matty); var verts = new[] { @@ -195,10 +195,10 @@ protected override void Draw(DrawingHandleScreen handle) handle.DrawPrimitives(DrawPrimitiveTopology.LineList, verts, otherDockConnection); // Draw the dock itself - var dockBL = matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f)); - var dockBR = matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f)); - var dockTR = matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f)); - var dockTL = matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f)); + var dockBL = Vector2.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f), matty); + var dockBR = Vector2.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f), matty); + var dockTR = Vector2.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f), matty); + var dockTL = Vector2.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f), matty); verts = new[] { @@ -308,14 +308,14 @@ protected override void Draw(DrawingHandleScreen handle) // Draw the dock's collision var invertedPosition = Vector2.Zero; invertedPosition.Y = -invertedPosition.Y; - var rotation = Matrix3.CreateRotation(-_angle.Value + MathF.PI); + var rotation = Matrix3Helpers.CreateRotation(-_angle.Value + MathF.PI); var ourDockConnection = new UIBox2( - ScalePosition(rotation.Transform(new Vector2(-0.2f, -0.7f))), - ScalePosition(rotation.Transform(new Vector2(0.2f, -0.5f)))); + ScalePosition(Vector2.Transform(new Vector2(-0.2f, -0.7f), rotation)), + ScalePosition(Vector2.Transform(new Vector2(0.2f, -0.5f), rotation))); var ourDock = new UIBox2( - ScalePosition(rotation.Transform(new Vector2(-0.5f, 0.5f))), - ScalePosition(rotation.Transform(new Vector2(0.5f, -0.5f)))); + ScalePosition(Vector2.Transform(new Vector2(-0.5f, 0.5f), rotation)), + ScalePosition(Vector2.Transform(new Vector2(0.5f, -0.5f), rotation))); var dockColor = Color.Magenta; var connectionColor = Color.Pink; diff --git a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs index 2f35a8dffd7e..8bd4a338cb0c 100644 --- a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs +++ b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs @@ -114,7 +114,7 @@ protected override void KeyBindUp(GUIBoundKeyEventArgs args) var beaconsOnly = EntManager.TryGetComponent(mapUid, out FTLDestinationComponent? destComp) && destComp.BeaconsOnly; - var mapTransform = Matrix3.CreateInverseTransform(Offset, Angle.Zero); + var mapTransform = Matrix3Helpers.CreateInverseTransform(Offset, Angle.Zero); if (beaconsOnly && TryGetBeacon(_beacons, mapTransform, args.RelativePixelPosition, PixelRect, out var foundBeacon, out _)) { @@ -203,7 +203,7 @@ private void DrawParallax(DrawingHandleScreen handle) /// /// /// - private List GetViewportMapObjects(Matrix3 matty, List mapObjects) + private List GetViewportMapObjects(Matrix3x2 matty, List mapObjects) { var results = new List(); var enlargement = new Vector2i((int) (16 * UIScale), (int) (16 * UIScale)); @@ -217,7 +217,7 @@ private List GetViewportMapObjects(Matrix3 matty, List m var mapCoords = _shuttles.GetMapCoordinates(mapObj); - var relativePos = matty.Transform(mapCoords.Position); + var relativePos = Vector2.Transform(mapCoords.Position, matty); relativePos = relativePos with { Y = -relativePos.Y }; var uiPosition = ScalePosition(relativePos); @@ -250,7 +250,7 @@ protected override void Draw(DrawingHandleScreen handle) DrawParallax(handle); var viewedMapUid = _mapManager.GetMapEntityId(ViewingMap); - var matty = Matrix3.CreateInverseTransform(Offset, Angle.Zero); + var matty = Matrix3Helpers.CreateInverseTransform(Offset, Angle.Zero); var realTime = _timing.RealTime; var viewBox = new Box2(Offset - WorldRangeVector, Offset + WorldRangeVector); var viewportObjects = GetViewportMapObjects(matty, mapObjects); @@ -267,7 +267,7 @@ protected override void Draw(DrawingHandleScreen handle) var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(shuttleXform); gridPos = Maps.GetGridPosition((gridUid, gridPhysics), gridPos, gridRot); - var gridRelativePos = matty.Transform(gridPos); + var gridRelativePos = Vector2.Transform(gridPos, matty); gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y }; var gridUiPos = ScalePosition(gridRelativePos); @@ -296,7 +296,7 @@ protected override void Draw(DrawingHandleScreen handle) continue; } - var adjustedPos = matty.Transform(mapCoords.Position); + var adjustedPos = Vector2.Transform(mapCoords.Position, matty); var localPos = ScalePosition(adjustedPos with { Y = -adjustedPos.Y}); handle.DrawCircle(localPos, exclusion.Range * MinimapScale, exclusionColor.WithAlpha(0.05f)); handle.DrawCircle(localPos, exclusion.Range * MinimapScale, exclusionColor, filled: false); @@ -319,7 +319,7 @@ protected override void Draw(DrawingHandleScreen handle) foreach (var (beaconName, coords, mapO) in GetBeacons(viewportObjects, matty, controlLocalBounds)) { - var localPos = matty.Transform(coords.Position); + var localPos = Vector2.Transform(coords.Position, matty); localPos = localPos with { Y = -localPos.Y }; var beaconUiPos = ScalePosition(localPos); var mapObject = GetMapObject(localPos, Angle.Zero, scale: 0.75f, scalePosition: true); @@ -360,7 +360,7 @@ protected override void Draw(DrawingHandleScreen handle) var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(grid.Owner); gridPos = Maps.GetGridPosition((grid, gridPhysics), gridPos, gridRot); - var gridRelativePos = matty.Transform(gridPos); + var gridRelativePos = Vector2.Transform(gridPos, matty); gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y }; var gridUiPos = ScalePosition(gridRelativePos); @@ -439,7 +439,7 @@ protected override void Draw(DrawingHandleScreen handle) var color = ftlFree ? Color.LimeGreen : Color.Magenta; - var gridRelativePos = matty.Transform(gridPos); + var gridRelativePos = Vector2.Transform(gridPos, matty); gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y }; var gridUiPos = ScalePosition(gridRelativePos); @@ -512,7 +512,7 @@ private void AddMapObject(List edges, List verts, ValueList /// Returns the beacons that intersect the viewport. /// - private IEnumerable<(string Beacon, MapCoordinates Coordinates, IMapObject MapObject)> GetBeacons(List mapObjs, Matrix3 mapTransform, UIBox2i area) + private IEnumerable<(string Beacon, MapCoordinates Coordinates, IMapObject MapObject)> GetBeacons(List mapObjs, Matrix3x2 mapTransform, UIBox2i area) { foreach (var mapO in mapObjs) { @@ -520,7 +520,7 @@ private void AddMapObject(List edges, List verts, ValueList GetMapObject(Vector2 localPos, Angle angle, float sca return mapObj; } - private bool TryGetBeacon(IEnumerable mapObjects, Matrix3 mapTransform, Vector2 mousePos, UIBox2i area, out ShuttleBeaconObject foundBeacon, out Vector2 foundLocalPos) + private bool TryGetBeacon(IEnumerable mapObjects, Matrix3x2 mapTransform, Vector2 mousePos, UIBox2i area, out ShuttleBeaconObject foundBeacon, out Vector2 foundLocalPos) { // In pixels const float BeaconSnapRange = 32f; @@ -579,7 +579,7 @@ private bool TryGetBeacon(IEnumerable mapObjects, Matrix3 mapTransfo if (!_shuttles.CanFTLBeacon(beaconObj.Coordinates)) continue; - var position = mapTransform.Transform(beaconCoords.Position); + var position = Vector2.Transform(beaconCoords.Position, mapTransform); var localPos = ScalePosition(position with {Y = -position.Y}); // If beacon not on screen then ignore it. diff --git a/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs index 00ee6890b28f..0b8720add217 100644 --- a/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs +++ b/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs @@ -137,10 +137,10 @@ protected override void Draw(DrawingHandleScreen handle) var mapPos = _transform.ToMapCoordinates(_coordinates.Value); var offset = _coordinates.Value.Position; - var posMatrix = Matrix3.CreateTransform(offset, _rotation.Value); + var posMatrix = Matrix3Helpers.CreateTransform(offset, _rotation.Value); var (_, ourEntRot, ourEntMatrix) = _transform.GetWorldPositionRotationMatrix(_coordinates.Value.EntityId); - Matrix3.Multiply(posMatrix, ourEntMatrix, out var ourWorldMatrix); - var ourWorldMatrixInvert = ourWorldMatrix.Invert(); + var ourWorldMatrix = Matrix3x2.Multiply(posMatrix, ourEntMatrix); + Matrix3x2.Invert(ourWorldMatrix, out var ourWorldMatrixInvert); // Draw our grid in detail var ourGridId = xform.GridUid; @@ -148,7 +148,7 @@ protected override void Draw(DrawingHandleScreen handle) fixturesQuery.HasComponent(ourGridId.Value)) { var ourGridMatrix = _transform.GetWorldMatrix(ourGridId.Value); - Matrix3.Multiply(in ourGridMatrix, in ourWorldMatrixInvert, out var matrix); + var matrix = Matrix3x2.Multiply(ourGridMatrix, ourWorldMatrixInvert); var color = _shuttles.GetIFFColor(ourGridId.Value, self: true); DrawGrid(handle, matrix, (ourGridId.Value, ourGrid), color); @@ -194,7 +194,7 @@ protected override void Draw(DrawingHandleScreen handle) continue; var gridMatrix = _transform.GetWorldMatrix(gUid); - Matrix3.Multiply(in gridMatrix, in ourWorldMatrixInvert, out var matty); + var matty = Matrix3x2.Multiply(gridMatrix, ourWorldMatrixInvert); var color = _shuttles.GetIFFColor(grid, self: false, iff); // Others default: @@ -207,7 +207,7 @@ protected override void Draw(DrawingHandleScreen handle) { var gridBounds = grid.Comp.LocalAABB; - var gridCentre = matty.Transform(gridBody.LocalCenter); + var gridCentre = Vector2.Transform(gridBody.LocalCenter, matty); gridCentre.Y = -gridCentre.Y; var distance = gridCentre.Length(); var labelText = Loc.GetString("shuttle-console-iff-label", ("name", labelName), @@ -242,7 +242,7 @@ protected override void Draw(DrawingHandleScreen handle) } } - private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3 matrix) + private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3x2 matrix) { if (!ShowDocks) return; @@ -255,7 +255,7 @@ private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3 matrix foreach (var state in docks) { var position = state.Coordinates.Position; - var uiPosition = matrix.Transform(position); + var uiPosition = Vector2.Transform(position, matrix); if (uiPosition.Length() > (WorldRange * 2f) - DockScale) continue; @@ -264,10 +264,10 @@ private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3 matrix var verts = new[] { - matrix.Transform(position + new Vector2(-DockScale, -DockScale)), - matrix.Transform(position + new Vector2(DockScale, -DockScale)), - matrix.Transform(position + new Vector2(DockScale, DockScale)), - matrix.Transform(position + new Vector2(-DockScale, DockScale)), + Vector2.Transform(position + new Vector2(-DockScale, -DockScale), matrix), + Vector2.Transform(position + new Vector2(DockScale, -DockScale), matrix), + Vector2.Transform(position + new Vector2(DockScale, DockScale), matrix), + Vector2.Transform(position + new Vector2(-DockScale, DockScale), matrix), }; for (var i = 0; i < verts.Length; i++) diff --git a/Content.Client/StatusIcon/StatusIconOverlay.cs b/Content.Client/StatusIcon/StatusIconOverlay.cs index 56107cbc0257..f1473bda7a2a 100644 --- a/Content.Client/StatusIcon/StatusIconOverlay.cs +++ b/Content.Client/StatusIcon/StatusIconOverlay.cs @@ -39,8 +39,8 @@ protected override void Draw(in OverlayDrawArgs args) var eyeRot = args.Viewport.Eye?.Rotation ?? default; var xformQuery = _entity.GetEntityQuery(); - var scaleMatrix = Matrix3.CreateScale(new Vector2(1, 1)); - var rotationMatrix = Matrix3.CreateRotation(-eyeRot); + var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(1, 1)); + var rotationMatrix = Matrix3Helpers.CreateRotation(-eyeRot); var query = _entity.AllEntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp, out var sprite, out var xform, out var meta)) @@ -59,9 +59,9 @@ protected override void Draw(in OverlayDrawArgs args) if (icons.Count == 0) continue; - var worldMatrix = Matrix3.CreateTranslation(worldPos); - Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld); - Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty); + var worldMatrix = Matrix3Helpers.CreateTranslation(worldPos); + var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix); + var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld); handle.SetTransform(matty); var countL = 0; diff --git a/Content.Client/Storage/Systems/StorageSystem.cs b/Content.Client/Storage/Systems/StorageSystem.cs index 8bf0dcd98129..b80a855f9865 100644 --- a/Content.Client/Storage/Systems/StorageSystem.cs +++ b/Content.Client/Storage/Systems/StorageSystem.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Linq; +using System.Numerics; using Content.Client.Animations; using Content.Shared.Hands; using Content.Shared.Storage; @@ -149,7 +150,7 @@ public void PickupAnimation(EntityUid item, EntityCoordinates initialCoords, Ent } var finalMapPos = finalCoords.ToMapPos(EntityManager, TransformSystem); - var finalPos = TransformSystem.GetInvWorldMatrix(initialCoords.EntityId).Transform(finalMapPos); + var finalPos = Vector2.Transform(finalMapPos, TransformSystem.GetInvWorldMatrix(initialCoords.EntityId)); _entityPickupAnimation.AnimateEntityPickup(item, initialCoords, finalPos, initialAngle); } diff --git a/Content.Client/UserInterface/Controls/DirectionIcon.cs b/Content.Client/UserInterface/Controls/DirectionIcon.cs index a6cc428091ac..c8fd63b43c9e 100644 --- a/Content.Client/UserInterface/Controls/DirectionIcon.cs +++ b/Content.Client/UserInterface/Controls/DirectionIcon.cs @@ -65,7 +65,7 @@ protected override void Draw(DrawingHandleScreen handle) if (_rotation != null) { var offset = (-_rotation.Value).RotateVec(Size * UIScale / 2) - Size * UIScale / 2; - handle.SetTransform(Matrix3.CreateTransform(GlobalPixelPosition - offset, -_rotation.Value)); + handle.SetTransform(Matrix3Helpers.CreateTransform(GlobalPixelPosition - offset, -_rotation.Value)); } base.Draw(handle); diff --git a/Content.Client/UserInterface/Controls/MapGridControl.xaml.cs b/Content.Client/UserInterface/Controls/MapGridControl.xaml.cs index f6b0929f3b23..a10155f3e8ce 100644 --- a/Content.Client/UserInterface/Controls/MapGridControl.xaml.cs +++ b/Content.Client/UserInterface/Controls/MapGridControl.xaml.cs @@ -169,7 +169,7 @@ protected Vector2 InverseMapPosition(Vector2 value) var inversePos = (value - MidPointVector) / MinimapScale; inversePos = inversePos with { Y = -inversePos.Y }; - inversePos = Matrix3.CreateTransform(Offset, Angle.Zero).Transform(inversePos); + inversePos = Vector2.Transform(inversePos, Matrix3Helpers.CreateTransform(Offset, Angle.Zero)); return inversePos; } diff --git a/Content.Client/Viewport/ScalingViewport.cs b/Content.Client/Viewport/ScalingViewport.cs index 1fa8f17161e2..ccd9636d7764 100644 --- a/Content.Client/Viewport/ScalingViewport.cs +++ b/Content.Client/Viewport/ScalingViewport.cs @@ -277,8 +277,8 @@ public MapCoordinates ScreenToMap(Vector2 coords) EnsureViewportCreated(); - var matrix = Matrix3.Invert(GetLocalToScreenMatrix()); - coords = matrix.Transform(coords); + Matrix3x2.Invert(GetLocalToScreenMatrix(), out var matrix); + coords = Vector2.Transform(coords, matrix); return _viewport!.LocalToWorld(coords); } @@ -291,8 +291,8 @@ public MapCoordinates PixelToMap(Vector2 coords) EnsureViewportCreated(); - var matrix = Matrix3.Invert(GetLocalToScreenMatrix()); - coords = matrix.Transform(coords); + Matrix3x2.Invert(GetLocalToScreenMatrix(), out var matrix); + coords = Vector2.Transform(coords, matrix); var ev = new PixelToMapEvent(coords, this, _viewport!); _entityManager.EventBus.RaiseEvent(EventSource.Local, ref ev); @@ -311,16 +311,16 @@ public Vector2 WorldToScreen(Vector2 map) var matrix = GetLocalToScreenMatrix(); - return matrix.Transform(vpLocal); + return Vector2.Transform(vpLocal, matrix); } - public Matrix3 GetWorldToScreenMatrix() + public Matrix3x2 GetWorldToScreenMatrix() { EnsureViewportCreated(); return _viewport!.GetWorldToLocalMatrix() * GetLocalToScreenMatrix(); } - public Matrix3 GetLocalToScreenMatrix() + public Matrix3x2 GetLocalToScreenMatrix() { EnsureViewportCreated(); @@ -329,9 +329,9 @@ public Matrix3 GetLocalToScreenMatrix() if (scaleFactor.X == 0 || scaleFactor.Y == 0) // Basically a nonsense scenario, at least make sure to return something that can be inverted. - return Matrix3.Identity; + return Matrix3x2.Identity; - return Matrix3.CreateTransform(GlobalPixelPosition + drawBox.TopLeft, 0, scaleFactor); + return Matrix3Helpers.CreateTransform(GlobalPixelPosition + drawBox.TopLeft, 0, scaleFactor); } private void EnsureViewportCreated() diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs index baac42d1937c..3e1a4b1906d5 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs @@ -81,7 +81,7 @@ public override void DoLunge(EntityUid user, EntityUid weapon, Angle angle, Vect case WeaponArcAnimation.None: var (mapPos, mapRot) = TransformSystem.GetWorldPositionRotation(userXform); var worldPos = mapPos + (mapRot - userXform.LocalRotation).RotateVec(localPos); - var newLocalPos = TransformSystem.GetInvWorldMatrix(xform.ParentUid).Transform(worldPos); + var newLocalPos = Vector2.Transform(worldPos, TransformSystem.GetInvWorldMatrix(xform.ParentUid)); TransformSystem.SetLocalPositionNoLerp(animationUid, newLocalPos, xform); if (arcComponent.Fadeout) _animation.Play(animationUid, GetFadeAnimation(sprite, 0f, 0.15f), FadeAnimationKey); diff --git a/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs b/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs index 8aea5d7ee627..e6cb596b9441 100644 --- a/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs +++ b/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using System.Numerics; using Content.Client.Resources; using Robust.Client.Graphics; using Robust.Client.ResourceManagement; @@ -53,7 +53,7 @@ protected override void Draw(DrawingHandleScreen handle) { // Scale rendering in this control by UIScale. var currentTransform = handle.GetTransform(); - handle.SetTransform(Matrix3.CreateScale(new Vector2(UIScale)) * currentTransform); + handle.SetTransform(Matrix3Helpers.CreateScale(new Vector2(UIScale)) * currentTransform); var countPerRow = CountPerRow(Size.X); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs b/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs index 7db1f513f7c4..2ddcc052d85a 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs @@ -14,7 +14,7 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood public MapGridComponent Grid; private bool _needToTransform = false; - private Matrix3 _matrix = Matrix3.Identity; + private Matrix3x2 _matrix = Matrix3x2.Identity; private Vector2 _offset; // Tiles which neighbor an exploding tile, but have not yet had the explosion spread to them due to an @@ -44,7 +44,7 @@ public ExplosionGridTileFlood( int typeIndex, Dictionary edgeTiles, EntityUid? referenceGrid, - Matrix3 spaceMatrix, + Matrix3x2 spaceMatrix, Angle spaceAngle) { Grid = grid; @@ -72,9 +72,10 @@ public ExplosionGridTileFlood( var transform = IoCManager.Resolve().GetComponent(Grid.Owner); var size = (float) Grid.TileSize; - _matrix.R0C2 = size / 2; - _matrix.R1C2 = size / 2; - _matrix *= transform.WorldMatrix * Matrix3.Invert(spaceMatrix); + _matrix.M31 = size / 2; + _matrix.M32 = size / 2; + Matrix3x2.Invert(spaceMatrix, out var invSpace); + _matrix *= transform.WorldMatrix * invSpace; var relativeAngle = transform.WorldRotation - spaceAngle; _offset = relativeAngle.RotateVec(new Vector2(size / 4, size / 4)); } @@ -228,7 +229,7 @@ private void JumpToSpace(Vector2i tile) return; } - var center = _matrix.Transform(tile); + var center = Vector2.Transform(tile, _matrix); SpaceJump.Add(new((int) MathF.Floor(center.X + _offset.X), (int) MathF.Floor(center.Y + _offset.Y))); SpaceJump.Add(new((int) MathF.Floor(center.X - _offset.Y), (int) MathF.Floor(center.Y + _offset.X))); SpaceJump.Add(new((int) MathF.Floor(center.X - _offset.X), (int) MathF.Floor(center.Y - _offset.Y))); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs index 719a2eca7970..556fe7c14158 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs @@ -60,7 +60,7 @@ private void OnGridRemoved(GridRemovalEvent ev) { Dictionary transformedEdges = new(); - var targetMatrix = Matrix3.Identity; + var targetMatrix = Matrix3x2.Identity; Angle targetAngle = new(); var tileSize = DefaultTileSize; var maxDistanceSq = (int) (maxDistance * maxDistance); @@ -75,9 +75,9 @@ private void OnGridRemoved(GridRemovalEvent ev) tileSize = targetGrid.TileSize; } - var offsetMatrix = Matrix3.Identity; - offsetMatrix.R0C2 = tileSize / 2f; - offsetMatrix.R1C2 = tileSize / 2f; + var offsetMatrix = Matrix3x2.Identity; + offsetMatrix.M31 = tileSize / 2f; + offsetMatrix.M32 = tileSize / 2f; // Here we can end up with a triple nested for loop: // foreach other grid @@ -106,7 +106,7 @@ private void OnGridRemoved(GridRemovalEvent ev) var xform = xforms.GetComponent(gridToTransform); var (_, gridWorldRotation, gridWorldMatrix, invGridWorldMatrid) = xform.GetWorldPositionRotationMatrixWithInv(xforms); - var localEpicentre = (Vector2i) invGridWorldMatrid.Transform(epicentre.Position); + var localEpicentre = (Vector2i) Vector2.Transform(epicentre.Position, invGridWorldMatrid); var matrix = offsetMatrix * gridWorldMatrix * targetMatrix; var angle = gridWorldRotation - targetAngle; @@ -119,7 +119,7 @@ private void OnGridRemoved(GridRemovalEvent ev) if (delta.X * delta.X + delta.Y * delta.Y > maxDistanceSq) // no Vector2.Length??? continue; - var center = matrix.Transform(tile); + var center = Vector2.Transform(tile, matrix); if ((dir & NeighborFlag.Cardinal) == 0) { diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index bce3dc21c233..c30a9ce30064 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -298,8 +298,8 @@ private static bool GridQueryCallback( /// Same as , but for SPAAAAAAACE. /// internal void ExplodeSpace(BroadphaseComponent lookup, - Matrix3 spaceMatrix, - Matrix3 invSpaceMatrix, + Matrix3x2 spaceMatrix, + Matrix3x2 invSpaceMatrix, Vector2i tile, float throwForce, DamageSpecifier damage, @@ -341,7 +341,7 @@ internal void ExplodeSpace(BroadphaseComponent lookup, } private static bool SpaceQueryCallback( - ref (List<(EntityUid, TransformComponent)> List, HashSet Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery XformQuery, Box2 GridBox, SharedTransformSystem System) state, + ref (List<(EntityUid, TransformComponent)> List, HashSet Processed, Matrix3x2 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery XformQuery, Box2 GridBox, SharedTransformSystem System) state, in EntityUid uid) { if (state.Processed.Contains(uid)) @@ -352,7 +352,7 @@ private static bool SpaceQueryCallback( if (xform.ParentUid == state.LookupOwner) { // parented directly to the map, use local position - if (state.GridBox.Contains(state.InvSpaceMatrix.Transform(xform.LocalPosition))) + if (state.GridBox.Contains(Vector2.Transform(xform.LocalPosition, state.InvSpaceMatrix))) state.List.Add((uid, xform)); return true; @@ -360,14 +360,14 @@ private static bool SpaceQueryCallback( // finally check if it intersects our tile var wpos = state.System.GetWorldPosition(xform); - if (state.GridBox.Contains(state.InvSpaceMatrix.Transform(wpos))) + if (state.GridBox.Contains(Vector2.Transform(wpos, state.InvSpaceMatrix))) state.List.Add((uid, xform)); return true; } private static bool SpaceQueryCallback( - ref (List<(EntityUid, TransformComponent)> List, HashSet Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery XformQuery, Box2 GridBox, SharedTransformSystem System) state, + ref (List<(EntityUid, TransformComponent)> List, HashSet Processed, Matrix3x2 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery XformQuery, Box2 GridBox, SharedTransformSystem System) state, in FixtureProxy proxy) { var uid = proxy.Entity; @@ -585,12 +585,12 @@ struct ExplosionData /// /// The matrix that defines the reference frame for the explosion in space. /// - private readonly Matrix3 _spaceMatrix; + private readonly Matrix3x2 _spaceMatrix; /// /// Inverse of /// - private readonly Matrix3 _invSpaceMatrix; + private readonly Matrix3x2 _invSpaceMatrix; /// /// Have all the tiles on all the grids been processed? @@ -656,7 +656,7 @@ public Explosion(ExplosionSystem system, List gridData, List tileSetIntensity, MapCoordinates epicenter, - Matrix3 spaceMatrix, + Matrix3x2 spaceMatrix, int area, float tileBreakScale, int maxTileBreak, @@ -695,7 +695,7 @@ public Explosion(ExplosionSystem system, }); _spaceMatrix = spaceMatrix; - _invSpaceMatrix = Matrix3.Invert(spaceMatrix); + Matrix3x2.Invert(spaceMatrix, out _invSpaceMatrix); } foreach (var grid in gridData) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs index a42dd1108313..8b4c0e14c5fa 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs @@ -26,7 +26,7 @@ public sealed partial class ExplosionSystem : EntitySystem /// The maximum intensity that the explosion can have at any given tile. This /// effectively caps the damage that this explosion can do. /// A list of tile-sets and a list of intensity values which describe the explosion. - private (int, List, ExplosionSpaceTileFlood?, Dictionary, Matrix3)? GetExplosionTiles( + private (int, List, ExplosionSpaceTileFlood?, Dictionary, Matrix3x2)? GetExplosionTiles( MapCoordinates epicenter, string typeID, float totalIntensity, @@ -84,7 +84,7 @@ public sealed partial class ExplosionSystem : EntitySystem Dictionary>? previousGridJump; // variables for transforming between grid and space-coordinates - var spaceMatrix = Matrix3.Identity; + var spaceMatrix = Matrix3x2.Identity; var spaceAngle = Angle.Zero; if (referenceGrid != null) { diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs index d332531502c7..5db8b55bf965 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Shared.Explosion; using Content.Shared.Explosion.Components; using Robust.Server.GameObjects; @@ -35,7 +36,7 @@ private void OnGetState(EntityUid uid, ExplosionVisualsComponent component, ref /// /// Constructor for the shared using the server-exclusive explosion classes. /// - private EntityUid CreateExplosionVisualEntity(MapCoordinates epicenter, string prototype, Matrix3 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable gridData, List iterationIntensity) + private EntityUid CreateExplosionVisualEntity(MapCoordinates epicenter, string prototype, Matrix3x2 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable gridData, List iterationIntensity) { var explosionEntity = Spawn(null, MapCoordinates.Nullspace); var comp = AddComp(explosionEntity); diff --git a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs index a3717fd94ce8..52afdcf8b491 100644 --- a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs +++ b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Popups; using Content.Shared.Chemistry.Components; @@ -317,7 +318,7 @@ private bool TryPuddleInteract(EntityUid user, EntityUid used, EntityUid target, var userXform = Transform(user); var targetPos = _transform.GetWorldPosition(target); - var localPos = _transform.GetInvWorldMatrix(userXform).Transform(targetPos); + var localPos = Vector2.Transform(targetPos, _transform.GetInvWorldMatrix(userXform)); localPos = userXform.LocalRotation.RotateVec(localPos); _melee.DoLunge(user, used, Angle.Zero, localPos, null, false); diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 22d368e57bde..5e2ef0c02c0f 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -417,7 +417,7 @@ public EntityCoordinates GetObserverSpawnPoint() { var gridXform = Transform(gridUid); - return new EntityCoordinates(gridUid, gridXform.InvWorldMatrix.Transform(toMap.Position)); + return new EntityCoordinates(gridUid, Vector2.Transform(toMap.Position, gridXform.InvWorldMatrix)); } return spawn; diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs index 38e88f6e1ed8..1acbf292f03a 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Server.Access.Systems; using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Components; @@ -366,8 +367,8 @@ public void SetSensor(EntityUid uid, SuitSensorMode mode, EntityUid? userUid = n if (transform.GridUid != null) { coordinates = new EntityCoordinates(transform.GridUid.Value, - _transform.GetInvWorldMatrix(xformQuery.GetComponent(transform.GridUid.Value), xformQuery) - .Transform(_transform.GetWorldPosition(transform, xformQuery))); + Vector2.Transform(_transform.GetWorldPosition(transform, xformQuery), + _transform.GetInvWorldMatrix(xformQuery.GetComponent(transform.GridUid.Value), xformQuery))); } else if (transform.MapUid != null) { diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs index 5daf38c4209e..b986ed5259e9 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs @@ -36,7 +36,7 @@ private Vector2 GetDiff(PathPoly start, PathPoly end) return Vector2.Zero; } - endPos = startXform.InvWorldMatrix.Transform(endXform.WorldMatrix.Transform(endPos)); + endPos = Vector2.Transform(Vector2.Transform(endPos, endXform.WorldMatrix), startXform.InvWorldMatrix); } // TODO: Numerics when we changeover. diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs index 52f7db77ed65..f4af65c61741 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs @@ -395,7 +395,7 @@ private Vector2i GetOrigin(Vector2 localPos) private Vector2i GetOrigin(EntityCoordinates coordinates, EntityUid gridUid) { - var localPos = _transform.GetInvWorldMatrix(gridUid).Transform(coordinates.ToMapPos(EntityManager, _transform)); + var localPos = Vector2.Transform(coordinates.ToMapPos(EntityManager, _transform), _transform.GetInvWorldMatrix(gridUid)); return new Vector2i((int) Math.Floor(localPos.X / ChunkSize), (int) Math.Floor(localPos.Y / ChunkSize)); } diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.cs index 3672ad047b44..b2958f0ccb55 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.cs @@ -405,7 +405,7 @@ public async void GetPathEvent( return null; } - var localPos = xform.InvWorldMatrix.Transform(coordinates.ToMapPos(EntityManager, _transform)); + var localPos = Vector2.Transform(coordinates.ToMapPos(EntityManager, _transform), xform.InvWorldMatrix); var origin = GetOrigin(localPos); if (!TryGetChunk(origin, comp, out var chunk)) diff --git a/Content.Server/Procedural/DungeonJob.PrefabDunGen.cs b/Content.Server/Procedural/DungeonJob.PrefabDunGen.cs index 1783a5679047..a19f7e4701d9 100644 --- a/Content.Server/Procedural/DungeonJob.PrefabDunGen.cs +++ b/Content.Server/Procedural/DungeonJob.PrefabDunGen.cs @@ -19,7 +19,7 @@ private async Task GeneratePrefabDungeon(PrefabDunGen prefab, EntityUid var gen = _prototype.Index(preset); var dungeonRotation = _dungeon.GetDungeonRotation(seed); - var dungeonTransform = Matrix3.CreateTransform(_position, dungeonRotation); + var dungeonTransform = Matrix3Helpers.CreateTransform(_position, dungeonRotation); var roomPackProtos = new Dictionary>(); foreach (var pack in _prototype.EnumeratePrototypes()) @@ -69,7 +69,7 @@ private async Task GeneratePrefabDungeon(PrefabDunGen prefab, EntityUid var dungeon = new Dungeon(); var availablePacks = new List(); var chosenPacks = new DungeonRoomPackPrototype?[gen.RoomPacks.Count]; - var packTransforms = new Matrix3[gen.RoomPacks.Count]; + var packTransforms = new Matrix3x2[gen.RoomPacks.Count]; var packRotations = new Angle[gen.RoomPacks.Count]; // Actually pick the room packs and rooms @@ -97,7 +97,7 @@ private async Task GeneratePrefabDungeon(PrefabDunGen prefab, EntityUid // Iterate every pack random.Shuffle(availablePacks); - Matrix3 packTransform = default!; + Matrix3x2 packTransform = default!; var found = false; DungeonRoomPackPrototype pack = default!; @@ -128,7 +128,7 @@ private async Task GeneratePrefabDungeon(PrefabDunGen prefab, EntityUid var aRotation = dir.AsDir().ToAngle(); // Use this pack - packTransform = Matrix3.CreateTransform(bounds.Center, aRotation); + packTransform = Matrix3Helpers.CreateTransform(bounds.Center, aRotation); packRotations[i] = aRotation; pack = aPack; break; @@ -168,7 +168,7 @@ private async Task GeneratePrefabDungeon(PrefabDunGen prefab, EntityUid { var roomDimensions = new Vector2i(roomSize.Width, roomSize.Height); Angle roomRotation = Angle.Zero; - Matrix3 matty; + Matrix3x2 matty; if (!roomProtos.TryGetValue(roomDimensions, out var roomProto)) { @@ -176,13 +176,13 @@ private async Task GeneratePrefabDungeon(PrefabDunGen prefab, EntityUid if (!roomProtos.TryGetValue(roomDimensions, out roomProto)) { - Matrix3.Multiply(packTransform, dungeonTransform, out matty); + matty = Matrix3x2.Multiply(packTransform, dungeonTransform); for (var x = roomSize.Left; x < roomSize.Right; x++) { for (var y = roomSize.Bottom; y < roomSize.Top; y++) { - var index = matty.Transform(new Vector2(x, y) + grid.TileSizeHalfVector - packCenter).Floored(); + var index = Vector2.Transform(new Vector2(x, y) + grid.TileSizeHalfVector - packCenter, matty).Floored(); tiles.Add((index, new Tile(_tileDefManager["FloorPlanetGrass"].TileId))); } } @@ -209,10 +209,10 @@ private async Task GeneratePrefabDungeon(PrefabDunGen prefab, EntityUid roomRotation += Math.PI; } - var roomTransform = Matrix3.CreateTransform(roomSize.Center - packCenter, roomRotation); + var roomTransform = Matrix3Helpers.CreateTransform(roomSize.Center - packCenter, roomRotation); - Matrix3.Multiply(roomTransform, packTransform, out matty); - Matrix3.Multiply(matty, dungeonTransform, out var dungeonMatty); + matty = Matrix3x2.Multiply(roomTransform, packTransform); + var dungeonMatty = Matrix3x2.Multiply(matty, dungeonTransform); // The expensive bit yippy. _dungeon.SpawnRoom(gridUid, grid, dungeonMatty, room); @@ -232,7 +232,7 @@ private async Task GeneratePrefabDungeon(PrefabDunGen prefab, EntityUid continue; } - var tilePos = dungeonMatty.Transform(new Vector2i(x + room.Offset.X, y + room.Offset.Y) + tileOffset); + var tilePos = Vector2.Transform(new Vector2i(x + room.Offset.X, y + room.Offset.Y) + tileOffset, dungeonMatty); exterior.Add(tilePos.Floored()); } } @@ -244,7 +244,7 @@ private async Task GeneratePrefabDungeon(PrefabDunGen prefab, EntityUid for (var y = 0; y < room.Size.Y; y++) { var roomTile = new Vector2i(x + room.Offset.X, y + room.Offset.Y); - var tilePos = dungeonMatty.Transform(roomTile + tileOffset); + var tilePos = Vector2.Transform(roomTile + tileOffset, dungeonMatty); var tileIndex = tilePos.Floored(); roomTiles.Add(tileIndex); diff --git a/Content.Server/Procedural/DungeonSystem.Rooms.cs b/Content.Server/Procedural/DungeonSystem.Rooms.cs index 03bcc2b4b13d..5b4de34906e6 100644 --- a/Content.Server/Procedural/DungeonSystem.Rooms.cs +++ b/Content.Server/Procedural/DungeonSystem.Rooms.cs @@ -67,7 +67,7 @@ public void SpawnRoom( bool clearExisting = false, bool rotation = false) { - var originTransform = Matrix3.CreateTranslation(origin); + var originTransform = Matrix3Helpers.CreateTranslation(origin.X, origin.Y); var roomRotation = Angle.Zero; if (rotation) @@ -75,8 +75,8 @@ public void SpawnRoom( roomRotation = GetRoomRotation(room, random); } - var roomTransform = Matrix3.CreateTransform((Vector2) room.Size / 2f, roomRotation); - Matrix3.Multiply(roomTransform, originTransform, out var finalTransform); + var roomTransform = Matrix3Helpers.CreateTransform((Vector2) room.Size / 2f, roomRotation); + var finalTransform = Matrix3x2.Multiply(roomTransform, originTransform); SpawnRoom(gridUid, grid, finalTransform, room, clearExisting); } @@ -101,7 +101,7 @@ public Angle GetRoomRotation(DungeonRoomPrototype room, Random random) public void SpawnRoom( EntityUid gridUid, MapGridComponent grid, - Matrix3 roomTransform, + Matrix3x2 roomTransform, DungeonRoomPrototype room, bool clearExisting = false) { @@ -116,7 +116,7 @@ public void SpawnRoom( // go BRRNNTTT on existing stuff if (clearExisting) { - var gridBounds = new Box2(roomTransform.Transform(Vector2.Zero), roomTransform.Transform(room.Size)); + var gridBounds = new Box2(Vector2.Transform(Vector2.Zero, roomTransform), Vector2.Transform(room.Size, roomTransform)); _entitySet.Clear(); // Polygon skin moment gridBounds = gridBounds.Enlarged(-0.05f); @@ -148,7 +148,7 @@ public void SpawnRoom( var indices = new Vector2i(x + room.Offset.X, y + room.Offset.Y); var tileRef = _maps.GetTileRef(templateMapUid, templateGrid, indices); - var tilePos = roomTransform.Transform(indices + tileOffset); + var tilePos = Vector2.Transform(indices + tileOffset, roomTransform); var rounded = tilePos.Floored(); _tiles.Add((rounded, tileRef.Tile)); } @@ -164,7 +164,7 @@ public void SpawnRoom( foreach (var templateEnt in _lookup.GetEntitiesIntersecting(templateMapUid, bounds, LookupFlags.Uncontained)) { var templateXform = _xformQuery.GetComponent(templateEnt); - var childPos = roomTransform.Transform(templateXform.LocalPosition - roomCenter); + var childPos = Vector2.Transform(templateXform.LocalPosition - roomCenter, roomTransform); var childRot = templateXform.LocalRotation + finalRoomRotation; var protoId = _metaQuery.GetComponent(templateEnt).EntityPrototype?.ID; @@ -192,7 +192,7 @@ public void SpawnRoom( // Offset by 0.5 because decals are offset from bot-left corner // So we convert it to center of tile then convert it back again after transform. // Do these shenanigans because 32x32 decals assume as they are centered on bottom-left of tiles. - var position = roomTransform.Transform(decal.Coordinates + Vector2Helpers.Half - roomCenter); + var position = Vector2.Transform(decal.Coordinates + Vector2Helpers.Half - roomCenter, roomTransform); position -= Vector2Helpers.Half; // Umm uhh I love decals so uhhhh idk what to do about this diff --git a/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs b/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs index b8193c4d2f39..ccee7cf227cb 100644 --- a/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs +++ b/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs @@ -195,11 +195,11 @@ private RadiationRay Gridcast(Entity grid, RadiationRay ray, b Vector2 srcLocal = sourceTrs.ParentUid == grid.Owner ? sourceTrs.LocalPosition - : gridTrs.InvLocalMatrix.Transform(ray.Source); + : Vector2.Transform(ray.Source, gridTrs.InvLocalMatrix); Vector2 dstLocal = destTrs.ParentUid == grid.Owner ? destTrs.LocalPosition - : gridTrs.InvLocalMatrix.Transform(ray.Destination); + : Vector2.Transform(ray.Destination, gridTrs.InvLocalMatrix); Vector2i sourceGrid = new( (int) Math.Floor(srcLocal.X / grid.Comp.TileSize), diff --git a/Content.Server/Salvage/FultonSystem.cs b/Content.Server/Salvage/FultonSystem.cs index ad998e535989..e2c0a6b4c5e2 100644 --- a/Content.Server/Salvage/FultonSystem.cs +++ b/Content.Server/Salvage/FultonSystem.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Shared.Salvage.Fulton; using Robust.Shared.Containers; using Robust.Shared.Map; @@ -61,8 +62,9 @@ private void Fulton(EntityUid uid, FultonedComponent component) var metadata = MetaData(uid); var oldCoords = xform.Coordinates; var offset = _random.NextVector2(1.5f); - var localPos = TransformSystem.GetInvWorldMatrix(beaconXform.ParentUid) - .Transform(TransformSystem.GetWorldPosition(beaconXform)) + offset; + var localPos = Vector2.Transform( + TransformSystem.GetWorldPosition(beaconXform), + TransformSystem.GetInvWorldMatrix(beaconXform.ParentUid)) + offset; TransformSystem.SetCoordinates(uid, new EntityCoordinates(beaconXform.ParentUid, localPos)); diff --git a/Content.Server/Shuttles/Events/FTLStartedEvent.cs b/Content.Server/Shuttles/Events/FTLStartedEvent.cs index 965da7f0c5e9..eafbeddd5c47 100644 --- a/Content.Server/Shuttles/Events/FTLStartedEvent.cs +++ b/Content.Server/Shuttles/Events/FTLStartedEvent.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Robust.Shared.Map; namespace Content.Server.Shuttles.Events; @@ -6,4 +7,4 @@ namespace Content.Server.Shuttles.Events; /// Raised when a shuttle has moved to FTL space. /// [ByRefEvent] -public readonly record struct FTLStartedEvent(EntityUid Entity, EntityCoordinates TargetCoordinates, EntityUid? FromMapUid, Matrix3 FTLFrom, Angle FromRotation); +public readonly record struct FTLStartedEvent(EntityUid Entity, EntityCoordinates TargetCoordinates, EntityUid? FromMapUid, Matrix3x2 FTLFrom, Angle FromRotation); diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index e87e781e620e..eb1f9796b27a 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Numerics; using Content.Server.Administration; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; @@ -277,7 +278,7 @@ private void DumpChildren(EntityUid uid, ref FTLStartedEvent args) foreach (var (ent, xform) in toDump) { var rotation = xform.LocalRotation; - _transform.SetCoordinates(ent, new EntityCoordinates(args.FromMapUid!.Value, args.FTLFrom.Transform(xform.LocalPosition))); + _transform.SetCoordinates(ent, new EntityCoordinates(args.FromMapUid!.Value, Vector2.Transform(xform.LocalPosition, args.FTLFrom))); _transform.SetWorldRotation(ent, args.FromRotation + rotation); } } diff --git a/Content.Server/Shuttles/Systems/DockingSystem.Shuttle.cs b/Content.Server/Shuttles/Systems/DockingSystem.Shuttle.cs index e46a7c715ffa..1a95ef9cb22b 100644 --- a/Content.Server/Shuttles/Systems/DockingSystem.Shuttle.cs +++ b/Content.Server/Shuttles/Systems/DockingSystem.Shuttle.cs @@ -46,13 +46,13 @@ private bool CanDock( FixturesComponent shuttleFixtures, MapGridComponent grid, bool isMap, - out Matrix3 matty, + out Matrix3x2 matty, out Box2 shuttleDockedAABB, out Angle gridRotation) { shuttleDockedAABB = Box2.UnitCentered; gridRotation = Angle.Zero; - matty = Matrix3.Identity; + matty = Matrix3x2.Identity; if (shuttleDock.Docked || gridDock.Docked || @@ -71,9 +71,9 @@ private bool CanDock( var gridDockAngle = gridDockXform.LocalRotation.Opposite(); var offsetAngle = gridDockAngle - shuttleDockAngle; - var stationDockMatrix = Matrix3.CreateInverseTransform(stationDockPos, shuttleDockAngle); - var gridXformMatrix = Matrix3.CreateTransform(gridDockXform.LocalPosition, gridDockAngle); - Matrix3.Multiply(in stationDockMatrix, in gridXformMatrix, out matty); + var stationDockMatrix = Matrix3Helpers.CreateInverseTransform(stationDockPos, shuttleDockAngle); + var gridXformMatrix = Matrix3Helpers.CreateTransform(gridDockXform.LocalPosition, gridDockAngle); + matty = Matrix3x2.Multiply(stationDockMatrix, gridXformMatrix); if (!ValidSpawn(grid, matty, offsetAngle, shuttleFixtures, isMap)) return false; @@ -193,7 +193,7 @@ private List GetDockingConfigs( } // Can't just use the AABB as we want to get bounds as tight as possible. - var gridPosition = new EntityCoordinates(targetGrid, matty.Transform(Vector2.Zero)); + var gridPosition = new EntityCoordinates(targetGrid, Vector2.Transform(Vector2.Zero, matty)); var spawnPosition = new EntityCoordinates(targetGridXform.MapUid!.Value, gridPosition.ToMapPos(EntityManager, _transform)); // TODO: use tight bounds @@ -303,9 +303,9 @@ private List GetDockingConfigs( /// /// Checks whether the shuttle can warp to the specified position. /// - private bool ValidSpawn(MapGridComponent grid, Matrix3 matty, Angle angle, FixturesComponent shuttleFixturesComp, bool isMap) + private bool ValidSpawn(MapGridComponent grid, Matrix3x2 matty, Angle angle, FixturesComponent shuttleFixturesComp, bool isMap) { - var transform = new Transform(matty.Transform(Vector2.Zero), angle); + var transform = new Transform(Vector2.Transform(Vector2.Zero, matty), angle); // Because some docking bounds are tight af need to check each chunk individually foreach (var fix in shuttleFixturesComp.Fixtures.Values) diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs index f346398cdaaa..8a8d2d883d09 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Server.Shuttles.Components; using Content.Shared.Audio; using Robust.Shared.Audio; @@ -38,8 +39,8 @@ private void OnShuttleCollide(EntityUid uid, ShuttleComponent component, ref Sta var otherXform = Transform(args.OtherEntity); - var ourPoint = ourXform.InvWorldMatrix.Transform(args.WorldPoint); - var otherPoint = otherXform.InvWorldMatrix.Transform(args.WorldPoint); + var ourPoint = Vector2.Transform(args.WorldPoint, ourXform.InvWorldMatrix); + var otherPoint = Vector2.Transform(args.WorldPoint, otherXform.InvWorldMatrix); var ourVelocity = _physics.GetLinearVelocity(uid, ourPoint, ourBody, ourXform); var otherVelocity = _physics.GetLinearVelocity(args.OtherEntity, otherPoint, otherBody, otherXform); diff --git a/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs b/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs index f1d0af6f9050..779b2f59719d 100644 --- a/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs +++ b/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Server.Atmos.Components; using Content.Server.Singularity.Components; using Content.Shared.Ghost; @@ -126,7 +127,7 @@ private bool CanGravPulseAffect(EntityUid entity) /// The minimum distance at which entities can be affected by the gravity pulse. /// The base velocity added to any entities within affected by the gravity pulse scaled by the displacement of those entities from the epicenter. /// (optional) The transform of the entity at the epicenter of the gravitational pulse. - public void GravPulse(EntityUid uid, float maxRange, float minRange, in Matrix3 baseMatrixDeltaV, TransformComponent? xform = null) + public void GravPulse(EntityUid uid, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV, TransformComponent? xform = null) { if (Resolve(uid, ref xform)) GravPulse(xform.Coordinates, maxRange, minRange, in baseMatrixDeltaV); @@ -154,7 +155,7 @@ public void GravPulse(EntityUid uid, float maxRange, float minRange, float baseR /// The maximum distance at which entities can be affected by the gravity pulse. /// The minimum distance at which entities can be affected by the gravity pulse. /// The base velocity added to any entities within affected by the gravity pulse scaled by the displacement of those entities from the epicenter. - public void GravPulse(EntityCoordinates entityPos, float maxRange, float minRange, in Matrix3 baseMatrixDeltaV) + public void GravPulse(EntityCoordinates entityPos, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV) => GravPulse(entityPos.ToMap(EntityManager, _transform), maxRange, minRange, in baseMatrixDeltaV); /// @@ -175,7 +176,7 @@ public void GravPulse(EntityCoordinates entityPos, float maxRange, float minRang /// The maximum distance at which entities can be affected by the gravity pulse. /// The minimum distance at which entities can be affected by the gravity pulse. Exists to prevent div/0 errors. /// The base velocity added to any entities within affected by the gravity pulse scaled by the displacement of those entities from the epicenter. - public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange, in Matrix3 baseMatrixDeltaV) + public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV) { if (mapPos == MapCoordinates.Nullspace) return; // No gravpulses in nullspace please. @@ -205,7 +206,7 @@ public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange, in continue; var scaling = (1f / distance2) * physics.Mass; // TODO: Variable falloff gradiants. - _physics.ApplyLinearImpulse(entity, (displacement * baseMatrixDeltaV) * scaling, body: physics); + _physics.ApplyLinearImpulse(entity, Vector2.Transform(displacement, baseMatrixDeltaV) * scaling, body: physics); } } @@ -218,10 +219,9 @@ public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange, in /// The base amount of velocity that will be added to entities in range towards the epicenter of the pulse. /// The base amount of velocity that will be added to entities in range counterclockwise relative to the epicenter of the pulse. public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange = 0.0f, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f) - => GravPulse(mapPos, maxRange, minRange, new Matrix3( - baseRadialDeltaV, +baseTangentialDeltaV, 0.0f, - -baseTangentialDeltaV, baseRadialDeltaV, 0.0f, - 0.0f, 0.0f, 1.0f + => GravPulse(mapPos, maxRange, minRange, new Matrix3x2( + baseRadialDeltaV, -baseTangentialDeltaV, 0.0f, + +baseTangentialDeltaV, baseRadialDeltaV, 0.0f )); #endregion GravPulse diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 7247109e37fd..cb893299a935 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -396,7 +396,7 @@ private void FireEffects(EntityCoordinates fromCoordinates, float distance, Angl var (_, gridRot, gridInvMatrix) = TransformSystem.GetWorldPositionRotationInvMatrix(gridXform, xformQuery); fromCoordinates = new EntityCoordinates(gridUid.Value, - gridInvMatrix.Transform(fromCoordinates.ToMapPos(EntityManager, TransformSystem))); + Vector2.Transform(fromCoordinates.ToMapPos(EntityManager, TransformSystem), gridInvMatrix)); // Use the fallback angle I guess? angle -= gridRot; diff --git a/Content.Shared/Explosion/Components/ExplosionVisualsComponent.cs b/Content.Shared/Explosion/Components/ExplosionVisualsComponent.cs index 7477b6c26eae..88e81c93c528 100644 --- a/Content.Shared/Explosion/Components/ExplosionVisualsComponent.cs +++ b/Content.Shared/Explosion/Components/ExplosionVisualsComponent.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Robust.Shared.GameStates; using Robust.Shared.Map; using Robust.Shared.Serialization; @@ -15,7 +16,7 @@ public sealed partial class ExplosionVisualsComponent : Component public Dictionary>> Tiles = new(); public List Intensity = new(); public string ExplosionType = string.Empty; - public Matrix3 SpaceMatrix; + public Matrix3x2 SpaceMatrix; public ushort SpaceTileSize; } @@ -27,7 +28,7 @@ public sealed class ExplosionVisualsState : ComponentState public Dictionary>> Tiles; public List Intensity; public string ExplosionType = string.Empty; - public Matrix3 SpaceMatrix; + public Matrix3x2 SpaceMatrix; public ushort SpaceTileSize; public ExplosionVisualsState( @@ -36,7 +37,7 @@ public ExplosionVisualsState( List intensity, Dictionary>? spaceTiles, Dictionary>> tiles, - Matrix3 spaceMatrix, + Matrix3x2 spaceMatrix, ushort spaceTileSize) { Epicenter = epicenter; diff --git a/Content.Shared/Item/SharedItemSystem.cs b/Content.Shared/Item/SharedItemSystem.cs index 29e82f8adeba..5eaa25f48475 100644 --- a/Content.Shared/Item/SharedItemSystem.cs +++ b/Content.Shared/Item/SharedItemSystem.cs @@ -192,7 +192,7 @@ public IReadOnlyList GetAdjustedItemShape(Entity entity, var shapes = GetItemShape(entity); var boundingShape = shapes.GetBoundingBox(); var boundingCenter = ((Box2) boundingShape).Center; - var matty = Matrix3.CreateTransform(boundingCenter, rotation); + var matty = Matrix3Helpers.CreateTransform(boundingCenter, rotation); var drift = boundingShape.BottomLeft - matty.TransformBox(boundingShape).BottomLeft; var adjustedShapes = new List(); diff --git a/Content.Shared/Maps/TurfSystem.cs b/Content.Shared/Maps/TurfSystem.cs index ad8b3ddea8d1..8a4bbf68bed3 100644 --- a/Content.Shared/Maps/TurfSystem.cs +++ b/Content.Shared/Maps/TurfSystem.cs @@ -44,7 +44,7 @@ public bool IsTileBlocked(EntityUid gridUid, var size = grid.TileSize; var localPos = new Vector2(indices.X * size + (size / 2f), indices.Y * size + (size / 2f)); - var worldPos = matrix.Transform(localPos); + var worldPos = Vector2.Transform(localPos, matrix); // This is scaled to 95 % so it doesn't encompass walls on other tiles. var tileAabb = Box2.UnitCentered.Scale(0.95f * size); diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 2d9993096bc5..b67acdea4f99 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -751,7 +751,7 @@ private void DoLungeAnimation(EntityUid user, EntityUid weapon, Angle angle, Map return; var invMatrix = TransformSystem.GetInvWorldMatrix(userXform); - var localPos = invMatrix.Transform(coordinates.Position); + var localPos = Vector2.Transform(coordinates.Position, invMatrix); if (localPos.LengthSquared() <= 0f) return; From 6235fa1ac69f72d0ed9ef4b089245f04e7ca01f9 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 2 Jun 2024 16:08:15 +1200 Subject: [PATCH 215/568] Content changes for entity categories PR (#27232) * Content changes for entity categories PR * Poke tests * Why are tests suddenly working? --- Content.IntegrationTests/Tests/EntityTest.cs | 11 +++-------- .../Rules/Components/NukeOperativeSpawnerComponent.cs | 5 +++-- .../Roles/Components/GhostRoleMobSpawnerComponent.cs | 2 +- .../Components/RandomHumanoidSpawnerComponent.cs | 2 +- .../Components/ConditionalSpawnerComponent.cs | 2 +- .../Spawners/Components/RandomSpawnerComponent.cs | 2 +- .../Spawners/Components/TimedSpawnerComponent.cs | 2 +- Content.Shared/Actions/BaseActionComponent.cs | 4 +++- .../Body/Prototypes/BodyPrototypeSerializer.cs | 2 +- Content.Shared/Dragon/SharedDragonRiftComponent.cs | 3 ++- Resources/Locale/en-US/entity-categories.ftl | 1 + Resources/Prototypes/Alerts/alerts.yml | 2 +- Resources/Prototypes/Alerts/revenant.yml | 2 +- .../Prototypes/Entities/Objects/Devices/flatpack.yml | 2 +- .../Entities/Structures/Machines/flatpacker.yml | 2 +- Resources/Prototypes/Entities/categories.yml | 4 ++++ 16 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 Resources/Locale/en-US/entity-categories.ftl create mode 100644 Resources/Prototypes/Entities/categories.yml diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 54af64122be0..926374cf0500 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -19,6 +19,8 @@ namespace Content.IntegrationTests.Tests [TestOf(typeof(EntityUid))] public sealed class EntityTest { + private static readonly ProtoId SpawnerCategory = "Spawner"; + [Test] public async Task SpawnAndDeleteAllEntitiesOnDifferentMaps() { @@ -234,14 +236,6 @@ public async Task SpawnAndDeleteEntityCountTest() "StationEvent", "TimedDespawn", - // Spawner entities - "DragonRift", - "RandomHumanoidSpawner", - "RandomSpawner", - "ConditionalSpawner", - "GhostRoleMobSpawner", - "NukeOperativeSpawner", - "TimedSpawner", // makes an announcement on mapInit. "AnnounceOnSpawn", }; @@ -253,6 +247,7 @@ public async Task SpawnAndDeleteEntityCountTest() .Where(p => !p.Abstract) .Where(p => !pair.IsTestPrototype(p)) .Where(p => !excluded.Any(p.Components.ContainsKey)) + .Where(p => p.Categories.All(x => x.ID != SpawnerCategory)) .Select(p => p.ID) .ToList(); diff --git a/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs b/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs index bb1b7c874606..54eaa6e32e4c 100644 --- a/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs @@ -1,3 +1,5 @@ +using Robust.Shared.Prototypes; + namespace Content.Server.GameTicking.Rules.Components; /// @@ -5,6 +7,5 @@ namespace Content.Server.GameTicking.Rules.Components; /// and providing loadout + name for the operative on spawn. /// TODO: Remove once systems can request spawns from the ghost role system directly. /// -[RegisterComponent] +[RegisterComponent, EntityCategory("Spawner")] public sealed partial class NukeOperativeSpawnerComponent : Component; - diff --git a/Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs b/Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs index 6c2a6986fc96..6116173f904b 100644 --- a/Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs +++ b/Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs @@ -5,7 +5,7 @@ namespace Content.Server.Ghost.Roles.Components /// /// Allows a ghost to take this role, spawning a new entity. /// - [RegisterComponent] + [RegisterComponent, EntityCategory("Spawner")] [Access(typeof(GhostRoleSystem))] public sealed partial class GhostRoleMobSpawnerComponent : Component { diff --git a/Content.Server/Humanoid/Components/RandomHumanoidSpawnerComponent.cs b/Content.Server/Humanoid/Components/RandomHumanoidSpawnerComponent.cs index b56664fe19e3..bb38e94e04ab 100644 --- a/Content.Server/Humanoid/Components/RandomHumanoidSpawnerComponent.cs +++ b/Content.Server/Humanoid/Components/RandomHumanoidSpawnerComponent.cs @@ -8,7 +8,7 @@ namespace Content.Server.Humanoid.Components; /// This is added to a marker entity in order to spawn a randomized /// humanoid ingame. /// -[RegisterComponent] +[RegisterComponent, EntityCategory("Spawner")] public sealed partial class RandomHumanoidSpawnerComponent : Component { [DataField("settings", customTypeSerializer: typeof(PrototypeIdSerializer))] diff --git a/Content.Server/Spawners/Components/ConditionalSpawnerComponent.cs b/Content.Server/Spawners/Components/ConditionalSpawnerComponent.cs index 5b98989bb3ee..1b06367b0d86 100644 --- a/Content.Server/Spawners/Components/ConditionalSpawnerComponent.cs +++ b/Content.Server/Spawners/Components/ConditionalSpawnerComponent.cs @@ -2,7 +2,7 @@ namespace Content.Server.Spawners.Components { - [RegisterComponent] + [RegisterComponent, EntityCategory("Spawner")] [Virtual] public partial class ConditionalSpawnerComponent : Component { diff --git a/Content.Server/Spawners/Components/RandomSpawnerComponent.cs b/Content.Server/Spawners/Components/RandomSpawnerComponent.cs index 9bf4d6d25317..e6a597f36586 100644 --- a/Content.Server/Spawners/Components/RandomSpawnerComponent.cs +++ b/Content.Server/Spawners/Components/RandomSpawnerComponent.cs @@ -2,7 +2,7 @@ namespace Content.Server.Spawners.Components { - [RegisterComponent] + [RegisterComponent, EntityCategory("Spawner")] public sealed partial class RandomSpawnerComponent : ConditionalSpawnerComponent { [ViewVariables(VVAccess.ReadWrite)] diff --git a/Content.Server/Spawners/Components/TimedSpawnerComponent.cs b/Content.Server/Spawners/Components/TimedSpawnerComponent.cs index b60afbc88ba3..828e5417177e 100644 --- a/Content.Server/Spawners/Components/TimedSpawnerComponent.cs +++ b/Content.Server/Spawners/Components/TimedSpawnerComponent.cs @@ -9,7 +9,7 @@ namespace Content.Server.Spawners.Components; /// Can configure the set of entities, spawn timing, spawn chance, /// and min/max number of entities to spawn. /// -[RegisterComponent] +[RegisterComponent, EntityCategory("Spawner")] public sealed partial class TimedSpawnerComponent : Component, ISerializationHooks { /// diff --git a/Content.Shared/Actions/BaseActionComponent.cs b/Content.Shared/Actions/BaseActionComponent.cs index 57c145a0ecf3..9156f747f5c9 100644 --- a/Content.Shared/Actions/BaseActionComponent.cs +++ b/Content.Shared/Actions/BaseActionComponent.cs @@ -1,14 +1,16 @@ using Robust.Shared.Audio; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; namespace Content.Shared.Actions; -// TODO ACTIONS make this a seprate component and remove the inheritance stuff. +// TODO ACTIONS make this a separate component and remove the inheritance stuff. // TODO ACTIONS convert to auto comp state? // TODO add access attribute. Need to figure out what to do with decal & mapping actions. // [Access(typeof(SharedActionsSystem))] +[EntityCategory("Actions")] public abstract partial class BaseActionComponent : Component { public abstract BaseActionEvent? BaseEvent { get; } diff --git a/Content.Shared/Body/Prototypes/BodyPrototypeSerializer.cs b/Content.Shared/Body/Prototypes/BodyPrototypeSerializer.cs index e2b54bf951e0..ae099767049b 100644 --- a/Content.Shared/Body/Prototypes/BodyPrototypeSerializer.cs +++ b/Content.Shared/Body/Prototypes/BodyPrototypeSerializer.cs @@ -182,7 +182,7 @@ public BodyPrototype Read(ISerializationManager serializationManager, MappingDat foreach (var (slotId, (part, connections, organs)) in allConnections) { - var slot = new BodyPrototypeSlot(part != null ? new EntProtoId(part) : null!, connections ?? new HashSet(), organs ?? new Dictionary()); + var slot = new BodyPrototypeSlot(part, connections ?? new HashSet(), organs ?? new Dictionary()); slots.Add(slotId, slot); } diff --git a/Content.Shared/Dragon/SharedDragonRiftComponent.cs b/Content.Shared/Dragon/SharedDragonRiftComponent.cs index 0d2bf4401803..8377dbfee7a6 100644 --- a/Content.Shared/Dragon/SharedDragonRiftComponent.cs +++ b/Content.Shared/Dragon/SharedDragonRiftComponent.cs @@ -1,9 +1,10 @@ using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Dragon; -[NetworkedComponent] +[NetworkedComponent, EntityCategory("Spawner")] public abstract partial class SharedDragonRiftComponent : Component { [DataField("state")] diff --git a/Resources/Locale/en-US/entity-categories.ftl b/Resources/Locale/en-US/entity-categories.ftl new file mode 100644 index 000000000000..190fe5713ac4 --- /dev/null +++ b/Resources/Locale/en-US/entity-categories.ftl @@ -0,0 +1 @@ +entity-category-name-actions = Actions diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index 7881cddd4aab..72412fde7cc3 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -28,7 +28,7 @@ - type: entity id: AlertSpriteView - categories: [ hideSpawnMenu ] + categories: [ HideSpawnMenu ] components: - type: Sprite layers: diff --git a/Resources/Prototypes/Alerts/revenant.yml b/Resources/Prototypes/Alerts/revenant.yml index 7f3f98949e00..38933df4fe0c 100644 --- a/Resources/Prototypes/Alerts/revenant.yml +++ b/Resources/Prototypes/Alerts/revenant.yml @@ -16,7 +16,7 @@ - type: entity id: AlertEssenceSpriteView - categories: [ hideSpawnMenu ] + categories: [ HideSpawnMenu ] components: - type: Sprite sprite: /Textures/Interface/Alerts/essence_counter.rsi diff --git a/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml b/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml index e3e77d5c88ef..b9c2b752dba4 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml @@ -4,7 +4,7 @@ name: base flatpack description: A flatpack used for constructing something. categories: - - hideSpawnMenu + - HideSpawnMenu components: - type: Item size: Large diff --git a/Resources/Prototypes/Entities/Structures/Machines/flatpacker.yml b/Resources/Prototypes/Entities/Structures/Machines/flatpacker.yml index b4f05cf68a7b..78f150400306 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/flatpacker.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/flatpacker.yml @@ -84,7 +84,7 @@ - type: entity id: FlatpackerNoBoardEffect categories: - - hideSpawnMenu + - HideSpawnMenu components: - type: Sprite sprite: Structures/Machines/autolathe.rsi diff --git a/Resources/Prototypes/Entities/categories.yml b/Resources/Prototypes/Entities/categories.yml new file mode 100644 index 000000000000..2fb56818f944 --- /dev/null +++ b/Resources/Prototypes/Entities/categories.yml @@ -0,0 +1,4 @@ +- type: entityCategory + id: Actions + name: entity-category-name-actions + hideSpawnMenu: true From 4c667b2d8f898abc4f7b9fcf9d8753f9cbee9afd Mon Sep 17 00:00:00 2001 From: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Date: Sat, 1 Jun 2024 23:13:12 -0500 Subject: [PATCH 216/568] Fix Lasers Being Blocked by External Airlocks and Shuttle Airlocks (#28065) --- .../Structures/Doors/Airlocks/external.yml | 11 +++++ .../Structures/Doors/Airlocks/shuttle.yml | 47 +++---------------- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml index 293aaac273d8..3197ba417f9e 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml @@ -33,3 +33,14 @@ sprite: Structures/Doors/Airlocks/Glass/external.rsi - type: PaintableAirlock group: ExternalGlass + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.49,-0.49,0.49,0.49" # don't want this colliding with walls or they won't close + density: 100 + mask: + - FullTileMask + layer: #removed opaque from the layer, allowing lasers to pass through glass airlocks + - GlassAirlockLayer diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml index 43d1228a408c..5d27cec1815e 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml @@ -82,29 +82,17 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/shuttle.rsi - snapCardinals: false - layers: - - state: closed - map: ["enum.DoorVisualLayers.Base"] - - state: closed_unlit - shader: unshaded - map: ["enum.DoorVisualLayers.BaseUnlit"] - - state: welded - map: ["enum.WeldableLayers.BaseWelded"] - - state: bolted_unlit - shader: unshaded - map: ["enum.DoorVisualLayers.BaseBolted"] - - state: emergency_unlit - shader: unshaded - map: ["enum.DoorVisualLayers.BaseEmergencyAccess"] - - state: panel_open - map: ["enum.WiresVisualLayers.MaintenancePanel"] - type: Occluder enabled: false - type: PaintableAirlock group: ShuttleGlass - type: Door occludes: false + - type: Fixtures + fixtures: + fix1: + layer: #removed opaque from the layer, allowing lasers to pass through glass airlocks + - GlassAirlockLayer - type: entity id: AirlockShuttleAssembly @@ -126,36 +114,13 @@ - type: entity id: AirlockGlassShuttleSyndicate - parent: AirlockShuttle + parent: AirlockGlassShuttle name: external airlock suffix: Glass, Docking description: Necessary for connecting two space craft together. components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/shuttle_syndicate.rsi - snapCardinals: false - layers: - - state: closed - map: ["enum.DoorVisualLayers.Base"] - - state: closed_unlit - shader: unshaded - map: ["enum.DoorVisualLayers.BaseUnlit"] - - state: welded - map: ["enum.WeldableLayers.BaseWelded"] - - state: bolted_unlit - shader: unshaded - map: ["enum.DoorVisualLayers.BaseBolted"] - - state: emergency_unlit - shader: unshaded - map: ["enum.DoorVisualLayers.BaseEmergencyAccess"] - - state: panel_open - map: ["enum.WiresVisualLayers.MaintenancePanel"] - - type: Occluder - enabled: false - - type: PaintableAirlock - group: ShuttleGlass - - type: Door - occludes: false - type: entity parent: AirlockShuttle From f5bc921bdd7e0c2c0c8a4ea7620b6022aa4e503e Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 2 Jun 2024 04:14:18 +0000 Subject: [PATCH 217/568] 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 0c671f49eec1..de9e112c59da 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Plykiya - changes: - - message: Ion storms now have a chance of happening from the start of shift. - type: Tweak - id: 6163 - time: '2024-03-16T03:47:14.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26165 - author: Krunk changes: - message: Zombies and animals can be stripped once again. @@ -3853,3 +3846,10 @@ id: 6662 time: '2024-06-02T01:41:06.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28496 +- author: Cojoke-dot + changes: + - message: Lasers now pass through Glass External Airlocks and Glass Shuttle Airlocks + type: Fix + id: 6663 + time: '2024-06-02T04:13:12.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28065 From 12839893464ef4af1bf3279e9f76f92e82b0afe1 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sun, 2 Jun 2024 06:17:53 +0200 Subject: [PATCH 218/568] Flash overlay rework and bugfixes (#27369) --- Content.Client/Entry/EntryPoint.cs | 1 - Content.Client/Flash/FlashOverlay.cs | 76 ++++++++------ Content.Client/Flash/FlashSystem.cs | 99 ++++++++++--------- .../Components/DamagedByFlashingComponent.cs | 3 +- .../Components/FlashImmunityComponent.cs | 19 ++-- Content.Server/Flash/FlashSystem.cs | 70 ++++++------- .../Flash/Components/FlashComponent.cs | 11 ++- .../Flash/Components/FlashedComponent.cs | 9 ++ Content.Shared/Flash/FlashableComponent.cs | 40 -------- Content.Shared/Flash/SharedFlashSystem.cs | 15 +-- .../Mobs/Cyborgs/base_borg_chassis.yml | 2 +- .../Entities/Mobs/NPCs/argocyte.yml | 1 - .../Prototypes/Entities/Mobs/NPCs/pets.yml | 2 - .../Entities/Mobs/NPCs/simplemob.yml | 2 + .../Prototypes/Entities/Mobs/Species/base.yml | 21 +--- .../Entities/Objects/base_shadow.yml | 3 - Resources/Prototypes/status_effects.yml | 3 + .../Textures/Shaders/flashed_effect.swsl | 2 +- 18 files changed, 179 insertions(+), 200 deletions(-) create mode 100644 Content.Shared/Flash/Components/FlashedComponent.cs delete mode 100644 Content.Shared/Flash/FlashableComponent.cs diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index b28c6a11fbfb..51210a8a93d4 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -152,7 +152,6 @@ public override void PostInit() _parallaxManager.LoadDefaultParallax(); _overlayManager.AddOverlay(new SingularityOverlay()); - _overlayManager.AddOverlay(new FlashOverlay()); _overlayManager.AddOverlay(new RadiationPulseOverlay()); _chatManager.Initialize(); _clientPreferencesManager.Initialize(); diff --git a/Content.Client/Flash/FlashOverlay.cs b/Content.Client/Flash/FlashOverlay.cs index fe9c888227ec..9ea00275e842 100644 --- a/Content.Client/Flash/FlashOverlay.cs +++ b/Content.Client/Flash/FlashOverlay.cs @@ -1,12 +1,11 @@ -using System.Numerics; +using Content.Shared.Flash; +using Content.Shared.Flash.Components; +using Content.Shared.StatusEffect; using Content.Client.Viewport; using Robust.Client.Graphics; using Robust.Client.State; using Robust.Client.Player; using Robust.Shared.Enums; -using Robust.Shared.Graphics; -using Robust.Shared.IoC; -using Robust.Shared.Maths; using Robust.Shared.Prototypes; using Robust.Shared.Timing; using SixLabors.ImageSharp.PixelFormats; @@ -17,66 +16,87 @@ public sealed class FlashOverlay : Overlay { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IClyde _displayManager = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + private readonly StatusEffectsSystem _statusSys; public override OverlaySpace Space => OverlaySpace.WorldSpace; private readonly ShaderInstance _shader; - private double _startTime = -1; - private double _lastsFor = 1; - private Texture? _screenshotTexture; + public float PercentComplete = 0.0f; + public Texture? ScreenshotTexture; public FlashOverlay() { IoCManager.InjectDependencies(this); - _shader = _prototypeManager.Index("FlashedEffect").Instance().Duplicate(); + _shader = _prototypeManager.Index("FlashedEffect").InstanceUnique(); + _statusSys = _entityManager.System(); } - public void ReceiveFlash(double duration) + protected override void FrameUpdate(FrameEventArgs args) + { + var playerEntity = _playerManager.LocalEntity; + + if (playerEntity == null) + return; + + if (!_entityManager.HasComponent(playerEntity) + || !_entityManager.TryGetComponent(playerEntity, out var status)) + return; + + if (!_statusSys.TryGetTime(playerEntity.Value, SharedFlashSystem.FlashedKey, out var time, status)) + return; + + var curTime = _timing.CurTime; + var lastsFor = (float) (time.Value.Item2 - time.Value.Item1).TotalSeconds; + var timeDone = (float) (curTime - time.Value.Item1).TotalSeconds; + + PercentComplete = timeDone / lastsFor; + } + + public void ReceiveFlash() { if (_stateManager.CurrentState is IMainViewportState state) { + // take a screenshot + // note that the callback takes a while and ScreenshotTexture will be null the first few Draws state.Viewport.Viewport.Screenshot(image => { var rgba32Image = image.CloneAs(SixLabors.ImageSharp.Configuration.Default); - _screenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image); + ScreenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image); }); } - - _startTime = _gameTiming.CurTime.TotalSeconds; - _lastsFor = duration; } - protected override void Draw(in OverlayDrawArgs args) + protected override bool BeforeDraw(in OverlayDrawArgs args) { if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp)) - return; - + return false; if (args.Viewport.Eye != eyeComp.Eye) - return; + return false; + + return PercentComplete < 1.0f; + } - var percentComplete = (float) ((_gameTiming.CurTime.TotalSeconds - _startTime) / _lastsFor); - if (percentComplete >= 1.0f) + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenshotTexture == null) return; var worldHandle = args.WorldHandle; + _shader.SetParameter("percentComplete", PercentComplete); worldHandle.UseShader(_shader); - _shader.SetParameter("percentComplete", percentComplete); - - if (_screenshotTexture != null) - { - worldHandle.DrawTextureRectRegion(_screenshotTexture, args.WorldBounds); - } - + worldHandle.DrawTextureRectRegion(ScreenshotTexture, args.WorldBounds); worldHandle.UseShader(null); } protected override void DisposeBehavior() { base.DisposeBehavior(); - _screenshotTexture = null; + ScreenshotTexture = null; + PercentComplete = 1.0f; } } } diff --git a/Content.Client/Flash/FlashSystem.cs b/Content.Client/Flash/FlashSystem.cs index ad8f8b0b82bf..9a0579f6aa38 100644 --- a/Content.Client/Flash/FlashSystem.cs +++ b/Content.Client/Flash/FlashSystem.cs @@ -1,62 +1,67 @@ using Content.Shared.Flash; +using Content.Shared.Flash.Components; +using Content.Shared.StatusEffect; using Robust.Client.Graphics; using Robust.Client.Player; -using Robust.Shared.GameStates; -using Robust.Shared.Timing; +using Robust.Shared.Player; -namespace Content.Client.Flash +namespace Content.Client.Flash; + +public sealed class FlashSystem : SharedFlashSystem { - public sealed class FlashSystem : SharedFlashSystem + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IOverlayManager _overlayMan = default!; + + private FlashOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + SubscribeLocalEvent(OnStatusAdded); + + _overlay = new(); + } + + private void OnPlayerAttached(EntityUid uid, FlashedComponent component, LocalPlayerAttachedEvent args) { - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IOverlayManager _overlayManager = default!; + _overlayMan.AddOverlay(_overlay); + } - public override void Initialize() + private void OnPlayerDetached(EntityUid uid, FlashedComponent component, LocalPlayerDetachedEvent args) + { + _overlay.PercentComplete = 1.0f; + _overlay.ScreenshotTexture = null; + _overlayMan.RemoveOverlay(_overlay); + } + + private void OnInit(EntityUid uid, FlashedComponent component, ComponentInit args) + { + if (_player.LocalEntity == uid) { - base.Initialize(); + _overlayMan.AddOverlay(_overlay); + } + } - SubscribeLocalEvent(OnFlashableHandleState); + private void OnShutdown(EntityUid uid, FlashedComponent component, ComponentShutdown args) + { + if (_player.LocalEntity == uid) + { + _overlay.PercentComplete = 1.0f; + _overlay.ScreenshotTexture = null; + _overlayMan.RemoveOverlay(_overlay); } + } - private void OnFlashableHandleState(EntityUid uid, FlashableComponent component, ref ComponentHandleState args) + private void OnStatusAdded(EntityUid uid, FlashedComponent component, StatusEffectAddedEvent args) + { + if (_player.LocalEntity == uid && args.Key == FlashedKey) { - if (args.Current is not FlashableComponentState state) - return; - - // Yes, this code is awful. I'm just porting it to an entity system so don't blame me. - if (_playerManager.LocalEntity != uid) - { - return; - } - - if (state.Time == default) - { - return; - } - - // Few things here: - // 1. If a shorter duration flash is applied then don't do anything - // 2. If the client-side time is later than when the flash should've ended don't do anything - var currentTime = _gameTiming.CurTime.TotalSeconds; - var newEndTime = state.Time.TotalSeconds + state.Duration; - var currentEndTime = component.LastFlash.TotalSeconds + component.Duration; - - if (currentEndTime > newEndTime) - { - return; - } - - if (currentTime > newEndTime) - { - return; - } - - component.LastFlash = state.Time; - component.Duration = state.Duration; - - var overlay = _overlayManager.GetOverlay(); - overlay.ReceiveFlash(component.Duration); + _overlay.ReceiveFlash(); } } } diff --git a/Content.Server/Flash/Components/DamagedByFlashingComponent.cs b/Content.Server/Flash/Components/DamagedByFlashingComponent.cs index 2a9024607d6a..ef3345429524 100644 --- a/Content.Server/Flash/Components/DamagedByFlashingComponent.cs +++ b/Content.Server/Flash/Components/DamagedByFlashingComponent.cs @@ -3,7 +3,6 @@ namespace Content.Server.Flash.Components; -// Also needed FlashableComponent on entity to work [RegisterComponent, Access(typeof(DamagedByFlashingSystem))] public sealed partial class DamagedByFlashingComponent : Component { @@ -11,5 +10,5 @@ public sealed partial class DamagedByFlashingComponent : Component /// damage from flashing /// [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] - public DamageSpecifier FlashDamage = new (); + public DamageSpecifier FlashDamage = new(); } diff --git a/Content.Server/Flash/Components/FlashImmunityComponent.cs b/Content.Server/Flash/Components/FlashImmunityComponent.cs index 80bbdd12828d..a982a9059f06 100644 --- a/Content.Server/Flash/Components/FlashImmunityComponent.cs +++ b/Content.Server/Flash/Components/FlashImmunityComponent.cs @@ -1,10 +1,13 @@ -namespace Content.Server.Flash.Components +namespace Content.Server.Flash.Components; + +/// +/// Makes the entity immune to being flashed. +/// When given to clothes in the "head", "eyes" or "mask" slot it protects the wearer. +/// +[RegisterComponent, Access(typeof(FlashSystem))] +public sealed partial class FlashImmunityComponent : Component { - [RegisterComponent, Access(typeof(FlashSystem))] - public sealed partial class FlashImmunityComponent : Component - { - [ViewVariables(VVAccess.ReadWrite)] - [DataField("enabled")] - public bool Enabled { get; set; } = true; - } + [ViewVariables(VVAccess.ReadWrite)] + [DataField("enabled")] + public bool Enabled { get; set; } = true; } diff --git a/Content.Server/Flash/FlashSystem.cs b/Content.Server/Flash/FlashSystem.cs index dd8cdab426cb..ccb58e94f814 100644 --- a/Content.Server/Flash/FlashSystem.cs +++ b/Content.Server/Flash/FlashSystem.cs @@ -9,18 +9,17 @@ using Content.Shared.Eye.Blinding.Components; using Content.Shared.Flash; using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory; -using Content.Shared.Physics; using Content.Shared.Tag; using Content.Shared.Traits.Assorted; using Content.Shared.Weapons.Melee.Events; +using Content.Shared.StatusEffect; +using Content.Shared.Examine; using Robust.Server.Audio; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Random; -using Robust.Shared.Timing; using InventoryComponent = Content.Shared.Inventory.InventoryComponent; namespace Content.Server.Flash @@ -31,14 +30,14 @@ internal sealed class FlashSystem : SharedFlashSystem [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly SharedChargesSystem _charges = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!; - [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly StunSystem _stun = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; public override void Initialize() { @@ -46,7 +45,7 @@ public override void Initialize() SubscribeLocalEvent(OnFlashMeleeHit); // ran before toggling light for extra-bright lantern - SubscribeLocalEvent(OnFlashUseInHand, before: new []{ typeof(HandheldLightSystem) }); + SubscribeLocalEvent(OnFlashUseInHand, before: new[] { typeof(HandheldLightSystem) }); SubscribeLocalEvent(OnInventoryFlashAttempt); SubscribeLocalEvent(OnFlashImmunityFlashAttempt); SubscribeLocalEvent(OnPermanentBlindnessFlashAttempt); @@ -114,31 +113,18 @@ public void Flash(EntityUid target, float flashDuration, float slowTo, bool displayPopup = true, - FlashableComponent? flashable = null, bool melee = false, TimeSpan? stunDuration = null) { - if (!Resolve(target, ref flashable, false)) - return; - var attempt = new FlashAttemptEvent(target, user, used); RaiseLocalEvent(target, attempt, true); if (attempt.Cancelled) return; - if (melee) - { - var ev = new AfterFlashedEvent(target, user, used); - if (user != null) - RaiseLocalEvent(user.Value, ref ev); - if (used != null) - RaiseLocalEvent(used.Value, ref ev); - } - - flashable.LastFlash = _timing.CurTime; - flashable.Duration = flashDuration / 1000f; // TODO: Make this sane... - Dirty(target, flashable); + // don't paralyze, slowdown or convert to rev if the target is immune to flashes + if (!_statusEffectsSystem.TryAddStatusEffect(target, FlashedKey, TimeSpan.FromSeconds(flashDuration / 1000f), true)) + return; if (stunDuration != null) { @@ -146,7 +132,7 @@ public void Flash(EntityUid target, } else { - _stun.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration/1000f), true, + _stun.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration / 1000f), true, slowTo, slowTo); } @@ -155,28 +141,40 @@ public void Flash(EntityUid target, _popup.PopupEntity(Loc.GetString("flash-component-user-blinds-you", ("user", Identity.Entity(user.Value, EntityManager))), target, target); } + + if (melee) + { + var ev = new AfterFlashedEvent(target, user, used); + if (user != null) + RaiseLocalEvent(user.Value, ref ev); + if (used != null) + RaiseLocalEvent(used.Value, ref ev); + } } public void FlashArea(Entity source, EntityUid? user, float range, float duration, float slowTo = 0.8f, bool displayPopup = false, float probability = 1f, SoundSpecifier? sound = null) { var transform = Transform(source); var mapPosition = _transform.GetMapCoordinates(transform); - var flashableQuery = GetEntityQuery(); + var statusEffectsQuery = GetEntityQuery(); + var damagedByFlashingQuery = GetEntityQuery(); foreach (var entity in _entityLookup.GetEntitiesInRange(transform.Coordinates, range)) { if (!_random.Prob(probability)) continue; - if (!flashableQuery.TryGetComponent(entity, out var flashable)) + // Is the entity affected by the flash either through status effects or by taking damage? + if (!statusEffectsQuery.HasComponent(entity) && !damagedByFlashingQuery.HasComponent(entity)) continue; - // Check for unobstructed entities while ignoring the mobs with flashable components. - if (!_interaction.InRangeUnobstructed(entity, mapPosition, range, flashable.CollisionGroup, predicate: (e) => flashableQuery.HasComponent(e) || e == source.Owner)) + // Check for entites in view + // put damagedByFlashingComponent in the predicate because shadow anomalies block vision. + if (!_examine.InRangeUnOccluded(entity, mapPosition, range, predicate: (e) => damagedByFlashingQuery.HasComponent(e))) continue; // They shouldn't have flash removed in between right? - Flash(entity, user, source, duration, slowTo, displayPopup, flashableQuery.GetComponent(entity)); + Flash(entity, user, source, duration, slowTo, displayPopup); } _audio.PlayPvs(sound, source, AudioParams.Default.WithVolume(1f).WithMaxDistance(3f)); @@ -195,13 +193,15 @@ private void OnInventoryFlashAttempt(EntityUid uid, InventoryComponent component private void OnFlashImmunityFlashAttempt(EntityUid uid, FlashImmunityComponent component, FlashAttemptEvent args) { - if(component.Enabled) + if (component.Enabled) args.Cancel(); } private void OnPermanentBlindnessFlashAttempt(EntityUid uid, PermanentBlindnessComponent component, FlashAttemptEvent args) { - args.Cancel(); + // check for total blindness + if (component.Blindness == 0) + args.Cancel(); } private void OnTemporaryBlindnessFlashAttempt(EntityUid uid, TemporaryBlindnessComponent component, FlashAttemptEvent args) @@ -210,6 +210,10 @@ private void OnTemporaryBlindnessFlashAttempt(EntityUid uid, TemporaryBlindnessC } } + /// + /// Called before a flash is used to check if the attempt is cancelled by blindness, items or FlashImmunityComponent. + /// Raised on the target hit by the flash, the user of the flash and the flash used. + /// public sealed class FlashAttemptEvent : CancellableEntityEventArgs { public readonly EntityUid Target; @@ -224,8 +228,8 @@ public FlashAttemptEvent(EntityUid target, EntityUid? user, EntityUid? used) } } /// - /// Called after a flash is used via melee on another person to check for rev conversion. - /// Raised on the user of the flash, the target hit by the flash, and the flash used. + /// Called after a flash is used via melee on another person to check for rev conversion. + /// Raised on the target hit by the flash, the user of the flash and the flash used. /// [ByRefEvent] public readonly struct AfterFlashedEvent @@ -241,6 +245,4 @@ public AfterFlashedEvent(EntityUid target, EntityUid? user, EntityUid? used) Used = used; } } - - } diff --git a/Content.Shared/Flash/Components/FlashComponent.cs b/Content.Shared/Flash/Components/FlashComponent.cs index a9098bc85a97..29f92eb94fc5 100644 --- a/Content.Shared/Flash/Components/FlashComponent.cs +++ b/Content.Shared/Flash/Components/FlashComponent.cs @@ -1,6 +1,6 @@ -using Content.Shared.Flash; using Robust.Shared.Audio; using Robust.Shared.GameStates; +using Robust.Shared.Serialization; namespace Content.Shared.Flash.Components { @@ -43,4 +43,13 @@ public sealed partial class FlashComponent : Component [DataField] public float Probability = 1f; } + + [Serializable, NetSerializable] + public enum FlashVisuals : byte + { + BaseLayer, + LightLayer, + Burnt, + Flashing, + } } diff --git a/Content.Shared/Flash/Components/FlashedComponent.cs b/Content.Shared/Flash/Components/FlashedComponent.cs new file mode 100644 index 000000000000..75bbb12304ac --- /dev/null +++ b/Content.Shared/Flash/Components/FlashedComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Flash.Components; + +/// +/// Exists for use as a status effect. Adds a shader to the client that obstructs vision. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class FlashedComponent : Component { } diff --git a/Content.Shared/Flash/FlashableComponent.cs b/Content.Shared/Flash/FlashableComponent.cs deleted file mode 100644 index c4f8074ceaf6..000000000000 --- a/Content.Shared/Flash/FlashableComponent.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Content.Shared.Physics; -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared.Flash -{ - [RegisterComponent, NetworkedComponent] - public sealed partial class FlashableComponent : Component - { - public float Duration; - public TimeSpan LastFlash; - - [DataField] - public CollisionGroup CollisionGroup = CollisionGroup.Opaque; - - public override bool SendOnlyToOwner => true; - } - - [Serializable, NetSerializable] - public sealed class FlashableComponentState : ComponentState - { - public float Duration { get; } - public TimeSpan Time { get; } - - public FlashableComponentState(float duration, TimeSpan time) - { - Duration = duration; - Time = time; - } - } - - [Serializable, NetSerializable] - public enum FlashVisuals : byte - { - BaseLayer, - LightLayer, - Burnt, - Flashing, - } -} diff --git a/Content.Shared/Flash/SharedFlashSystem.cs b/Content.Shared/Flash/SharedFlashSystem.cs index 16fdbfc2f3e5..f83f02a31058 100644 --- a/Content.Shared/Flash/SharedFlashSystem.cs +++ b/Content.Shared/Flash/SharedFlashSystem.cs @@ -1,19 +1,10 @@ -using Robust.Shared.GameStates; +using Content.Shared.StatusEffect; namespace Content.Shared.Flash { public abstract class SharedFlashSystem : EntitySystem { - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnFlashableGetState); - } - - private static void OnFlashableGetState(EntityUid uid, FlashableComponent component, ref ComponentGetState args) - { - args.State = new FlashableComponentState(component.Duration, component.LastFlash); - } + [ValidatePrototypeId] + public const string FlashedKey = "Flashed"; } } diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 8bf55e48bd3e..baea453cb88e 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -126,6 +126,7 @@ - Stun - KnockedDown - SlowedDown + - Flashed - type: TypingIndicator proto: robot - type: Speech @@ -147,7 +148,6 @@ locked: true - type: ActivatableUIRequiresLock - type: LockedWiresPanel - - type: Flashable - type: Damageable damageContainer: Silicon - type: Destructible diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/argocyte.yml b/Resources/Prototypes/Entities/Mobs/NPCs/argocyte.yml index 39e68b63a789..3b6c4e8ed920 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/argocyte.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/argocyte.yml @@ -48,7 +48,6 @@ sprite: Mobs/Effects/onfire.rsi normalState: Generic_mob_burning - type: Climbing - - type: Flashable - type: NameIdentifier group: GenericNumber diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml index 23eb462db18f..c1cfa4083604 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml @@ -781,7 +781,6 @@ amount: 3 - id: DrinkTequilaBottleFull amount: 1 - - type: Flashable - type: Tag tags: - CannotSuicide @@ -808,7 +807,6 @@ # name: ghost-role-information-tropico-name # description: ghost-role-information-tropico-description # - type: GhostTakeoverAvailable -# - type: Flashable - type: Tag tags: - VimPilot diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml index 12ea40b1e3c5..4936d883e578 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml @@ -25,6 +25,7 @@ - ForcedSleep - TemporaryBlindness - Pacified + - Flashed - type: Buckle - type: StandingState - type: Tag @@ -99,6 +100,7 @@ - TemporaryBlindness - Pacified - StaminaModifier + - Flashed - type: Bloodstream bloodMaxVolume: 150 - type: MobPrice diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index a49f8ff34c35..eeabecef8b2c 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -140,6 +140,7 @@ - TemporaryBlindness - Pacified - StaminaModifier + - Flashed - type: Reflect enabled: false reflectProb: 0 @@ -234,7 +235,6 @@ id: BaseMobSpeciesOrganic abstract: true components: - - type: Flashable - type: Barotrauma damage: types: @@ -249,24 +249,7 @@ Heat: -0.07 groups: Brute: -0.07 - # Organs - - type: StatusEffects - allowed: - - Stun - - KnockedDown - - SlowedDown - - Stutter - - SeeingRainbows - - Electrocution - - ForcedSleep - - TemporaryBlindness - - Drunk - - SlurredSpeech - - RatvarianLanguage - - PressureImmunity - - Muted - - Pacified - - StaminaModifier + - type: Blindable # Other - type: Temperature diff --git a/Resources/Prototypes/Entities/Objects/base_shadow.yml b/Resources/Prototypes/Entities/Objects/base_shadow.yml index 2375855bf90b..1cefef9e5648 100644 --- a/Resources/Prototypes/Entities/Objects/base_shadow.yml +++ b/Resources/Prototypes/Entities/Objects/base_shadow.yml @@ -15,9 +15,6 @@ path: /Audio/Items/hiss.ogg params: variation: 0.08 - - type: Flashable - collisionGroup: - - None - type: DamagedByFlashing flashDamage: types: diff --git a/Resources/Prototypes/status_effects.yml b/Resources/Prototypes/status_effects.yml index a991bf4035f8..27609b1bb542 100644 --- a/Resources/Prototypes/status_effects.yml +++ b/Resources/Prototypes/status_effects.yml @@ -59,3 +59,6 @@ - type: statusEffect id: StaminaModifier + +- type: statusEffect + id: Flashed diff --git a/Resources/Textures/Shaders/flashed_effect.swsl b/Resources/Textures/Shaders/flashed_effect.swsl index ec486ab53130..519a9fdd99a0 100644 --- a/Resources/Textures/Shaders/flashed_effect.swsl +++ b/Resources/Textures/Shaders/flashed_effect.swsl @@ -11,7 +11,7 @@ void fragment() { highp vec4 textureMix = mix(tex1, tex2, 0.5); - // Gradually mixes between the texture mix and a full-white texture, causing the "blinding" effect + // Gradually mixes between the texture mix and a full-black texture, causing the "blinding" effect highp vec4 mixed = mix(vec4(0.0, 0.0, 0.0, 1.0), textureMix, percentComplete); COLOR = vec4(mixed.rgb, remaining); From 827211369cd18c9377a9994ec97fd242a74b87fd Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 2 Jun 2024 04:18:59 +0000 Subject: [PATCH 219/568] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index de9e112c59da..a6e7cbbc7112 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: Krunk - changes: - - message: Zombies and animals can be stripped once again. - type: Fix - - message: Dead or critical mobs can no longer be stripped instantly. - type: Fix - id: 6164 - time: '2024-03-16T03:50:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26166 - author: metalgearsloth changes: - message: Fix store refunds. @@ -3853,3 +3844,12 @@ id: 6663 time: '2024-06-02T04:13:12.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28065 +- author: slarticodefast + changes: + - message: Fixed the flash effect getting darker with each appearence. + type: Fix + - message: Fixed short-sightedness making you immune to flashes. + type: Fix + id: 6664 + time: '2024-06-02T04:17:53.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27369 From 452b53988c336ba6c45c16e9082f5bc91108bce1 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 2 Jun 2024 16:26:42 +1200 Subject: [PATCH 220/568] Add debug asserts to ensure that network groups are up to date (#28495) --- .../Power/Pow3r/BatteryRampPegSolver.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs b/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs index 0afd86679b17..12118968b70f 100644 --- a/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs +++ b/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using Robust.Shared.Utility; using System.Linq; using Robust.Shared.Threading; @@ -37,6 +38,7 @@ public void Tick(float frameTime, PowerState state, IParallelManager parallel) DebugTools.Assert(state.GroupedNets.Select(x => x.Count).Sum() == state.Networks.Count); _networkJob.State = state; _networkJob.FrameTime = frameTime; + ValidateNetworkGroups(state, state.GroupedNets); // Each network height layer can be run in parallel without issues. foreach (var group in state.GroupedNets) @@ -321,9 +323,69 @@ private List> GroupByNetworkDepth(PowerState state) RecursivelyEstimateNetworkDepth(state, network, groupedNetworks); } + ValidateNetworkGroups(state, groupedNetworks); return groupedNetworks; } + /// + /// Validate that network grouping is up to date. I.e., that it is safe to solve each networking in a given + /// group in parallel. This assumes that batteries are the only device that connects to multiple networks, and + /// is thus the only obstacle to solving everything in parallel. + /// + [Conditional("DEBUG")] + private void ValidateNetworkGroups(PowerState state, List> groupedNetworks) + { + HashSet nets = new(); + HashSet netIds = new(); + foreach (var layer in groupedNetworks) + { + nets.Clear(); + netIds.Clear(); + + foreach (var net in layer) + { + foreach (var batteryId in net.BatteryLoads) + { + var battery = state.Batteries[batteryId]; + if (battery.LinkedNetworkDischarging == default) + continue; + + var subNet = state.Networks[battery.LinkedNetworkDischarging]; + if (battery.LinkedNetworkDischarging == net.Id) + { + DebugTools.Assert(subNet == net); + continue; + } + + DebugTools.Assert(!nets.Contains(subNet)); + DebugTools.Assert(!netIds.Contains(subNet.Id)); + DebugTools.Assert(subNet.Height < net.Height); + } + + foreach (var batteryId in net.BatterySupplies) + { + var battery = state.Batteries[batteryId]; + if (battery.LinkedNetworkCharging == default) + continue; + + var parentNet = state.Networks[battery.LinkedNetworkCharging]; + if (battery.LinkedNetworkCharging == net.Id) + { + DebugTools.Assert(parentNet == net); + continue; + } + + DebugTools.Assert(!nets.Contains(parentNet)); + DebugTools.Assert(!netIds.Contains(parentNet.Id)); + DebugTools.Assert(parentNet.Height > net.Height); + } + + DebugTools.Assert(nets.Add(net)); + DebugTools.Assert(netIds.Add(net.Id)); + } + } + } + private static void RecursivelyEstimateNetworkDepth(PowerState state, Network network, List> groupedNetworks) { network.Height = -2; From 79e841056ba2b4eb7f32c6c46c3c0c7d547c0f53 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 2 Jun 2024 16:27:48 +1200 Subject: [PATCH 221/568] Update engine to v224.0.0 (#28502) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index c89c529ba4fa..ff4548f108a5 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit c89c529ba4fa7516e2265f3c47549da35ab6c3d8 +Subproject commit ff4548f108a5b11e1a81cb7c01f7462400e4af2b From ba04f94e480f9aed5f72eec0953396945f5d5d88 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Sat, 1 Jun 2024 23:21:07 -0700 Subject: [PATCH 222/568] Emergency Fix for Whitelist logic (#28510) fix issue Co-authored-by: plykiya --- Content.Shared/Materials/SharedMaterialStorageSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Materials/SharedMaterialStorageSystem.cs b/Content.Shared/Materials/SharedMaterialStorageSystem.cs index af815bdb2b7f..a27e0fb9cf0b 100644 --- a/Content.Shared/Materials/SharedMaterialStorageSystem.cs +++ b/Content.Shared/Materials/SharedMaterialStorageSystem.cs @@ -123,7 +123,7 @@ public bool CanChangeMaterialAmount(EntityUid uid, string materialId, int volume if (!CanTakeVolume(uid, volume, component)) return false; - if (component.MaterialWhiteList == null ? false : component.MaterialWhiteList.Contains(materialId)) + if (component.MaterialWhiteList == null ? false : !component.MaterialWhiteList.Contains(materialId)) return false; var amount = component.Storage.GetValueOrDefault(materialId); From 16be126ca1e65ebc94a0d688d7d5eca749fc272d Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 2 Jun 2024 20:11:48 +1200 Subject: [PATCH 223/568] Remove `noSpawn: true` from action entity prototypes (#28508) --- Resources/Prototypes/Actions/borgs.yml | 1 - Resources/Prototypes/Actions/crit.yml | 3 --- Resources/Prototypes/Actions/diona.yml | 2 -- Resources/Prototypes/Actions/internals.yml | 1 - Resources/Prototypes/Actions/mech.yml | 3 --- Resources/Prototypes/Actions/ninja.yml | 6 ----- Resources/Prototypes/Actions/polymorph.yml | 4 --- Resources/Prototypes/Actions/revenant.yml | 5 ---- Resources/Prototypes/Actions/speech.yml | 1 - Resources/Prototypes/Actions/spider.yml | 2 -- Resources/Prototypes/Actions/types.yml | 27 ------------------- .../Clothing/Masks/base_clothingmask.yml | 3 +-- .../Entities/Clothing/Neck/misc.yml | 1 - .../Entities/Clothing/Shoes/magboots.yml | 5 ---- .../Entities/Clothing/Shoes/misc.yml | 1 - .../Entities/Mobs/NPCs/regalrat.yml | 6 ----- .../Entities/Mobs/Player/admin_ghost.yml | 6 ----- .../Entities/Mobs/Player/dragon.yml | 3 --- .../Entities/Mobs/Player/guardian.yml | 1 - .../Entities/Mobs/Player/observer.yml | 5 ---- .../Objects/Devices/chameleon_projector.yml | 2 -- .../Prototypes/Entities/Objects/Fun/pai.yml | 2 -- .../Objects/Specific/Chapel/bibles.yml | 1 - .../Specific/Robotics/borg_modules.yml | 1 - .../Entities/Objects/Tools/jetpacks.yml | 1 - Resources/Prototypes/Magic/event_spells.yml | 1 - .../Prototypes/Magic/forcewall_spells.yml | 1 - Resources/Prototypes/Magic/knock_spell.yml | 1 - .../Prototypes/Magic/projectile_spells.yml | 3 --- Resources/Prototypes/Magic/rune_spells.yml | 4 --- Resources/Prototypes/Magic/smite_spells.yml | 1 - Resources/Prototypes/Magic/spawn_spells.yml | 1 - Resources/Prototypes/Magic/staves.yml | 1 - .../Prototypes/Magic/teleport_spells.yml | 1 - Resources/Prototypes/Magic/utility_spells.yml | 1 - .../Prototypes/Roles/Jobs/Civilian/mime.yml | 1 - 36 files changed, 1 insertion(+), 108 deletions(-) diff --git a/Resources/Prototypes/Actions/borgs.yml b/Resources/Prototypes/Actions/borgs.yml index 6d35c69cf6ac..a0168ef00fc4 100644 --- a/Resources/Prototypes/Actions/borgs.yml +++ b/Resources/Prototypes/Actions/borgs.yml @@ -2,7 +2,6 @@ id: ActionViewLaws name: View Laws description: View the laws that you must follow. - noSpawn: true components: - type: InstantAction itemIconStyle: NoItem diff --git a/Resources/Prototypes/Actions/crit.yml b/Resources/Prototypes/Actions/crit.yml index 705ee6ee6b39..c5712844bf47 100644 --- a/Resources/Prototypes/Actions/crit.yml +++ b/Resources/Prototypes/Actions/crit.yml @@ -3,7 +3,6 @@ id: ActionCritSuccumb name: Succumb description: Accept your fate. - noSpawn: true components: - type: InstantAction itemIconStyle: NoItem @@ -18,7 +17,6 @@ id: ActionCritFakeDeath name: Fake Death description: Pretend to take your final breath while staying alive. - noSpawn: true components: - type: InstantAction itemIconStyle: NoItem @@ -34,7 +32,6 @@ id: ActionCritLastWords name: Say Last Words description: Whisper your last words to anyone nearby, and then succumb to your fate. You only have 30 characters to work with. - noSpawn: true components: - type: InstantAction itemIconStyle: NoItem diff --git a/Resources/Prototypes/Actions/diona.yml b/Resources/Prototypes/Actions/diona.yml index 11db30386a52..9f80f18178e7 100644 --- a/Resources/Prototypes/Actions/diona.yml +++ b/Resources/Prototypes/Actions/diona.yml @@ -2,7 +2,6 @@ id: DionaGibAction name: Gib Yourself! description: Split apart into 3 nymphs. - noSpawn: true components: - type: InstantAction icon: @@ -16,7 +15,6 @@ id: DionaReformAction name: Reform description: Reform back into a whole Diona. - noSpawn: true components: - type: InstantAction icon: diff --git a/Resources/Prototypes/Actions/internals.yml b/Resources/Prototypes/Actions/internals.yml index dd83a45332fb..5982c3daa2a2 100644 --- a/Resources/Prototypes/Actions/internals.yml +++ b/Resources/Prototypes/Actions/internals.yml @@ -2,7 +2,6 @@ id: ActionToggleInternals name: Toggle Internals description: Breathe from the equipped gas tank. Also requires equipped breath mask. - noSpawn: true components: - type: InstantAction icon: diff --git a/Resources/Prototypes/Actions/mech.yml b/Resources/Prototypes/Actions/mech.yml index 2005133a70b7..48092f9c5adf 100644 --- a/Resources/Prototypes/Actions/mech.yml +++ b/Resources/Prototypes/Actions/mech.yml @@ -2,7 +2,6 @@ id: ActionMechCycleEquipment name: Cycle description: Cycles currently selected equipment - noSpawn: true components: - type: InstantAction itemIconStyle: NoItem @@ -16,7 +15,6 @@ id: ActionMechOpenUI name: Control Panel description: Opens the control panel for the mech - noSpawn: true components: - type: InstantAction itemIconStyle: NoItem @@ -30,7 +28,6 @@ id: ActionMechEject name: Eject description: Ejects the pilot from the mech - noSpawn: true components: - type: InstantAction itemIconStyle: NoItem diff --git a/Resources/Prototypes/Actions/ninja.yml b/Resources/Prototypes/Actions/ninja.yml index 5fe6f23b2767..adaf563692d9 100644 --- a/Resources/Prototypes/Actions/ninja.yml +++ b/Resources/Prototypes/Actions/ninja.yml @@ -3,7 +3,6 @@ id: ActionToggleNinjaGloves name: Toggle ninja gloves description: Toggles all glove actions on left click. Includes your doorjack, draining power, stunning enemies, downloading research and calling in a threat. - noSpawn: true components: - type: InstantAction priority: -13 @@ -14,7 +13,6 @@ id: ActionCreateThrowingStar name: Create throwing star description: Channels suit power into creating a throwing star that deals extra stamina damage. - noSpawn: true components: - type: InstantAction useDelay: 0.5 @@ -29,7 +27,6 @@ id: ActionRecallKatana name: Recall katana description: Teleports the Energy Katana linked to this suit to its wearer, cost based on distance. - noSpawn: true components: - type: InstantAction useDelay: 1 @@ -44,7 +41,6 @@ id: ActionNinjaEmp name: EM Burst description: Disable any nearby technology with an electro-magnetic pulse. - noSpawn: true components: - type: InstantAction icon: @@ -58,7 +54,6 @@ id: ActionTogglePhaseCloak name: Phase cloak description: Toggles your suit's phase cloak. Beware that if you are hit, all abilities are disabled for 5 seconds, including your cloak! - noSpawn: true components: - type: InstantAction # have to plan (un)cloaking ahead of time @@ -71,7 +66,6 @@ id: ActionEnergyKatanaDash name: Katana dash description: Teleport to anywhere you can see, if your Energy Katana is in your hand. - noSpawn: true components: - type: WorldTargetAction icon: diff --git a/Resources/Prototypes/Actions/polymorph.yml b/Resources/Prototypes/Actions/polymorph.yml index 445dc8d9f54f..81feba4eaccc 100644 --- a/Resources/Prototypes/Actions/polymorph.yml +++ b/Resources/Prototypes/Actions/polymorph.yml @@ -2,14 +2,12 @@ id: ActionRevertPolymorph name: Revert description: Revert back into your original form. - noSpawn: true components: - type: InstantAction event: !type:RevertPolymorphActionEvent - type: entity id: ActionPolymorph - noSpawn: true components: - type: InstantAction event: !type:PolymorphActionEvent @@ -19,7 +17,6 @@ id: ActionPolymorphWizardSpider name: Spider Polymorph description: Polymorphs you into a Spider. - noSpawn: true components: - type: InstantAction useDelay: 60 @@ -34,7 +31,6 @@ id: ActionPolymorphWizardRod name: Rod Form description: CLANG! - noSpawn: true components: - type: InstantAction useDelay: 60 diff --git a/Resources/Prototypes/Actions/revenant.yml b/Resources/Prototypes/Actions/revenant.yml index da7b4ba56f21..dca491a99b53 100644 --- a/Resources/Prototypes/Actions/revenant.yml +++ b/Resources/Prototypes/Actions/revenant.yml @@ -2,7 +2,6 @@ id: ActionRevenantShop name: Shop description: Opens the ability shop. - noSpawn: true components: - type: InstantAction icon: Interface/Actions/shop.png @@ -12,7 +11,6 @@ id: ActionRevenantDefile name: Defile description: Costs 30 Essence. - noSpawn: true components: - type: InstantAction icon: Interface/Actions/defile.png @@ -23,7 +21,6 @@ id: ActionRevenantOverloadLights name: Overload Lights description: Costs 40 Essence. - noSpawn: true components: - type: InstantAction icon: Interface/Actions/overloadlight.png @@ -34,7 +31,6 @@ # id: ActionRevenantBlight # name: Blight # description: Costs 50 Essence. -# noSpawn: true # components: # - type: InstantAction # icon: Interface/Actions/blight.png @@ -45,7 +41,6 @@ id: ActionRevenantMalfunction name: Malfunction description: Costs 60 Essence. - noSpawn: true components: - type: InstantAction icon: Interface/Actions/malfunction.png diff --git a/Resources/Prototypes/Actions/speech.yml b/Resources/Prototypes/Actions/speech.yml index 39db04b1b31f..c71ec74880c5 100644 --- a/Resources/Prototypes/Actions/speech.yml +++ b/Resources/Prototypes/Actions/speech.yml @@ -2,7 +2,6 @@ id: ActionConfigureMeleeSpeech name: Set Battlecry description: Set a custom battlecry for when you attack! - noSpawn: true components: - type: InstantAction itemIconStyle: BigItem diff --git a/Resources/Prototypes/Actions/spider.yml b/Resources/Prototypes/Actions/spider.yml index 14b9fb6ccbbb..fe37085ecabc 100644 --- a/Resources/Prototypes/Actions/spider.yml +++ b/Resources/Prototypes/Actions/spider.yml @@ -2,7 +2,6 @@ id: ActionSpiderWeb name: Spider Web description: Spawns a web that slows your prey down. - noSpawn: true components: - type: InstantAction icon: Interface/Actions/web.png @@ -13,7 +12,6 @@ id: ActionSericulture name: Weave silk description: Weave a bit of silk for use in arts and crafts. - noSpawn: true components: - type: InstantAction icon: Interface/Actions/web.png diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index fb156a732e6d..4b3d75bf920e 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -13,7 +13,6 @@ id: ActionScream name: Scream description: AAAAAAAAAAAAAAAAAAAAAAAAA - noSpawn: true components: - type: InstantAction useDelay: 10 @@ -25,7 +24,6 @@ id: ActionTurnUndead name: Turn Undead description: Succumb to your infection and become a zombie. - noSpawn: true components: - type: InstantAction checkCanInteract: false @@ -37,7 +35,6 @@ id: ActionToggleLight name: Toggle Light description: Turn the light on and off. - noSpawn: true components: - type: InstantAction icon: { sprite: Objects/Tools/flashlight.rsi, state: flashlight } @@ -48,7 +45,6 @@ id: ActionOpenStorageImplant name: Open Storage Implant description: Opens the storage implant embedded under your skin - noSpawn: true components: - type: InstantAction itemIconStyle: BigAction @@ -63,7 +59,6 @@ id: ActionActivateMicroBomb name: Activate Microbomb description: Activates your internal microbomb, completely destroying you and your equipment - noSpawn: true components: - type: InstantAction checkCanInteract: false @@ -80,7 +75,6 @@ id: ActionActivateDeathAcidifier name: Activate Death-Acidifier description: Activates your death-acidifier, completely melting you and your equipment - noSpawn: true components: - type: InstantAction checkCanInteract: false @@ -96,7 +90,6 @@ id: ActionActivateFreedomImplant name: Break Free description: Activating your freedom implant will free you from any hand restraints - noSpawn: true components: - type: InstantAction charges: 3 @@ -112,7 +105,6 @@ id: ActionOpenUplinkImplant name: Open Uplink description: Opens the syndicate uplink embedded under your skin - noSpawn: true components: - type: InstantAction itemIconStyle: BigAction @@ -126,7 +118,6 @@ id: ActionActivateEmpImplant name: Activate EMP description: Triggers a small EMP pulse around you - noSpawn: true components: - type: InstantAction checkCanInteract: false @@ -143,7 +134,6 @@ id: ActionActivateScramImplant name: SCRAM! description: Randomly teleports you within a large distance. - noSpawn: true components: - type: InstantAction checkCanInteract: false @@ -160,7 +150,6 @@ id: ActionActivateDnaScramblerImplant name: Scramble DNA description: Randomly changes your name and appearance. - noSpawn: true components: - type: InstantAction charges: 1 @@ -175,7 +164,6 @@ id: ActionMorphGeras name: Morph into Geras description: Morphs you into a Geras - a miniature version of you which allows you to move fast, at the cost of your inventory. - noSpawn: true components: - type: InstantAction itemIconStyle: BigAction @@ -190,7 +178,6 @@ id: ActionToggleSuitPiece name: Toggle Suit Piece description: Remember to equip the important pieces of your suit before going into action. - noSpawn: true components: - type: InstantAction itemIconStyle: BigItem @@ -201,7 +188,6 @@ id: ActionCombatModeToggle name: "[color=red]Combat Mode[/color]" description: Enter combat mode - noSpawn: true components: - type: InstantAction checkCanInteract: false @@ -216,7 +202,6 @@ parent: ActionCombatModeToggle name: "[color=red]Combat Mode[/color]" description: Enter combat mode - noSpawn: true components: - type: InstantAction enabled: false @@ -227,7 +212,6 @@ id: ActionChangeVoiceMask name: Set name description: Change the name others hear to something else. - noSpawn: true components: - type: InstantAction icon: { sprite: Interface/Actions/voice-mask.rsi, state: icon } @@ -237,7 +221,6 @@ id: ActionVendingThrow name: Dispense Item description: Randomly dispense an item from your stock. - noSpawn: true components: - type: InstantAction useDelay: 30 @@ -247,7 +230,6 @@ id: ActionArtifactActivate name: Activate Artifact description: Immediately activates your current artifact node. - noSpawn: true components: - type: InstantAction icon: @@ -260,7 +242,6 @@ id: ActionToggleBlock name: Block description: Raise or lower your shield. - noSpawn: true components: - type: InstantAction icon: { sprite: Objects/Weapons/Melee/shields.rsi, state: teleriot-icon } @@ -271,7 +252,6 @@ id: ActionClearNetworkLinkOverlays name: Clear network link overlays description: Clear network link overlays. - noSpawn: true components: - type: InstantAction clientExclusive: true @@ -285,7 +265,6 @@ id: ActionAnimalLayEgg name: Lay egg description: Uses hunger to lay an egg. - noSpawn: true components: - type: InstantAction icon: { sprite: Objects/Consumable/Food/egg.rsi, state: icon } @@ -296,7 +275,6 @@ id: ActionSleep name: Sleep description: Go to sleep. - noSpawn: true components: - type: InstantAction checkCanInteract: false @@ -308,7 +286,6 @@ id: ActionWake name: Wake up description: Stop sleeping. - noSpawn: true components: - type: InstantAction icon: { sprite: Clothing/Head/Hats/pyjamasyndicatered.rsi, state: icon } @@ -320,7 +297,6 @@ id: ActionActivateHonkImplant name: Honk description: Activates your honking implant, which will produce the signature sound of the clown. - noSpawn: true components: - type: InstantAction icon: { sprite: Objects/Fun/bikehorn.rsi, state: icon } @@ -331,7 +307,6 @@ id: ActionFireStarter name: Ignite description: Ignites enemies in a radius around you. - noSpawn: true components: - type: InstantAction priority: -1 @@ -343,7 +318,6 @@ id: ActionToggleEyes name: Open/Close eyes description: Close your eyes to protect your peepers, or open your eyes to enjoy the pretty lights. - noSpawn: true components: - type: InstantAction icon: Interface/Actions/eyeopen.png @@ -357,7 +331,6 @@ id: ActionToggleWagging name: action-name-toggle-wagging description: action-description-toggle-wagging - noSpawn: true components: - type: InstantAction icon: { sprite: Mobs/Customization/reptilian_parts.rsi, state: tail_smooth_behind } diff --git a/Resources/Prototypes/Entities/Clothing/Masks/base_clothingmask.yml b/Resources/Prototypes/Entities/Clothing/Masks/base_clothingmask.yml index 3531a26a6c5b..00dcd8263f44 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/base_clothingmask.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/base_clothingmask.yml @@ -21,7 +21,6 @@ id: ActionToggleMask name: Toggle Mask description: Handy, but prevents insertion of pie into your pie hole. - noSpawn: true components: - type: InstantAction icon: { sprite: Clothing/Mask/gas.rsi, state: icon } @@ -49,4 +48,4 @@ Quantity: 10 - type: Tag tags: - - ClothMade \ No newline at end of file + - ClothMade diff --git a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml index 51325c0bbbd0..8dfc709bc4f1 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml @@ -70,7 +70,6 @@ - type: entity id: ActionStethoscope name: Listen with stethoscope - noSpawn: true components: - type: EntityTargetAction icon: diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index 485d530ea5a7..e80ed74305c6 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -132,7 +132,6 @@ id: ActionBaseToggleMagboots name: Toggle Magboots description: Toggles the magboots on and off. - noSpawn: true components: - type: InstantAction itemIconStyle: NoItem @@ -141,7 +140,6 @@ - type: entity id: ActionToggleMagboots parent: ActionBaseToggleMagboots - noSpawn: true components: - type: InstantAction icon: { sprite: Clothing/Shoes/Boots/magboots.rsi, state: icon } @@ -150,7 +148,6 @@ - type: entity id: ActionToggleMagbootsAdvanced parent: ActionBaseToggleMagboots - noSpawn: true components: - type: InstantAction icon: { sprite: Clothing/Shoes/Boots/magboots-advanced.rsi, state: icon } @@ -159,7 +156,6 @@ - type: entity id: ActionToggleMagbootsSci parent: ActionBaseToggleMagboots - noSpawn: true components: - type: InstantAction icon: { sprite: Clothing/Shoes/Boots/magboots-science.rsi, state: icon } @@ -168,7 +164,6 @@ - type: entity id: ActionToggleMagbootsSyndie parent: ActionBaseToggleMagboots - noSpawn: true components: - type: InstantAction icon: { sprite: Clothing/Shoes/Boots/magboots-syndicate.rsi, state: icon } diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml index d5a695c7c0ba..cbea9d319d91 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml @@ -128,7 +128,6 @@ id: ActionToggleSpeedBoots name: Toggle Speed Boots description: Toggles the speed boots on and off. - noSpawn: true components: - type: InstantAction itemIconStyle: NoItem diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml index aa12eac1c2dc..38debe2a9a79 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml @@ -301,7 +301,6 @@ id: ActionRatKingRaiseArmy name: Raise Army description: Spend some hunger to summon an allied rat to help defend you. - noSpawn: true components: - type: InstantAction useDelay: 4 @@ -314,7 +313,6 @@ id: ActionRatKingDomain name: Rat King's Domain description: Spend some hunger to release a cloud of ammonia into the air. - noSpawn: true components: - type: InstantAction useDelay: 6 @@ -327,7 +325,6 @@ id: ActionRatKingOrderStay name: Stay description: Command your army to stand in place. - noSpawn: true components: - type: InstantAction useDelay: 1 @@ -346,7 +343,6 @@ id: ActionRatKingOrderFollow name: Follow description: Command your army to follow you around. - noSpawn: true components: - type: InstantAction useDelay: 1 @@ -365,7 +361,6 @@ id: ActionRatKingOrderCheeseEm name: Cheese 'Em description: Command your army to attack whoever you point at. - noSpawn: true components: - type: InstantAction useDelay: 1 @@ -384,7 +379,6 @@ id: ActionRatKingOrderLoose name: Loose description: Command your army to act at their own will. - noSpawn: true components: - type: InstantAction useDelay: 1 diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index 0fa85fc84f21..4a83593bc342 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -97,7 +97,6 @@ id: ActionAGhostShowSolar name: Solar Control Interface description: View a solar control interface. - noSpawn: true components: - type: InstantAction icon: { sprite: Structures/Machines/parts.rsi, state: box_0 } @@ -110,7 +109,6 @@ id: ActionAGhostShowCommunications name: Communications Interface description: View a communications interface. - noSpawn: true components: - type: InstantAction icon: { sprite: Structures/Machines/parts.rsi, state: box_0 } @@ -123,7 +121,6 @@ id: ActionAGhostShowRadar name: Mass Scanner Interface description: View a mass scanner interface. - noSpawn: true components: - type: InstantAction icon: { sprite: Structures/Machines/parts.rsi, state: box_0 } @@ -136,7 +133,6 @@ id: ActionAGhostShowCargo name: Cargo Ordering Interface description: View a cargo ordering interface. - noSpawn: true components: - type: InstantAction icon: { sprite: Structures/Machines/parts.rsi, state: box_0 } @@ -149,7 +145,6 @@ id: ActionAGhostShowCrewMonitoring name: Crew Monitoring Interface description: View a crew monitoring interface. - noSpawn: true components: - type: InstantAction icon: { sprite: Structures/Machines/parts.rsi, state: box_0 } @@ -162,7 +157,6 @@ id: ActionAGhostShowStationRecords name: Station Records Interface description: View a station records Interface - noSpawn: true components: - type: InstantAction icon: { sprite: Structures/Machines/parts.rsi, state: box_0 } diff --git a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index 258488af9b46..869fb880841c 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml @@ -203,7 +203,6 @@ id: ActionSpawnRift name: Summon Carp Rift description: Summons a carp rift that will periodically spawns carps. - noSpawn: true components: - type: InstantAction icon: @@ -217,7 +216,6 @@ id: ActionDevour name: "[color=red]Devour[/color]" description: Attempt to break a structure with your jaws or swallow a creature. - noSpawn: true components: - type: EntityTargetAction icon: { sprite : Interface/Actions/devour.rsi, state: icon } @@ -226,7 +224,6 @@ priority: 1 - type: entity - noSpawn: true id: ActionDragonsBreath name: "[color=orange]Dragon's Breath[/color]" description: Spew out flames at anyone foolish enough to attack you! diff --git a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml index 80ee2c55b88a..9ccfdf4e507c 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml @@ -259,7 +259,6 @@ id: ActionToggleGuardian name: Toggle Guardian description: Either manifests the guardian or recalls it back into your body - noSpawn: true components: - type: InstantAction icon: Interface/Actions/manifest.png diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml index 114c3fa74798..85be42cc0255 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/observer.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/observer.yml @@ -53,7 +53,6 @@ id: ActionGhostBoo name: Boo! description: Scare your crew members because of boredom! - noSpawn: true components: - type: InstantAction icon: Interface/Actions/scream.png @@ -65,7 +64,6 @@ id: ActionToggleLighting name: Toggle All Lighting description: Toggle all light rendering to better observe dark areas. - noSpawn: true components: - type: InstantAction icon: Interface/VerbIcons/light.svg.192dpi.png @@ -77,7 +75,6 @@ id: ActionToggleFov name: Toggle FoV description: Toggles field-of-view in order to see what players see. - noSpawn: true components: - type: InstantAction icon: Interface/VerbIcons/vv.svg.192dpi.png @@ -89,7 +86,6 @@ id: ActionToggleGhosts name: Toggle Ghosts description: Toggle the visibility of other ghosts. - noSpawn: true components: - type: InstantAction icon: { sprite: Mobs/Ghosts/ghost_human.rsi, state: icon } @@ -101,7 +97,6 @@ id: ActionToggleGhostHearing name: Toggle Ghost Hearing description: Toggle between hearing all messages and hearing only radio & nearby messages. - noSpawn: true components: - type: InstantAction checkCanInteract: false diff --git a/Resources/Prototypes/Entities/Objects/Devices/chameleon_projector.yml b/Resources/Prototypes/Entities/Objects/Devices/chameleon_projector.yml index e02128102102..20e138f81a0c 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/chameleon_projector.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/chameleon_projector.yml @@ -49,7 +49,6 @@ # actions - type: entity - noSpawn: true id: ActionDisguiseNoRot name: Toggle Rotation description: Use this to prevent your disguise from rotating, making it easier to hide in some scenarios. @@ -59,7 +58,6 @@ event: !type:DisguiseToggleNoRotEvent - type: entity - noSpawn: true id: ActionDisguiseAnchor name: Toggle Anchored description: For many objects you will want to be anchored to not be completely obvious. diff --git a/Resources/Prototypes/Entities/Objects/Fun/pai.yml b/Resources/Prototypes/Entities/Objects/Fun/pai.yml index 1b9c5303c6b2..02bbce2843b4 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/pai.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/pai.yml @@ -136,7 +136,6 @@ id: ActionPAIPlayMidi name: Play MIDI description: Open your portable MIDI interface to soothe your owner. - noSpawn: true components: - type: InstantAction checkCanInteract: false @@ -149,7 +148,6 @@ id: ActionPAIOpenMap name: Open Map description: Open your map interface and guide your owner. - noSpawn: true components: - type: InstantAction checkCanInteract: false diff --git a/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml b/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml index f6b2b0a70c6e..58f7e0155082 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml @@ -80,7 +80,6 @@ id: ActionBibleSummon name: Summon familiar description: Summon a familiar that will aid you and gain humanlike intelligence once inhabited by a soul. - noSpawn: true components: - type: InstantAction icon: { sprite: Clothing/Head/Hats/witch.rsi, state: icon } diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index 07206711d966..74e91a768cdd 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -32,7 +32,6 @@ id: ActionBorgSwapModule name: Swap Module description: Select this module, enabling you to use the tools it provides. - noSpawn: true components: - type: InstantAction itemIconStyle: BigItem diff --git a/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml b/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml index 04f8b9f9f6d1..6fee48daf147 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml @@ -62,7 +62,6 @@ id: ActionToggleJetpack name: Toggle jetpack description: Toggles the jetpack, giving you movement outside the station. - noSpawn: true components: - type: InstantAction icon: diff --git a/Resources/Prototypes/Magic/event_spells.yml b/Resources/Prototypes/Magic/event_spells.yml index e59e1b2db881..01006b4ffe09 100644 --- a/Resources/Prototypes/Magic/event_spells.yml +++ b/Resources/Prototypes/Magic/event_spells.yml @@ -2,7 +2,6 @@ id: ActionSummonGhosts name: Summon Ghosts description: Makes all current ghosts permanently invisible - noSpawn: true components: - type: InstantAction useDelay: 120 diff --git a/Resources/Prototypes/Magic/forcewall_spells.yml b/Resources/Prototypes/Magic/forcewall_spells.yml index d3d8fef76086..f1865cf72291 100644 --- a/Resources/Prototypes/Magic/forcewall_spells.yml +++ b/Resources/Prototypes/Magic/forcewall_spells.yml @@ -2,7 +2,6 @@ id: ActionForceWall name: Forcewall description: Creates a magical barrier. - noSpawn: true components: - type: InstantAction useDelay: 10 diff --git a/Resources/Prototypes/Magic/knock_spell.yml b/Resources/Prototypes/Magic/knock_spell.yml index e2c3dcfd4c7e..5ba456d3be99 100644 --- a/Resources/Prototypes/Magic/knock_spell.yml +++ b/Resources/Prototypes/Magic/knock_spell.yml @@ -2,7 +2,6 @@ id: ActionKnock name: Knock description: This spell opens nearby doors. - noSpawn: true components: - type: InstantAction useDelay: 10 diff --git a/Resources/Prototypes/Magic/projectile_spells.yml b/Resources/Prototypes/Magic/projectile_spells.yml index b8db7557bba6..71bbc096c555 100644 --- a/Resources/Prototypes/Magic/projectile_spells.yml +++ b/Resources/Prototypes/Magic/projectile_spells.yml @@ -2,7 +2,6 @@ id: ActionFireball name: Fireball description: Fires an explosive fireball towards the clicked location. - noSpawn: true components: - type: Magic - type: WorldTargetAction @@ -29,7 +28,6 @@ parent: ActionFireball name: Fireball II description: Fires a fireball, but faster! - noSpawn: true components: - type: WorldTargetAction useDelay: 10 @@ -52,7 +50,6 @@ parent: ActionFireball name: Fireball III description: The fastest fireball in the west! - noSpawn: true components: - type: WorldTargetAction useDelay: 8 diff --git a/Resources/Prototypes/Magic/rune_spells.yml b/Resources/Prototypes/Magic/rune_spells.yml index 42022f578503..7ba357e7c134 100644 --- a/Resources/Prototypes/Magic/rune_spells.yml +++ b/Resources/Prototypes/Magic/rune_spells.yml @@ -2,7 +2,6 @@ id: ActionFlashRune name: Flash Rune description: Summons a rune that flashes if used. - noSpawn: true components: - type: InstantAction useDelay: 10 @@ -17,7 +16,6 @@ id: ActionExplosionRune name: Explosion Rune description: Summons a rune that explodes if used. - noSpawn: true components: - type: InstantAction useDelay: 20 @@ -32,7 +30,6 @@ id: ActionIgniteRune name: Ignite Rune description: Summons a rune that ignites if used. - noSpawn: true components: - type: InstantAction useDelay: 15 @@ -47,7 +44,6 @@ id: ActionStunRune name: Stun Rune description: Summons a rune that stuns if used. - noSpawn: true components: - type: InstantAction useDelay: 10 diff --git a/Resources/Prototypes/Magic/smite_spells.yml b/Resources/Prototypes/Magic/smite_spells.yml index e629e5650588..10f5bdd53893 100644 --- a/Resources/Prototypes/Magic/smite_spells.yml +++ b/Resources/Prototypes/Magic/smite_spells.yml @@ -2,7 +2,6 @@ id: ActionSmite name: Smite description: Instantly gibs a target. - noSpawn: true components: - type: EntityTargetAction useDelay: 60 diff --git a/Resources/Prototypes/Magic/spawn_spells.yml b/Resources/Prototypes/Magic/spawn_spells.yml index 3f8148b83cba..76674d5bfa88 100644 --- a/Resources/Prototypes/Magic/spawn_spells.yml +++ b/Resources/Prototypes/Magic/spawn_spells.yml @@ -2,7 +2,6 @@ id: ActionSpawnMagicarpSpell name: Summon Magicarp description: This spell summons three Magi-Carp to your aid! May or may not turn on user. - noSpawn: true components: - type: WorldTargetAction useDelay: 10 diff --git a/Resources/Prototypes/Magic/staves.yml b/Resources/Prototypes/Magic/staves.yml index ef94a3910fd7..ccabb516fd4c 100644 --- a/Resources/Prototypes/Magic/staves.yml +++ b/Resources/Prototypes/Magic/staves.yml @@ -34,7 +34,6 @@ - type: entity id: ActionRgbLight - noSpawn: true components: - type: EntityTargetAction whitelist: { components: [ PointLight ] } diff --git a/Resources/Prototypes/Magic/teleport_spells.yml b/Resources/Prototypes/Magic/teleport_spells.yml index cc89cf8ee0d8..6f1ed9a6e43a 100644 --- a/Resources/Prototypes/Magic/teleport_spells.yml +++ b/Resources/Prototypes/Magic/teleport_spells.yml @@ -2,7 +2,6 @@ id: ActionBlink name: Blink description: Teleport to the clicked location. - noSpawn: true components: - type: WorldTargetAction useDelay: 10 diff --git a/Resources/Prototypes/Magic/utility_spells.yml b/Resources/Prototypes/Magic/utility_spells.yml index dccdda378981..90bcdc7487bd 100644 --- a/Resources/Prototypes/Magic/utility_spells.yml +++ b/Resources/Prototypes/Magic/utility_spells.yml @@ -2,7 +2,6 @@ id: ActionChargeSpell name: Charge description: Adds a charge back to your wand - noSpawn: true components: - type: InstantAction useDelay: 30 diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml b/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml index 1eac292eee3a..27a164210bc3 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml @@ -33,7 +33,6 @@ id: ActionMimeInvisibleWall name: Create Invisible Wall description: Create an invisible wall in front of you, if placeable there. - noSpawn: true components: - type: InstantAction priority: -1 From 77a3d6c6f12e10031f6a6e5723705c515389279b Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Mon, 3 Jun 2024 02:59:22 +1200 Subject: [PATCH 224/568] Update engine to v224.0.1 (#28520) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index ff4548f108a5..f64821875655 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit ff4548f108a5b11e1a81cb7c01f7462400e4af2b +Subproject commit f64821875655dfec4e2c951d5c22c0db873fe86e From 9a23a6c695400d2b583b1a77e503cd366fb28cdc Mon Sep 17 00:00:00 2001 From: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Date: Sun, 2 Jun 2024 12:52:40 -0400 Subject: [PATCH 225/568] Fix mid round antags not starting (#28523) --- Content.Server/StationEvents/Events/StationEventSystem.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Content.Server/StationEvents/Events/StationEventSystem.cs b/Content.Server/StationEvents/Events/StationEventSystem.cs index 38390cedb1a9..35dc646bce67 100644 --- a/Content.Server/StationEvents/Events/StationEventSystem.cs +++ b/Content.Server/StationEvents/Events/StationEventSystem.cs @@ -98,7 +98,11 @@ public override void Update(float frameTime) if (!GameTicker.IsGameRuleAdded(uid, ruleData)) continue; - if (stationEvent.EndTime != null && Timing.CurTime >= stationEvent.EndTime && GameTicker.IsGameRuleActive(uid, ruleData)) + if (!GameTicker.IsGameRuleActive(uid, ruleData) && !HasComp(uid)) + { + GameTicker.StartGameRule(uid, ruleData); + } + else if (stationEvent.EndTime != null && Timing.CurTime >= stationEvent.EndTime && GameTicker.IsGameRuleActive(uid, ruleData)) { GameTicker.EndGameRule(uid, ruleData); } From 6b40b5269e1c8ee9c7475ec753b3f9baf2931cea Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 2 Jun 2024 16:53:47 +0000 Subject: [PATCH 226/568] 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 a6e7cbbc7112..0bfd57a3b93f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix store refunds. - type: Fix - id: 6165 - time: '2024-03-16T14:06:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26173 - author: LordCarve changes: - message: Decayed anomalies no longer show as having gone supercritical in logs. @@ -3853,3 +3846,10 @@ id: 6664 time: '2024-06-02T04:17:53.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27369 +- author: AJCM-git + changes: + - message: Midround antags work again + type: Fix + id: 6665 + time: '2024-06-02T16:52:40.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28523 From 66835ac84e01890aaffaa823b9d08cb25758f66f Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Sun, 2 Jun 2024 19:30:27 +0200 Subject: [PATCH 227/568] Beacons no longer glitch off on grid split (#28518) --- Content.Server/Pinpointer/NavMapSystem.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Content.Server/Pinpointer/NavMapSystem.cs b/Content.Server/Pinpointer/NavMapSystem.cs index dba964753f87..424b6427de4b 100644 --- a/Content.Server/Pinpointer/NavMapSystem.cs +++ b/Content.Server/Pinpointer/NavMapSystem.cs @@ -237,6 +237,16 @@ private void RefreshGrid(EntityUid uid, NavMapComponent component, MapGridCompon component.Chunks.Clear(); component.Beacons.Clear(); + // Refresh beacons + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var qUid, out var qNavComp, out var qTransComp)) + { + if (qTransComp.ParentUid != uid) + continue; + + UpdateNavMapBeaconData(qUid, qNavComp); + } + // Loop over all tiles var tileRefs = _mapSystem.GetAllTiles(uid, mapGrid); From 054118757b619c5b0307e10c16797d166c639dc7 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 2 Jun 2024 17:31:33 +0000 Subject: [PATCH 228/568] 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 0bfd57a3b93f..ec5569327f65 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: LordCarve - changes: - - message: Decayed anomalies no longer show as having gone supercritical in logs. - type: Fix - id: 6166 - time: '2024-03-16T17:31:21.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26180 - author: Velcroboy changes: - message: Tweaked gas canisters to pass through cargo flaps. @@ -3853,3 +3846,10 @@ id: 6665 time: '2024-06-02T16:52:40.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28523 +- author: Errant-4 + changes: + - message: Map labels no longer suddenly disappear for the rest of the round. + type: Fix + id: 6666 + time: '2024-06-02T17:30:27.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28518 From c5918648668e50cd3cce7a651f978d3bc2bdafee Mon Sep 17 00:00:00 2001 From: UBlueberry <161545003+UBlueberry@users.noreply.github.com> Date: Sun, 2 Jun 2024 15:13:57 -0400 Subject: [PATCH 229/568] Drawl capitalization coldfix (part 2: rise of accidentally commiting to master) (#26639) fixed finally yay --- .../EntitySystems/SouthernAccentSystem.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Content.Server/Speech/EntitySystems/SouthernAccentSystem.cs b/Content.Server/Speech/EntitySystems/SouthernAccentSystem.cs index b9260eb84415..c1f8a0be30be 100644 --- a/Content.Server/Speech/EntitySystems/SouthernAccentSystem.cs +++ b/Content.Server/Speech/EntitySystems/SouthernAccentSystem.cs @@ -5,9 +5,12 @@ namespace Content.Server.Speech.EntitySystems; public sealed class SouthernAccentSystem : EntitySystem { - private static readonly Regex RegexIng = new(@"ing\b"); - private static readonly Regex RegexAnd = new(@"\band\b"); - private static readonly Regex RegexDve = new("d've"); + private static readonly Regex RegexLowerIng = new(@"ing\b"); + private static readonly Regex RegexUpperIng = new(@"ING\b"); + private static readonly Regex RegexLowerAnd = new(@"\band\b"); + private static readonly Regex RegexUpperAnd = new(@"\bAND\b"); + private static readonly Regex RegexLowerDve = new(@"d've\b"); + private static readonly Regex RegexUpperDve = new(@"D'VE\b"); [Dependency] private readonly ReplacementAccentSystem _replacement = default!; @@ -24,9 +27,12 @@ private void OnAccent(EntityUid uid, SouthernAccentComponent component, AccentGe message = _replacement.ApplyReplacements(message, "southern"); //They shoulda started runnin' an' hidin' from me! - message = RegexIng.Replace(message, "in'"); - message = RegexAnd.Replace(message, "an'"); - message = RegexDve.Replace(message, "da"); + message = RegexLowerIng.Replace(message, "in'"); + message = RegexUpperIng.Replace(message, "IN'"); + message = RegexLowerAnd.Replace(message, "an'"); + message = RegexUpperAnd.Replace(message, "AN'"); + message = RegexLowerDve.Replace(message, "da"); + message = RegexUpperDve.Replace(message, "DA"); args.Message = message; } }; From 4eadbbc39d2df69aace020583f1f5c57d0826c85 Mon Sep 17 00:00:00 2001 From: Killerqu00 <47712032+Killerqu00@users.noreply.github.com> Date: Sun, 2 Jun 2024 21:40:02 +0200 Subject: [PATCH 230/568] fix typos in bonfire descriptions (#28515) * fix typo * a --- Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml b/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml index cc69a6304d11..29efdaea5d36 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml @@ -2,7 +2,7 @@ id: Bonfire parent: BaseStructure name: bonfire - description: What can be better then late evening under the sky with guitar and friends. + description: What can be better than a late evening under the sky with guitar and friends? components: - type: Sprite noRot: true From 81821e87af5e14c6399482c0cc0f34aa73b86a02 Mon Sep 17 00:00:00 2001 From: stellar-novas Date: Sun, 2 Jun 2024 15:49:31 -0400 Subject: [PATCH 231/568] Update to nixpkgs 24.05 (#28529) --- flake.lock | 14 +++++++------- flake.nix | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/flake.lock b/flake.lock index 6ab38fa41bd8..7baaa468ea56 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -20,16 +20,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1708210246, - "narHash": "sha256-Q8L9XwrBK53fbuuIFMbjKvoV7ixfLFKLw4yV+SD28Y8=", + "lastModified": 1717352157, + "narHash": "sha256-hbBzucWOhwxt3QzeAyUojtD6/aHH81JssDfhFfmqOy0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "69405156cffbdf2be50153f13cbdf9a0bea38e49", + "rev": "44f538ab12e2726af450877a5529f4fd88ddb0fb", "type": "github" }, "original": { "owner": "NixOS", - "ref": "release-23.11", + "ref": "release-24.05", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index e2e119eb9971..095e6b017c7a 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,7 @@ { description = "Development environment for Space Station 14"; - inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-23.11"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-24.05"; inputs.flake-utils.url = "github:numtide/flake-utils"; outputs = { self, nixpkgs, flake-utils }: From 504208c6656bb7e56681c23e3e46092c3205c940 Mon Sep 17 00:00:00 2001 From: Voomra Date: Mon, 3 Jun 2024 02:51:19 +0300 Subject: [PATCH 232/568] fix: localize PraySystem UI (#28535) tweak: localize PraySystem UI --- Content.Server/Prayer/PrayerSystem.cs | 2 +- Resources/Locale/en-US/prayers/prayers.ftl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Server/Prayer/PrayerSystem.cs b/Content.Server/Prayer/PrayerSystem.cs index c8ef368dadfd..3b1ec3fa0838 100644 --- a/Content.Server/Prayer/PrayerSystem.cs +++ b/Content.Server/Prayer/PrayerSystem.cs @@ -54,7 +54,7 @@ private void AddPrayVerb(EntityUid uid, PrayableComponent comp, GetVerbsEvent + _quickDialog.OpenDialog(actor.PlayerSession, Loc.GetString(comp.Verb), Loc.GetString("prayer-popup-notify-pray-ui-message"), (string message) => { Pray(actor.PlayerSession, comp, message); }); diff --git a/Resources/Locale/en-US/prayers/prayers.ftl b/Resources/Locale/en-US/prayers/prayers.ftl index 07713bc8216c..532ba4954f09 100644 --- a/Resources/Locale/en-US/prayers/prayers.ftl +++ b/Resources/Locale/en-US/prayers/prayers.ftl @@ -13,3 +13,4 @@ prayer-popup-notify-centcom-sent = You left a voicemail message for Central Comm prayer-popup-notify-syndicate-sent = You left a voicemail message for Syndicate High Command... prayer-popup-notify-pray-sent = Your message has been sent to the gods... prayer-popup-notify-pray-locked = You don't feel worthy enough... +prayer-popup-notify-pray-ui-message = Message From 947ed7bb22655e2499f27e521766e094663b13dc Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 2 Jun 2024 23:52:25 +0000 Subject: [PATCH 233/568] 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 ec5569327f65..53f9f65ab332 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Velcroboy - changes: - - message: Tweaked gas canisters to pass through cargo flaps. - type: Tweak - id: 6167 - time: '2024-03-17T00:55:32.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26193 - author: metalgearsloth changes: - message: Fix ID cards sometimes not loading properly. @@ -3853,3 +3846,10 @@ id: 6666 time: '2024-06-02T17:30:27.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28518 +- author: Voomra + changes: + - message: localize Pray UI + type: Fix + id: 6667 + time: '2024-06-02T23:51:19.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28535 From 307a0428c7fa324a69ec93fc0faf2a3679cd83de Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 3 Jun 2024 03:05:18 +0200 Subject: [PATCH 234/568] Shut up a child throwing a tantrum (#28538) --- Resources/Credits/GitHub.txt | 2 +- Tools/contribs_shared.ps1 | 2 ++ Tools/dump_github_contributors.ps1 | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index b3627580b1b8..f240c9524751 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, bhenrich, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blueDev2, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, DexlerXD, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, DuskyJay, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, Jark255, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, waylon531, weaversam8, whateverusername0, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem +0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, bhenrich, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blueDev2, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, DexlerXD, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, DuskyJay, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, Jark255, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, RamZ, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, waylon531, weaversam8, whateverusername0, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem diff --git a/Tools/contribs_shared.ps1 b/Tools/contribs_shared.ps1 index ba97c50a9a38..a3638b41b8d0 100644 --- a/Tools/contribs_shared.ps1 +++ b/Tools/contribs_shared.ps1 @@ -12,3 +12,5 @@ $ignore = @{ "ZDDM" = $true "TYoung86" = $true } + +$add = @("RamZ") diff --git a/Tools/dump_github_contributors.ps1 b/Tools/dump_github_contributors.ps1 index 51d8612b28ad..10e74bcf0b52 100755 --- a/Tools/dump_github_contributors.ps1 +++ b/Tools/dump_github_contributors.ps1 @@ -29,7 +29,7 @@ function load_contribs([string] $repo) $engineJson = load_contribs("space-wizards/RobustToolbox") $contentJson = load_contribs("space-wizards/space-station-14") -($engineJson).login + ($contentJson).login ` +($engineJson).login + ($contentJson).login + ($add) ` | select -unique ` | Where-Object { -not $ignore[$_] }` | ForEach-Object { if($replacements[$_] -eq $null){ $_ } else { $replacements[$_] }} ` From 9dbdacda9533304004382ebf5f8c63a32e50916e Mon Sep 17 00:00:00 2001 From: Whisper <121047731+QuietlyWhisper@users.noreply.github.com> Date: Sun, 2 Jun 2024 22:18:49 -0400 Subject: [PATCH 235/568] Rename admin cloak to weh cloak (#28540) --- Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml b/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml index 35a793791cc7..a329c4034b6e 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml @@ -8,7 +8,7 @@ sprite: Clothing/Neck/Cloaks/centcomcloakformal.rsi - type: StealTarget stealGroup: HeadCloak # leaving this here because I suppose it might be interesting? - + - type: entity parent: ClothingNeckBase id: ClothingNeckCloakCap @@ -118,7 +118,7 @@ - type: entity parent: ClothingNeckBase id: ClothingNeckCloakAdmin - name: admin cloak + name: weh cloak description: Weh! components: - type: Sprite From 7ecff3e6996c4141e70611f4fb7585d1fcb983e5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 3 Jun 2024 02:19:55 +0000 Subject: [PATCH 236/568] 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 53f9f65ab332..b60296408149 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix ID cards sometimes not loading properly. - type: Fix - id: 6168 - time: '2024-03-17T01:10:59.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26195 - author: 21Melkuu changes: - message: Add new explosion-proof backpack in aplink. @@ -3853,3 +3846,10 @@ id: 6667 time: '2024-06-02T23:51:19.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28535 +- author: Whisper + changes: + - message: Renamed the player-obtainable "admin cloak" to "weh cloak". + type: Tweak + id: 6668 + time: '2024-06-03T02:18:49.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28540 From 509e3aedf7b91c1e1d11e5a885608bc92909f552 Mon Sep 17 00:00:00 2001 From: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Date: Sun, 2 Jun 2024 23:28:38 -0400 Subject: [PATCH 237/568] RespawnRuleSystem tweaks. (#28528) --- .../Components/RespawnDeadRuleComponent.cs | 5 + .../Components/RespawnTrackerComponent.cs | 12 +- .../GameTicking/Rules/DeathMatchRuleSystem.cs | 4 +- .../GameTicking/Rules/RespawnRuleSystem.cs | 115 +++++++++--------- Resources/Prototypes/GameRules/roundstart.yml | 11 ++ 5 files changed, 84 insertions(+), 63 deletions(-) diff --git a/Content.Server/GameTicking/Rules/Components/RespawnDeadRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/RespawnDeadRuleComponent.cs index fafe811dd922..f6e4a3b1290a 100644 --- a/Content.Server/GameTicking/Rules/Components/RespawnDeadRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/RespawnDeadRuleComponent.cs @@ -6,4 +6,9 @@ [RegisterComponent, Access(typeof(RespawnRuleSystem))] public sealed partial class RespawnDeadRuleComponent : Component { + /// + /// Whether or not we want to add everyone who dies to the respawn tracker + /// + [DataField] + public bool AlwaysRespawnDead; } diff --git a/Content.Server/GameTicking/Rules/Components/RespawnTrackerComponent.cs b/Content.Server/GameTicking/Rules/Components/RespawnTrackerComponent.cs index 3d338c2d1336..b9c8fe109624 100644 --- a/Content.Server/GameTicking/Rules/Components/RespawnTrackerComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/RespawnTrackerComponent.cs @@ -13,18 +13,24 @@ public sealed partial class RespawnTrackerComponent : Component /// A list of the people that should be respawned. /// Used to make sure that we don't respawn aghosts or observers. /// - [DataField("players")] + [DataField] public HashSet Players = new(); /// /// The delay between dying and respawning. /// - [DataField("respawnDelay")] + [DataField] public TimeSpan RespawnDelay = TimeSpan.Zero; /// /// A dictionary of player netuserids and when they will respawn. /// - [DataField("respawnQueue")] + [DataField] public Dictionary RespawnQueue = new(); + + /// + /// Whether or not to delete the original body when respawning + /// + [DataField] + public bool DeleteBody = true; } diff --git a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs index ad7c63ff58df..9e3203d170bc 100644 --- a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs @@ -56,7 +56,7 @@ private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev) _mind.TransferTo(newMind, mob); SetOutfitCommand.SetOutfit(mob, dm.Gear, EntityManager); EnsureComp(mob); - _respawn.AddToTracker(ev.Player.UserId, uid, tracker); + _respawn.AddToTracker(ev.Player.UserId, (uid, tracker)); _point.EnsurePlayer(ev.Player.UserId, uid, point); @@ -73,7 +73,7 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent ev) { if (!GameTicker.IsGameRuleActive(uid, rule)) continue; - _respawn.AddToTracker(ev.Mob, uid, tracker); + _respawn.AddToTracker((ev.Mob, null), (uid, tracker)); } } diff --git a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs index 5215da96aa8d..3f8d31f622ff 100644 --- a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Chat.Managers; +using Content.Server.Database.Migrations.Postgres; using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Systems; @@ -34,38 +35,6 @@ public override void Initialize() SubscribeLocalEvent(OnMobStateChanged); } - private void OnSuicide(SuicideEvent ev) - { - if (!TryComp(ev.Victim, out var actor)) - return; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out _, out var respawn)) - { - if (respawn.Players.Remove(actor.PlayerSession.UserId)) - QueueDel(ev.Victim); - } - } - - private void OnMobStateChanged(MobStateChangedEvent args) - { - if (args.NewMobState == MobState.Alive) - return; - - if (!TryComp(args.Target, out var actor)) - return; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var rule)) - { - if (!GameTicker.IsGameRuleActive(uid, rule)) - continue; - - if (RespawnPlayer(args.Target, uid, actor: actor)) - break; - } - } - public override void Update(float frameTime) { base.Update(frameTime); @@ -75,8 +44,7 @@ public override void Update(float frameTime) foreach (var tracker in EntityQuery()) { - var queue = new Dictionary(tracker.RespawnQueue); - foreach (var (player, time) in queue) + foreach (var (player, time) in tracker.RespawnQueue) { if (_timing.CurTime < time) continue; @@ -92,53 +60,84 @@ public override void Update(float frameTime) } } - /// - /// Adds a given player to the respawn tracker, ensuring that they are respawned if they die. - /// - public void AddToTracker(EntityUid player, EntityUid tracker, RespawnTrackerComponent? component = null, ActorComponent? actor = null) + private void OnSuicide(SuicideEvent ev) { - if (!Resolve(tracker, ref component) || !Resolve(player, ref actor, false)) - return; + if (!TryComp(ev.Victim, out var actor)) + return; - AddToTracker(actor.PlayerSession.UserId, tracker, component); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out var respawn)) + { + if (respawn.Players.Remove(actor.PlayerSession.UserId)) + QueueDel(ev.Victim); + } } - /// - /// Adds a given player to the respawn tracker, ensuring that they are respawned if they die. - /// - public void AddToTracker(NetUserId id, EntityUid tracker, RespawnTrackerComponent? component = null) + private void OnMobStateChanged(MobStateChangedEvent args) { - if (!Resolve(tracker, ref component)) + if (args.NewMobState != MobState.Dead) return; - component.Players.Add(id); + if (!TryComp(args.Target, out var actor)) + return; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var respawnRule, out var tracker, out var rule)) + { + if (!GameTicker.IsGameRuleActive(uid, rule)) + continue; + + if (respawnRule.AlwaysRespawnDead) + AddToTracker(actor.PlayerSession.UserId, (uid, tracker)); + if (RespawnPlayer((args.Target, actor), (uid, tracker))) + break; + } } /// /// Attempts to directly respawn a player, skipping the lobby screen. /// - public bool RespawnPlayer(EntityUid player, EntityUid respawnTracker, RespawnTrackerComponent? component = null, ActorComponent? actor = null) + public bool RespawnPlayer(Entity player, Entity respawnTracker) { - if (!Resolve(respawnTracker, ref component) || !Resolve(player, ref actor, false)) + if (!respawnTracker.Comp.Players.Contains(player.Comp.PlayerSession.UserId) || respawnTracker.Comp.RespawnQueue.ContainsKey(player.Comp.PlayerSession.UserId)) return false; - if (!component.Players.Contains(actor.PlayerSession.UserId) || component.RespawnQueue.ContainsKey(actor.PlayerSession.UserId)) - return false; - - if (component.RespawnDelay == TimeSpan.Zero) + if (respawnTracker.Comp.RespawnDelay == TimeSpan.Zero) { if (_station.GetStations().FirstOrNull() is not { } station) return false; - QueueDel(player); - GameTicker.MakeJoinGame(actor.PlayerSession, station, silent: true); + if (respawnTracker.Comp.DeleteBody) + QueueDel(player); + GameTicker.MakeJoinGame(player.Comp.PlayerSession, station, silent: true); return false; } - var msg = Loc.GetString("rule-respawn-in-seconds", ("second", component.RespawnDelay.TotalSeconds)); + var msg = Loc.GetString("rule-respawn-in-seconds", ("second", respawnTracker.Comp.RespawnDelay.TotalSeconds)); var wrappedMsg = Loc.GetString("chat-manager-server-wrap-message", ("message", msg)); - _chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMsg, respawnTracker, false, actor.PlayerSession.Channel, Color.LimeGreen); - component.RespawnQueue[actor.PlayerSession.UserId] = _timing.CurTime + component.RespawnDelay; + _chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMsg, respawnTracker, false, player.Comp.PlayerSession.Channel, Color.LimeGreen); + + respawnTracker.Comp.RespawnQueue[player.Comp.PlayerSession.UserId] = _timing.CurTime + respawnTracker.Comp.RespawnDelay; + return true; } + + /// + /// Adds a given player to the respawn tracker, ensuring that they are respawned if they die. + /// + public void AddToTracker(Entity player, Entity respawnTracker) + { + if (!Resolve(respawnTracker, ref respawnTracker.Comp) || !Resolve(player, ref player.Comp, false)) + return; + + AddToTracker(player.Comp.PlayerSession.UserId, (respawnTracker, respawnTracker.Comp)); + } + + /// + /// Adds a given player to the respawn tracker, ensuring that they are respawned if they die. + /// + public void AddToTracker(NetUserId id, Entity tracker) + { + tracker.Comp.Players.Add(id); + } } diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 93350163f68f..cf2c5b2f475a 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -5,6 +5,17 @@ components: - type: GameRule +- type: entity + noSpawn: true + parent: BaseGameRule + id: RespawnDeadRule + components: + - type: RespawnDeadRule + alwaysRespawnDead: true + - type: RespawnTracker + respawnDelay: 10 + deleteBody: false + - type: entity noSpawn: true parent: BaseGameRule From 31eb3ed62ce607205aaac59416c53f7f2c40da97 Mon Sep 17 00:00:00 2001 From: Verm <32827189+Vermidia@users.noreply.github.com> Date: Sun, 2 Jun 2024 22:28:53 -0500 Subject: [PATCH 238/568] Welding tweaks (#27959) --- .../Configurable/ConfigurationSystem.cs | 6 +- .../DamageOnToolInteractComponent.cs | 27 ++++--- .../Systems/DamageOnToolInteractSystem.cs | 5 +- .../Mech/Systems/MechAssemblySystem.cs | 4 +- Content.Server/Mech/Systems/MechSystem.cs | 4 +- .../PneumaticCannon/PneumaticCannonSystem.cs | 8 +-- .../Melee/EnergySword/EnergySwordSystem.cs | 6 +- .../ArtifactElectricityTriggerSystem.cs | 4 +- .../EntitySystems/AnchorableSystem.cs | 2 +- .../EntitySystems/EncryptionKeySystem.cs | 4 +- .../Tools/Components/ToolComponent.cs | 71 ++++++++----------- .../Components/ToolRefinableComponent.cs | 11 +-- .../Tools/Systems/SharedToolSystem.Welder.cs | 22 +++--- .../Tools/Systems/SharedToolSystem.cs | 3 +- .../Tools/Systems/ToolRefinableSystem.cs | 20 +++--- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 2 +- .../Prototypes/Entities/Mobs/NPCs/xeno.yml | 2 +- .../Consumable/Drinks/drinks_bottles.yml | 2 +- .../Objects/Consumable/Drinks/drinks_cans.yml | 2 +- .../Entities/Objects/Materials/shards.yml | 10 +-- .../Entities/Objects/Misc/broken_bottle.yml | 2 +- .../Objects/Misc/fire_extinguisher.yml | 2 +- .../Entities/Objects/Power/lights.yml | 14 ++-- .../Objects/Specific/Medical/surgery.yml | 8 +-- .../Entities/Objects/Tools/cowtools.yml | 10 +-- .../Entities/Objects/Tools/gas_tanks.yml | 2 +- .../Entities/Objects/Tools/jaws_of_life.yml | 4 +- .../Entities/Objects/Tools/tools.yml | 4 +- .../Entities/Objects/Tools/welders.yml | 4 +- .../Objects/Weapons/Melee/baseball_bat.yml | 2 +- .../Structures/Storage/Tanks/tanks.yml | 4 +- .../Structures/Wallmounts/walldispenser.yml | 3 +- .../XenoArch/Effects/utility_effects.yml | 2 +- 33 files changed, 137 insertions(+), 139 deletions(-) rename Content.Server/Construction/Components/WelderRefinableComponent.cs => Content.Shared/Tools/Components/ToolRefinableComponent.cs (74%) rename Content.Server/Construction/RefiningSystem.cs => Content.Shared/Tools/Systems/ToolRefinableSystem.cs (59%) diff --git a/Content.Server/Configurable/ConfigurationSystem.cs b/Content.Server/Configurable/ConfigurationSystem.cs index 5f5f1ef7d166..bf89c3f7ed13 100644 --- a/Content.Server/Configurable/ConfigurationSystem.cs +++ b/Content.Server/Configurable/ConfigurationSystem.cs @@ -1,6 +1,7 @@ using Content.Shared.Configurable; using Content.Shared.Interaction; using Content.Shared.Tools.Components; +using Content.Shared.Tools.Systems; using Robust.Server.GameObjects; using Robust.Shared.Containers; using Robust.Shared.Player; @@ -11,6 +12,7 @@ namespace Content.Server.Configurable; public sealed class ConfigurationSystem : EntitySystem { [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly SharedToolSystem _toolSystem = default!; public override void Initialize() { @@ -28,7 +30,7 @@ private void OnInteractUsing(EntityUid uid, ConfigurationComponent component, In if (args.Handled) return; - if (!TryComp(args.Used, out ToolComponent? tool) || !tool.Qualities.Contains(component.QualityNeeded)) + if (!_toolSystem.HasQuality(args.Used, component.QualityNeeded)) return; args.Handled = _uiSystem.TryOpenUi(uid, ConfigurationUiKey.Key, args.User); @@ -68,7 +70,7 @@ private void OnUpdate(EntityUid uid, ConfigurationComponent component, Configura private void OnInsert(EntityUid uid, ConfigurationComponent component, ContainerIsInsertingAttemptEvent args) { - if (!TryComp(args.EntityUid, out ToolComponent? tool) || !tool.Qualities.Contains(component.QualityNeeded)) + if (!_toolSystem.HasQuality(args.EntityUid, component.QualityNeeded)) return; args.Cancel(); diff --git a/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs b/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs index e54090cdbbfb..547c29a202d2 100644 --- a/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs +++ b/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs @@ -1,22 +1,19 @@ using Content.Shared.Damage; using Content.Shared.Tools; -using Robust.Shared.Utility; +using Robust.Shared.Prototypes; -namespace Content.Server.Damage.Components +namespace Content.Server.Damage.Components; + +[RegisterComponent] +public sealed partial class DamageOnToolInteractComponent : Component { - [RegisterComponent] - public sealed partial class DamageOnToolInteractComponent : Component - { - [DataField("tools")] - public PrototypeFlags Tools { get; private set; } = new (); + [DataField] + public ProtoId Tools { get; private set; } - // TODO: Remove this snowflake stuff, make damage per-tool quality perhaps? - [DataField("weldingDamage")] - [ViewVariables(VVAccess.ReadWrite)] - public DamageSpecifier? WeldingDamage { get; private set; } + // TODO: Remove this snowflake stuff, make damage per-tool quality perhaps? + [DataField] + public DamageSpecifier? WeldingDamage { get; private set; } - [DataField("defaultDamage")] - [ViewVariables(VVAccess.ReadWrite)] - public DamageSpecifier? DefaultDamage { get; private set; } - } + [DataField] + public DamageSpecifier? DefaultDamage { get; private set; } } diff --git a/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs b/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs index 42676c1891cd..5980455e49c1 100644 --- a/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs +++ b/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Database; using Content.Shared.Interaction; using Content.Shared.Tools.Components; +using Content.Shared.Tools.Systems; using ItemToggleComponent = Content.Shared.Item.ItemToggle.Components.ItemToggleComponent; namespace Content.Server.Damage.Systems @@ -12,6 +13,7 @@ public sealed class DamageOnToolInteractSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly SharedToolSystem _toolSystem = default!; public override void Initialize() { @@ -42,8 +44,7 @@ private void OnInteracted(EntityUid uid, DamageOnToolInteractComponent component args.Handled = true; } else if (component.DefaultDamage is {} damage - && EntityManager.TryGetComponent(args.Used, out ToolComponent? tool) - && tool.Qualities.ContainsAny(component.Tools)) + && _toolSystem.HasQuality(args.Used, component.Tools)) { var dmg = _damageableSystem.TryChangeDamage(args.Target, damage, origin: args.User); diff --git a/Content.Server/Mech/Systems/MechAssemblySystem.cs b/Content.Server/Mech/Systems/MechAssemblySystem.cs index c1fff819b4c9..4b408343b7a3 100644 --- a/Content.Server/Mech/Systems/MechAssemblySystem.cs +++ b/Content.Server/Mech/Systems/MechAssemblySystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Interaction; using Content.Shared.Tag; using Content.Shared.Tools.Components; +using Content.Shared.Tools.Systems; using Robust.Server.Containers; using Robust.Shared.Containers; @@ -15,6 +16,7 @@ public sealed class MechAssemblySystem : EntitySystem { [Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly SharedToolSystem _toolSystem = default!; /// public override void Initialize() @@ -30,7 +32,7 @@ private void OnInit(EntityUid uid, MechAssemblyComponent component, ComponentIni private void OnInteractUsing(EntityUid uid, MechAssemblyComponent component, InteractUsingEvent args) { - if (TryComp(args.Used, out var toolComp) && toolComp.Qualities.Contains(component.QualityNeeded)) + if (_toolSystem.HasQuality(args.Used, component.QualityNeeded)) { foreach (var tag in component.RequiredParts.Keys) { diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index 53c6c62cdb81..68b973f58877 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -17,6 +17,7 @@ using Content.Shared.Verbs; using Content.Shared.Wires; using Content.Server.Body.Systems; +using Content.Shared.Tools.Systems; using Robust.Server.Containers; using Robust.Server.GameObjects; using Robust.Shared.Containers; @@ -35,6 +36,7 @@ public sealed partial class MechSystem : SharedMechSystem [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; + [Dependency] private readonly SharedToolSystem _toolSystem = default!; /// public override void Initialize() @@ -87,7 +89,7 @@ private void OnInteractUsing(EntityUid uid, MechComponent component, InteractUsi return; } - if (TryComp(args.Used, out var tool) && tool.Qualities.Contains("Prying") && component.BatterySlot.ContainedEntity != null) + if (_toolSystem.HasQuality(args.Used, "Prying") && component.BatterySlot.ContainedEntity != null) { var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.BatteryRemovalDelay, new RemoveBatteryEvent(), uid, target: uid, used: args.Target) diff --git a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs index 6e0e0c503a9e..7882522d30ba 100644 --- a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs +++ b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs @@ -7,7 +7,7 @@ using Content.Shared.Interaction; using Content.Shared.PneumaticCannon; using Content.Shared.StatusEffect; -using Content.Shared.Tools.Components; +using Content.Shared.Tools.Systems; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; @@ -22,6 +22,7 @@ public sealed class PneumaticCannonSystem : SharedPneumaticCannonSystem [Dependency] private readonly GunSystem _gun = default!; [Dependency] private readonly StunSystem _stun = default!; [Dependency] private readonly ItemSlotsSystem _slots = default!; + [Dependency] private readonly SharedToolSystem _toolSystem = default!; public override void Initialize() { @@ -38,10 +39,7 @@ private void OnInteractUsing(EntityUid uid, PneumaticCannonComponent component, if (args.Handled) return; - if (!TryComp(args.Used, out var tool)) - return; - - if (!tool.Qualities.Contains(component.ToolModifyPower)) + if (!_toolSystem.HasQuality(args.Used, component.ToolModifyPower)) return; var val = (int) component.Power; diff --git a/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs b/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs index e8897781f5e7..5970e1631961 100644 --- a/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs +++ b/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs @@ -2,8 +2,7 @@ using Content.Shared.Light; using Content.Shared.Light.Components; using Content.Shared.Toggleable; -using Content.Shared.Tools.Components; -using Content.Shared.Item; +using Content.Shared.Tools.Systems; using Robust.Shared.Random; namespace Content.Server.Weapons.Melee.EnergySword; @@ -13,6 +12,7 @@ public sealed class EnergySwordSystem : EntitySystem [Dependency] private readonly SharedRgbLightControllerSystem _rgbSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedToolSystem _toolSystem = default!; public override void Initialize() { @@ -38,7 +38,7 @@ private void OnInteractUsing(EntityUid uid, EnergySwordComponent comp, InteractU if (args.Handled) return; - if (!TryComp(args.Used, out ToolComponent? tool) || !tool.Qualities.ContainsAny("Pulsing")) + if (!_toolSystem.HasQuality(args.Used, "Pulsing")) return; args.Handled = true; diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs index aa2a16aa1b2e..019e09bbbbc0 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs @@ -3,12 +3,14 @@ using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Shared.Interaction; using Content.Shared.Tools.Components; +using Content.Shared.Tools.Systems; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; public sealed class ArtifactElectricityTriggerSystem : EntitySystem { [Dependency] private readonly ArtifactSystem _artifactSystem = default!; + [Dependency] private readonly SharedToolSystem _toolSystem = default!; public override void Initialize() { @@ -42,7 +44,7 @@ private void OnInteractUsing(EntityUid uid, ArtifactElectricityTriggerComponent if (args.Handled) return; - if (!TryComp(args.Used, out ToolComponent? tool) || !tool.Qualities.ContainsAny("Pulsing")) + if (!_toolSystem.HasQuality(args.Used, "Pulsing")) return; args.Handled = _artifactSystem.TryActivateArtifact(uid, args.User); diff --git a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs index e9ef053f629f..efb5dfd02482 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -79,7 +79,7 @@ private void OnInteractUsing(EntityUid uid, AnchorableComponent anchorable, Inte return; // If the used entity doesn't have a tool, return early. - if (!TryComp(args.Used, out ToolComponent? usedTool) || !usedTool.Qualities.Contains(anchorable.Tool)) + if (!TryComp(args.Used, out ToolComponent? usedTool) || !_tool.HasQuality(args.Used, anchorable.Tool, usedTool)) return; args.Handled = true; diff --git a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs index 746147eb5b4d..ea07b5f8a5ce 100644 --- a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs +++ b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs @@ -6,10 +6,8 @@ using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Radio.Components; -using Content.Shared.Tools; using Content.Shared.Tools.Components; using Content.Shared.Wires; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Network; @@ -106,7 +104,7 @@ private void OnInteractUsing(EntityUid uid, EncryptionKeyHolderComponent compone TryInsertKey(uid, component, args); } else if (TryComp(args.Used, out var tool) - && tool.Qualities.Contains(component.KeysExtractionMethod) + && _tool.HasQuality(args.Used, component.KeysExtractionMethod, tool) && component.KeyContainer.ContainedEntities.Count > 0) // dont block deconstruction { args.Handled = true; diff --git a/Content.Shared/Tools/Components/ToolComponent.cs b/Content.Shared/Tools/Components/ToolComponent.cs index 92857ab9054d..a7210c6fa07b 100644 --- a/Content.Shared/Tools/Components/ToolComponent.cs +++ b/Content.Shared/Tools/Components/ToolComponent.cs @@ -1,52 +1,43 @@ +using Content.Shared.Tools.Systems; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Utility; -namespace Content.Shared.Tools.Components -{ - [RegisterComponent, NetworkedComponent] // TODO move tool system to shared, and make it a friend. - public sealed partial class ToolComponent : Component - { - [DataField("qualities")] - public PrototypeFlags Qualities { get; set; } = new(); - - /// - /// For tool interactions that have a delay before action this will modify the rate, time to wait is divided by this value - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("speed")] - public float SpeedModifier { get; set; } = 1; +namespace Content.Shared.Tools.Components; - [DataField("useSound")] - public SoundSpecifier? UseSound { get; set; } - } +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedToolSystem))] +public sealed partial class ToolComponent : Component +{ + [DataField] + public PrototypeFlags Qualities = []; /// - /// Attempt event called *before* any do afters to see if the tool usage should succeed or not. - /// Raised on both the tool and then target. + /// For tool interactions that have a delay before action this will modify the rate, time to wait is divided by this value /// - public sealed class ToolUseAttemptEvent : CancellableEntityEventArgs - { - public EntityUid User { get; } + [DataField] + public float SpeedModifier = 1; - public ToolUseAttemptEvent(EntityUid user) - { - User = user; - } - } + [DataField] + public SoundSpecifier? UseSound; +} - /// - /// Event raised on the user of a tool to see if they can actually use it. - /// - [ByRefEvent] - public struct ToolUserAttemptUseEvent - { - public EntityUid? Target; - public bool Cancelled = false; +/// +/// Attempt event called *before* any do afters to see if the tool usage should succeed or not. +/// Raised on both the tool and then target. +/// +public sealed class ToolUseAttemptEvent(EntityUid user, float fuel) : CancellableEntityEventArgs +{ + public EntityUid User { get; } = user; + public float Fuel = fuel; +} - public ToolUserAttemptUseEvent(EntityUid? target) - { - Target = target; - } - } +/// +/// Event raised on the user of a tool to see if they can actually use it. +/// +[ByRefEvent] +public struct ToolUserAttemptUseEvent(EntityUid? target) +{ + public EntityUid? Target = target; + public bool Cancelled = false; } diff --git a/Content.Server/Construction/Components/WelderRefinableComponent.cs b/Content.Shared/Tools/Components/ToolRefinableComponent.cs similarity index 74% rename from Content.Server/Construction/Components/WelderRefinableComponent.cs rename to Content.Shared/Tools/Components/ToolRefinableComponent.cs index 2fe88f2670c1..5a311cdda8e3 100644 --- a/Content.Server/Construction/Components/WelderRefinableComponent.cs +++ b/Content.Shared/Tools/Components/ToolRefinableComponent.cs @@ -1,15 +1,16 @@ using Content.Shared.Storage; -using Content.Shared.Tools; +using Robust.Shared.GameStates; using Robust.Shared.Prototypes; +using Content.Shared.Tools.Systems; -namespace Content.Server.Construction.Components; +namespace Content.Shared.Tools.Components; /// /// Used for something that can be refined by welder. /// For example, glass shard can be refined to glass sheet. /// -[RegisterComponent, Access(typeof(RefiningSystem))] -public sealed partial class WelderRefinableComponent : Component +[RegisterComponent, NetworkedComponent, Access(typeof(ToolRefinablSystem))] +public sealed partial class ToolRefinableComponent : Component { /// /// The items created when the item is refined. @@ -27,7 +28,7 @@ public sealed partial class WelderRefinableComponent : Component /// The amount of fuel it takes to refine a given item. /// [DataField] - public float RefineFuel; + public float RefineFuel = 3f; /// /// The tool type needed in order to refine this item. diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs b/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs index e790b59cd121..60eafce47429 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs @@ -16,8 +16,15 @@ public void InitializeWelder() { SubscribeLocalEvent(OnWelderExamine); SubscribeLocalEvent(OnWelderAfterInteract); - SubscribeLocalEvent>(OnWelderToolUseAttempt); + + SubscribeLocalEvent((uid, comp, ev) => { + CanCancelWelderUse((uid, comp), ev.User, ev.Fuel, ev); + }); + SubscribeLocalEvent>((uid, comp, ev) => { + CanCancelWelderUse((uid, comp), ev.Event.User, ev.Event.Fuel, ev); + }); SubscribeLocalEvent(OnWelderDoAfter); + SubscribeLocalEvent(OnToggle); SubscribeLocalEvent(OnActivateAttempt); } @@ -120,23 +127,20 @@ private void OnWelderAfterInteract(Entity entity, ref AfterInte } } - private void OnWelderToolUseAttempt(Entity entity, ref DoAfterAttemptEvent args) + private void CanCancelWelderUse(Entity entity, EntityUid user, float requiredFuel, CancellableEntityEventArgs ev) { - var user = args.DoAfter.Args.User; - if (!ItemToggle.IsActivated(entity.Owner)) { _popup.PopupClient(Loc.GetString("welder-component-welder-not-lit-message"), entity, user); - args.Cancel(); - return; + ev.Cancel(); } - var (fuel, _) = GetWelderFuelAndCapacity(entity); + var (currentFuel, _) = GetWelderFuelAndCapacity(entity); - if (args.Event.Fuel > fuel) + if (requiredFuel > currentFuel) { _popup.PopupClient(Loc.GetString("welder-component-cannot-weld-message"), entity, user); - args.Cancel(); + ev.Cancel(); } } diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.cs b/Content.Shared/Tools/Systems/SharedToolSystem.cs index 9edae9b78fd0..72e984fd82a1 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.cs @@ -217,7 +217,7 @@ private bool CanStartToolUse(EntityUid tool, EntityUid user, EntityUid? target, return false; // check if the tool allows being used - var beforeAttempt = new ToolUseAttemptEvent(user); + var beforeAttempt = new ToolUseAttemptEvent(user, fuel); RaiseLocalEvent(tool, beforeAttempt); if (beforeAttempt.Cancelled) return false; @@ -296,4 +296,3 @@ public LatticeCuttingCompleteEvent(NetCoordinates coordinates) public sealed partial class CableCuttingFinishedEvent : SimpleDoAfterEvent; #endregion - diff --git a/Content.Server/Construction/RefiningSystem.cs b/Content.Shared/Tools/Systems/ToolRefinableSystem.cs similarity index 59% rename from Content.Server/Construction/RefiningSystem.cs rename to Content.Shared/Tools/Systems/ToolRefinableSystem.cs index ce7eb49ef1d7..e8ac4d492d77 100644 --- a/Content.Server/Construction/RefiningSystem.cs +++ b/Content.Shared/Tools/Systems/ToolRefinableSystem.cs @@ -1,25 +1,26 @@ -using Content.Server.Construction.Components; using Content.Shared.Construction; using Content.Shared.Interaction; using Content.Shared.Storage; -using Content.Shared.Tools.Systems; +using Content.Shared.Tools.Components; +using Robust.Shared.Network; using Robust.Shared.Random; -namespace Content.Server.Construction; +namespace Content.Shared.Tools.Systems; -public sealed class RefiningSystem : EntitySystem +public sealed class ToolRefinablSystem : EntitySystem { + [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnInteractUsing); - SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnDoAfter); } - private void OnInteractUsing(EntityUid uid, WelderRefinableComponent component, InteractUsingEvent args) + private void OnInteractUsing(EntityUid uid, ToolRefinableComponent component, InteractUsingEvent args) { if (args.Handled) return; @@ -34,11 +35,14 @@ private void OnInteractUsing(EntityUid uid, WelderRefinableComponent component, fuel: component.RefineFuel); } - private void OnDoAfter(EntityUid uid, WelderRefinableComponent component, WelderRefineDoAfterEvent args) + private void OnDoAfter(EntityUid uid, ToolRefinableComponent component, WelderRefineDoAfterEvent args) { if (args.Cancelled) return; + if (_net.IsClient) + return; + var xform = Transform(uid); var spawns = EntitySpawnCollection.GetSpawns(component.RefineResult, _random); foreach (var spawn in spawns) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index b5f69e6f56b6..51647f698524 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -2255,7 +2255,7 @@ - DoorBumpOpener - FootstepSound - type: Tool # Open door from xeno.yml. - speed: 1.5 + speedModifier: 1.5 qualities: - Prying useSound: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index a3d4eafacbbc..8c262d23da65 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -22,7 +22,7 @@ NavSmash: !type:Bool true - type: Tool - speed: 1.5 + speedModifier: 1.5 qualities: - Prying - type: Prying diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml index 35ad43586bf4..7287119019e8 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml @@ -80,7 +80,7 @@ - type: Tool qualities: - Rolling - speed: 0.75 # not as good as a rolling pin but does the job + speedModifier: 0.75 # not as good as a rolling pin but does the job - type: PhysicalComposition materialComposition: Glass: 100 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml index 5d092673edfa..f5cb260e03bd 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml @@ -56,7 +56,7 @@ - type: Tool qualities: - Rolling - speed: 0.25 # its small so takes longer to roll the entire dough flat + speedModifier: 0.25 # its small so takes longer to roll the entire dough flat - type: SpaceGarbage - type: TrashOnSolutionEmpty solution: drink diff --git a/Resources/Prototypes/Entities/Objects/Materials/shards.yml b/Resources/Prototypes/Entities/Objects/Materials/shards.yml index 561140a46a44..fa6937dac341 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/shards.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/shards.yml @@ -85,7 +85,7 @@ components: - type: Sprite color: "#bbeeff" - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - type: DamageUserOnTrigger @@ -120,7 +120,7 @@ damage: types: Slash: 4.5 - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - id: PartRodMetal1 @@ -156,7 +156,7 @@ damage: types: Slash: 5.5 - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - id: SheetPlasma1 @@ -195,7 +195,7 @@ types: Slash: 4.5 Radiation: 2 - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - id: SheetUranium1 @@ -230,7 +230,7 @@ components: - type: Sprite color: "#e0aa36" - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - id: SheetBrass1 diff --git a/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml b/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml index 32222d0036cd..9d3ef6c42427 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml @@ -26,6 +26,6 @@ materialComposition: Glass: 50 - type: SpaceGarbage - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index 112ce99710d3..71629919ae4e 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -46,7 +46,7 @@ - type: Tool qualities: - Rolling - speed: 0.5 # its very big, akward to use + speedModifier: 0.5 # its very big, akward to use - type: Appearance - type: GenericVisualizer visuals: diff --git a/Resources/Prototypes/Entities/Objects/Power/lights.yml b/Resources/Prototypes/Entities/Objects/Power/lights.yml index bccf10bee535..3f7da1efab35 100644 --- a/Resources/Prototypes/Entities/Objects/Power/lights.yml +++ b/Resources/Prototypes/Entities/Objects/Power/lights.yml @@ -66,7 +66,7 @@ materialComposition: Glass: 25 - type: SpaceGarbage - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 @@ -273,7 +273,7 @@ - type: Construction graph: CyanLight node: icon - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - id: ShardCrystalCyan @@ -293,7 +293,7 @@ - type: Construction graph: BlueLight node: icon - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - id: ShardCrystalBlue @@ -313,7 +313,7 @@ - type: Construction graph: PinkLight node: icon - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - id: ShardCrystalPink @@ -333,7 +333,7 @@ - type: Construction graph: OrangeLight node: icon - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - id: ShardCrystalOrange @@ -353,7 +353,7 @@ - type: Construction graph: RedLight node: icon - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - id: ShardCrystalRed @@ -373,7 +373,7 @@ - type: Construction graph: GreenLight node: icon - - type: WelderRefinable + - type: ToolRefinable refineResult: - id: SheetGlass1 - id: ShardCrystalGreen diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml index aa0cf4618721..c4f7798154e3 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml @@ -178,7 +178,7 @@ - type: Tool qualities: - Sawing - speed: 1.0 + speedModifier: 1.0 # No melee for regular saw because have you ever seen someone use a band saw as a weapon? It's dumb. - type: entity @@ -200,7 +200,7 @@ - type: Tool qualities: - Sawing - speed: 0.5 + speedModifier: 0.5 - type: entity name: circular saw @@ -222,7 +222,7 @@ - type: Tool qualities: - Sawing - speed: 1.5 + speedModifier: 1.5 - type: entity name: advanced circular saw @@ -245,4 +245,4 @@ - type: Tool qualities: - Sawing - speed: 2.0 + speedModifier: 2.0 diff --git a/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml b/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml index 87959ebef3db..c9b37b8b1aed 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml @@ -21,7 +21,7 @@ - Cutting useSound: path: /Audio/Items/wirecutter.ogg - speed: 0.05 + speedModifier: 0.05 - type: Item sprite: Objects/Tools/Cowtools/haycutters.rsi @@ -46,7 +46,7 @@ - Screwing useSound: collection: Screwdriver - speed: 0.05 + speedModifier: 0.05 - type: entity name: wronch @@ -69,7 +69,7 @@ - Anchoring useSound: path: /Audio/Items/ratchet.ogg - speed: 0.05 + speedModifier: 0.05 - type: entity name: cowbar @@ -93,7 +93,7 @@ - Prying useSound: path: /Audio/Items/crowbar.ogg - speed: 0.05 + speedModifier: 0.05 - type: ToolTileCompatible - type: Prying @@ -127,7 +127,7 @@ size: Small sprite: Objects/Tools/Cowtools/cowelder.rsi - type: Tool - speed: 0.05 + speedModifier: 0.05 - type: entity name: milkalyzer diff --git a/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml b/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml index 7bdd32f45795..53423e84a4fe 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml @@ -51,7 +51,7 @@ - type: Tool qualities: - Rolling - speed: 0.6 # fairly unwieldly but nice round surface + speedModifier: 0.6 # fairly unwieldly but nice round surface - type: entity parent: GasTankRoundBase diff --git a/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml b/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml index 8e2b7597970d..c80e53870e58 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml @@ -21,7 +21,7 @@ - type: Tool qualities: - Prying - speed: 1.5 + speedModifier: 1.5 useSound: /Audio/Items/jaws_pry.ogg - type: Prying pryPowered: true @@ -69,7 +69,7 @@ - type: Tool qualities: - Prying - speed: 3.0 + speedModifier: 3.0 - type: MultipleTool entries: - behavior: Prying diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index 452a905329e2..7930482960ff 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -304,7 +304,7 @@ - type: Tool qualities: - Screwing - speed: 1.5 + speedModifier: 1.5 useSound: /Audio/Items/drill_use.ogg - type: MultipleTool statusShowBehavior: true @@ -479,7 +479,7 @@ - type: Tool qualities: - Screwing - speed: 1.2 # Kept for future adjustments. Currently 1.2x for balance + speedModifier: 1.2 # Kept for future adjustments. Currently 1.2x for balance useSound: /Audio/Items/drill_use.ogg - type: ToolTileCompatible - type: MultipleTool diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index 9bf3f2e2cb9d..9db30edb52ed 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -139,7 +139,7 @@ Quantity: 250 maxVol: 250 - type: Tool - speed: 1.3 + speedModifier: 1.3 - type: entity name: experimental welding tool @@ -190,7 +190,7 @@ Quantity: 50 maxVol: 50 - type: Tool - speed: 0.7 + speedModifier: 0.7 - type: PointLight enabled: false radius: 1.0 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml index 5347096bf10e..818c4bd67685 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml @@ -26,7 +26,7 @@ - type: Tool qualities: - Rolling - speed: 0.75 # a bit unwieldly but does the job + speedModifier: 0.75 # a bit unwieldly but does the job - type: Clothing quickEquip: false slots: diff --git a/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml b/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml index 934298b62076..df19550cdbde 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml @@ -25,8 +25,7 @@ - type: ReagentTank tankType: Fuel - type: DamageOnToolInteract - tools: - - Welding + tools: Welding weldingDamage: types: Heat: 10 @@ -217,4 +216,3 @@ fillBaseName: watertank-2- - type: ExaminableSolution solution: tank - diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/walldispenser.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/walldispenser.yml index 72ea308af087..3570264a57ac 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/walldispenser.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/walldispenser.yml @@ -82,8 +82,7 @@ - type: ReagentTank tankType: Fuel - type: DamageOnToolInteract - tools: - - Welding + tools: Welding weldingDamage: types: Heat: 20 diff --git a/Resources/Prototypes/XenoArch/Effects/utility_effects.yml b/Resources/Prototypes/XenoArch/Effects/utility_effects.yml index 84df09af33d7..a89670805799 100644 --- a/Resources/Prototypes/XenoArch/Effects/utility_effects.yml +++ b/Resources/Prototypes/XenoArch/Effects/utility_effects.yml @@ -220,7 +220,7 @@ - type: Tool qualities: - Screwing - speed: 2 # Very powerful multitool to balance out the desire to sell or scrap for points + speedModifier: 2 # Very powerful multitool to balance out the desire to sell or scrap for points useSound: /Audio/Items/drill_use.ogg - type: Tag tags: From 91875e91e13e77af5c9ca3b0c8a65a0fa232d5cd Mon Sep 17 00:00:00 2001 From: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Date: Sun, 2 Jun 2024 23:30:00 -0400 Subject: [PATCH 239/568] Strip Items From Things Before Biomassing Them (#28544) --- .../Medical/BiomassReclaimer/BiomassReclaimerSystem.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs index f8d5139a46a8..a6285294c94c 100644 --- a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs +++ b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Humanoid; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Content.Shared.Inventory; using Content.Shared.Jittering; using Content.Shared.Medical; using Content.Shared.Mind; @@ -36,6 +37,7 @@ namespace Content.Server.Medical.BiomassReclaimer public sealed class BiomassReclaimerSystem : EntitySystem { [Dependency] private readonly IConfigurationManager _configManager = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedJitteringSystem _jitteringSystem = default!; [Dependency] private readonly SharedAudioSystem _sharedAudioSystem = default!; @@ -49,6 +51,7 @@ public sealed class BiomassReclaimerSystem : EntitySystem [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly MaterialStorageSystem _material = default!; [Dependency] private readonly SharedMindSystem _minds = default!; + [Dependency] private readonly InventorySystem _inventory = default!; [ValidatePrototypeId] public const string BiomassPrototype = "Biomass"; @@ -221,6 +224,12 @@ private void StartProcessing(EntityUid toProcess, Entity Date: Mon, 3 Jun 2024 03:29:59 +0000 Subject: [PATCH 240/568] 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 b60296408149..3086e6a71c07 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: 21Melkuu - changes: - - message: Add new explosion-proof backpack in aplink. - type: Add - id: 6169 - time: '2024-03-17T02:21:13.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26187 - author: Ilya246 changes: - message: Syndicate decoy bombs may now be purchased from the uplink. @@ -3853,3 +3846,10 @@ id: 6668 time: '2024-06-03T02:18:49.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28540 +- author: Vermidia + changes: + - message: Glass shards are no longer weldable with an unlit welder. + type: Fix + id: 6669 + time: '2024-06-03T03:28:53.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27959 From 42217079b76084ef2d45d3b32b96027ae36c5355 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 3 Jun 2024 03:31:08 +0000 Subject: [PATCH 241/568] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3086e6a71c07..941b93313644 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: Ilya246 - changes: - - message: Syndicate decoy bombs may now be purchased from the uplink. - type: Add - - message: Syndicate bomb description no longer lies about their minimum detonation - time. - type: Fix - id: 6170 - time: '2024-03-17T02:31:41.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26034 - author: wafehling changes: - message: Added 18 new bounties to cargo bounty system. @@ -3853,3 +3843,11 @@ id: 6669 time: '2024-06-03T03:28:53.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27959 +- author: AJCM-git + changes: + - message: Biomass reclaimers no longer act as a void to any unfortunate belongings + a corpse may be wearing + type: Tweak + id: 6670 + time: '2024-06-03T03:30:00.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28544 From be35a5d36991237525a490116af5363e06ab5216 Mon Sep 17 00:00:00 2001 From: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Date: Sun, 2 Jun 2024 23:33:30 -0400 Subject: [PATCH 242/568] Header of Gas tank UI screen now fetched from Entity name and local (#28545) --- .../GasTank/GasTankBoundUserInterface.cs | 3 +- .../Systems/Atmos/GasTank/GasTankWindow.cs | 307 +++++++++--------- .../Locale/en-US/atmos/gas-tank-component.ftl | 1 - 3 files changed, 151 insertions(+), 160 deletions(-) diff --git a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs index ee8cb28d2ccc..4702f8f36594 100644 --- a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs +++ b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs @@ -1,6 +1,5 @@ using Content.Shared.Atmos.Components; using JetBrains.Annotations; -using Robust.Client.GameObjects; namespace Content.Client.UserInterface.Systems.Atmos.GasTank { @@ -30,7 +29,7 @@ public void ToggleInternals() protected override void Open() { base.Open(); - _window = new GasTankWindow(this); + _window = new GasTankWindow(this, EntMan.GetComponent(Owner).EntityName); _window.OnClose += Close; _window.OpenCentered(); } diff --git a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs index 7797a096de30..c23850a65034 100644 --- a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs +++ b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs @@ -10,201 +10,194 @@ using Robust.Client.UserInterface.CustomControls; using static Robust.Client.UserInterface.Controls.BoxContainer; -namespace Content.Client.UserInterface.Systems.Atmos.GasTank +namespace Content.Client.UserInterface.Systems.Atmos.GasTank; + +public sealed class GasTankWindow + : BaseWindow { - public sealed class GasTankWindow - : BaseWindow - { - private GasTankBoundUserInterface _owner; - private readonly Label _lblName; - private readonly BoxContainer _topContainer; - private readonly Control _contentContainer; + private readonly RichTextLabel _lblPressure; + private readonly FloatSpinBox _spbPressure; + private readonly RichTextLabel _lblInternals; + private readonly Button _btnInternals; + public GasTankWindow(GasTankBoundUserInterface owner, string uidName) + { + Control contentContainer; + BoxContainer topContainer; + TextureButton btnClose; + var resourceCache = IoCManager.Resolve(); + var rootContainer = new LayoutContainer { Name = "GasTankRoot" }; + AddChild(rootContainer); - private readonly IResourceCache _resourceCache = default!; - private readonly RichTextLabel _lblPressure; - private readonly FloatSpinBox _spbPressure; - private readonly RichTextLabel _lblInternals; - private readonly Button _btnInternals; + MouseFilter = MouseFilterMode.Stop; - public GasTankWindow(GasTankBoundUserInterface owner) + var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); + var back = new StyleBoxTexture { - TextureButton btnClose; - _resourceCache = IoCManager.Resolve(); - _owner = owner; - var rootContainer = new LayoutContainer {Name = "GasTankRoot"}; - AddChild(rootContainer); + Texture = panelTex, + Modulate = Color.FromHex("#25252A"), + }; - MouseFilter = MouseFilterMode.Stop; + back.SetPatchMargin(StyleBox.Margin.All, 10); - var panelTex = _resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); - var back = new StyleBoxTexture - { - Texture = panelTex, - Modulate = Color.FromHex("#25252A"), - }; - - back.SetPatchMargin(StyleBox.Margin.All, 10); - - var topPanel = new PanelContainer - { - PanelOverride = back, - MouseFilter = MouseFilterMode.Pass - }; + var topPanel = new PanelContainer + { + PanelOverride = back, + MouseFilter = MouseFilterMode.Pass + }; - var bottomWrap = new LayoutContainer - { - Name = "BottomWrap" - }; + var bottomWrap = new LayoutContainer + { + Name = "BottomWrap" + }; - rootContainer.AddChild(topPanel); - rootContainer.AddChild(bottomWrap); + rootContainer.AddChild(topPanel); + rootContainer.AddChild(bottomWrap); - LayoutContainer.SetAnchorPreset(topPanel, LayoutContainer.LayoutPreset.Wide); - LayoutContainer.SetMarginBottom(topPanel, -85); + LayoutContainer.SetAnchorPreset(topPanel, LayoutContainer.LayoutPreset.Wide); + LayoutContainer.SetMarginBottom(topPanel, -85); - LayoutContainer.SetAnchorPreset(bottomWrap, LayoutContainer.LayoutPreset.VerticalCenterWide); - LayoutContainer.SetGrowHorizontal(bottomWrap, LayoutContainer.GrowDirection.Both); + LayoutContainer.SetAnchorPreset(bottomWrap, LayoutContainer.LayoutPreset.VerticalCenterWide); + LayoutContainer.SetGrowHorizontal(bottomWrap, LayoutContainer.GrowDirection.Both); - var topContainerWrap = new BoxContainer + var topContainerWrap = new BoxContainer + { + Orientation = LayoutOrientation.Vertical, + Children = { - Orientation = LayoutOrientation.Vertical, - Children = + (topContainer = new BoxContainer { - (_topContainer = new BoxContainer - { - Orientation = LayoutOrientation.Vertical - }), - new Control {MinSize = new Vector2(0, 110)} - } - }; + Orientation = LayoutOrientation.Vertical + }), + new Control {MinSize = new Vector2(0, 110)} + } + }; - rootContainer.AddChild(topContainerWrap); + rootContainer.AddChild(topContainerWrap); - LayoutContainer.SetAnchorPreset(topContainerWrap, LayoutContainer.LayoutPreset.Wide); + LayoutContainer.SetAnchorPreset(topContainerWrap, LayoutContainer.LayoutPreset.Wide); - var font = _resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13); + var font = resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13); - var topRow = new BoxContainer + var topRow = new BoxContainer + { + Orientation = LayoutOrientation.Horizontal, + Margin = new Thickness(4, 2, 12, 2), + Children = { - Orientation = LayoutOrientation.Horizontal, - Margin = new Thickness(4, 2, 12, 2), - Children = + (new Label { - (_lblName = new Label - { - Text = Loc.GetString("gas-tank-window-label"), - FontOverride = font, - FontColorOverride = StyleNano.NanoGold, - VerticalAlignment = VAlignment.Center, - HorizontalExpand = true, - HorizontalAlignment = HAlignment.Left, - Margin = new Thickness(0, 0, 20, 0), - }), - (btnClose = new TextureButton - { - StyleClasses = {DefaultWindow.StyleClassWindowCloseButton}, - VerticalAlignment = VAlignment.Center - }) - } - }; - - var middle = new PanelContainer - { - PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#202025")}, - Children = + Text = uidName, + FontOverride = font, + FontColorOverride = StyleNano.NanoGold, + VerticalAlignment = VAlignment.Center, + HorizontalExpand = true, + HorizontalAlignment = HAlignment.Left, + Margin = new Thickness(0, 0, 20, 0), + }), + (btnClose = new TextureButton { - (_contentContainer = new BoxContainer - { - Orientation = LayoutOrientation.Vertical, - Margin = new Thickness(8, 4), - }) - } - }; - - _topContainer.AddChild(topRow); - _topContainer.AddChild(new PanelContainer - { - MinSize = new Vector2(0, 2), - PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#525252ff")} - }); - _topContainer.AddChild(middle); - _topContainer.AddChild(new PanelContainer + StyleClasses = {DefaultWindow.StyleClassWindowCloseButton}, + VerticalAlignment = VAlignment.Center + }) + } + }; + + var middle = new PanelContainer + { + PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#202025") }, + Children = { - MinSize = new Vector2(0, 2), - PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#525252ff")} - }); + (contentContainer = new BoxContainer + { + Orientation = LayoutOrientation.Vertical, + Margin = new Thickness(8, 4), + }) + } + }; + topContainer.AddChild(topRow); + topContainer.AddChild(new PanelContainer + { + MinSize = new Vector2(0, 2), + PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") } + }); + topContainer.AddChild(middle); + topContainer.AddChild(new PanelContainer + { + MinSize = new Vector2(0, 2), + PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") } + }); - _lblPressure = new RichTextLabel(); - _contentContainer.AddChild(_lblPressure); - //internals - _lblInternals = new RichTextLabel - {MinSize = new Vector2(200, 0), VerticalAlignment = VAlignment.Center}; - _btnInternals = new Button {Text = Loc.GetString("gas-tank-window-internals-toggle-button") }; + _lblPressure = new RichTextLabel(); + contentContainer.AddChild(_lblPressure); - _contentContainer.AddChild( - new BoxContainer - { - Orientation = LayoutOrientation.Horizontal, - Margin = new Thickness(0, 7, 0, 0), - Children = {_lblInternals, _btnInternals} - }); + //internals + _lblInternals = new RichTextLabel + { MinSize = new Vector2(200, 0), VerticalAlignment = VAlignment.Center }; + _btnInternals = new Button { Text = Loc.GetString("gas-tank-window-internals-toggle-button") }; - // Separator - _contentContainer.AddChild(new Control + contentContainer.AddChild( + new BoxContainer { - MinSize = new Vector2(0, 10) - }); - - _contentContainer.AddChild(new Label - { - Text = Loc.GetString("gas-tank-window-output-pressure-label"), - Align = Label.AlignMode.Center + Orientation = LayoutOrientation.Horizontal, + Margin = new Thickness(0, 7, 0, 0), + Children = { _lblInternals, _btnInternals } }); - _spbPressure = new FloatSpinBox - { - IsValid = f => f >= 0 || f <= 3000, - Margin = new Thickness(25, 0, 25, 7) - }; - _contentContainer.AddChild(_spbPressure); - - // Handlers - _spbPressure.OnValueChanged += args => - { - _owner.SetOutputPressure(args.Value); - }; - _btnInternals.OnPressed += args => - { - _owner.ToggleInternals(); - }; + // Separator + contentContainer.AddChild(new Control + { + MinSize = new Vector2(0, 10) + }); - btnClose.OnPressed += _ => Close(); - } + contentContainer.AddChild(new Label + { + Text = Loc.GetString("gas-tank-window-output-pressure-label"), + Align = Label.AlignMode.Center + }); + _spbPressure = new FloatSpinBox + { + IsValid = f => f >= 0 || f <= 3000, + Margin = new Thickness(25, 0, 25, 7) + }; + contentContainer.AddChild(_spbPressure); - public void UpdateState(GasTankBoundUserInterfaceState state) + // Handlers + _spbPressure.OnValueChanged += args => { - _lblPressure.SetMarkup(Loc.GetString("gas-tank-window-tank-pressure-text", ("tankPressure", $"{state.TankPressure:0.##}"))); - _btnInternals.Disabled = !state.CanConnectInternals; - _lblInternals.SetMarkup(Loc.GetString("gas-tank-window-internal-text", - ("status", Loc.GetString(state.InternalsConnected ? "gas-tank-window-internal-connected" : "gas-tank-window-internal-disconnected")))); - if (state.OutputPressure.HasValue) - { - _spbPressure.Value = state.OutputPressure.Value; - } - } + owner.SetOutputPressure(args.Value); + }; - protected override DragMode GetDragModeFor(Vector2 relativeMousePos) + _btnInternals.OnPressed += args => { - return DragMode.Move; - } + owner.ToggleInternals(); + }; - protected override bool HasPoint(Vector2 point) + btnClose.OnPressed += _ => Close(); + } + + public void UpdateState(GasTankBoundUserInterfaceState state) + { + _lblPressure.SetMarkup(Loc.GetString("gas-tank-window-tank-pressure-text", ("tankPressure", $"{state.TankPressure:0.##}"))); + _btnInternals.Disabled = !state.CanConnectInternals; + _lblInternals.SetMarkup(Loc.GetString("gas-tank-window-internal-text", + ("status", Loc.GetString(state.InternalsConnected ? "gas-tank-window-internal-connected" : "gas-tank-window-internal-disconnected")))); + if (state.OutputPressure.HasValue) { - return false; + _spbPressure.Value = state.OutputPressure.Value; } } + + protected override DragMode GetDragModeFor(Vector2 relativeMousePos) + { + return DragMode.Move; + } + + protected override bool HasPoint(Vector2 point) + { + return false; + } } diff --git a/Resources/Locale/en-US/atmos/gas-tank-component.ftl b/Resources/Locale/en-US/atmos/gas-tank-component.ftl index ae10d5630c12..1f2d8f1e9f8d 100644 --- a/Resources/Locale/en-US/atmos/gas-tank-component.ftl +++ b/Resources/Locale/en-US/atmos/gas-tank-component.ftl @@ -14,7 +14,6 @@ comp-gas-tank-examine-closed-valve = Gas release valve is [color=green]closed[/c control-verb-open-control-panel-text = Open Control Panel ## UI -gas-tank-window-label = Gas Tank gas-tank-window-internals-toggle-button = Toggle gas-tank-window-output-pressure-label = Output Pressure gas-tank-window-tank-pressure-text = Pressure: {$tankPressure} kPA From 39481fb2b018921bca058f49433422db848cd7ef Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 3 Jun 2024 03:34:36 +0000 Subject: [PATCH 243/568] 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 941b93313644..f30481fee770 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: wafehling - changes: - - message: Added 18 new bounties to cargo bounty system. - type: Add - id: 6171 - time: '2024-03-17T17:06:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26160 - author: Ko4erga changes: - message: Added craftable high and small wooden fences, bench. New stairs. @@ -3851,3 +3844,10 @@ id: 6670 time: '2024-06-03T03:30:00.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28544 +- author: Laneron + changes: + - message: Jetpack UI window now has correctly displayed header + type: Fix + id: 6671 + time: '2024-06-03T03:33:31.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28545 From 0d864d324cd0ca06e06484b38eadd8ea5c06ef5b Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 3 Jun 2024 01:12:13 -0400 Subject: [PATCH 244/568] Add support for HeldPrefix to SolutionContainerVisualsSystem (#28532) --- .../Visualizers/SolutionContainerVisualsSystem.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Content.Client/Chemistry/Visualizers/SolutionContainerVisualsSystem.cs b/Content.Client/Chemistry/Visualizers/SolutionContainerVisualsSystem.cs index f1e8e8d7aa44..17b88fb5a8f5 100644 --- a/Content.Client/Chemistry/Visualizers/SolutionContainerVisualsSystem.cs +++ b/Content.Client/Chemistry/Visualizers/SolutionContainerVisualsSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.Hands; +using Content.Shared.Item; using Content.Shared.Rounding; using Robust.Client.GameObjects; using Robust.Shared.Prototypes; @@ -150,6 +151,9 @@ private void OnGetHeldVisuals(EntityUid uid, SolutionContainerVisualsComponent c if (!TryComp(uid, out AppearanceComponent? appearance)) return; + if (!TryComp(uid, out var item)) + return; + if (!AppearanceSystem.TryGetData(uid, SolutionContainerVisuals.FillFraction, out var fraction, appearance)) return; @@ -159,7 +163,8 @@ private void OnGetHeldVisuals(EntityUid uid, SolutionContainerVisualsComponent c { var layer = new PrototypeLayerData(); - var key = "inhand-" + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite; + var heldPrefix = item.HeldPrefix == null ? "inhand-" : $"{item.HeldPrefix}-inhand-"; + var key = heldPrefix + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite; layer.State = key; From d36ff569ab65692dffdc8a0ac8c7d8b8655bff46 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:21:46 +1200 Subject: [PATCH 245/568] Add PrototypeUploadTest (#28522) --- .../PrototypeTests/PrototypeUploadTest.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs diff --git a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs new file mode 100644 index 000000000000..c641cd9bd1ca --- /dev/null +++ b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs @@ -0,0 +1,85 @@ +using Content.Shared.Tag; +using Robust.Client.Upload.Commands; +using Robust.Shared.GameObjects; +using Robust.Shared.Prototypes; +using Robust.Shared.Upload; + +namespace Content.IntegrationTests.Tests.PrototypeTests; + +public sealed class PrototypeUploadTest +{ + public const string IdA = "UploadTestPrototype"; + public const string IdB = $"{IdA}NoParent"; + public const string IdC = $"{IdA}Abstract"; + public const string IdD = $"{IdA}UploadedParent"; + + private const string File = $@" +- type: entity + parent: BaseStructure # BaseItem can cause AllItemsHaveSpritesTest to fail + id: {IdA} + +- type: entity + id: {IdB} + +- type: entity + id: {IdC} + abstract: true + components: + - type: Tag + +- type: entity + id: {IdD} + parent: {IdC} +"; + + [Test] + [TestOf(typeof(LoadPrototypeCommand))] + public async Task TestFileUpload() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings {Connected = true}); + var sCompFact = pair.Server.ResolveDependency(); + var cCompFact = pair.Client.ResolveDependency(); + + Assert.That(!pair.Server.ProtoMan.TryIndex(IdA, out _)); + Assert.That(!pair.Server.ProtoMan.TryIndex(IdB, out _)); + Assert.That(!pair.Server.ProtoMan.TryIndex(IdC, out _)); + Assert.That(!pair.Server.ProtoMan.TryIndex(IdD, out _)); + + Assert.That(!pair.Client.ProtoMan.TryIndex(IdA, out _)); + Assert.That(!pair.Client.ProtoMan.TryIndex(IdB, out _)); + Assert.That(!pair.Client.ProtoMan.TryIndex(IdC, out _)); + Assert.That(!pair.Client.ProtoMan.TryIndex(IdD, out _)); + + var protoLoad = pair.Client.ResolveDependency(); + await pair.Client.WaitPost(() => protoLoad.SendGamePrototype(File)); + await pair.RunTicksSync(10); + + Assert.That(pair.Server.ProtoMan.TryIndex(IdA, out var sProtoA)); + Assert.That(pair.Server.ProtoMan.TryIndex(IdB, out var sProtoB)); + Assert.That(!pair.Server.ProtoMan.TryIndex(IdC, out _)); + Assert.That(pair.Server.ProtoMan.TryIndex(IdD, out var sProtoD)); + + Assert.That(pair.Client.ProtoMan.TryIndex(IdA, out var cProtoA)); + Assert.That(pair.Client.ProtoMan.TryIndex(IdB, out var cProtoB)); + Assert.That(!pair.Client.ProtoMan.TryIndex(IdC, out _)); + Assert.That(pair.Client.ProtoMan.TryIndex(IdD, out var cProtoD)); + + // Arbitrarily choosing TagComponent to check that inheritance works for uploaded prototypes. + + await pair.Server.WaitPost(() => + { + Assert.That(sProtoA!.TryGetComponent(out _, sCompFact), Is.True); + Assert.That(sProtoB!.TryGetComponent(out _, sCompFact), Is.False); + Assert.That(sProtoD!.TryGetComponent(out _, sCompFact), Is.True); + }); + + await pair.Client.WaitPost(() => + { + Assert.That(cProtoA!.TryGetComponent(out _, cCompFact), Is.True); + Assert.That(cProtoB!.TryGetComponent(out _, cCompFact), Is.False); + Assert.That(cProtoD!.TryGetComponent(out _, cCompFact), Is.True); + }); + + await pair.CleanReturnAsync(); + } +} From 36396e3f864b85783f831dde21f9f94b121b2da5 Mon Sep 17 00:00:00 2001 From: Verm <32827189+Vermidia@users.noreply.github.com> Date: Mon, 3 Jun 2024 06:54:32 -0500 Subject: [PATCH 246/568] Changes SyndiCat spawner to a radio (#28492) --- Resources/Locale/en-US/store/uplink-catalog.ftl | 4 ++-- Resources/Prototypes/Catalog/uplink_catalog.yml | 2 +- .../reinforcement_teleporter.yml | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 3ae2d65c16c3..1b9fa8aff93b 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -370,8 +370,8 @@ uplink-syndicate-sponge-box-desc = A box containing 6 syndicate sponges disguise uplink-slipocalypse-clustersoap-name = Slipocalypse Clustersoap uplink-slipocalypse-clustersoap-desc = Scatters arounds small pieces of syndicate-brand soap after being thrown, these pieces of soap evaporate after 60 seconds. -uplink-mobcat-microbomb-name = SyndiCat -uplink-mobcat-microbomb-desc = A hand cat equipped with a microbomb implant. Explodes when seriously injured. Can bite painfully +uplink-mobcat-microbomb-name = SyndiCat Teleporter +uplink-mobcat-microbomb-desc = Call in a handy cat equipped with a microbomb implant. Explodes when seriously injured. Can bite painfully. uplink-chameleon-projector-name = Chameleon Projector uplink-chameleon-projector-desc = Disappear in plain sight by creating a hologram of an item around you. Do not use this to play the game "Object Search". diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index c8d6eb4f618f..efec2417d0b5 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -999,7 +999,7 @@ name: uplink-mobcat-microbomb-name description: uplink-mobcat-microbomb-desc icon: { sprite: /Textures/Mobs/Pets/cat.rsi, state: syndicat } - productEntity: MobCatSyndy + productEntity: ReinforcementRadioSyndicateSyndiCat cost: Telecrystal: 6 categories: diff --git a/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/reinforcement_teleporter.yml b/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/reinforcement_teleporter.yml index c62783fcee98..2085422c9b55 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/reinforcement_teleporter.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/reinforcement_teleporter.yml @@ -54,6 +54,23 @@ prototype: MobMonkeySyndicateAgentNukeops selectablePrototypes: ["SyndicateMonkeyNukeops", "SyndicateKoboldNukeops"] +- type: entity + parent: ReinforcementRadioSyndicate + id: ReinforcementRadioSyndicateSyndiCat + name: syndicat reinforcement radio + description: Calls in a faithfully trained cat with a microbomb to assist you. + components: + - type: GhostRole + name: ghost-role-information-SyndiCat-name + description: ghost-role-information-SyndiCat-description + rules: ghost-role-information-SyndiCat-rules + raffle: + settings: default + - type: GhostRoleMobSpawner + prototype: MobCatSyndy + - type: EmitSoundOnUse + sound: /Audio/Animals/cat_meow.ogg + - type: entity parent: ReinforcementRadioSyndicate id: ReinforcementRadioSyndicateCyborgAssault # Reinforcement radio exclusive to nukeops uplink From ba5af33ee42b82d7c653e7724ddf67b0a8c3cf1a Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 3 Jun 2024 11:55:38 +0000 Subject: [PATCH 247/568] 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 f30481fee770..7235a60a0dfd 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Ko4erga - changes: - - message: Added craftable high and small wooden fences, bench. New stairs. - type: Add - id: 6172 - time: '2024-03-17T21:18:59.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26182 - author: potato1234_x changes: - message: Changed the puddle sprites so the smoothing is less jagged @@ -3851,3 +3844,10 @@ id: 6671 time: '2024-06-03T03:33:31.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28545 +- author: Vermidia + changes: + - message: SyndiCats now spawn from a radio like other reinforcements. + type: Tweak + id: 6672 + time: '2024-06-03T11:54:32.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28492 From eb026c9b5b582420ba8e97f1bdee02dc5193c736 Mon Sep 17 00:00:00 2001 From: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Date: Mon, 3 Jun 2024 07:05:14 -0500 Subject: [PATCH 248/568] Gives Medibots Random Lines(not a good title) (#28543) --- Resources/Locale/en-US/advertisements/other/medibot.ftl | 9 +++++++++ .../Catalog/VendingMachines/advertisements.yml | 6 ++++++ Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml | 2 ++ 3 files changed, 17 insertions(+) create mode 100644 Resources/Locale/en-US/advertisements/other/medibot.ftl diff --git a/Resources/Locale/en-US/advertisements/other/medibot.ftl b/Resources/Locale/en-US/advertisements/other/medibot.ftl new file mode 100644 index 000000000000..9e919ce49f37 --- /dev/null +++ b/Resources/Locale/en-US/advertisements/other/medibot.ftl @@ -0,0 +1,9 @@ +advertisement-medibot-1 = What kind of medbay is this? Everyone's dropping like dead flies. +advertisement-medibot-2 = I knew it, I should've been a plastic surgeon. +advertisement-medibot-3 = There's always a catch, and I'm the best there is. +advertisement-medibot-4 = An apple a day keeps me away. +advertisement-medibot-5 = I'm different! +advertisement-medibot-6 = Fuck you. +advertisement-medibot-7 = Why are we still here? Just to suffer? +advertisement-medibot-8 = I...I've never lost a patient before. Not today, I mean. +advertisement-medibot-9 = Lexorin in. diff --git a/Resources/Prototypes/Catalog/VendingMachines/advertisements.yml b/Resources/Prototypes/Catalog/VendingMachines/advertisements.yml index 6dbb60af6a97..c0052dba07c5 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/advertisements.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/advertisements.yml @@ -255,3 +255,9 @@ values: prefix: advertisement-virodrobe- count: 3 + +- type: localizedDataset + id: MedibotAds + values: + prefix: advertisement-medibot- + count: 9 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index b2fefb67ae95..12788e457f25 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -287,6 +287,8 @@ interactFailureString: petting-failure-medibot interactSuccessSound: path: /Audio/Ambience/Objects/periodic_beep.ogg + - type: Advertise + pack: MedibotAds - type: entity parent: MobSiliconBase From 64ff99231ad36ccdad2dfd1f1a07370643c7c3b9 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 3 Jun 2024 12:06:20 +0000 Subject: [PATCH 249/568] 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 7235a60a0dfd..784f6f0de02a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: potato1234_x - changes: - - message: Changed the puddle sprites so the smoothing is less jagged - type: Tweak - id: 6173 - time: '2024-03-17T23:15:45.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26171 - author: FungiFellow changes: - message: Changed Monkey Reinf Price to 6TC @@ -3851,3 +3844,10 @@ id: 6672 time: '2024-06-03T11:54:32.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28492 +- author: Cojoke-dot + changes: + - message: Medibots will now occasionally talk + type: Tweak + id: 6673 + time: '2024-06-03T12:05:14.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28543 From a0b49116be679f2bd13c009a70f6aa4ef58b8839 Mon Sep 17 00:00:00 2001 From: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Date: Mon, 3 Jun 2024 07:10:24 -0500 Subject: [PATCH 250/568] Adds Chat Triggers to the Gasp Emote (#28466) --- Resources/Prototypes/Voice/speech_emotes.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Voice/speech_emotes.yml b/Resources/Prototypes/Voice/speech_emotes.yml index e57183697641..e47d6382a1db 100644 --- a/Resources/Prototypes/Voice/speech_emotes.yml +++ b/Resources/Prototypes/Voice/speech_emotes.yml @@ -1,4 +1,4 @@ -# vocal emotes +# vocal emotes - type: emote id: Scream name: chat-emote-name-scream @@ -345,6 +345,16 @@ components: - Respirator chatMessages: ["chat-emote-msg-gasp"] + chatTriggers: + - gasp + - gasp. + - gasp! + - gasps + - gasps. + - gasps! + - gasped + - gasped. + - gasped! - type: emote id: DefaultDeathgasp From f8f55dea1ee00e4e32d3852116d4c45e38694703 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 3 Jun 2024 12:11:31 +0000 Subject: [PATCH 251/568] 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 784f6f0de02a..91acfe32966e 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: FungiFellow - changes: - - message: Changed Monkey Reinf Price to 6TC - type: Tweak - id: 6174 - time: '2024-03-18T01:25:58.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26214 - author: Golinth changes: - message: Criminal record icons now show up below job icons @@ -3851,3 +3844,10 @@ id: 6673 time: '2024-06-03T12:05:14.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28543 +- author: Cojoke-dot + changes: + - message: The player can now use the gasping emote + type: Tweak + id: 6674 + time: '2024-06-03T12:10:25.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28466 From 50f8de08a4ce5f7647ea4cd15ddca4ac2790b55e Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:34:50 +0000 Subject: [PATCH 252/568] prevent nukie kidnapping (#28387) --- Content.Server/Antag/AntagSelectionSystem.cs | 21 +++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index 47da1b6475f3..710bb8f3d765 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -182,20 +182,20 @@ protected override void Started(EntityUid uid, AntagSelectionComponent component .Where(x => GameTicker.PlayerGameStatuses[x.UserId] == PlayerGameStatus.JoinedGame) .ToList(); - ChooseAntags((uid, component), players); + ChooseAntags((uid, component), players, midround: true); } /// /// Chooses antagonists from the given selection of players /// - public void ChooseAntags(Entity ent, IList pool) + public void ChooseAntags(Entity ent, IList pool, bool midround = false) { if (ent.Comp.SelectionsComplete) return; foreach (var def in ent.Comp.Definitions) { - ChooseAntags(ent, pool, def); + ChooseAntags(ent, pool, def, midround: midround); } ent.Comp.SelectionsComplete = true; @@ -204,17 +204,28 @@ public void ChooseAntags(Entity ent, IList /// Chooses antagonists from the given selection of players for the given antag definition. /// - public void ChooseAntags(Entity ent, IList pool, AntagSelectionDefinition def) + /// Disable picking players for pre-spawn antags in the middle of a round + public void ChooseAntags(Entity ent, IList pool, AntagSelectionDefinition def, bool midround = false) { var playerPool = GetPlayerPool(ent, pool, def); var count = GetTargetAntagCount(ent, GetTotalPlayerCount(pool), def); // if there is both a spawner and players getting picked, let it fall back to a spawner. var noSpawner = def.SpawnerPrototype == null; + var picking = def.PickPlayer; + if (midround && ent.Comp.SelectionTime == AntagSelectionTime.PrePlayerSpawn) + { + // prevent antag selection from happening if the round is on-going, requiring a spawner if used midround. + // this is so rules like nukies, if added by an admin midround, dont make random living people nukies + Log.Info($"Antags for rule {ent:?} get picked pre-spawn so only spawners will be made."); + DebugTools.Assert(def.SpawnerPrototype != null, $"Rule {ent:?} had no spawner for pre-spawn rule added mid-round!"); + picking = false; + } + for (var i = 0; i < count; i++) { var session = (ICommonSession?) null; - if (def.PickPlayer) + if (picking) { if (!playerPool.TryPickAndTake(RobustRandom, out session) && noSpawner) { From 977bd5ad6dd1a6fe7a32721f296129cd3bb6bbce Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:39:25 +0000 Subject: [PATCH 253/568] make GameRule use entity category (#28294) --- .../Components/GameRuleComponent.cs | 3 +- Resources/Locale/en-US/entity-categories.ftl | 1 + Resources/Prototypes/Entities/categories.yml | 5 +++ .../Prototypes/GameRules/cargo_gifts.yml | 10 ------ Resources/Prototypes/GameRules/events.yml | 33 ------------------- Resources/Prototypes/GameRules/midround.yml | 1 - Resources/Prototypes/GameRules/roundstart.yml | 15 --------- Resources/Prototypes/GameRules/variation.yml | 8 ----- 8 files changed, 8 insertions(+), 68 deletions(-) diff --git a/Content.Server/GameTicking/Components/GameRuleComponent.cs b/Content.Server/GameTicking/Components/GameRuleComponent.cs index 1e6c3f0ab1df..635452b3f3c3 100644 --- a/Content.Server/GameTicking/Components/GameRuleComponent.cs +++ b/Content.Server/GameTicking/Components/GameRuleComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Destructible.Thresholds; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.GameTicking.Components; @@ -7,7 +8,7 @@ namespace Content.Server.GameTicking.Components; /// Component attached to all gamerule entities. /// Used to both track the entity as well as store basic data /// -[RegisterComponent] +[RegisterComponent, EntityCategory("GameRules")] public sealed partial class GameRuleComponent : Component { /// diff --git a/Resources/Locale/en-US/entity-categories.ftl b/Resources/Locale/en-US/entity-categories.ftl index 190fe5713ac4..457397845dad 100644 --- a/Resources/Locale/en-US/entity-categories.ftl +++ b/Resources/Locale/en-US/entity-categories.ftl @@ -1 +1,2 @@ entity-category-name-actions = Actions +entity-category-name-game-rules = Game Rules diff --git a/Resources/Prototypes/Entities/categories.yml b/Resources/Prototypes/Entities/categories.yml index 2fb56818f944..0535a8a4c27a 100644 --- a/Resources/Prototypes/Entities/categories.yml +++ b/Resources/Prototypes/Entities/categories.yml @@ -2,3 +2,8 @@ id: Actions name: entity-category-name-actions hideSpawnMenu: true + +- type: entityCategory + id: GameRules + name: entity-category-name-game-rules + hideSpawnMenu: true diff --git a/Resources/Prototypes/GameRules/cargo_gifts.yml b/Resources/Prototypes/GameRules/cargo_gifts.yml index c5a525129707..ffc2ed404b70 100644 --- a/Resources/Prototypes/GameRules/cargo_gifts.yml +++ b/Resources/Prototypes/GameRules/cargo_gifts.yml @@ -17,7 +17,6 @@ - type: entity id: GiftsPizzaPartySmall parent: CargoGiftsBase - noSpawn: true components: - type: StationEvent weight: 5 @@ -35,7 +34,6 @@ - type: entity id: GiftsPizzaPartyLarge parent: CargoGiftsBase - noSpawn: true components: - type: StationEvent weight: 2 @@ -53,7 +51,6 @@ - type: entity id: GiftsEngineering parent: CargoGiftsBase - noSpawn: true components: - type: StationEvent weight: 5 @@ -74,7 +71,6 @@ - type: entity id: GiftsVendingRestock parent: CargoGiftsBase - noSpawn: true components: - type: StationEvent weight: 4 @@ -95,7 +91,6 @@ - type: entity id: GiftsJanitor parent: CargoGiftsBase - noSpawn: true components: - type: StationEvent weight: 6 @@ -113,7 +108,6 @@ - type: entity id: GiftsMedical parent: CargoGiftsBase - noSpawn: true components: - type: StationEvent weight: 8 @@ -133,7 +127,6 @@ - type: entity id: GiftsSpacingSupplies parent: CargoGiftsBase - noSpawn: true components: - type: StationEvent weight: 4 @@ -153,7 +146,6 @@ - type: entity id: GiftsFireProtection parent: CargoGiftsBase - noSpawn: true components: - type: StationEvent weight: 4 @@ -171,7 +163,6 @@ - type: entity id: GiftsSecurityGuns parent: CargoGiftsBase - noSpawn: true components: - type: StationEvent weight: 3 @@ -190,7 +181,6 @@ - type: entity id: GiftsSecurityRiot parent: CargoGiftsBase - noSpawn: true components: - type: StationEvent weight: 4 diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 9f6e6a812574..f421cf68183c 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -2,7 +2,6 @@ id: BaseStationEvent parent: BaseGameRule abstract: true - noSpawn: true components: - type: GameRule delay: @@ -13,7 +12,6 @@ id: BaseStationEventShortDelay parent: BaseGameRule abstract: true - noSpawn: true components: - type: GameRule delay: @@ -24,7 +22,6 @@ id: BaseStationEventLongDelay parent: BaseGameRule abstract: true - noSpawn: true components: - type: GameRule delay: @@ -34,7 +31,6 @@ - type: entity id: AnomalySpawn parent: BaseStationEventShortDelay - noSpawn: true components: - type: StationEvent startAnnouncementColor: "#18abf5" @@ -47,7 +43,6 @@ - type: entity id: BluespaceArtifact parent: BaseStationEventShortDelay - noSpawn: true components: - type: GameRule delay: @@ -64,7 +59,6 @@ - type: entity id: BluespaceLocker parent: BaseGameRule - noSpawn: true components: - type: StationEvent weight: 2 @@ -76,7 +70,6 @@ - type: entity id: BreakerFlip parent: BaseGameRule - noSpawn: true components: - type: StationEvent weight: 7 @@ -87,7 +80,6 @@ - type: entity id: BureaucraticError parent: BaseGameRule - noSpawn: true components: - type: StationEvent startAnnouncement: station-event-bureaucratic-error-announcement @@ -99,7 +91,6 @@ - type: entity id: ClericalError parent: BaseGameRule - noSpawn: true components: - type: StationEvent startAnnouncement: station-event-clerical-error-announcement @@ -111,7 +102,6 @@ - type: entity parent: BaseGameRule id: ClosetSkeleton - noSpawn: true components: - type: StationEvent weight: 5 @@ -123,7 +113,6 @@ - type: entity parent: BaseGameRule id: DragonSpawn - noSpawn: true components: - type: StationEvent weight: 6.5 @@ -153,7 +142,6 @@ - type: entity parent: BaseGameRule id: NinjaSpawn - noSpawn: true components: - type: StationEvent weight: 6 @@ -166,7 +154,6 @@ - type: entity parent: BaseGameRule id: RevenantSpawn - noSpawn: true components: - type: StationEvent weight: 7.5 @@ -180,7 +167,6 @@ #- type: entity # id: FalseAlarm # parent: BaseGameRule -# noSpawn: true # components: # - type: StationEvent # weight: 15 @@ -190,7 +176,6 @@ - type: entity id: GasLeak parent: BaseStationEventShortDelay - noSpawn: true components: - type: StationEvent startAnnouncement: station-event-gas-leak-start-announcement @@ -203,7 +188,6 @@ - type: entity id: KudzuGrowth parent: BaseStationEventLongDelay - noSpawn: true components: - type: StationEvent earliestStart: 15 @@ -215,7 +199,6 @@ - type: entity id: MeteorSwarm parent: BaseStationEventLongDelay - noSpawn: true components: - type: StationEvent earliestStart: 30 @@ -233,7 +216,6 @@ - type: entity id: MouseMigration parent: BaseStationEventShortDelay - noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-creatures-start-announcement @@ -259,7 +241,6 @@ - type: entity id: CockroachMigration parent: BaseStationEventShortDelay - noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-creatures-start-announcement @@ -277,7 +258,6 @@ - type: entity id: PowerGridCheck parent: BaseStationEventShortDelay - noSpawn: true components: - type: StationEvent weight: 5 @@ -294,7 +274,6 @@ - type: entity id: RandomSentience parent: BaseGameRule - noSpawn: true components: - type: StationEvent weight: 6 @@ -307,7 +286,6 @@ - type: entity parent: BaseGameRule id: SolarFlare - noSpawn: true components: - type: StationEvent weight: 8 @@ -336,7 +314,6 @@ - type: entity id: VentClog parent: BaseStationEventLongDelay - noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-clog-start-announcement @@ -351,7 +328,6 @@ - type: entity id: SlimesSpawn parent: BaseStationEventShortDelay - noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-creatures-start-announcement @@ -373,7 +349,6 @@ - type: entity id: SpiderSpawn parent: BaseStationEventShortDelay - noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-creatures-start-announcement @@ -391,7 +366,6 @@ - type: entity id: SpiderClownSpawn parent: BaseStationEventShortDelay - noSpawn: true components: - type: StationEvent startAnnouncement: station-event-vent-creatures-start-announcement @@ -409,7 +383,6 @@ - type: entity id: ZombieOutbreak parent: BaseGameRule - noSpawn: true components: - type: StationEvent earliestStart: 50 @@ -441,7 +414,6 @@ prototype: InitialInfected - type: entity - noSpawn: true parent: BaseNukeopsRule id: LoneOpsSpawn components: @@ -475,7 +447,6 @@ prototype: Nukeops - type: entity - noSpawn: true parent: BaseTraitorRule id: SleeperAgentsRule components: @@ -501,7 +472,6 @@ - type: entity id: MassHallucinations parent: BaseGameRule - noSpawn: true components: - type: StationEvent weight: 7 @@ -518,7 +488,6 @@ - type: entity id: ImmovableRodSpawn parent: BaseGameRule - noSpawn: true components: - type: StationEvent startAnnouncement: station-event-immovable-rod-start-announcement @@ -556,7 +525,6 @@ orGroup: rodProto - type: entity - noSpawn: true parent: BaseGameRule id: IonStorm components: @@ -569,7 +537,6 @@ - type: entity id: MimicVendorRule parent: BaseGameRule - noSpawn: true components: - type: StationEvent earliestStart: 0 diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index fe5af117e68b..c190b0333ad6 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -18,7 +18,6 @@ threats: NinjaThreats - type: entity - noSpawn: true parent: BaseGameRule id: Thief components: diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index cf2c5b2f475a..3493749457d5 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -1,12 +1,10 @@ - type: entity id: BaseGameRule abstract: true - noSpawn: true components: - type: GameRule - type: entity - noSpawn: true parent: BaseGameRule id: RespawnDeadRule components: @@ -17,7 +15,6 @@ deleteBody: false - type: entity - noSpawn: true parent: BaseGameRule id: SubGamemodesRule components: @@ -29,7 +26,6 @@ - type: entity id: DeathMatch31 parent: BaseGameRule - noSpawn: true components: - type: DeathMatchRule rewardSpawns: @@ -59,7 +55,6 @@ - type: entity id: InactivityTimeRestart parent: BaseGameRule - noSpawn: true components: - type: InactivityRule inactivityMaxTime: 600 @@ -68,7 +63,6 @@ - type: entity id: MaxTimeRestart parent: BaseGameRule - noSpawn: true components: - type: MaxTimeRestartRule roundMaxTime: 300 @@ -88,7 +82,6 @@ - type: AntagLoadProfileRule - type: entity - noSpawn: true parent: BaseNukeopsRule id: Nukeops components: @@ -166,7 +159,6 @@ agentName: traitor-round-end-agent-name - type: entity - noSpawn: true parent: BaseTraitorRule id: Traitor components: @@ -188,7 +180,6 @@ - type: entity id: Revolutionary parent: BaseGameRule - noSpawn: true components: - type: GameRule minPlayers: 15 @@ -213,21 +204,18 @@ - type: entity id: Sandbox parent: BaseGameRule - noSpawn: true components: - type: SandboxRule - type: entity id: Secret parent: BaseGameRule - noSpawn: true components: - type: SecretRule - type: entity id: Zombie parent: BaseGameRule - noSpawn: true components: - type: GameRule minPlayers: 20 @@ -260,14 +248,12 @@ - type: entity id: BasicStationEventScheduler parent: BaseGameRule - noSpawn: true components: - type: BasicStationEventScheduler - type: entity id: RampingStationEventScheduler parent: BaseGameRule - noSpawn: true components: - type: RampingStationEventScheduler @@ -275,7 +261,6 @@ - type: entity id: BasicRoundstartVariation parent: BaseGameRule - noSpawn: true components: - type: RoundstartStationVariationRule rules: diff --git a/Resources/Prototypes/GameRules/variation.yml b/Resources/Prototypes/GameRules/variation.yml index 2884d5f9d6fa..093a832d72f8 100644 --- a/Resources/Prototypes/GameRules/variation.yml +++ b/Resources/Prototypes/GameRules/variation.yml @@ -4,7 +4,6 @@ id: BaseVariationPass parent: BaseGameRule abstract: true - noSpawn: true components: - type: StationVariationPassRule @@ -13,14 +12,12 @@ - type: entity id: BasicPoweredLightVariationPass parent: BaseVariationPass - noSpawn: true components: - type: PoweredLightVariationPass - type: entity id: SolidWallRustingVariationPass parent: BaseVariationPass - noSpawn: true components: - type: WallReplaceVariationPass - type: EntityReplaceVariationPass @@ -32,7 +29,6 @@ - type: entity id: ReinforcedWallRustingVariationPass parent: BaseVariationPass - noSpawn: true components: - type: ReinforcedWallReplaceVariationPass - type: EntityReplaceVariationPass @@ -44,7 +40,6 @@ - type: entity id: BasicTrashVariationPass parent: BaseVariationPass - noSpawn: true components: - type: EntitySpawnVariationPass tilesPerEntityAverage: 35 @@ -105,7 +100,6 @@ - type: entity id: BasicPuddleMessVariationPass parent: BaseVariationPass - noSpawn: true components: - type: PuddleMessVariationPass randomPuddleSolutionFill: RandomFillTrashPuddle @@ -113,7 +107,6 @@ - type: entity id: BloodbathPuddleMessVariationPass parent: BaseVariationPass - noSpawn: true components: - type: PuddleMessVariationPass tilesPerSpillAverage: 150 @@ -123,7 +116,6 @@ - type: entity id: CutWireVariationPass parent: BaseVariationPass - noSpawn: true components: - type: CutWireVariationPass wireCutChance: 0.01 From e784f2cf3c638d9df1834751924916548389a1ab Mon Sep 17 00:00:00 2001 From: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Date: Mon, 3 Jun 2024 08:04:07 -0500 Subject: [PATCH 254/568] Make projectiles not hit crates unless clicked on (#28072) --- .../RequireProjectileTargetComponent.cs | 14 +++++ .../Systems/RequireProjectileTargetSystem.cs | 51 +++++++++++++++++++ .../Systems/MobStateSystem.Subscribers.cs | 17 ------- Resources/Prototypes/Entities/Mobs/base.yml | 4 +- .../Storage/Crates/base_structurecrates.yml | 1 + 5 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 Content.Shared/Damage/Components/RequireProjectileTargetComponent.cs create mode 100644 Content.Shared/Damage/Systems/RequireProjectileTargetSystem.cs diff --git a/Content.Shared/Damage/Components/RequireProjectileTargetComponent.cs b/Content.Shared/Damage/Components/RequireProjectileTargetComponent.cs new file mode 100644 index 000000000000..5bd8292daae7 --- /dev/null +++ b/Content.Shared/Damage/Components/RequireProjectileTargetComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Damage.Components; + +/// +/// Prevent the object from getting hit by projetiles unless you target the object. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(RequireProjectileTargetSystem))] +public sealed partial class RequireProjectileTargetComponent : Component +{ + [DataField, AutoNetworkedField] + public bool Active = true; +} diff --git a/Content.Shared/Damage/Systems/RequireProjectileTargetSystem.cs b/Content.Shared/Damage/Systems/RequireProjectileTargetSystem.cs new file mode 100644 index 000000000000..79b374a60f35 --- /dev/null +++ b/Content.Shared/Damage/Systems/RequireProjectileTargetSystem.cs @@ -0,0 +1,51 @@ +using Content.Shared.Projectiles; +using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Standing; +using Robust.Shared.Physics.Events; + +namespace Content.Shared.Damage.Components; + +public sealed class RequireProjectileTargetSystem : EntitySystem +{ + public override void Initialize() + { + SubscribeLocalEvent(PreventCollide); + SubscribeLocalEvent(StandingBulletHit); + SubscribeLocalEvent(LayingBulletPass); + } + + private void PreventCollide(Entity ent, ref PreventCollideEvent args) + { + if (args.Cancelled) + return; + + if (!ent.Comp.Active) + return; + + var other = args.OtherEntity; + if (HasComp(other) && + CompOrNull(other)?.Target != ent) + { + args.Cancelled = true; + } + } + + private void SetActive(Entity ent, bool value) + { + if (ent.Comp.Active == value) + return; + + ent.Comp.Active = value; + Dirty(ent); + } + + private void StandingBulletHit(Entity ent, ref StoodEvent args) + { + SetActive(ent, false); + } + + private void LayingBulletPass(Entity ent, ref DownedEvent args) + { + SetActive(ent, true); + } +} diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs index ee747554e14b..08b351e61e85 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -46,7 +46,6 @@ private void SubscribeEvents() SubscribeLocalEvent(OnSleepAttempt); SubscribeLocalEvent(OnCombatModeShouldHandInteract); SubscribeLocalEvent(OnAttemptPacifiedAttack); - SubscribeLocalEvent(OnPreventCollide); } private void OnStateExitSubscribers(EntityUid target, MobStateComponent component, MobState state) @@ -179,21 +178,5 @@ private void OnAttemptPacifiedAttack(Entity ent, ref AttemptP args.Cancelled = true; } - private void OnPreventCollide(Entity ent, ref PreventCollideEvent args) - { - if (args.Cancelled) - return; - - if (IsAlive(ent, ent)) - return; - - var other = args.OtherEntity; - if (HasComp(other) && - CompOrNull(other)?.Target != ent.Owner) - { - args.Cancelled = true; - } - } - #endregion } diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 0a2b68d0a1da..fae47113107d 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -1,4 +1,4 @@ -# The progenitor. This should only container the most basic components possible. +# The progenitor. This should only container the most basic components possible. # Only put things on here if every mob *must* have it. This includes ghosts. - type: entity save: false @@ -43,6 +43,8 @@ - type: MovementSpeedModifier - type: Polymorphable - type: StatusIcon + - type: RequireProjectileTarget + active: False # Used for mobs that have health and can take damage. - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml index 2d84541231eb..01c226cb0fd0 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml @@ -89,6 +89,7 @@ node: crategenericsteel containers: - entity_storage + - type: RequireProjectileTarget - type: entity parent: CrateGeneric From 33d963a149a0b0ad0a7dc29a0c3ef808c98f8bd2 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 3 Jun 2024 13:05:14 +0000 Subject: [PATCH 255/568] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 91acfe32966e..0587709ee3db 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Golinth - changes: - - message: Criminal record icons now show up below job icons - type: Tweak - id: 6175 - time: '2024-03-18T01:37:00.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26203 - author: PJB3005 changes: - message: Fixed pressure damage calculations to what they were always supposed @@ -3851,3 +3844,13 @@ id: 6674 time: '2024-06-03T12:10:25.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28466 +- author: Cojoke-dot + changes: + - message: Bullets no longer hit crates unless directly clicked on while shooting. + type: Tweak + - message: Bullets now pass over mobs that are lying down unless directly clicked + on while shooting. + type: Tweak + id: 6675 + time: '2024-06-03T13:04:07.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28072 From 8d78b7442af48ff87607efbcdfb4692a00678daa Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:23:52 +0000 Subject: [PATCH 256/568] make objectives use entityCategory (#28269) --- .../Components/ObjectiveComponent.cs | 10 +++-- Resources/Locale/en-US/entity-categories.ftl | 1 + Resources/Prototypes/Entities/categories.yml | 5 +++ .../Prototypes/Objectives/base_objectives.yml | 6 +-- Resources/Prototypes/Objectives/dragon.yml | 2 - Resources/Prototypes/Objectives/ninja.yml | 6 --- Resources/Prototypes/Objectives/thief.yml | 41 ------------------- Resources/Prototypes/Objectives/traitor.yml | 19 --------- 8 files changed, 15 insertions(+), 75 deletions(-) diff --git a/Content.Shared/Objectives/Components/ObjectiveComponent.cs b/Content.Shared/Objectives/Components/ObjectiveComponent.cs index 95fbc6856116..36d3fa0bded7 100644 --- a/Content.Shared/Objectives/Components/ObjectiveComponent.cs +++ b/Content.Shared/Objectives/Components/ObjectiveComponent.cs @@ -2,6 +2,7 @@ using Content.Shared.Objectives; using Content.Shared.Objectives.Systems; using Robust.Shared.Utility; +using Robust.Shared.Prototypes; namespace Content.Shared.Objectives.Components; @@ -9,32 +10,33 @@ namespace Content.Shared.Objectives.Components; /// Required component for an objective entity prototype. /// [RegisterComponent, Access(typeof(SharedObjectivesSystem))] +[EntityCategory("Objectives")] public sealed partial class ObjectiveComponent : Component { /// /// Difficulty rating used to avoid assigning too many difficult objectives. /// - [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] + [DataField(required: true)] public float Difficulty; /// /// Organisation that issued this objective, used for grouping and as a header above common objectives. /// - [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] + [DataField(required: true)] public string Issuer = string.Empty; /// /// Unique objectives can only have 1 per prototype id. /// Set this to false if you want multiple objectives of the same prototype. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public bool Unique = true; /// /// Icon of this objective to display in the character menu. /// Can be specified by an handler but is usually done in the prototype. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public SpriteSpecifier? Icon; } diff --git a/Resources/Locale/en-US/entity-categories.ftl b/Resources/Locale/en-US/entity-categories.ftl index 457397845dad..6067830b7aaa 100644 --- a/Resources/Locale/en-US/entity-categories.ftl +++ b/Resources/Locale/en-US/entity-categories.ftl @@ -1,2 +1,3 @@ entity-category-name-actions = Actions entity-category-name-game-rules = Game Rules +entity-category-name-objectives = Objectives \ No newline at end of file diff --git a/Resources/Prototypes/Entities/categories.yml b/Resources/Prototypes/Entities/categories.yml index 0535a8a4c27a..bc4dd104def7 100644 --- a/Resources/Prototypes/Entities/categories.yml +++ b/Resources/Prototypes/Entities/categories.yml @@ -7,3 +7,8 @@ id: GameRules name: entity-category-name-game-rules hideSpawnMenu: true + +- type: entityCategory + id: Objectives + name: entity-category-name-objectives + hideSpawnMenu: true \ No newline at end of file diff --git a/Resources/Prototypes/Objectives/base_objectives.yml b/Resources/Prototypes/Objectives/base_objectives.yml index 2ab5149213a6..1fbd23dfce22 100644 --- a/Resources/Prototypes/Objectives/base_objectives.yml +++ b/Resources/Prototypes/Objectives/base_objectives.yml @@ -1,6 +1,6 @@ # OBJECTIVE STYLE -# in comments anything that says final prototype means the objective that isnt abstract -# the final prototype must be noSpawn to avoid showing in f5 +# in comments anything that says final prototype means the objective that isnt abstract. +# you dont need noSpawn because Objectives category is automatically added, which has hideSpawnmenu # components are listed in this order: # 1. Objective # 2. requirement components @@ -8,7 +8,7 @@ # 4. the condition component # all objectives should inherit this at some point -# then have its difficulty etc fields set in the final objective prototypes +# then have its icon etc fields set in the final objective prototypes - type: entity abstract: true id: BaseObjective diff --git a/Resources/Prototypes/Objectives/dragon.yml b/Resources/Prototypes/Objectives/dragon.yml index 2cf7eb292f79..10ca942cb39a 100644 --- a/Resources/Prototypes/Objectives/dragon.yml +++ b/Resources/Prototypes/Objectives/dragon.yml @@ -13,7 +13,6 @@ - DragonRole - type: entity - noSpawn: true parent: BaseDragonObjective id: CarpRiftsObjective components: @@ -30,7 +29,6 @@ - type: CarpRiftsCondition - type: entity - noSpawn: true parent: [BaseDragonObjective, BaseSurviveObjective] id: DragonSurviveObjective name: Survive diff --git a/Resources/Prototypes/Objectives/ninja.yml b/Resources/Prototypes/Objectives/ninja.yml index bc17992e90df..3c2fbcaca889 100644 --- a/Resources/Prototypes/Objectives/ninja.yml +++ b/Resources/Prototypes/Objectives/ninja.yml @@ -13,7 +13,6 @@ - NinjaRole - type: entity - noSpawn: true parent: BaseNinjaObjective id: DoorjackObjective components: @@ -29,7 +28,6 @@ - type: DoorjackCondition - type: entity - noSpawn: true parent: BaseNinjaObjective id: StealResearchObjective description: Your gloves can be used to hack a research server and steal its precious data. If science has been slacking you'll have to get to work. @@ -45,7 +43,6 @@ - type: StealResearchCondition - type: entity - noSpawn: true parent: [BaseNinjaObjective, BaseCodeObjective] id: SpiderChargeObjective description: This bomb can be detonated in a specific location. Note that the bomb will not work anywhere else! @@ -56,7 +53,6 @@ state: icon - type: entity - noSpawn: true parent: [BaseNinjaObjective, BaseSurviveObjective] id: NinjaSurviveObjective name: Survive @@ -68,7 +64,6 @@ state: icon - type: entity - noSpawn: true parent: [BaseNinjaObjective, BaseCodeObjective] id: TerrorObjective name: Call in a threat @@ -80,7 +75,6 @@ state: red_phone - type: entity - noSpawn: true parent: [BaseNinjaObjective, BaseCodeObjective] id: MassArrestObjective name: Set everyone to wanted diff --git a/Resources/Prototypes/Objectives/thief.yml b/Resources/Prototypes/Objectives/thief.yml index 18154850973b..8b5307e9a093 100644 --- a/Resources/Prototypes/Objectives/thief.yml +++ b/Resources/Prototypes/Objectives/thief.yml @@ -55,7 +55,6 @@ # Collections - type: entity - noSpawn: true parent: BaseThiefStealCollectionObjective id: FigurineStealCollectionObjective components: @@ -67,7 +66,6 @@ difficulty: 0.25 - type: entity - noSpawn: true parent: BaseThiefStealCollectionObjective id: HeadCloakStealCollectionObjective components: @@ -79,7 +77,6 @@ difficulty: 1.5 - type: entity - noSpawn: true parent: BaseThiefStealCollectionObjective id: HeadBedsheetStealCollectionObjective components: @@ -91,7 +88,6 @@ difficulty: 1.0 - type: entity - noSpawn: true parent: BaseThiefStealCollectionObjective id: StampStealCollectionObjective components: @@ -103,7 +99,6 @@ difficulty: 1.0 - type: entity - noSpawn: true parent: BaseThiefStealCollectionObjective id: DoorRemoteStealCollectionObjective components: @@ -115,7 +110,6 @@ difficulty: 1.5 - type: entity - noSpawn: true parent: BaseThiefStealCollectionObjective id: TechnologyDiskStealCollectionObjective components: @@ -130,7 +124,6 @@ difficulty: 0.8 - type: entity - noSpawn: true parent: BaseThiefStealCollectionObjective id: IDCardsStealCollectionObjective components: @@ -145,7 +138,6 @@ - type: entity - noSpawn: true parent: BaseThiefStealCollectionObjective id: LAMPStealCollectionObjective components: @@ -163,7 +155,6 @@ # steal item - type: entity #Security subgroup - noSpawn: true parent: BaseThiefStealObjective id: ForensicScannerStealObjective components: @@ -175,7 +166,6 @@ difficulty: 1 - type: entity - noSpawn: true parent: BaseThiefStealObjective id: FlippoEngravedLighterStealObjective components: @@ -187,7 +177,6 @@ difficulty: 0.8 - type: entity - noSpawn: true parent: BaseThiefStealObjective id: ClothingHeadHatWardenStealObjective components: @@ -197,7 +186,6 @@ difficulty: 1.2 - type: entity #Medical subgroup - noSpawn: true parent: BaseThiefStealObjective id: ClothingOuterHardsuitVoidParamedStealObjective components: @@ -209,7 +197,6 @@ difficulty: 1 - type: entity - noSpawn: true parent: BaseThiefStealObjective id: MedicalTechFabCircuitboardStealObjective components: @@ -221,7 +208,6 @@ difficulty: 1 - type: entity - noSpawn: true parent: BaseThiefStealObjective id: ClothingHeadsetAltMedicalStealObjective components: @@ -233,7 +219,6 @@ difficulty: 1 - type: entity #Engineering subgroup - noSpawn: true parent: BaseThiefStealObjective id: FireAxeStealObjective components: @@ -245,7 +230,6 @@ difficulty: 0.8 - type: entity - noSpawn: true parent: BaseThiefStealObjective id: AmePartFlatpackStealObjective components: @@ -257,7 +241,6 @@ difficulty: 1 - type: entity #Cargo subgroup - noSpawn: true parent: BaseThiefStealObjective id: ExpeditionsCircuitboardStealObjective components: @@ -269,7 +252,6 @@ difficulty: 0.7 - type: entity - noSpawn: true parent: BaseThiefStealObjective id: CargoShuttleCircuitboardStealObjective components: @@ -281,7 +263,6 @@ difficulty: 0.7 - type: entity - noSpawn: true parent: BaseThiefStealObjective id: SalvageShuttleCircuitboardStealObjective components: @@ -293,7 +274,6 @@ difficulty: 0.7 - type: entity #Service subgroup - noSpawn: true parent: BaseThiefStealObjective id: ClothingEyesHudBeerStealObjective components: @@ -305,7 +285,6 @@ difficulty: 0.3 - type: entity - noSpawn: true parent: BaseThiefStealObjective id: BibleStealObjective components: @@ -317,7 +296,6 @@ difficulty: 0.4 - type: entity #Other subgroup - noSpawn: true parent: BaseThiefStealObjective id: ClothingNeckGoldmedalStealObjective components: @@ -329,7 +307,6 @@ difficulty: 1 - type: entity - noSpawn: true parent: BaseThiefStealObjective id: ClothingNeckClownmedalStealObjective components: @@ -343,7 +320,6 @@ # Structures - type: entity - noSpawn: true parent: BaseThiefStealStructureObjective id: NuclearBombStealObjective components: @@ -355,7 +331,6 @@ difficulty: 2.5 #Good luck - type: entity - noSpawn: true parent: BaseThiefStealStructureObjective id: FaxMachineCaptainStealObjective components: @@ -367,7 +342,6 @@ difficulty: 2 - type: entity - noSpawn: true parent: BaseThiefStealStructureObjective id: ChemDispenserStealObjective components: @@ -379,7 +353,6 @@ difficulty: 1 - type: entity - noSpawn: true parent: BaseThiefStealStructureObjective id: XenoArtifactStealObjective components: @@ -391,7 +364,6 @@ difficulty: 0.5 - type: entity - noSpawn: true parent: BaseThiefStealStructureObjective id: FreezerHeaterStealObjective components: @@ -403,7 +375,6 @@ difficulty: 0.5 - type: entity - noSpawn: true parent: BaseThiefStealStructureObjective id: TegStealObjective components: @@ -415,7 +386,6 @@ difficulty: 1 - type: entity - noSpawn: true parent: BaseThiefStealStructureObjective id: BoozeDispenserStealObjective components: @@ -427,7 +397,6 @@ difficulty: 0.5 - type: entity - noSpawn: true parent: BaseThiefStealStructureObjective id: AltarNanotrasenStealObjective components: @@ -439,7 +408,6 @@ difficulty: 0.5 - type: entity - noSpawn: true parent: BaseThiefStealStructureObjective id: PlantRDStealObjective components: @@ -453,7 +421,6 @@ # Animal - type: entity - noSpawn: true parent: BaseThiefStealAnimalObjective id: IanStealObjective components: @@ -465,7 +432,6 @@ difficulty: 2.5 - type: entity - noSpawn: true parent: BaseThiefStealAnimalObjective id: BingusStealObjective components: @@ -475,7 +441,6 @@ difficulty: 1 - type: entity - noSpawn: true parent: BaseThiefStealAnimalObjective id: McGriffStealObjective components: @@ -487,7 +452,6 @@ difficulty: 1 - type: entity - noSpawn: true parent: BaseThiefStealAnimalObjective id: WalterStealObjective components: @@ -499,7 +463,6 @@ difficulty: 1 - type: entity - noSpawn: true parent: BaseThiefStealAnimalObjective id: MortyStealObjective components: @@ -509,7 +472,6 @@ difficulty: 0.5 - type: entity - noSpawn: true parent: BaseThiefStealAnimalObjective id: RenaultStealObjective components: @@ -521,7 +483,6 @@ difficulty: 2 - type: entity - noSpawn: true parent: BaseThiefStealAnimalObjective id: ShivaStealObjective components: @@ -533,7 +494,6 @@ difficulty: 2 - type: entity - noSpawn: true parent: BaseThiefStealAnimalObjective id: TropicoStealObjective components: @@ -547,7 +507,6 @@ # Escape - type: entity - noSpawn: true parent: [BaseThiefObjective, BaseLivingObjective] id: EscapeThiefShuttleObjective name: Escape to centcom alive and unrestrained. diff --git a/Resources/Prototypes/Objectives/traitor.yml b/Resources/Prototypes/Objectives/traitor.yml index 14397535d4d2..ad5f56a443ea 100644 --- a/Resources/Prototypes/Objectives/traitor.yml +++ b/Resources/Prototypes/Objectives/traitor.yml @@ -36,7 +36,6 @@ # state - type: entity - noSpawn: true parent: [BaseTraitorObjective, BaseLivingObjective] id: EscapeShuttleObjective name: Escape to centcom alive and unrestrained. @@ -50,7 +49,6 @@ - type: EscapeShuttleCondition - type: entity - noSpawn: true parent: BaseTraitorObjective id: DieObjective name: Die a glorious death @@ -69,7 +67,6 @@ - type: DieCondition #- type: entity -# noSpawn: true # parent: [BaseTraitorObjective, BaseLivingObjective] # id: HijackShuttleObjective # name: Hijack emergency shuttle @@ -85,7 +82,6 @@ # kill - type: entity - noSpawn: true parent: [BaseTraitorObjective, BaseKillObjective] id: KillRandomPersonObjective description: Do it however you like, just make sure they don't make it to centcom. @@ -98,7 +94,6 @@ - type: PickRandomPerson - type: entity - noSpawn: true parent: [BaseTraitorObjective, BaseKillObjective] id: KillRandomHeadObjective description: We need this head gone and you probably know why. Good luck, agent. @@ -119,7 +114,6 @@ # social - type: entity - noSpawn: true parent: [BaseTraitorSocialObjective, BaseKeepAliveObjective] id: RandomTraitorAliveObjective description: Identify yourself at your own risk. We just need them alive. @@ -131,7 +125,6 @@ - type: RandomTraitorAlive - type: entity - noSpawn: true parent: [BaseTraitorSocialObjective, BaseHelpProgressObjective] id: RandomTraitorProgressObjective description: Identify yourself at your own risk. We just need them to succeed. @@ -157,7 +150,6 @@ owner: job-name-cmo - type: entity - noSpawn: true parent: BaseCMOStealObjective id: CMOHyposprayStealObjective components: @@ -165,7 +157,6 @@ stealGroup: Hypospray - type: entity - noSpawn: true parent: BaseCMOStealObjective id: CMOCrewMonitorStealObjective components: @@ -185,7 +176,6 @@ owner: job-name-rd - type: entity - noSpawn: true parent: BaseRDStealObjective id: RDHardsuitStealObjective components: @@ -196,7 +186,6 @@ difficulty: 3 - type: entity - noSpawn: true parent: BaseRDStealObjective id: HandTeleporterStealObjective components: @@ -206,7 +195,6 @@ ## hos - type: entity - noSpawn: true parent: BaseTraitorStealObjective id: SecretDocumentsStealObjective components: @@ -222,7 +210,6 @@ ## ce - type: entity - noSpawn: true parent: BaseTraitorStealObjective id: MagbootsStealObjective components: @@ -235,7 +222,6 @@ ## qm - type: entity - noSpawn: true parent: BaseTraitorStealObjective id: ClipboardStealObjective components: @@ -248,7 +234,6 @@ ## hop - type: entity - noSpawn: true parent: BaseTraitorStealObjective id: CorgiMeatStealObjective components: @@ -274,7 +259,6 @@ job: Captain - type: entity - noSpawn: true parent: BaseCaptainObjective id: CaptainIDStealObjective components: @@ -282,7 +266,6 @@ stealGroup: CaptainIDCard - type: entity - noSpawn: true parent: BaseCaptainObjective id: CaptainJetpackStealObjective components: @@ -290,7 +273,6 @@ stealGroup: JetpackCaptainFilled - type: entity - noSpawn: true parent: BaseCaptainObjective id: CaptainGunStealObjective components: @@ -299,7 +281,6 @@ owner: job-name-captain - type: entity - noSpawn: true parent: BaseCaptainObjective id: NukeDiskStealObjective components: From def04cdaf52ee4e39fe269d3a92d94d02e1e3dc0 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Mon, 3 Jun 2024 06:30:47 -0700 Subject: [PATCH 257/568] Add more info to GettingAttackedAttemptEvent (#28548) --- Content.Shared/ActionBlocker/ActionBlockerSystem.cs | 6 +----- .../Interaction/Events/GettingAttackedAttemptEvent.cs | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index d2883b5ef5be..f1c77fda43b2 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -1,13 +1,9 @@ -using Content.Shared.Bed.Sleep; using Content.Shared.Body.Events; -using Content.Shared.DragDrop; using Content.Shared.Emoting; using Content.Shared.Hands; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Item; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; using Content.Shared.Movement.Components; using Content.Shared.Movement.Events; using Content.Shared.Speech; @@ -194,7 +190,7 @@ public bool CanAttack(EntityUid uid, EntityUid? target = null, Entity [ByRefEvent] -public record struct GettingAttackedAttemptEvent(bool Cancelled); +public record struct GettingAttackedAttemptEvent(EntityUid Attacker, EntityUid? Weapon, bool Disarm, bool Cancelled = false); From 0b4388d5d423669f7a3d431ea4f983f3ef50a06d Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Tue, 4 Jun 2024 01:36:07 +1200 Subject: [PATCH 258/568] Try fix KeyNotFoundException in KillTrackingSystem (#28553) --- .../KillTracking/KillTrackingSystem.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Content.Server/KillTracking/KillTrackingSystem.cs b/Content.Server/KillTracking/KillTrackingSystem.cs index 63627fd1b971..ba27ea5d9e5a 100644 --- a/Content.Server/KillTracking/KillTrackingSystem.cs +++ b/Content.Server/KillTracking/KillTrackingSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Damage; using Content.Shared.FixedPoint; using Content.Shared.Mobs; +using Content.Shared.Mobs.Systems; using Robust.Shared.Player; namespace Content.Server.KillTracking; @@ -14,7 +15,8 @@ public sealed class KillTrackingSystem : EntitySystem /// public override void Initialize() { - SubscribeLocalEvent(OnDamageChanged); + // Add damage to LifetimeDamage before MobStateChangedEvent gets raised + SubscribeLocalEvent(OnDamageChanged, before: [ typeof(MobThresholdSystem) ]); SubscribeLocalEvent(OnMobStateChanged); } @@ -50,7 +52,7 @@ private void OnMobStateChanged(EntityUid uid, KillTrackerComponent component, Mo var largestSource = GetLargestSource(component.LifetimeDamage); largestSource ??= killImpulse; - KillSource? killSource; + KillSource killSource; KillSource? assistSource = null; if (killImpulse is KillEnvironmentSource) @@ -69,13 +71,13 @@ private void OnMobStateChanged(EntityUid uid, KillTrackerComponent component, Mo killSource = killImpulse; // no assist is given to environmental kills - if (largestSource is not KillEnvironmentSource) + if (largestSource is not KillEnvironmentSource + && component.LifetimeDamage.TryGetValue(largestSource, out var largestDamage)) { - // you have to do at least 50% of largest source's damage to get the assist. - if (component.LifetimeDamage[largestSource] >= component.LifetimeDamage[killSource] / 2) - { + var killDamage = component.LifetimeDamage.GetValueOrDefault(killSource); + // you have to do at least twice as much damage as the killing source to get the assist. + if (largestDamage >= killDamage / 2) assistSource = largestSource; - } } } From 87ffbab4614ff616447d7a89d161a699d5750013 Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:37:30 +0200 Subject: [PATCH 259/568] Remove locale related changelog (#28547) --- Resources/Changelog/Changelog.yml | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0587709ee3db..cab645fa8d43 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -3787,25 +3787,18 @@ id: 6666 time: '2024-06-02T17:30:27.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28518 -- author: Voomra - changes: - - message: localize Pray UI - type: Fix - id: 6667 - time: '2024-06-02T23:51:19.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/28535 - author: Whisper changes: - message: Renamed the player-obtainable "admin cloak" to "weh cloak". type: Tweak - id: 6668 + id: 6667 time: '2024-06-03T02:18:49.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28540 - author: Vermidia changes: - message: Glass shards are no longer weldable with an unlit welder. type: Fix - id: 6669 + id: 6668 time: '2024-06-03T03:28:53.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27959 - author: AJCM-git @@ -3813,35 +3806,35 @@ - message: Biomass reclaimers no longer act as a void to any unfortunate belongings a corpse may be wearing type: Tweak - id: 6670 + id: 6669 time: '2024-06-03T03:30:00.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28544 - author: Laneron changes: - message: Jetpack UI window now has correctly displayed header type: Fix - id: 6671 + id: 6670 time: '2024-06-03T03:33:31.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28545 - author: Vermidia changes: - message: SyndiCats now spawn from a radio like other reinforcements. type: Tweak - id: 6672 + id: 6671 time: '2024-06-03T11:54:32.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28492 - author: Cojoke-dot changes: - message: Medibots will now occasionally talk type: Tweak - id: 6673 + id: 6672 time: '2024-06-03T12:05:14.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28543 - author: Cojoke-dot changes: - message: The player can now use the gasping emote type: Tweak - id: 6674 + id: 6673 time: '2024-06-03T12:10:25.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28466 - author: Cojoke-dot @@ -3851,6 +3844,6 @@ - message: Bullets now pass over mobs that are lying down unless directly clicked on while shooting. type: Tweak - id: 6675 + id: 6674 time: '2024-06-03T13:04:07.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28072 From 8c105810109a076c30518f0b122b58fcac354bd7 Mon Sep 17 00:00:00 2001 From: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:12:21 -0400 Subject: [PATCH 260/568] Cleans up StatusIconSystem and fixing some bugs (#28270) --- .../UI/AgentIDCardBoundUserInterface.cs | 4 +- .../Access/UI/AgentIDCardWindow.xaml.cs | 8 +-- Content.Client/Antag/AntagStatusIconSystem.cs | 55 ------------------- .../Commands/ShowHealthBarsCommand.cs | 1 + Content.Client/LateJoin/LateJoinGui.cs | 2 +- .../Lobby/UI/HumanoidProfileEditor.xaml.cs | 2 +- .../Overlays/EntityHealthBarOverlay.cs | 26 +++++---- .../Overlays/ShowCriminalRecordIconsSystem.cs | 4 +- .../Overlays/ShowHealthBarsSystem.cs | 14 ++++- .../Overlays/ShowHealthIconsSystem.cs | 3 +- .../Overlays/ShowHungerIconsSystem.cs | 2 +- Content.Client/Overlays/ShowJobIconsSystem.cs | 2 +- .../Overlays/ShowMindShieldIconsSystem.cs | 4 +- .../Overlays/ShowSyndicateIconsSystem.cs | 3 +- .../Overlays/ShowThirstIconsSystem.cs | 2 +- .../Revolutionary/RevolutionarySystem.cs | 37 +++++-------- .../SSDIndicator/SSDIndicatorSystem.cs | 3 +- .../StatusIcon/StatusIconOverlay.cs | 4 +- Content.Client/StatusIcon/StatusIconSystem.cs | 36 +++++++++++- Content.Client/Stealth/StealthSystem.cs | 3 +- Content.Client/Zombies/ZombieSystem.cs | 48 +++++++--------- .../Access/Components/AgentIDCardComponent.cs | 6 +- .../Access/Systems/AgentIDCardSystem.cs | 8 +-- .../Access/Systems/IdCardConsoleSystem.cs | 2 +- .../Access/Systems/PresetIdCardSystem.cs | 4 +- .../Revolutionary/RevolutionarySystem.cs | 5 ++ .../Station/Systems/StationSpawningSystem.cs | 4 +- .../Access/SharedAgentIDCardSystem.cs | 10 ++-- .../Antag/IAntagStatusIconComponent.cs | 12 ---- .../Antag/ShowAntagIconsComponent.cs | 9 +++ .../Overlays/ShowHealthBarsComponent.cs | 5 ++ .../Components/HeadRevolutionaryComponent.cs | 5 +- .../Components/RevolutionaryComponent.cs | 5 +- .../Components/ShowRevIconsComponent.cs | 11 ---- .../SharedRevolutionarySystem.cs | 22 +++----- Content.Shared/Roles/JobPrototype.cs | 4 +- .../SSDIndicator/SSDIndicatorComponent.cs | 5 +- .../Components/StatusIconComponent.cs | 14 +---- .../StatusIcon/StatusIconPrototype.cs | 42 +++++++++++--- .../Zombies/InitialInfectedComponent.cs | 8 +-- .../Zombies/ShowZombieIconsComponent.cs | 12 ---- Content.Shared/Zombies/ZombieComponent.cs | 5 +- .../Entities/Mobs/Player/admin_ghost.yml | 3 +- .../{ => StatusIcon}/StatusEffects/health.yml | 0 .../{ => StatusIcon}/StatusEffects/hunger.yml | 0 .../{ => StatusIcon}/StatusEffects/ssd.yml | 0 Resources/Prototypes/StatusIcon/antag.yml | 17 ++++++ .../{StatusEffects => StatusIcon}/job.yml | 0 .../security.yml | 0 49 files changed, 223 insertions(+), 258 deletions(-) delete mode 100644 Content.Client/Antag/AntagStatusIconSystem.cs create mode 100644 Content.Server/Revolutionary/RevolutionarySystem.cs delete mode 100644 Content.Shared/Antag/IAntagStatusIconComponent.cs create mode 100644 Content.Shared/Antag/ShowAntagIconsComponent.cs delete mode 100644 Content.Shared/Revolutionary/Components/ShowRevIconsComponent.cs delete mode 100644 Content.Shared/Zombies/ShowZombieIconsComponent.cs rename Resources/Prototypes/{ => StatusIcon}/StatusEffects/health.yml (100%) rename Resources/Prototypes/{ => StatusIcon}/StatusEffects/hunger.yml (100%) rename Resources/Prototypes/{ => StatusIcon}/StatusEffects/ssd.yml (100%) rename Resources/Prototypes/{StatusEffects => StatusIcon}/job.yml (100%) rename Resources/Prototypes/{StatusEffects => StatusIcon}/security.yml (100%) diff --git a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs index c3fac8cb92a5..761f52988a9d 100644 --- a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs +++ b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs @@ -1,5 +1,7 @@ using Content.Shared.Access.Systems; +using Content.Shared.StatusIcon; using Robust.Client.GameObjects; +using Robust.Shared.Prototypes; namespace Content.Client.Access.UI { @@ -40,7 +42,7 @@ private void OnJobChanged(string newJob) SendMessage(new AgentIDCardJobChangedMessage(newJob)); } - public void OnJobIconChanged(string newJobIconId) + public void OnJobIconChanged(ProtoId newJobIconId) { SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId)); } diff --git a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs index 9a38c0c48530..6d0b2a184f49 100644 --- a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs +++ b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs @@ -38,7 +38,7 @@ public AgentIDCardWindow(AgentIDCardBoundUserInterface bui) JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text); } - public void SetAllowedIcons(HashSet icons, string currentJobIconId) + public void SetAllowedIcons(HashSet> icons, string currentJobIconId) { IconGrid.DisposeAllChildren(); @@ -46,10 +46,8 @@ public void SetAllowedIcons(HashSet icons, string currentJobIconId) var i = 0; foreach (var jobIconId in icons) { - if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon)) - { + if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon)) continue; - } String styleBase = StyleBase.ButtonOpenBoth; var modulo = i % JobIconColumnCount; @@ -77,7 +75,7 @@ public void SetAllowedIcons(HashSet icons, string currentJobIconId) }; jobIconButton.AddChild(jobIconTexture); - jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID); + jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIconId); IconGrid.AddChild(jobIconButton); if (jobIconId.Equals(currentJobIconId)) diff --git a/Content.Client/Antag/AntagStatusIconSystem.cs b/Content.Client/Antag/AntagStatusIconSystem.cs deleted file mode 100644 index 804ae21ad4a4..000000000000 --- a/Content.Client/Antag/AntagStatusIconSystem.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Content.Shared.Antag; -using Content.Shared.Revolutionary.Components; -using Content.Shared.StatusIcon; -using Content.Shared.StatusIcon.Components; -using Content.Shared.Zombies; -using Robust.Client.Player; -using Robust.Shared.Prototypes; - -namespace Content.Client.Antag; - -/// -/// Used for assigning specified icons for antags. -/// -public sealed class AntagStatusIconSystem : SharedStatusIconSystem -{ - [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IPlayerManager _player = default!; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(GetRevIcon); - SubscribeLocalEvent(GetIcon); - SubscribeLocalEvent(GetIcon); - SubscribeLocalEvent(GetIcon); - } - - /// - /// Adds a Status Icon on an entity if the player is supposed to see it. - /// - private void GetIcon(EntityUid uid, T comp, ref GetStatusIconsEvent ev) where T: IAntagStatusIconComponent - { - var ent = _player.LocalSession?.AttachedEntity; - - var canEv = new CanDisplayStatusIconsEvent(ent); - RaiseLocalEvent(uid, ref canEv); - - if (!canEv.Cancelled) - ev.StatusIcons.Add(_prototype.Index(comp.StatusIcon)); - } - - - /// - /// Adds the Rev Icon on an entity if the player is supposed to see it. This additional function is needed to deal - /// with a special case where if someone is a head rev we only want to display the headrev icon. - /// - private void GetRevIcon(EntityUid uid, RevolutionaryComponent comp, ref GetStatusIconsEvent ev) - { - if (HasComp(uid)) - return; - - GetIcon(uid, comp, ref ev); - - } -} diff --git a/Content.Client/Commands/ShowHealthBarsCommand.cs b/Content.Client/Commands/ShowHealthBarsCommand.cs index bd3e21718f0a..ef918313a0d1 100644 --- a/Content.Client/Commands/ShowHealthBarsCommand.cs +++ b/Content.Client/Commands/ShowHealthBarsCommand.cs @@ -35,6 +35,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) var showHealthBarsComponent = new ShowHealthBarsComponent { DamageContainers = args.ToList(), + HealthStatusIcon = "", NetSyncEnabled = false }; diff --git a/Content.Client/LateJoin/LateJoinGui.cs b/Content.Client/LateJoin/LateJoinGui.cs index ba9351d6746b..252aa9aafad4 100644 --- a/Content.Client/LateJoin/LateJoinGui.cs +++ b/Content.Client/LateJoin/LateJoinGui.cs @@ -244,7 +244,7 @@ private void RebuildUI() VerticalAlignment = VAlignment.Center }; - var jobIcon = _prototypeManager.Index(prototype.Icon); + var jobIcon = _prototypeManager.Index(prototype.Icon); icon.Texture = _sprites.Frame0(jobIcon.Icon); jobSelector.AddChild(icon); diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index eb182c83ee11..53c332c1857f 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -806,7 +806,7 @@ public void RefreshJobs() TextureScale = new Vector2(2, 2), VerticalAlignment = VAlignment.Center }; - var jobIcon = _prototypeManager.Index(job.Icon); + var jobIcon = _prototypeManager.Index(job.Icon); icon.Texture = jobIcon.Icon.Frame0(); selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon); diff --git a/Content.Client/Overlays/EntityHealthBarOverlay.cs b/Content.Client/Overlays/EntityHealthBarOverlay.cs index 758bb562f92a..55978d98f7e2 100644 --- a/Content.Client/Overlays/EntityHealthBarOverlay.cs +++ b/Content.Client/Overlays/EntityHealthBarOverlay.cs @@ -1,14 +1,17 @@ using System.Numerics; +using Content.Client.StatusIcon; using Content.Client.UserInterface.Systems; using Content.Shared.Damage; using Content.Shared.FixedPoint; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.StatusIcon; using Content.Shared.StatusIcon.Components; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Enums; +using Robust.Shared.Prototypes; using static Robust.Shared.Maths.Color; namespace Content.Client.Overlays; @@ -19,19 +22,27 @@ namespace Content.Client.Overlays; public sealed class EntityHealthBarOverlay : Overlay { private readonly IEntityManager _entManager; + private readonly IPrototypeManager _prototype; + private readonly SharedTransformSystem _transform; private readonly MobStateSystem _mobStateSystem; private readonly MobThresholdSystem _mobThresholdSystem; + private readonly StatusIconSystem _statusIconSystem; private readonly ProgressColorSystem _progressColor; + + public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; public HashSet DamageContainers = new(); + public ProtoId StatusIcon; - public EntityHealthBarOverlay(IEntityManager entManager) + public EntityHealthBarOverlay(IEntityManager entManager, IPrototypeManager prototype) { _entManager = entManager; + _prototype = prototype; _transform = _entManager.System(); _mobStateSystem = _entManager.System(); _mobThresholdSystem = _entManager.System(); + _statusIconSystem = _entManager.System(); _progressColor = _entManager.System(); } @@ -44,6 +55,7 @@ protected override void Draw(in OverlayDrawArgs args) const float scale = 1f; var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale)); var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation); + _prototype.TryIndex(StatusIcon, out var statusIcon); var query = _entManager.AllEntityQueryEnumerator(); while (query.MoveNext(out var uid, @@ -52,31 +64,23 @@ protected override void Draw(in OverlayDrawArgs args) out var damageableComponent, out var spriteComponent)) { - if (_entManager.TryGetComponent(uid, out var metaDataComponent) && - metaDataComponent.Flags.HasFlag(MetaDataFlags.InContainer)) - { + if (statusIcon != null && !_statusIconSystem.IsVisible((uid, _entManager.GetComponent(uid)), statusIcon)) continue; - } + // We want the stealth user to still be able to see his health bar himself if (!xformQuery.TryGetComponent(uid, out var xform) || xform.MapID != args.MapId) - { continue; - } if (damageableComponent.DamageContainerID == null || !DamageContainers.Contains(damageableComponent.DamageContainerID)) - { continue; - } // we use the status icon component bounds if specified otherwise use sprite var bounds = _entManager.GetComponentOrNull(uid)?.Bounds ?? spriteComponent.Bounds; var worldPos = _transform.GetWorldPosition(xform, xformQuery); if (!bounds.Translated(worldPos).Intersects(args.WorldAABB)) - { continue; - } // we are all progressing towards death every day if (CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent) is not { } deathProgress) diff --git a/Content.Client/Overlays/ShowCriminalRecordIconsSystem.cs b/Content.Client/Overlays/ShowCriminalRecordIconsSystem.cs index 8f23cd510cb3..c353b1727295 100644 --- a/Content.Client/Overlays/ShowCriminalRecordIconsSystem.cs +++ b/Content.Client/Overlays/ShowCriminalRecordIconsSystem.cs @@ -19,10 +19,10 @@ public override void Initialize() private void OnGetStatusIconsEvent(EntityUid uid, CriminalRecordComponent component, ref GetStatusIconsEvent ev) { - if (!IsActive || ev.InContainer) + if (!IsActive) return; - if (_prototype.TryIndex(component.StatusIcon.Id, out var iconPrototype)) + if (_prototype.TryIndex(component.StatusIcon, out var iconPrototype)) ev.StatusIcons.Add(iconPrototype); } } diff --git a/Content.Client/Overlays/ShowHealthBarsSystem.cs b/Content.Client/Overlays/ShowHealthBarsSystem.cs index 170f552cf3ff..1eb712a8988f 100644 --- a/Content.Client/Overlays/ShowHealthBarsSystem.cs +++ b/Content.Client/Overlays/ShowHealthBarsSystem.cs @@ -2,6 +2,8 @@ using Content.Shared.Overlays; using Robust.Client.Graphics; using System.Linq; +using Robust.Client.Player; +using Robust.Shared.Prototypes; namespace Content.Client.Overlays; @@ -11,6 +13,7 @@ namespace Content.Client.Overlays; public sealed class ShowHealthBarsSystem : EquipmentHudSystem { [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; private EntityHealthBarOverlay _overlay = default!; @@ -18,16 +21,21 @@ public override void Initialize() { base.Initialize(); - _overlay = new(EntityManager); + _overlay = new(EntityManager, _prototype); } protected override void UpdateInternal(RefreshEquipmentHudEvent component) { base.UpdateInternal(component); - foreach (var damageContainerId in component.Components.SelectMany(x => x.DamageContainers)) + foreach (var comp in component.Components) { - _overlay.DamageContainers.Add(damageContainerId); + foreach (var damageContainerId in comp.DamageContainers) + { + _overlay.DamageContainers.Add(damageContainerId); + } + + _overlay.StatusIcon = comp.HealthStatusIcon; } if (!_overlayMan.HasOverlay()) diff --git a/Content.Client/Overlays/ShowHealthIconsSystem.cs b/Content.Client/Overlays/ShowHealthIconsSystem.cs index a546cf4d8281..d8af91482b3e 100644 --- a/Content.Client/Overlays/ShowHealthIconsSystem.cs +++ b/Content.Client/Overlays/ShowHealthIconsSystem.cs @@ -24,7 +24,6 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnGetStatusIconsEvent); - } protected override void UpdateInternal(RefreshEquipmentHudEvent component) @@ -46,7 +45,7 @@ protected override void DeactivateInternal() private void OnGetStatusIconsEvent(Entity entity, ref GetStatusIconsEvent args) { - if (!IsActive || args.InContainer) + if (!IsActive) return; var healthIcons = DecideHealthIcons(entity); diff --git a/Content.Client/Overlays/ShowHungerIconsSystem.cs b/Content.Client/Overlays/ShowHungerIconsSystem.cs index b1c0f3a1a0c9..6b0d575a8102 100644 --- a/Content.Client/Overlays/ShowHungerIconsSystem.cs +++ b/Content.Client/Overlays/ShowHungerIconsSystem.cs @@ -18,7 +18,7 @@ public override void Initialize() private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent component, ref GetStatusIconsEvent ev) { - if (!IsActive || ev.InContainer) + if (!IsActive) return; if (_hunger.TryGetStatusIconPrototype(component, out var iconPrototype)) diff --git a/Content.Client/Overlays/ShowJobIconsSystem.cs b/Content.Client/Overlays/ShowJobIconsSystem.cs index e24b99f3e87d..a6d4f70af6b4 100644 --- a/Content.Client/Overlays/ShowJobIconsSystem.cs +++ b/Content.Client/Overlays/ShowJobIconsSystem.cs @@ -25,7 +25,7 @@ public override void Initialize() private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent ev) { - if (!IsActive || ev.InContainer) + if (!IsActive) return; var iconId = JobIconForNoId; diff --git a/Content.Client/Overlays/ShowMindShieldIconsSystem.cs b/Content.Client/Overlays/ShowMindShieldIconsSystem.cs index 8bf39b875f66..cdb9c54fdfa4 100644 --- a/Content.Client/Overlays/ShowMindShieldIconsSystem.cs +++ b/Content.Client/Overlays/ShowMindShieldIconsSystem.cs @@ -19,10 +19,10 @@ public override void Initialize() private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev) { - if (!IsActive || ev.InContainer) + if (!IsActive) return; - if (_prototype.TryIndex(component.MindShieldStatusIcon.Id, out var iconPrototype)) + if (_prototype.TryIndex(component.MindShieldStatusIcon, out var iconPrototype)) ev.StatusIcons.Add(iconPrototype); } } diff --git a/Content.Client/Overlays/ShowSyndicateIconsSystem.cs b/Content.Client/Overlays/ShowSyndicateIconsSystem.cs index 660ef198e147..782178a29dbe 100644 --- a/Content.Client/Overlays/ShowSyndicateIconsSystem.cs +++ b/Content.Client/Overlays/ShowSyndicateIconsSystem.cs @@ -19,11 +19,10 @@ public override void Initialize() private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent component, ref GetStatusIconsEvent ev) { - if (!IsActive || ev.InContainer) + if (!IsActive) return; if (_prototype.TryIndex(component.SyndStatusIcon, out var iconPrototype)) ev.StatusIcons.Add(iconPrototype); } } - diff --git a/Content.Client/Overlays/ShowThirstIconsSystem.cs b/Content.Client/Overlays/ShowThirstIconsSystem.cs index b08aa4340b21..44be1f7a67f5 100644 --- a/Content.Client/Overlays/ShowThirstIconsSystem.cs +++ b/Content.Client/Overlays/ShowThirstIconsSystem.cs @@ -18,7 +18,7 @@ public override void Initialize() private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent component, ref GetStatusIconsEvent ev) { - if (!IsActive || ev.InContainer) + if (!IsActive) return; if (_thirst.TryGetStatusIconPrototype(component, out var iconPrototype)) diff --git a/Content.Client/Revolutionary/RevolutionarySystem.cs b/Content.Client/Revolutionary/RevolutionarySystem.cs index 682c73f93e78..8e7e687fa8fc 100644 --- a/Content.Client/Revolutionary/RevolutionarySystem.cs +++ b/Content.Client/Revolutionary/RevolutionarySystem.cs @@ -1,44 +1,37 @@ -using Content.Shared.Antag; using Content.Shared.Revolutionary.Components; -using Content.Shared.Ghost; +using Content.Shared.Revolutionary; using Content.Shared.StatusIcon.Components; +using Robust.Shared.Prototypes; namespace Content.Client.Revolutionary; /// /// Used for the client to get status icons from other revs. /// -public sealed class RevolutionarySystem : EntitySystem +public sealed class RevolutionarySystem : SharedRevolutionarySystem { + [Dependency] private readonly IPrototypeManager _prototype = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnCanShowRevIcon); - SubscribeLocalEvent(OnCanShowRevIcon); + SubscribeLocalEvent(GetRevIcon); + SubscribeLocalEvent(GetHeadRevIcon); } - /// - /// Determine whether a client should display the rev icon. - /// - private void OnCanShowRevIcon(EntityUid uid, T comp, ref CanDisplayStatusIconsEvent args) where T : IAntagStatusIconComponent + private void GetRevIcon(Entity ent, ref GetStatusIconsEvent args) { - args.Cancelled = !CanDisplayIcon(args.User, comp.IconVisibleToGhost); + if (HasComp(ent)) + return; + + if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype)) + args.StatusIcons.Add(iconPrototype); } - /// - /// The criteria that determine whether a client should see Rev/Head rev icons. - /// - private bool CanDisplayIcon(EntityUid? uid, bool visibleToGhost) + private void GetHeadRevIcon(Entity ent, ref GetStatusIconsEvent args) { - if (HasComp(uid) || HasComp(uid)) - return true; - - if (visibleToGhost && HasComp(uid)) - return true; - - return HasComp(uid); + if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype)) + args.StatusIcons.Add(iconPrototype); } - } diff --git a/Content.Client/SSDIndicator/SSDIndicatorSystem.cs b/Content.Client/SSDIndicator/SSDIndicatorSystem.cs index 587450a2f66b..e7311953170b 100644 --- a/Content.Client/SSDIndicator/SSDIndicatorSystem.cs +++ b/Content.Client/SSDIndicator/SSDIndicatorSystem.cs @@ -30,13 +30,12 @@ private void OnGetStatusIcon(EntityUid uid, SSDIndicatorComponent component, ref { if (component.IsSSD && _cfg.GetCVar(CCVars.ICShowSSDIndicator) && - !args.InContainer && !_mobState.IsDead(uid) && !HasComp(uid) && TryComp(uid, out var mindContainer) && mindContainer.ShowExamineInfo) { - args.StatusIcons.Add(_prototype.Index(component.Icon)); + args.StatusIcons.Add(_prototype.Index(component.Icon)); } } } diff --git a/Content.Client/StatusIcon/StatusIconOverlay.cs b/Content.Client/StatusIcon/StatusIconOverlay.cs index f1473bda7a2a..4b3daae22fd8 100644 --- a/Content.Client/StatusIcon/StatusIconOverlay.cs +++ b/Content.Client/StatusIcon/StatusIconOverlay.cs @@ -45,7 +45,7 @@ protected override void Draw(in OverlayDrawArgs args) var query = _entity.AllEntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp, out var sprite, out var xform, out var meta)) { - if (xform.MapID != args.MapId) + if (xform.MapID != args.MapId || !sprite.Visible) continue; var bounds = comp.Bounds ?? sprite.Bounds; @@ -72,6 +72,8 @@ protected override void Draw(in OverlayDrawArgs args) foreach (var proto in icons) { + if (!_statusIcon.IsVisible((uid, meta), proto)) + continue; var curTime = _timing.RealTime; var texture = _sprite.GetFrame(proto.Icon, curTime); diff --git a/Content.Client/StatusIcon/StatusIconSystem.cs b/Content.Client/StatusIcon/StatusIconSystem.cs index 980fd9f2a929..63f57767695e 100644 --- a/Content.Client/StatusIcon/StatusIconSystem.cs +++ b/Content.Client/StatusIcon/StatusIconSystem.cs @@ -1,7 +1,11 @@ using Content.Shared.CCVar; +using Content.Shared.Ghost; using Content.Shared.StatusIcon; using Content.Shared.StatusIcon.Components; +using Content.Shared.Stealth.Components; +using Content.Shared.Whitelist; using Robust.Client.Graphics; +using Robust.Client.Player; using Robust.Shared.Configuration; namespace Content.Client.StatusIcon; @@ -13,6 +17,8 @@ public sealed class StatusIconSystem : SharedStatusIconSystem { [Dependency] private readonly IConfigurationManager _configuration = default!; [Dependency] private readonly IOverlayManager _overlay = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; private bool _globalEnabled; private bool _localEnabled; @@ -54,10 +60,34 @@ public List GetStatusIcons(EntityUid uid, MetaDataComponent? met if (meta.EntityLifeStage >= EntityLifeStage.Terminating) return list; - var inContainer = (meta.Flags & MetaDataFlags.InContainer) != 0; - var ev = new GetStatusIconsEvent(list, inContainer); + var ev = new GetStatusIconsEvent(list); RaiseLocalEvent(uid, ref ev); return ev.StatusIcons; } -} + /// + /// For overlay to check if an entity can be seen. + /// + public bool IsVisible(Entity ent, StatusIconData data) + { + var viewer = _playerManager.LocalSession?.AttachedEntity; + + // Always show our icons to our entity + if (viewer == ent.Owner) + return true; + + if (data.VisibleToGhosts && HasComp(viewer)) + return true; + + if (data.HideInContainer && (ent.Comp.Flags & MetaDataFlags.InContainer) != 0) + return false; + + if (data.HideOnStealth && TryComp(ent, out var stealth) && stealth.Enabled) + return false; + + if (data.ShowTo != null && !_entityWhitelist.IsValid(data.ShowTo, viewer)) + return false; + + return true; + } +} diff --git a/Content.Client/Stealth/StealthSystem.cs b/Content.Client/Stealth/StealthSystem.cs index b60ffc2a4089..0b94e41f6bc6 100644 --- a/Content.Client/Stealth/StealthSystem.cs +++ b/Content.Client/Stealth/StealthSystem.cs @@ -1,4 +1,5 @@ using Content.Client.Interactable.Components; +using Content.Client.StatusIcon; using Content.Shared.Stealth; using Content.Shared.Stealth.Components; using Robust.Client.GameObjects; @@ -18,6 +19,7 @@ public override void Initialize() base.Initialize(); _shader = _protoMan.Index("Stealth").InstanceUnique(); + SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShaderRender); @@ -93,4 +95,3 @@ private void OnShaderRender(EntityUid uid, StealthComponent component, BeforePos args.Sprite.Color = new Color(visibility, visibility, 1, 1); } } - diff --git a/Content.Client/Zombies/ZombieSystem.cs b/Content.Client/Zombies/ZombieSystem.cs index 49b5d6aec184..d250e418504f 100644 --- a/Content.Client/Zombies/ZombieSystem.cs +++ b/Content.Client/Zombies/ZombieSystem.cs @@ -1,59 +1,53 @@ using System.Linq; using Content.Shared.Ghost; using Content.Shared.Humanoid; +using Content.Shared.StatusIcon; using Content.Shared.StatusIcon.Components; using Content.Shared.Zombies; using Robust.Client.GameObjects; +using Robust.Shared.Prototypes; namespace Content.Client.Zombies; -public sealed class ZombieSystem : EntitySystem +public sealed class ZombieSystem : SharedZombieSystem { + [Dependency] private readonly IPrototypeManager _prototype = default!; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(OnCanDisplayStatusIcons); - SubscribeLocalEvent(OnCanDisplayStatusIcons); + SubscribeLocalEvent(GetZombieIcon); + SubscribeLocalEvent(GetInitialInfectedIcon); } - private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartup args) + private void GetZombieIcon(Entity ent, ref GetStatusIconsEvent args) { - if (HasComp(uid)) - return; - - if (!TryComp(uid, out var sprite)) - return; - - for (var i = 0; i < sprite.AllLayers.Count(); i++) - { - sprite.LayerSetColor(i, component.SkinColor); - } + var iconPrototype = _prototype.Index(ent.Comp.StatusIcon); + args.StatusIcons.Add(iconPrototype); } - /// - /// Determines whether a player should be able to see the StatusIcon for zombies. - /// - private void OnCanDisplayStatusIcons(EntityUid uid, ZombieComponent component, ref CanDisplayStatusIconsEvent args) + private void GetInitialInfectedIcon(Entity ent, ref GetStatusIconsEvent args) { - if (HasComp(args.User) || HasComp(args.User) || HasComp(args.User)) - return; - - if (component.IconVisibleToGhost && HasComp(args.User)) + if (HasComp(ent)) return; - args.Cancelled = true; + var iconPrototype = _prototype.Index(ent.Comp.StatusIcon); + args.StatusIcons.Add(iconPrototype); } - private void OnCanDisplayStatusIcons(EntityUid uid, InitialInfectedComponent component, ref CanDisplayStatusIconsEvent args) + private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartup args) { - if (HasComp(args.User) && !HasComp(args.User)) + if (HasComp(uid)) return; - if (component.IconVisibleToGhost && HasComp(args.User)) + if (!TryComp(uid, out var sprite)) return; - args.Cancelled = true; + for (var i = 0; i < sprite.AllLayers.Count(); i++) + { + sprite.LayerSetColor(i, component.SkinColor); + } } } diff --git a/Content.Server/Access/Components/AgentIDCardComponent.cs b/Content.Server/Access/Components/AgentIDCardComponent.cs index 4b92b43ea992..7a97c5f5653f 100644 --- a/Content.Server/Access/Components/AgentIDCardComponent.cs +++ b/Content.Server/Access/Components/AgentIDCardComponent.cs @@ -1,5 +1,5 @@ using Content.Shared.StatusIcon; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; +using Robust.Shared.Prototypes; namespace Content.Server.Access.Components { @@ -9,7 +9,7 @@ public sealed partial class AgentIDCardComponent : Component /// /// Set of job icons that the agent ID card can show. /// - [DataField("icons", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet Icons = new(); + [DataField] + public HashSet> Icons; } } diff --git a/Content.Server/Access/Systems/AgentIDCardSystem.cs b/Content.Server/Access/Systems/AgentIDCardSystem.cs index d5e9dc357ddf..f0ce3acbdb88 100644 --- a/Content.Server/Access/Systems/AgentIDCardSystem.cs +++ b/Content.Server/Access/Systems/AgentIDCardSystem.cs @@ -90,14 +90,10 @@ private void OnNameChanged(EntityUid uid, AgentIDCardComponent comp, AgentIDCard private void OnJobIconChanged(EntityUid uid, AgentIDCardComponent comp, AgentIDCardJobIconChangedMessage args) { if (!TryComp(uid, out var idCard)) - { return; - } - if (!_prototypeManager.TryIndex(args.JobIconId, out var jobIcon)) - { + if (!_prototypeManager.TryIndex(args.JobIconId, out var jobIcon)) return; - } _cardSystem.TryChangeJobIcon(uid, jobIcon, idCard); @@ -109,7 +105,7 @@ private bool TryFindJobProtoFromIcon(StatusIconPrototype jobIcon, [NotNullWhen(t { foreach (var jobPrototype in _prototypeManager.EnumeratePrototypes()) { - if(jobPrototype.Icon == jobIcon.ID) + if (jobPrototype.Icon == jobIcon.ID) { job = jobPrototype; return true; diff --git a/Content.Server/Access/Systems/IdCardConsoleSystem.cs b/Content.Server/Access/Systems/IdCardConsoleSystem.cs index 4e63c93a837d..e02664f2bbdf 100644 --- a/Content.Server/Access/Systems/IdCardConsoleSystem.cs +++ b/Content.Server/Access/Systems/IdCardConsoleSystem.cs @@ -129,7 +129,7 @@ private void TryWriteToTargetId(EntityUid uid, _idCard.TryChangeJobTitle(targetId, newJobTitle, player: player); if (_prototype.TryIndex(newJobProto, out var job) - && _prototype.TryIndex(job.Icon, out var jobIcon)) + && _prototype.TryIndex(job.Icon, out var jobIcon)) { _idCard.TryChangeJobIcon(targetId, jobIcon, player: player); _idCard.TryChangeJobDepartment(targetId, job); diff --git a/Content.Server/Access/Systems/PresetIdCardSystem.cs b/Content.Server/Access/Systems/PresetIdCardSystem.cs index 3e775b9c35da..426e523243c3 100644 --- a/Content.Server/Access/Systems/PresetIdCardSystem.cs +++ b/Content.Server/Access/Systems/PresetIdCardSystem.cs @@ -82,9 +82,7 @@ private void SetupIdAccess(EntityUid uid, PresetIdCardComponent id, bool extende _cardSystem.TryChangeJobTitle(uid, job.LocalizedName); _cardSystem.TryChangeJobDepartment(uid, job); - if (_prototypeManager.TryIndex(job.Icon, out var jobIcon)) - { + if (_prototypeManager.TryIndex(job.Icon, out var jobIcon)) _cardSystem.TryChangeJobIcon(uid, jobIcon); - } } } diff --git a/Content.Server/Revolutionary/RevolutionarySystem.cs b/Content.Server/Revolutionary/RevolutionarySystem.cs new file mode 100644 index 000000000000..597c4896b90a --- /dev/null +++ b/Content.Server/Revolutionary/RevolutionarySystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Revolutionary; + +namespace Content.Server.Revolutionary; + +public sealed class RevolutionarySystem : SharedRevolutionarySystem; diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index f175565a5a0e..557a1f95662e 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -257,10 +257,8 @@ public void SetPdaAndIdCardData(EntityUid entity, string characterName, JobProto _cardSystem.TryChangeFullName(cardId, characterName, card); _cardSystem.TryChangeJobTitle(cardId, jobPrototype.LocalizedName, card); - if (_prototypeManager.TryIndex(jobPrototype.Icon, out var jobIcon)) - { + if (_prototypeManager.TryIndex(jobPrototype.Icon, out var jobIcon)) _cardSystem.TryChangeJobIcon(cardId, jobIcon, card); - } var extendedAccess = false; if (station != null) diff --git a/Content.Shared/Access/SharedAgentIDCardSystem.cs b/Content.Shared/Access/SharedAgentIDCardSystem.cs index d027a3937f5a..91aa626fe398 100644 --- a/Content.Shared/Access/SharedAgentIDCardSystem.cs +++ b/Content.Shared/Access/SharedAgentIDCardSystem.cs @@ -1,3 +1,5 @@ +using Content.Shared.StatusIcon; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Access.Systems @@ -23,12 +25,12 @@ public enum AgentIDCardUiKey : byte [Serializable, NetSerializable] public sealed class AgentIDCardBoundUserInterfaceState : BoundUserInterfaceState { - public readonly HashSet Icons; + public readonly HashSet> Icons; public string CurrentName { get; } public string CurrentJob { get; } public string CurrentJobIconId { get; } - public AgentIDCardBoundUserInterfaceState(string currentName, string currentJob, string currentJobIconId, HashSet icons) + public AgentIDCardBoundUserInterfaceState(string currentName, string currentJob, string currentJobIconId, HashSet> icons) { Icons = icons; CurrentName = currentName; @@ -62,9 +64,9 @@ public AgentIDCardJobChangedMessage(string job) [Serializable, NetSerializable] public sealed class AgentIDCardJobIconChangedMessage : BoundUserInterfaceMessage { - public string JobIconId { get; } + public ProtoId JobIconId { get; } - public AgentIDCardJobIconChangedMessage(string jobIconId) + public AgentIDCardJobIconChangedMessage(ProtoId jobIconId) { JobIconId = jobIconId; } diff --git a/Content.Shared/Antag/IAntagStatusIconComponent.cs b/Content.Shared/Antag/IAntagStatusIconComponent.cs deleted file mode 100644 index 981937c91630..000000000000 --- a/Content.Shared/Antag/IAntagStatusIconComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Content.Shared.StatusIcon; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Antag; - -public interface IAntagStatusIconComponent -{ - public ProtoId StatusIcon { get; set; } - - public bool IconVisibleToGhost { get; set; } -} - diff --git a/Content.Shared/Antag/ShowAntagIconsComponent.cs b/Content.Shared/Antag/ShowAntagIconsComponent.cs new file mode 100644 index 000000000000..c451b69c3e0c --- /dev/null +++ b/Content.Shared/Antag/ShowAntagIconsComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Antag; + +/// +/// Determines whether Someone can see antags icons +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class ShowAntagIconsComponent: Component; diff --git a/Content.Shared/Overlays/ShowHealthBarsComponent.cs b/Content.Shared/Overlays/ShowHealthBarsComponent.cs index 48e3162269a6..1297d5683854 100644 --- a/Content.Shared/Overlays/ShowHealthBarsComponent.cs +++ b/Content.Shared/Overlays/ShowHealthBarsComponent.cs @@ -1,5 +1,7 @@ using Content.Shared.Damage.Prototypes; +using Content.Shared.StatusIcon; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Shared.Overlays; @@ -15,4 +17,7 @@ public sealed partial class ShowHealthBarsComponent : Component /// [DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer))] public List DamageContainers = new(); + + [DataField] + public ProtoId HealthStatusIcon = "HealthIconFine"; } diff --git a/Content.Shared/Revolutionary/Components/HeadRevolutionaryComponent.cs b/Content.Shared/Revolutionary/Components/HeadRevolutionaryComponent.cs index d2c8374fefee..ef2bad65e0de 100644 --- a/Content.Shared/Revolutionary/Components/HeadRevolutionaryComponent.cs +++ b/Content.Shared/Revolutionary/Components/HeadRevolutionaryComponent.cs @@ -9,7 +9,7 @@ namespace Content.Shared.Revolutionary.Components; /// Component used for marking a Head Rev for conversion and winning/losing. /// [RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))] -public sealed partial class HeadRevolutionaryComponent : Component, IAntagStatusIconComponent +public sealed partial class HeadRevolutionaryComponent : Component { /// /// The status icon corresponding to the head revolutionary. @@ -24,7 +24,4 @@ public sealed partial class HeadRevolutionaryComponent : Component, IAntagStatus public TimeSpan StunTime = TimeSpan.FromSeconds(3); public override bool SessionSpecific => true; - - [DataField] - public bool IconVisibleToGhost { get; set; } = true; } diff --git a/Content.Shared/Revolutionary/Components/RevolutionaryComponent.cs b/Content.Shared/Revolutionary/Components/RevolutionaryComponent.cs index 73f533cf6906..1f6b45ddea19 100644 --- a/Content.Shared/Revolutionary/Components/RevolutionaryComponent.cs +++ b/Content.Shared/Revolutionary/Components/RevolutionaryComponent.cs @@ -10,7 +10,7 @@ namespace Content.Shared.Revolutionary.Components; /// Used for marking regular revs as well as storing icon prototypes so you can see fellow revs. /// [RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))] -public sealed partial class RevolutionaryComponent : Component, IAntagStatusIconComponent +public sealed partial class RevolutionaryComponent : Component { /// /// The status icon prototype displayed for revolutionaries @@ -25,7 +25,4 @@ public sealed partial class RevolutionaryComponent : Component, IAntagStatusIcon public SoundSpecifier RevStartSound = new SoundPathSpecifier("/Audio/Ambience/Antag/headrev_start.ogg"); public override bool SessionSpecific => true; - - [DataField] - public bool IconVisibleToGhost { get; set; } = true; } diff --git a/Content.Shared/Revolutionary/Components/ShowRevIconsComponent.cs b/Content.Shared/Revolutionary/Components/ShowRevIconsComponent.cs deleted file mode 100644 index 11e20db3f89b..000000000000 --- a/Content.Shared/Revolutionary/Components/ShowRevIconsComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Revolutionary.Components; - -/// -/// Determines whether Someone can see rev/headrev icons on revs. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class ShowRevIconsComponent: Component -{ -} diff --git a/Content.Shared/Revolutionary/SharedRevolutionarySystem.cs b/Content.Shared/Revolutionary/SharedRevolutionarySystem.cs index ddaf73fcc9f9..bbf91193cc31 100644 --- a/Content.Shared/Revolutionary/SharedRevolutionarySystem.cs +++ b/Content.Shared/Revolutionary/SharedRevolutionarySystem.cs @@ -1,4 +1,3 @@ -using Content.Shared.Ghost; using Content.Shared.IdentityManagement; using Content.Shared.Mindshield.Components; using Content.Shared.Popups; @@ -6,10 +5,11 @@ using Content.Shared.Stunnable; using Robust.Shared.GameStates; using Robust.Shared.Player; +using Content.Shared.Antag; namespace Content.Shared.Revolutionary; -public sealed class SharedRevolutionarySystem : EntitySystem +public abstract class SharedRevolutionarySystem : EntitySystem { [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedStunSystem _sharedStun = default!; @@ -23,7 +23,7 @@ public override void Initialize() SubscribeLocalEvent(OnRevCompGetStateAttempt); SubscribeLocalEvent(DirtyRevComps); SubscribeLocalEvent(DirtyRevComps); - SubscribeLocalEvent(DirtyRevComps); + SubscribeLocalEvent(DirtyRevComps); } /// @@ -52,7 +52,7 @@ private void MindShieldImplanted(EntityUid uid, MindShieldComponent comp, MapIni /// private void OnRevCompGetStateAttempt(EntityUid uid, HeadRevolutionaryComponent comp, ref ComponentGetStateAttemptEvent args) { - args.Cancelled = !CanGetState(args.Player, comp.IconVisibleToGhost); + args.Cancelled = !CanGetState(args.Player); } /// @@ -60,30 +60,24 @@ private void OnRevCompGetStateAttempt(EntityUid uid, HeadRevolutionaryComponent /// private void OnRevCompGetStateAttempt(EntityUid uid, RevolutionaryComponent comp, ref ComponentGetStateAttemptEvent args) { - args.Cancelled = !CanGetState(args.Player, comp.IconVisibleToGhost); + args.Cancelled = !CanGetState(args.Player); } /// /// The criteria that determine whether a Rev/HeadRev component should be sent to a client. /// /// The Player the component will be sent to. - /// Whether the component permits the icon to be visible to observers. /// - private bool CanGetState(ICommonSession? player, bool visibleToGhosts) + private bool CanGetState(ICommonSession? player) { //Apparently this can be null in replays so I am just returning true. - if (player is null) + if (player?.AttachedEntity is not {} uid) return true; - var uid = player.AttachedEntity; - if (HasComp(uid) || HasComp(uid)) return true; - if (visibleToGhosts && HasComp(uid)) - return true; - - return HasComp(uid); + return HasComp(uid); } /// /// Dirties all the Rev components so they are sent to clients. diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index 2959b564972a..0b3bfb44384c 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -97,8 +97,8 @@ public sealed partial class JobPrototype : IPrototype [DataField("jobEntity", customTypeSerializer: typeof(PrototypeIdSerializer))] public string? JobEntity = null; - [DataField("icon", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Icon { get; private set; } = "JobIconUnknown"; + [DataField] + public ProtoId Icon { get; private set; } = "JobIconUnknown"; [DataField("special", serverOnly: true)] public JobSpecial[] Special { get; private set; } = Array.Empty(); diff --git a/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs b/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs index 66310505a1a4..ee67e3296f68 100644 --- a/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs +++ b/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.StatusIcon; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.SSDIndicator; @@ -16,6 +17,6 @@ public sealed partial class SSDIndicatorComponent : Component public bool IsSSD = true; [ViewVariables(VVAccess.ReadWrite)] - [DataField("icon", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Icon = "SSDIcon"; + [DataField] + public ProtoId Icon = "SSDIcon"; } diff --git a/Content.Shared/StatusIcon/Components/StatusIconComponent.cs b/Content.Shared/StatusIcon/Components/StatusIconComponent.cs index 385f9760c659..c56be7c96a52 100644 --- a/Content.Shared/StatusIcon/Components/StatusIconComponent.cs +++ b/Content.Shared/StatusIcon/Components/StatusIconComponent.cs @@ -24,16 +24,4 @@ public sealed partial class StatusIconComponent : Component /// /// [ByRefEvent] -public record struct GetStatusIconsEvent(List StatusIcons, bool InContainer); - -/// -/// Event raised on the Client-side to determine whether to display a status icon on an entity. -/// -/// The player that will see the icons -[ByRefEvent] -public record struct CanDisplayStatusIconsEvent(EntityUid? User = null) -{ - public EntityUid? User = User; - - public bool Cancelled = false; -} +public record struct GetStatusIconsEvent(List StatusIcons); diff --git a/Content.Shared/StatusIcon/StatusIconPrototype.cs b/Content.Shared/StatusIcon/StatusIconPrototype.cs index 2bd13b9361d3..29a1b60114f2 100644 --- a/Content.Shared/StatusIcon/StatusIconPrototype.cs +++ b/Content.Shared/StatusIcon/StatusIconPrototype.cs @@ -1,3 +1,5 @@ +using Content.Shared.Stealth.Components; +using Content.Shared.Whitelist; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; @@ -15,26 +17,45 @@ public partial class StatusIconData : IComparable /// /// The icon that's displayed on the entity. /// - [DataField("icon", required: true)] + [DataField(required: true)] public SpriteSpecifier Icon = default!; /// /// A priority for the order in which the icons will be displayed. /// - [DataField("priority")] + [DataField] public int Priority = 10; + /// + /// Whether or not to hide the icon to ghosts + /// + [DataField] + public bool VisibleToGhosts = true; + + /// + /// Whether or not to hide the icon when we are inside a container like a locker or a crate. + /// + [DataField] + public bool HideInContainer = true; + + /// + /// Whether or not to hide the icon when the entity has an active + /// + [DataField] + public bool HideOnStealth = true; + + /// + /// Specifies what entities and components/tags this icon can be shown to. + /// + [DataField] + public EntityWhitelist? ShowTo; + /// /// A preference for where the icon will be displayed. None | Left | Right /// - [DataField("locationPreference")] + [DataField] public StatusIconLocationPreference LocationPreference = StatusIconLocationPreference.None; - public int CompareTo(StatusIconData? other) - { - return Priority.CompareTo(other?.Priority ?? int.MaxValue); - } - /// /// The layer the icon is displayed on. Mod is drawn above Base. Base | Mod /// @@ -52,6 +73,11 @@ public int CompareTo(StatusIconData? other) /// [DataField] public bool IsShaded = false; + + public int CompareTo(StatusIconData? other) + { + return Priority.CompareTo(other?.Priority ?? int.MaxValue); + } } /// diff --git a/Content.Shared/Zombies/InitialInfectedComponent.cs b/Content.Shared/Zombies/InitialInfectedComponent.cs index 3200dd7f5ee7..3149de63bddf 100644 --- a/Content.Shared/Zombies/InitialInfectedComponent.cs +++ b/Content.Shared/Zombies/InitialInfectedComponent.cs @@ -1,4 +1,3 @@ -using Content.Shared.Antag; using Content.Shared.StatusIcon; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -6,11 +5,8 @@ namespace Content.Shared.Zombies; [RegisterComponent, NetworkedComponent] -public sealed partial class InitialInfectedComponent : Component, IAntagStatusIconComponent +public sealed partial class InitialInfectedComponent : Component { - [DataField("initialInfectedStatusIcon")] - public ProtoId StatusIcon { get; set; } = "InitialInfectedFaction"; - [DataField] - public bool IconVisibleToGhost { get; set; } = true; + public ProtoId StatusIcon = "InitialInfectedFaction"; } diff --git a/Content.Shared/Zombies/ShowZombieIconsComponent.cs b/Content.Shared/Zombies/ShowZombieIconsComponent.cs deleted file mode 100644 index a2bc85c07461..000000000000 --- a/Content.Shared/Zombies/ShowZombieIconsComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Zombies; - -/// -/// Makes it so an entity can view ZombieAntagIcons. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class ShowZombieIconsComponent: Component -{ - -} diff --git a/Content.Shared/Zombies/ZombieComponent.cs b/Content.Shared/Zombies/ZombieComponent.cs index 3673a2c51d58..2cd0cdb96d77 100644 --- a/Content.Shared/Zombies/ZombieComponent.cs +++ b/Content.Shared/Zombies/ZombieComponent.cs @@ -14,7 +14,7 @@ namespace Content.Shared.Zombies; [RegisterComponent, NetworkedComponent] -public sealed partial class ZombieComponent : Component, IAntagStatusIconComponent +public sealed partial class ZombieComponent : Component { /// /// The baseline infection chance you have if you are completely nude @@ -97,9 +97,6 @@ public sealed partial class ZombieComponent : Component, IAntagStatusIconCompone [DataField("zombieStatusIcon")] public ProtoId StatusIcon { get; set; } = "ZombieFaction"; - [DataField] - public bool IconVisibleToGhost { get; set; } = true; - /// /// Healing each second /// diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index 4a83593bc342..8b0f60ca58aa 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -84,8 +84,7 @@ - type: Stripping - type: SolutionScanner - type: IgnoreUIRange - - type: ShowRevIcons - - type: ShowZombieIcons + - type: ShowAntagIcons - type: Inventory templateId: aghost - type: InventorySlots diff --git a/Resources/Prototypes/StatusEffects/health.yml b/Resources/Prototypes/StatusIcon/StatusEffects/health.yml similarity index 100% rename from Resources/Prototypes/StatusEffects/health.yml rename to Resources/Prototypes/StatusIcon/StatusEffects/health.yml diff --git a/Resources/Prototypes/StatusEffects/hunger.yml b/Resources/Prototypes/StatusIcon/StatusEffects/hunger.yml similarity index 100% rename from Resources/Prototypes/StatusEffects/hunger.yml rename to Resources/Prototypes/StatusIcon/StatusEffects/hunger.yml diff --git a/Resources/Prototypes/StatusEffects/ssd.yml b/Resources/Prototypes/StatusIcon/StatusEffects/ssd.yml similarity index 100% rename from Resources/Prototypes/StatusEffects/ssd.yml rename to Resources/Prototypes/StatusIcon/StatusEffects/ssd.yml diff --git a/Resources/Prototypes/StatusIcon/antag.yml b/Resources/Prototypes/StatusIcon/antag.yml index 0dbdfce4f97c..0da545b8a203 100644 --- a/Resources/Prototypes/StatusIcon/antag.yml +++ b/Resources/Prototypes/StatusIcon/antag.yml @@ -1,6 +1,11 @@ - type: statusIcon id: ZombieFaction priority: 11 + showTo: + components: + - ShowAntagIcons + - Zombie + - InitialInfected icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Zombie @@ -8,6 +13,10 @@ - type: statusIcon id: InitialInfectedFaction priority: 11 + showTo: + components: + - ShowAntagIcons + - InitialInfected icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: InitialInfected @@ -15,6 +24,10 @@ - type: statusIcon id: RevolutionaryFaction priority: 11 + showTo: + components: + - ShowAntagIcons + - Revolutionary icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Revolutionary @@ -22,6 +35,10 @@ - type: statusIcon id: HeadRevolutionaryFaction priority: 11 + showTo: + components: + - ShowAntagIcons + - Revolutionary icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: HeadRevolutionary diff --git a/Resources/Prototypes/StatusEffects/job.yml b/Resources/Prototypes/StatusIcon/job.yml similarity index 100% rename from Resources/Prototypes/StatusEffects/job.yml rename to Resources/Prototypes/StatusIcon/job.yml diff --git a/Resources/Prototypes/StatusEffects/security.yml b/Resources/Prototypes/StatusIcon/security.yml similarity index 100% rename from Resources/Prototypes/StatusEffects/security.yml rename to Resources/Prototypes/StatusIcon/security.yml From 1e04b3c9f492e82bf28da9e9e97bcad65bb01c67 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 3 Jun 2024 16:13:28 +0000 Subject: [PATCH 261/568] Automatic changelog update --- Resources/Changelog/Changelog.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index cab645fa8d43..f2d31318272d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -3847,3 +3847,11 @@ id: 6674 time: '2024-06-03T13:04:07.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28072 +- author: AJCM-git + changes: + - message: Ninja stealth mode, stealth boxes and everything with stealth won't show + up in equipment HUDs like the medical or security HUD anymore. + type: Fix + id: 6675 + time: '2024-06-03T16:12:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28270 From 619d82ed426c50a72d71ca195d901c11b09adea9 Mon Sep 17 00:00:00 2001 From: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:22:26 -0400 Subject: [PATCH 262/568] Small code cleanup to health bar (#28554) --- Content.Client/Commands/ShowHealthBarsCommand.cs | 2 +- Content.Client/Overlays/EntityHealthBarOverlay.cs | 2 +- Content.Shared/Overlays/ShowHealthBarsComponent.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Client/Commands/ShowHealthBarsCommand.cs b/Content.Client/Commands/ShowHealthBarsCommand.cs index ef918313a0d1..0811f9666378 100644 --- a/Content.Client/Commands/ShowHealthBarsCommand.cs +++ b/Content.Client/Commands/ShowHealthBarsCommand.cs @@ -35,7 +35,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) var showHealthBarsComponent = new ShowHealthBarsComponent { DamageContainers = args.ToList(), - HealthStatusIcon = "", + HealthStatusIcon = null, NetSyncEnabled = false }; diff --git a/Content.Client/Overlays/EntityHealthBarOverlay.cs b/Content.Client/Overlays/EntityHealthBarOverlay.cs index 55978d98f7e2..4f928437395a 100644 --- a/Content.Client/Overlays/EntityHealthBarOverlay.cs +++ b/Content.Client/Overlays/EntityHealthBarOverlay.cs @@ -33,7 +33,7 @@ public sealed class EntityHealthBarOverlay : Overlay public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; public HashSet DamageContainers = new(); - public ProtoId StatusIcon; + public ProtoId? StatusIcon; public EntityHealthBarOverlay(IEntityManager entManager, IPrototypeManager prototype) { diff --git a/Content.Shared/Overlays/ShowHealthBarsComponent.cs b/Content.Shared/Overlays/ShowHealthBarsComponent.cs index 1297d5683854..4229e27af604 100644 --- a/Content.Shared/Overlays/ShowHealthBarsComponent.cs +++ b/Content.Shared/Overlays/ShowHealthBarsComponent.cs @@ -19,5 +19,5 @@ public sealed partial class ShowHealthBarsComponent : Component public List DamageContainers = new(); [DataField] - public ProtoId HealthStatusIcon = "HealthIconFine"; + public ProtoId? HealthStatusIcon = "HealthIconFine"; } From b5e8a696225a3c790939b67a832067caa1354c67 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 3 Jun 2024 12:24:32 -0400 Subject: [PATCH 263/568] Add a test for sliceable cargo bounty exploits (#28357) --- Content.IntegrationTests/Tests/CargoTest.cs | 79 +++++++++++++++++++ .../Cargo/Systems/CargoSystem.Bounty.cs | 17 +++- .../Prototypes/Catalog/Bounties/bounties.yml | 6 ++ 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/Content.IntegrationTests/Tests/CargoTest.cs b/Content.IntegrationTests/Tests/CargoTest.cs index 09f179cf4f5a..8e1d536054f2 100644 --- a/Content.IntegrationTests/Tests/CargoTest.cs +++ b/Content.IntegrationTests/Tests/CargoTest.cs @@ -3,8 +3,13 @@ using System.Numerics; using Content.Server.Cargo.Components; using Content.Server.Cargo.Systems; +using Content.Server.Nutrition.Components; +using Content.Server.Nutrition.EntitySystems; using Content.Shared.Cargo.Prototypes; +using Content.Shared.IdentityManagement; using Content.Shared.Stacks; +using Content.Shared.Tag; +using Content.Shared.Whitelist; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -149,6 +154,80 @@ await server.WaitAssertion(() => await pair.CleanReturnAsync(); } + /// + /// Tests to see if any items that are valid for cargo bounties can be sliced into items that + /// are also valid for the same bounty entry. + /// + [Test] + public async Task NoSliceableBountyArbitrageTest() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var testMap = await pair.CreateTestMap(); + + var entManager = server.ResolveDependency(); + var mapManager = server.ResolveDependency(); + var protoManager = server.ResolveDependency(); + var componentFactory = server.ResolveDependency(); + var whitelist = entManager.System(); + var cargo = entManager.System(); + var sliceableSys = entManager.System(); + + var bounties = protoManager.EnumeratePrototypes().ToList(); + + await server.WaitAssertion(() => + { + var mapId = testMap.MapId; + var grid = mapManager.CreateGridEntity(mapId); + var coord = new EntityCoordinates(grid.Owner, 0, 0); + + var sliceableEntityProtos = protoManager.EnumeratePrototypes() + .Where(p => !p.Abstract) + .Where(p => !pair.IsTestPrototype(p)) + .Where(p => p.TryGetComponent(out _, componentFactory)) + .Select(p => p.ID) + .ToList(); + + foreach (var proto in sliceableEntityProtos) + { + var ent = entManager.SpawnEntity(proto, coord); + var sliceable = entManager.GetComponent(ent); + + // Check each bounty + foreach (var bounty in bounties) + { + // Check each entry in the bounty + foreach (var entry in bounty.Entries) + { + // See if the entity counts as part of this bounty entry + if (!cargo.IsValidBountyEntry(ent, entry)) + continue; + + // Spawn a slice + var slice = entManager.SpawnEntity(sliceable.Slice, coord); + + // See if the slice also counts for this bounty entry + if (!cargo.IsValidBountyEntry(slice, entry)) + { + entManager.DeleteEntity(slice); + continue; + } + + entManager.DeleteEntity(slice); + + // If for some reason it can only make one slice, that's okay, I guess + Assert.That(sliceable.TotalCount, Is.EqualTo(1), $"{proto} counts as part of cargo bounty {bounty.ID} and slices into {sliceable.TotalCount} slices which count for the same bounty!"); + } + } + + entManager.DeleteEntity(ent); + } + mapManager.DeleteMap(mapId); + }); + + await pair.CleanReturnAsync(); + } [TestPrototypes] private const string StackProto = @" diff --git a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs index 0fcfd160bb39..554b349b9ba6 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs @@ -300,6 +300,21 @@ public bool IsBountyComplete(EntityUid container, IEnumerable + /// Determines whether the meets the criteria for the bounty . + /// + /// true if is a valid item for the bounty entry, otherwise false + public bool IsValidBountyEntry(EntityUid entity, CargoBountyItemEntry entry) + { + if (!_whitelistSys.IsValid(entry.Whitelist, entity)) + return false; + + if (entry.Blacklist != null && _whitelistSys.IsValid(entry.Blacklist, entity)) + return false; + + return true; + } + public bool IsBountyComplete(HashSet entities, IEnumerable entries, out HashSet bountyEntities) { bountyEntities = new(); @@ -313,7 +328,7 @@ public bool IsBountyComplete(HashSet entities, IEnumerable(); foreach (var entity in entities) { - if (!_whitelistSys.IsValid(entry.Whitelist, entity) || (entry.Blacklist != null && _whitelistSys.IsValid(entry.Blacklist, entity))) + if (!IsValidBountyEntry(entity, entry)) continue; count += _stackQuery.CompOrNull(entity)?.Count ?? 1; diff --git a/Resources/Prototypes/Catalog/Bounties/bounties.yml b/Resources/Prototypes/Catalog/Bounties/bounties.yml index bedfe442872e..08bb2a142250 100644 --- a/Resources/Prototypes/Catalog/Bounties/bounties.yml +++ b/Resources/Prototypes/Catalog/Bounties/bounties.yml @@ -42,6 +42,9 @@ whitelist: tags: - Bread + blacklist: + tags: + - Slice - type: cargoBounty id: BountyCarrot @@ -533,6 +536,9 @@ whitelist: tags: - Meat + blacklist: + components: + - SliceableFood - type: cargoBounty id: BountyFruit From ee8224bce22c5d5ca76faaa43b2fc8441f356610 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 3 Jun 2024 12:59:59 -0400 Subject: [PATCH 264/568] Tip 51 does not exist (#28555) --- Resources/Locale/en-US/tips.ftl | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Locale/en-US/tips.ftl b/Resources/Locale/en-US/tips.ftl index 52912d1ba407..90a061645267 100644 --- a/Resources/Locale/en-US/tips.ftl +++ b/Resources/Locale/en-US/tips.ftl @@ -48,6 +48,7 @@ tips-dataset-47 = As a Salvage Specialist, you can use your proto-kinetic accele tips-dataset-48 = As a Salvage Specialist, never forget to mine ore! Ore can be sold to cargo for a pretty penny, be used for construction, and also be used by Scientists for fancy technology. tips-dataset-49 = As a Salvage Specialist, try asking science for a tethergun. It can be used to grab items off of salvage wrecks extremely efficiently! tips-dataset-50 = As a Salvage Specialist, try asking science for a grappling hook. It can be used to propel yourself onto wrecks, or if stuck in space you don't have to rely on the proto-kinetic accelerator. +tips-dataset-51 = Tip #51 does not exist and has never existed. Ignore any rumors to the contrary. tips-dataset-52 = As a Salvage Specialist, consider cooperating with the Cargo Technicians. They can order you a wide variety of useful items, including ones that may be hard to get otherwise, such laser guns and shuttle building materials. tips-dataset-53 = As a Cargo Technician, consider asking science for a Ripley APLU. When paired with a hydraulic clamp, you can grab valuable maintenance objects like fuel tanks much more easily, and make deliveries in a swift manner. tips-dataset-54 = As a Cargo Technician, try to maintain a surplus of materials. They are extremely useful for Scientists and Station Engineers to have immediate access to. From a4d16017589e82822c9880b146b8d3dab9621093 Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Mon, 3 Jun 2024 21:47:06 +0300 Subject: [PATCH 265/568] Accent trait limit (#28046) --- .../Lobby/UI/HumanoidProfileEditor.xaml.cs | 99 +++++++++++++---- .../UI/Roles/TraitPreferenceSelector.xaml | 2 +- .../UI/Roles/TraitPreferenceSelector.xaml.cs | 9 +- Content.Server/Traits/TraitSystem.cs | 30 ++--- .../Preferences/HumanoidCharacterProfile.cs | 32 +++++- .../Traits/TraitCategoryPrototype.cs | 26 +++++ Content.Shared/Traits/TraitPrototype.cs | 104 ++++++++++-------- .../ui/humanoid-profile-editor.ftl | 11 +- Resources/Locale/en-US/traits/traits.ftl | 18 ++- Resources/Prototypes/Traits/categories.yml | 8 ++ Resources/Prototypes/Traits/disabilities.yml | 42 ++++--- .../Prototypes/Traits/inconveniences.yml | 18 --- Resources/Prototypes/Traits/neutral.yml | 33 ------ Resources/Prototypes/Traits/speech.yml | 88 +++++++++++++++ 14 files changed, 357 insertions(+), 163 deletions(-) create mode 100644 Content.Shared/Traits/TraitCategoryPrototype.cs create mode 100644 Resources/Prototypes/Traits/categories.yml delete mode 100644 Resources/Prototypes/Traits/inconveniences.yml delete mode 100644 Resources/Prototypes/Traits/neutral.yml create mode 100644 Resources/Prototypes/Traits/speech.yml diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index 53c332c1857f..ac43fa11a853 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -7,6 +7,7 @@ using Content.Client.Lobby.UI.Roles; using Content.Client.Message; using Content.Client.Players.PlayTimeTracking; +using Content.Client.Stylesheets; using Content.Client.UserInterface.Systems.Guidebook; using Content.Shared.CCVar; using Content.Shared.Clothing; @@ -466,38 +467,96 @@ public void RefreshTraits() var traits = _prototypeManager.EnumeratePrototypes().OrderBy(t => Loc.GetString(t.Name)).ToList(); TabContainer.SetTabTitle(3, Loc.GetString("humanoid-profile-editor-traits-tab")); - if (traits.Count > 0) + if (traits.Count < 1) { - foreach (var trait in traits) + TraitsList.AddChild(new Label { - var selector = new TraitPreferenceSelector(trait); + Text = Loc.GetString("humanoid-profile-editor-no-traits"), + FontColorOverride = Color.Gray, + }); + return; + } - if (Profile?.TraitPreferences.Contains(trait.ID) == true) - { - selector.Preference = true; - } - else + //Setup model + Dictionary> model = new(); + List defaultTraits = new(); + model.Add("default", defaultTraits); + + foreach (var trait in traits) + { + if (trait.Category == null) + { + defaultTraits.Add(trait.ID); + continue; + } + + if (!model.ContainsKey(trait.Category)) + { + model.Add(trait.Category, new()); + } + model[trait.Category].Add(trait.ID); + } + + //Create UI view from model + foreach (var (categoryId, traitId) in model) + { + TraitCategoryPrototype? category = null; + if (categoryId != "default") + { + category = _prototypeManager.Index(categoryId); + // Label + TraitsList.AddChild(new Label { - selector.Preference = false; - } + Text = Loc.GetString(category.Name), + Margin = new Thickness(0, 10, 0, 0), + StyleClasses = { StyleBase.StyleClassLabelHeading }, + }); + } + + List selectors = new(); + var selectionCount = 0; + + foreach (var traitProto in traitId) + { + var trait = _prototypeManager.Index(traitProto); + var selector = new TraitPreferenceSelector(trait); + + selector.Preference = Profile?.TraitPreferences.Contains(trait.ID) == true; + if (selector.Preference) + selectionCount += trait.Cost; selector.PreferenceChanged += preference => { - Profile = Profile?.WithTraitPreference(trait.ID, preference); + Profile = Profile?.WithTraitPreference(trait.ID, categoryId, preference); SetDirty(); + RefreshTraits(); // If too many traits are selected, they will be reset to the real value. }; + selectors.Add(selector); + } - TraitsList.AddChild(selector); + // Selection counter + if (category is { MaxTraitPoints: >= 0 }) + { + TraitsList.AddChild(new Label + { + Text = Loc.GetString("humanoid-profile-editor-trait-count-hint", ("current", selectionCount) ,("max", category.MaxTraitPoints)), + FontColorOverride = Color.Gray + }); } - } - else - { - TraitsList.AddChild(new Label + + foreach (var selector in selectors) { - // TODO: Localise - Text = "No traits available :(", - FontColorOverride = Color.Gray, - }); + if (selector == null) + continue; + + if (category is { MaxTraitPoints: >= 0 } && + selector.Cost + selectionCount > category.MaxTraitPoints) + { + selector.Checkbox.Label.FontColorOverride = Color.Red; + } + + TraitsList.AddChild(selector); + } } } diff --git a/Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml b/Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml index 18dabe309041..266b4b8eeee4 100644 --- a/Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml +++ b/Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml @@ -2,6 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - + diff --git a/Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml.cs b/Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml.cs index 498a5ca4e504..a52a3fa2dbc3 100644 --- a/Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml.cs +++ b/Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml.cs @@ -9,6 +9,8 @@ namespace Content.Client.Lobby.UI.Roles; [GenerateTypedNameReferences] public sealed partial class TraitPreferenceSelector : Control { + public int Cost; + public bool Preference { get => Checkbox.Pressed; @@ -20,7 +22,12 @@ public bool Preference public TraitPreferenceSelector(TraitPrototype trait) { RobustXamlLoader.Load(this); - Checkbox.Text = Loc.GetString(trait.Name); + + var text = trait.Cost != 0 ? $"[{trait.Cost}] " : ""; + text += Loc.GetString(trait.Name); + + Cost = trait.Cost; + Checkbox.Text = text; Checkbox.OnToggled += OnCheckBoxToggled; if (trait.Description is { } desc) diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs index 22ee0e4861f4..f7531f7e2ce7 100644 --- a/Content.Server/Traits/TraitSystem.cs +++ b/Content.Server/Traits/TraitSystem.cs @@ -38,27 +38,21 @@ private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent args) continue; // Add all components required by the prototype - foreach (var entry in traitPrototype.Components.Values) - { - if (HasComp(args.Mob, entry.Component.GetType())) - continue; - - var comp = (Component) _serializationManager.CreateCopy(entry.Component, notNullableOverride: true); - comp.Owner = args.Mob; - EntityManager.AddComponent(args.Mob, comp); - } + EntityManager.AddComponents(args.Mob, traitPrototype.Components, false); // Add item required by the trait - if (traitPrototype.TraitGear != null) - { - if (!TryComp(args.Mob, out HandsComponent? handsComponent)) - continue; + if (traitPrototype.TraitGear == null) + continue; - var coords = Transform(args.Mob).Coordinates; - var inhandEntity = EntityManager.SpawnEntity(traitPrototype.TraitGear, coords); - _sharedHandsSystem.TryPickup(args.Mob, inhandEntity, checkActionBlocker: false, - handsComp: handsComponent); - } + if (!TryComp(args.Mob, out HandsComponent? handsComponent)) + continue; + + var coords = Transform(args.Mob).Coordinates; + var inhandEntity = EntityManager.SpawnEntity(traitPrototype.TraitGear, coords); + _sharedHandsSystem.TryPickup(args.Mob, + inhandEntity, + checkActionBlocker: false, + handsComp: handsComponent); } } } diff --git a/Content.Shared/Preferences/HumanoidCharacterProfile.cs b/Content.Shared/Preferences/HumanoidCharacterProfile.cs index f47a3ac3dbe7..bd55bbb40a8f 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -346,13 +346,43 @@ public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref) }; } - public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref) + public HumanoidCharacterProfile WithTraitPreference(string traitId, string? categoryId, bool pref) { + var prototypeManager = IoCManager.Resolve(); + var traitProto = prototypeManager.Index(traitId); + + TraitCategoryPrototype? categoryProto = null; + if (categoryId != null && categoryId != "default") + categoryProto = prototypeManager.Index(categoryId); + var list = new HashSet(_traitPreferences); if (pref) { list.Add(traitId); + + if (categoryProto == null || categoryProto.MaxTraitPoints < 0) + { + return new(this) + { + _traitPreferences = list, + }; + } + + var count = 0; + foreach (var trait in list) + { + var traitProtoTemp = prototypeManager.Index(trait); + count += traitProtoTemp.Cost; + } + + if (count > categoryProto.MaxTraitPoints && traitProto.Cost != 0) + { + return new(this) + { + _traitPreferences = _traitPreferences, + }; + } } else { diff --git a/Content.Shared/Traits/TraitCategoryPrototype.cs b/Content.Shared/Traits/TraitCategoryPrototype.cs new file mode 100644 index 000000000000..1da624173a3d --- /dev/null +++ b/Content.Shared/Traits/TraitCategoryPrototype.cs @@ -0,0 +1,26 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Traits; + +/// +/// Traits category with general settings. Allows you to limit the number of taken traits in one category +/// +[Prototype] +public sealed partial class TraitCategoryPrototype : IPrototype +{ + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// Name of the trait category displayed in the UI + /// + [DataField] + public LocId Name { get; private set; } = string.Empty; + + /// + /// The maximum number of traits that can be taken in this category. If -1, you can take as many traits as you like. + /// + [DataField] + public int MaxTraitPoints = -1; +} diff --git a/Content.Shared/Traits/TraitPrototype.cs b/Content.Shared/Traits/TraitPrototype.cs index 34feb8da22c3..c79d3cbf308e 100644 --- a/Content.Shared/Traits/TraitPrototype.cs +++ b/Content.Shared/Traits/TraitPrototype.cs @@ -1,55 +1,63 @@ using Content.Shared.Whitelist; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -// don't worry about it +namespace Content.Shared.Traits; -namespace Content.Shared.Traits +/// +/// Describes a trait. +/// +[Prototype] +public sealed partial class TraitPrototype : IPrototype { + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// The name of this trait. + /// + [DataField] + public LocId Name { get; private set; } = string.Empty; + + /// + /// The description of this trait. + /// + [DataField] + public LocId? Description { get; private set; } + + /// + /// Don't apply this trait to entities this whitelist IS NOT valid for. + /// + [DataField] + public EntityWhitelist? Whitelist; + /// - /// Describes a trait. - /// - [Prototype("trait")] - public sealed partial class TraitPrototype : IPrototype - { - [ViewVariables] - [IdDataField] - public string ID { get; private set; } = default!; - - /// - /// The name of this trait. - /// - [DataField("name")] - public string Name { get; private set; } = ""; - - /// - /// The description of this trait. - /// - [DataField("description")] - public string? Description { get; private set; } - - /// - /// Don't apply this trait to entities this whitelist IS NOT valid for. - /// - [DataField("whitelist")] - public EntityWhitelist? Whitelist; - - /// - /// Don't apply this trait to entities this whitelist IS valid for. (hence, a blacklist) - /// - [DataField("blacklist")] - public EntityWhitelist? Blacklist; - - /// - /// The components that get added to the player, when they pick this trait. - /// - [DataField("components")] - public ComponentRegistry Components { get; private set; } = default!; - - /// - /// Gear that is given to the player, when they pick this trait. - /// - [DataField("traitGear", required: false, customTypeSerializer:typeof(PrototypeIdSerializer))] - public string? TraitGear; - } + /// Don't apply this trait to entities this whitelist IS valid for. (hence, a blacklist) + /// + [DataField] + public EntityWhitelist? Blacklist; + + /// + /// The components that get added to the player, when they pick this trait. + /// + [DataField] + public ComponentRegistry Components { get; private set; } = default!; + + /// + /// Gear that is given to the player, when they pick this trait. + /// + [DataField] + public EntProtoId? TraitGear; + + /// + /// Trait Price. If negative number, points will be added. + /// + [DataField] + public int Cost = 0; + + /// + /// Adds a trait to a category, allowing you to limit the selection of some traits to the settings of that category. + /// + [DataField] + public ProtoId? Category; } diff --git a/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl b/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl index c7a24d540585..bfdbeb2f140b 100644 --- a/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl +++ b/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl @@ -42,7 +42,7 @@ humanoid-profile-editor-department-jobs-label = {$departmentName} jobs humanoid-profile-editor-antags-tab = Antags humanoid-profile-editor-antag-preference-yes-button = Yes humanoid-profile-editor-antag-preference-no-button = No -humanoid-profile-editor-traits-tab = Traits + humanoid-profile-editor-job-priority-high-button = High humanoid-profile-editor-job-priority-medium-button = Medium humanoid-profile-editor-job-priority-low-button = Low @@ -50,3 +50,12 @@ humanoid-profile-editor-job-priority-never-button = Never humanoid-profile-editor-naming-rules-warning = Warning: Offensive or LRP IC names and descriptions will lead to admin intervention on this server. Read our \[Rules\] for more. humanoid-profile-editor-markings-tab = Markings humanoid-profile-editor-flavortext-tab = Description + +# Traits +humanoid-profile-editor-traits-tab = Traits +humanoid-profile-editor-no-traits = No traits available + +humanoid-profile-editor-trait-count-hint = Points available: [{$current}/{$max}] + +trait-category-disabilities = Disabilities +trait-category-speech = Speech traits \ No newline at end of file diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index cbf65308f3ea..e3aed1c8b246 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -12,7 +12,7 @@ trait-pacifist-desc = You cannot attack or hurt any living beings. permanent-blindness-trait-examined = [color=lightblue]{CAPITALIZE(POSS-ADJ($target))} eyes are glassy and unfocused. It doesn't seem like {SUBJECT($target)} can see you well, if at all.[/color] -trait-lightweight-name = Lightweight Drunk +trait-lightweight-name = Lightweight drunk trait-lightweight-desc = Alcohol has a stronger effect on you trait-muted-name = Muted @@ -24,23 +24,29 @@ trait-paracusia-desc = You hear sounds that aren't really there trait-unrevivable-name = Unrevivable trait-unrevivable-desc = You are unable to be revived by defibrillators. -trait-pirate-accent-name = Pirate Accent +trait-pirate-accent-name = Pirate accent trait-pirate-accent-desc = You can't stop speaking like a pirate! trait-accentless-name = Accentless trait-accentless-desc = You don't have the accent that your species would usually have -trait-frontal-lisp-name = Frontal Lisp +trait-frontal-lisp-name = Frontal lisp trait-frontal-lisp-desc = You thpeak with a lithp -trait-socialanxiety-name = Social Anxiety +trait-socialanxiety-name = Social anxiety trait-socialanxiety-desc = You are anxious when you speak and stutter. -trait-southern-name = Southern Drawl +trait-southern-name = Southern drawl trait-southern-desc = You have a different way of speakin'. trait-snoring-name = Snoring trait-snoring-desc = You will snore while sleeping. trait-liar-name = Pathological liar -trait-liar-desc = You can hardly bring yourself to tell the truth. Sometimes you lie anyway. \ No newline at end of file +trait-liar-desc = You can hardly bring yourself to tell the truth. Sometimes you lie anyway. + +trait-cowboy-name = Cowboy accent +trait-cowboy-desc = You speak with a distinct cowboy accent! + +trait-italian-name = Italian accent +trait-italian-desc = Mamma mia! You seem to have lived in space italy! \ No newline at end of file diff --git a/Resources/Prototypes/Traits/categories.yml b/Resources/Prototypes/Traits/categories.yml new file mode 100644 index 000000000000..a3621648a433 --- /dev/null +++ b/Resources/Prototypes/Traits/categories.yml @@ -0,0 +1,8 @@ +- type: traitCategory + id: Disabilities + name: trait-category-disabilities + +- type: traitCategory + id: SpeechTraits + name: trait-category-speech + maxTraitPoints: 2 diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index be1e981549c6..6e0026e44ebd 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -3,6 +3,7 @@ name: trait-blindness-name description: trait-blindness-desc traitGear: WhiteCane + category: Disabilities whitelist: components: - Blindable @@ -14,6 +15,7 @@ name: trait-poor-vision-name description: trait-poor-vision-desc traitGear: ClothingEyesGlasses + category: Disabilities whitelist: components: - Blindable @@ -25,6 +27,7 @@ id: Narcolepsy name: trait-narcolepsy-name description: trait-narcolepsy-desc + category: Disabilities components: - type: Narcolepsy timeBetweenIncidents: 300, 600 @@ -34,25 +37,15 @@ id: Pacifist name: trait-pacifist-name description: trait-pacifist-desc + category: Disabilities components: - type: Pacified -- type: trait - id: Paracusia - name: trait-paracusia-name - description: trait-paracusia-desc - components: - - type: Paracusia - minTimeBetweenIncidents: 0.1 - maxTimeBetweenIncidents: 300 - maxSoundDistance: 7 - sounds: - collection: Paracusia - - type: trait id: Unrevivable name: trait-unrevivable-name description: trait-unrevivable-desc + category: Disabilities components: - type: Unrevivable @@ -60,6 +53,7 @@ id: Muted name: trait-muted-name description: trait-muted-desc + category: Disabilities blacklist: components: - BorgChassis @@ -67,15 +61,31 @@ - type: Muted - type: trait - id: FrontalLisp - name: trait-frontal-lisp-name - description: trait-frontal-lisp-desc + id: LightweightDrunk + name: trait-lightweight-name + description: trait-lightweight-desc + category: Disabilities components: - - type: FrontalLisp + - type: LightweightDrunk + boozeStrengthMultiplier: 2 + +- type: trait + id: Paracusia + name: trait-paracusia-name + description: trait-paracusia-desc + category: Disabilities + components: + - type: Paracusia + minTimeBetweenIncidents: 0.1 + maxTimeBetweenIncidents: 300 + maxSoundDistance: 7 + sounds: + collection: Paracusia - type: trait id: Snoring name: trait-snoring-name description: trait-snoring-desc + category: Disabilities components: - type: Snoring diff --git a/Resources/Prototypes/Traits/inconveniences.yml b/Resources/Prototypes/Traits/inconveniences.yml deleted file mode 100644 index 657781d1b595..000000000000 --- a/Resources/Prototypes/Traits/inconveniences.yml +++ /dev/null @@ -1,18 +0,0 @@ -- type: trait - id: LightweightDrunk - name: trait-lightweight-name - description: trait-lightweight-desc - components: - - type: LightweightDrunk - boozeStrengthMultiplier: 2 - -- type: trait - id: SocialAnxiety - name: trait-socialanxiety-name - description: trait-socialanxiety-desc - components: - - type: StutteringAccent - matchRandomProb: 0.1 - fourRandomProb: 0 - threeRandomProb: 0 - cutRandomProb: 0 diff --git a/Resources/Prototypes/Traits/neutral.yml b/Resources/Prototypes/Traits/neutral.yml deleted file mode 100644 index 78d2bba04961..000000000000 --- a/Resources/Prototypes/Traits/neutral.yml +++ /dev/null @@ -1,33 +0,0 @@ -- type: trait - id: PirateAccent - name: trait-pirate-accent-name - description: trait-pirate-accent-desc - components: - - type: PirateAccent - -- type: trait - id: Accentless - name: trait-accentless-name - description: trait-accentless-desc - components: - - type: Accentless - removes: - - type: LizardAccent - - type: MothAccent - - type: ReplacementAccent - accent: dwarf - -- type: trait - id: Southern - name: trait-southern-name - description: trait-southern-desc - components: - - type: SouthernAccent - -- type: trait - id: Liar - name: trait-liar-name - description: trait-liar-desc - components: - - type: ReplacementAccent - accent: liar diff --git a/Resources/Prototypes/Traits/speech.yml b/Resources/Prototypes/Traits/speech.yml new file mode 100644 index 000000000000..9448e160b5f7 --- /dev/null +++ b/Resources/Prototypes/Traits/speech.yml @@ -0,0 +1,88 @@ +# Free + +- type: trait + id: Accentless + name: trait-accentless-name + description: trait-accentless-desc + category: SpeechTraits + components: + - type: Accentless + removes: + - type: LizardAccent + - type: MothAccent + - type: ReplacementAccent + accent: dwarf + +# 1 Cost + +- type: trait + id: SouthernAccent + name: trait-southern-name + description: trait-southern-desc + category: SpeechTraits + cost: 1 + components: + - type: SouthernAccent + +- type: trait + id: PirateAccent + name: trait-pirate-accent-name + description: trait-pirate-accent-desc + category: SpeechTraits + cost: 1 + components: + - type: PirateAccent + +- type: trait + id: CowboyAccent + name: trait-cowboy-name + description: trait-cowboy-desc + category: SpeechTraits + cost: 1 + components: + - type: ReplacementAccent + accent: cowboy + +- type: trait + id: ItalianAccent + name: trait-italian-name + description: trait-italian-desc + category: SpeechTraits + cost: 1 + components: + - type: ReplacementAccent + accent: italian + +- type: trait + id: Liar + name: trait-liar-name + description: trait-liar-desc + category: SpeechTraits + cost: 1 + components: + - type: ReplacementAccent + accent: liar + +# 2 Cost + +- type: trait + id: SocialAnxiety + name: trait-socialanxiety-name + description: trait-socialanxiety-desc + category: SpeechTraits + cost: 2 + components: + - type: StutteringAccent + matchRandomProb: 0.1 + fourRandomProb: 0 + threeRandomProb: 0 + cutRandomProb: 0 + +- type: trait + id: FrontalLisp + name: trait-frontal-lisp-name + description: trait-frontal-lisp-desc + category: SpeechTraits + cost: 2 + components: + - type: FrontalLisp From 3e2eb6cc7a6f82b5aedc853ad74ceeb1e8ce8838 Mon Sep 17 00:00:00 2001 From: RenQ <164364533+ImRenQ@users.noreply.github.com> Date: Mon, 3 Jun 2024 21:48:01 +0300 Subject: [PATCH 266/568] "Death gasp" for a moth (#28409) --- Resources/Audio/Effects/Gasp/attributions.yml | 6 ++++++ Resources/Audio/Effects/Gasp/moth_DeathGasp.ogg | Bin 0 -> 18078 bytes .../Prototypes/SoundCollections/deathgasp.yml | 5 +++++ .../Prototypes/Voice/speech_emote_sounds.yml | 2 +- 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Resources/Audio/Effects/Gasp/moth_DeathGasp.ogg diff --git a/Resources/Audio/Effects/Gasp/attributions.yml b/Resources/Audio/Effects/Gasp/attributions.yml index fe8f817c5a35..8d84b770f3ab 100644 --- a/Resources/Audio/Effects/Gasp/attributions.yml +++ b/Resources/Audio/Effects/Gasp/attributions.yml @@ -24,3 +24,9 @@ license: "CC-BY-SA-3.0" copyright: "Taken from tgstation at https://github.com/tgstation/tgstation/commit/f7a49c4068f1277e6857baf0892d355f1c055974" source: "https://github.com/tgstation/tgstation/tree/f7a49c4068f1277e6857baf0892d355f1c055974/sound/voice/human" + +- files: + - moth_DeathGasp.ogg + license: "CC-BY-SA-3.0" + copyright: "Taken from tgstation at https://github.com/tgstation/tgstation/commit/948ad3dd5b22803a01cd74c27f37e509dc61395b" + source: "https://github.com/tgstation/tgstation/blob/948ad3dd5b22803a01cd74c27f37e509dc61395b/sound/voice/moth/moth_death.ogg" diff --git a/Resources/Audio/Effects/Gasp/moth_DeathGasp.ogg b/Resources/Audio/Effects/Gasp/moth_DeathGasp.ogg new file mode 100644 index 0000000000000000000000000000000000000000..df23cfa472ac1043b1fc1eacde71a493402f9133 GIT binary patch literal 18078 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 Date: Mon, 3 Jun 2024 20:48:44 +0200 Subject: [PATCH 267/568] Make borgs shown in logprobe (#27788) --- Content.Shared/Access/Systems/AccessReaderSystem.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index 3670e24bd32a..efdbff3bb88a 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -6,7 +6,9 @@ using Content.Shared.Emag.Systems; using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; +using Content.Shared.NameIdentifier; using Content.Shared.PDA; +using Content.Shared.Silicons.Borgs.Components; using Content.Shared.StationRecords; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -393,6 +395,9 @@ private void LogAccess(Entity ent, EntityUid accessor) ent.Comp.AccessLog.Dequeue(); string? name = null; + if (TryComp(accessor, out var nameIdentifier)) + name = nameIdentifier.FullIdentifier; + // TODO pass the ID card on IsAllowed() instead of using this expensive method // Set name if the accessor has a card and that card has a name and allows itself to be recorded if (_idCardSystem.TryFindIdCard(accessor, out var idCard) From b1c123efae5fdcc1ba8d968c32ef85321afb939c Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 3 Jun 2024 18:49:52 +0000 Subject: [PATCH 268/568] Automatic changelog update --- Resources/Changelog/Changelog.yml | 51 ++++++++++++++----------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f2d31318272d..0ae775e19368 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,32 +1,4 @@ Entries: -- author: PJB3005 - changes: - - message: Fixed pressure damage calculations to what they were always supposed - to be. This means pressure damage now starts happening at more extreme values - (20 kPa for low pressure damage instead of 50 kPa) and high pressure damage - is scaled different. It also fixed the HUD alerts so that the "warning" alerts - show up before you actually start taking damage. - type: Fix - - message: Air alarms now start complaining about low pressure much earlier, when - the pressure drops so much that you can't breathe oxygen tank. - type: Tweak - id: 6176 - time: '2024-03-18T05:16:31.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26217 -- author: HappyRoach - changes: - - message: The correct magazines now appear in the WT550 safe. - type: Fix - id: 6177 - time: '2024-03-18T06:10:28.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26208 -- author: vanx - changes: - - message: You can now wear most guns in the suit slot, and see them on yourself! - type: Tweak - id: 6178 - time: '2024-03-18T06:16:09.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26152 - author: Dutch-VanDerLinde changes: - message: Romerol now properly works on the dead. @@ -3855,3 +3827,26 @@ id: 6675 time: '2024-06-03T16:12:21.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28270 +- author: TheShuEd + changes: + - message: Added italian and cowboy accents + type: Add + - message: Now you can't choose all the accents at once. + type: Fix + id: 6676 + time: '2024-06-03T18:47:06.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28046 +- author: RenQ + changes: + - message: The moths now have their own "death grasp" + type: Add + id: 6677 + time: '2024-06-03T18:48:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28409 +- author: lzk228 + changes: + - message: Borgs ID now will be shown in LogProbe instead of Unknown. + type: Tweak + id: 6678 + time: '2024-06-03T18:48:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27788 From af7f70bade908658416be91c5571cab1160e18ec Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 3 Jun 2024 15:40:46 -0400 Subject: [PATCH 269/568] Add integration test for LocalizedDatasets (#28485) --- .../LocalizedDatasetPrototypeTest.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs diff --git a/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs b/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs new file mode 100644 index 000000000000..b30c0a370e0b --- /dev/null +++ b/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs @@ -0,0 +1,35 @@ +using System.Linq; +using Content.Shared.Dataset; +using Robust.Shared.Localization; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.Localization; + +[TestFixture] +public sealed class LocalizedDatasetPrototypeTest +{ + [Test] + public async Task ValidProtoIdsTest() + { + await using var pair = await PoolManager.GetServerClient(); + + var server = pair.Server; + var protoMan = server.ResolveDependency(); + var localizationMan = server.ResolveDependency(); + + var protos = protoMan.EnumeratePrototypes().OrderBy(p => p.ID); + + // Check each prototype + foreach (var proto in protos) + { + // Check each value in the prototype + foreach (var locId in proto.Values) + { + // Make sure the localization manager has a string for the LocId + Assert.That(localizationMan.HasString(locId), $"LocalizedDataset {proto.ID} with prefix \"{proto.Values.Prefix}\" specifies {proto.Values.Count} entries, but no localized string was found matching {locId}!"); + } + } + + await pair.CleanReturnAsync(); + } +} From 98ab251f9212699a816aaaf31792ffcda768e8c9 Mon Sep 17 00:00:00 2001 From: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:55:43 -0500 Subject: [PATCH 270/568] Remove duplicate entities (#28561) --- .../Entities/Structures/Doors/Airlocks/access.yml | 9 --------- Resources/migration.yml | 3 +++ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml index d798d0c3be72..e92da80c4b2e 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml @@ -73,15 +73,6 @@ containers: board: [ DoorElectronicsHydroponics ] -- type: entity - parent: AirlockCommandLocked - id: AirlockServiceCaptainLocked - suffix: Captain, Locked - components: - - type: ContainerFill - containers: - board: [ DoorElectronicsCaptain ] - - type: entity parent: AirlockExternal id: AirlockExternalLocked diff --git a/Resources/migration.yml b/Resources/migration.yml index cf1a0f7a6023..65e914aed688 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -345,3 +345,6 @@ CrateJanitorExplosive: ClosetJanitorBombFilled # 2024-05-27 DoorRemoteFirefight: null + +# 2024-06-03 +AirlockServiceCaptainLocked: AirlockCaptainLocked From 5e51a1d73c3a90438969b8100a32e43e44a5a5fb Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Tue, 4 Jun 2024 09:05:51 +1200 Subject: [PATCH 271/568] Add StorageInteractionTest (#28541) --- .../Tests/Chemistry/DispenserTest.cs | 2 +- .../Interaction/ComputerContruction.cs | 10 +- .../Interaction/GrilleWindowConstruction.cs | 13 +- .../Interaction/MachineConstruction.cs | 7 +- .../Construction/Interaction/PanelScrewing.cs | 12 +- .../Interaction/PlaceableDeconstruction.cs | 4 +- .../Interaction/WallConstruction.cs | 9 +- .../Interaction/WindowConstruction.cs | 8 +- .../Construction/Interaction/WindowRepair.cs | 2 +- .../Tests/DoAfter/DoAfterCancellationTests.cs | 39 +- .../EncryptionKeys/RemoveEncryptionKeys.cs | 16 +- .../InteractionTest.EntitySpecifier.cs | 6 +- .../Interaction/InteractionTest.Helpers.cs | 353 +++++++++++++----- .../Tests/Interaction/InteractionTest.cs | 29 +- .../Tests/Payload/ModularGrenadeTests.cs | 14 +- .../Tests/Storage/StorageInteractionTest.cs | 75 ++++ .../Tests/Tiles/TileConstructionTests.cs | 16 +- .../Tests/Weldable/WeldableTests.cs | 2 +- 18 files changed, 427 insertions(+), 190 deletions(-) create mode 100644 Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs diff --git a/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs b/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs index a5449308be4a..52b7e555a9d3 100644 --- a/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs @@ -18,7 +18,7 @@ public async Task InsertEjectBuiTest() ToggleNeedPower(); // Insert beaker - await Interact("Beaker"); + await InteractUsing("Beaker"); Assert.That(Hands.ActiveHandEntity, Is.Null); // Open BUI diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/ComputerContruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/ComputerContruction.cs index 5412469ac5d6..8af5edaf3165 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/ComputerContruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/ComputerContruction.cs @@ -16,10 +16,8 @@ public async Task ConstructComputer() await StartConstruction(Computer); // Initial interaction (ghost turns into real entity) - await Interact(Steel, 5); - ClientAssertPrototype(ComputerFrame, ClientTarget); - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; - ClientTarget = null; + await InteractUsing(Steel, 5); + ClientAssertPrototype(ComputerFrame, Target); // Perform construction steps await Interact( @@ -41,7 +39,7 @@ public async Task DeconstructComputer() await StartDeconstruction(ComputerId); // Initial interaction turns id computer into generic computer - await Interact(Screw); + await InteractUsing(Screw); AssertPrototype(ComputerFrame); // Perform deconstruction steps @@ -71,7 +69,7 @@ public async Task ChangeComputer() await SpawnTarget(ComputerId); // Initial interaction turns id computer into generic computer - await Interact(Screw); + await InteractUsing(Screw); AssertPrototype(ComputerFrame); // Perform partial deconstruction steps diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/GrilleWindowConstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/GrilleWindowConstruction.cs index 0de39d27577f..ef6a7b09ae34 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/GrilleWindowConstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/GrilleWindowConstruction.cs @@ -17,17 +17,14 @@ public async Task WindowOnGrille() { // Construct Grille await StartConstruction(Grille); - await Interact(Rod, 10); - ClientAssertPrototype(Grille, ClientTarget); - - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; + await InteractUsing(Rod, 10); + ClientAssertPrototype(Grille, Target); var grille = Target; // Construct Window await StartConstruction(Window); - await Interact(Glass, 10); - ClientAssertPrototype(Window, ClientTarget); - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; + await InteractUsing(Glass, 10); + ClientAssertPrototype(Window, Target); // Deconstruct Window await Interact(Screw, Wrench); @@ -35,7 +32,7 @@ public async Task WindowOnGrille() // Deconstruct Grille Target = grille; - await Interact(Cut); + await InteractUsing(Cut); AssertDeleted(); } diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs index f52f820a4ce9..06874f39edba 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs @@ -14,9 +14,8 @@ public sealed class MachineConstruction : InteractionTest public async Task ConstructProtolathe() { await StartConstruction(MachineFrame); - await Interact(Steel, 5); - ClientAssertPrototype(Unfinished, ClientTarget); - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; + await InteractUsing(Steel, 5); + ClientAssertPrototype(Unfinished, Target); await Interact(Wrench, Cable); AssertPrototype(MachineFrame); await Interact(ProtolatheBoard, Bin1, Bin1, Manipulator1, Manipulator1, Beaker, Beaker, Screw); @@ -51,7 +50,7 @@ public async Task ChangeMachine() AssertPrototype(MachineFrame); // Change it into an autolathe - await Interact("AutolatheMachineCircuitboard"); + await InteractUsing("AutolatheMachineCircuitboard"); AssertPrototype(MachineFrame); await Interact(Bin1, Bin1, Bin1, Manipulator1, Glass, Screw); AssertPrototype("Autolathe"); diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/PanelScrewing.cs b/Content.IntegrationTests/Tests/Construction/Interaction/PanelScrewing.cs index b6d960e28821..636d58bf96fb 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/PanelScrewing.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/PanelScrewing.cs @@ -19,21 +19,21 @@ public async Task WiresPanelScrewing(string prototype) // Open & close panel Assert.That(comp.Open, Is.False); - await Interact(Screw); + await InteractUsing(Screw); Assert.That(comp.Open, Is.True); - await Interact(Screw); + await InteractUsing(Screw); Assert.That(comp.Open, Is.False); // Interrupted DoAfters - await Interact(Screw, awaitDoAfters: false); + await InteractUsing(Screw, awaitDoAfters: false); await CancelDoAfters(); Assert.That(comp.Open, Is.False); - await Interact(Screw); + await InteractUsing(Screw); Assert.That(comp.Open, Is.True); - await Interact(Screw, awaitDoAfters: false); + await InteractUsing(Screw, awaitDoAfters: false); await CancelDoAfters(); Assert.That(comp.Open, Is.True); - await Interact(Screw); + await InteractUsing(Screw); Assert.That(comp.Open, Is.False); } } diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/PlaceableDeconstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/PlaceableDeconstruction.cs index bc0cb9bcef38..783c14c06825 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/PlaceableDeconstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/PlaceableDeconstruction.cs @@ -13,9 +13,9 @@ public async Task DeconstructTable() { await StartDeconstruction("Table"); Assert.That(Comp().IsPlaceable); - await Interact(Wrench); + await InteractUsing(Wrench); AssertPrototype("TableFrame"); - await Interact(Wrench); + await InteractUsing(Wrench); AssertDeleted(); await AssertEntityLookup((Steel, 1), (Rod, 2)); } diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs index 67a2f8025dc0..292bf0c55abe 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs @@ -12,11 +12,10 @@ public sealed class WallConstruction : InteractionTest public async Task ConstructWall() { await StartConstruction(Wall); - await Interact(Steel, 2); + await InteractUsing(Steel, 2); Assert.That(Hands.ActiveHandEntity, Is.Null); - ClientAssertPrototype(Girder, ClientTarget); - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; - await Interact(Steel, 2); + ClientAssertPrototype(Girder, Target); + await InteractUsing(Steel, 2); Assert.That(Hands.ActiveHandEntity, Is.Null); AssertPrototype(WallSolid); } @@ -25,7 +24,7 @@ public async Task ConstructWall() public async Task DeconstructWall() { await StartDeconstruction(WallSolid); - await Interact(Weld); + await InteractUsing(Weld); AssertPrototype(Girder); await Interact(Wrench, Screw); AssertDeleted(); diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/WindowConstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/WindowConstruction.cs index 46bb892ed99a..2ece6b3e397d 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/WindowConstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/WindowConstruction.cs @@ -11,8 +11,8 @@ public sealed class WindowConstruction : InteractionTest public async Task ConstructWindow() { await StartConstruction(Window); - await Interact(Glass, 5); - ClientAssertPrototype(Window, ClientTarget); + await InteractUsing(Glass, 5); + ClientAssertPrototype(Window, Target); } [Test] @@ -28,8 +28,8 @@ public async Task DeconstructWindow() public async Task ConstructReinforcedWindow() { await StartConstruction(RWindow); - await Interact(RGlass, 5); - ClientAssertPrototype(RWindow, ClientTarget); + await InteractUsing(RGlass, 5); + ClientAssertPrototype(RWindow, Target); } [Test] diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs b/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs index abd4bc265b30..6eea519af3b5 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs @@ -24,7 +24,7 @@ public async Task RepairReinforcedWindow() Assert.That(comp.Damage.GetTotal(), Is.GreaterThan(FixedPoint2.Zero)); // Repair the entity - await Interact(Weld); + await InteractUsing(Weld); Assert.That(comp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero)); // Validate that we can still deconstruct the entity (i.e., that welding deconstruction is not blocked). diff --git a/Content.IntegrationTests/Tests/DoAfter/DoAfterCancellationTests.cs b/Content.IntegrationTests/Tests/DoAfter/DoAfterCancellationTests.cs index 0ebd17d8879b..1aaf4a5184ce 100644 --- a/Content.IntegrationTests/Tests/DoAfter/DoAfterCancellationTests.cs +++ b/Content.IntegrationTests/Tests/DoAfter/DoAfterCancellationTests.cs @@ -16,31 +16,31 @@ public sealed class DoAfterCancellationTests : InteractionTest public async Task CancelWallDeconstruct() { await StartDeconstruction(WallConstruction.WallSolid); - await Interact(Weld, awaitDoAfters: false); + await InteractUsing(Weld, awaitDoAfters: false); // Failed do-after has no effect await CancelDoAfters(); AssertPrototype(WallConstruction.WallSolid); // Second attempt works fine - await Interact(Weld); + await InteractUsing(Weld); AssertPrototype(WallConstruction.Girder); // Repeat for wrenching interaction AssertAnchored(); - await Interact(Wrench, awaitDoAfters: false); + await InteractUsing(Wrench, awaitDoAfters: false); await CancelDoAfters(); AssertAnchored(); AssertPrototype(WallConstruction.Girder); - await Interact(Wrench); + await InteractUsing(Wrench); AssertAnchored(false); // Repeat for screwdriver interaction. AssertExists(); - await Interact(Screw, awaitDoAfters: false); + await InteractUsing(Screw, awaitDoAfters: false); await CancelDoAfters(); AssertExists(); - await Interact(Screw); + await InteractUsing(Screw); AssertDeleted(); } @@ -48,17 +48,16 @@ public async Task CancelWallDeconstruct() public async Task CancelWallConstruct() { await StartConstruction(WallConstruction.Wall); - await Interact(Steel, 5, awaitDoAfters: false); + await InteractUsing(Steel, 5, awaitDoAfters: false); await CancelDoAfters(); - await Interact(Steel, 5); - ClientAssertPrototype(WallConstruction.Girder, ClientTarget); - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; - await Interact(Steel, 5, awaitDoAfters: false); + await InteractUsing(Steel, 5); + ClientAssertPrototype(WallConstruction.Girder, Target); + await InteractUsing(Steel, 5, awaitDoAfters: false); await CancelDoAfters(); AssertPrototype(WallConstruction.Girder); - await Interact(Steel, 5); + await InteractUsing(Steel, 5); AssertPrototype(WallConstruction.WallSolid); } @@ -66,11 +65,11 @@ public async Task CancelWallConstruct() public async Task CancelTilePry() { await SetTile(Floor); - await Interact(Pry, awaitDoAfters: false); + await InteractUsing(Pry, awaitDoAfters: false); await CancelDoAfters(); await AssertTile(Floor); - await Interact(Pry); + await InteractUsing(Pry); await AssertTile(Plating); } @@ -78,7 +77,7 @@ public async Task CancelTilePry() public async Task CancelRepeatedTilePry() { await SetTile(Floor); - await Interact(Pry, awaitDoAfters: false); + await InteractUsing(Pry, awaitDoAfters: false); await RunTicks(1); Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1)); await AssertTile(Floor); @@ -89,7 +88,7 @@ public async Task CancelRepeatedTilePry() await AssertTile(Floor); // Third do after will work fine - await Interact(Pry); + await InteractUsing(Pry); Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0)); await AssertTile(Plating); } @@ -102,7 +101,7 @@ public async Task CancelRepeatedWeld() Assert.That(comp.IsWelded, Is.False); - await Interact(Weld, awaitDoAfters: false); + await InteractUsing(Weld, awaitDoAfters: false); await RunTicks(1); Assert.Multiple(() => { @@ -120,7 +119,7 @@ public async Task CancelRepeatedWeld() }); // Third do after will work fine - await Interact(Weld); + await InteractUsing(Weld); Assert.Multiple(() => { Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0)); @@ -128,7 +127,7 @@ public async Task CancelRepeatedWeld() }); // Repeat test for un-welding - await Interact(Weld, awaitDoAfters: false); + await InteractUsing(Weld, awaitDoAfters: false); await RunTicks(1); Assert.Multiple(() => { @@ -141,7 +140,7 @@ public async Task CancelRepeatedWeld() Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0)); Assert.That(comp.IsWelded, Is.True); }); - await Interact(Weld); + await InteractUsing(Weld); Assert.Multiple(() => { Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0)); diff --git a/Content.IntegrationTests/Tests/EncryptionKeys/RemoveEncryptionKeys.cs b/Content.IntegrationTests/Tests/EncryptionKeys/RemoveEncryptionKeys.cs index 9e3dbd8863ef..f5e8c22242ea 100644 --- a/Content.IntegrationTests/Tests/EncryptionKeys/RemoveEncryptionKeys.cs +++ b/Content.IntegrationTests/Tests/EncryptionKeys/RemoveEncryptionKeys.cs @@ -22,7 +22,7 @@ public async Task HeadsetKeys() }); // Remove the key - await Interact(Screw); + await InteractUsing(Screw); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(0)); @@ -34,7 +34,7 @@ public async Task HeadsetKeys() await AssertEntityLookup(("EncryptionKeyCommon", 1)); // Re-insert a key. - await Interact("EncryptionKeyCentCom"); + await InteractUsing("EncryptionKeyCentCom"); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(1)); @@ -59,7 +59,7 @@ public async Task CommsServerKeys() }); // cannot remove keys without opening panel - await Interact(Pry); + await InteractUsing(Pry); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.GreaterThan(0)); @@ -68,7 +68,7 @@ public async Task CommsServerKeys() }); // Open panel - await Interact(Screw); + await InteractUsing(Screw); Assert.Multiple(() => { Assert.That(panel.Open, Is.True); @@ -79,7 +79,7 @@ public async Task CommsServerKeys() }); // Now remove the keys - await Interact(Pry); + await InteractUsing(Pry); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(0)); @@ -87,7 +87,7 @@ public async Task CommsServerKeys() }); // Reinsert a key - await Interact("EncryptionKeyCentCom"); + await InteractUsing("EncryptionKeyCentCom"); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(1)); @@ -97,7 +97,7 @@ public async Task CommsServerKeys() }); // Remove it again - await Interact(Pry); + await InteractUsing(Pry); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(0)); @@ -106,7 +106,7 @@ public async Task CommsServerKeys() // Prying again will start deconstructing the machine. AssertPrototype("TelecomServerFilled"); - await Interact(Pry); + await InteractUsing(Pry); AssertPrototype("MachineFrame"); } } diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs index 37dca721373b..053152dbe1be 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs @@ -33,7 +33,7 @@ protected sealed class EntitySpecifier public int Quantity; /// - /// If true, a check has been performed to see if the prototype ia an entity prototype with a stack component, + /// If true, a check has been performed to see if the prototype is an entity prototype with a stack component, /// in which case the specifier was converted into a stack-specifier /// public bool Converted; @@ -100,7 +100,7 @@ await Server.WaitPost(() => if (!ProtoMan.TryIndex(spec.Prototype, out var entProto)) { - Assert.Fail($"Unkown prototype: {spec.Prototype}"); + Assert.Fail($"Unknown prototype: {spec.Prototype}"); return default; } @@ -120,7 +120,7 @@ await Server.WaitPost(() => /// /// Convert an entity-uid to a matching entity specifier. Useful when doing entity lookups & checking that the - /// right quantity of entities/materials werre produced. Returns null if passed an entity with a null prototype. + /// right quantity of entities/materials were produced. Returns null if passed an entity with a null prototype. /// protected EntitySpecifier? ToEntitySpecifier(EntityUid uid) { diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs index 19ca83a9715d..f4826cb2495c 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs @@ -14,6 +14,7 @@ using Content.Shared.Gravity; using Content.Shared.Item; using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.GameObjects; using Robust.Shared.Input; @@ -44,8 +45,9 @@ await Client.WaitPost(() => return; var comp = CEntMan.GetComponent(clientTarget!.Value); - ClientTarget = clientTarget; - ConstructionGhostId = comp.Owner.Id; + Target = CEntMan.GetNetEntity(clientTarget.Value); + Assert.That(Target.Value.IsClientSide()); + ConstructionGhostId = clientTarget.Value.GetHashCode(); }); await RunTicks(1); @@ -129,21 +131,20 @@ await Server.WaitPost(() => /// /// Place an entity prototype into the players hand. Deletes any currently held entity. /// - /// - /// Automatically enables welders. - /// - protected async Task PlaceInHands(string id, int quantity = 1, bool enableWelder = true) + /// The entity or stack prototype to spawn and place into the users hand + /// The number of entities to spawn. If the prototype is a stack, this sets the stack count. + /// Whether or not to automatically enable any toggleable items + protected async Task PlaceInHands(string id, int quantity = 1, bool enableToggleable = true) { - return await PlaceInHands((id, quantity), enableWelder); + return await PlaceInHands((id, quantity), enableToggleable); } /// /// Place an entity prototype into the players hand. Deletes any currently held entity. /// - /// - /// Automatically enables welders. - /// - protected async Task PlaceInHands(EntitySpecifier entity, bool enableWelder = true) + /// The entity type & quantity to spawn and place into the users hand + /// Whether or not to automatically enable any toggleable items + protected async Task PlaceInHands(EntitySpecifier entity, bool enableToggleable = true) { if (Hands.ActiveHand == null) { @@ -165,7 +166,7 @@ await Server.WaitPost(() => Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHand, false, false, Hands)); // turn on welders - if (enableWelder && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated) + if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated) { Assert.That(ItemToggleSys.TryActivate(item, playerEnt, itemToggle: itemToggle)); } @@ -173,7 +174,7 @@ await Server.WaitPost(() => await RunTicks(1); Assert.That(Hands.ActiveHandEntity, Is.EqualTo(item)); - if (enableWelder && itemToggle != null) + if (enableToggleable && itemToggle != null) Assert.That(itemToggle.Activated); return SEntMan.GetNetEntity(item); @@ -254,21 +255,20 @@ await Server.WaitPost(() => /// /// Place an entity prototype into the players hand and interact with the given entity (or target position) /// - /// - /// Empty strings imply empty hands. - /// - protected async Task Interact(string id, int quantity = 1, bool shouldSucceed = true, bool awaitDoAfters = true) + /// The entity or stack prototype to spawn and place into the users hand + /// The number of entities to spawn. If the prototype is a stack, this sets the stack count. + /// Whether or not to wait for any do-afters to complete + protected async Task InteractUsing(string id, int quantity = 1, bool awaitDoAfters = true) { - await Interact((id, quantity), shouldSucceed, awaitDoAfters); + await InteractUsing((id, quantity), awaitDoAfters); } /// - /// Place an entity prototype into the players hand and interact with the given entity (or target position) + /// Place an entity prototype into the players hand and interact with the given entity (or target position). /// - /// - /// Empty strings imply empty hands. - /// - protected async Task Interact(EntitySpecifier entity, bool shouldSucceed = true, bool awaitDoAfters = true) + /// The entity type & quantity to spawn and place into the users hand + /// Whether or not to wait for any do-afters to complete + protected async Task InteractUsing(EntitySpecifier entity, bool awaitDoAfters = true) { // For every interaction, we will also examine the entity, just in case this breaks something, somehow. // (e.g., servers attempt to assemble construction examine hints). @@ -278,38 +278,80 @@ protected async Task Interact(EntitySpecifier entity, bool shouldSucceed = true, } await PlaceInHands(entity); - await Interact(shouldSucceed, awaitDoAfters); + await Interact(awaitDoAfters); } /// /// Interact with an entity using the currently held entity. /// - protected async Task Interact(bool shouldSucceed = true, bool awaitDoAfters = true) + /// Whether or not to wait for any do-afters to complete + protected async Task Interact(bool awaitDoAfters = true) { - var clientTarget = ClientTarget; - - if ((clientTarget?.IsValid() != true || CEntMan.Deleted(clientTarget)) && (Target == null || Target.Value.IsValid())) + if (Target == null || !Target.Value.IsClientSide()) { - await Server.WaitPost(() => InteractSys.UserInteraction(SEntMan.GetEntity(Player), SEntMan.GetCoordinates(TargetCoords), SEntMan.GetEntity(Target))); - await RunTicks(1); + await Interact(Target, TargetCoords, awaitDoAfters); + return; } - else - { - // The entity is client-side, so attempt to start construction - var clientEnt = ClientTarget ?? CEntMan.GetEntity(Target); - await Client.WaitPost(() => CConSys.TryStartConstruction(clientEnt!.Value)); - await RunTicks(5); - } + // The target is a client-side entity, so we will just attempt to start construction under the assumption that + // it is a construction ghost. + + await Client.WaitPost(() => CConSys.TryStartConstruction(CTarget!.Value)); + await RunTicks(5); + + if (awaitDoAfters) + await AwaitDoAfters(); + + await CheckTargetChange(); + } + + /// + protected async Task Interact(NetEntity? target, NetCoordinates coordinates, bool awaitDoAfters = true) + { + Assert.That(SEntMan.TryGetEntity(target, out var sTarget) || target == null); + var coords = SEntMan.GetCoordinates(coordinates); + Assert.That(coords.IsValid(SEntMan)); + await Interact(sTarget, coords, awaitDoAfters); + } + + /// + /// Interact with an entity using the currently held entity. + /// + protected async Task Interact(EntityUid? target, EntityCoordinates coordinates, bool awaitDoAfters = true) + { + Assert.That(SEntMan.TryGetEntity(Player, out var player)); + + await Server.WaitPost(() => InteractSys.UserInteraction(player!.Value, coordinates, target)); + await RunTicks(1); + + if (awaitDoAfters) + await AwaitDoAfters(); + + await CheckTargetChange(); + } + + /// + /// Activate an entity. + /// + protected async Task Activate(NetEntity? target = null, bool awaitDoAfters = true) + { + target ??= Target; + Assert.That(target, Is.Not.Null); + Assert.That(SEntMan.TryGetEntity(target!.Value, out var sTarget)); + Assert.That(SEntMan.TryGetEntity(Player, out var player)); + + await Server.WaitPost(() => InteractSys.InteractionActivate(player!.Value, sTarget!.Value)); + await RunTicks(1); if (awaitDoAfters) - await AwaitDoAfters(shouldSucceed); + await AwaitDoAfters(); - await CheckTargetChange(shouldSucceed && awaitDoAfters); + await CheckTargetChange(); } /// - /// Variant of that performs several interactions using different entities. + /// Variant of that performs several interactions using different entities. + /// Useful for quickly finishing multiple construction steps. /// /// /// Empty strings imply empty hands. @@ -318,7 +360,7 @@ protected async Task Interact(params EntitySpecifier[] specifiers) { foreach (var spec in specifiers) { - await Interact(spec); + await InteractUsing(spec); } } @@ -338,7 +380,7 @@ protected async Task ThrowItem(NetCoordinates? target = null, float minDis /// /// Wait for any currently active DoAfters to finish. /// - protected async Task AwaitDoAfters(bool shouldSucceed = true, int maxExpected = 1) + protected async Task AwaitDoAfters(int maxExpected = 1) { if (!ActiveDoAfters.Any()) return; @@ -353,13 +395,12 @@ protected async Task AwaitDoAfters(bool shouldSucceed = true, int maxExpected = await RunTicks(10); } - if (!shouldSucceed) - return; - foreach (var doAfter in doAfters) { Assert.That(!doAfter.Cancelled); } + + await RunTicks(5); } /// @@ -398,39 +439,28 @@ await Server.WaitPost(() => /// Check if the test's target entity has changed. E.g., construction interactions will swap out entities while /// a structure is being built. /// - protected async Task CheckTargetChange(bool shouldSucceed) + protected async Task CheckTargetChange() { if (Target == null) return; - var target = Target.Value; + var originalTarget = Target.Value; await RunTicks(5); - if (ClientTarget != null && CEntMan.IsClientSide(ClientTarget.Value)) + if (Target.Value.IsClientSide() && CTestSystem.Ghosts.TryGetValue(ConstructionGhostId, out var newWeh)) { - Assert.That(CEntMan.Deleted(ClientTarget.Value), Is.EqualTo(shouldSucceed), - $"Construction ghost was {(shouldSucceed ? "not deleted" : "deleted")}."); - - if (shouldSucceed) - { - Assert.That(CTestSystem.Ghosts.TryGetValue(ConstructionGhostId, out var newWeh), - $"Failed to get construction entity from ghost Id"); - - await Client.WaitPost(() => CLogger.Debug($"Construction ghost {ConstructionGhostId} became entity {newWeh}")); - Target = newWeh; - } + CLogger.Debug($"Construction ghost {ConstructionGhostId} became entity {newWeh}"); + Target = newWeh; } if (STestSystem.EntChanges.TryGetValue(Target.Value, out var newServerWeh)) { - await Server.WaitPost( - () => SLogger.Debug($"Construction entity {Target.Value} changed to {newServerWeh}")); - + SLogger.Debug($"Construction entity {Target.Value} changed to {newServerWeh}"); Target = newServerWeh; } - if (Target != target) - await CheckTargetChange(shouldSucceed); + if (Target != originalTarget) + await CheckTargetChange(); } #region Asserts @@ -444,16 +474,10 @@ protected void ClientAssertPrototype(string? prototype, NetEntity? target = null return; } - var meta = SEntMan.GetComponent(SEntMan.GetEntity(target.Value)); + var meta = CEntMan.GetComponent(CEntMan.GetEntity(target.Value)); Assert.That(meta.EntityPrototype?.ID, Is.EqualTo(prototype)); } - protected void ClientAssertPrototype(string? prototype, EntityUid? target) - { - var netEnt = CTestSystem.Ghosts[target.GetHashCode()]; - AssertPrototype(prototype, netEnt); - } - protected void AssertPrototype(string? prototype, NetEntity? target = null) { target ??= Target; @@ -699,6 +723,8 @@ protected async Task FindEntity( protected IEnumerable ActiveDoAfters => DoAfters.DoAfters.Values.Where(x => !x.Cancelled && !x.Completed); + #region Component + /// /// Convenience method to get components on the target. Returns SERVER-SIDE components. /// @@ -708,9 +734,23 @@ protected T Comp(NetEntity? target = null) where T : IComponent if (target == null) Assert.Fail("No target specified"); - return SEntMan.GetComponent(SEntMan.GetEntity(target!.Value)); + return SEntMan.GetComponent(ToServer(target!.Value)); + } + + /// + protected bool TryComp(NetEntity? target, [NotNullWhen(true)] out T? comp) where T : IComponent + { + return SEntMan.TryGetComponent(ToServer(target), out comp); + } + + /// + protected bool TryComp([NotNullWhen(true)] out T? comp) where T : IComponent + { + return SEntMan.TryGetComponent(STarget, out comp); } + #endregion + /// /// Set the tile at the target position to some prototype. /// @@ -833,23 +873,70 @@ protected bool TryGetBui(Enum key, [NotNullWhen(true)] out BoundUserInterface? b return true; } + protected bool IsUiOpen(Enum key) + { + if (!TryComp(Player, out UserInterfaceUserComponent? user)) + return false; + + foreach (var keys in user.OpenInterfaces.Values) + { + if (keys.Contains(key)) + return true; + } + + return false; + } + #endregion #region UI /// - /// Presses and releases a button on some client-side window. Will fail if the button cannot be found. + /// Attempts to find, and then presses and releases a control on some client-side window. + /// Will fail if the control cannot be found. + /// + protected async Task ClickControl(string name, BoundKeyFunction? function = null) + where TWindow : BaseWindow + where TControl : Control + { + var window = GetWindow(); + var control = GetControlFromField(name, window); + await ClickControl(control, function); + } + + /// + /// Attempts to find, and then presses and releases a control on some client-side widget. + /// Will fail if the control cannot be found. /// - protected async Task ClickControl(string name) where TWindow : BaseWindow + protected async Task ClickWidgetControl(string name, BoundKeyFunction? function = null) + where TWidget : UIWidget, new() + where TControl : Control + { + var widget = GetWidget(); + var control = GetControlFromField(name, widget); + await ClickControl(control, function); + } + + /// + protected async Task ClickControl(string name, BoundKeyFunction? function = null) + where TWindow : BaseWindow { - await ClickControl(GetControl(name)); + await ClickControl(name, function); + } + + /// + protected async Task ClickWidgetControl(string name, BoundKeyFunction? function = null) + where TWidget : UIWidget, new() + { + await ClickWidgetControl(name, function); } /// - /// Simulates a click and release at the center of some UI Constrol. + /// Simulates a click and release at the center of some UI control. /// - protected async Task ClickControl(Control control) + protected async Task ClickControl(Control control, BoundKeyFunction? function = null) { + function ??= EngineKeyFunctions.UIClick; var screenCoords = new ScreenCoordinates( control.GlobalPixelPosition + control.PixelSize / 2, control.Window?.Id ?? default); @@ -858,7 +945,7 @@ protected async Task ClickControl(Control control) var relativePixelPos = screenCoords.Position - control.GlobalPixelPosition; var args = new GUIBoundKeyEventArgs( - EngineKeyFunctions.UIClick, + function.Value, BoundKeyState.Down, screenCoords, default, @@ -869,7 +956,7 @@ protected async Task ClickControl(Control control) await RunTicks(1); args = new GUIBoundKeyEventArgs( - EngineKeyFunctions.UIClick, + function.Value, BoundKeyState.Up, screenCoords, default, @@ -881,31 +968,26 @@ protected async Task ClickControl(Control control) } /// - /// Attempts to find a control on some client-side window. Will fail if the control cannot be found. + /// Attempt to retrieve a control by looking for a field on some other control. /// - protected TControl GetControl(string name) - where TWindow : BaseWindow + /// + /// Will fail if the control cannot be found. + /// + protected TControl GetControlFromField(string name, Control parent) where TControl : Control - { - var control = GetControl(name); - Assert.That(control.GetType().IsAssignableTo(typeof(TControl))); - return (TControl) control; - } - - protected Control GetControl(string name) where TWindow : BaseWindow { const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - var field = typeof(TWindow).GetField(name, flags); - var prop = typeof(TWindow).GetProperty(name, flags); + var parentType = parent.GetType(); + var field = parentType.GetField(name, flags); + var prop = parentType.GetProperty(name, flags); if (field == null && prop == null) { - Assert.Fail($"Window {typeof(TWindow).Name} does not have a field or property named {name}"); + Assert.Fail($"Window {parentType.Name} does not have a field or property named {name}"); return default!; } - var window = GetWindow(); - var fieldOrProp = field?.GetValue(window) ?? prop?.GetValue(window); + var fieldOrProp = field?.GetValue(parent) ?? prop?.GetValue(parent); if (fieldOrProp is not Control control) { @@ -913,7 +995,59 @@ protected Control GetControl(string name) where TWindow : BaseWindow return default!; } - return control; + Assert.That(control.GetType().IsAssignableTo(typeof(TControl))); + return (TControl) control; + } + + /// + /// Attempt to retrieve a control that matches some predicate by iterating through a control's children. + /// + /// + /// Will fail if the control cannot be found. + /// + protected TControl GetControlFromChildren(Func predicate, Control parent, bool recursive = true) + where TControl : Control + { + if (TryGetControlFromChildren(predicate, parent, out var control, recursive)) + return control; + + Assert.Fail($"Failed to find a {nameof(TControl)} that satisfies the predicate in {parent.Name}"); + return default!; + } + + /// + /// Attempt to retrieve a control of a given type by iterating through a control's children. + /// + protected TControl GetControlFromChildren(Control parent, bool recursive = false) + where TControl : Control + { + return GetControlFromChildren(static _ => true, parent, recursive); + } + + /// + /// Attempt to retrieve a control that matches some predicate by iterating through a control's children. + /// + protected bool TryGetControlFromChildren( + Func predicate, + Control parent, + [NotNullWhen(true)] out TControl? control, + bool recursive = true) + where TControl : Control + { + foreach (var ctrl in parent.Children) + { + if (ctrl is TControl cast && predicate(cast)) + { + control = cast; + return true; + } + + if (recursive && TryGetControlFromChildren(predicate, ctrl, out control)) + return true; + } + + control = null; + return false; } /// @@ -944,7 +1078,6 @@ protected bool TryFindWindow([NotNullWhen(true)] out TWindow? window) w return window != null; } - /// /// Attempts to find a currently open client-side window. /// @@ -962,6 +1095,34 @@ protected bool TryFindWindow(Type type, [NotNullWhen(true)] out BaseWindow? wind return window != null; } + + /// + /// Attempts to find client-side UI widget. + /// + protected UIWidget GetWidget() + where TWidget : UIWidget, new() + { + if (TryFindWidget(out TWidget? widget)) + return widget; + + Assert.Fail($"Could not find a {typeof(TWidget).Name} widget"); + return default!; + } + + /// + /// Attempts to find client-side UI widget. + /// + private bool TryFindWidget([NotNullWhen(true)] out TWidget? uiWidget) + where TWidget : UIWidget, new() + { + uiWidget = null; + var screen = UiMan.ActiveScreen; + if (screen == null) + return false; + + return screen.TryGetWidget(out uiWidget); + } + #endregion #region Power diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index e5f794feaa04..089addfaefd0 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -1,8 +1,10 @@ #nullable enable +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; using Content.Client.Construction; using Content.Client.Examine; +using Content.Client.Gameplay; using Content.IntegrationTests.Pair; using Content.Server.Body.Systems; using Content.Server.Hands.Systems; @@ -24,6 +26,7 @@ using Robust.Shared.Timing; using Robust.UnitTesting; using Content.Shared.Item.ItemToggle; +using Robust.Client.State; namespace Content.IntegrationTests.Tests.Interaction; @@ -64,15 +67,12 @@ public abstract partial class InteractionTest /// The player entity that performs all these interactions. Defaults to an admin-observer with 1 hand. /// protected NetEntity Player; - - protected EntityUid SPlayer => ToServer(Player); - protected EntityUid CPlayer => ToClient(Player); + protected EntityUid SPlayer; + protected EntityUid CPlayer; protected ICommonSession ClientSession = default!; protected ICommonSession ServerSession = default!; - public EntityUid? ClientTarget; - /// /// The current target entity. This is the default entity for various helper functions. /// @@ -108,6 +108,7 @@ public abstract partial class InteractionTest protected InteractionTestSystem STestSystem = default!; protected SharedTransformSystem Transform = default!; protected ISawmill SLogger = default!; + protected SharedUserInterfaceSystem SUiSys = default!; // CLIENT dependencies protected IEntityManager CEntMan = default!; @@ -119,6 +120,7 @@ public abstract partial class InteractionTest protected ExamineSystem ExamineSys = default!; protected InteractionTestSystem CTestSystem = default!; protected ISawmill CLogger = default!; + protected SharedUserInterfaceSystem CUiSys = default!; // player components protected HandsComponent Hands = default!; @@ -168,6 +170,7 @@ public virtual async Task Setup() STestSystem = SEntMan.System(); Stack = SEntMan.System(); SLogger = Server.ResolveDependency().RootSawmill; + SUiSys = Client.System(); // client dependencies CEntMan = Client.ResolveDependency(); @@ -179,6 +182,7 @@ public virtual async Task Setup() CConSys = CEntMan.System(); ExamineSys = CEntMan.System(); CLogger = Client.ResolveDependency().RootSawmill; + CUiSys = Client.System(); // Setup map. await Pair.CreateTestMap(); @@ -204,15 +208,16 @@ await Server.WaitPost(() => old = cPlayerMan.LocalEntity; Player = SEntMan.GetNetEntity(SEntMan.SpawnEntity(PlayerPrototype, SEntMan.GetCoordinates(PlayerCoords))); - var serverPlayerEnt = SEntMan.GetEntity(Player); - Server.PlayerMan.SetAttachedEntity(ServerSession, serverPlayerEnt); - Hands = SEntMan.GetComponent(serverPlayerEnt); - DoAfters = SEntMan.GetComponent(serverPlayerEnt); + SPlayer = SEntMan.GetEntity(Player); + Server.PlayerMan.SetAttachedEntity(ServerSession, SPlayer); + Hands = SEntMan.GetComponent(SPlayer); + DoAfters = SEntMan.GetComponent(SPlayer); }); // Check player got attached. await RunTicks(5); - Assert.That(CEntMan.GetNetEntity(cPlayerMan.LocalEntity), Is.EqualTo(Player)); + CPlayer = ToClient(Player); + Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(CPlayer)); // Delete old player entity. await Server.WaitPost(() => @@ -235,6 +240,10 @@ await Server.WaitPost(() => } }); + // Change UI state to in-game. + var state = Client.ResolveDependency(); + await Client.WaitPost(() => state.RequestStateChange()); + // Final player asserts/checks. await Pair.ReallyBeIdle(5); Assert.Multiple(() => diff --git a/Content.IntegrationTests/Tests/Payload/ModularGrenadeTests.cs b/Content.IntegrationTests/Tests/Payload/ModularGrenadeTests.cs index 70179fdec1aa..4db79373d389 100644 --- a/Content.IntegrationTests/Tests/Payload/ModularGrenadeTests.cs +++ b/Content.IntegrationTests/Tests/Payload/ModularGrenadeTests.cs @@ -22,32 +22,32 @@ public async Task AssembleAndDetonateGrenade() Target = SEntMan.GetNetEntity(await FindEntity("ModularGrenade")); await Drop(); - await Interact(Cable); + await InteractUsing(Cable); // Insert & remove trigger AssertComp(false); - await Interact(Trigger); + await InteractUsing(Trigger); AssertComp(); await FindEntity(Trigger, LookupFlags.Uncontained, shouldSucceed: false); - await Interact(Pry); + await InteractUsing(Pry); AssertComp(false); // Trigger was dropped to floor, not deleted. await FindEntity(Trigger, LookupFlags.Uncontained); // Re-insert - await Interact(Trigger); + await InteractUsing(Trigger); AssertComp(); // Insert & remove payload. - await Interact(Payload); + await InteractUsing(Payload); await FindEntity(Payload, LookupFlags.Uncontained, shouldSucceed: false); - await Interact(Pry); + await InteractUsing(Pry); var ent = await FindEntity(Payload, LookupFlags.Uncontained); await Delete(ent); // successfully insert a second time - await Interact(Payload); + await InteractUsing(Payload); ent = await FindEntity(Payload); var sys = SEntMan.System(); Assert.That(sys.IsEntityInContainer(ent)); diff --git a/Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs b/Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs new file mode 100644 index 000000000000..34402dd5e627 --- /dev/null +++ b/Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs @@ -0,0 +1,75 @@ +using Content.Client.UserInterface.Systems.Hotbar.Widgets; +using Content.Client.UserInterface.Systems.Storage.Controls; +using Content.IntegrationTests.Tests.Interaction; +using Content.Shared.Input; +using Content.Shared.PDA; +using Content.Shared.Storage; +using Robust.Client.UserInterface; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.Tests.Storage; + +public sealed class StorageInteractionTest : InteractionTest +{ + /// + /// Check that players can interact with items in storage if the storage UI is open + /// + [Test] + public async Task UiInteractTest() + { + var sys = Server.System(); + + await SpawnTarget("ClothingBackpack"); + var backpack = ToServer(Target); + + // Initially no BUI is open. + Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.False); + Assert.That(IsUiOpen(PdaUiKey.Key), Is.False); + + // Activating the backpack opens the UI + await Activate(); + Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.True); + Assert.That(IsUiOpen(PdaUiKey.Key), Is.False); + + // Pick up a PDA + var pda = await PlaceInHands("PassengerPDA"); + var sPda = ToServer(pda); + Assert.That(sys.IsEntityInContainer(sPda), Is.True); + Assert.That(sys.TryGetContainingContainer((sPda, null), out var container)); + Assert.That(container!.Owner, Is.EqualTo(SPlayer)); + + // Insert the PDA into the backpack + await Interact(); + Assert.That(sys.TryGetContainingContainer((sPda, null), out container)); + Assert.That(container!.Owner, Is.EqualTo(backpack)); + + // Use "e" / ActivateInWorld to open the PDA UI while it is still in the backpack. + var ctrl = GetStorageControl(pda); + await ClickControl(ctrl, ContentKeyFunctions.ActivateItemInWorld); + await RunTicks(10); + Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.True); + Assert.That(IsUiOpen(PdaUiKey.Key), Is.True); + + // Click on the pda to pick it up and remove it from the backpack. + await ClickControl(ctrl, ContentKeyFunctions.MoveStoredItem); + await RunTicks(10); + Assert.That(sys.TryGetContainingContainer((sPda, null), out container)); + Assert.That(container!.Owner, Is.EqualTo(SPlayer)); + + // UIs should still be open + Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.True); + Assert.That(IsUiOpen(PdaUiKey.Key), Is.True); + } + + /// + /// Retrieve the control that corresponds to the given entity in the currently open storage UI. + /// + private ItemGridPiece GetStorageControl(NetEntity target) + { + var uid = ToClient(target); + var hotbar = GetWidget(); + var storageContainer = GetControlFromField(nameof(HotbarGui.StorageContainer), hotbar); + return GetControlFromChildren(c => c.Entity == uid, storageContainer); + } +} diff --git a/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs b/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs index 083e817d6975..6ea8b6882ad3 100644 --- a/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs +++ b/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs @@ -15,10 +15,10 @@ public async Task PlaceThenCutLattice() await AssertTile(Plating, PlayerCoords); AssertGridCount(1); await SetTile(null); - await Interact(Rod); + await InteractUsing(Rod); await AssertTile(Lattice); Assert.That(Hands.ActiveHandEntity, Is.Null); - await Interact(Cut); + await InteractUsing(Cut); await AssertTile(null); await AssertEntityLookup((Rod, 1)); AssertGridCount(1); @@ -43,14 +43,14 @@ public async Task CutThenPlaceLatticeNewGrid() // Place Lattice var oldPos = TargetCoords; TargetCoords = SEntMan.GetNetCoordinates(new EntityCoordinates(MapData.MapUid, 1, 0)); - await Interact(Rod); + await InteractUsing(Rod); TargetCoords = oldPos; await AssertTile(Lattice); AssertGridCount(1); // Cut lattice Assert.That(Hands.ActiveHandEntity, Is.Null); - await Interact(Cut); + await InteractUsing(Cut); await AssertTile(null); AssertGridCount(0); @@ -76,25 +76,25 @@ public async Task FloorConstructDeconstruct() // Space -> Lattice var oldPos = TargetCoords; TargetCoords = SEntMan.GetNetCoordinates(new EntityCoordinates(MapData.MapUid, 1, 0)); - await Interact(Rod); + await InteractUsing(Rod); TargetCoords = oldPos; await AssertTile(Lattice); AssertGridCount(1); // Lattice -> Plating - await Interact(Steel); + await InteractUsing(Steel); Assert.That(Hands.ActiveHandEntity, Is.Null); await AssertTile(Plating); AssertGridCount(1); // Plating -> Tile - await Interact(FloorItem); + await InteractUsing(FloorItem); Assert.That(Hands.ActiveHandEntity, Is.Null); await AssertTile(Floor); AssertGridCount(1); // Tile -> Plating - await Interact(Pry); + await InteractUsing(Pry); await AssertTile(Plating); AssertGridCount(1); diff --git a/Content.IntegrationTests/Tests/Weldable/WeldableTests.cs b/Content.IntegrationTests/Tests/Weldable/WeldableTests.cs index 6227f3dee1b9..e7eadeda0a47 100644 --- a/Content.IntegrationTests/Tests/Weldable/WeldableTests.cs +++ b/Content.IntegrationTests/Tests/Weldable/WeldableTests.cs @@ -18,7 +18,7 @@ public async Task WeldLocker() Assert.That(comp.IsWelded, Is.False); - await Interact(Weld); + await InteractUsing(Weld); Assert.That(comp.IsWelded, Is.True); AssertPrototype(Locker); // Prototype did not change. } From 2bd4f859661f0cbe9d7c357e9038f2671460c443 Mon Sep 17 00:00:00 2001 From: Verm <32827189+Vermidia@users.noreply.github.com> Date: Mon, 3 Jun 2024 16:07:41 -0500 Subject: [PATCH 272/568] Fix Weh Juice and Other emote chems not working (#28562) --- Content.Server/Chat/Systems/ChatSystem.Emote.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Chat/Systems/ChatSystem.Emote.cs b/Content.Server/Chat/Systems/ChatSystem.Emote.cs index 23e1517d7548..5897aac61a16 100644 --- a/Content.Server/Chat/Systems/ChatSystem.Emote.cs +++ b/Content.Server/Chat/Systems/ChatSystem.Emote.cs @@ -81,7 +81,7 @@ public void TryEmoteWithChat( bool ignoreActionBlocker = false ) { - if (_whitelistSystem.IsWhitelistFailOrNull(emote.Whitelist, source) || _whitelistSystem.IsBlacklistPass(emote.Blacklist, source)) + if (_whitelistSystem.IsWhitelistFail(emote.Whitelist, source) || _whitelistSystem.IsBlacklistPass(emote.Blacklist, source)) return; if (!emote.Available && From c5ff647ca6257862cfd391926470694f909759f4 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:40:03 -0700 Subject: [PATCH 273/568] Replace obsolete EntityWhitelist IsValid usages part 2 (#28506) --- Content.Client/Outline/TargetOutlineSystem.cs | 3 ++- Content.Server/Chat/Systems/ChatSystem.Emote.cs | 1 + .../TileReactions/CreateEntityTileReaction.cs | 5 +++-- .../Construction/ConstructionSystem.Initial.cs | 6 ++++-- .../GameTicking/Rules/LoadMapRuleSystem.cs | 4 +++- .../VariationPass/CutWireVariationPassSystem.cs | 5 ++++- .../Holiday/Christmas/RandomGiftSystem.cs | 6 ++++-- .../Mech/Systems/MechEquipmentSystem.cs | 4 +++- Content.Server/Mech/Systems/MechSystem.cs | 4 +++- .../Ninja/Systems/StunProviderSystem.cs | 4 +++- .../EntitySystems/AnimalHusbandrySystem.cs | 6 ++++-- .../Nutrition/EntitySystems/FoodSystem.cs | 4 +++- .../ObjectiveBlacklistRequirementSystem.cs | 5 ++++- .../Objectives/Systems/RoleRequirementSystem.cs | 4 +++- .../Polymorph/Systems/PolymorphSystem.Collide.cs | 7 +++++-- Content.Server/Sticky/Systems/StickySystem.cs | 7 ++++--- .../Storage/EntitySystems/ItemCounterSystem.cs | 6 ++++-- .../Store/Conditions/BuyerWhitelistCondition.cs | 15 ++++----------- .../Store/Conditions/StoreWhitelistCondition.cs | 15 ++++----------- .../Systems/SurveillanceCameraMicrophoneSystem.cs | 5 +++-- Content.Server/Traits/TraitSystem.cs | 8 ++++---- .../Equipment/Systems/ArtifactCrusherSystem.cs | 4 +++- .../Effects/Systems/DamageNearbyArtifactSystem.cs | 6 ++++-- Content.Shared/Actions/SharedActionsSystem.cs | 4 +++- .../Buckle/SharedBuckleSystem.Buckle.cs | 7 +++++-- .../Conditions/EntityWhitelistCondition.cs | 3 ++- Content.Shared/Inventory/InventorySystem.Equip.cs | 11 ++++------- .../EntitySystems/SharedHandLabelerSystem.cs | 6 ++++-- .../Materials/SharedMaterialReclaimerSystem.cs | 8 ++++---- .../Mech/EntitySystems/SharedMechSystem.cs | 4 +++- .../Systems/SpeedModifierContactsSystem.cs | 4 +++- .../Ninja/Systems/EmagProviderSystem.cs | 3 ++- Content.Shared/Placeable/ItemPlacerSystem.cs | 6 ++++-- Content.Shared/Random/RulesSystem.cs | 5 +++-- Content.Shared/Sound/SharedEmitSoundSystem.cs | 4 +++- .../StepTrigger/Systems/StepTriggerSystem.cs | 4 +++- Content.Shared/Storage/EntitySystems/BinSystem.cs | 6 ++++-- .../EntitySystems/SharedItemMapperSystem.cs | 6 ++++-- .../Teleportation/Systems/SwapTeleporterSystem.cs | 6 ++++-- .../UserInterface/ActivatableUISystem.cs | 6 ++++-- .../Ranged/Systems/SharedGunSystem.Ballistic.cs | 3 ++- .../Ranged/Systems/SharedGunSystem.Clothing.cs | 2 +- 42 files changed, 141 insertions(+), 91 deletions(-) diff --git a/Content.Client/Outline/TargetOutlineSystem.cs b/Content.Client/Outline/TargetOutlineSystem.cs index 2a6867f51f55..df57578b1f51 100644 --- a/Content.Client/Outline/TargetOutlineSystem.cs +++ b/Content.Client/Outline/TargetOutlineSystem.cs @@ -22,6 +22,7 @@ public sealed class TargetOutlineSystem : EntitySystem [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; private bool _enabled = false; @@ -137,7 +138,7 @@ private void HighlightTargets() // check the entity whitelist if (valid && Whitelist != null) - valid = Whitelist.IsValid(entity); + valid = _whitelistSystem.IsWhitelistPass(Whitelist, entity); // and check the cancellable event if (valid && ValidationEvent != null) diff --git a/Content.Server/Chat/Systems/ChatSystem.Emote.cs b/Content.Server/Chat/Systems/ChatSystem.Emote.cs index 5897aac61a16..20a4f1874650 100644 --- a/Content.Server/Chat/Systems/ChatSystem.Emote.cs +++ b/Content.Server/Chat/Systems/ChatSystem.Emote.cs @@ -81,6 +81,7 @@ public void TryEmoteWithChat( bool ignoreActionBlocker = false ) { + if (_whitelistSystem.IsWhitelistFail(emote.Whitelist, source) || _whitelistSystem.IsBlacklistPass(emote.Blacklist, source)) return; diff --git a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs index 29f9275bdf47..6b106b1fc0a3 100644 --- a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs @@ -1,4 +1,4 @@ -using Content.Shared.Chemistry.Reaction; +using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using Content.Shared.Maps; @@ -47,7 +47,8 @@ public FixedPoint2 TileReact(TileRef tile, int acc = 0; foreach (var ent in tile.GetEntitiesInTile()) { - if (Whitelist.IsValid(ent)) + var whitelistSystem = entityManager.System(); + if (whitelistSystem.IsWhitelistPass(Whitelist, ent)) acc += 1; if (acc >= MaxOnTile) diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index 04d3722c66cb..5161ac358ac9 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -14,6 +14,7 @@ using Content.Shared.Interaction; using Content.Shared.Inventory; using Content.Shared.Storage; +using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Player; @@ -30,6 +31,7 @@ public sealed partial class ConstructionSystem [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly EntityLookupSystem _lookupSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; // --- WARNING! LEGACY CODE AHEAD! --- // This entire file contains the legacy code for initial construction. @@ -337,7 +339,7 @@ public async Task TryStartItemConstruction(string prototype, EntityUid use return false; } - if (constructionPrototype.EntityWhitelist != null && !constructionPrototype.EntityWhitelist.IsValid(user)) + if (_whitelistSystem.IsWhitelistFail(constructionPrototype.EntityWhitelist, user)) { _popup.PopupEntity(Loc.GetString("construction-system-cannot-start"), user, user); return false; @@ -422,7 +424,7 @@ private async void HandleStartStructureConstruction(TryStartStructureConstructio return; } - if (constructionPrototype.EntityWhitelist != null && !constructionPrototype.EntityWhitelist.IsValid(user)) + if (_whitelistSystem.IsWhitelistFail(constructionPrototype.EntityWhitelist, user)) { _popup.PopupEntity(Loc.GetString("construction-system-cannot-start"), user, user); return; diff --git a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs index 3a80d82fd92b..c60cf2513e0e 100644 --- a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs @@ -3,6 +3,7 @@ using Content.Server.GameTicking.Rules.Components; using Content.Server.GridPreloader; using Content.Server.Spawners.Components; +using Content.Shared.Whitelist; using Robust.Server.GameObjects; using Robust.Server.Maps; using Robust.Shared.Map; @@ -19,6 +20,7 @@ public sealed class LoadMapRuleSystem : GameRuleSystem [Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly GridPreloaderSystem _gridPreloader = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -99,7 +101,7 @@ private void OnSelectLocation(Entity ent, ref AntagSelectL if (xform.GridUid == null || !ent.Comp.MapGrids.Contains(xform.GridUid.Value)) continue; - if (ent.Comp.SpawnerWhitelist != null && !ent.Comp.SpawnerWhitelist.IsValid(uid, EntityManager)) + if (_whitelistSystem.IsWhitelistFail(ent.Comp.SpawnerWhitelist, uid)) continue; args.Coordinates.Add(_transform.GetMapCoordinates(xform)); diff --git a/Content.Server/GameTicking/Rules/VariationPass/CutWireVariationPassSystem.cs b/Content.Server/GameTicking/Rules/VariationPass/CutWireVariationPassSystem.cs index fd94c74ac8c1..372de4bbb45d 100644 --- a/Content.Server/GameTicking/Rules/VariationPass/CutWireVariationPassSystem.cs +++ b/Content.Server/GameTicking/Rules/VariationPass/CutWireVariationPassSystem.cs @@ -1,5 +1,6 @@ using Content.Server.GameTicking.Rules.VariationPass.Components; using Content.Server.Wires; +using Content.Shared.Whitelist; using Robust.Shared.Random; namespace Content.Server.GameTicking.Rules.VariationPass; @@ -11,6 +12,8 @@ namespace Content.Server.GameTicking.Rules.VariationPass; /// public sealed class CutWireVariationPassSystem : VariationPassSystem { + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + protected override void ApplyVariation(Entity ent, ref StationVariationPassEvent args) { var wiresCut = 0; @@ -22,7 +25,7 @@ protected override void ApplyVariation(Entity ent continue; // Check against blacklist - if (ent.Comp.Blacklist.IsValid(uid)) + if (_whitelistSystem.IsBlacklistPass(ent.Comp.Blacklist, uid)) continue; if (Random.Prob(ent.Comp.WireCutChance)) diff --git a/Content.Server/Holiday/Christmas/RandomGiftSystem.cs b/Content.Server/Holiday/Christmas/RandomGiftSystem.cs index 9e56d0a49377..ee542572d7e0 100644 --- a/Content.Server/Holiday/Christmas/RandomGiftSystem.cs +++ b/Content.Server/Holiday/Christmas/RandomGiftSystem.cs @@ -1,9 +1,10 @@ -using Content.Server.Administration.Logs; +using Content.Server.Administration.Logs; using Content.Server.Hands.Systems; using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.Interaction.Events; using Content.Shared.Item; +using Content.Shared.Whitelist; using Robust.Server.Audio; using Robust.Server.GameObjects; using Robust.Shared.Map.Components; @@ -24,6 +25,7 @@ public sealed class RandomGiftSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; private readonly List _possibleGiftsSafe = new(); private readonly List _possibleGiftsUnsafe = new(); @@ -40,7 +42,7 @@ public override void Initialize() private void OnExamined(EntityUid uid, RandomGiftComponent component, ExaminedEvent args) { - if (!component.ContentsViewers.IsValid(args.Examiner, EntityManager) || component.SelectedEntity is null) + if (_whitelistSystem.IsWhitelistFail(component.ContentsViewers, args.Examiner) || component.SelectedEntity is null) return; var name = _prototype.Index(component.SelectedEntity).Name; diff --git a/Content.Server/Mech/Systems/MechEquipmentSystem.cs b/Content.Server/Mech/Systems/MechEquipmentSystem.cs index f51c0444e69d..f9fe5e464135 100644 --- a/Content.Server/Mech/Systems/MechEquipmentSystem.cs +++ b/Content.Server/Mech/Systems/MechEquipmentSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Interaction; using Content.Shared.Mech.Components; using Content.Shared.Mech.Equipment.Components; +using Content.Shared.Whitelist; namespace Content.Server.Mech.Systems; @@ -14,6 +15,7 @@ public sealed class MechEquipmentSystem : EntitySystem [Dependency] private readonly MechSystem _mech = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; /// public override void Initialize() @@ -40,7 +42,7 @@ private void OnUsed(EntityUid uid, MechEquipmentComponent component, AfterIntera if (mechComp.EquipmentContainer.ContainedEntities.Count >= mechComp.MaxEquipmentAmount) return; - if (mechComp.EquipmentWhitelist != null && !mechComp.EquipmentWhitelist.IsValid(args.Used)) + if (_whitelistSystem.IsWhitelistFail(mechComp.EquipmentWhitelist, args.Used)) return; _popup.PopupEntity(Loc.GetString("mech-equipment-begin-install", ("item", uid)), mech); diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index 68b973f58877..b738d28b4674 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -22,6 +22,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Containers; using Robust.Shared.Player; +using Content.Shared.Whitelist; namespace Content.Server.Mech.Systems; @@ -36,6 +37,7 @@ public sealed partial class MechSystem : SharedMechSystem [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!; /// @@ -224,7 +226,7 @@ private void OnMechEntry(EntityUid uid, MechComponent component, MechEntryEvent if (args.Cancelled || args.Handled) return; - if (component.PilotWhitelist != null && !component.PilotWhitelist.IsValid(args.User)) + if (_whitelistSystem.IsWhitelistFail(component.PilotWhitelist, args.User)) { _popup.PopupEntity(Loc.GetString("mech-no-enter", ("item", uid)), args.User); return; diff --git a/Content.Server/Ninja/Systems/StunProviderSystem.cs b/Content.Server/Ninja/Systems/StunProviderSystem.cs index 970ca78e2cc0..1768606ad20f 100644 --- a/Content.Server/Ninja/Systems/StunProviderSystem.cs +++ b/Content.Server/Ninja/Systems/StunProviderSystem.cs @@ -9,6 +9,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Audio.Systems; using Robust.Shared.Timing; +using Content.Shared.Whitelist; namespace Content.Server.Ninja.Systems; @@ -24,6 +25,7 @@ public sealed class StunProviderSystem : SharedStunProviderSystem [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -42,7 +44,7 @@ private void OnBeforeInteractHand(EntityUid uid, StunProviderComponent comp, Bef if (args.Handled || comp.BatteryUid == null || !_gloves.AbilityCheck(uid, args, out var target)) return; - if (target == uid || !comp.Whitelist.IsValid(target, EntityManager)) + if (target == uid || _whitelistSystem.IsWhitelistFail(comp.Whitelist, target)) return; if (_timing.CurTime < comp.NextStun) diff --git a/Content.Server/Nutrition/EntitySystems/AnimalHusbandrySystem.cs b/Content.Server/Nutrition/EntitySystems/AnimalHusbandrySystem.cs index cd0541340564..e224c7c47927 100644 --- a/Content.Server/Nutrition/EntitySystems/AnimalHusbandrySystem.cs +++ b/Content.Server/Nutrition/EntitySystems/AnimalHusbandrySystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Administration.Logs; +using Content.Server.Administration.Logs; using Content.Server.Popups; using Content.Shared.Database; using Content.Shared.IdentityManagement; @@ -9,6 +9,7 @@ using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Storage; +using Content.Shared.Whitelist; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -33,6 +34,7 @@ public sealed class AnimalHusbandrySystem : EntitySystem [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; private readonly HashSet _failedAttempts = new(); private readonly HashSet _birthQueue = new(); @@ -174,7 +176,7 @@ public bool IsValidPartner(EntityUid uid, EntityUid partner, ReproductiveCompone if (!CanReproduce(partner)) return false; - return component.PartnerWhitelist.IsValid(partner); + return _whitelistSystem.IsWhitelistPass(component.PartnerWhitelist, partner); } /// diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 2c7632aadca5..1862b4e19f14 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -32,6 +32,7 @@ using Robust.Shared.Utility; using System.Linq; using Robust.Server.GameObjects; +using Content.Shared.Whitelist; namespace Content.Server.Nutrition.EntitySystems; @@ -57,6 +58,7 @@ public sealed class FoodSystem : EntitySystem [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly StomachSystem _stomach = default!; [Dependency] private readonly UtensilSystem _utensil = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public const float MaxFeedDistance = 1.0f; @@ -408,7 +410,7 @@ private bool IsDigestibleBy(EntityUid food, FoodComponent component, List<(Stoma if (comp.SpecialDigestible == null) continue; // Check if the food is in the whitelist - if (comp.SpecialDigestible.IsValid(food, EntityManager)) + if (_whitelistSystem.IsWhitelistPass(comp.SpecialDigestible, food)) return true; // They can only eat whitelist food and the food isn't in the whitelist. It's not edible. return false; diff --git a/Content.Server/Objectives/Systems/ObjectiveBlacklistRequirementSystem.cs b/Content.Server/Objectives/Systems/ObjectiveBlacklistRequirementSystem.cs index 56b245ce8479..8fe7e7e20315 100644 --- a/Content.Server/Objectives/Systems/ObjectiveBlacklistRequirementSystem.cs +++ b/Content.Server/Objectives/Systems/ObjectiveBlacklistRequirementSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Objectives.Components; using Content.Shared.Objectives.Components; +using Content.Shared.Whitelist; namespace Content.Server.Objectives.Systems; @@ -8,6 +9,8 @@ namespace Content.Server.Objectives.Systems; /// public sealed class ObjectiveBlacklistRequirementSystem : EntitySystem { + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + public override void Initialize() { base.Initialize(); @@ -22,7 +25,7 @@ private void OnCheck(EntityUid uid, ObjectiveBlacklistRequirementComponent comp, foreach (var objective in args.Mind.AllObjectives) { - if (comp.Blacklist.IsValid(objective, EntityManager)) + if (_whitelistSystem.IsBlacklistPass(comp.Blacklist, objective)) { args.Cancelled = true; return; diff --git a/Content.Server/Objectives/Systems/RoleRequirementSystem.cs b/Content.Server/Objectives/Systems/RoleRequirementSystem.cs index 97aee218f063..8421987bae9f 100644 --- a/Content.Server/Objectives/Systems/RoleRequirementSystem.cs +++ b/Content.Server/Objectives/Systems/RoleRequirementSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Objectives.Components; using Content.Shared.Objectives.Components; +using Content.Shared.Whitelist; namespace Content.Server.Objectives.Systems; @@ -8,6 +9,7 @@ namespace Content.Server.Objectives.Systems; /// public sealed class RoleRequirementSystem : EntitySystem { + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { base.Initialize(); @@ -22,7 +24,7 @@ private void OnCheck(EntityUid uid, RoleRequirementComponent comp, ref Requireme // this whitelist trick only works because roles are components on the mind and not entities // if that gets reworked then this will need changing - if (!comp.Roles.IsValid(args.MindId, EntityManager)) + if (_whitelistSystem.IsWhitelistFail(comp.Roles, args.MindId)) args.Cancelled = true; } } diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs index b4240d409ad8..b29f46e09e9d 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs @@ -1,6 +1,7 @@ using Content.Server.Polymorph.Components; using Content.Shared.Polymorph; using Content.Shared.Projectiles; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Physics.Events; using Robust.Shared.Prototypes; @@ -9,6 +10,8 @@ namespace Content.Server.Polymorph.Systems; public partial class PolymorphSystem { + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + /// /// Need to do this so we don't get a collection enumeration error in physics by polymorphing /// an entity we're colliding with @@ -39,8 +42,8 @@ private void OnPolymorphCollide(EntityUid uid, PolymorphOnCollideComponent compo return; var other = args.OtherEntity; - if (!component.Whitelist.IsValid(other, EntityManager) - || component.Blacklist != null && component.Blacklist.IsValid(other, EntityManager)) + if (_whitelistSystem.IsWhitelistFail(component.Whitelist, other) || + _whitelistSystem.IsBlacklistPass(component.Blacklist, other)) return; _queuedPolymorphUpdates.Enqueue(new (other, component.Sound, component.Polymorph)); diff --git a/Content.Server/Sticky/Systems/StickySystem.cs b/Content.Server/Sticky/Systems/StickySystem.cs index effe1b72f718..21a7f4d03959 100644 --- a/Content.Server/Sticky/Systems/StickySystem.cs +++ b/Content.Server/Sticky/Systems/StickySystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Sticky; using Content.Shared.Sticky.Components; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.Utility; @@ -20,6 +21,7 @@ public sealed class StickySystem : EntitySystem [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; private const string StickerSlotId = "stickers_container"; @@ -67,9 +69,8 @@ private bool StartSticking(EntityUid uid, EntityUid user, EntityUid target, Stic return false; // check whitelist and blacklist - if (component.Whitelist != null && !component.Whitelist.IsValid(target)) - return false; - if (component.Blacklist != null && component.Blacklist.IsValid(target)) + if (_whitelistSystem.IsWhitelistFail(component.Whitelist, target) || + _whitelistSystem.IsBlacklistPass(component.Blacklist, target)) return false; var attemptEv = new AttemptEntityStickEvent(target, user); diff --git a/Content.Server/Storage/EntitySystems/ItemCounterSystem.cs b/Content.Server/Storage/EntitySystems/ItemCounterSystem.cs index 415e8d924671..43fcb32d1f07 100644 --- a/Content.Server/Storage/EntitySystems/ItemCounterSystem.cs +++ b/Content.Server/Storage/EntitySystems/ItemCounterSystem.cs @@ -1,6 +1,7 @@ -using Content.Shared.Storage; +using Content.Shared.Storage; using Content.Shared.Storage.Components; using Content.Shared.Storage.EntitySystems; +using Content.Shared.Whitelist; using JetBrains.Annotations; using Robust.Shared.Containers; @@ -9,6 +10,7 @@ namespace Content.Server.Storage.EntitySystems [UsedImplicitly] public sealed class ItemCounterSystem : SharedItemCounterSystem { + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; protected override int? GetCount(ContainerModifiedMessage msg, ItemCounterComponent itemCounter) { if (!EntityManager.TryGetComponent(msg.Container.Owner, out StorageComponent? component)) @@ -19,7 +21,7 @@ public sealed class ItemCounterSystem : SharedItemCounterSystem var count = 0; foreach (var entity in component.Container.ContainedEntities) { - if (itemCounter.Count.IsValid(entity)) + if (_whitelistSystem.IsWhitelistPass(itemCounter.Count, entity)) count++; } diff --git a/Content.Server/Store/Conditions/BuyerWhitelistCondition.cs b/Content.Server/Store/Conditions/BuyerWhitelistCondition.cs index 859703a72a70..ff4a9a19cdd2 100644 --- a/Content.Server/Store/Conditions/BuyerWhitelistCondition.cs +++ b/Content.Server/Store/Conditions/BuyerWhitelistCondition.cs @@ -23,18 +23,11 @@ public sealed partial class BuyerWhitelistCondition : ListingCondition public override bool Condition(ListingConditionArgs args) { var ent = args.EntityManager; + var whitelistSystem = ent.System(); - if (Whitelist != null) - { - if (!Whitelist.IsValid(args.Buyer, ent)) - return false; - } - - if (Blacklist != null) - { - if (Blacklist.IsValid(args.Buyer, ent)) - return false; - } + if (whitelistSystem.IsWhitelistFail(Whitelist, args.Buyer) || + whitelistSystem.IsBlacklistPass(Blacklist, args.Buyer)) + return false; return true; } diff --git a/Content.Server/Store/Conditions/StoreWhitelistCondition.cs b/Content.Server/Store/Conditions/StoreWhitelistCondition.cs index 20ec5cecce8a..ced4dfa9c0b4 100644 --- a/Content.Server/Store/Conditions/StoreWhitelistCondition.cs +++ b/Content.Server/Store/Conditions/StoreWhitelistCondition.cs @@ -26,18 +26,11 @@ public override bool Condition(ListingConditionArgs args) return false; var ent = args.EntityManager; + var whitelistSystem = ent.System(); - if (Whitelist != null) - { - if (!Whitelist.IsValid(args.StoreEntity.Value, ent)) - return false; - } - - if (Blacklist != null) - { - if (Blacklist.IsValid(args.StoreEntity.Value, ent)) - return false; - } + if (whitelistSystem.IsWhitelistFail(Whitelist, args.StoreEntity.Value) || + whitelistSystem.IsBlacklistPass(Blacklist, args.StoreEntity.Value)) + return false; return true; } diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs index 1adab9930d91..4deb5238a53c 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Chat.Systems; using Content.Server.Speech; using Content.Server.Speech.Components; +using Content.Shared.Whitelist; using Robust.Shared.Player; using static Content.Server.Chat.Systems.ChatSystem; @@ -9,7 +10,7 @@ namespace Content.Server.SurveillanceCamera; public sealed class SurveillanceCameraMicrophoneSystem : EntitySystem { [Dependency] private readonly SharedTransformSystem _xforms = default!; - + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { base.Initialize(); @@ -60,7 +61,7 @@ private void OnInit(EntityUid uid, SurveillanceCameraMicrophoneComponent compone public void CanListen(EntityUid uid, SurveillanceCameraMicrophoneComponent microphone, ListenAttemptEvent args) { // TODO maybe just make this a part of ActiveListenerComponent? - if (microphone.Blacklist.IsValid(args.Source)) + if (_whitelistSystem.IsBlacklistPass(microphone.Blacklist, args.Source)) args.Cancel(); } diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs index f7531f7e2ce7..f41512b6ac2d 100644 --- a/Content.Server/Traits/TraitSystem.cs +++ b/Content.Server/Traits/TraitSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Traits; +using Content.Shared.Whitelist; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager; @@ -12,6 +13,7 @@ public sealed class TraitSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly ISerializationManager _serializationManager = default!; [Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -31,10 +33,8 @@ private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent args) return; } - if (traitPrototype.Whitelist != null && !traitPrototype.Whitelist.IsValid(args.Mob)) - continue; - - if (traitPrototype.Blacklist != null && traitPrototype.Blacklist.IsValid(args.Mob)) + if (_whitelistSystem.IsWhitelistFail(traitPrototype.Whitelist, args.Mob) || + _whitelistSystem.IsBlacklistPass(traitPrototype.Blacklist, args.Mob)) continue; // Add all components required by the prototype diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs index 6606f284327e..f65ba46f7a5d 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Body.Components; using Content.Shared.Damage; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Content.Shared.Xenoarchaeology.Equipment; using Robust.Shared.Collections; using Robust.Shared.Random; @@ -25,6 +26,7 @@ public sealed class ArtifactCrusherSystem : SharedArtifactCrusherSystem [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; /// public override void Initialize() @@ -92,7 +94,7 @@ public void FinishCrushing(Entity public override void Initialize() @@ -24,7 +26,7 @@ private void OnActivated(EntityUid uid, DamageNearbyArtifactComponent component, ents.Add(args.Activator.Value); foreach (var ent in ents) { - if (component.Whitelist != null && !component.Whitelist.IsValid(ent)) + if (_whitelistSystem.IsWhitelistFail(component.Whitelist, ent)) continue; if (!_random.Prob(component.DamageChance)) diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index bf86d2c1e492..0e302f1e0288 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Inventory.Events; using Content.Shared.Mind; using Content.Shared.Rejuvenate; +using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -29,6 +30,7 @@ public abstract class SharedActionsSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -477,7 +479,7 @@ private bool ValidateEntityTargetBase(EntityUid user, EntityUid target, EntityTa if (!target.IsValid() || Deleted(target)) return false; - if (action.Whitelist != null && !action.Whitelist.IsValid(target, EntityManager)) + if (_whitelistSystem.IsWhitelistFail(action.Whitelist, target)) return false; if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, target)) diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index 00040211e36c..e7bfb53f08c7 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -15,6 +15,7 @@ using Content.Shared.Stunnable; using Content.Shared.Throwing; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Utility; @@ -24,6 +25,8 @@ namespace Content.Shared.Buckle; public abstract partial class SharedBuckleSystem { + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + private void InitializeBuckle() { SubscribeLocalEvent(OnBuckleComponentStartup); @@ -224,8 +227,8 @@ private bool CanBuckle( } // Does it pass the Whitelist - if (strapComp.Whitelist != null && - !strapComp.Whitelist.IsValid(buckleUid, EntityManager) || strapComp.Blacklist?.IsValid(buckleUid, EntityManager) == true) + if (_whitelistSystem.IsWhitelistFail(strapComp.Whitelist, buckleUid) || + _whitelistSystem.IsBlacklistPass(strapComp.Blacklist, buckleUid)) { if (_netManager.IsServer) _popup.PopupEntity(Loc.GetString("buckle-component-cannot-fit-message"), userUid, buckleUid, PopupType.Medium); diff --git a/Content.Shared/Construction/Conditions/EntityWhitelistCondition.cs b/Content.Shared/Construction/Conditions/EntityWhitelistCondition.cs index 22d86b54fb73..b7b3c56ba4c0 100644 --- a/Content.Shared/Construction/Conditions/EntityWhitelistCondition.cs +++ b/Content.Shared/Construction/Conditions/EntityWhitelistCondition.cs @@ -31,7 +31,8 @@ public sealed partial class EntityWhitelistCondition : IConstructionCondition public bool Condition(EntityUid user, EntityCoordinates location, Direction direction) { - return Whitelist.IsValid(user); + var whitelistSystem = IoCManager.Resolve().System(); + return whitelistSystem.IsWhitelistPass(Whitelist, user); } public ConstructionGuideEntry GenerateGuideEntry() diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index 7fd156213b45..7acfafee4a63 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -11,6 +11,7 @@ using Content.Shared.Movement.Systems; using Content.Shared.Popups; using Content.Shared.Strip.Components; +using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Timing; @@ -30,6 +31,7 @@ public abstract partial class InventorySystem [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [ValidatePrototypeId] private const string PocketableItemSize = "Small"; @@ -267,13 +269,8 @@ public bool CanEquip(EntityUid actor, EntityUid target, EntityUid itemUid, strin return false; } - if (slotDefinition.Whitelist != null && !slotDefinition.Whitelist.IsValid(itemUid)) - { - reason = "inventory-component-can-equip-does-not-fit"; - return false; - } - - if (slotDefinition.Blacklist != null && slotDefinition.Blacklist.IsValid(itemUid)) + if (_whitelistSystem.IsWhitelistFail(slotDefinition.Whitelist, itemUid) || + _whitelistSystem.IsBlacklistPass(slotDefinition.Blacklist, itemUid)) { reason = "inventory-component-can-equip-does-not-fit"; return false; diff --git a/Content.Shared/Labels/EntitySystems/SharedHandLabelerSystem.cs b/Content.Shared/Labels/EntitySystems/SharedHandLabelerSystem.cs index 7dbeee3e770a..0763bb101cae 100644 --- a/Content.Shared/Labels/EntitySystems/SharedHandLabelerSystem.cs +++ b/Content.Shared/Labels/EntitySystems/SharedHandLabelerSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Labels.Components; using Content.Shared.Popups; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.GameStates; using Robust.Shared.Network; @@ -16,6 +17,7 @@ public abstract class SharedHandLabelerSystem : EntitySystem [Dependency] private readonly SharedLabelSystem _labelSystem = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly INetManager _netManager = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -77,7 +79,7 @@ private void AddLabelTo(EntityUid uid, HandLabelerComponent? handLabeler, Entity private void OnUtilityVerb(EntityUid uid, HandLabelerComponent handLabeler, GetVerbsEvent args) { - if (args.Target is not { Valid: true } target || !handLabeler.Whitelist.IsValid(target) || !args.CanAccess) + if (args.Target is not { Valid: true } target || _whitelistSystem.IsWhitelistFail(handLabeler.Whitelist, target) || !args.CanAccess) return; var labelerText = handLabeler.AssignedLabel == string.Empty ? Loc.GetString("hand-labeler-remove-label-text") : Loc.GetString("hand-labeler-add-label-text"); @@ -96,7 +98,7 @@ private void OnUtilityVerb(EntityUid uid, HandLabelerComponent handLabeler, GetV private void AfterInteractOn(EntityUid uid, HandLabelerComponent handLabeler, AfterInteractEvent args) { - if (args.Target is not { Valid: true } target || !handLabeler.Whitelist.IsValid(target) || !args.CanReach) + if (args.Target is not { Valid: true } target || _whitelistSystem.IsWhitelistFail(handLabeler.Whitelist, target) || !args.CanReach) return; Labeling(uid, target, args.User, handLabeler); diff --git a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs index fb1702bb94e0..9a7da17d4863 100644 --- a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs +++ b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Examine; using Content.Shared.Mobs.Components; using Content.Shared.Stacks; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -29,6 +30,7 @@ public abstract class SharedMaterialReclaimerSystem : EntitySystem [Dependency] protected readonly SharedAmbientSoundSystem AmbientSound = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] protected readonly SharedContainerSystem Container = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public const string ActiveReclaimerContainerId = "active-material-reclaimer-container"; @@ -91,10 +93,8 @@ public bool TryStartProcessItem(EntityUid uid, EntityUid item, MaterialReclaimer if (HasComp(item) && !CanGib(uid, item, component)) // whitelist? We be gibbing, boy! return false; - if (component.Whitelist is {} whitelist && !whitelist.IsValid(item)) - return false; - - if (component.Blacklist is {} blacklist && blacklist.IsValid(item)) + if (_whitelistSystem.IsWhitelistFail(component.Whitelist, item) || + _whitelistSystem.IsBlacklistPass(component.Blacklist, item)) return false; if (Container.TryGetContainingContainer(item, out _) && !Container.TryRemoveFromContainer(item)) diff --git a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs index 73b7c0847fc1..2ec48085c47d 100644 --- a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs +++ b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Movement.Systems; using Content.Shared.Popups; using Content.Shared.Weapons.Melee; +using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.Network; using Robust.Shared.Serialization; @@ -37,6 +38,7 @@ public abstract class SharedMechSystem : EntitySystem [Dependency] private readonly SharedMoverController _mover = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; /// public override void Initialize() @@ -216,7 +218,7 @@ public void InsertEquipment(EntityUid uid, EntityUid toInsert, MechComponent? co if (component.EquipmentContainer.ContainedEntities.Count >= component.MaxEquipmentAmount) return; - if (component.EquipmentWhitelist != null && !component.EquipmentWhitelist.IsValid(toInsert)) + if (_whitelistSystem.IsWhitelistFail(component.EquipmentWhitelist, toInsert)) return; equipmentComponent.EquipmentOwner = uid; diff --git a/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs index 400a675cd253..6e1b3a29aec5 100644 --- a/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs +++ b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Movement.Components; +using Content.Shared.Whitelist; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; @@ -9,6 +10,7 @@ public sealed class SpeedModifierContactsSystem : EntitySystem { [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly MovementSpeedModifierSystem _speedModifierSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; // TODO full-game-save // Either these need to be processed before a map is saved, or slowed/slowing entities need to update on init. @@ -86,7 +88,7 @@ private void MovementSpeedCheck(EntityUid uid, SpeedModifiedByContactComponent c if (!TryComp(ent, out var slowContactsComponent)) continue; - if (slowContactsComponent.IgnoreWhitelist != null && slowContactsComponent.IgnoreWhitelist.IsValid(uid)) + if (_whitelistSystem.IsWhitelistPass(slowContactsComponent.IgnoreWhitelist, uid)) continue; walkSpeed += slowContactsComponent.WalkSpeedModifier; diff --git a/Content.Shared/Ninja/Systems/EmagProviderSystem.cs b/Content.Shared/Ninja/Systems/EmagProviderSystem.cs index df9cf8ac8252..6838e7982cb0 100644 --- a/Content.Shared/Ninja/Systems/EmagProviderSystem.cs +++ b/Content.Shared/Ninja/Systems/EmagProviderSystem.cs @@ -17,6 +17,7 @@ public sealed class EmagProviderSystem : EntitySystem [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; [Dependency] private readonly TagSystem _tags = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -35,7 +36,7 @@ private void OnBeforeInteractHand(EntityUid uid, EmagProviderComponent comp, Bef return; // only allowed to emag entities on the whitelist - if (comp.Whitelist != null && !comp.Whitelist.IsValid(target, EntityManager)) + if (_whitelistSystem.IsWhitelistFail(comp.Whitelist, target)) return; // only allowed to emag non-immune entities diff --git a/Content.Shared/Placeable/ItemPlacerSystem.cs b/Content.Shared/Placeable/ItemPlacerSystem.cs index 9be6a4acd5a6..d1ea88b82a0c 100644 --- a/Content.Shared/Placeable/ItemPlacerSystem.cs +++ b/Content.Shared/Placeable/ItemPlacerSystem.cs @@ -1,4 +1,5 @@ -using Robust.Shared.Physics.Events; +using Content.Shared.Whitelist; +using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; namespace Content.Shared.Placeable; @@ -11,6 +12,7 @@ public sealed class ItemPlacerSystem : EntitySystem { [Dependency] private readonly CollisionWakeSystem _wake = default!; [Dependency] private readonly PlaceableSurfaceSystem _placeableSurface = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -22,7 +24,7 @@ public override void Initialize() private void OnStartCollide(EntityUid uid, ItemPlacerComponent comp, ref StartCollideEvent args) { - if (comp.Whitelist != null && !comp.Whitelist.IsValid(args.OtherEntity)) + if (_whitelistSystem.IsWhitelistFail(comp.Whitelist, args.OtherEntity)) return; if (TryComp(args.OtherEntity, out var wakeComp)) diff --git a/Content.Shared/Random/RulesSystem.cs b/Content.Shared/Random/RulesSystem.cs index 6b8a58abb712..58d67c268e3a 100644 --- a/Content.Shared/Random/RulesSystem.cs +++ b/Content.Shared/Random/RulesSystem.cs @@ -1,6 +1,7 @@ using System.Numerics; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; +using Content.Shared.Whitelist; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; @@ -14,7 +15,7 @@ public sealed class RulesSystem : EntitySystem [Dependency] private readonly AccessReaderSystem _reader = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public bool IsTrue(EntityUid uid, RulesPrototype rules) { var inRange = new HashSet>(); @@ -158,7 +159,7 @@ public bool IsTrue(EntityUid uid, RulesPrototype rules) foreach (var ent in _lookup.GetEntitiesInRange(xform.MapID, worldPos, entity.Range)) { - if (!entity.Whitelist.IsValid(ent, EntityManager)) + if (_whitelistSystem.IsWhitelistFail(entity.Whitelist, ent)) continue; count++; diff --git a/Content.Shared/Sound/SharedEmitSoundSystem.cs b/Content.Shared/Sound/SharedEmitSoundSystem.cs index 0dcdc44c9f21..efc18abaa065 100644 --- a/Content.Shared/Sound/SharedEmitSoundSystem.cs +++ b/Content.Shared/Sound/SharedEmitSoundSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Popups; using Content.Shared.Sound.Components; using Content.Shared.Throwing; +using Content.Shared.Whitelist; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -34,6 +35,7 @@ public abstract class SharedEmitSoundSystem : EntitySystem [Dependency] private readonly SharedAmbientSoundSystem _ambient = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -123,7 +125,7 @@ private void OnEmitSoundOnDrop(EntityUid uid, EmitSoundOnDropComponent component private void OnEmitSoundOnInteractUsing(Entity ent, ref InteractUsingEvent args) { - if (ent.Comp.Whitelist.IsValid(args.Used, EntityManager)) + if (_whitelistSystem.IsWhitelistPass(ent.Comp.Whitelist, args.Used)) { TryEmitSound(ent, ent.Comp, args.User); } diff --git a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs index d81ad754d1ef..14703f3177ea 100644 --- a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs +++ b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Gravity; using Content.Shared.StepTrigger.Components; +using Content.Shared.Whitelist; using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; @@ -12,6 +13,7 @@ public sealed class StepTriggerSystem : EntitySystem [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!; [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -67,7 +69,7 @@ private bool Update(EntityUid uid, StepTriggerComponent component, TransformComp if (ent == uid) continue; - if (component.Blacklist.IsValid(ent.Value, EntityManager) == true) + if (_whitelistSystem.IsBlacklistPass(component.Blacklist, ent.Value)) { return false; } diff --git a/Content.Shared/Storage/EntitySystems/BinSystem.cs b/Content.Shared/Storage/EntitySystems/BinSystem.cs index 1cc95337ea4a..8c2cb4c4fcbf 100644 --- a/Content.Shared/Storage/EntitySystems/BinSystem.cs +++ b/Content.Shared/Storage/EntitySystems/BinSystem.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Examine; @@ -7,6 +7,7 @@ using Content.Shared.Item; using Content.Shared.Storage.Components; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.Network; @@ -21,6 +22,7 @@ public sealed class BinSystem : EntitySystem [Dependency] private readonly ISharedAdminLogManager _admin = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public const string BinContainerId = "bin-container"; @@ -130,7 +132,7 @@ public bool TryInsertIntoBin(EntityUid uid, EntityUid toInsert, BinComponent? co if (component.Items.Count >= component.MaxItems) return false; - if (component.Whitelist != null && !component.Whitelist.IsValid(toInsert)) + if (_whitelistSystem.IsWhitelistFail(component.Whitelist, toInsert)) return false; _container.Insert(toInsert, component.ItemContainer); diff --git a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs index a5e151f4ea62..2557f8100c66 100644 --- a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs @@ -1,5 +1,6 @@ -using System.Linq; +using System.Linq; using Content.Shared.Storage.Components; +using Content.Shared.Whitelist; using JetBrains.Annotations; using Robust.Shared.Containers; @@ -15,6 +16,7 @@ public abstract class SharedItemMapperSystem : EntitySystem { [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; /// public override void Initialize() @@ -93,7 +95,7 @@ private bool TryGetLayers(EntityUid uid, var list = new List(); foreach (var mapLayerData in itemMapper.MapLayers.Values) { - var count = containedLayers.Count(ent => mapLayerData.ServerWhitelist.IsValid(ent)); + var count = containedLayers.Count(ent => _whitelistSystem.IsWhitelistPass(mapLayerData.ServerWhitelist, ent)); if (count >= mapLayerData.MinCount && count <= mapLayerData.MaxCount) { list.Add(mapLayerData.Layer); diff --git a/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs b/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs index bc73baa61adf..58c249fec54f 100644 --- a/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs +++ b/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Popups; using Content.Shared.Teleportation.Components; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Map.Components; @@ -24,6 +25,7 @@ public sealed class SwapTeleporterSystem : EntitySystem [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; private EntityQuery _xformQuery; @@ -51,8 +53,8 @@ private void OnInteract(Entity ent, ref AfterInteractEv if (!TryComp(target, out var targetComp)) return; - if (!comp.TeleporterWhitelist.IsValid(target, EntityManager) || - !targetComp.TeleporterWhitelist.IsValid(uid, EntityManager)) + if (_whitelistSystem.IsWhitelistFail(comp.TeleporterWhitelist, target) || + _whitelistSystem.IsWhitelistFail(targetComp.TeleporterWhitelist, uid)) { return; } diff --git a/Content.Shared/UserInterface/ActivatableUISystem.cs b/Content.Shared/UserInterface/ActivatableUISystem.cs index 1bb11f337f65..b1006b2a7423 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Interaction.Events; using Content.Shared.Popups; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.Utility; namespace Content.Shared.UserInterface; @@ -19,6 +20,7 @@ public sealed partial class ActivatableUISystem : EntitySystem [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() { @@ -96,7 +98,7 @@ private bool ShouldAddVerb(EntityUid uid, ActivatableUIComponent component, G if (!args.CanAccess) return false; - if (!component.RequiredItems?.IsValid(args.Using ?? default, EntityManager) ?? false) + if (_whitelistSystem.IsWhitelistFail(component.RequiredItems, args.Using ?? default)) return false; if (component.RequireHands) @@ -156,7 +158,7 @@ private void OnInteractUsing(EntityUid uid, ActivatableUIComponent component, In if (component.RequiredItems == null) return; - if (!component.RequiredItems.IsValid(args.Used, EntityManager)) + if (_whitelistSystem.IsWhitelistFail(component.RequiredItems, args.Used)) return; args.Handled = InteractUI(args.User, uid, component); diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 1f9e6cee76aa..9123661c8e96 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -15,6 +15,7 @@ public abstract partial class SharedGunSystem { [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + protected virtual void InitializeBallistic() { SubscribeLocalEvent(OnBallisticInit); @@ -125,7 +126,7 @@ void SimulateInsertAmmo(EntityUid ammo, EntityUid ammoProvider, EntityCoordinate if (ent == null) continue; - if (!target.Whitelist.IsValid(ent.Value)) + if (_whitelistSystem.IsWhitelistFail(target.Whitelist, ent.Value)) { Popup( Loc.GetString("gun-ballistic-transfer-invalid", diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Clothing.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Clothing.cs index 77ee419ac3bc..d4aa024f4c84 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Clothing.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Clothing.cs @@ -42,7 +42,7 @@ private bool TryGetClothingSlotEntity(EntityUid uid, ClothingSlotAmmoProviderCom while (enumerator.NextItem(out var item)) { - if (component.ProviderWhitelist == null || !component.ProviderWhitelist.IsValid(item, EntityManager)) + if (_whitelistSystem.IsWhitelistFailOrNull(component.ProviderWhitelist, item)) continue; slotEntity = item; From b51f04a765a68d5d5b86f83f49eb14c812ea0ac0 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:42:50 -0700 Subject: [PATCH 274/568] Gets rid of obsolete EntityWhitelist.IsValid() function (#28565) Gets rid of obsolete IsValid function Co-authored-by: plykiya --- Content.Shared/Whitelist/EntityWhitelist.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Content.Shared/Whitelist/EntityWhitelist.cs b/Content.Shared/Whitelist/EntityWhitelist.cs index 895759be9582..3e4e2fecb2e5 100644 --- a/Content.Shared/Whitelist/EntityWhitelist.cs +++ b/Content.Shared/Whitelist/EntityWhitelist.cs @@ -54,14 +54,4 @@ public sealed partial class EntityWhitelist /// [DataField] public bool RequireAll; - - [Obsolete("Use WhitelistSystem")] - public bool IsValid(EntityUid uid, IEntityManager? man = null) - { - var sys = man?.System() ?? - IoCManager.Resolve().GetEntitySystem(); - - return sys.IsValid(this, uid); - - } } From 2ae5c953de0ee6bb5cafa1c983106c9c4596c8d6 Mon Sep 17 00:00:00 2001 From: Verm <32827189+Vermidia@users.noreply.github.com> Date: Mon, 3 Jun 2024 18:45:00 -0500 Subject: [PATCH 275/568] Humans can no longer honk on command (#28566) * Humans can no longer honk on command * Undo change it emote file * I hate tabs * Some comments --- .../Chat/Systems/ChatSystem.Emote.cs | 45 ++++++++++++++----- .../Chemistry/ReagentEffects/Emote.cs | 7 ++- Resources/Prototypes/Reagents/fun.yml | 1 + Resources/Prototypes/Reagents/toxins.yml | 1 + 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/Content.Server/Chat/Systems/ChatSystem.Emote.cs b/Content.Server/Chat/Systems/ChatSystem.Emote.cs index 20a4f1874650..e4e5c39eeb67 100644 --- a/Content.Server/Chat/Systems/ChatSystem.Emote.cs +++ b/Content.Server/Chat/Systems/ChatSystem.Emote.cs @@ -49,18 +49,20 @@ private void CacheEmotes() /// Whether or not this message should appear in the adminlog window /// Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all... /// The name to use for the speaking entity. Usually this should just be modified via . If this is set, the event will not get raised. + /// Bypasses whitelist/blacklist/availibility checks for if the entity can use this emote public void TryEmoteWithChat( EntityUid source, string emoteId, ChatTransmitRange range = ChatTransmitRange.Normal, bool hideLog = false, string? nameOverride = null, - bool ignoreActionBlocker = false + bool ignoreActionBlocker = false, + bool forceEmote = false ) { if (!_prototypeManager.TryIndex(emoteId, out var proto)) return; - TryEmoteWithChat(source, proto, range, hideLog: hideLog, nameOverride, ignoreActionBlocker: ignoreActionBlocker); + TryEmoteWithChat(source, proto, range, hideLog: hideLog, nameOverride, ignoreActionBlocker: ignoreActionBlocker, forceEmote: forceEmote); } /// @@ -72,22 +74,18 @@ public void TryEmoteWithChat( /// Whether or not this message should appear in the chat window /// Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all... /// The name to use for the speaking entity. Usually this should just be modified via . If this is set, the event will not get raised. + /// Bypasses whitelist/blacklist/availibility checks for if the entity can use this emote public void TryEmoteWithChat( EntityUid source, EmotePrototype emote, ChatTransmitRange range = ChatTransmitRange.Normal, bool hideLog = false, string? nameOverride = null, - bool ignoreActionBlocker = false + bool ignoreActionBlocker = false, + bool forceEmote = false ) { - - if (_whitelistSystem.IsWhitelistFail(emote.Whitelist, source) || _whitelistSystem.IsBlacklistPass(emote.Blacklist, source)) - return; - - if (!emote.Available && - TryComp(source, out var speech) && - !speech.AllowedEmotes.Contains(emote.ID)) + if (!forceEmote && !AllowedToUseEmote(source, emote)) return; // check if proto has valid message for chat @@ -156,15 +154,40 @@ public bool TryPlayEmoteSound(EntityUid uid, EmoteSoundsPrototype? proto, string _audio.PlayPvs(sound, uid, param); return true; } - + /// + /// Checks if a valid emote was typed, to play sounds and etc and invokes an event. + /// + /// + /// private void TryEmoteChatInput(EntityUid uid, string textInput) { var actionLower = textInput.ToLower(); if (!_wordEmoteDict.TryGetValue(actionLower, out var emote)) return; + if (!AllowedToUseEmote(uid, emote)) + return; + InvokeEmoteEvent(uid, emote); } + /// + /// Checks if we can use this emote based on the emotes whitelist, blacklist, and availibility to the entity. + /// + /// The entity that is speaking + /// The emote being used + /// + private bool AllowedToUseEmote(EntityUid source, EmotePrototype emote) + { + if ((_whitelistSystem.IsWhitelistFail(emote.Whitelist, source) || _whitelistSystem.IsBlacklistPass(emote.Blacklist, source))) + return false; + + if (!emote.Available && + TryComp(source, out var speech) && + !speech.AllowedEmotes.Contains(emote.ID)) + return false; + + return true; + } private void InvokeEmoteEvent(EntityUid uid, EmotePrototype proto) { diff --git a/Content.Server/Chemistry/ReagentEffects/Emote.cs b/Content.Server/Chemistry/ReagentEffects/Emote.cs index a4d49e4ad1cf..db6de6cc75be 100644 --- a/Content.Server/Chemistry/ReagentEffects/Emote.cs +++ b/Content.Server/Chemistry/ReagentEffects/Emote.cs @@ -8,7 +8,7 @@ namespace Content.Server.Chemistry.ReagentEffects; /// -/// Tries to force someone to emote (scream, laugh, etc). +/// Tries to force someone to emote (scream, laugh, etc). Still respects whitelists/blacklists and other limits of the specified emote unless forced. /// [UsedImplicitly] public sealed partial class Emote : ReagentEffect @@ -19,6 +19,9 @@ public sealed partial class Emote : ReagentEffect [DataField] public bool ShowInChat; + [DataField] + public bool Force = false; + // JUSTIFICATION: Emoting is flavor, so same reason popup messages are not in here. protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => null; @@ -30,7 +33,7 @@ public override void Effect(ReagentEffectArgs args) var chatSys = args.EntityManager.System(); if (ShowInChat) - chatSys.TryEmoteWithChat(args.SolutionEntity, EmoteId, ChatTransmitRange.GhostRangeLimit); + chatSys.TryEmoteWithChat(args.SolutionEntity, EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: Force); else chatSys.TryEmoteWithoutChat(args.SolutionEntity, EmoteId); diff --git a/Resources/Prototypes/Reagents/fun.yml b/Resources/Prototypes/Reagents/fun.yml index befa8d829688..1df2636c8ceb 100644 --- a/Resources/Prototypes/Reagents/fun.yml +++ b/Resources/Prototypes/Reagents/fun.yml @@ -333,6 +333,7 @@ - !type:Emote emote: Weh showInChat: true + force: true probability: 0.5 - !type:Polymorph prototype: ArtifactLizard # Does the same thing as the original YML I made for this reagent. diff --git a/Resources/Prototypes/Reagents/toxins.yml b/Resources/Prototypes/Reagents/toxins.yml index f5b196acf6b6..9665de7b099e 100644 --- a/Resources/Prototypes/Reagents/toxins.yml +++ b/Resources/Prototypes/Reagents/toxins.yml @@ -560,6 +560,7 @@ - !type:Emote emote: Honk showInChat: true + force: true probability: 0.2 - !type:HealthChange conditions: From 7d22897d02706507be7f918bfafa493fccc702e3 Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Mon, 3 Jun 2024 19:52:15 -0400 Subject: [PATCH 276/568] Job starting gear is now defined in the starting gear rather than backpack prototypes (#27605) * --- .../Tests/Roles/StartingGearStorageTests.cs | 72 +++ .../Station/Systems/StationSpawningSystem.cs | 15 +- Content.Shared/Roles/StartingGearPrototype.cs | 68 ++- .../Station/SharedStationSpawningSystem.cs | 20 +- .../en-US/preferences/loadout-groups.ftl | 11 +- .../Fills/Backpacks/StarterGear/backpack.yml | 422 ------------------ .../Fills/Backpacks/StarterGear/duffelbag.yml | 275 ------------ .../Fills/Backpacks/StarterGear/satchel.yml | 300 ------------- .../Catalog/Fills/Backpacks/duffelbag.yml | 49 -- .../Entities/Clothing/Back/backpacks.yml | 10 + .../Prototypes/Entities/Mobs/NPCs/human.yml | 2 +- .../Loadouts/Jobs/Cargo/cargo_technician.yml | 6 +- .../Loadouts/Jobs/Cargo/quartermaster.yml | 28 -- .../Jobs/Cargo/salvage_specialist.yml | 6 +- .../Loadouts/Jobs/Civilian/botanist.yml | 6 +- .../Loadouts/Jobs/Civilian/chaplain.yml | 28 -- .../Loadouts/Jobs/Civilian/clown.yml | 6 +- .../Loadouts/Jobs/Civilian/lawyer.yml | 30 +- .../Loadouts/Jobs/Civilian/mime.yml | 6 +- .../Loadouts/Jobs/Civilian/musician.yml | 28 -- .../Loadouts/Jobs/Civilian/passenger.yml | 6 +- .../Loadouts/Jobs/Command/captain.yml | 6 +- .../Jobs/Command/head_of_personnel.yml | 29 +- .../Engineering/atmospheric_technician.yml | 6 +- .../Jobs/Engineering/chief_engineer.yml | 28 -- .../Jobs/Engineering/station_engineer.yml | 6 +- .../Loadouts/Jobs/Medical/chemist.yml | 6 +- .../Jobs/Medical/chief_medical_officer.yml | 28 -- .../Loadouts/Jobs/Medical/medical_doctor.yml | 6 +- .../Loadouts/Jobs/Medical/paramedic.yml | 28 -- .../Jobs/Science/research_director.yml | 28 -- .../Loadouts/Jobs/Science/scientist.yml | 6 +- .../Loadouts/Jobs/Security/detective.yml | 28 -- .../Jobs/Security/security_officer.yml | 6 +- .../Prototypes/Loadouts/loadout_groups.yml | 82 +--- .../Prototypes/Loadouts/role_loadouts.yml | 18 +- Resources/Prototypes/Roles/Antags/Thief.yml | 7 + Resources/Prototypes/Roles/Antags/ninja.yml | 30 ++ Resources/Prototypes/Roles/Antags/nukeops.yml | 63 +++ Resources/Prototypes/Roles/Antags/pirate.yml | 16 +- .../Prototypes/Roles/Antags/revolutionary.yml | 7 + Resources/Prototypes/Roles/Antags/traitor.yml | 33 ++ .../Roles/Jobs/Cargo/cargo_technician.yml | 5 +- .../Roles/Jobs/Cargo/quartermaster.yml | 6 +- .../Roles/Jobs/Cargo/salvage_specialist.yml | 5 +- .../Roles/Jobs/Civilian/assistant.yml | 3 + .../Roles/Jobs/Civilian/bartender.yml | 3 + .../Roles/Jobs/Civilian/botanist.yml | 3 + .../Roles/Jobs/Civilian/chaplain.yml | 5 + .../Prototypes/Roles/Jobs/Civilian/chef.yml | 3 + .../Prototypes/Roles/Jobs/Civilian/clown.yml | 7 +- .../Roles/Jobs/Civilian/janitor.yml | 3 + .../Prototypes/Roles/Jobs/Civilian/lawyer.yml | 4 + .../Roles/Jobs/Civilian/librarian.yml | 4 + .../Prototypes/Roles/Jobs/Civilian/mime.yml | 4 + .../Roles/Jobs/Civilian/musician.yml | 7 +- .../Roles/Jobs/Civilian/service_worker.yml | 3 + .../Prototypes/Roles/Jobs/Command/captain.yml | 5 + .../Roles/Jobs/Command/head_of_personnel.yml | 4 + .../Engineering/atmospheric_technician.yml | 3 + .../Roles/Jobs/Engineering/chief_engineer.yml | 4 + .../Jobs/Engineering/station_engineer.yml | 3 + .../Jobs/Engineering/technical_assistant.yml | 5 +- .../Roles/Jobs/Fun/cult_startinggear.yml | 10 +- .../Roles/Jobs/Fun/emergencyresponseteam.yml | 174 +++++++- .../Roles/Jobs/Fun/misc_startinggear.yml | 219 +++------ .../Roles/Jobs/Fun/wizard_startinggear.yml | 22 +- .../Prototypes/Roles/Jobs/Medical/chemist.yml | 5 +- .../Jobs/Medical/chief_medical_officer.yml | 4 + .../Roles/Jobs/Medical/medical_doctor.yml | 3 + .../Roles/Jobs/Medical/medical_intern.yml | 5 +- .../Roles/Jobs/Medical/paramedic.yml | 4 + .../Roles/Jobs/Science/research_assistant.yml | 3 + .../Roles/Jobs/Science/research_director.yml | 6 +- .../Roles/Jobs/Science/scientist.yml | 4 +- .../Roles/Jobs/Security/detective.yml | 6 + .../Roles/Jobs/Security/head_of_security.yml | 5 + .../Roles/Jobs/Security/security_cadet.yml | 7 +- .../Roles/Jobs/Security/security_officer.yml | 5 + .../Prototypes/Roles/Jobs/Security/warden.yml | 5 + .../Prototypes/Roles/Jobs/Wildcards/boxer.yml | 3 + .../Roles/Jobs/Wildcards/psychologist.yml | 3 + .../Roles/Jobs/Wildcards/reporter.yml | 3 + .../Roles/Jobs/Wildcards/zookeeper.yml | 3 + Resources/migration.yml | 6 +- 85 files changed, 728 insertions(+), 1727 deletions(-) create mode 100644 Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs delete mode 100644 Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml delete mode 100644 Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/duffelbag.yml delete mode 100644 Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/satchel.yml diff --git a/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs b/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs new file mode 100644 index 000000000000..0f15a02eaa29 --- /dev/null +++ b/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs @@ -0,0 +1,72 @@ +using System.Linq; +using Content.Shared.Roles; +using Content.Server.Storage.EntitySystems; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Collections; + +namespace Content.IntegrationTests.Tests.Roles; + +[TestFixture] +public sealed class StartingGearPrototypeStorageTest +{ + /// + /// Checks that a storage fill on a StartingGearPrototype will properly fill + /// + [Test] + public async Task TestStartingGearStorage() + { + var settings = new PoolSettings { Connected = true, Dirty = true }; + await using var pair = await PoolManager.GetServerClient(settings); + var server = pair.Server; + var mapManager = server.ResolveDependency(); + var storageSystem = server.System(); + + var protos = server.ProtoMan + .EnumeratePrototypes() + .Where(p => !p.Abstract) + .ToList() + .OrderBy(p => p.ID); + + var testMap = await pair.CreateTestMap(); + var coords = testMap.GridCoords; + + await server.WaitAssertion(() => + { + foreach (var gearProto in protos) + { + var backpackProto = gearProto.GetGear("back"); + if (backpackProto == string.Empty) + continue; + + var bag = server.EntMan.SpawnEntity(backpackProto, coords); + var ents = new ValueList(); + + foreach (var (slot, entProtos) in gearProto.Storage) + { + if (entProtos.Count == 0) + continue; + + foreach (var ent in entProtos) + { + ents.Add(server.EntMan.SpawnEntity(ent, coords)); + } + + foreach (var ent in ents) + { + if (!storageSystem.CanInsert(bag, ent, out _)) + Assert.Fail($"StartingGearPrototype {gearProto.ID} could not successfully put items into storage {bag.Id}"); + + server.EntMan.DeleteEntity(ent); + } + } + + server.EntMan.DeleteEntity(bag); + } + + mapManager.DeleteMap(testMap.MapId); + }); + + await pair.CleanReturnAsync(); + } +} diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 557a1f95662e..b91082ff2616 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -179,13 +179,6 @@ public EntityUid SpawnPlayerMob( profile = HumanoidCharacterProfile.RandomWithSpecies(speciesId); } - if (prototype?.StartingGear != null) - { - var startingGear = _prototypeManager.Index(prototype.StartingGear); - EquipStartingGear(entity.Value, startingGear, raiseEvent: false); - } - - // Run loadouts after so stuff like storage loadouts can get var jobLoadout = LoadoutSystem.GetJobPrototype(prototype?.ID); if (_prototypeManager.TryIndex(jobLoadout, out RoleLoadoutPrototype? roleProto)) @@ -203,6 +196,12 @@ public EntityUid SpawnPlayerMob( EquipRoleLoadout(entity.Value, loadout, roleProto); } + if (prototype?.StartingGear != null) + { + var startingGear = _prototypeManager.Index(prototype.StartingGear); + EquipStartingGear(entity.Value, startingGear, raiseEvent: false); + } + var gearEquippedEv = new StartingGearEquippedEvent(entity.Value); RaiseLocalEvent(entity.Value, ref gearEquippedEv); @@ -309,4 +308,4 @@ public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoid HumanoidCharacterProfile = humanoidCharacterProfile; Station = station; } -} +} \ No newline at end of file diff --git a/Content.Shared/Roles/StartingGearPrototype.cs b/Content.Shared/Roles/StartingGearPrototype.cs index fc9ecec7afe0..d96d014f88d3 100644 --- a/Content.Shared/Roles/StartingGearPrototype.cs +++ b/Content.Shared/Roles/StartingGearPrototype.cs @@ -1,30 +1,50 @@ -using Content.Shared.Preferences; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; -namespace Content.Shared.Roles +namespace Content.Shared.Roles; + +[Prototype] +public sealed partial class StartingGearPrototype : IPrototype, IInheritingPrototype { - [Prototype("startingGear")] - public sealed partial class StartingGearPrototype : IPrototype + /// + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = string.Empty; + + /// + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; private set; } + + /// + [AbstractDataField] + public bool Abstract { get; } + + /// + /// The slot and entity prototype ID of the equipment that is to be spawned and equipped onto the entity. + /// + [DataField] + [AlwaysPushInheritance] + public Dictionary Equipment = new(); + + /// + /// The inhand items that are equipped when this starting gear is equipped onto an entity. + /// + [DataField] + [AlwaysPushInheritance] + public List Inhand = new(0); + + /// + /// Inserts entities into the specified slot's storage (if it does have storage). + /// + [DataField] + [AlwaysPushInheritance] + public Dictionary> Storage = new(); + + /// + /// Gets the entity prototype ID of a slot in this starting gear. + /// + public string GetGear(string slot) { - [DataField] - public Dictionary Equipment = new(); - - [DataField] - public List Inhand = new(0); - - /// - /// Inserts entities into the specified slot's storage (if it does have storage). - /// - [DataField] - public Dictionary> Storage = new(); - - [ViewVariables] - [IdDataField] - public string ID { get; private set; } = string.Empty; - - public string GetGear(string slot) - { - return Equipment.TryGetValue(slot, out var equipment) ? equipment : string.Empty; - } + return Equipment.TryGetValue(slot, out var equipment) ? equipment : string.Empty; } } diff --git a/Content.Shared/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index f352c9db63e9..ca53998115bc 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -15,9 +15,9 @@ public abstract class SharedStationSpawningSystem : EntitySystem { [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] protected readonly InventorySystem InventorySystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly SharedStorageSystem _storage = default!; - [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SharedStorageSystem _storage = default!; + [Dependency] private readonly SharedTransformSystem _xformSystem = default!; private EntityQuery _handsQuery; private EntityQuery _inventoryQuery; @@ -91,7 +91,7 @@ public void EquipStartingGear(EntityUid entity, StartingGearPrototype? startingG if (!string.IsNullOrEmpty(equipmentStr)) { var equipmentEntity = EntityManager.SpawnEntity(equipmentStr, xform.Coordinates); - InventorySystem.TryEquip(entity, equipmentEntity, slot.Name, silent: true, force:true); + InventorySystem.TryEquip(entity, equipmentEntity, slot.Name, silent: true, force: true); } } } @@ -122,15 +122,15 @@ public void EquipStartingGear(EntityUid entity, StartingGearPrototype? startingG if (entProtos.Count == 0) continue; - foreach (var ent in entProtos) - { - ents.Add(Spawn(ent, coords)); - } - if (inventoryComp != null && InventorySystem.TryGetSlotEntity(entity, slot, out var slotEnt, inventoryComponent: inventoryComp) && _storageQuery.TryComp(slotEnt, out var storage)) { + foreach (var ent in entProtos) + { + ents.Add(Spawn(ent, coords)); + } + foreach (var ent in ents) { _storage.Insert(slotEnt.Value, ent, out _, storageComp: storage, playSound: false); @@ -145,4 +145,4 @@ public void EquipStartingGear(EntityUid entity, StartingGearPrototype? startingG RaiseLocalEvent(entity, ref ev); } } -} +} \ No newline at end of file diff --git a/Resources/Locale/en-US/preferences/loadout-groups.ftl b/Resources/Locale/en-US/preferences/loadout-groups.ftl index 68a47cba19fc..28863268dfc6 100644 --- a/Resources/Locale/en-US/preferences/loadout-groups.ftl +++ b/Resources/Locale/en-US/preferences/loadout-groups.ftl @@ -1,6 +1,7 @@ # Miscellaneous loadout-group-trinkets = Trinkets loadout-group-glasses = Glasses +loadout-group-backpack = Backpack # Command loadout-group-captain-head = Captain head @@ -19,7 +20,6 @@ loadout-group-hop-outerclothing = Head of Personnel outer clothing loadout-group-passenger-jumpsuit = Passenger jumpsuit loadout-group-passenger-mask = Passenger mask loadout-group-passenger-gloves = Passenger gloves -loadout-group-passenger-backpack = Passenger backpack loadout-group-passenger-outerclothing = Passenger outer clothing loadout-group-passenger-shoes = Passenger shoes @@ -36,12 +36,10 @@ loadout-group-librarian-jumpsuit = Librarian jumpsuit loadout-group-lawyer-jumpsuit = Lawyer jumpsuit loadout-group-lawyer-neck = Lawyer neck -loadout-group-lawyer-backpack = Lawyer backpack loadout-group-chaplain-head = Chaplain head loadout-group-chaplain-mask = Chaplain mask loadout-group-chaplain-jumpsuit = Chaplain jumpsuit -loadout-group-chaplain-backpack = Chaplain backpack loadout-group-chaplain-outerclothing = Chaplain outer clothing loadout-group-chaplain-neck = Chaplain neck @@ -67,13 +65,11 @@ loadout-group-mime-jumpsuit = Mime jumpsuit loadout-group-mime-backpack = Mime backpack loadout-group-mime-outerclothing = Mime outer clothing -loadout-group-musician-backpack = Musician backpack loadout-group-musician-outerclothing = Musician outer clothing # Cargo loadout-group-quartermaster-head = Quartermaster head loadout-group-quartermaster-jumpsuit = Quartermaster jumpsuit -loadout-group-quartermaster-backpack = Quartermaster backpack loadout-group-quartermaster-neck = Quartermaster neck loadout-group-quartermaster-outerclothing = Quartermaster outer clothing loadout-group-quartermaster-shoes = Quartermaster shoes @@ -91,7 +87,6 @@ loadout-group-salvage-specialist-shoes = Salvage Specialist shoes # Engineering loadout-group-chief-engineer-head = Chief Engineer head loadout-group-chief-engineer-jumpsuit = Chief Engineer jumpsuit -loadout-group-chief-engineer-backpack = Chief Engineer backpack loadout-group-chief-engineer-outerclothing = Chief Engineer outer clothing loadout-group-chief-engineer-neck = Chief Engineer neck loadout-group-chief-engineer-shoes = Chief Engineer shoes @@ -114,7 +109,6 @@ loadout-group-atmospheric-technician-shoes = Atmospheric Technician shoes loadout-group-research-director-head = Research Director head loadout-group-research-director-neck = Research Director neck loadout-group-research-director-jumpsuit = Research Director jumpsuit -loadout-group-research-director-backpack = Research Director backpack loadout-group-research-director-outerclothing = Research Director outer clothing loadout-group-research-director-shoes = Research Director shoes @@ -150,7 +144,6 @@ loadout-group-security-id = Security ID loadout-group-detective-head = Detective head loadout-group-detective-neck = Detective neck loadout-group-detective-jumpsuit = Detective jumpsuit -loadout-group-detective-backpack = Detective backpack loadout-group-detective-outerclothing = Detective outer clothing loadout-group-security-cadet-jumpsuit = Security cadet jumpsuit @@ -162,7 +155,6 @@ loadout-group-medical-mask = Medical mask loadout-group-chief-medical-officer-head = Chief Medical Officer head loadout-group-chief-medical-officer-jumpsuit = Chief Medical Officer jumpsuit loadout-group-chief-medical-officer-outerclothing = Chief Medical Officer outer clothing -loadout-group-chief-medical-officer-backpack = Chief Medical Officer backpack loadout-group-chief-medical-officer-shoes = Chief Medical Officer shoes loadout-group-chief-medical-officer-neck = Chief Medical Officer neck @@ -183,7 +175,6 @@ loadout-group-paramedic-head = Paramedic head loadout-group-paramedic-jumpsuit = Paramedic jumpsuit loadout-group-paramedic-outerclothing = Paramedic outer clothing loadout-group-paramedic-shoes = Paramedic shoes -loadout-group-paramedic-backpack = Paramedic backpack # Wildcards loadout-group-reporter-jumpsuit = Reporter jumpsuit diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml deleted file mode 100644 index 71c679a2aa52..000000000000 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml +++ /dev/null @@ -1,422 +0,0 @@ -- type: entity - parent: ClothingBackpack - id: ClothingBackpackFilled - noSpawn: true - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackClown - id: ClothingBackpackClownFilled - components: - - type: StorageFill - contents: - - id: BoxHug - - id: RubberStampClown - - id: CrayonRainbow - -- type: entity - noSpawn: true - parent: ClothingBackpackSecurity - id: ClothingBackpackSecurityFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalSecurity - - id: Flash - - id: MagazinePistol - -- type: entity - noSpawn: true - parent: ClothingBackpackSecurity - id: ClothingBackpackSecurityFilledDetective - components: - - type: StorageFill - contents: - - id: BoxSurvivalSecurity - - id: Flash - - id: ForensicPad - - id: ForensicScanner - -- type: entity - noSpawn: true - parent: ClothingBackpackMedical - id: ClothingBackpackMedicalFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - -- type: entity - noSpawn: true - parent: ClothingBackpackMedical - id: ClothingBackpackParamedicFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - - id: EmergencyRollerBedSpawnFolded - -- type: entity - noSpawn: true - parent: ClothingBackpackCaptain - id: ClothingBackpackCaptainFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- name: StationCharter - #- name: TelescopicBaton -- type: entity - noSpawn: true - parent: ClothingBackpackEngineering - id: ClothingBackpackChiefEngineerFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackScience - id: ClothingBackpackResearchDirectorFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpack - id: ClothingBackpackHOPFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackIan - id: ClothingBackpackHOPIanFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackMedical - id: ClothingBackpackCMOFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackCargo - id: ClothingBackpackQuartermasterFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackSecurity - id: ClothingBackpackHOSFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalSecurity - - id: Flash - - id: MagazinePistol - -- type: entity - noSpawn: true - parent: ClothingBackpackEngineering - id: ClothingBackpackEngineeringFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - -- type: entity - noSpawn: true - parent: ClothingBackpackAtmospherics - id: ClothingBackpackAtmosphericsFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - -- type: entity - noSpawn: true - parent: ClothingBackpackScience - id: ClothingBackpackScienceFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackHydroponics - id: ClothingBackpackHydroponicsFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackMime - id: ClothingBackpackMimeFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: RubberStampMime - -- type: entity - noSpawn: true - parent: ClothingBackpackChemistry - id: ClothingBackpackChemistryFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - -- type: entity - noSpawn: true - parent: ClothingBackpack - id: ClothingBackpackChaplainFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Bible - - id: RubberStampChaplain - -- type: entity - noSpawn: true - parent: ClothingBackpack - id: ClothingBackpackLawyerFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: RubberStampLawyer - -- type: entity - noSpawn: true - parent: ClothingBackpack - id: ClothingBackpackMusicianFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: AcousticGuitarInstrument - - id: SaxophoneInstrument - -- type: entity - noSpawn: true - parent: ClothingBackpack - id: ClothingBackpackLibrarianFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: BookRandom - -- type: entity - noSpawn: true - parent: ClothingBackpack - id: ClothingBackpackDetectiveFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Lighter - - id: CigPackBlack - - id: HandLabeler - - id: BoxForensicPad - -# ERT - -- type: entity - noSpawn: true - parent: ClothingBackpackERTLeader - id: ClothingBackpackERTLeaderFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - id: WeaponDisabler - - id: MedicatedSuture - - id: RegenerativeMesh - - id: BoxZiptie - - id: CrowbarRed - - id: MagazineMagnum - -- type: entity - noSpawn: true - parent: ClothingBackpackERTSecurity - id: ClothingBackpackERTSecurityFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - id: WeaponDisabler - - id: MedicatedSuture - - id: RegenerativeMesh - - id: BoxZiptie - - id: CrowbarRed - - id: MagazinePistol - -- type: entity - noSpawn: true - parent: ClothingBackpackERTMedical - id: ClothingBackpackERTMedicalFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - - id: Hypospray - - id: MedkitAdvancedFilled - - id: CrowbarRed - - id: OmnizineChemistryBottle - - id: EpinephrineChemistryBottle - - id: EpinephrineChemistryBottle - -- type: entity - noSpawn: true - parent: ClothingBackpackERTEngineer - id: ClothingBackpackERTEngineerFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - id: trayScanner - - id: RCD - - id: RCDAmmo - amount: 2 - - id: CableMVStack - - id: CableHVStack - - id: CableApcStack - - id: SheetPlasteel - - id: SheetSteel - - id: SheetGlass - -- type: entity - noSpawn: true - parent: ClothingBackpackERTJanitor - id: ClothingBackpackERTJanitorFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - id: LightReplacer - - id: BoxLightMixed - - id: BoxLightMixed - - id: Soap - - id: CrowbarRed - - id: AdvMopItem - -- type: entity - noSpawn: true - parent: ClothingBackpackERTChaplain - id: ClothingBackpackERTChaplainFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - id: BoxCandle - - id: BoxBodyBag - - id: DrinkWaterMelonJuiceJug - - id: Lantern - - id: Lantern - - id: Bible - - id: CrowbarRed - - id: FoodBakedBunHotX - - id: FoodBakedBunHotX - - id: FoodBakedBunHotX - - id: FoodBakedBunHotX - - id: Lighter - -# Death Squad - -- type: entity - noSpawn: false - parent: ClothingBackpackERTSecurity - id: ClothingBackpackDeathSquadFilled - name: death squad backpack - description: Holds the kit of CentComm's most feared agents. - components: - - type: Storage - grid: - - 0,0,7,6 - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - id: WeaponPulseRifle - - id: WeaponPulsePistol - - id: WeaponRevolverMateba - - id: SpeedLoaderMagnumAP - - id: SpeedLoaderMagnumAP - - id: BoxFlashbang - - id: ToolDebug # spanish army knife - - id: WelderExperimental - - id: Hypospray - - id: DeathAcidifierImplanter # crew will try to steal their amazing hardsuits - - id: FreedomImplanter - -# Cargo - -- type: entity - noSpawn: true - parent: ClothingBackpackCargo - id: ClothingBackpackCargoFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackSalvage - id: ClothingBackpackSalvageFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -# Pirate - -- type: entity - parent: ClothingBackpackSatchelLeather - id: ClothingBackpackPirateFilled - suffix: Filled, Pirate - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Cutlass - - id: WeaponRevolverPirate - - id: ClothingEyesEyepatch diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/duffelbag.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/duffelbag.yml deleted file mode 100644 index 477b8b2f185f..000000000000 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/duffelbag.yml +++ /dev/null @@ -1,275 +0,0 @@ -- type: entity - noSpawn: true - parent: ClothingBackpackDuffel - id: ClothingBackpackDuffelFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelClown - id: ClothingBackpackDuffelClownFilled - components: - - type: StorageFill - contents: - - id: BoxHug - - id: RubberStampClown - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelSecurity - id: ClothingBackpackDuffelSecurityFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalSecurity - - id: Flash - - id: MagazinePistol - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelSecurity - id: ClothingBackpackDuffelSecurityFilledDetective - components: - - type: StorageFill - contents: - - id: BoxSurvivalSecurity - - id: Flash - - id: ForensicPad - - id: ForensicScanner - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelBrigmedic - id: ClothingBackpackDuffelBrigmedicFilled - components: - - type: StorageFill - contents: - - id: Flash - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelMedical - id: ClothingBackpackDuffelMedicalFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelMedical - id: ClothingBackpackDuffelParamedicFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - - id: EmergencyRollerBedSpawnFolded - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelCaptain - id: ClothingBackpackDuffelCaptainFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- name: StationCharter - #- name: TelescopicBaton -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelEngineering - id: ClothingBackpackDuffelChiefEngineerFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelScience - id: ClothingBackpackDuffelResearchDirectorFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffel - id: ClothingBackpackDuffelHOPFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelMedical - id: ClothingBackpackDuffelCMOFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelCargo - id: ClothingBackpackDuffelQuartermasterFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelSecurity - id: ClothingBackpackDuffelHOSFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalSecurity - - id: Flash - - id: MagazinePistol - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelEngineering - id: ClothingBackpackDuffelEngineeringFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelAtmospherics - id: ClothingBackpackDuffelAtmosphericsFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelScience - id: ClothingBackpackDuffelScienceFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelHydroponics - id: ClothingBackpackDuffelHydroponicsFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelMime - id: ClothingBackpackDuffelMimeFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: RubberStampMime - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelChemistry - id: ClothingBackpackDuffelChemistryFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffel - id: ClothingBackpackDuffelChaplainFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Bible - - id: RubberStampChaplain - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffel - id: ClothingBackpackDuffelLawyerFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: RubberStampLawyer - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffel - id: ClothingBackpackDuffelMusicianFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: AcousticGuitarInstrument - - id: SaxophoneInstrument - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffel - id: ClothingBackpackDuffelLibrarianFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: BookRandom - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffel - id: ClothingBackpackDuffelDetectiveFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Lighter - - id: CigPackBlack - - id: BoxForensicPad - - id: HandLabeler - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelCargo - id: ClothingBackpackDuffelCargoFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackDuffelSalvage - id: ClothingBackpackDuffelSalvageFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/satchel.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/satchel.yml deleted file mode 100644 index fe411688298f..000000000000 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/satchel.yml +++ /dev/null @@ -1,300 +0,0 @@ -- type: entity - noSpawn: true - parent: ClothingBackpackSatchel - id: ClothingBackpackSatchelFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchel - id: ClothingBackpackSatchelTools - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Crowbar - - id: Wrench - - id: Screwdriver - - id: Wirecutter - - id: Welder - - id: Multitool - -- type: entity - parent: ClothingBackpackSatchelClown - id: ClothingBackpackSatchelClownFilled - components: - - type: StorageFill - contents: - - id: BoxHug - - id: RubberStampClown - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelSecurity - id: ClothingBackpackSatchelSecurityFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalSecurity - - id: Flash - - id: MagazinePistol - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelSecurity - id: ClothingBackpackSatchelSecurityFilledDetective - components: - - type: StorageFill - contents: - - id: BoxSurvivalSecurity - - id: Flash - - id: ForensicPad - - id: ForensicScanner - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelBrigmedic - id: ClothingBackpackSatchelBrigmedicFilled - components: - - type: StorageFill - contents: - - id: Flash - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelMedical - id: ClothingBackpackSatchelMedicalFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelMedical - id: ClothingBackpackSatchelParamedicFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - - id: EmergencyRollerBedSpawnFolded - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelCaptain - id: ClothingBackpackSatchelCaptainFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- name: StationCharter - #- name: TelescopicBaton -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelEngineering - id: ClothingBackpackSatchelChiefEngineerFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelScience - id: ClothingBackpackSatchelResearchDirectorFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchel - id: ClothingBackpackSatchelHOPFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelMedical - id: ClothingBackpackSatchelCMOFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelCargo - id: ClothingBackpackSatchelQuartermasterFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Flash - #- id: TelescopicBaton - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelSecurity - id: ClothingBackpackSatchelHOSFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalSecurity - - id: Flash - - id: MagazinePistol - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelEngineering - id: ClothingBackpackSatchelEngineeringFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelAtmospherics - id: ClothingBackpackSatchelAtmosphericsFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelScience - id: ClothingBackpackSatchelScienceFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelHydroponics - id: ClothingBackpackSatchelHydroponicsFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelChemistry - id: ClothingBackpackSatchelChemistryFilled - components: - - type: StorageFill - contents: - - id: BoxSurvivalMedical - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchel - id: ClothingBackpackSatchelChaplainFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: Bible - - id: RubberStampChaplain - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchel - id: ClothingBackpackSatchelLawyerFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: RubberStampLawyer - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchel - id: ClothingBackpackSatchelMusicianFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: AcousticGuitarInstrument - - id: SaxophoneInstrument - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchel - id: ClothingBackpackSatchelLibrarianFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: BookRandom - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchel - id: ClothingBackpackSatchelDetectiveFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: BoxForensicPad - - id: Lighter - - id: CigPackBlack - - id: HandLabeler - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelCargo - id: ClothingBackpackSatchelCargoFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelSalvage - id: ClothingBackpackSatchelSalvageFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelMime - id: ClothingBackpackSatchelMimeFilled - components: - - type: StorageFill - contents: - - id: BoxSurvival - - id: RubberStampMime - -- type: entity - noSpawn: true - parent: ClothingBackpackSatchelHolding - id: ClothingBackpackSatchelHoldingAdmin - components: - - type: StorageFill - contents: - - id: GasAnalyzer - - id: trayScanner - - id: AccessConfiguratorUniversal - - type: Unremoveable diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml index 4e4b6d938268..ae57ea634213 100644 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml +++ b/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml @@ -13,23 +13,6 @@ - id: Retractor - id: Scalpel -- type: entity - id: ClothingBackpackDuffelCBURNFilled - parent: ClothingBackpackDuffelCBURN - suffix: Filled - components: - - type: StorageFill - contents: - - id: BoxSurvivalEngineering - - id: WeaponShotgunDoubleBarreled - - id: BoxShotgunIncendiary - amount: 2 - - id: GrenadeFlashBang - amount: 2 - - id: PillAmbuzolPlus - - id: PillAmbuzol - amount: 4 - - type: entity parent: ClothingBackpackDuffelSyndicateMedicalBundle id: ClothingBackpackDuffelSyndicateFilledMedical @@ -320,38 +303,6 @@ - id: PillAmbuzol amount: 3 -- type: entity - parent: ClothingBackpackDuffelSyndicateBundle - id: ClothingBackpackDuffelSyndicateOperative - name: operative duffelbag - components: - - type: StorageFill - contents: - - id: BoxSurvivalSyndicate - - id: WeaponPistolViper - - id: PinpointerSyndicateNuclear - - id: DeathAcidifierImplanter - - -- type: entity - parent: ClothingBackpackDuffelSyndicateMedicalBundle - id: ClothingBackpackDuffelSyndicateOperativeMedic - name: operative medic duffelbag - description: A large duffel bag for holding extra medical supplies. - components: - - type: StorageFill - contents: - - id: SyndiHypo - - id: BoxSurvivalSyndicate - - id: SawAdvanced - - id: Cautery - - id: CombatKnife - - id: WeaponPistolViper - - id: PinpointerSyndicateNuclear - - id: HandheldHealthAnalyzer - - id: CombatMedipen - - id: DeathAcidifierImplanter - - type: entity parent: ClothingBackpackDuffelSyndicateMedicalBundle id: ClothingBackpackDuffelSyndicateMedicalBundleFilled diff --git a/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml b/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml index 5d9fc64e128d..ceaa7442959c 100644 --- a/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml +++ b/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml @@ -257,6 +257,16 @@ - type: Sprite sprite: Clothing/Back/Backpacks/ertchaplain.rsi +- type: entity + parent: ClothingBackpackERTSecurity + id: ClothingBackpackDeathSquad + name: death squad backpack + description: Holds the kit of CentComm's most feared agents. + components: + - type: Storage + grid: + - 0,0,7,6 + #Syndicate - type: entity parent: ClothingBackpack diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/human.yml b/Resources/Prototypes/Entities/Mobs/NPCs/human.yml index 2133694342e8..ec2ed5070d39 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/human.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/human.yml @@ -7,7 +7,7 @@ - type: InputMover - type: MobMover - type: Loadout - prototypes: [PassengerGear] + prototypes: [LimitedPassengerGear] - type: NpcFactionMember factions: - NanoTrasen diff --git a/Resources/Prototypes/Loadouts/Jobs/Cargo/cargo_technician.yml b/Resources/Prototypes/Loadouts/Jobs/Cargo/cargo_technician.yml index f2922d8cfd5f..ecc353445818 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Cargo/cargo_technician.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Cargo/cargo_technician.yml @@ -35,7 +35,7 @@ - type: startingGear id: CargoTechnicianBackpack equipment: - back: ClothingBackpackCargoFilled + back: ClothingBackpackCargo - type: loadout id: CargoTechnicianSatchel @@ -44,7 +44,7 @@ - type: startingGear id: CargoTechnicianSatchel equipment: - back: ClothingBackpackSatchelCargoFilled + back: ClothingBackpackSatchelCargo - type: loadout id: CargoTechnicianDuffel @@ -53,7 +53,7 @@ - type: startingGear id: CargoTechnicianDuffel equipment: - back: ClothingBackpackDuffelCargoFilled + back: ClothingBackpackDuffelCargo # OuterClothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Cargo/quartermaster.yml b/Resources/Prototypes/Loadouts/Jobs/Cargo/quartermaster.yml index d738306c6500..f02da1231615 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Cargo/quartermaster.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Cargo/quartermaster.yml @@ -82,34 +82,6 @@ equipment: neck: ClothingNeckMantleQM -# Back -- type: loadout - id: QuartermasterBackpack - equipment: QuartermasterBackpack - -- type: startingGear - id: QuartermasterBackpack - equipment: - back: ClothingBackpackQuartermasterFilled - -- type: loadout - id: QuartermasterSatchel - equipment: QuartermasterSatchel - -- type: startingGear - id: QuartermasterSatchel - equipment: - back: ClothingBackpackSatchelQuartermasterFilled - -- type: loadout - id: QuartermasterDuffel - equipment: QuartermasterDuffel - -- type: startingGear - id: QuartermasterDuffel - equipment: - back: ClothingBackpackDuffelQuartermasterFilled - # OuterClothing - type: loadout id: QuartermasterWintercoat diff --git a/Resources/Prototypes/Loadouts/Jobs/Cargo/salvage_specialist.yml b/Resources/Prototypes/Loadouts/Jobs/Cargo/salvage_specialist.yml index 2b42fab50ae3..abf787d5100d 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Cargo/salvage_specialist.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Cargo/salvage_specialist.yml @@ -6,7 +6,7 @@ - type: startingGear id: SalvageSpecialistBackpack equipment: - back: ClothingBackpackSalvageFilled + back: ClothingBackpackSalvage - type: loadout id: SalvageSpecialistSatchel @@ -15,7 +15,7 @@ - type: startingGear id: SalvageSpecialistSatchel equipment: - back: ClothingBackpackSatchelSalvageFilled + back: ClothingBackpackSatchelSalvage - type: loadout id: SalvageSpecialistDuffel @@ -24,7 +24,7 @@ - type: startingGear id: SalvageSpecialistDuffel equipment: - back: ClothingBackpackDuffelSalvageFilled + back: ClothingBackpackDuffelSalvage # OuterClothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Civilian/botanist.yml b/Resources/Prototypes/Loadouts/Jobs/Civilian/botanist.yml index 5ab2cecd1534..7dba761a18b6 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Civilian/botanist.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Civilian/botanist.yml @@ -53,7 +53,7 @@ - type: startingGear id: BotanistBackpack equipment: - back: ClothingBackpackHydroponicsFilled + back: ClothingBackpackHydroponics - type: loadout id: BotanistSatchel @@ -62,7 +62,7 @@ - type: startingGear id: BotanistSatchel equipment: - back: ClothingBackpackSatchelHydroponicsFilled + back: ClothingBackpackSatchelHydroponics - type: loadout id: BotanistDuffel @@ -71,7 +71,7 @@ - type: startingGear id: BotanistDuffel equipment: - back: ClothingBackpackDuffelHydroponicsFilled + back: ClothingBackpackDuffelHydroponics # Outer clothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Civilian/chaplain.yml b/Resources/Prototypes/Loadouts/Jobs/Civilian/chaplain.yml index 623e18a01d04..160f06d5cf26 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Civilian/chaplain.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Civilian/chaplain.yml @@ -91,34 +91,6 @@ equipment: jumpsuit: ClothingUniformJumpsuitMonasticRobeLight -# Back -- type: loadout - id: ChaplainBackpack - equipment: ChaplainBackpack - -- type: startingGear - id: ChaplainBackpack - equipment: - back: ClothingBackpackChaplainFilled - -- type: loadout - id: ChaplainSatchel - equipment: ChaplainSatchel - -- type: startingGear - id: ChaplainSatchel - equipment: - back: ClothingBackpackSatchelChaplainFilled - -- type: loadout - id: ChaplainDuffel - equipment: ChaplainDuffel - -- type: startingGear - id: ChaplainDuffel - equipment: - back: ClothingBackpackDuffelChaplainFilled - # Neck - type: loadout id: ChaplainNeck diff --git a/Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml b/Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml index 8b315d50c193..d3a3ce2e6c0a 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml @@ -35,7 +35,7 @@ - type: startingGear id: ClownBackpack equipment: - back: ClothingBackpackClownFilled + back: ClothingBackpackClown - type: loadout id: ClownSatchel @@ -44,7 +44,7 @@ - type: startingGear id: ClownSatchel equipment: - back: ClothingBackpackSatchelClownFilled + back: ClothingBackpackSatchelClown - type: loadout id: ClownDuffel @@ -53,7 +53,7 @@ - type: startingGear id: ClownDuffel equipment: - back: ClothingBackpackDuffelClownFilled + back: ClothingBackpackDuffelClown # Shoes - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Civilian/lawyer.yml b/Resources/Prototypes/Loadouts/Jobs/Civilian/lawyer.yml index 5edd3ecf76de..00be12443078 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Civilian/lawyer.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Civilian/lawyer.yml @@ -97,32 +97,4 @@ - type: startingGear id: LawyerNeck equipment: - neck: ClothingNeckLawyerbadge - -# Backpack -- type: loadout - id: LawyerBackpack - equipment: LawyerBackpack - -- type: startingGear - id: LawyerBackpack - equipment: - back: ClothingBackpackLawyerFilled - -- type: loadout - id: LawyerSatchel - equipment: LawyerSatchel - -- type: startingGear - id: LawyerSatchel - equipment: - back: ClothingBackpackSatchelLawyerFilled - -- type: loadout - id: LawyerDuffel - equipment: LawyerDuffel - -- type: startingGear - id: LawyerDuffel - equipment: - back: ClothingBackpackDuffelLawyerFilled + neck: ClothingNeckLawyerbadge \ No newline at end of file diff --git a/Resources/Prototypes/Loadouts/Jobs/Civilian/mime.yml b/Resources/Prototypes/Loadouts/Jobs/Civilian/mime.yml index 95647052b872..e60b2807ff45 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Civilian/mime.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Civilian/mime.yml @@ -81,7 +81,7 @@ - type: startingGear id: MimeBackpack equipment: - back: ClothingBackpackMimeFilled + back: ClothingBackpackMime - type: loadout id: MimeSatchel @@ -90,7 +90,7 @@ - type: startingGear id: MimeSatchel equipment: - back: ClothingBackpackSatchelMimeFilled + back: ClothingBackpackSatchelMime - type: loadout id: MimeDuffel @@ -99,7 +99,7 @@ - type: startingGear id: MimeDuffel equipment: - back: ClothingBackpackDuffelMimeFilled + back: ClothingBackpackDuffelMime # Outerclothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Civilian/musician.yml b/Resources/Prototypes/Loadouts/Jobs/Civilian/musician.yml index 579481c0eef9..c26da03628ea 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Civilian/musician.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Civilian/musician.yml @@ -1,31 +1,3 @@ -# Back -- type: loadout - id: MusicianBackpack - equipment: MusicianBackpack - -- type: startingGear - id: MusicianBackpack - equipment: - back: ClothingBackpackMusicianFilled - -- type: loadout - id: MusicianSatchel - equipment: MusicianSatchel - -- type: startingGear - id: MusicianSatchel - equipment: - back: ClothingBackpackSatchelMusicianFilled - -- type: loadout - id: MusicianDuffel - equipment: MusicianDuffel - -- type: startingGear - id: MusicianDuffel - equipment: - back: ClothingBackpackDuffelMusicianFilled - # Outerclothing - type: loadout id: MusicianWintercoat diff --git a/Resources/Prototypes/Loadouts/Jobs/Civilian/passenger.yml b/Resources/Prototypes/Loadouts/Jobs/Civilian/passenger.yml index 5c09b1299f32..84685e1702a8 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Civilian/passenger.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Civilian/passenger.yml @@ -75,7 +75,7 @@ - type: startingGear id: CommonBackpack equipment: - back: ClothingBackpackFilled + back: ClothingBackpack - type: loadout id: CommonSatchel @@ -84,7 +84,7 @@ - type: startingGear id: CommonSatchel equipment: - back: ClothingBackpackSatchelFilled + back: ClothingBackpackSatchel - type: loadout id: CommonDuffel @@ -93,7 +93,7 @@ - type: startingGear id: CommonDuffel equipment: - back: ClothingBackpackDuffelFilled + back: ClothingBackpackDuffel # Gloves - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml b/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml index 6ec2d7f78902..50786d615434 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml @@ -90,7 +90,7 @@ - type: startingGear id: CaptainBackpack equipment: - back: ClothingBackpackCaptainFilled + back: ClothingBackpackCaptain - type: loadout id: CaptainSatchel @@ -99,7 +99,7 @@ - type: startingGear id: CaptainSatchel equipment: - back: ClothingBackpackSatchelCaptainFilled + back: ClothingBackpackSatchelCaptain - type: loadout id: CaptainDuffel @@ -108,7 +108,7 @@ - type: startingGear id: CaptainDuffel equipment: - back: ClothingBackpackDuffelCaptainFilled + back: ClothingBackpackDuffelCaptain # Outer clothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml b/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml index 0abba16f1246..1e4e5d527ffa 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml @@ -57,33 +57,6 @@ neck: ClothingNeckMantleHOP # Back -- type: loadout - id: HoPBackpack - equipment: HoPBackpack - -- type: startingGear - id: HoPBackpack - equipment: - back: ClothingBackpackHOPFilled - -- type: loadout - id: HoPSatchel - equipment: HoPSatchel - -- type: startingGear - id: HoPSatchel - equipment: - back: ClothingBackpackSatchelHOPFilled - -- type: loadout - id: HoPDuffel - equipment: HoPDuffel - -- type: startingGear - id: HoPDuffel - equipment: - back: ClothingBackpackDuffelHOPFilled - - type: loadout id: HoPBackpackIan equipment: HoPBackpackIan @@ -94,7 +67,7 @@ - type: startingGear id: HoPBackpackIan equipment: - back: ClothingBackpackHOPIanFilled + back: ClothingBackpackIan # Outerclothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Engineering/atmospheric_technician.yml b/Resources/Prototypes/Loadouts/Jobs/Engineering/atmospheric_technician.yml index fc9bbbf28234..5f1e753b9f73 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Engineering/atmospheric_technician.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Engineering/atmospheric_technician.yml @@ -34,7 +34,7 @@ - type: startingGear id: AtmosphericTechnicianBackpack equipment: - back: ClothingBackpackAtmosphericsFilled + back: ClothingBackpackAtmospherics - type: loadout id: AtmosphericTechnicianSatchel @@ -43,7 +43,7 @@ - type: startingGear id: AtmosphericTechnicianSatchel equipment: - back: ClothingBackpackSatchelAtmosphericsFilled + back: ClothingBackpackSatchelAtmospherics - type: loadout id: AtmosphericTechnicianDuffel @@ -52,7 +52,7 @@ - type: startingGear id: AtmosphericTechnicianDuffel equipment: - back: ClothingBackpackDuffelAtmosphericsFilled + back: ClothingBackpackDuffelAtmospherics # OuterClothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Engineering/chief_engineer.yml b/Resources/Prototypes/Loadouts/Jobs/Engineering/chief_engineer.yml index 23732050669b..5bfbb8c20118 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Engineering/chief_engineer.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Engineering/chief_engineer.yml @@ -68,34 +68,6 @@ equipment: neck: ClothingNeckMantleCE -# Back -- type: loadout - id: ChiefEngineerBackpack - equipment: ChiefEngineerBackpack - -- type: startingGear - id: ChiefEngineerBackpack - equipment: - back: ClothingBackpackChiefEngineerFilled - -- type: loadout - id: ChiefEngineerSatchel - equipment: ChiefEngineerSatchel - -- type: startingGear - id: ChiefEngineerSatchel - equipment: - back: ClothingBackpackSatchelChiefEngineerFilled - -- type: loadout - id: ChiefEngineerDuffel - equipment: ChiefEngineerDuffel - -- type: startingGear - id: ChiefEngineerDuffel - equipment: - back: ClothingBackpackDuffelChiefEngineerFilled - # OuterClothing - type: loadout id: ChiefEngineerWintercoat diff --git a/Resources/Prototypes/Loadouts/Jobs/Engineering/station_engineer.yml b/Resources/Prototypes/Loadouts/Jobs/Engineering/station_engineer.yml index 7104598b3ae6..7f9e20e2aa4f 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Engineering/station_engineer.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Engineering/station_engineer.yml @@ -118,7 +118,7 @@ - type: startingGear id: StationEngineerBackpack equipment: - back: ClothingBackpackEngineeringFilled + back: ClothingBackpackEngineering - type: loadout id: StationEngineerSatchel @@ -127,7 +127,7 @@ - type: startingGear id: StationEngineerSatchel equipment: - back: ClothingBackpackSatchelEngineeringFilled + back: ClothingBackpackSatchelEngineering - type: loadout id: StationEngineerDuffel @@ -136,7 +136,7 @@ - type: startingGear id: StationEngineerDuffel equipment: - back: ClothingBackpackDuffelEngineeringFilled + back: ClothingBackpackDuffelEngineering # OuterClothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Medical/chemist.yml b/Resources/Prototypes/Loadouts/Jobs/Medical/chemist.yml index 9b05120b1aa3..289910c8c852 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Medical/chemist.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Medical/chemist.yml @@ -25,7 +25,7 @@ - type: startingGear id: ChemistBackpack equipment: - back: ClothingBackpackChemistryFilled + back: ClothingBackpackChemistry - type: loadout id: ChemistSatchel @@ -34,7 +34,7 @@ - type: startingGear id: ChemistSatchel equipment: - back: ClothingBackpackSatchelChemistryFilled + back: ClothingBackpackSatchelChemistry - type: loadout id: ChemistDuffel @@ -43,7 +43,7 @@ - type: startingGear id: ChemistDuffel equipment: - back: ClothingBackpackDuffelChemistryFilled + back: ClothingBackpackDuffelChemistry # Outer clothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Medical/chief_medical_officer.yml b/Resources/Prototypes/Loadouts/Jobs/Medical/chief_medical_officer.yml index 8cf8148518a5..f7436a4281a6 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Medical/chief_medical_officer.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Medical/chief_medical_officer.yml @@ -73,34 +73,6 @@ equipment: neck: ClothingNeckMantleCMO -# Back -- type: loadout - id: ChiefMedicalOfficerBackpack - equipment: ChiefMedicalOfficerBackpack - -- type: startingGear - id: ChiefMedicalOfficerBackpack - equipment: - back: ClothingBackpackCMOFilled - -- type: loadout - id: ChiefMedicalOfficerSatchel - equipment: ChiefMedicalOfficerSatchel - -- type: startingGear - id: ChiefMedicalOfficerSatchel - equipment: - back: ClothingBackpackSatchelCMOFilled - -- type: loadout - id: ChiefMedicalOfficerDuffel - equipment: ChiefMedicalOfficerDuffel - -- type: startingGear - id: ChiefMedicalOfficerDuffel - equipment: - back: ClothingBackpackDuffelCMOFilled - # Outer clothing - type: loadout id: ChiefMedicalOfficerLabCoat diff --git a/Resources/Prototypes/Loadouts/Jobs/Medical/medical_doctor.yml b/Resources/Prototypes/Loadouts/Jobs/Medical/medical_doctor.yml index ebad9b9f8477..a64d60ba1f68 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Medical/medical_doctor.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Medical/medical_doctor.yml @@ -178,7 +178,7 @@ - type: startingGear id: MedicalDoctorBackpack equipment: - back: ClothingBackpackMedicalFilled + back: ClothingBackpackMedical - type: loadout id: MedicalDoctorSatchel @@ -187,7 +187,7 @@ - type: startingGear id: MedicalDoctorSatchel equipment: - back: ClothingBackpackSatchelMedicalFilled + back: ClothingBackpackSatchelMedical - type: loadout id: MedicalDoctorDuffel @@ -196,7 +196,7 @@ - type: startingGear id: MedicalDoctorDuffel equipment: - back: ClothingBackpackDuffelMedicalFilled + back: ClothingBackpackDuffelMedical # OuterClothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Medical/paramedic.yml b/Resources/Prototypes/Loadouts/Jobs/Medical/paramedic.yml index f393109eea42..7fd8d4a32f10 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Medical/paramedic.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Medical/paramedic.yml @@ -27,34 +27,6 @@ equipment: jumpsuit: ClothingUniformJumpskirtParamedic -# Back -- type: loadout - id: ParamedicBackpack - equipment: ParamedicBackpack - -- type: startingGear - id: ParamedicBackpack - equipment: - back: ClothingBackpackParamedicFilled - -- type: loadout - id: ParamedicSatchel - equipment: ParamedicSatchel - -- type: startingGear - id: ParamedicSatchel - equipment: - back: ClothingBackpackSatchelParamedicFilled - -- type: loadout - id: ParamedicDuffel - equipment: ParamedicDuffel - -- type: startingGear - id: ParamedicDuffel - equipment: - back: ClothingBackpackDuffelParamedicFilled - # Outer clothing - type: loadout id: ParamedicWindbreaker diff --git a/Resources/Prototypes/Loadouts/Jobs/Science/research_director.yml b/Resources/Prototypes/Loadouts/Jobs/Science/research_director.yml index 4d81b801e806..748e5d217c2d 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Science/research_director.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Science/research_director.yml @@ -43,34 +43,6 @@ equipment: jumpsuit: ClothingUniformJumpskirtResearchDirector -# Back -- type: loadout - id: ResearchDirectorBackpack - equipment: ResearchDirectorBackpack - -- type: startingGear - id: ResearchDirectorBackpack - equipment: - back: ClothingBackpackResearchDirectorFilled - -- type: loadout - id: ResearchDirectorSatchel - equipment: ResearchDirectorSatchel - -- type: startingGear - id: ResearchDirectorSatchel - equipment: - back: ClothingBackpackSatchelResearchDirectorFilled - -- type: loadout - id: ResearchDirectorDuffel - equipment: ResearchDirectorDuffel - -- type: startingGear - id: ResearchDirectorDuffel - equipment: - back: ClothingBackpackDuffelResearchDirectorFilled - # OuterClothing - type: loadout id: ResearchDirectorLabCoat diff --git a/Resources/Prototypes/Loadouts/Jobs/Science/scientist.yml b/Resources/Prototypes/Loadouts/Jobs/Science/scientist.yml index c03ae4cfbc4d..ee30a9cf4158 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Science/scientist.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Science/scientist.yml @@ -120,7 +120,7 @@ - type: startingGear id: ScientistBackpack equipment: - back: ClothingBackpackScienceFilled + back: ClothingBackpackScience - type: loadout id: ScientistSatchel @@ -129,7 +129,7 @@ - type: startingGear id: ScientistSatchel equipment: - back: ClothingBackpackSatchelScienceFilled + back: ClothingBackpackSatchelScience - type: loadout id: ScientistDuffel @@ -138,7 +138,7 @@ - type: startingGear id: ScientistDuffel equipment: - back: ClothingBackpackDuffelScienceFilled + back: ClothingBackpackDuffelScience # OuterClothing - type: loadout diff --git a/Resources/Prototypes/Loadouts/Jobs/Security/detective.yml b/Resources/Prototypes/Loadouts/Jobs/Security/detective.yml index c16d24e5b83a..888ed971e17d 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Security/detective.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Security/detective.yml @@ -64,34 +64,6 @@ equipment: jumpsuit: ClothingUniformJumpskirtDetectiveGrey -# Back -- type: loadout - id: DetectiveBackpack - equipment: DetectiveBackpack - -- type: startingGear - id: DetectiveBackpack - equipment: - back: ClothingBackpackSecurityFilledDetective - -- type: loadout - id: DetectiveSatchel - equipment: DetectiveSatchel - -- type: startingGear - id: DetectiveSatchel - equipment: - back: ClothingBackpackSatchelSecurityFilledDetective - -- type: loadout - id: DetectiveDuffel - equipment: DetectiveDuffel - -- type: startingGear - id: DetectiveDuffel - equipment: - back: ClothingBackpackDuffelSecurityFilledDetective - # OuterClothing - type: loadout id: DetectiveArmorVest diff --git a/Resources/Prototypes/Loadouts/Jobs/Security/security_officer.yml b/Resources/Prototypes/Loadouts/Jobs/Security/security_officer.yml index b2b9e1e0c12d..321cc46feb56 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Security/security_officer.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Security/security_officer.yml @@ -110,7 +110,7 @@ - type: startingGear id: SecurityBackpack equipment: - back: ClothingBackpackSecurityFilled + back: ClothingBackpackSecurity - type: loadout id: SecuritySatchel @@ -119,7 +119,7 @@ - type: startingGear id: SecuritySatchel equipment: - back: ClothingBackpackSatchelSecurityFilled + back: ClothingBackpackSatchelSecurity - type: loadout id: SecurityDuffel @@ -128,7 +128,7 @@ - type: startingGear id: SecurityDuffel equipment: - back: ClothingBackpackDuffelSecurityFilled + back: ClothingBackpackDuffelSecurity # Belt - type: loadout diff --git a/Resources/Prototypes/Loadouts/loadout_groups.yml b/Resources/Prototypes/Loadouts/loadout_groups.yml index d889514a33b9..7f8a9ff4e488 100644 --- a/Resources/Prototypes/Loadouts/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/loadout_groups.yml @@ -103,9 +103,9 @@ id: HoPBackpack name: loadout-group-hop-backpack loadouts: - - HoPBackpack - - HoPSatchel - - HoPDuffel + - CommonBackpack + - CommonSatchel + - CommonDuffel - HoPBackpackIan - type: loadoutGroup @@ -122,8 +122,8 @@ loadouts: - GreyJumpsuit - GreyJumpskirt - - RainbowJumpsuit - AncientJumpsuit + - RainbowJumpsuit - type: loadoutGroup id: PassengerFace @@ -141,7 +141,7 @@ - type: loadoutGroup id: CommonBackpack - name: loadout-group-passenger-backpack + name: loadout-group-backpack loadouts: - CommonBackpack - CommonSatchel @@ -247,14 +247,6 @@ loadouts: - LawyerNeck -- type: loadoutGroup - id: LawyerBackpack - name: loadout-group-lawyer-backpack - loadouts: - - LawyerBackpack - - LawyerSatchel - - LawyerDuffel - - type: loadoutGroup id: ChaplainHead name: loadout-group-chaplain-head @@ -282,14 +274,6 @@ - ChaplainRobesLight - ChaplainRobesDark -- type: loadoutGroup - id: ChaplainBackpack - name: loadout-group-chaplain-backpack - loadouts: - - ChaplainBackpack - - ChaplainSatchel - - ChaplainDuffel - - type: loadoutGroup id: ChaplainOuterClothing name: loadout-group-chaplain-outerclothing @@ -446,14 +430,6 @@ loadouts: - MimeWintercoat -- type: loadoutGroup - id: MusicianBackpack - name: loadout-group-musician-backpack - loadouts: - - MusicianBackpack - - MusicianSatchel - - MusicianDuffel - - type: loadoutGroup id: MusicianOuterClothing name: loadout-group-musician-outerclothing @@ -480,14 +456,6 @@ - QuartermasterTurtleneckSkirt - QuartermasterFormalSuit -- type: loadoutGroup - id: QuartermasterBackpack - name: loadout-group-quartermaster-backpack - loadouts: - - QuartermasterBackpack - - QuartermasterSatchel - - QuartermasterDuffel - - type: loadoutGroup id: QuartermasterNeck name: loadout-group-quartermaster-neck @@ -586,14 +554,6 @@ - ChiefEngineerTurtleneck - ChiefEngineerTurtleneckSkirt -- type: loadoutGroup - id: ChiefEngineerBackpack - name: loadout-group-chief-engineer-backpack - loadouts: - - ChiefEngineerBackpack - - ChiefEngineerSatchel - - ChiefEngineerDuffel - - type: loadoutGroup id: ChiefEngineerNeck name: loadout-group-chief-engineer-neck @@ -725,14 +685,6 @@ - ResearchDirectorJumpsuit - ResearchDirectorJumpskirt -- type: loadoutGroup - id: ResearchDirectorBackpack - name: loadout-group-research-director-backpack - loadouts: - - ResearchDirectorBackpack - - ResearchDirectorSatchel - - ResearchDirectorDuffel - - type: loadoutGroup id: ResearchDirectorOuterClothing name: loadout-group-research-director-outerclothing @@ -964,14 +916,6 @@ - NoirJumpsuit - NoirJumpskirt -- type: loadoutGroup - id: DetectiveBackpack - name: loadout-group-detective-backpack - loadouts: - - DetectiveBackpack - - DetectiveSatchel - - DetectiveDuffel - - type: loadoutGroup id: DetectiveOuterClothing name: loadout-group-detective-outerclothing @@ -1013,14 +957,6 @@ - ChiefMedicalOfficerLabCoat - ChiefMedicalOfficerWintercoat -- type: loadoutGroup - id: ChiefMedicalOfficerBackpack - name: loadout-group-chief-medical-officer-backpack - loadouts: - - ChiefMedicalOfficerBackpack - - ChiefMedicalOfficerSatchel - - ChiefMedicalOfficerDuffel - - type: loadoutGroup id: ChiefMedicalOfficerNeck name: loadout-group-chief-medical-officer-neck @@ -1160,14 +1096,6 @@ - ParamedicWindbreaker - ParamedicWintercoat -- type: loadoutGroup - id: ParamedicBackpack - name: loadout-group-paramedic-backpack - loadouts: - - ParamedicBackpack - - ParamedicSatchel - - ParamedicDuffel - - type: loadoutGroup id: ParamedicShoes name: loadout-group-paramedic-shoes diff --git a/Resources/Prototypes/Loadouts/role_loadouts.yml b/Resources/Prototypes/Loadouts/role_loadouts.yml index 5bc6fc4572b0..4ff2775dfc1d 100644 --- a/Resources/Prototypes/Loadouts/role_loadouts.yml +++ b/Resources/Prototypes/Loadouts/role_loadouts.yml @@ -75,7 +75,7 @@ groups: - LawyerNeck - LawyerJumpsuit - - LawyerBackpack + - CommonBackpack - Glasses - Trinkets @@ -86,7 +86,7 @@ - ChaplainMask - ChaplainNeck - ChaplainJumpsuit - - ChaplainBackpack + - CommonBackpack - ChaplainOuterClothing - Glasses - Trinkets @@ -137,7 +137,7 @@ - type: roleLoadout id: JobMusician groups: - - MusicianBackpack + - CommonBackpack - MusicianOuterClothing - Glasses - Trinkets @@ -149,7 +149,7 @@ - QuartermasterHead - QuartermasterNeck - QuartermasterJumpsuit - - QuartermasterBackpack + - CargoTechnicianBackpack - QuartermasterOuterClothing - QuartermasterShoes - Glasses @@ -181,7 +181,7 @@ groups: - ChiefEngineerHead - ChiefEngineerJumpsuit - - ChiefEngineerBackpack + - StationEngineerBackpack - ChiefEngineerNeck - ChiefEngineerOuterClothing - ChiefEngineerShoes @@ -221,7 +221,7 @@ - ResearchDirectorHead - ResearchDirectorNeck - ResearchDirectorJumpsuit - - ResearchDirectorBackpack + - ScientistBackpack - ResearchDirectorOuterClothing - ScientistGloves - ResearchDirectorShoes @@ -292,7 +292,7 @@ - DetectiveHead - DetectiveNeck - DetectiveJumpsuit - - DetectiveBackpack + - SecurityBackpack - DetectiveOuterClothing - SecurityShoes - Trinkets @@ -312,7 +312,7 @@ - MedicalMask - ChiefMedicalOfficerJumpsuit - MedicalGloves - - ChiefMedicalOfficerBackpack + - MedicalBackpack - ChiefMedicalOfficerOuterClothing - ChiefMedicalOfficerNeck - ChiefMedicalOfficerShoes @@ -359,7 +359,7 @@ - MedicalMask - ParamedicJumpsuit - MedicalGloves - - ParamedicBackpack + - MedicalBackpack - ParamedicOuterClothing - ParamedicShoes - Glasses diff --git a/Resources/Prototypes/Roles/Antags/Thief.yml b/Resources/Prototypes/Roles/Antags/Thief.yml index 131db8cf1da8..d85d366f6b55 100644 --- a/Resources/Prototypes/Roles/Antags/Thief.yml +++ b/Resources/Prototypes/Roles/Antags/Thief.yml @@ -4,3 +4,10 @@ antagonist: true setPreference: true objective: roles-antag-thief-objective + +- type: startingGear + id: ThiefGear + storage: + back: + - ToolboxThief + - ClothingHandsChameleonThief \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Antags/ninja.yml b/Resources/Prototypes/Roles/Antags/ninja.yml index a7492bd1b160..cb32dc2c102d 100644 --- a/Resources/Prototypes/Roles/Antags/ninja.yml +++ b/Resources/Prototypes/Roles/Antags/ninja.yml @@ -4,3 +4,33 @@ antagonist: true setPreference: false objective: roles-antag-space-ninja-objective + +#Ninja Gear +- type: startingGear + id: SpaceNinjaGear + equipment: + jumpsuit: ClothingUniformJumpsuitNinja + back: ClothingBackpackSatchel + mask: ClothingMaskNinja + head: ClothingHeadHelmetSpaceNinja + eyes: ClothingEyesVisorNinja + gloves: ClothingHandsGlovesSpaceNinja + outerClothing: ClothingOuterSuitSpaceNinja + shoes: ClothingShoesSpaceNinja + id: AgentIDCard + ears: ClothingHeadsetGrey + pocket1: SpiderCharge + pocket2: PinpointerStation + belt: EnergyKatana + suitstorage: OxygenTankFilled + inhand: + - JetpackBlackFilled + storage: + back: # belt holds katana so satchel has the tools for sabotaging things + - BoxSurvival + - Crowbar + - Wrench + - Screwdriver + - Wirecutter + - Welder + - Multitool \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Antags/nukeops.yml b/Resources/Prototypes/Roles/Antags/nukeops.yml index 4075005e6859..e91d50f53cd9 100644 --- a/Resources/Prototypes/Roles/Antags/nukeops.yml +++ b/Resources/Prototypes/Roles/Antags/nukeops.yml @@ -34,3 +34,66 @@ department: Security time: 18000 # 5h # should be changed to nukie playtime when thats tracked (wyci) + +#Nuclear Operative Gear +- type: startingGear + id: SyndicateOperativeGearFull + equipment: + jumpsuit: ClothingUniformJumpsuitOperative + back: ClothingBackpackDuffelSyndicate + mask: ClothingMaskGasSyndicate + eyes: ClothingEyesHudSyndicate + ears: ClothingHeadsetAltSyndicate + gloves: ClothingHandsGlovesCombat + outerClothing: ClothingOuterHardsuitSyndie + shoes: ClothingShoesBootsCombatFilled + id: SyndiPDA + pocket1: DoubleEmergencyOxygenTankFilled + pocket2: BaseUplinkRadio40TC + belt: ClothingBeltMilitaryWebbing + storage: + back: + - BoxSurvivalSyndicate + - WeaponPistolViper + - PinpointerSyndicateNuclear + - DeathAcidifierImplanter + +#Nuclear Operative Commander Gear +- type: startingGear + id: SyndicateCommanderGearFull + parent: SyndicateOperativeGearFull + equipment: + neck: SyndicateWhistle + outerClothing: ClothingOuterHardsuitSyndieCommander + inhand: + - NukeOpsDeclarationOfWar + +#Nuclear Operative Medic Gear +- type: startingGear + id: SyndicateOperativeMedicFull + parent: SyndicateOperativeGearFull + equipment: + eyes: ClothingEyesHudSyndicateAgent + outerClothing: ClothingOuterHardsuitSyndieMedic + shoes: ClothingShoesBootsMagSyndie + id: SyndiAgentPDA + belt: ClothingBeltMilitaryWebbingMedFilled + storage: + back: + - SyndiHypo + - BoxSurvivalSyndicate + - SawAdvanced + - Cautery + - CombatKnife + - WeaponPistolViper + - PinpointerSyndicateNuclear + - HandheldHealthAnalyzer + - CombatMedipen + - DeathAcidifierImplanter + +#Lone Operative Gear +- type: startingGear + id: SyndicateLoneOperativeGearFull + parent: SyndicateOperativeGearFull + equipment: + pocket2: BaseUplinkRadio60TC \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Antags/pirate.yml b/Resources/Prototypes/Roles/Antags/pirate.yml index 235ccfdab86f..849e9dc1f289 100644 --- a/Resources/Prototypes/Roles/Antags/pirate.yml +++ b/Resources/Prototypes/Roles/Antags/pirate.yml @@ -2,7 +2,7 @@ id: PirateGear equipment: jumpsuit: ClothingUniformJumpsuitPirate - back: ClothingBackpackPirateFilled + back: ClothingBackpackSatchelLeather head: ClothingHeadBandBlack shoes: ClothingShoesBootsLaceup id: PiratePDA @@ -11,25 +11,15 @@ - type: startingGear id: PirateCaptainGear + parent: PirateGear equipment: - jumpsuit: ClothingUniformJumpsuitPirate - back: ClothingBackpackPirateFilled head: ClothingHeadHatPirate - shoes: ClothingShoesBootsLaceup - id: PiratePDA - belt: ClothingBeltUtility - pocket1: AppraisalTool pocket2: EnergyCutlass outerClothing: ClothingOuterCoatPirate - type: startingGear id: PirateFirstmateGear + parent: PirateGear equipment: - jumpsuit: ClothingUniformJumpsuitPirate - back: ClothingBackpackPirateFilled head: ClothingHeadHatPirateTricord - shoes: ClothingShoesBootsLaceup - id: PiratePDA - belt: ClothingBeltUtility - pocket1: AppraisalTool outerClothing: ClothingOuterCoatGentle diff --git a/Resources/Prototypes/Roles/Antags/revolutionary.yml b/Resources/Prototypes/Roles/Antags/revolutionary.yml index c5e6cb814987..3bdc9d7977c7 100644 --- a/Resources/Prototypes/Roles/Antags/revolutionary.yml +++ b/Resources/Prototypes/Roles/Antags/revolutionary.yml @@ -11,3 +11,10 @@ antagonist: true setPreference: false objective: roles-antag-rev-objective + +- type: startingGear + id: HeadRevGear + storage: + back: + - Flash + - ClothingEyesGlassesSunglasses \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Antags/traitor.yml b/Resources/Prototypes/Roles/Antags/traitor.yml index a4e1fe8d1097..f192733b7e1e 100644 --- a/Resources/Prototypes/Roles/Antags/traitor.yml +++ b/Resources/Prototypes/Roles/Antags/traitor.yml @@ -4,3 +4,36 @@ antagonist: true setPreference: true objective: roles-antag-syndicate-agent-objective + +# Syndicate Operative Outfit - Monkey +- type: startingGear + id: SyndicateOperativeGearMonkey + equipment: + head: ClothingHeadHatOutlawHat + jumpsuit: ClothingUniformJumpsuitOperative + mask: CigaretteSyndicate + +# Syndicate Operative Outfit - Barratry +- type: startingGear + id: SyndicateOperativeGearExtremelyBasic + equipment: + jumpsuit: ClothingUniformJumpsuitOperative + back: ClothingBackpackSyndicate + shoes: ClothingShoesBootsCombatFilled + gloves: ClothingHandsGlovesColorBlack + storage: + back: + - BoxSurvivalSyndicate + - WeaponPistolViper + - PinpointerSyndicateNuclear + - DeathAcidifierImplanter + +#Syndicate Operative Outfit - Basic +- type: startingGear + id: SyndicateOperativeGearBasic + parent: SyndicateOperativeGearExtremelyBasic + equipment: + ears: ClothingHeadsetAltSyndicate + gloves: ClothingHandsGlovesCombat + pocket1: BaseUplinkRadio40TC + id: SyndiPDA \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml b/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml index 46c5273549d1..78ab1847a459 100644 --- a/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml +++ b/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml @@ -17,4 +17,7 @@ equipment: id: CargoPDA ears: ClothingHeadsetCargo - pocket1: AppraisalTool \ No newline at end of file + pocket1: AppraisalTool + storage: + back: + - BoxSurvival \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml b/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml index 36d904c56eaf..e39c327e903a 100644 --- a/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml +++ b/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml @@ -41,4 +41,8 @@ id: QuartermasterPDA ears: ClothingHeadsetQM belt: BoxFolderClipboard - pocket1: AppraisalTool \ No newline at end of file + pocket1: AppraisalTool + storage: + back: + - BoxSurvival + - Flash \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Cargo/salvage_specialist.yml b/Resources/Prototypes/Roles/Jobs/Cargo/salvage_specialist.yml index 7b3fcd6b58dc..4dd20f51fb03 100644 --- a/Resources/Prototypes/Roles/Jobs/Cargo/salvage_specialist.yml +++ b/Resources/Prototypes/Roles/Jobs/Cargo/salvage_specialist.yml @@ -23,4 +23,7 @@ equipment: jumpsuit: ClothingUniformJumpsuitSalvageSpecialist id: SalvagePDA - ears: ClothingHeadsetCargo \ No newline at end of file + ears: ClothingHeadsetCargo + storage: + back: + - BoxSurvival \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/assistant.yml b/Resources/Prototypes/Roles/Jobs/Civilian/assistant.yml index b43f929d6948..06b66448cb8f 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/assistant.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/assistant.yml @@ -14,3 +14,6 @@ equipment: id: PassengerPDA ears: ClothingHeadsetGrey + storage: + back: + - BoxSurvival diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/bartender.yml b/Resources/Prototypes/Roles/Jobs/Civilian/bartender.yml index a49e3cef7fac..6bceebc65ea6 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/bartender.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/bartender.yml @@ -24,3 +24,6 @@ shoes: ClothingShoesColorBlack id: BartenderPDA ears: ClothingHeadsetService + storage: + back: + - BoxSurvival diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/botanist.yml b/Resources/Prototypes/Roles/Jobs/Civilian/botanist.yml index feed9e7e6bf6..e8cda189145a 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/botanist.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/botanist.yml @@ -21,3 +21,6 @@ id: BotanistPDA ears: ClothingHeadsetService belt: ClothingBeltPlantFilled + storage: + back: + - BoxSurvival diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml index e3dc1cb23db4..0ae49f853c63 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml @@ -20,3 +20,8 @@ shoes: ClothingShoesColorBlack id: ChaplainPDA ears: ClothingHeadsetService + storage: + back: + - BoxSurvival + - Bible + - RubberStampChaplain diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/chef.yml b/Resources/Prototypes/Roles/Jobs/Civilian/chef.yml index 1dda98bb37d4..0d8d1911e56d 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/chef.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/chef.yml @@ -25,3 +25,6 @@ id: ChefPDA ears: ClothingHeadsetService belt: ClothingBeltChefFilled + storage: + back: + - BoxSurvival diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml b/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml index c70b4a99435d..627469f6c550 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml @@ -32,4 +32,9 @@ pocket1: BikeHorn pocket2: ClownRecorder id: ClownPDA - ears: ClothingHeadsetService \ No newline at end of file + ears: ClothingHeadsetService + storage: + back: + - BoxHug + - RubberStampClown + - CrayonRainbow \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/janitor.yml b/Resources/Prototypes/Roles/Jobs/Civilian/janitor.yml index b768bd25be5a..9b006cbed16d 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/janitor.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/janitor.yml @@ -32,3 +32,6 @@ head: ClothingHeadHatCatEars ears: ClothingHeadsetService belt: ClothingBeltJanitorFilled + storage: + back: + - BoxSurvival diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/lawyer.yml b/Resources/Prototypes/Roles/Jobs/Civilian/lawyer.yml index 1a321aff400a..3ec0eba51ca7 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/lawyer.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/lawyer.yml @@ -24,3 +24,7 @@ # TODO add copy of space law inhand: - BriefcaseBrownFilled + storage: + back: + - BoxSurvival + - RubberStampLawyer diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml b/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml index 909d8da19daa..f5826b76bfb9 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml @@ -18,3 +18,7 @@ ears: ClothingHeadsetService pocket1: d10Dice pocket2: HandLabeler # for making named bestsellers + storage: + back: + - BoxSurvival + - BookRandom diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml b/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml index 27a164210bc3..b7e932acb4fd 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml @@ -28,6 +28,10 @@ pocket2: Paper id: MimePDA ears: ClothingHeadsetService + storage: + back: + - BoxSurvival + - RubberStampMime - type: entity id: ActionMimeInvisibleWall diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/musician.yml b/Resources/Prototypes/Roles/Jobs/Civilian/musician.yml index 1ba1e998f68b..4b508c907d78 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/musician.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/musician.yml @@ -21,4 +21,9 @@ eyes: ClothingEyesGlassesSunglasses shoes: ClothingShoesBootsLaceup id: MusicianPDA - ears: ClothingHeadsetService \ No newline at end of file + ears: ClothingHeadsetService + storage: + back: + - BoxSurvival + - AcousticGuitarInstrument + - SaxophoneInstrument \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/service_worker.yml b/Resources/Prototypes/Roles/Jobs/Civilian/service_worker.yml index db2382667371..efda20712fbd 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/service_worker.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/service_worker.yml @@ -21,3 +21,6 @@ shoes: ClothingShoesColorBlack id: ServiceWorkerPDA ears: ClothingHeadsetService + storage: + back: + - BoxSurvival diff --git a/Resources/Prototypes/Roles/Jobs/Command/captain.yml b/Resources/Prototypes/Roles/Jobs/Command/captain.yml index dd7445670aa1..1e93ff73cce3 100644 --- a/Resources/Prototypes/Roles/Jobs/Command/captain.yml +++ b/Resources/Prototypes/Roles/Jobs/Command/captain.yml @@ -40,3 +40,8 @@ gloves: ClothingHandsGlovesCaptain id: CaptainPDA ears: ClothingHeadsetAltCommand + storage: + back: + - BoxSurvival + - Flash + # - StationCharter \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml b/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml index 5f119b643469..72a862f2648f 100644 --- a/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml +++ b/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml @@ -64,3 +64,7 @@ gloves: ClothingHandsGlovesHop ears: ClothingHeadsetAltCommand belt: BoxFolderClipboard + storage: + back: + - BoxSurvival + - Flash \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Engineering/atmospheric_technician.yml b/Resources/Prototypes/Roles/Jobs/Engineering/atmospheric_technician.yml index 01af70888d18..49a897863379 100644 --- a/Resources/Prototypes/Roles/Jobs/Engineering/atmospheric_technician.yml +++ b/Resources/Prototypes/Roles/Jobs/Engineering/atmospheric_technician.yml @@ -23,3 +23,6 @@ id: AtmosPDA belt: ClothingBeltUtilityEngineering ears: ClothingHeadsetEngineering + storage: + back: + - BoxSurvivalEngineering \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml b/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml index c55fbfbd4184..8cc934d634ae 100644 --- a/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml +++ b/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml @@ -44,3 +44,7 @@ eyes: ClothingEyesGlassesMeson ears: ClothingHeadsetCE belt: ClothingBeltUtilityEngineering + storage: + back: + - BoxSurvivalEngineering + - Flash \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Engineering/station_engineer.yml b/Resources/Prototypes/Roles/Jobs/Engineering/station_engineer.yml index d37847968108..1bff9257bba6 100644 --- a/Resources/Prototypes/Roles/Jobs/Engineering/station_engineer.yml +++ b/Resources/Prototypes/Roles/Jobs/Engineering/station_engineer.yml @@ -23,3 +23,6 @@ eyes: ClothingEyesGlassesMeson belt: ClothingBeltUtilityEngineering ears: ClothingHeadsetEngineering + storage: + back: + - BoxSurvivalEngineering \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Engineering/technical_assistant.yml b/Resources/Prototypes/Roles/Jobs/Engineering/technical_assistant.yml index f3b21e644442..8bbaf2a3903a 100644 --- a/Resources/Prototypes/Roles/Jobs/Engineering/technical_assistant.yml +++ b/Resources/Prototypes/Roles/Jobs/Engineering/technical_assistant.yml @@ -26,4 +26,7 @@ id: TechnicalAssistantPDA belt: ClothingBeltUtilityEngineering ears: ClothingHeadsetEngineering - pocket2: BookEngineersHandbook \ No newline at end of file + pocket2: BookEngineersHandbook + storage: + back: + - BoxSurvivalEngineering \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Fun/cult_startinggear.yml b/Resources/Prototypes/Roles/Jobs/Fun/cult_startinggear.yml index 8748367830b4..5542a7a69dde 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/cult_startinggear.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/cult_startinggear.yml @@ -3,21 +3,27 @@ id: CultLeaderGear equipment: jumpsuit: ClothingUniformJumpsuitColorBlack - back: ClothingBackpackFilled + back: ClothingBackpack head: ClothingHeadHelmetCult neck: BedsheetCult outerClothing: ClothingOuterArmorCult shoes: ClothingShoesCult id: PassengerPDA ears: ClothingHeadsetService + storage: + back: + - BoxSurvival - type: startingGear id: CultistGear equipment: jumpsuit: ClothingUniformJumpsuitColorBlack - back: ClothingBackpackFilled + back: ClothingBackpack head: ClothingHeadHatHoodCulthood outerClothing: ClothingOuterRobesCult shoes: ClothingShoesColorRed id: PassengerPDA ears: ClothingHeadsetService + storage: + back: + - BoxSurvival diff --git a/Resources/Prototypes/Roles/Jobs/Fun/emergencyresponseteam.yml b/Resources/Prototypes/Roles/Jobs/Fun/emergencyresponseteam.yml index f37085e005b6..337b7e1de913 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/emergencyresponseteam.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/emergencyresponseteam.yml @@ -18,7 +18,7 @@ id: ERTLeaderGear equipment: jumpsuit: ClothingUniformJumpsuitERTLeader - back: ClothingBackpackERTLeaderFilled + back: ClothingBackpackERTLeader shoes: ClothingShoesBootsCombatFilled head: ClothingHeadHelmetERTLeader eyes: ClothingEyesGlassesSecurity @@ -29,12 +29,21 @@ belt: ClothingBeltSecurityFilled pocket1: WeaponPistolN1984 pocket2: FlashlightSeclite + storage: + back: + - BoxSurvivalEngineering + - WeaponDisabler + - MedicatedSuture + - RegenerativeMesh + - BoxZiptie + - CrowbarRed + - MagazineMagnum - type: startingGear id: ERTLeaderGearEVA equipment: jumpsuit: ClothingUniformJumpsuitERTLeader - back: ClothingBackpackERTLeaderFilled + back: ClothingBackpackERTLeader shoes: ClothingShoesBootsMagAdv mask: ClothingMaskGasERT eyes: ClothingEyesGlassesSecurity @@ -46,12 +55,21 @@ belt: ClothingBeltSecurityFilled pocket1: WeaponPistolN1984 pocket2: FlashlightSeclite + storage: + back: + - BoxSurvivalEngineering + - WeaponDisabler + - MedicatedSuture + - RegenerativeMesh + - BoxZiptie + - CrowbarRed + - MagazineMagnum - type: startingGear id: ERTLeaderGearEVALecter equipment: jumpsuit: ClothingUniformJumpsuitERTLeader - back: ClothingBackpackERTLeaderFilled + back: ClothingBackpackERTLeader shoes: ClothingShoesBootsMagAdv mask: ClothingMaskGasERT eyes: ClothingEyesGlassesSecurity @@ -65,6 +83,15 @@ pocket2: MagazineRifle inhand: - AirTankFilled + storage: + back: + - BoxSurvivalEngineering + - WeaponDisabler + - MedicatedSuture + - RegenerativeMesh + - BoxZiptie + - CrowbarRed + - MagazineMagnum # Chaplain - type: job @@ -90,7 +117,7 @@ id: ERTChaplainGear equipment: jumpsuit: ClothingUniformJumpsuitERTChaplain - back: ClothingBackpackERTChaplainFilled + back: ClothingBackpackERTChaplain shoes: ClothingShoesLeather head: ClothingHeadHatFez eyes: ClothingEyesGlasses @@ -102,12 +129,27 @@ belt: ClothingBeltStorageWaistbag pocket1: Flare pocket2: DrinkWaterBottleFull + storage: + back: + - BoxSurvivalEngineering + - BoxCandle + - BoxBodyBag + - DrinkWaterMelonJuiceJug + - Lantern + - Lantern + - Bible + - CrowbarRed + - FoodBakedBunHotX + - FoodBakedBunHotX + - FoodBakedBunHotX + - FoodBakedBunHotX + - Lighter - type: startingGear id: ERTChaplainGearEVA equipment: jumpsuit: ClothingUniformJumpsuitERTChaplain - back: ClothingBackpackERTChaplainFilled + back: ClothingBackpackERTChaplain shoes: ClothingShoesBootsMagAdv mask: ClothingMaskGasERT eyes: ClothingEyesGlasses @@ -120,6 +162,21 @@ belt: ClothingBeltStorageWaistbag pocket1: Flare pocket2: DrinkWaterBottleFull + storage: + back: + - BoxSurvivalEngineering + - BoxCandle + - BoxBodyBag + - DrinkWaterMelonJuiceJug + - Lantern + - Lantern + - Bible + - CrowbarRed + - FoodBakedBunHotX + - FoodBakedBunHotX + - FoodBakedBunHotX + - FoodBakedBunHotX + - Lighter # Engineer - type: job @@ -141,7 +198,7 @@ id: ERTEngineerGear equipment: jumpsuit: ClothingUniformJumpsuitERTEngineer - back: ClothingBackpackERTEngineerFilled + back: ClothingBackpackERTEngineer shoes: ClothingShoesBootsWork head: ClothingHeadHelmetERTEngineer eyes: ClothingEyesGlassesMeson @@ -152,12 +209,25 @@ belt: ClothingBeltChiefEngineerFilled pocket1: Flare pocket2: GasAnalyzer + storage: + back: + - BoxSurvivalEngineering + - trayScanner + - RCD + - RCDAmmo + - RCDAmmo + - CableMVStack + - CableHVStack + - CableApcStack + - SheetPlasteel + - SheetSteel + - SheetGlass - type: startingGear id: ERTEngineerGearEVA equipment: jumpsuit: ClothingUniformJumpsuitERTEngineer - back: ClothingBackpackERTEngineerFilled + back: ClothingBackpackERTEngineer shoes: ClothingShoesBootsMagAdv mask: ClothingMaskGasERT eyes: ClothingEyesGlassesMeson @@ -169,6 +239,19 @@ belt: ClothingBeltChiefEngineerFilled pocket1: Flare pocket2: GasAnalyzer + storage: + back: + - BoxSurvivalEngineering + - trayScanner + - RCD + - RCDAmmo + - RCDAmmo + - CableMVStack + - CableHVStack + - CableApcStack + - SheetPlasteel + - SheetSteel + - SheetGlass # Security - type: job @@ -190,7 +273,7 @@ id: ERTSecurityGear equipment: jumpsuit: ClothingUniformJumpsuitERTSecurity - back: ClothingBackpackERTSecurityFilled + back: ClothingBackpackERTSecurity shoes: ClothingShoesBootsCombatFilled head: ClothingHeadHelmetERTSecurity eyes: ClothingEyesGlassesSecurity @@ -201,12 +284,21 @@ belt: ClothingBeltSecurityFilled pocket1: WeaponPistolMk58 pocket2: FlashlightSeclite + storage: + back: + - BoxSurvivalEngineering + - WeaponDisabler + - MedicatedSuture + - RegenerativeMesh + - BoxZiptie + - CrowbarRed + - MagazinePistol - type: startingGear id: ERTSecurityGearEVA equipment: jumpsuit: ClothingUniformJumpsuitERTSecurity - back: ClothingBackpackERTSecurityFilled + back: ClothingBackpackERTSecurity shoes: ClothingShoesBootsMag mask: ClothingMaskGasERT eyes: ClothingEyesGlassesSecurity @@ -218,12 +310,21 @@ belt: ClothingBeltSecurityFilled pocket1: WeaponPistolMk58 pocket2: FlashlightSeclite + storage: + back: + - BoxSurvivalEngineering + - WeaponDisabler + - MedicatedSuture + - RegenerativeMesh + - BoxZiptie + - CrowbarRed + - MagazinePistol - type: startingGear id: ERTSecurityGearEVALecter equipment: jumpsuit: ClothingUniformJumpsuitERTSecurity - back: ClothingBackpackERTSecurityFilled + back: ClothingBackpackERTSecurity shoes: ClothingShoesBootsMag mask: ClothingMaskGasERT eyes: ClothingEyesGlassesSecurity @@ -237,6 +338,15 @@ pocket2: MagazineRifle inhand: - AirTankFilled + storage: + back: + - BoxSurvivalEngineering + - WeaponDisabler + - MedicatedSuture + - RegenerativeMesh + - BoxZiptie + - CrowbarRed + - MagazinePistol # Medical - type: job @@ -258,7 +368,7 @@ id: ERTMedicalGear equipment: jumpsuit: ClothingUniformJumpsuitERTMedic - back: ClothingBackpackERTMedicalFilled + back: ClothingBackpackERTMedical shoes: ClothingShoesBootsCombatFilled head: ClothingHeadHelmetERTMedic eyes: ClothingEyesHudMedical @@ -268,12 +378,21 @@ ears: ClothingHeadsetAltCentCom belt: ClothingBeltMedicalFilled pocket1: Flare + storage: + back: + - BoxSurvivalMedical + - Hypospray + - MedkitAdvancedFilled + - CrowbarRed + - OmnizineChemistryBottle + - EpinephrineChemistryBottle + - EpinephrineChemistryBottle - type: startingGear id: ERTMedicalGearEVA equipment: jumpsuit: ClothingUniformJumpsuitERTMedic - back: ClothingBackpackERTMedicalFilled + back: ClothingBackpackERTMedical shoes: ClothingShoesBootsMag mask: ClothingMaskGasERT eyes: ClothingEyesHudMedical @@ -284,6 +403,15 @@ ears: ClothingHeadsetAltCentCom belt: ClothingBeltMedicalFilled pocket1: Flare + storage: + back: + - BoxSurvivalMedical + - Hypospray + - MedkitAdvancedFilled + - CrowbarRed + - OmnizineChemistryBottle + - EpinephrineChemistryBottle + - EpinephrineChemistryBottle # Janitor - type: job @@ -305,7 +433,7 @@ id: ERTJanitorGear equipment: jumpsuit: ClothingUniformJumpsuitERTJanitor - back: ClothingBackpackERTJanitorFilled + back: ClothingBackpackERTJanitor shoes: ClothingShoesGaloshes head: ClothingHeadHelmetERTJanitor gloves: ClothingHandsGlovesColorPurple @@ -314,12 +442,21 @@ ears: ClothingHeadsetAltCentCom belt: ClothingBeltJanitorFilled pocket1: Flare + storage: + back: + - BoxSurvivalEngineering + - LightReplacer + - BoxLightMixed + - BoxLightMixed + - Soap + - CrowbarRed + - AdvMopItem - type: startingGear id: ERTJanitorGearEVA equipment: jumpsuit: ClothingUniformJumpsuitERTJanitor - back: ClothingBackpackERTJanitorFilled + back: ClothingBackpackERTJanitor shoes: ClothingShoesBootsMag mask: ClothingMaskGasERT gloves: ClothingHandsGlovesColorPurple @@ -329,3 +466,12 @@ ears: ClothingHeadsetAltCentCom belt: ClothingBeltJanitorFilled pocket1: Flare + storage: + back: + - BoxSurvivalEngineering + - LightReplacer + - BoxLightMixed + - BoxLightMixed + - Soap + - CrowbarRed + - AdvMopItem \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml index d4cfc3e29cf8..e12bee8c79e0 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml @@ -5,7 +5,7 @@ id: SkeletonBiker equipment: jumpsuit: ClothingUniformJumpsuitColorBlack - back: ClothingBackpackFilled + back: ClothingBackpack head: ClothingHeadBandSkull eyes: ClothingEyesGlassesSunglasses outerClothing: ClothingOuterCoatGentle @@ -13,35 +13,16 @@ shoes: ClothingShoesBootsJack id: PassengerPDA ears: ClothingHeadsetGrey - -#Space Ninja Outfit -- type: startingGear - id: SpaceNinjaGear - equipment: - jumpsuit: ClothingUniformJumpsuitNinja - # belt holds katana so satchel has the tools for sabotaging things - back: ClothingBackpackSatchelTools - mask: ClothingMaskNinja - head: ClothingHeadHelmetSpaceNinja - eyes: ClothingEyesVisorNinja - gloves: ClothingHandsGlovesSpaceNinja - outerClothing: ClothingOuterSuitSpaceNinja - shoes: ClothingShoesSpaceNinja - id: AgentIDCard - ears: ClothingHeadsetGrey - pocket1: SpiderCharge - pocket2: PinpointerStation - belt: EnergyKatana - suitstorage: OxygenTankFilled - inhand: - - JetpackBlackFilled + storage: + back: + - BoxSurvival #Deathsquad Outfit - type: startingGear id: DeathSquadGear equipment: jumpsuit: ClothingUniformJumpsuitDeathSquad - back: ClothingBackpackDeathSquadFilled + back: ClothingBackpackDeathSquad mask: ClothingMaskGasDeathSquad eyes: ClothingEyesHudSecurity ears: ClothingHeadsetAltCentCom @@ -53,23 +34,20 @@ pocket1: EnergySword pocket2: EnergyShield belt: ClothingBeltMilitaryWebbingMedFilled - -# Syndicate Operative Outfit - Monkey -- type: startingGear - id: SyndicateOperativeGearMonkey - equipment: - head: ClothingHeadHatOutlawHat - jumpsuit: ClothingUniformJumpsuitOperative - mask: CigaretteSyndicate - -# Syndicate Operative Outfit - Barratry -- type: startingGear - id: SyndicateOperativeGearExtremelyBasic - equipment: - jumpsuit: ClothingUniformJumpsuitOperative - back: ClothingBackpackDuffelSyndicateOperative - shoes: ClothingShoesBootsCombatFilled - gloves: ClothingHandsGlovesColorBlack + storage: + back: + - BoxSurvivalEngineering + - WeaponPulseRifle + - WeaponPulsePistol + - WeaponRevolverMateba + - SpeedLoaderMagnumAP + - SpeedLoaderMagnumAP + - BoxFlashbang + - ToolDebug # spanish army knife + - WelderExperimental + - Hypospray + - DeathAcidifierImplanter # crew will try to steal their amazing hardsuits + - FreedomImplanter # Syndicate Operative Outfit - Civilian - type: startingGear @@ -82,122 +60,35 @@ id: SyndiPDA ears: ClothingHeadsetAltSyndicate -#Syndicate Operative Outfit - Basic -- type: startingGear - id: SyndicateOperativeGearBasic - equipment: - jumpsuit: ClothingUniformJumpsuitOperative - back: ClothingBackpackDuffelSyndicateOperative - ears: ClothingHeadsetAltSyndicate - gloves: ClothingHandsGlovesCombat - shoes: ClothingShoesBootsCombatFilled - pocket1: BaseUplinkRadio40TC - id: SyndiPDA - -#Syndicate Operative Outfit - Full Kit -- type: startingGear - id: SyndicateOperativeGearFull - equipment: - jumpsuit: ClothingUniformJumpsuitOperative - back: ClothingBackpackDuffelSyndicateOperative - mask: ClothingMaskGasSyndicate - eyes: ClothingEyesHudSyndicate - ears: ClothingHeadsetAltSyndicate - gloves: ClothingHandsGlovesCombat - outerClothing: ClothingOuterHardsuitSyndie - shoes: ClothingShoesBootsCombatFilled - id: SyndiPDA - pocket1: DoubleEmergencyOxygenTankFilled - pocket2: BaseUplinkRadio40TC - belt: ClothingBeltMilitaryWebbing - -#Nuclear Operative Commander Gear -- type: startingGear - id: SyndicateCommanderGearFull - equipment: - jumpsuit: ClothingUniformJumpsuitOperative - back: ClothingBackpackDuffelSyndicateOperative - mask: ClothingMaskGasSyndicate - eyes: ClothingEyesHudSyndicate - ears: ClothingHeadsetAltSyndicate - neck: SyndicateWhistle - gloves: ClothingHandsGlovesCombat - outerClothing: ClothingOuterHardsuitSyndieCommander - shoes: ClothingShoesBootsCombatFilled - id: SyndiPDA - pocket1: DoubleEmergencyOxygenTankFilled - pocket2: BaseUplinkRadio40TC - belt: ClothingBeltMilitaryWebbing - inhand: - - NukeOpsDeclarationOfWar - -#Nuclear Operative Medic Gear -- type: startingGear - id: SyndicateOperativeMedicFull - equipment: - jumpsuit: ClothingUniformJumpsuitOperative - back: ClothingBackpackDuffelSyndicateOperativeMedic - mask: ClothingMaskGasSyndicate - eyes: ClothingEyesHudSyndicateAgent - ears: ClothingHeadsetAltSyndicate - gloves: ClothingHandsGlovesCombat - outerClothing: ClothingOuterHardsuitSyndieMedic - shoes: ClothingShoesBootsMagSyndie - id: SyndiAgentPDA - pocket1: DoubleEmergencyOxygenTankFilled - pocket2: BaseUplinkRadio40TC - belt: ClothingBeltMilitaryWebbingMedFilled - -#Syndicate Lone Operative Outfit - Full Kit -- type: startingGear - id: SyndicateLoneOperativeGearFull - equipment: - jumpsuit: ClothingUniformJumpsuitOperative - back: ClothingBackpackDuffelSyndicateOperative - mask: ClothingMaskGasSyndicate - eyes: ClothingEyesHudSyndicate - ears: ClothingHeadsetAltSyndicate - gloves: ClothingHandsGlovesCombat - outerClothing: ClothingOuterHardsuitSyndie - shoes: ClothingShoesBootsCombatFilled - id: SyndiPDA - pocket1: DoubleEmergencyOxygenTankFilled - pocket2: BaseUplinkRadio60TC - belt: ClothingBeltMilitaryWebbing - -# Syndicate Footsoldier Gear +# Syndicate Footsoldier Gear - No Headset - type: startingGear - id: SyndicateFootsoldierGear + id: SyndicateFootsoldierGearRuin equipment: jumpsuit: ClothingUniformJumpsuitOperative head: ClothingHeadHelmetSwatSyndicate mask: ClothingMaskGas outerClothing: ClothingOuterArmorBasic - ears: ClothingHeadsetAltSyndicate gloves: ClothingHandsGlovesCombat - back: ClothingBackpackFilled + back: ClothingBackpack shoes: ClothingShoesBootsCombat id: SyndiPDA + storage: + back: + - BoxSurvival -# Syndicate Footsoldier Gear - No Headset +# Syndicate Footsoldier Gear - type: startingGear - id: SyndicateFootsoldierGearRuin + id: SyndicateFootsoldierGear + parent: SyndicateFootsoldierGearRuin equipment: - jumpsuit: ClothingUniformJumpsuitOperative - head: ClothingHeadHelmetSwatSyndicate - mask: ClothingMaskGas - outerClothing: ClothingOuterArmorBasic - gloves: ClothingHandsGlovesCombat - back: ClothingBackpackFilled - shoes: ClothingShoesBootsCombat - id: SyndiPDA + ears: ClothingHeadsetAltSyndicate # Nanotrasen Paramilitary Unit Gear - type: startingGear id: NanotrasenParamilitaryGear equipment: jumpsuit: ClothingUniformJumpsuitSec - back: ClothingBackpackSecurityFilled + back: ClothingBackpackSecurity shoes: ClothingShoesBootsCombatFilled eyes: ClothingEyesGlassesSecurity head: ClothingHeadHelmetSwat @@ -205,13 +96,18 @@ outerClothing: ClothingOuterArmorBasicSlim ears: ClothingHeadsetSecurity gloves: ClothingHandsGlovesCombat + storage: + back: + - BoxSurvivalSecurity + - Flash + - MagazinePistol #CBURN Unit Gear - Full Kit - type: startingGear id: CBURNGear equipment: jumpsuit: ClothingUniformJumpsuitColorBrown - back: ClothingBackpackDuffelCBURNFilled + back: ClothingBackpackDuffelCBURN mask: ClothingMaskGasERT eyes: ClothingEyesGlassesSecurity ears: ClothingHeadsetAltCentCom @@ -223,6 +119,14 @@ pocket2: WeaponLaserGun suitstorage: OxygenTankFilled belt: ClothingBeltBandolier + storage: + back: + - BoxSurvivalEngineering + - WeaponShotgunDoubleBarreled + - BoxShotgunIncendiary + - GrenadeFlashBang + - PillAmbuzolPlus + - PillAmbuzol - type: startingGear id: BoxingKangarooGear @@ -237,14 +141,6 @@ jumpsuit: ClothingUniformJumpsuitJacketMonkey id: PunPunIDCard -# Passenger but without the ID, bag, or headset - -- type: startingGear - id: LimitedPassengerGear - equipment: - jumpsuit: ClothingUniformJumpsuitColorGrey - shoes: ClothingShoesColorBlack - # DeathMatch Gear - type: startingGear @@ -278,24 +174,13 @@ - type: startingGear id: MobAghostGear equipment: - back: ClothingBackpackSatchelHoldingAdmin + back: ClothingBackpackSatchelHolding id: AdminPDA - -#Head Rev Gear -- type: startingGear - id: HeadRevGear storage: back: - - Flash - - ClothingEyesGlassesSunglasses - -#Thief Gear -- type: startingGear - id: ThiefGear - storage: - back: - - ToolboxThief - - ClothingHandsChameleonThief + - GasAnalyzer + - trayScanner + - AccessConfiguratorUniversal #Gladiator with spear - type: startingGear @@ -325,10 +210,16 @@ shoes: ClothingShoesClownBanana jumpsuit: ClothingUniformJumpsuitClownBanana mask: ClothingMaskClownBanana - ears: ClothingHeadsetService pocket1: BikeHorn pocket2: ClownRecorder +# Passenger but without the ID, bag, or headset +- type: startingGear + id: LimitedPassengerGear + equipment: + jumpsuit: ClothingUniformJumpsuitColorGrey + shoes: ClothingShoesColorBlack + #Clown Troupe - type: startingGear id: ClownTroupe diff --git a/Resources/Prototypes/Roles/Jobs/Fun/wizard_startinggear.yml b/Resources/Prototypes/Roles/Jobs/Fun/wizard_startinggear.yml index 596e42776b7c..d053d8da1cce 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/wizard_startinggear.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/wizard_startinggear.yml @@ -3,41 +3,33 @@ id: WizardBlueGear equipment: jumpsuit: ClothingUniformJumpsuitColorDarkBlue - back: ClothingBackpackFilled head: ClothingHeadHatWizard outerClothing: ClothingOuterWizard shoes: ClothingShoesWizard id: PassengerPDA ears: ClothingHeadsetService + storage: + back: + - BoxSurvival - type: startingGear id: WizardRedGear + parent: WizardBlueGear equipment: jumpsuit: ClothingUniformJumpsuitColorRed - back: ClothingBackpackFilled head: ClothingHeadHatRedwizard outerClothing: ClothingOuterWizardRed - shoes: ClothingShoesWizard - id: PassengerPDA - ears: ClothingHeadsetService - type: startingGear id: WizardVioletGear + parent: WizardBlueGear equipment: jumpsuit: ClothingUniformJumpsuitColorPurple - back: ClothingBackpackFilled head: ClothingHeadHatVioletwizard outerClothing: ClothingOuterWizardViolet - shoes: ClothingShoesWizard - id: PassengerPDA - ears: ClothingHeadsetService - type: startingGear id: WizardHardsuitGear + parent: WizardVioletGear equipment: - jumpsuit: ClothingUniformJumpsuitColorPurple - back: ClothingBackpackFilled - outerClothing: ClothingOuterHardsuitWizard - shoes: ClothingShoesWizard - id: PassengerPDA - ears: ClothingHeadsetService + outerClothing: ClothingOuterHardsuitWizard \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Medical/chemist.yml b/Resources/Prototypes/Roles/Jobs/Medical/chemist.yml index 0684d9d36ac6..fefd0110accd 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/chemist.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/chemist.yml @@ -22,4 +22,7 @@ ears: ClothingHeadsetMedical belt: ChemBag pocket1: HandLabeler - eyes: ClothingEyesGlassesChemical \ No newline at end of file + eyes: ClothingEyesGlassesChemical + storage: + back: + - BoxSurvivalMedical \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml index e20edb32d45a..3f6bd5e4529d 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml @@ -44,3 +44,7 @@ id: CMOPDA ears: ClothingHeadsetCMO belt: ClothingBeltMedicalFilled + storage: + back: + - BoxSurvivalMedical + - Flash diff --git a/Resources/Prototypes/Roles/Jobs/Medical/medical_doctor.yml b/Resources/Prototypes/Roles/Jobs/Medical/medical_doctor.yml index 27efb3b8b268..be058518a68d 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/medical_doctor.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/medical_doctor.yml @@ -21,3 +21,6 @@ equipment: ears: ClothingHeadsetMedical belt: ClothingBeltMedicalFilled + storage: + back: + - BoxSurvivalMedical \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Medical/medical_intern.yml b/Resources/Prototypes/Roles/Jobs/Medical/medical_intern.yml index 199645d5c380..3fe26e6056b4 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/medical_intern.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/medical_intern.yml @@ -23,4 +23,7 @@ id: MedicalInternPDA ears: ClothingHeadsetMedical belt: ClothingBeltMedicalFilled - pocket2: BookMedicalReferenceBook \ No newline at end of file + pocket2: BookMedicalReferenceBook + storage: + back: + - BoxSurvivalMedical \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Medical/paramedic.yml b/Resources/Prototypes/Roles/Jobs/Medical/paramedic.yml index fb9fd2822dc1..242fac8d0c02 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/paramedic.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/paramedic.yml @@ -24,3 +24,7 @@ id: ParamedicPDA ears: ClothingHeadsetMedical belt: ClothingBeltMedicalEMTFilled + storage: + back: + - BoxSurvivalMedical + - EmergencyRollerBedSpawnFolded \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Science/research_assistant.yml b/Resources/Prototypes/Roles/Jobs/Science/research_assistant.yml index b50c61ebdb2b..aec2dd6fff3d 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/research_assistant.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/research_assistant.yml @@ -23,3 +23,6 @@ id: ResearchAssistantPDA ears: ClothingHeadsetScience pocket2: BookScientistsGuidebook + storage: + back: + - BoxSurvival \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml index adf53d9c7089..fd6e00a8e3f9 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml @@ -33,4 +33,8 @@ id: ResearchDirectorGear equipment: id: RnDPDA - ears: ClothingHeadsetRD \ No newline at end of file + ears: ClothingHeadsetRD + storage: + back: + - BoxSurvival + - Flash \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Science/scientist.yml b/Resources/Prototypes/Roles/Jobs/Science/scientist.yml index 5188e47057ee..b2e63fead8a6 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/scientist.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/scientist.yml @@ -18,4 +18,6 @@ id: ScientistGear equipment: ears: ClothingHeadsetScience - + storage: + back: + - BoxSurvival \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Security/detective.yml b/Resources/Prototypes/Roles/Jobs/Security/detective.yml index b17d0d41df85..47258fa00bf1 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/detective.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/detective.yml @@ -28,3 +28,9 @@ id: DetectivePDA ears: ClothingHeadsetSecurity belt: ClothingBeltHolsterFilled + storage: + back: + - BoxSurvivalSecurity + - Flash + - ForensicPad + - ForensicScanner \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml index 8646ca8dddd1..aac7c7b35089 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml @@ -47,3 +47,8 @@ gloves: ClothingHandsGlovesCombat ears: ClothingHeadsetAltSecurity pocket1: WeaponPistolMk58 + storage: + back: + - BoxSurvivalSecurity + - Flash + - MagazinePistol \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Security/security_cadet.yml b/Resources/Prototypes/Roles/Jobs/Security/security_cadet.yml index 17e09b232e98..b68336c95305 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/security_cadet.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/security_cadet.yml @@ -34,4 +34,9 @@ ears: ClothingHeadsetSecurity belt: ClothingBeltSecurityFilled pocket1: WeaponPistolMk58 - pocket2: BookSecurity \ No newline at end of file + pocket2: BookSecurity + storage: + back: + - BoxSurvivalSecurity + - Flash + - MagazinePistol \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml index edf16448d7ca..c773396601dd 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml @@ -28,3 +28,8 @@ eyes: ClothingEyesGlassesSecurity ears: ClothingHeadsetSecurity pocket1: WeaponPistolMk58 + storage: + back: + - BoxSurvivalSecurity + - Flash + - MagazinePistol \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Security/warden.yml b/Resources/Prototypes/Roles/Jobs/Security/warden.yml index 5c1dc985d1e8..7f157525767d 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/warden.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/warden.yml @@ -31,3 +31,8 @@ id: WardenPDA ears: ClothingHeadsetSecurity pocket1: WeaponPistolMk58 + storage: + back: + - BoxSurvivalSecurity + - Flash + - MagazinePistol \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Wildcards/boxer.yml b/Resources/Prototypes/Roles/Jobs/Wildcards/boxer.yml index 0f19323724a8..aceb7a10f6ac 100644 --- a/Resources/Prototypes/Roles/Jobs/Wildcards/boxer.yml +++ b/Resources/Prototypes/Roles/Jobs/Wildcards/boxer.yml @@ -17,3 +17,6 @@ ears: ClothingHeadsetService shoes: ClothingShoesColorRed belt: ClothingBeltChampion + storage: + back: + - BoxSurvival \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Wildcards/psychologist.yml b/Resources/Prototypes/Roles/Jobs/Wildcards/psychologist.yml index d1d07c008c8b..17bed0ee7f6e 100644 --- a/Resources/Prototypes/Roles/Jobs/Wildcards/psychologist.yml +++ b/Resources/Prototypes/Roles/Jobs/Wildcards/psychologist.yml @@ -19,3 +19,6 @@ shoes: ClothingShoesLeather id: PsychologistPDA ears: ClothingHeadsetMedical + storage: + back: + - BoxSurvivalMedical \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Wildcards/reporter.yml b/Resources/Prototypes/Roles/Jobs/Wildcards/reporter.yml index d2d70d856a58..b379a0ec2350 100644 --- a/Resources/Prototypes/Roles/Jobs/Wildcards/reporter.yml +++ b/Resources/Prototypes/Roles/Jobs/Wildcards/reporter.yml @@ -16,3 +16,6 @@ shoes: ClothingShoesColorWhite id: ReporterPDA ears: ClothingHeadsetService + storage: + back: + - BoxSurvival \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Wildcards/zookeeper.yml b/Resources/Prototypes/Roles/Jobs/Wildcards/zookeeper.yml index 27e7fa65ff6f..815ead412c78 100644 --- a/Resources/Prototypes/Roles/Jobs/Wildcards/zookeeper.yml +++ b/Resources/Prototypes/Roles/Jobs/Wildcards/zookeeper.yml @@ -18,3 +18,6 @@ shoes: ClothingShoesColorWhite id: ZookeeperPDA ears: ClothingHeadsetService + storage: + back: + - BoxSurvival \ No newline at end of file diff --git a/Resources/migration.yml b/Resources/migration.yml index 65e914aed688..5f73d4d2530b 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -251,11 +251,11 @@ AirlockGlassShuttleEasyPryLocked: AirlockExternalGlassShuttleLocked AirlockShuttleEasyPryLocked: AirlockExternalShuttleLocked # 2024-03-10 -ClothingBackpackFilledDetective: ClothingBackpackSecurityFilledDetective -ClothingBackpackDuffelFilledDetective: ClothingBackpackDuffelSecurityFilledDetective -ClothingBackpackSatchelFilledDetective: ClothingBackpackSatchelSecurityFilledDetective FoodChili: FoodChiliPepper FoodChilly: FoodChillyPepper +# ClothingBackpackFilledDetective: ClothingBackpackSecurityFilledDetective +# ClothingBackpackDuffelFilledDetective: ClothingBackpackDuffelSecurityFilledDetective +# ClothingBackpackSatchelFilledDetective: ClothingBackpackSatchelSecurityFilledDetective # 2024-03-11 ImprovisedExplosive: FireBomb From 16b3fb1204d3921200822bf558c510c585ba33e3 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Tue, 4 Jun 2024 00:04:19 +0000 Subject: [PATCH 277/568] LoadMapRule grid storage rework (#28210) * --- .../Tests/GameRules/NukeOpsTest.cs | 10 +-- .../Rules/Components/LoadMapRuleComponent.cs | 26 +++--- .../Rules/Components/RuleGridsComponent.cs | 30 +++++++ .../Rules/GameRuleSystem.Utility.cs | 4 + .../GameTicking/Rules/LoadMapRuleSystem.cs | 90 +++++++------------ .../GameTicking/Rules/NukeopsRuleSystem.cs | 12 +-- .../GameTicking/Rules/RuleGridsSystem.cs | 78 ++++++++++++++++ .../GridPreloader/GridPreloaderSystem.cs | 9 +- .../Events/StationEventSystem.cs | 9 -- Resources/Prototypes/GameRules/events.yml | 1 + Resources/Prototypes/GameRules/midround.yml | 1 - Resources/Prototypes/GameRules/roundstart.yml | 1 + .../Prototypes/GameRules/unknown_shuttles.yml | 49 +++------- 13 files changed, 190 insertions(+), 130 deletions(-) create mode 100644 Content.Server/GameTicking/Rules/Components/RuleGridsComponent.cs create mode 100644 Content.Server/GameTicking/Rules/RuleGridsSystem.cs diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs index ea19a30005ee..2360ea0bf4f2 100644 --- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs @@ -114,8 +114,8 @@ public async Task TryStopNukeOpsFromConstantlyFailing() // The game rule exists, and all the stations/shuttles/maps are properly initialized var rule = entMan.AllComponents().Single().Component; - var mapRule = entMan.AllComponents().Single().Component; - foreach (var grid in mapRule.MapGrids) + var gridsRule = entMan.AllComponents().Single().Component; + foreach (var grid in gridsRule.MapGrids) { Assert.That(entMan.EntityExists(grid)); Assert.That(entMan.HasComponent(grid)); @@ -129,7 +129,7 @@ public async Task TryStopNukeOpsFromConstantlyFailing() Assert.That(entMan.EntityExists(nukieShuttlEnt)); EntityUid? nukieStationEnt = null; - foreach (var grid in mapRule.MapGrids) + foreach (var grid in gridsRule.MapGrids) { if (entMan.HasComponent(grid)) { @@ -144,8 +144,8 @@ public async Task TryStopNukeOpsFromConstantlyFailing() Assert.That(entMan.EntityExists(nukieStation.Station)); Assert.That(nukieStation.Station, Is.Not.EqualTo(rule.TargetStation)); - Assert.That(server.MapMan.MapExists(mapRule.Map)); - var nukieMap = mapSys.GetMap(mapRule.Map!.Value); + Assert.That(server.MapMan.MapExists(gridsRule.Map)); + var nukieMap = mapSys.GetMap(gridsRule.Map!.Value); var targetStation = entMan.GetComponent(rule.TargetStation!.Value); var targetGrid = targetStation.Grids.First(); diff --git a/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs index b7aef0c61dc2..1f0505c60fc0 100644 --- a/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs @@ -1,8 +1,6 @@ +using Content.Server.GameTicking.Rules; using Content.Server.Maps; using Content.Shared.GridPreloader.Prototypes; -using Content.Shared.Storage; -using Content.Shared.Whitelist; -using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -10,25 +8,27 @@ namespace Content.Server.GameTicking.Rules.Components; /// /// This is used for a game rule that loads a map when activated. +/// Works with . /// -[RegisterComponent] +[RegisterComponent, Access(typeof(LoadMapRuleSystem))] public sealed partial class LoadMapRuleComponent : Component { - [DataField] - public MapId? Map; - + /// + /// A to load on a new map. + /// [DataField] public ProtoId? GameMap; + /// + /// A map path to load on a new map. + /// [DataField] public ResPath? MapPath; + /// + /// A to move to a new map. + /// If there are no instances left nothing is done. + /// [DataField] public ProtoId? PreloadedGrid; - - [DataField] - public List MapGrids = new(); - - [DataField] - public EntityWhitelist? SpawnerWhitelist; } diff --git a/Content.Server/GameTicking/Rules/Components/RuleGridsComponent.cs b/Content.Server/GameTicking/Rules/Components/RuleGridsComponent.cs new file mode 100644 index 000000000000..eec6f8881568 --- /dev/null +++ b/Content.Server/GameTicking/Rules/Components/RuleGridsComponent.cs @@ -0,0 +1,30 @@ +using Content.Server.GameTicking.Rules; +using Content.Shared.Whitelist; +using Robust.Shared.Map; + +/// +/// Stores grids created by another gamerule component. +/// With AntagSelection, spawners on these grids can be used for its antags. +/// +[RegisterComponent, Access(typeof(RuleGridsSystem))] +public sealed partial class RuleGridsComponent : Component +{ + /// + /// The map that was loaded. + /// + [DataField] + public MapId? Map; + + /// + /// The grid entities that have been loaded. + /// + [DataField] + public List MapGrids = new(); + + /// + /// Whitelist for a spawner to be considered for an antag. + /// All spawners must have SpawnPointComponent regardless to be found. + /// + [DataField] + public EntityWhitelist? SpawnerWhitelist; +} diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs index cbd981e99e69..9ac7a6edbb90 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs @@ -126,4 +126,8 @@ protected bool TryFindRandomTileOnStation(Entity station, return found; } + protected void ForceEndSelf(EntityUid uid, GameRuleComponent? component = null) + { + GameTicker.EndGameRule(uid, component); + } } diff --git a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs index c60cf2513e0e..1c09d6e86e10 100644 --- a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs @@ -1,9 +1,6 @@ -using Content.Server.Antag; using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.GridPreloader; -using Content.Server.Spawners.Components; -using Content.Shared.Whitelist; using Robust.Server.GameObjects; using Robust.Server.Maps; using Robust.Shared.Map; @@ -14,97 +11,70 @@ namespace Content.Server.GameTicking.Rules; public sealed class LoadMapRuleSystem : GameRuleSystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly MapSystem _map = default!; [Dependency] private readonly MapLoaderSystem _mapLoader = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly GridPreloaderSystem _gridPreloader = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSelectLocation); - SubscribeLocalEvent(OnGridSplit); - } - - private void OnGridSplit(ref GridSplitEvent args) - { - var rule = QueryActiveRules(); - while (rule.MoveNext(out _, out var mapComp, out _)) - { - if (!mapComp.MapGrids.Contains(args.Grid)) - continue; - - mapComp.MapGrids.AddRange(args.NewGrids); - break; - } - } protected override void Added(EntityUid uid, LoadMapRuleComponent comp, GameRuleComponent rule, GameRuleAddedEvent args) { - if (comp.Map != null) + if (comp.PreloadedGrid != null && !_gridPreloader.PreloadingEnabled) + { + // Preloading will never work if it's disabled, duh + Log.Debug($"Immediately ending {ToPrettyString(uid):rule} as preloading grids is disabled by cvar."); + ForceEndSelf(uid, rule); return; + } // grid preloading needs map to init after moving it - var mapUid = comp.PreloadedGrid != null ? _map.CreateMap(out var mapId, false) : _map.CreateMap(out mapId); - _metaData.SetEntityName(mapUid, $"LoadMapRule destination for rule {ToPrettyString(uid)}"); - comp.Map = mapId; + var mapUid = _map.CreateMap(out var mapId, runMapInit: comp.PreloadedGrid == null); + + Log.Info($"Created map {mapId} for {ToPrettyString(uid):rule}"); + IReadOnlyList grids; if (comp.GameMap != null) { var gameMap = _prototypeManager.Index(comp.GameMap.Value); - comp.MapGrids.AddRange(GameTicker.LoadGameMap(gameMap, comp.Map.Value, new MapLoadOptions())); + grids = GameTicker.LoadGameMap(gameMap, mapId, new MapLoadOptions()); } - else if (comp.MapPath != null) + else if (comp.MapPath is {} path) { - if (!_mapLoader.TryLoad(comp.Map.Value, - comp.MapPath.Value.ToString(), - out var roots, - new MapLoadOptions { LoadMap = true })) + var options = new MapLoadOptions { LoadMap = true }; + if (!_mapLoader.TryLoad(mapId, path.ToString(), out var roots, options)) { - _mapManager.DeleteMap(mapId); + Log.Error($"Failed to load map from {path}!"); + Del(mapUid); + ForceEndSelf(uid, rule); return; } - comp.MapGrids.AddRange(roots); + grids = roots; } - else if (comp.PreloadedGrid != null) + else if (comp.PreloadedGrid is {} preloaded) { // TODO: If there are no preloaded grids left, any rule announcements will still go off! - if (!_gridPreloader.TryGetPreloadedGrid(comp.PreloadedGrid.Value, out var loadedShuttle)) + if (!_gridPreloader.TryGetPreloadedGrid(preloaded, out var loadedShuttle)) { - _mapManager.DeleteMap(mapId); + Log.Error($"Failed to get a preloaded grid with {preloaded}!"); + Del(mapUid); + ForceEndSelf(uid, rule); return; } _transform.SetParent(loadedShuttle.Value, mapUid); - comp.MapGrids.Add(loadedShuttle.Value); - _map.InitializeMap(mapId); + grids = new List() { loadedShuttle.Value }; + _map.InitializeMap(mapUid); } else { Log.Error($"No valid map prototype or map path associated with the rule {ToPrettyString(uid)}"); + Del(mapUid); + ForceEndSelf(uid, rule); + return; } - } - - private void OnSelectLocation(Entity ent, ref AntagSelectLocationEvent args) - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var xform)) - { - if (xform.MapID != ent.Comp.Map) - continue; - - if (xform.GridUid == null || !ent.Comp.MapGrids.Contains(xform.GridUid.Value)) - continue; - if (_whitelistSystem.IsWhitelistFail(ent.Comp.SpawnerWhitelist, uid)) - continue; - - args.Coordinates.Add(_transform.GetMapCoordinates(xform)); - } + var ev = new RuleLoadedGridsEvent(mapId, grids); + RaiseLocalEvent(uid, ref ev); } } diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 1b62778d7585..6688bfd980c6 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -260,10 +260,10 @@ private void OnMapInit(Entity ent, ref MapInitEvent arg { var map = Transform(ent).MapID; - var rules = EntityQueryEnumerator(); - while (rules.MoveNext(out var uid, out _, out var mapRule)) + var rules = EntityQueryEnumerator(); + while (rules.MoveNext(out var uid, out _, out var grids)) { - if (map != mapRule.Map) + if (map != grids.Map) continue; ent.Comp.AssociatedRule = uid; break; @@ -324,7 +324,7 @@ private void OnWarDeclared(ref WarDeclaredEvent ev) if (nukeops.WarDeclaredTime != null) continue; - if (TryComp(uid, out var mapComp) && Transform(ev.DeclaratorEntity).MapID != mapComp.Map) + if (TryComp(uid, out var grids) && Transform(ev.DeclaratorEntity).MapID != grids.Map) continue; var newStatus = GetWarCondition(nukeops, ev.Status); @@ -445,7 +445,7 @@ private void CheckRoundShouldEnd(Entity ent) // Check that there are spawns available and that they can access the shuttle. var spawnsAvailable = EntityQuery(true).Any(); - if (spawnsAvailable && CompOrNull(ent)?.Map == shuttleMapId) + if (spawnsAvailable && CompOrNull(ent)?.Map == shuttleMapId) return; // Ghost spawns can still access the shuttle. Continue the round. // The shuttle is inaccessible to both living nuke operatives and yet to spawn nuke operatives, @@ -478,7 +478,7 @@ private void OnAfterAntagEntSelected(Entity ent, ref After /// Is this method the shitty glue holding together the last of my sanity? yes. /// Do i have a better solution? not presently. /// - private EntityUid? GetOutpost(Entity ent) + private EntityUid? GetOutpost(Entity ent) { if (!Resolve(ent, ref ent.Comp, false)) return null; diff --git a/Content.Server/GameTicking/Rules/RuleGridsSystem.cs b/Content.Server/GameTicking/Rules/RuleGridsSystem.cs new file mode 100644 index 000000000000..9eae9e3c95f7 --- /dev/null +++ b/Content.Server/GameTicking/Rules/RuleGridsSystem.cs @@ -0,0 +1,78 @@ +using Content.Server.Antag; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Spawners.Components; +using Content.Shared.Whitelist; +using Robust.Server.Physics; +using Robust.Shared.Map; + +namespace Content.Server.GameTicking.Rules; + +/// +/// Handles storing grids from and antags spawning on their spawners. +/// +public sealed class RuleGridsSystem : GameRuleSystem +{ + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGridSplit); + + SubscribeLocalEvent(OnLoadedGrids); + SubscribeLocalEvent(OnSelectLocation); + } + + private void OnGridSplit(ref GridSplitEvent args) + { + var rule = QueryActiveRules(); + while (rule.MoveNext(out _, out var comp, out _)) + { + if (!comp.MapGrids.Contains(args.Grid)) + continue; + + comp.MapGrids.AddRange(args.NewGrids); + break; // only 1 rule can own a grid, not multiple + } + } + + private void OnLoadedGrids(Entity ent, ref RuleLoadedGridsEvent args) + { + var (uid, comp) = ent; + if (comp.Map != null && args.Map != comp.Map) + { + Log.Warning($"{ToPrettyString(uid):rule} loaded grids on multiple maps {comp.Map} and {args.Map}, the second will be ignored."); + return; + } + + comp.Map = args.Map; + comp.MapGrids.AddRange(args.Grids); + } + + private void OnSelectLocation(Entity ent, ref AntagSelectLocationEvent args) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out var xform)) + { + if (xform.MapID != ent.Comp.Map) + continue; + + if (xform.GridUid is not {} grid || !ent.Comp.MapGrids.Contains(grid)) + continue; + + if (_whitelist.IsWhitelistFail(ent.Comp.SpawnerWhitelist, uid)) + continue; + + args.Coordinates.Add(_transform.GetMapCoordinates(xform)); + } + } +} + +/// +/// Raised by another gamerule system to store loaded grids, and have other systems work with it. +/// A single rule can only load grids for a single map, attempts to load more are ignored. +/// +[ByRefEvent] +public record struct RuleLoadedGridsEvent(MapId Map, IReadOnlyList Grids); diff --git a/Content.Server/GridPreloader/GridPreloaderSystem.cs b/Content.Server/GridPreloader/GridPreloaderSystem.cs index 569fe54141c8..e12ce41a31e8 100644 --- a/Content.Server/GridPreloader/GridPreloaderSystem.cs +++ b/Content.Server/GridPreloader/GridPreloaderSystem.cs @@ -24,12 +24,19 @@ public sealed class GridPreloaderSystem : SharedGridPreloaderSystem [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + /// + /// Whether the preloading CVar is set or not. + /// + public bool PreloadingEnabled; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnRoundRestart); SubscribeLocalEvent(OnPostGameMapLoad); + + Subs.CVar(_cfg, CCVars.PreloadGrids, value => PreloadingEnabled = value, true); } private void OnRoundRestart(RoundRestartCleanupEvent ev) @@ -52,7 +59,7 @@ private void EnsurePreloadedGridMap() if (GetPreloaderEntity() != null) return; - if (!_cfg.GetCVar(CCVars.PreloadGrids)) + if (!PreloadingEnabled) return; var mapUid = _map.CreateMap(out var mapId, false); diff --git a/Content.Server/StationEvents/Events/StationEventSystem.cs b/Content.Server/StationEvents/Events/StationEventSystem.cs index 35dc646bce67..06dfdd5276be 100644 --- a/Content.Server/StationEvents/Events/StationEventSystem.cs +++ b/Content.Server/StationEvents/Events/StationEventSystem.cs @@ -108,13 +108,4 @@ public override void Update(float frameTime) } } } - - #region Helper Functions - - protected void ForceEndSelf(EntityUid uid, GameRuleComponent? component = null) - { - GameTicker.EndGameRule(uid, component); - } - - #endregion } diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index f421cf68183c..d3b0971be081 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -422,6 +422,7 @@ weight: 5.5 minimumPlayers: 20 duration: 1 + - type: RuleGrids - type: LoadMapRule preloadedGrid: ShuttleStriker - type: NukeopsRule diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index c190b0333ad6..5a38c2221545 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -3,7 +3,6 @@ - type: entity id: Ninja parent: BaseGameRule - noSpawn: true components: - type: GenericAntagRule agentName: ninja-round-end-agent-name diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 3493749457d5..88f80fd4f6a2 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -78,6 +78,7 @@ - operationPrefix - operationSuffix - type: NukeopsRule + - type: RuleGrids - type: AntagSelection - type: AntagLoadProfileRule diff --git a/Resources/Prototypes/GameRules/unknown_shuttles.yml b/Resources/Prototypes/GameRules/unknown_shuttles.yml index 359d7bffc042..1ce365cd816b 100644 --- a/Resources/Prototypes/GameRules/unknown_shuttles.yml +++ b/Resources/Prototypes/GameRules/unknown_shuttles.yml @@ -1,7 +1,7 @@ -- type: entity - id: UnknownShuttleCargoLost +- type: entity + abstract: true parent: BaseGameRule - noSpawn: true + id: BaseUnknownShuttleRule components: - type: StationEvent startAnnouncement: station-event-unknown-shuttle-incoming @@ -10,65 +10,44 @@ weight: 5 reoccurrenceDelay: 30 duration: 1 + - type: RuleGrids + - type: LoadMapRule + +- type: entity + parent: BaseUnknownShuttleRule + id: UnknownShuttleCargoLost + components: - type: LoadMapRule preloadedGrid: ShuttleCargoLost - type: entity + parent: BaseUnknownShuttleRule id: UnknownShuttleTravelingCuisine - parent: BaseGameRule - noSpawn: true components: - - type: StationEvent - startAnnouncement: station-event-unknown-shuttle-incoming - startAudio: - path: /Audio/Announcements/attention.ogg - weight: 5 - reoccurrenceDelay: 30 - duration: 1 - type: LoadMapRule preloadedGrid: TravelingCuisine - type: entity + parent: BaseUnknownShuttleRule id: UnknownShuttleDisasterEvacPod - parent: BaseGameRule - noSpawn: true components: - - type: StationEvent - startAnnouncement: station-event-unknown-shuttle-incoming - startAudio: - path: /Audio/Announcements/attention.ogg - weight: 5 - reoccurrenceDelay: 30 - duration: 1 - type: LoadMapRule preloadedGrid: DisasterEvacPod - type: entity + parent: BaseUnknownShuttleRule id: UnknownShuttleHonki - parent: BaseGameRule - noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-unknown-shuttle-incoming - startAudio: - path: /Audio/Announcements/attention.ogg weight: 2 - reoccurrenceDelay: 30 - duration: 1 - type: LoadMapRule preloadedGrid: Honki - type: entity + parent: BaseUnknownShuttleRule id: UnknownShuttleSyndieEvacPod - parent: BaseGameRule - noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-unknown-shuttle-incoming - startAudio: - path: /Audio/Announcements/attention.ogg weight: 2 - reoccurrenceDelay: 30 - duration: 1 - type: LoadMapRule preloadedGrid: SyndieEvacPod From 9d2b4ed3b22e548f02aeee7caa855b65b37dda24 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 3 Jun 2024 20:23:43 -0400 Subject: [PATCH 278/568] Add an integration test for solution fill level sprites. (#28564) --- .../Tests/FillLevelSpriteTest.cs | 71 +++++++++++++++++++ .../Prototypes/Entities/Objects/Fun/darts.yml | 4 -- .../Entities/Objects/Specific/chemistry.yml | 4 ++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 Content.IntegrationTests/Tests/FillLevelSpriteTest.cs diff --git a/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs b/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs new file mode 100644 index 000000000000..37e777fa8cf8 --- /dev/null +++ b/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs @@ -0,0 +1,71 @@ +using System.Linq; +using Content.Shared.Chemistry.Components; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests; + +/// +/// Tests to see if any entity prototypes specify solution fill level sprites that don't exist. +/// +[TestFixture] +public sealed class FillLevelSpriteTest +{ + private static readonly string[] HandStateNames = ["left", "right"]; + + [Test] + public async Task FillLevelSpritesExist() + { + await using var pair = await PoolManager.GetServerClient(); + var client = pair.Client; + var protoMan = client.ResolveDependency(); + var componentFactory = client.ResolveDependency(); + + await client.WaitAssertion(() => + { + var protos = protoMan.EnumeratePrototypes() + .Where(p => !p.Abstract) + .Where(p => !pair.IsTestPrototype(p)) + .Where(p => p.TryGetComponent(out _, componentFactory)) + .OrderBy(p => p.ID) + .ToList(); + + foreach (var proto in protos) + { + Assert.That(proto.TryGetComponent(out var visuals, componentFactory)); + Assert.That(proto.TryGetComponent(out var sprite, componentFactory)); + + var rsi = sprite.BaseRSI; + + // Test base sprite fills + if (!string.IsNullOrEmpty(visuals.FillBaseName)) + { + for (var i = 1; i <= visuals.MaxFillLevels; i++) + { + var state = $"{visuals.FillBaseName}{i}"; + Assert.That(rsi.TryGetState(state, out _), @$"{proto.ID} has SolutionContainerVisualsComponent with + MaxFillLevels = {visuals.MaxFillLevels}, but {rsi.Path} doesn't have state {state}!"); + } + } + + // Test inhand sprite fills + if (!string.IsNullOrEmpty(visuals.InHandsFillBaseName)) + { + for (var i = 1; i <= visuals.InHandsMaxFillLevels; i++) + { + foreach (var handname in HandStateNames) + { + var state = $"inhand-{handname}{visuals.InHandsFillBaseName}{i}"; + Assert.That(rsi.TryGetState(state, out _), @$"{proto.ID} has SolutionContainerVisualsComponent with + InHandsMaxFillLevels = {visuals.InHandsMaxFillLevels}, but {rsi.Path} doesn't have state {state}!"); + } + + } + } + } + }); + + await pair.CleanReturnAsync(); + } +} diff --git a/Resources/Prototypes/Entities/Objects/Fun/darts.yml b/Resources/Prototypes/Entities/Objects/Fun/darts.yml index 36c841995e5c..c0b5eb33999d 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/darts.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/darts.yml @@ -83,10 +83,6 @@ max: 1 - !type:DoActsBehavior acts: [ "Destruction" ] - - type: Appearance - - type: SolutionContainerVisuals - maxFillLevels: 1 - fillBaseName: dart - type: entity parent: Dart diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml index ce0e0b629a3e..dd15a90baa23 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml @@ -233,6 +233,8 @@ sprite: Objects/Specific/Chemistry/beaker_cryostasis.rsi layers: - state: beakernoreact + - type: SolutionContainerVisuals + maxFillLevels: 0 - type: SolutionContainerManager solutions: beaker: @@ -251,6 +253,8 @@ sprite: Objects/Specific/Chemistry/beaker_bluespace.rsi layers: - state: beakerbluespace + - type: SolutionContainerVisuals + maxFillLevels: 0 - type: SolutionContainerManager solutions: beaker: From a3a1538d32c4e0c7d9aeab9790e0c522a211fc79 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Tue, 4 Jun 2024 11:53:24 +0000 Subject: [PATCH 279/568] move gamerule components to shared (#28572) * move MinMax to shared * cleanup MinMax * move other ticking components to shared just because * remove unused prototype file * update everything to use shared components * test * test 2 * test 3 --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../DestructibleDestructionTest.cs | 1 + .../Tests/GameRules/FailAndStartPresetTest.cs | 2 +- .../Tests/GameRules/RuleMaxTimeRestartTest.cs | 2 +- Content.Server/Administration/ServerApi.cs | 2 +- Content.Server/Antag/AntagSelectionSystem.cs | 2 +- .../Components/AntagSelectionComponent.cs | 2 +- .../Antag/MobReplacementRuleSystem.cs | 2 +- .../Behaviors/SpawnEntitiesBehavior.cs | 6 ++--- .../Destructible/Thresholds/MinMax.cs | 26 ------------------- .../GameTicking/GameTicker.GameRule.cs | 2 +- .../GameTicking/Rules/DeathMatchRuleSystem.cs | 2 +- .../GameTicking/Rules/GameRulePrototype.cs | 15 ----------- .../Rules/GameRuleSystem.Utility.cs | 2 +- .../GameTicking/Rules/GameRuleSystem.cs | 2 +- .../Rules/InactivityTimeRestartRuleSystem.cs | 2 +- .../Rules/KillCalloutRuleSystem.cs | 2 +- .../GameTicking/Rules/LoadMapRuleSystem.cs | 2 +- .../Rules/MaxTimeRestartRuleSystem.cs | 2 +- .../GameTicking/Rules/NukeopsRuleSystem.cs | 2 +- .../GameTicking/Rules/RespawnRuleSystem.cs | 2 +- .../Rules/RevolutionaryRuleSystem.cs | 2 +- .../RoundstartStationVariationRuleSystem.cs | 2 +- .../GameTicking/Rules/SandboxRuleSystem.cs | 2 +- .../GameTicking/Rules/SecretRuleSystem.cs | 2 +- .../GameTicking/Rules/SubGamemodesSystem.cs | 2 +- .../GameTicking/Rules/TraitorRuleSystem.cs | 2 +- .../GameTicking/Rules/ZombieRuleSystem.cs | 2 +- Content.Server/Objectives/ObjectivesSystem.cs | 2 +- .../PowerMonitoringConsoleSystem.cs | 2 +- .../EntitySystems/ConditionalSpawnerSystem.cs | 2 +- .../BasicStationEventSchedulerSystem.cs | 2 +- .../Events/AlertLevelInterceptionRule.cs | 4 +-- .../StationEvents/Events/AnomalySpawnRule.cs | 2 +- .../Events/BluespaceArtifactRule.cs | 2 +- .../Events/BluespaceLockerRule.cs | 2 +- .../StationEvents/Events/BreakerFlipRule.cs | 2 +- .../Events/BureaucraticErrorRule.cs | 2 +- .../StationEvents/Events/CargoGiftsRule.cs | 2 +- .../StationEvents/Events/ClericalErrorRule.cs | 2 +- .../StationEvents/Events/FalseAlarmRule.cs | 2 +- .../StationEvents/Events/GasLeakRule.cs | 2 +- .../StationEvents/Events/ImmovableRodRule.cs | 2 +- .../StationEvents/Events/IonStormRule.cs | 2 +- .../StationEvents/Events/KudzuGrowthRule.cs | 2 +- .../Events/MassHallucinationsRule.cs | 2 +- .../StationEvents/Events/MeteorSwarmRule.cs | 2 +- .../StationEvents/Events/NinjaSpawnRule.cs | 2 +- .../Events/PowerGridCheckRule.cs | 2 +- .../Events/RandomEntityStorageSpawnRule.cs | 2 +- .../Events/RandomSentienceRule.cs | 2 +- .../StationEvents/Events/RandomSpawnRule.cs | 2 +- .../StationEvents/Events/SolarFlareRule.cs | 2 +- .../Events/StationEventSystem.cs | 2 +- .../StationEvents/Events/VentClogRule.cs | 2 +- .../StationEvents/Events/VentCrittersRule.cs | 2 +- .../RampingStationEventSchedulerSystem.cs | 2 +- .../Destructible/Thresholds/MinMax.cs | 24 +++++++++++++++++ .../Components/ActiveGameRuleComponent.cs | 6 ++--- .../Components/DelayedStartRuleComponent.cs | 2 +- .../Components/EndedGameRuleComponent.cs | 6 ++--- .../Components/GameRuleComponent.cs | 4 +-- 61 files changed, 88 insertions(+), 108 deletions(-) delete mode 100644 Content.Server/Destructible/Thresholds/MinMax.cs delete mode 100644 Content.Server/GameTicking/Rules/GameRulePrototype.cs create mode 100644 Content.Shared/Destructible/Thresholds/MinMax.cs rename {Content.Server => Content.Shared}/GameTicking/Components/ActiveGameRuleComponent.cs (67%) rename {Content.Server => Content.Shared}/GameTicking/Components/DelayedStartRuleComponent.cs (91%) rename {Content.Server => Content.Shared}/GameTicking/Components/EndedGameRuleComponent.cs (61%) rename {Content.Server => Content.Shared}/GameTicking/Components/GameRuleComponent.cs (94%) diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs index e14a8264678d..a50238d8f50d 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs @@ -3,6 +3,7 @@ using Content.Server.Destructible.Thresholds.Behaviors; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; +using Content.Shared.Destructible.Thresholds; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes; diff --git a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs index 1fed226beee5..09a27c1baea8 100644 --- a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs @@ -1,9 +1,9 @@ #nullable enable using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Presets; using Content.Shared.CCVar; using Content.Shared.GameTicking; +using Content.Shared.GameTicking.Components; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.GameRules; diff --git a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs index 20a157e33e83..4708bd10c08a 100644 --- a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs @@ -1,9 +1,9 @@ using Content.Server.GameTicking; using Content.Server.GameTicking.Commands; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Shared.CCVar; +using Content.Shared.GameTicking.Components; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Timing; diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index 04fd38598fb2..d0f23db637b4 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -8,13 +8,13 @@ using System.Threading.Tasks; using Content.Server.Administration.Systems; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; using Content.Server.Maps; using Content.Server.RoundEnd; using Content.Shared.Administration.Managers; using Content.Shared.CCVar; +using Content.Shared.GameTicking.Components; using Content.Shared.Prototypes; using Robust.Server.ServerStatus; using Robust.Shared.Asynchronous; diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index 710bb8f3d765..52ccb1bd708d 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Antag.Components; using Content.Server.Chat.Managers; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles.Components; @@ -15,6 +14,7 @@ using Content.Server.Station.Systems; using Content.Shared.Antag; using Content.Shared.GameTicking; +using Content.Shared.GameTicking.Components; using Content.Shared.Ghost; using Content.Shared.Humanoid; using Content.Shared.Players; diff --git a/Content.Server/Antag/Components/AntagSelectionComponent.cs b/Content.Server/Antag/Components/AntagSelectionComponent.cs index f70c27d12f28..1ab16e8aec87 100644 --- a/Content.Server/Antag/Components/AntagSelectionComponent.cs +++ b/Content.Server/Antag/Components/AntagSelectionComponent.cs @@ -1,6 +1,6 @@ using Content.Server.Administration.Systems; -using Content.Server.Destructible.Thresholds; using Content.Shared.Antag; +using Content.Shared.Destructible.Thresholds; using Content.Shared.Roles; using Content.Shared.Storage; using Content.Shared.Whitelist; diff --git a/Content.Server/Antag/MobReplacementRuleSystem.cs b/Content.Server/Antag/MobReplacementRuleSystem.cs index 18837b5a7c87..eeff765d8bb7 100644 --- a/Content.Server/Antag/MobReplacementRuleSystem.cs +++ b/Content.Server/Antag/MobReplacementRuleSystem.cs @@ -1,7 +1,7 @@ using Content.Server.Antag.Mimic; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.VendingMachines; using Robust.Shared.Map; using Robust.Shared.Random; diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs index 057b6df9df21..4ec6fa9e908e 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs @@ -1,12 +1,12 @@ using System.Numerics; using Content.Server.Forensics; using Content.Server.Stack; +using Content.Shared.Destructible.Thresholds; using Content.Shared.Prototypes; using Content.Shared.Stacks; using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; namespace Content.Server.Destructible.Thresholds.Behaviors { @@ -17,8 +17,8 @@ public sealed partial class SpawnEntitiesBehavior : IThresholdBehavior /// /// Entities spawned on reaching this threshold, from a min to a max. /// - [DataField("spawn", customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] - public Dictionary Spawn { get; set; } = new(); + [DataField] + public Dictionary Spawn = new(); [DataField("offset")] public float Offset { get; set; } = 0.5f; diff --git a/Content.Server/Destructible/Thresholds/MinMax.cs b/Content.Server/Destructible/Thresholds/MinMax.cs deleted file mode 100644 index c44864183abe..000000000000 --- a/Content.Server/Destructible/Thresholds/MinMax.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Robust.Shared.Random; - -namespace Content.Server.Destructible.Thresholds -{ - [Serializable] - [DataDefinition] - public partial struct MinMax - { - [DataField("min")] - public int Min; - - [DataField("max")] - public int Max; - - public MinMax(int min, int max) - { - Min = min; - Max = max; - } - - public int Next(IRobustRandom random) - { - return random.Next(Min, Max + 1); - } - } -} diff --git a/Content.Server/GameTicking/GameTicker.GameRule.cs b/Content.Server/GameTicking/GameTicker.GameRule.cs index a6d0a4baf0a9..2a33d01bd09e 100644 --- a/Content.Server/GameTicking/GameTicker.GameRule.cs +++ b/Content.Server/GameTicking/GameTicker.GameRule.cs @@ -1,9 +1,9 @@ using System.Linq; using Content.Server.Administration; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Shared.Administration; using Content.Shared.Database; +using Content.Shared.GameTicking.Components; using Content.Shared.Prototypes; using JetBrains.Annotations; using Robust.Shared.Console; diff --git a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs index 9e3203d170bc..557e44f09528 100644 --- a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs @@ -1,12 +1,12 @@ using System.Linq; using Content.Server.Administration.Commands; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.KillTracking; using Content.Server.Mind; using Content.Server.Points; using Content.Server.RoundEnd; using Content.Server.Station.Systems; +using Content.Shared.GameTicking.Components; using Content.Shared.Points; using Content.Shared.Storage; using Robust.Server.GameObjects; diff --git a/Content.Server/GameTicking/Rules/GameRulePrototype.cs b/Content.Server/GameTicking/Rules/GameRulePrototype.cs deleted file mode 100644 index 47f99184f73b..000000000000 --- a/Content.Server/GameTicking/Rules/GameRulePrototype.cs +++ /dev/null @@ -1,15 +0,0 @@ - - -namespace Content.Server.GameTicking.Rules; - -/* -[Prototype("gameRule")] -public sealed partial class GameRulePrototype : IPrototype -{ - [IdDataField] - public string ID { get; private set; } = default!; - - [DataField("config", required: true)] - public GameRuleConfiguration Configuration { get; private set; } = default!; -} -*/ diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs index 9ac7a6edbb90..5a5eb720cfb7 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs @@ -1,8 +1,8 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Random.Helpers; using Robust.Server.GameObjects; using Robust.Shared.Collections; diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.cs index 05374aa1396b..730748ce6b9e 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.cs @@ -1,6 +1,6 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Robust.Server.GameObjects; using Robust.Shared.Random; using Robust.Shared.Timing; diff --git a/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs b/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs index 01fa387595cc..e56537c43811 100644 --- a/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs @@ -1,7 +1,7 @@ using System.Threading; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Robust.Server.Player; using Robust.Shared.Player; using Timer = Robust.Shared.Timing.Timer; diff --git a/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs b/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs index 3da55e30c9e5..8f706fd2ad36 100644 --- a/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs @@ -1,8 +1,8 @@ using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.KillTracking; using Content.Shared.Chat; +using Content.Shared.GameTicking.Components; using Robust.Server.Player; using Robust.Shared.Player; using Robust.Shared.Random; diff --git a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs index 1c09d6e86e10..3594242bcdee 100644 --- a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs @@ -1,6 +1,6 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.GridPreloader; +using Content.Shared.GameTicking.Components; using Robust.Server.GameObjects; using Robust.Server.Maps; using Robust.Shared.Map; diff --git a/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs b/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs index cae99fee9fc3..db9df8a5b009 100644 --- a/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs @@ -1,7 +1,7 @@ using System.Threading; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Timer = Robust.Shared.Timing.Timer; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 6688bfd980c6..5c3459ef81b5 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -11,6 +11,7 @@ using Content.Server.Station.Components; using Content.Server.Store.Components; using Content.Server.Store.Systems; +using Content.Shared.GameTicking.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.NPC.Components; @@ -24,7 +25,6 @@ using Robust.Shared.Random; using Robust.Shared.Utility; using System.Linq; -using Content.Server.GameTicking.Components; using Content.Shared.Store.Components; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs index 3f8d31f622ff..38102f1a2e9b 100644 --- a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs @@ -1,9 +1,9 @@ using Content.Server.Chat.Managers; using Content.Server.Database.Migrations.Postgres; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Systems; using Content.Shared.Chat; +using Content.Shared.GameTicking.Components; using Content.Shared.Interaction.Events; using Content.Shared.Mind; using Content.Shared.Mobs; diff --git a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs index 58ced9629503..c5f88ab6cf1e 100644 --- a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs @@ -12,6 +12,7 @@ using Content.Server.Shuttles.Systems; using Content.Server.Station.Systems; using Content.Shared.Database; +using Content.Shared.GameTicking.Components; using Content.Shared.Humanoid; using Content.Shared.IdentityManagement; using Content.Shared.Mind; @@ -27,7 +28,6 @@ using Content.Shared.Zombies; using Robust.Shared.Prototypes; using Robust.Shared.Timing; -using Content.Server.GameTicking.Components; using Content.Shared.Cuffs.Components; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs b/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs index f09ed3ebc3cd..570889155b32 100644 --- a/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs @@ -1,9 +1,9 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Shuttles.Systems; using Content.Server.Station.Components; using Content.Server.Station.Events; +using Content.Shared.GameTicking.Components; using Content.Shared.Storage; using Robust.Shared.Prototypes; using Robust.Shared.Random; diff --git a/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs b/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs index c60670a3ad7a..23e9ee5a7d21 100644 --- a/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs @@ -1,6 +1,6 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Sandbox; +using Content.Shared.GameTicking.Components; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs index d25262b797af..320f9d197aac 100644 --- a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs @@ -1,10 +1,10 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Server.Administration.Logs; -using Content.Server.GameTicking.Components; using Content.Server.Chat.Managers; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Random; using Content.Shared.CCVar; using Content.Shared.Database; diff --git a/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs b/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs index 4486ee40fbbd..4fe3827ce5ce 100644 --- a/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs +++ b/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs @@ -1,5 +1,5 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Storage; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 29de85a42e50..17442da85770 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -5,6 +5,7 @@ using Content.Server.PDA.Ringer; using Content.Server.Roles; using Content.Server.Traitor.Uplink; +using Content.Shared.GameTicking.Components; using Content.Shared.Mind; using Content.Shared.NPC.Systems; using Content.Shared.Objectives.Components; @@ -15,7 +16,6 @@ using Robust.Shared.Random; using System.Linq; using System.Text; -using Content.Server.GameTicking.Components; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs index 97e5fd3fc468..95d1f791ef29 100644 --- a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Station.Components; using Content.Server.Station.Systems; using Content.Server.Zombies; +using Content.Shared.GameTicking.Components; using Content.Shared.Humanoid; using Content.Shared.Mind; using Content.Shared.Mobs; @@ -15,7 +16,6 @@ using Robust.Shared.Player; using Robust.Shared.Timing; using System.Globalization; -using Content.Server.GameTicking.Components; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index f8ecc22828e5..18077b413ad5 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -1,6 +1,7 @@ using Content.Server.GameTicking; using Content.Server.Shuttles.Systems; using Content.Shared.Cuffs.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Mind; using Content.Shared.Objectives.Components; using Content.Shared.Objectives.Systems; @@ -9,7 +10,6 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using System.Linq; -using Content.Server.GameTicking.Components; using System.Text; using Robust.Server.Player; diff --git a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs index 42c84b7f43b6..35b17dc95849 100644 --- a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Power.NodeGroups; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Pinpointer; using Content.Shared.Power; using JetBrains.Annotations; @@ -13,7 +14,6 @@ using Robust.Shared.Map.Components; using Robust.Shared.Utility; using System.Linq; -using Content.Server.GameTicking.Components; namespace Content.Server.Power.EntitySystems; diff --git a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs index 75f861879890..f57481b05b63 100644 --- a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs +++ b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs @@ -1,8 +1,8 @@ using System.Numerics; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Spawners.Components; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs b/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs index 7e32f546bed1..501d9033b9c0 100644 --- a/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs @@ -1,11 +1,11 @@ using System.Linq; using Content.Server.Administration; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Shared.Administration; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; using Robust.Shared.Random; using Robust.Shared.Toolshed; diff --git a/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs b/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs index a78a542d3b31..916d7d16883f 100644 --- a/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs +++ b/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs @@ -1,6 +1,6 @@ -using Content.Server.GameTicking.Components; using Content.Server.StationEvents.Components; using Content.Server.AlertLevel; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; @@ -20,4 +20,4 @@ protected override void Started(EntityUid uid, AlertLevelInterceptionRuleCompone _alertLevelSystem.SetLevel(chosenStation.Value, component.AlertLevel, true, true, true); } -} \ No newline at end of file +} diff --git a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs index cf66a7c052e4..06da91e25693 100644 --- a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs +++ b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs @@ -1,7 +1,7 @@ using Content.Server.Anomaly; -using Content.Server.GameTicking.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs index aaa0f0cffc32..fc4f180adbdd 100644 --- a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs +++ b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs @@ -1,5 +1,5 @@ -using Content.Server.GameTicking.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Random; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/BluespaceLockerRule.cs b/Content.Server/StationEvents/Events/BluespaceLockerRule.cs index eef9850e739d..b19485bc31ee 100644 --- a/Content.Server/StationEvents/Events/BluespaceLockerRule.cs +++ b/Content.Server/StationEvents/Events/BluespaceLockerRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Resist; using Content.Server.Station.Components; @@ -6,6 +5,7 @@ using Content.Server.Storage.Components; using Content.Server.Storage.EntitySystems; using Content.Shared.Access.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Coordinates; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/BreakerFlipRule.cs b/Content.Server/StationEvents/Events/BreakerFlipRule.cs index ef2825938974..0cfe87051d5e 100644 --- a/Content.Server/StationEvents/Events/BreakerFlipRule.cs +++ b/Content.Server/StationEvents/Events/BreakerFlipRule.cs @@ -1,8 +1,8 @@ -using Content.Server.GameTicking.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs b/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs index ccfb8aee58e2..b49049a10d43 100644 --- a/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs +++ b/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs @@ -1,9 +1,9 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.Station.Systems; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/Events/CargoGiftsRule.cs b/Content.Server/StationEvents/Events/CargoGiftsRule.cs index 4cddf4674b30..ff2d1ca63117 100644 --- a/Content.Server/StationEvents/Events/CargoGiftsRule.cs +++ b/Content.Server/StationEvents/Events/CargoGiftsRule.cs @@ -2,9 +2,9 @@ using Content.Server.Cargo.Components; using Content.Server.Cargo.Systems; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Prototypes; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/ClericalErrorRule.cs b/Content.Server/StationEvents/Events/ClericalErrorRule.cs index 854ee685b33d..e52c2c05aafe 100644 --- a/Content.Server/StationEvents/Events/ClericalErrorRule.cs +++ b/Content.Server/StationEvents/Events/ClericalErrorRule.cs @@ -1,9 +1,9 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.StationRecords; using Content.Server.StationRecords.Systems; using Content.Shared.StationRecords; +using Content.Shared.GameTicking.Components; using Robust.Shared.Random; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/FalseAlarmRule.cs b/Content.Server/StationEvents/Events/FalseAlarmRule.cs index 115b6d905eae..e1c642a85d7a 100644 --- a/Content.Server/StationEvents/Events/FalseAlarmRule.cs +++ b/Content.Server/StationEvents/Events/FalseAlarmRule.cs @@ -1,6 +1,6 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/Events/GasLeakRule.cs b/Content.Server/StationEvents/Events/GasLeakRule.cs index c17f17a8279d..9e1f70474c1b 100644 --- a/Content.Server/StationEvents/Events/GasLeakRule.cs +++ b/Content.Server/StationEvents/Events/GasLeakRule.cs @@ -1,7 +1,7 @@ using Content.Server.Atmos.EntitySystems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Audio; using Robust.Shared.Random; using Robust.Shared.Timing; diff --git a/Content.Server/StationEvents/Events/ImmovableRodRule.cs b/Content.Server/StationEvents/Events/ImmovableRodRule.cs index aa193f2f4cbc..17a92e1b3de3 100644 --- a/Content.Server/StationEvents/Events/ImmovableRodRule.cs +++ b/Content.Server/StationEvents/Events/ImmovableRodRule.cs @@ -1,9 +1,9 @@ using System.Numerics; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.ImmovableRod; using Content.Server.StationEvents.Components; using Content.Server.Weapons.Ranged.Systems; +using Content.Shared.GameTicking.Components; using Content.Shared.Storage; using Robust.Shared.Prototypes; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/Events/IonStormRule.cs b/Content.Server/StationEvents/Events/IonStormRule.cs index 8361cc6048a3..926ecc2db921 100644 --- a/Content.Server/StationEvents/Events/IonStormRule.cs +++ b/Content.Server/StationEvents/Events/IonStormRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using System.Linq; using Content.Server.Silicons.Laws; using Content.Server.Station.Components; @@ -7,6 +6,7 @@ using Content.Shared.Database; using Content.Shared.Dataset; using Content.Shared.FixedPoint; +using Content.Shared.GameTicking.Components; using Content.Shared.Random; using Content.Shared.Random.Helpers; using Content.Shared.Silicons.Laws; diff --git a/Content.Server/StationEvents/Events/KudzuGrowthRule.cs b/Content.Server/StationEvents/Events/KudzuGrowthRule.cs index 5b56e03846f9..42c57ffcaae5 100644 --- a/Content.Server/StationEvents/Events/KudzuGrowthRule.cs +++ b/Content.Server/StationEvents/Events/KudzuGrowthRule.cs @@ -1,6 +1,6 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/MassHallucinationsRule.cs b/Content.Server/StationEvents/Events/MassHallucinationsRule.cs index d6f609bee1d1..bfb7699e9d6e 100644 --- a/Content.Server/StationEvents/Events/MassHallucinationsRule.cs +++ b/Content.Server/StationEvents/Events/MassHallucinationsRule.cs @@ -1,7 +1,7 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.Traits.Assorted; +using Content.Shared.GameTicking.Components; using Content.Shared.Mind.Components; using Content.Shared.Traits.Assorted; diff --git a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs index 455011259dca..b97cde86a02e 100644 --- a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs +++ b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs @@ -1,7 +1,7 @@ using System.Numerics; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; diff --git a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs index d9d68a386cf5..9cbc193ce61f 100644 --- a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs +++ b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs @@ -1,8 +1,8 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Ninja.Systems; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Map; using Robust.Shared.Map.Components; diff --git a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs index d547fc944619..e00369a321c9 100644 --- a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs +++ b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs @@ -1,10 +1,10 @@ using System.Threading; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.Player; diff --git a/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs b/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs index 87d50fc8b2a4..a9f279381805 100644 --- a/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs +++ b/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs @@ -1,8 +1,8 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.Storage.Components; using Content.Server.Storage.EntitySystems; +using Content.Shared.GameTicking.Components; using Robust.Shared.Map; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/Events/RandomSentienceRule.cs b/Content.Server/StationEvents/Events/RandomSentienceRule.cs index 06bb470602ba..3869f227bd4d 100644 --- a/Content.Server/StationEvents/Events/RandomSentienceRule.cs +++ b/Content.Server/StationEvents/Events/RandomSentienceRule.cs @@ -1,8 +1,8 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Ghost.Roles.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/RandomSpawnRule.cs b/Content.Server/StationEvents/Events/RandomSpawnRule.cs index 77744d44e465..e904c24ba603 100644 --- a/Content.Server/StationEvents/Events/RandomSpawnRule.cs +++ b/Content.Server/StationEvents/Events/RandomSpawnRule.cs @@ -1,6 +1,6 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/SolarFlareRule.cs b/Content.Server/StationEvents/Events/SolarFlareRule.cs index 0370b4ee61d0..19f6e393d201 100644 --- a/Content.Server/StationEvents/Events/SolarFlareRule.cs +++ b/Content.Server/StationEvents/Events/SolarFlareRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Radio; using Robust.Shared.Random; @@ -8,6 +7,7 @@ using Content.Shared.Radio.Components; using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/StationEventSystem.cs b/Content.Server/StationEvents/Events/StationEventSystem.cs index 06dfdd5276be..c4c51afd2a1f 100644 --- a/Content.Server/StationEvents/Events/StationEventSystem.cs +++ b/Content.Server/StationEvents/Events/StationEventSystem.cs @@ -1,10 +1,10 @@ using Content.Server.Administration.Logs; using Content.Server.Chat.Systems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.Station.Systems; using Content.Server.StationEvents.Components; using Content.Shared.Database; +using Content.Shared.GameTicking.Components; using Robust.Shared.Audio.Systems; using Robust.Shared.Player; using Robust.Shared.Prototypes; diff --git a/Content.Server/StationEvents/Events/VentClogRule.cs b/Content.Server/StationEvents/Events/VentClogRule.cs index 867f41dcccfa..043ea0375aaa 100644 --- a/Content.Server/StationEvents/Events/VentClogRule.cs +++ b/Content.Server/StationEvents/Events/VentClogRule.cs @@ -6,9 +6,9 @@ using Robust.Shared.Random; using System.Linq; using Content.Server.Fluids.EntitySystems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/VentCrittersRule.cs b/Content.Server/StationEvents/Events/VentCrittersRule.cs index c2605039bce2..fba9cfa6a60e 100644 --- a/Content.Server/StationEvents/Events/VentCrittersRule.cs +++ b/Content.Server/StationEvents/Events/VentCrittersRule.cs @@ -1,7 +1,7 @@ -using Content.Server.GameTicking.Components; using Content.Server.StationEvents.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Storage; using Robust.Shared.Map; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs index 6c1ad4f48911..f65105de0669 100644 --- a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs @@ -1,10 +1,10 @@ using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.StationEvents.Events; using Content.Shared.CCVar; +using Content.Shared.GameTicking.Components; using Robust.Shared.Configuration; using Robust.Shared.Random; diff --git a/Content.Shared/Destructible/Thresholds/MinMax.cs b/Content.Shared/Destructible/Thresholds/MinMax.cs new file mode 100644 index 000000000000..e086a0f61c30 --- /dev/null +++ b/Content.Shared/Destructible/Thresholds/MinMax.cs @@ -0,0 +1,24 @@ +using Robust.Shared.Random; + +namespace Content.Shared.Destructible.Thresholds; + +[DataDefinition, Serializable] +public partial struct MinMax +{ + [DataField] + public int Min; + + [DataField] + public int Max; + + public MinMax(int min, int max) + { + Min = min; + Max = max; + } + + public int Next(IRobustRandom random) + { + return random.Next(Min, Max + 1); + } +} diff --git a/Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs b/Content.Shared/GameTicking/Components/ActiveGameRuleComponent.cs similarity index 67% rename from Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs rename to Content.Shared/GameTicking/Components/ActiveGameRuleComponent.cs index b9e6fa5d4b8b..51bdd1c0371d 100644 --- a/Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs +++ b/Content.Shared/GameTicking/Components/ActiveGameRuleComponent.cs @@ -1,10 +1,8 @@ -namespace Content.Server.GameTicking.Components; +namespace Content.Shared.GameTicking.Components; /// /// Added to game rules before and removed before . /// Mutually exclusive with . /// [RegisterComponent] -public sealed partial class ActiveGameRuleComponent : Component -{ -} +public sealed partial class ActiveGameRuleComponent : Component; diff --git a/Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs b/Content.Shared/GameTicking/Components/DelayedStartRuleComponent.cs similarity index 91% rename from Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs rename to Content.Shared/GameTicking/Components/DelayedStartRuleComponent.cs index de4be83627de..9275da29b01c 100644 --- a/Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs +++ b/Content.Shared/GameTicking/Components/DelayedStartRuleComponent.cs @@ -1,6 +1,6 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.GameTicking.Components; +namespace Content.Shared.GameTicking.Components; /// /// Generic component used to track a gamerule that's start has been delayed. diff --git a/Content.Server/GameTicking/Components/EndedGameRuleComponent.cs b/Content.Shared/GameTicking/Components/EndedGameRuleComponent.cs similarity index 61% rename from Content.Server/GameTicking/Components/EndedGameRuleComponent.cs rename to Content.Shared/GameTicking/Components/EndedGameRuleComponent.cs index 3234bfff3a0e..5e209ed78a2e 100644 --- a/Content.Server/GameTicking/Components/EndedGameRuleComponent.cs +++ b/Content.Shared/GameTicking/Components/EndedGameRuleComponent.cs @@ -1,10 +1,8 @@ -namespace Content.Server.GameTicking.Components; +namespace Content.Shared.GameTicking.Components; /// /// Added to game rules before . /// Mutually exclusive with . /// [RegisterComponent] -public sealed partial class EndedGameRuleComponent : Component -{ -} +public sealed partial class EndedGameRuleComponent : Component; diff --git a/Content.Server/GameTicking/Components/GameRuleComponent.cs b/Content.Shared/GameTicking/Components/GameRuleComponent.cs similarity index 94% rename from Content.Server/GameTicking/Components/GameRuleComponent.cs rename to Content.Shared/GameTicking/Components/GameRuleComponent.cs index 635452b3f3c3..4e93c2b00382 100644 --- a/Content.Server/GameTicking/Components/GameRuleComponent.cs +++ b/Content.Shared/GameTicking/Components/GameRuleComponent.cs @@ -1,8 +1,8 @@ -using Content.Server.Destructible.Thresholds; +using Content.Shared.Destructible.Thresholds; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.GameTicking.Components; +namespace Content.Shared.GameTicking.Components; /// /// Component attached to all gamerule entities. From 87fe804831e15ccd757d5830c9413811ed2fb780 Mon Sep 17 00:00:00 2001 From: Ghagliiarghii <68826635+Ghagliiarghii@users.noreply.github.com> Date: Tue, 4 Jun 2024 07:57:17 -0400 Subject: [PATCH 280/568] tyvm, tysm (#28577) * tyvm, tysm * cya -> see you * ya --- Resources/Locale/en-US/speech/speech-chatsan.ftl | 9 +++++++++ Resources/Prototypes/Accents/word_replacements.yml | 3 +++ 2 files changed, 12 insertions(+) diff --git a/Resources/Locale/en-US/speech/speech-chatsan.ftl b/Resources/Locale/en-US/speech/speech-chatsan.ftl index 25e6c6f1ea98..ef9450729522 100644 --- a/Resources/Locale/en-US/speech/speech-chatsan.ftl +++ b/Resources/Locale/en-US/speech/speech-chatsan.ftl @@ -120,3 +120,12 @@ chatsan-replacement-43 = i guess chatsan-word-44 = tbf chatsan-replacement-44 = to be fair + +chatsan-word-45 = tysm +chatsan-replacement-45 = thank you so much + +chatsan-word-46 = tyvm +chatsan-replacement-46 = thank you very much + +chatsan-word-47 = cya +chatsan-replacement-47 = see ya \ No newline at end of file diff --git a/Resources/Prototypes/Accents/word_replacements.yml b/Resources/Prototypes/Accents/word_replacements.yml index 9a801d786de8..40cc94e4537f 100644 --- a/Resources/Prototypes/Accents/word_replacements.yml +++ b/Resources/Prototypes/Accents/word_replacements.yml @@ -425,6 +425,9 @@ chatsan-word-42: chatsan-replacement-42 chatsan-word-43: chatsan-replacement-43 chatsan-word-44: chatsan-replacement-44 + chatsan-word-45: chatsan-replacement-45 + chatsan-word-46: chatsan-replacement-46 + chatsan-word-47: chatsan-replacement-47 - type: accent id: liar From 58647e703683e848730589ab003a94400f6c1075 Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:11:48 +0200 Subject: [PATCH 281/568] Don't switch-unwield if mob has more than 2 hands (#28438) --- Content.Shared/Wieldable/WieldableSystem.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Wieldable/WieldableSystem.cs b/Content.Shared/Wieldable/WieldableSystem.cs index c09044f84b43..cb95c1d7e780 100644 --- a/Content.Shared/Wieldable/WieldableSystem.cs +++ b/Content.Shared/Wieldable/WieldableSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Shared.Examine; using Content.Shared.Hands; using Content.Shared.Hands.Components; @@ -94,7 +95,8 @@ private void OnGunWielded(EntityUid uid, GunWieldBonusComponent component, ref I private void OnDeselectWieldable(EntityUid uid, WieldableComponent component, HandDeselectedEvent args) { - if (!component.Wielded) + if (!component.Wielded || + _handsSystem.EnumerateHands(args.User).Count() > 2) return; TryUnwield(uid, component, args.User); From d4fe7eda515a1374ec753e740f025aefcae18e5a Mon Sep 17 00:00:00 2001 From: Hannah Giovanna Dawson Date: Tue, 4 Jun 2024 14:23:09 +0100 Subject: [PATCH 282/568] SS14-26964 Clown Waddling Replicates, etc (#26983) --- .../Movement/Systems/WaddleAnimationSystem.cs | 156 ++++++++---------- .../Movement/Systems/WaddleAnimationSystem.cs | 5 + .../Components/WaddleWhenWornComponent.cs | 11 +- .../EntitySystems}/WaddleClothingSystem.cs | 17 +- .../Components/WaddleAnimationComponent.cs | 28 ++-- .../Systems/SharedWaddleAnimationSystem.cs | 106 ++++++++++++ 6 files changed, 207 insertions(+), 116 deletions(-) create mode 100644 Content.Server/Movement/Systems/WaddleAnimationSystem.cs rename {Content.Client/Clothing/Systems => Content.Shared/Clothing/EntitySystems}/WaddleClothingSystem.cs (59%) create mode 100644 Content.Shared/Movement/Systems/SharedWaddleAnimationSystem.cs diff --git a/Content.Client/Movement/Systems/WaddleAnimationSystem.cs b/Content.Client/Movement/Systems/WaddleAnimationSystem.cs index 9555c1f6b9e4..0ed2d04f694a 100644 --- a/Content.Client/Movement/Systems/WaddleAnimationSystem.cs +++ b/Content.Client/Movement/Systems/WaddleAnimationSystem.cs @@ -2,94 +2,115 @@ using Content.Client.Buckle; using Content.Client.Gravity; using Content.Shared.ActionBlocker; -using Content.Shared.Buckle.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Components; -using Content.Shared.Movement.Events; -using Content.Shared.StatusEffect; -using Content.Shared.Stunnable; +using Content.Shared.Movement.Systems; using Robust.Client.Animations; using Robust.Client.GameObjects; using Robust.Shared.Animations; -using Robust.Shared.Timing; namespace Content.Client.Movement.Systems; -public sealed class WaddleAnimationSystem : EntitySystem +public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem { [Dependency] private readonly AnimationPlayerSystem _animation = default!; [Dependency] private readonly GravitySystem _gravity = default!; - [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; [Dependency] private readonly BuckleSystem _buckle = default!; [Dependency] private readonly MobStateSystem _mobState = default!; public override void Initialize() { - SubscribeLocalEvent(OnMovementInput); - SubscribeLocalEvent(OnStartedWalking); - SubscribeLocalEvent(OnStoppedWalking); + base.Initialize(); + + SubscribeAllEvent(OnStartWaddling); SubscribeLocalEvent(OnAnimationCompleted); - SubscribeLocalEvent(OnStunned); - SubscribeLocalEvent(OnKnockedDown); - SubscribeLocalEvent(OnBuckleChange); + SubscribeAllEvent(OnStopWaddling); } - private void OnMovementInput(EntityUid entity, WaddleAnimationComponent component, MoveInputEvent args) + private void OnStartWaddling(StartedWaddlingEvent msg, EntitySessionEventArgs args) { - // Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume - // they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR. - if (!_timing.IsFirstTimePredicted) - { + if (TryComp(GetEntity(msg.Entity), out var comp)) + StartWaddling((GetEntity(msg.Entity), comp)); + } + + private void OnStopWaddling(StoppedWaddlingEvent msg, EntitySessionEventArgs args) + { + if (TryComp(GetEntity(msg.Entity), out var comp)) + StopWaddling((GetEntity(msg.Entity), comp)); + } + + private void StartWaddling(Entity entity) + { + if (_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName)) return; - } - if (!args.HasDirectionalMovement && component.IsCurrentlyWaddling) - { - var stopped = new StoppedWaddlingEvent(entity); + if (!TryComp(entity.Owner, out var mover)) + return; - RaiseLocalEvent(entity, ref stopped); + if (_gravity.IsWeightless(entity.Owner)) + return; + if (!_actionBlocker.CanMove(entity.Owner, mover)) return; - } - // Only start waddling if we're not currently AND we're actually moving. - if (component.IsCurrentlyWaddling || !args.HasDirectionalMovement) + // Do nothing if buckled in + if (_buckle.IsBuckled(entity.Owner)) return; - var started = new StartedWaddlingEvent(entity); + // Do nothing if crit or dead (for obvious reasons) + if (_mobState.IsIncapacitated(entity.Owner)) + return; - RaiseLocalEvent(entity, ref started); + PlayWaddleAnimationUsing( + (entity.Owner, entity.Comp), + CalculateAnimationLength(entity.Comp, mover), + CalculateTumbleIntensity(entity.Comp) + ); } - private void OnStartedWalking(EntityUid uid, WaddleAnimationComponent component, StartedWaddlingEvent args) + private static float CalculateTumbleIntensity(WaddleAnimationComponent component) { - if (_animation.HasRunningAnimation(uid, component.KeyName)) - return; + return component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity; + } + + private static float CalculateAnimationLength(WaddleAnimationComponent component, InputMoverComponent mover) + { + return mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength; + } - if (!TryComp(uid, out var mover)) + private void OnAnimationCompleted(Entity entity, ref AnimationCompletedEvent args) + { + if (args.Key != entity.Comp.KeyName) return; - if (_gravity.IsWeightless(uid)) + if (!TryComp(entity.Owner, out var mover)) return; + PlayWaddleAnimationUsing( + (entity.Owner, entity.Comp), + CalculateAnimationLength(entity.Comp, mover), + CalculateTumbleIntensity(entity.Comp) + ); + } - if (!_actionBlocker.CanMove(uid, mover)) + private void StopWaddling(Entity entity) + { + if (!_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName)) return; - // Do nothing if buckled in - if (_buckle.IsBuckled(uid)) - return; + _animation.Stop(entity.Owner, entity.Comp.KeyName); - // Do nothing if crit or dead (for obvious reasons) - if (_mobState.IsIncapacitated(uid)) + if (!TryComp(entity.Owner, out var sprite)) return; - var tumbleIntensity = component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity; - var len = mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength; + sprite.Offset = new Vector2(); + sprite.Rotation = Angle.FromDegrees(0); + } - component.LastStep = !component.LastStep; - component.IsCurrentlyWaddling = true; + private void PlayWaddleAnimationUsing(Entity entity, float len, float tumbleIntensity) + { + entity.Comp.LastStep = !entity.Comp.LastStep; var anim = new Animation() { @@ -116,58 +137,13 @@ private void OnStartedWalking(EntityUid uid, WaddleAnimationComponent component, KeyFrames = { new AnimationTrackProperty.KeyFrame(new Vector2(), 0), - new AnimationTrackProperty.KeyFrame(component.HopIntensity, len/2), + new AnimationTrackProperty.KeyFrame(entity.Comp.HopIntensity, len/2), new AnimationTrackProperty.KeyFrame(new Vector2(), len/2), } } } }; - _animation.Play(uid, anim, component.KeyName); - } - - private void OnStoppedWalking(EntityUid uid, WaddleAnimationComponent component, StoppedWaddlingEvent args) - { - StopWaddling(uid, component); - } - - private void OnAnimationCompleted(EntityUid uid, WaddleAnimationComponent component, AnimationCompletedEvent args) - { - var started = new StartedWaddlingEvent(uid); - - RaiseLocalEvent(uid, ref started); - } - - private void OnStunned(EntityUid uid, WaddleAnimationComponent component, StunnedEvent args) - { - StopWaddling(uid, component); - } - - private void OnKnockedDown(EntityUid uid, WaddleAnimationComponent component, KnockedDownEvent args) - { - StopWaddling(uid, component); - } - - private void OnBuckleChange(EntityUid uid, WaddleAnimationComponent component, BuckleChangeEvent args) - { - StopWaddling(uid, component); - } - - private void StopWaddling(EntityUid uid, WaddleAnimationComponent component) - { - if (!component.IsCurrentlyWaddling) - return; - - _animation.Stop(uid, component.KeyName); - - if (!TryComp(uid, out var sprite)) - { - return; - } - - sprite.Offset = new Vector2(); - sprite.Rotation = Angle.FromDegrees(0); - - component.IsCurrentlyWaddling = false; + _animation.Play(entity.Owner, anim, entity.Comp.KeyName); } } diff --git a/Content.Server/Movement/Systems/WaddleAnimationSystem.cs b/Content.Server/Movement/Systems/WaddleAnimationSystem.cs new file mode 100644 index 000000000000..e6083210e1bf --- /dev/null +++ b/Content.Server/Movement/Systems/WaddleAnimationSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Movement.Systems; + +namespace Content.Server.Movement.Systems; + +public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem; diff --git a/Content.Shared/Clothing/Components/WaddleWhenWornComponent.cs b/Content.Shared/Clothing/Components/WaddleWhenWornComponent.cs index 5cd7a7245775..fb7490ef4fbe 100644 --- a/Content.Shared/Clothing/Components/WaddleWhenWornComponent.cs +++ b/Content.Shared/Clothing/Components/WaddleWhenWornComponent.cs @@ -1,35 +1,36 @@ using System.Numerics; +using Robust.Shared.GameStates; namespace Content.Shared.Clothing.Components; /// /// Defines something as causing waddling when worn. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class WaddleWhenWornComponent : Component { /// /// How high should they hop during the waddle? Higher hop = more energy. /// - [DataField] + [DataField, AutoNetworkedField] public Vector2 HopIntensity = new(0, 0.25f); /// /// How far should they rock backward and forward during the waddle? /// Each step will alternate between this being a positive and negative rotation. More rock = more scary. /// - [DataField] + [DataField, AutoNetworkedField] public float TumbleIntensity = 20.0f; /// /// How long should a complete step take? Less time = more chaos. /// - [DataField] + [DataField, AutoNetworkedField] public float AnimationLength = 0.66f; /// /// How much shorter should the animation be when running? /// - [DataField] + [DataField, AutoNetworkedField] public float RunAnimationLengthMultiplier = 0.568f; } diff --git a/Content.Client/Clothing/Systems/WaddleClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/WaddleClothingSystem.cs similarity index 59% rename from Content.Client/Clothing/Systems/WaddleClothingSystem.cs rename to Content.Shared/Clothing/EntitySystems/WaddleClothingSystem.cs index b8ac3c207bf2..b445eb258e9a 100644 --- a/Content.Client/Clothing/Systems/WaddleClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/WaddleClothingSystem.cs @@ -1,8 +1,9 @@ -using Content.Shared.Clothing.Components; +using Content.Shared.Clothing; +using Content.Shared.Clothing.Components; using Content.Shared.Movement.Components; using Content.Shared.Inventory.Events; -namespace Content.Client.Clothing.Systems; +namespace Content.Shared.Clothing.EntitySystems; public sealed class WaddleClothingSystem : EntitySystem { @@ -10,13 +11,13 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnGotEquipped); - SubscribeLocalEvent(OnGotUnequipped); + SubscribeLocalEvent(OnGotEquipped); + SubscribeLocalEvent(OnGotUnequipped); } - private void OnGotEquipped(EntityUid entity, WaddleWhenWornComponent comp, GotEquippedEvent args) + private void OnGotEquipped(EntityUid entity, WaddleWhenWornComponent comp, ClothingGotEquippedEvent args) { - var waddleAnimComp = EnsureComp(args.Equipee); + var waddleAnimComp = EnsureComp(args.Wearer); waddleAnimComp.AnimationLength = comp.AnimationLength; waddleAnimComp.HopIntensity = comp.HopIntensity; @@ -24,8 +25,8 @@ private void OnGotEquipped(EntityUid entity, WaddleWhenWornComponent comp, GotEq waddleAnimComp.TumbleIntensity = comp.TumbleIntensity; } - private void OnGotUnequipped(EntityUid entity, WaddleWhenWornComponent comp, GotUnequippedEvent args) + private void OnGotUnequipped(EntityUid entity, WaddleWhenWornComponent comp, ClothingGotUnequippedEvent args) { - RemComp(args.Equipee); + RemComp(args.Wearer); } } diff --git a/Content.Shared/Movement/Components/WaddleAnimationComponent.cs b/Content.Shared/Movement/Components/WaddleAnimationComponent.cs index c43ef3042eb9..3cd9a3749e81 100644 --- a/Content.Shared/Movement/Components/WaddleAnimationComponent.cs +++ b/Content.Shared/Movement/Components/WaddleAnimationComponent.cs @@ -1,31 +1,32 @@ using System.Numerics; +using Robust.Shared.Serialization; namespace Content.Shared.Movement.Components; /// /// Declares that an entity has started to waddle like a duck/clown. /// -/// The newly be-waddled. -[ByRefEvent] -public record struct StartedWaddlingEvent(EntityUid Entity) +/// The newly be-waddled. +[Serializable, NetSerializable] +public sealed class StartedWaddlingEvent(NetEntity entity) : EntityEventArgs { - public EntityUid Entity = Entity; + public NetEntity Entity = entity; } /// /// Declares that an entity has stopped waddling like a duck/clown. /// -/// The former waddle-er. -[ByRefEvent] -public record struct StoppedWaddlingEvent(EntityUid Entity) +/// The former waddle-er. +[Serializable, NetSerializable] +public sealed class StoppedWaddlingEvent(NetEntity entity) : EntityEventArgs { - public EntityUid Entity = Entity; + public NetEntity Entity = entity; } /// /// Defines something as having a waddle animation when it moves. /// -[RegisterComponent] +[RegisterComponent, AutoGenerateComponentState] public sealed partial class WaddleAnimationComponent : Component { /// @@ -38,26 +39,26 @@ public sealed partial class WaddleAnimationComponent : Component /// /// How high should they hop during the waddle? Higher hop = more energy. /// - [DataField] + [DataField, AutoNetworkedField] public Vector2 HopIntensity = new(0, 0.25f); /// /// How far should they rock backward and forward during the waddle? /// Each step will alternate between this being a positive and negative rotation. More rock = more scary. /// - [DataField] + [DataField, AutoNetworkedField] public float TumbleIntensity = 20.0f; /// /// How long should a complete step take? Less time = more chaos. /// - [DataField] + [DataField, AutoNetworkedField] public float AnimationLength = 0.66f; /// /// How much shorter should the animation be when running? /// - [DataField] + [DataField, AutoNetworkedField] public float RunAnimationLengthMultiplier = 0.568f; /// @@ -68,5 +69,6 @@ public sealed partial class WaddleAnimationComponent : Component /// /// Stores if we're currently waddling so we can start/stop as appropriate and can tell other systems our state. /// + [AutoNetworkedField] public bool IsCurrentlyWaddling; } diff --git a/Content.Shared/Movement/Systems/SharedWaddleAnimationSystem.cs b/Content.Shared/Movement/Systems/SharedWaddleAnimationSystem.cs new file mode 100644 index 000000000000..2fcb4fc60bbe --- /dev/null +++ b/Content.Shared/Movement/Systems/SharedWaddleAnimationSystem.cs @@ -0,0 +1,106 @@ +using Content.Shared.Buckle.Components; +using Content.Shared.Gravity; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Events; +using Content.Shared.Movement.Systems; +using Content.Shared.Standing; +using Content.Shared.Stunnable; +using Robust.Shared.Timing; + +namespace Content.Shared.Movement.Systems; + +public abstract class SharedWaddleAnimationSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() + { + // Startup + SubscribeLocalEvent(OnComponentStartup); + + // Start moving possibilities + SubscribeLocalEvent(OnMovementInput); + SubscribeLocalEvent(OnStood); + + // Stop moving possibilities + SubscribeLocalEvent((Entity ent, ref StunnedEvent _) => StopWaddling(ent)); + SubscribeLocalEvent((Entity ent, ref DownedEvent _) => StopWaddling(ent)); + SubscribeLocalEvent((Entity ent, ref BuckleChangeEvent _) => StopWaddling(ent)); + SubscribeLocalEvent(OnGravityChanged); + } + + private void OnGravityChanged(Entity ent, ref GravityChangedEvent args) + { + if (!args.HasGravity && ent.Comp.IsCurrentlyWaddling) + StopWaddling(ent); + } + + private void OnComponentStartup(Entity entity, ref ComponentStartup args) + { + if (!TryComp(entity.Owner, out var moverComponent)) + return; + + // If the waddler is currently moving, make them start waddling + if ((moverComponent.HeldMoveButtons & MoveButtons.AnyDirection) == MoveButtons.AnyDirection) + { + RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner))); + } + } + + private void OnMovementInput(Entity entity, ref MoveInputEvent args) + { + // Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume + // they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR. + if (!_timing.IsFirstTimePredicted) + { + return; + } + + if (!args.HasDirectionalMovement && entity.Comp.IsCurrentlyWaddling) + { + StopWaddling(entity); + + return; + } + + // Only start waddling if we're not currently AND we're actually moving. + if (entity.Comp.IsCurrentlyWaddling || !args.HasDirectionalMovement) + return; + + entity.Comp.IsCurrentlyWaddling = true; + + RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner))); + } + + private void OnStood(Entity entity, ref StoodEvent args) + { + // Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume + // they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR. + if (!_timing.IsFirstTimePredicted) + { + return; + } + + if (!TryComp(entity.Owner, out var mover)) + { + return; + } + + if ((mover.HeldMoveButtons & MoveButtons.AnyDirection) == MoveButtons.None) + return; + + if (entity.Comp.IsCurrentlyWaddling) + return; + + entity.Comp.IsCurrentlyWaddling = true; + + RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner))); + } + + private void StopWaddling(Entity entity) + { + entity.Comp.IsCurrentlyWaddling = false; + + RaiseNetworkEvent(new StoppedWaddlingEvent(GetNetEntity(entity.Owner))); + } +} From 0b4fc7e298bc37cc7db053bf4d2c2adb7e873d0a Mon Sep 17 00:00:00 2001 From: Hannah Giovanna Dawson Date: Tue, 4 Jun 2024 14:26:19 +0100 Subject: [PATCH 283/568] Make projectile & hitscan reflection only try once with the best reflector (#28539) --- .../Weapons/Reflect/ReflectSystem.cs | 250 ++++++++++-------- .../Weapons/Reflect/ReflectUserComponent.cs | 5 +- 2 files changed, 143 insertions(+), 112 deletions(-) diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 03ad97edff27..9b89be6202ad 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -49,73 +49,123 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnReflectCollide); - SubscribeLocalEvent(OnReflectHitscan); + SubscribeLocalEvent(OnObjectReflectProjectileAttempt); + SubscribeLocalEvent(OnObjectReflectHitscanAttempt); SubscribeLocalEvent(OnReflectEquipped); SubscribeLocalEvent(OnReflectUnequipped); SubscribeLocalEvent(OnReflectHandEquipped); SubscribeLocalEvent(OnReflectHandUnequipped); SubscribeLocalEvent(OnToggleReflect); - SubscribeLocalEvent(OnReflectUserCollide); - SubscribeLocalEvent(OnReflectUserHitscan); + SubscribeLocalEvent(OnUserProjectileReflectAttempt); + SubscribeLocalEvent(OnUserHitscanReflectAttempt); } - private void OnReflectUserHitscan(EntityUid uid, ReflectUserComponent component, ref HitScanReflectAttemptEvent args) + private void OnUserHitscanReflectAttempt(Entity user, ref HitScanReflectAttemptEvent args) { if (args.Reflected) return; - foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.WITHOUT_POCKET)) - { - if (!TryReflectHitscan(uid, ent, args.Shooter, args.SourceItem, args.Direction, out var dir)) - continue; + if (!UserCanReflect(user, out var bestReflectorUid)) + return; - args.Direction = dir.Value; - args.Reflected = true; - break; - } + if (!TryReflectHitscan(user.Owner, bestReflectorUid.Value, args.Shooter, args.SourceItem, args.Direction, out var dir)) + return; + + args.Direction = dir.Value; + args.Reflected = true; } - private void OnReflectUserCollide(EntityUid uid, ReflectUserComponent component, ref ProjectileReflectAttemptEvent args) + private void OnUserProjectileReflectAttempt(Entity user, ref ProjectileReflectAttemptEvent args) { - foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.WITHOUT_POCKET)) - { - if (!TryReflectProjectile(uid, ent, args.ProjUid)) - continue; + if (args.Cancelled) + return; - args.Cancelled = true; - break; - } + if (!TryComp(args.ProjUid, out var reflectiveComponent)) + return; + + if (!UserCanReflect(user, out var bestReflectorUid, (args.ProjUid, reflectiveComponent))) + return; + + if (!TryReflectProjectile(user, bestReflectorUid.Value, (args.ProjUid, args.Component))) + return; + + args.Cancelled = true; + } + + private void OnObjectReflectHitscanAttempt(Entity obj, ref HitScanReflectAttemptEvent args) + { + if (args.Reflected || (obj.Comp.Reflects & args.Reflective) == 0x0) + return; + + if (!TryReflectHitscan(obj, obj, args.Shooter, args.SourceItem, args.Direction, out var dir)) + return; + + args.Direction = dir.Value; + args.Reflected = true; } - private void OnReflectCollide(EntityUid uid, ReflectComponent component, ref ProjectileReflectAttemptEvent args) + private void OnObjectReflectProjectileAttempt(Entity obj, ref ProjectileReflectAttemptEvent args) { if (args.Cancelled) return; - if (TryReflectProjectile(uid, uid, args.ProjUid, reflect: component)) - args.Cancelled = true; + if (!TryReflectProjectile(obj, obj, (args.ProjUid, args.Component))) + return; + + args.Cancelled = true; + } + + /// + /// Can a user reflect something that's hit them? Returns true if so, and the best reflector available in the user's equipment. + /// + private bool UserCanReflect(Entity user, [NotNullWhen(true)] out Entity? bestReflector, Entity? projectile = null) + { + bestReflector = null; + + foreach (var entityUid in _inventorySystem.GetHandOrInventoryEntities(user.Owner, SlotFlags.WITHOUT_POCKET)) + { + if (!TryComp(entityUid, out var comp)) + continue; + + if (!comp.Enabled) + continue; + + if (bestReflector != null && bestReflector.Value.Comp.ReflectProb >= comp.ReflectProb) + continue; + + if (projectile != null && (comp.Reflects & projectile.Value.Comp.Reflective) == 0x0) + continue; + + bestReflector = (entityUid, comp); + } + + return bestReflector != null; } - private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid projectile, ProjectileComponent? projectileComp = null, ReflectComponent? reflect = null) + private bool TryReflectProjectile(EntityUid user, Entity reflector, Entity projectile) { - // Do we have the components needed to try a reflect at all? if ( - !Resolve(reflector, ref reflect, false) || - !reflect.Enabled || + // Is it on? + !reflector.Comp.Enabled || + // Is the projectile deflectable? !TryComp(projectile, out var reflective) || - (reflect.Reflects & reflective.Reflective) == 0x0 || + // Does the deflector deflect the type of projecitle? + (reflector.Comp.Reflects & reflective.Reflective) == 0x0 || + // Is the projectile correctly set up with physics? !TryComp(projectile, out var physics) || - TryComp(reflector, out var staminaComponent) && staminaComponent.Critical || + // If the user of the reflector is a mob with stamina, is it capable of deflecting? + TryComp(user, out var staminaComponent) && staminaComponent.Critical || _standing.IsDown(reflector) ) return false; - if (!_random.Prob(CalcReflectChance(reflector, reflect))) + // If this dice roll fails, the shot isn't deflected + if (!_random.Prob(GetReflectChance(reflector))) return false; - var rotation = _random.NextAngle(-reflect.Spread / 2, reflect.Spread / 2).Opposite(); + // Below handles what happens after being deflected. + var rotation = _random.NextAngle(-reflector.Comp.Spread / 2, reflector.Comp.Spread / 2).Opposite(); var existingVelocity = _physics.GetMapLinearVelocity(projectile, component: physics); var relativeVelocity = existingVelocity - _physics.GetMapLinearVelocity(user); var newVelocity = rotation.RotateVec(relativeVelocity); @@ -132,98 +182,52 @@ private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid if (_netManager.IsServer) { _popup.PopupEntity(Loc.GetString("reflect-shot"), user); - _audio.PlayPvs(reflect.SoundOnReflect, user, AudioHelpers.WithVariation(0.05f, _random)); + _audio.PlayPvs(reflector.Comp.SoundOnReflect, user, AudioHelpers.WithVariation(0.05f, _random)); } - if (Resolve(projectile, ref projectileComp, false)) - { - _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)} from {ToPrettyString(projectileComp.Weapon)} shot by {projectileComp.Shooter}"); + _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)} from {ToPrettyString(projectile.Comp.Weapon)} shot by {projectile.Comp.Shooter}"); - projectileComp.Shooter = user; - projectileComp.Weapon = user; - Dirty(projectile, projectileComp); - } - else - { - _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)}"); - } + projectile.Comp.Shooter = user; + projectile.Comp.Weapon = user; + Dirty(projectile); return true; } - private float CalcReflectChance(EntityUid reflector, ReflectComponent reflect) - { - /* - * The rules of deflection are as follows: - * If you innately reflect things via magic, biology etc., you always have a full chance. - * If you are standing up and standing still, you're prepared to deflect and have full chance. - * If you have velocity, your deflection chance depends on your velocity, clamped. - * If you are floating, your chance is the minimum value possible. - * You cannot deflect if you are knocked down or stunned. - */ - - if (reflect.Innate) - return reflect.ReflectProb; - - if (_gravity.IsWeightless(reflector)) - return reflect.MinReflectProb; - - if (!TryComp(reflector, out var reflectorPhysics)) - return reflect.ReflectProb; - - return MathHelper.Lerp( - reflect.MinReflectProb, - reflect.ReflectProb, - // Inverse progression between velocities fed in as progression between probabilities. We go high -> low so the output here needs to be _inverted_. - 1 - Math.Clamp((reflectorPhysics.LinearVelocity.Length() - reflect.VelocityBeforeNotMaxProb) / (reflect.VelocityBeforeMinProb - reflect.VelocityBeforeNotMaxProb), 0, 1) - ); - } - - private void OnReflectHitscan(EntityUid uid, ReflectComponent component, ref HitScanReflectAttemptEvent args) - { - if (args.Reflected || - (component.Reflects & args.Reflective) == 0x0) - { - return; - } - - if (TryReflectHitscan(uid, uid, args.Shooter, args.SourceItem, args.Direction, out var dir)) - { - args.Direction = dir.Value; - args.Reflected = true; - } - } - private bool TryReflectHitscan( EntityUid user, - EntityUid reflector, + Entity reflector, EntityUid? shooter, EntityUid shotSource, Vector2 direction, [NotNullWhen(true)] out Vector2? newDirection) { - if (!TryComp(reflector, out var reflect) || - !reflect.Enabled || - TryComp(reflector, out var staminaComponent) && staminaComponent.Critical || - _standing.IsDown(reflector)) + if ( + // Is the reflector enabled? + !reflector.Comp.Enabled || + // If the user is a mob with stamina, is it capable of deflecting? + TryComp(user, out var staminaComponent) && staminaComponent.Critical || + _standing.IsDown(user)) { newDirection = null; return false; } - if (!_random.Prob(CalcReflectChance(reflector, reflect))) + // If this dice roll fails, the shot is not deflected. + if (!_random.Prob(GetReflectChance(reflector))) { newDirection = null; return false; } + // Below handles what happens after being deflected. if (_netManager.IsServer) { _popup.PopupEntity(Loc.GetString("reflect-shot"), user); - _audio.PlayPvs(reflect.SoundOnReflect, user, AudioHelpers.WithVariation(0.05f, _random)); + _audio.PlayPvs(reflector.Comp.SoundOnReflect, user, AudioHelpers.WithVariation(0.05f, _random)); } - var spread = _random.NextAngle(-reflect.Spread / 2, reflect.Spread / 2); + var spread = _random.NextAngle(-reflector.Comp.Spread / 2, reflector.Comp.Spread / 2); newDirection = -spread.RotateVec(direction); if (shooter != null) @@ -234,51 +238,81 @@ private bool TryReflectHitscan( return true; } - private void OnReflectEquipped(EntityUid uid, ReflectComponent component, GotEquippedEvent args) + private float GetReflectChance(Entity reflector) + { + /* + * The rules of deflection are as follows: + * If you innately reflect things via magic, biology etc., you always have a full chance. + * If you are standing up and standing still, you're prepared to deflect and have full chance. + * If you have velocity, your deflection chance depends on your velocity, clamped. + * If you are floating, your chance is the minimum value possible. + */ + + if (reflector.Comp.Innate) + return reflector.Comp.ReflectProb; + + if (_gravity.IsWeightless(reflector)) + return reflector.Comp.MinReflectProb; + + if (!TryComp(reflector, out var reflectorPhysics)) + return reflector.Comp.ReflectProb; + + return MathHelper.Lerp( + reflector.Comp.MinReflectProb, + reflector.Comp.ReflectProb, + // Inverse progression between velocities fed in as progression between probabilities. We go high -> low so the output here needs to be _inverted_. + 1 - Math.Clamp((reflectorPhysics.LinearVelocity.Length() - reflector.Comp.VelocityBeforeNotMaxProb) / (reflector.Comp.VelocityBeforeMinProb - reflector.Comp.VelocityBeforeNotMaxProb), 0, 1) + ); + } + + private void OnReflectEquipped(Entity reflector, ref GotEquippedEvent args) { if (_gameTiming.ApplyingState) return; EnsureComp(args.Equipee); - if (component.Enabled) + if (reflector.Comp.Enabled) EnableAlert(args.Equipee); } - private void OnReflectUnequipped(EntityUid uid, ReflectComponent comp, GotUnequippedEvent args) + private void OnReflectUnequipped(Entity reflector, ref GotUnequippedEvent args) { RefreshReflectUser(args.Equipee); } - private void OnReflectHandEquipped(EntityUid uid, ReflectComponent component, GotEquippedHandEvent args) + private void OnReflectHandEquipped(Entity reflector, ref GotEquippedHandEvent args) { if (_gameTiming.ApplyingState) return; EnsureComp(args.User); - if (component.Enabled) + if (reflector.Comp.Enabled) EnableAlert(args.User); } - private void OnReflectHandUnequipped(EntityUid uid, ReflectComponent component, GotUnequippedHandEvent args) + private void OnReflectHandUnequipped(Entity reflector, ref GotUnequippedHandEvent args) { RefreshReflectUser(args.User); } - private void OnToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggledEvent args) + private void OnToggleReflect(Entity reflector, ref ItemToggledEvent args) { - comp.Enabled = args.Activated; - Dirty(uid, comp); + reflector.Comp.Enabled = args.Activated; + Dirty(reflector); + + if (args.User == null) + return; - if (comp.Enabled) - EnableAlert(uid); + if (reflector.Comp.Enabled) + EnableAlert(args.User.Value); else - DisableAlert(uid); + DisableAlert(args.User.Value); } /// - /// Refreshes whether someone has reflection potential so we can raise directed events on them. + /// Refreshes whether someone has reflection potential, so we can raise directed events on them. /// private void RefreshReflectUser(EntityUid user) { diff --git a/Content.Shared/Weapons/Reflect/ReflectUserComponent.cs b/Content.Shared/Weapons/Reflect/ReflectUserComponent.cs index 44fe60813e78..44ef481a3724 100644 --- a/Content.Shared/Weapons/Reflect/ReflectUserComponent.cs +++ b/Content.Shared/Weapons/Reflect/ReflectUserComponent.cs @@ -7,7 +7,4 @@ namespace Content.Shared.Weapons.Reflect; /// Reflection events will then be relayed. /// [RegisterComponent, NetworkedComponent] -public sealed partial class ReflectUserComponent : Component -{ - -} +public sealed partial class ReflectUserComponent : Component; From b3cfd9b4ff7b1f06bd917c158058229773b92955 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 4 Jun 2024 13:27:27 +0000 Subject: [PATCH 284/568] 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 0ae775e19368..6a20b3a411d1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Dutch-VanDerLinde - changes: - - message: Romerol now properly works on the dead. - type: Tweak - id: 6179 - time: '2024-03-18T06:25:36.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26222 - author: ElectroJr changes: - message: Added an option to try and ignore some errors if a replay fails to load, @@ -3850,3 +3843,11 @@ id: 6678 time: '2024-06-03T18:48:44.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27788 +- author: FairlySadPanda + changes: + - message: Stacking multiple deflecting items at once has been removed; now only + your best item is used. + type: Tweak + id: 6679 + time: '2024-06-04T13:26:20.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28539 From d5019cee96a922f7e0547bc507fdfa1ed9471a39 Mon Sep 17 00:00:00 2001 From: Kevin Zheng Date: Tue, 4 Jun 2024 06:54:22 -0800 Subject: [PATCH 285/568] Add CVar to disable pow3r parallel processing (#28488) * Add CVar to disable pow3r parallel processing * Cache CVar value * Fix pointyhat * Move all CVar-ing to Content.Server --- .../Power/EntitySystems/PowerNetSystem.cs | 13 ++++++++++++- Content.Server/Power/Pow3r/BatteryRampPegSolver.cs | 9 +++++++-- Content.Shared/CCVar/CCVars.cs | 6 ++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Content.Server/Power/EntitySystems/PowerNetSystem.cs b/Content.Server/Power/EntitySystems/PowerNetSystem.cs index 7bd057951c9b..6c35ba20083c 100644 --- a/Content.Server/Power/EntitySystems/PowerNetSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerNetSystem.cs @@ -3,9 +3,11 @@ using Content.Server.Power.Components; using Content.Server.Power.NodeGroups; using Content.Server.Power.Pow3r; +using Content.Shared.CCVar; using Content.Shared.Power; using JetBrains.Annotations; using Robust.Server.GameObjects; +using Robust.Shared.Configuration; using Robust.Shared.Threading; namespace Content.Server.Power.EntitySystems @@ -18,6 +20,7 @@ public sealed class PowerNetSystem : EntitySystem { [Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly PowerNetConnectorSystem _powerNetConnector = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IParallelManager _parMan = default!; [Dependency] private readonly PowerReceiverSystem _powerReceiver = default!; @@ -25,13 +28,14 @@ public sealed class PowerNetSystem : EntitySystem private readonly HashSet _powerNetReconnectQueue = new(); private readonly HashSet _apcNetReconnectQueue = new(); - private readonly BatteryRampPegSolver _solver = new(); + private BatteryRampPegSolver _solver = new(); public override void Initialize() { base.Initialize(); UpdatesAfter.Add(typeof(NodeGroupSystem)); + _solver = new(_cfg.GetCVar(CCVars.DebugPow3rDisableParallel)); SubscribeLocalEvent(ApcPowerReceiverInit); SubscribeLocalEvent(ApcPowerReceiverShutdown); @@ -53,6 +57,13 @@ public override void Initialize() SubscribeLocalEvent(PowerSupplierShutdown); SubscribeLocalEvent(PowerSupplierPaused); SubscribeLocalEvent(PowerSupplierUnpaused); + + Subs.CVar(_cfg, CCVars.DebugPow3rDisableParallel, DebugPow3rDisableParallelChanged); + } + + private void DebugPow3rDisableParallelChanged(bool val) + { + _solver = new(val); } private void ApcPowerReceiverInit(EntityUid uid, ApcPowerReceiverComponent component, ComponentInit args) diff --git a/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs b/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs index 12118968b70f..599c55b6ba4b 100644 --- a/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs +++ b/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs @@ -9,9 +9,11 @@ namespace Content.Server.Power.Pow3r public sealed class BatteryRampPegSolver : IPowerSolver { private UpdateNetworkJob _networkJob; + private bool _disableParallel; - public BatteryRampPegSolver() + public BatteryRampPegSolver(bool disableParallel = false) { + _disableParallel = disableParallel; _networkJob = new() { Solver = this, @@ -56,7 +58,10 @@ public void Tick(float frameTime, PowerState state, IParallelManager parallel) // suppliers + discharger) Then decide based on total layer size whether its worth parallelizing that // layer? _networkJob.Networks = group; - parallel.ProcessNow(_networkJob, group.Count); + if (_disableParallel) + parallel.ProcessSerialNow(_networkJob, group.Count); + else + parallel.ProcessNow(_networkJob, group.Count); } ClearBatteries(state); diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index f41e0a1e6f10..b3da71fde1a4 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -2062,5 +2062,11 @@ public static readonly CVarDef /// public static readonly CVarDef DebugOptionVisualizerTest = CVarDef.Create("debug.option_visualizer_test", false, CVar.CLIENTONLY); + + /// + /// Set to true to disable parallel processing in the pow3r solver. + /// + public static readonly CVarDef DebugPow3rDisableParallel = + CVarDef.Create("debug.pow3r_disable_parallel", true, CVar.SERVERONLY); } } From 71a6e60818fbaab41b90a854e38dd2d8e6850304 Mon Sep 17 00:00:00 2001 From: Moony Date: Tue, 4 Jun 2024 10:59:33 -0500 Subject: [PATCH 286/568] Revert "Buff the AME until somebody fixes engineering" (#28419) --- Content.Server/Ame/AmeNodeGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Ame/AmeNodeGroup.cs b/Content.Server/Ame/AmeNodeGroup.cs index 608356f76059..40da6222d201 100644 --- a/Content.Server/Ame/AmeNodeGroup.cs +++ b/Content.Server/Ame/AmeNodeGroup.cs @@ -182,7 +182,7 @@ public float CalculatePower(int fuel, int cores) // Fuel is squared so more fuel vastly increases power and efficiency // We divide by the number of cores so a larger AME is less efficient at the same fuel settings // this results in all AMEs having the same efficiency at the same fuel-per-core setting - return 2000000f * fuel * fuel / cores; + return 20000f * fuel * fuel / cores; } public int GetTotalStability() From b394eb9e84ac2faf6018849a842c6fa7d999c1ed Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 4 Jun 2024 16:00:39 +0000 Subject: [PATCH 287/568] 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 6a20b3a411d1..600334df6b85 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: ElectroJr - changes: - - message: Added an option to try and ignore some errors if a replay fails to load, - type: Add - id: 6180 - time: '2024-03-18T07:31:37.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26212 - author: Tayrtahn changes: - message: Cyborg recharging stations are able to charge cyborgs again. @@ -3851,3 +3844,11 @@ id: 6679 time: '2024-06-04T13:26:20.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28539 +- author: moonheart08 + changes: + - message: AME power output is no longer super-buffed. Larger stations will now + require multiple engines to stay powered. + type: Tweak + id: 6680 + time: '2024-06-04T15:59:33.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28419 From 627187988e76b97e81663747faafb21da182ebe0 Mon Sep 17 00:00:00 2001 From: icekot8 <93311212+icekot8@users.noreply.github.com> Date: Tue, 4 Jun 2024 19:34:07 +0300 Subject: [PATCH 288/568] hand teleport portals now may start in the same grid. (#28556) --- Content.Server/Teleportation/HandTeleporterSystem.cs | 2 +- .../Teleportation/Components/HandTeleporterComponent.cs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Content.Server/Teleportation/HandTeleporterSystem.cs b/Content.Server/Teleportation/HandTeleporterSystem.cs index d4c6753c4b52..1cd2e1d8c25c 100644 --- a/Content.Server/Teleportation/HandTeleporterSystem.cs +++ b/Content.Server/Teleportation/HandTeleporterSystem.cs @@ -98,7 +98,7 @@ private void HandlePortalUpdating(EntityUid uid, HandTeleporterComponent compone if (xform.ParentUid != xform.GridUid) // Still, don't portal. return; - if (xform.ParentUid != Transform(component.FirstPortal!.Value).ParentUid) + if (!component.AllowPortalsOnDifferentGrids && xform.ParentUid != Transform(component.FirstPortal!.Value).ParentUid) { // Whoops. Fizzle time. Crime time too because yippee I'm not refactoring this logic right now (I started to, I'm not going to.) FizzlePortals(uid, component, user, true); diff --git a/Content.Shared/Teleportation/Components/HandTeleporterComponent.cs b/Content.Shared/Teleportation/Components/HandTeleporterComponent.cs index 6abd4a7d2114..6ea29d3fd6ad 100644 --- a/Content.Shared/Teleportation/Components/HandTeleporterComponent.cs +++ b/Content.Shared/Teleportation/Components/HandTeleporterComponent.cs @@ -1,4 +1,4 @@ -using Content.Shared.DoAfter; +using Content.Shared.DoAfter; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -20,6 +20,12 @@ public sealed partial class HandTeleporterComponent : Component [ViewVariables, DataField("secondPortal")] public EntityUid? SecondPortal = null; + /// + /// Portals can't be placed on different grids? + /// + [DataField] + public bool AllowPortalsOnDifferentGrids; + [DataField("firstPortalPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] public string FirstPortalPrototype = "PortalRed"; From 0e93d13cb78b86136d11daeec06464ef26ccff65 Mon Sep 17 00:00:00 2001 From: blueDev2 <89804215+blueDev2@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:12:01 -0400 Subject: [PATCH 289/568] Microwave recipes now uses stacktype id instead of entity prototype id for stacked entities (#28225) --- .../Kitchen/EntitySystems/MicrowaveSystem.cs | 64 ++++++++++++++++--- .../Recipes/Cooking/medical_recipes.yml | 4 +- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs index 8938aa8b1dc4..c69ed49d50a6 100644 --- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs @@ -35,6 +35,7 @@ using System.Linq; using Robust.Shared.Prototypes; using Robust.Shared.Timing; +using Content.Shared.Stacks; namespace Content.Server.Kitchen.EntitySystems { @@ -58,6 +59,8 @@ public sealed class MicrowaveSystem : EntitySystem [Dependency] private readonly UserInterfaceSystem _userInterface = default!; [Dependency] private readonly HandsSystem _handsSystem = default!; [Dependency] private readonly SharedItemSystem _item = default!; + [Dependency] private readonly SharedStackSystem _stack = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; [ValidatePrototypeId] private const string MalfunctionSpark = "Spark"; @@ -199,16 +202,41 @@ private void SubtractContents(MicrowaveComponent component, FoodRecipePrototype { foreach (var item in component.Storage.ContainedEntities) { - var metaData = MetaData(item); - if (metaData.EntityPrototype == null) + string? itemID = null; + + // If an entity has a stack component, use the stacktype instead of prototype id + if (TryComp(item, out var stackComp)) + { + itemID = _prototype.Index(stackComp.StackTypeId).Spawn; + } + else + { + var metaData = MetaData(item); + if (metaData.EntityPrototype == null) + { + continue; + } + itemID = metaData.EntityPrototype.ID; + } + + if (itemID != recipeSolid.Key) { continue; } - if (metaData.EntityPrototype.ID == recipeSolid.Key) + if (stackComp is not null) + { + if (stackComp.Count == 1) + { + _container.Remove(item, component.Storage); + } + _stack.Use(item, 1, stackComp); + break; + } + else { _container.Remove(item, component.Storage); - EntityManager.DeleteEntity(item); + Del(item); break; } } @@ -448,17 +476,35 @@ public void Wzhzhzh(EntityUid uid, MicrowaveComponent component, EntityUid? user AddComp(item); - var metaData = MetaData(item); //this simply begs for cooking refactor - if (metaData.EntityPrototype == null) + string? solidID = null; + int amountToAdd = 1; + + // If a microwave recipe uses a stacked item, use the default stack prototype id instead of prototype id + if (TryComp(item, out var stackComp)) + { + solidID = _prototype.Index(stackComp.StackTypeId).Spawn; + amountToAdd = stackComp.Count; + } + else + { + var metaData = MetaData(item); //this simply begs for cooking refactor + if (metaData.EntityPrototype is not null) + solidID = metaData.EntityPrototype.ID; + } + + if (solidID is null) + { continue; + } + - if (solidsDict.ContainsKey(metaData.EntityPrototype.ID)) + if (solidsDict.ContainsKey(solidID)) { - solidsDict[metaData.EntityPrototype.ID]++; + solidsDict[solidID] += amountToAdd; } else { - solidsDict.Add(metaData.EntityPrototype.ID, 1); + solidsDict.Add(solidID, amountToAdd); } if (!TryComp(item, out var solMan)) diff --git a/Resources/Prototypes/Recipes/Cooking/medical_recipes.yml b/Resources/Prototypes/Recipes/Cooking/medical_recipes.yml index 9d1947f03ebc..3bc9e22b0e4f 100644 --- a/Resources/Prototypes/Recipes/Cooking/medical_recipes.yml +++ b/Resources/Prototypes/Recipes/Cooking/medical_recipes.yml @@ -13,7 +13,7 @@ time: 10 solids: FoodPoppy: 1 - Brutepack: 1 + Brutepack: 10 MaterialCloth1: 1 reagents: TranexamicAcid: 20 @@ -26,7 +26,7 @@ time: 10 solids: FoodAloe: 1 - Ointment: 1 + Ointment: 10 MaterialCloth1: 1 reagents: Sigynate: 20 From 1de261d1b1813ef00e5ef665594bc8d5e66dc77e Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 4 Jun 2024 17:13:07 +0000 Subject: [PATCH 290/568] 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 600334df6b85..b14fae9c41ad 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Tayrtahn - changes: - - message: Cyborg recharging stations are able to charge cyborgs again. - type: Fix - id: 6181 - time: '2024-03-18T13:37:49.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26230 - author: DoutorWhite changes: - message: Introduce new health status icons. @@ -3852,3 +3845,10 @@ id: 6680 time: '2024-06-04T15:59:33.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28419 +- author: blueDev2 + changes: + - message: Fixed microwave recipes that use stacked materials + type: Fix + id: 6681 + time: '2024-06-04T17:12:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28225 From a4881f56b021d6512955b2add88135ef446953d6 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Tue, 4 Jun 2024 14:48:24 -0400 Subject: [PATCH 291/568] Add "fill level" sprites to mops and damp rag (#28590) --- .../Objects/Specific/Janitorial/janitor.yml | 34 ++++++++- .../Specific/Janitorial/advmop.rsi/fill-1.png | Bin 0 -> 200 bytes .../Specific/Janitorial/advmop.rsi/fill-2.png | Bin 0 -> 216 bytes .../advmop.rsi/inhand-left-fill-1.png | Bin 0 -> 155 bytes .../advmop.rsi/inhand-left-fill-2.png | Bin 0 -> 187 bytes .../advmop.rsi/inhand-right-fill-1.png | Bin 0 -> 157 bytes .../advmop.rsi/inhand-right-fill-2.png | Bin 0 -> 195 bytes .../Specific/Janitorial/advmop.rsi/meta.json | 62 +++++++++++++---- .../advmop.rsi/wielded-inhand-left-fill-1.png | Bin 0 -> 252 bytes .../advmop.rsi/wielded-inhand-left-fill-2.png | Bin 0 -> 282 bytes .../wielded-inhand-right-fill-1.png | Bin 0 -> 254 bytes .../wielded-inhand-right-fill-2.png | Bin 0 -> 279 bytes .../Specific/Janitorial/mop.rsi/fill-1.png | Bin 0 -> 179 bytes .../Specific/Janitorial/mop.rsi/fill-2.png | Bin 0 -> 191 bytes .../Specific/Janitorial/mop.rsi/fill-3.png | Bin 0 -> 197 bytes .../Janitorial/mop.rsi/inhand-left-fill-1.png | Bin 0 -> 145 bytes .../Janitorial/mop.rsi/inhand-left-fill-2.png | Bin 0 -> 156 bytes .../mop.rsi/inhand-right-fill-1.png | Bin 0 -> 147 bytes .../mop.rsi/inhand-right-fill-2.png | Bin 0 -> 160 bytes .../Specific/Janitorial/mop.rsi/meta.json | 65 ++++++++++++++---- .../mop.rsi/wielded-inhand-left-fill-1.png | Bin 0 -> 153 bytes .../mop.rsi/wielded-inhand-left-fill-2.png | Bin 0 -> 186 bytes .../mop.rsi/wielded-inhand-right-fill-1.png | Bin 0 -> 151 bytes .../mop.rsi/wielded-inhand-right-fill-2.png | Bin 0 -> 189 bytes .../Specific/Janitorial/rag.rsi/fill-1.png | Bin 0 -> 133 bytes .../Specific/Janitorial/rag.rsi/fill-2.png | Bin 0 -> 163 bytes .../Specific/Janitorial/rag.rsi/fill-3.png | Bin 0 -> 173 bytes .../Specific/Janitorial/rag.rsi/meta.json | 35 ++++++---- 28 files changed, 156 insertions(+), 40 deletions(-) create mode 100644 Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/inhand-left-fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/inhand-left-fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/inhand-right-fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/inhand-right-fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/wielded-inhand-left-fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/wielded-inhand-left-fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/wielded-inhand-right-fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/wielded-inhand-right-fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/inhand-left-fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/inhand-left-fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/inhand-right-fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/inhand-right-fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/wielded-inhand-left-fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/wielded-inhand-left-fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/wielded-inhand-right-fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/mop.rsi/wielded-inhand-right-fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/rag.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/rag.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/rag.rsi/fill-3.png diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index 868d012a87b9..cb5f875204bd 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -6,7 +6,17 @@ components: - type: Sprite sprite: Objects/Specific/Janitorial/mop.rsi - state: mop + layers: + - state: mop + - map: ["enum.SolutionContainerLayers.Fill"] + state: fill-3 + visible: false + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 3 + fillBaseName: fill- + inHandsFillBaseName: -fill- + inHandsMaxFillLevels: 2 - type: MeleeWeapon damage: types: @@ -49,7 +59,17 @@ components: - type: Sprite sprite: Objects/Specific/Janitorial/advmop.rsi - state: advmop + layers: + - state: advmop + - map: ["enum.SolutionContainerLayers.Fill"] + state: fill-2 + visible: false + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 2 + fillBaseName: fill- + inHandsFillBaseName: -fill- + inHandsMaxFillLevels: 2 - type: MeleeWeapon damage: types: @@ -244,7 +264,15 @@ components: - type: Sprite sprite: Objects/Specific/Janitorial/rag.rsi - state: rag + layers: + - state: rag + - map: ["enum.SolutionContainerLayers.Fill"] + state: fill-3 + visible: false + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 3 + fillBaseName: fill- - type: Spillable solution: absorbed - type: MeleeWeapon diff --git a/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/fill-1.png b/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..d7e1ad3ef2f064a8d1ea982f935565ac368e0949 GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|ECYN(Tn`*L zkgGGtOR=cm>{y-Q#x~>KW%|qK#p*D){y-Q#x~>KW%|qK#p*D)lHr)@ivQ=Y!~jB@u~ z-k-;X7VSD!dF#f4|6C*W|C=ZjiNRRpQ_N uyh=TYBt!I>XFO%kMY4`g2>sb4Y}v}bd!}~A6ie52AbC$$KbLh*2~7Y_j4>Ag literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/inhand-right-fill-2.png b/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/inhand-right-fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..b5136f499b53fabcea456e3dd7281420130e70b1 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|YCT;XLn`LH zy>*bc*?@;N;7kP1g`Gy?%q62>kO*+jb_W{CwcU z((4~%H&!k1y8SoFwnpyGeC0*5SN0v-_2uz9ONdg2I`{l@3#^uJIL-cLN708>?j85o k=Ks5=oMd%#waxMk3^&cKm%V8e@&l>%boFyt=akR{0QfdalmGw# literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/meta.json b/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/meta.json index 31192c23bf2e..b176776adbef 100644 --- a/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/meta.json @@ -1,30 +1,68 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/b136cf653c4926e475f8d39b34cd1b713331865a, wielded versions by Psychpsyo", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/b136cf653c4926e475f8d39b34cd1b713331865a, wielded versions by Psychpsyo. Fill levels by Tayrtahn on GitHub.", "size": { "x": 32, "y": 32 }, "states": [ { - "name": "advmop" + "name": "advmop" }, { - "name": "inhand-left", - "directions": 4 + "name": "fill-1" }, { - "name": "inhand-right", - "directions": 4 + "name": "fill-2" }, - { - "name": "wielded-inhand-left", - "directions": 4 + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-left-fill-1", + "directions": 4 + }, + { + "name": "inhand-left-fill-2", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-right-fill-1", + "directions": 4 + }, + { + "name": "inhand-right-fill-2", + "directions": 4 }, - { - "name": "wielded-inhand-right", - "directions": 4 + { + "name": "wielded-inhand-left", + "directions": 4 + }, + { + "name": "wielded-inhand-left-fill-1", + "directions": 4 + }, + { + "name": "wielded-inhand-left-fill-2", + "directions": 4 + }, + { + "name": "wielded-inhand-right", + "directions": 4 + }, + { + "name": "wielded-inhand-right-fill-1", + "directions": 4 + }, + { + "name": "wielded-inhand-right-fill-2", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/wielded-inhand-left-fill-1.png b/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/wielded-inhand-left-fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..57c1acc26e0a8cf5bc31f91b52fbbbb1cc5793a8 GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`=mz+NxOyoT z)fsL~Gd`2XaHglHCs$|AyjUHEgvktUIkmO5D_5?}0;*(S_zwoN*T_5sa`;Pv{DL7O z5R&0oLubXJYe1f>XFU6Ne5;~}vGU17|GjkP>6cA@V!#Ph z_(0s@bQNm`qt79>W3|E;_zXC>XUsqBU;vZ?g8K~T5(2rk4@hc9J>*(&@9RE;ddCm{ dvlEmVlq7$J9&mMWvd{vl^K|udS?83{1OR07UM&Cs literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/wielded-inhand-left-fill-2.png b/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/wielded-inhand-left-fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..4f0245bb850ed5f5adabb9c7ab427bd9771da2c6 GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`=mz+NxOyoT z)fsL~Gd`2XaHglHCs$|AyjUHEgvktUIkmO5D_5?}0;*(S_zwoN*T_5sa`;Pv{DL7O z5R&0oLubXJYe1<6PZ!6Kh}O5)E^-}E;9$LAr||dx`PkxSnH#!N&79lWId{nkRBgC2 zeYt>wLxTgKL+o9~kfou48Z{hq-m)byFip)&su=_tthjt0Mn`p^$(346Fh$_3-lYzyw6_uhyN1;`=x~klC`w7+d+=@ MboFyt=akR{0L9{JbpQYW literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/wielded-inhand-right-fill-1.png b/Resources/Textures/Objects/Specific/Janitorial/advmop.rsi/wielded-inhand-right-fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..07d042cc75b5da0ffa1eeecddfeeab4db9ab278b GIT binary patch literal 254 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`=mz+NxYikN z^inM9>FG&hIFn|4X64G2wY9bLVs#i2CNsF@I2;1`74H4tA$#cp&(@~24G*pB zDlIrS9NL!4w5f&HfxE#^!TARBlXC^{ODfobazOC6;Y0D)W=@tC`5Y#k44ofy`glX(f`=mz+NxYikN z^inM9>FG&hIFn|4X64G2wY9bLVs#i2CNsF@jex8m@^bEux$ur0V!*6@KmrppZLIX z-OY9Zb{UTjiwDpD))3f*Bw}#~OaxR2>Ei8hW}ohFJ6_ zCrGd^PDo+m3f*Bw}#~OaxR2>Ei+IqS;hFJ8z z?K9*9+HmAX)&Kg9O>6Ev$jCY565R2lCeFoHc)^|XT(>893v_t(2zB<(F%Em-U&E{& b@~n-OS(Y&%euoAl&>#j+S3j3^P63f*Bw}#~OaxR2>EiI(xb}hFJ8z z?NbzDP~bWI>em1LGm@jE?OWR j_rhPtzE>sWSt|EC4kopd?F)K=#xZ!h`njxgN@xNAXQfPj literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/inhand-left-fill-1.png b/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/inhand-left-fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..3007efa01e25ef63550f83ec0f74af6f5ccbc648 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|yggkULn`LH zy|t0|fB_G)!Oyk-`u9D3^EODqx2++DHG=&FCr}j#$gD4$Qu6(;*1BE$o9TAK`K05{an^LB{Ts5HPkhR literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/inhand-right-fill-1.png b/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/inhand-right-fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..17495e71826c0b9ad172f6879e8026e96b408c66 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|d_7$pLn`LH zy=lnTV8Fxd@I3nO{vTmCx>=Z87w(Eyjy(5<1*i%HwuO6Mx_kU-fa$;WpL_5AXNHJ2 luy0jLxp)75-uu;`-_7y3Ai%uoLy#ay&ePS;Wt~$(699$tGM4}V literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/inhand-right-fill-2.png b/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/inhand-right-fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..58c3bd2c42ff3e804745f8b893da469f69a79668 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|qC8z3Ln`LH zy>*bc!GMSPK$`Bi@atT{TRbj^aK2;P@TUBd6i^ii*iW8mYyA7Rtya&rqd5gGGF!L4 ys;!lW$T38({il25>uLdiW;fOM_wI#1kF{jfFw>KEc$QWNQsU|A=d#Wzp$PzY&ofm3 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/meta.json b/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/meta.json index 5ef52189cab3..34768c16f71f 100644 --- a/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/meta.json @@ -1,30 +1,71 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428, wielded versions from Easypoll", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428, wielded versions from Easypoll, fill levels by Tayrtahn on GitHub.", "size": { "x": 32, "y": 32 }, "states": [ { - "name": "mop" + "name": "mop" }, { - "name": "inhand-left", - "directions": 4 + "name": "fill-1" }, { - "name": "inhand-right", - "directions": 4 + "name": "fill-2" }, - { - "name": "wielded-inhand-left", - "directions": 4 + { + "name": "fill-3" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-left-fill-1", + "directions": 4 + }, + { + "name": "inhand-left-fill-2", + "directions": 4 }, - { - "name": "wielded-inhand-right", - "directions": 4 + { + "name": "inhand-right-fill-1", + "directions": 4 + }, + { + "name": "inhand-right-fill-2", + "directions": 4 + }, + { + "name": "wielded-inhand-left", + "directions": 4 + }, + { + "name": "wielded-inhand-right", + "directions": 4 + }, + { + "name": "wielded-inhand-left-fill-1", + "directions": 4 + }, + { + "name": "wielded-inhand-left-fill-2", + "directions": 4 + }, + { + "name": "wielded-inhand-right-fill-1", + "directions": 4 + }, + { + "name": "wielded-inhand-right-fill-2", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/wielded-inhand-left-fill-1.png b/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/wielded-inhand-left-fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..997f3ae0398d3aab2bf9de11d8ba7e1ad495a62e GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|f<0XvLn`LH zy}6P1fPw&vJi?#|6e*n=7ZcW4WH_dPB-X8=^nz;H+7M+n(=5-Rl|Wors-SUfgi%(P{TL^Y!*! dTHejxP{tklFT8&(L;4nwYEM@`mvv4FO#u8pM2!Fd literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/wielded-inhand-right-fill-1.png b/Resources/Textures/Objects/Specific/Janitorial/mop.rsi/wielded-inhand-right-fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..956f9bbbd19035e10cfb628e544b645c0be7dabc GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|0zF+ELn`LH zy=lnT;K0G+@aXjW=h^uSTLXE{Z83Qh6vPTt!|=h~GweHGzQwaU=h`mEJoDbPY?^_k tG)RVl;ehSFl~C0!g?voY|n;^ENcgcI*_D0XTX_GUVfQlLZ1x?zzt1W)V8{?BN zEcDc3pb%AgDrj<`WX1P$$>O06LAh>ho>+&DTSFM@*B^**Yt}<6>F8TD< hy(Qpe5;H^1Q8t~{@P5-ruOEPvdb;|#taD0e0swh>MyCJ( literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/rag.rsi/fill-1.png b/Resources/Textures/Objects/Specific/Janitorial/rag.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..6fb322fbdf212a7f567ac6f49be5a319b6679acb GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}_MR?|ArY-_ zFKiTKP~dU8xM3M{+S!PkH!8Zq{#`FFZ~-b|Shgz5FK+uO)u)#k7yAUAJ9=)yTK2H= d`TH0i|y~EXe=> literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/rag.rsi/fill-2.png b/Resources/Textures/Objects/Specific/Janitorial/rag.rsi/fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..7114d9a2e74ef60e00ed2786870d451fa2eea9b4 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}v7RoDArY-_ z&j)ffC6Yz{Z)v-aZ1$OXp2_{f&a4~jD=R)kPYifr$UNg5--lb?iV_n)iUPGWc)I$z JtaD0e0sv&AJiPz_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/rag.rsi/fill-3.png b/Resources/Textures/Objects/Specific/Janitorial/rag.rsi/fill-3.png new file mode 100644 index 0000000000000000000000000000000000000000..0705b79b5086902440124793aea3a85690e2f1d2 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}>7Fi*ArY-_ zFCOGPpup4m@P?u6VMm$8mv>x#ebhNB!?Rh0>wkIoq3+H!pyD6Ko}rf}glv|)y=eQE zH_Bey9_QRhyXn7h>6@E(Zl~2 Date: Tue, 4 Jun 2024 18:49:31 +0000 Subject: [PATCH 292/568] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index b14fae9c41ad..574ea1adb35b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: DoutorWhite - changes: - - message: Introduce new health status icons. - type: Add - - message: Enhance the Medical HUD to display the health status of players and mobs - using the newly added icons. - type: Tweak - id: 6182 - time: '2024-03-18T13:56:12.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26027 - author: Plykiya changes: - message: Lone operatives now start with 60TC instead of 40TC. @@ -3852,3 +3842,10 @@ id: 6681 time: '2024-06-04T17:12:01.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28225 +- author: Tayrtahn + changes: + - message: Mops and rags now show the liquids they soak up. + type: Add + id: 6682 + time: '2024-06-04T18:48:24.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28590 From ec5a3b0fd2c56749e0addddacc90caf6ff002078 Mon Sep 17 00:00:00 2001 From: TsjipTsjip <19798667+TsjipTsjip@users.noreply.github.com> Date: Tue, 4 Jun 2024 23:47:52 +0200 Subject: [PATCH 293/568] Reword some criminal records text (#28597) * Update criminal-records.ftl * Update criminal-records.ftl * Update criminal-records.ftl --- .../Locale/en-US/criminal-records/criminal-records.ftl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Resources/Locale/en-US/criminal-records/criminal-records.ftl b/Resources/Locale/en-US/criminal-records/criminal-records.ftl index f603b446666c..6d6a97300c23 100644 --- a/Resources/Locale/en-US/criminal-records/criminal-records.ftl +++ b/Resources/Locale/en-US/criminal-records/criminal-records.ftl @@ -31,14 +31,14 @@ criminal-records-permission-denied = Permission denied ## Security channel notifications -criminal-records-console-wanted = {$name} is wanted by {$officer} for: {$reason}. +criminal-records-console-wanted = {$name} was made wanted by {$officer} for: {$reason}. criminal-records-console-suspected = {$officer} marked {$name} as suspicious because of: {$reason} -criminal-records-console-not-suspected = {$name} has been cleared as a suspect by {$officer}. +criminal-records-console-not-suspected = {$name} has been cleared of suspicion by {$officer}. criminal-records-console-detained = {$name} has been detained by {$officer}. criminal-records-console-released = {$name} has been released by {$officer}. -criminal-records-console-not-wanted = {$name} is no longer wanted, according to {$officer}. +criminal-records-console-not-wanted = {$officer} cleared the wanted status of {$name}. criminal-records-console-paroled = {$name} has been released on parole by {$officer}. -criminal-records-console-not-parole = {$name}'s parole status has been lifted by {$officer}. +criminal-records-console-not-parole = {$officer} cleared the parole status of {$name}. criminal-records-console-unknown-officer = ## Filters From 41549db28284d75fbf5c376030af7171aeb6278a Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Wed, 5 Jun 2024 00:04:31 -0700 Subject: [PATCH 294/568] Remove obsolete VisibilitySystem functions (#28610) Remove obsolete visibility functions Co-authored-by: plykiya --- Content.Server/Ghost/GhostSystem.cs | 16 ++++++++-------- .../Pointing/EntitySystems/PointingSystem.cs | 2 +- .../Revenant/EntitySystems/CorporealSystem.cs | 10 +++++----- .../Revenant/EntitySystems/RevenantSystem.cs | 12 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 1a411f13a05f..dce80a450fd5 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -154,8 +154,8 @@ private void OnGhostStartup(EntityUid uid, GhostComponent component, ComponentSt if (_ticker.RunLevel != GameRunLevel.PostRound) { - _visibilitySystem.AddLayer(uid, visibility, (int) VisibilityFlags.Ghost, false); - _visibilitySystem.RemoveLayer(uid, visibility, (int) VisibilityFlags.Normal, false); + _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Ghost, false); + _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Normal, false); _visibilitySystem.RefreshVisibility(uid, visibilityComponent: visibility); } @@ -174,8 +174,8 @@ private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentS // Entity can't be seen by ghosts anymore. if (TryComp(uid, out VisibilityComponent? visibility)) { - _visibilitySystem.RemoveLayer(uid, visibility, (int) VisibilityFlags.Ghost, false); - _visibilitySystem.AddLayer(uid, visibility, (int) VisibilityFlags.Normal, false); + _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Ghost, false); + _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Normal, false); _visibilitySystem.RefreshVisibility(uid, visibilityComponent: visibility); } @@ -382,13 +382,13 @@ public void MakeVisible(bool visible) { if (visible) { - _visibilitySystem.AddLayer(uid, vis, (int) VisibilityFlags.Normal, false); - _visibilitySystem.RemoveLayer(uid, vis, (int) VisibilityFlags.Ghost, false); + _visibilitySystem.AddLayer((uid, vis), (int) VisibilityFlags.Normal, false); + _visibilitySystem.RemoveLayer((uid, vis), (int) VisibilityFlags.Ghost, false); } else { - _visibilitySystem.AddLayer(uid, vis, (int) VisibilityFlags.Ghost, false); - _visibilitySystem.RemoveLayer(uid, vis, (int) VisibilityFlags.Normal, false); + _visibilitySystem.AddLayer((uid, vis), (int) VisibilityFlags.Ghost, false); + _visibilitySystem.RemoveLayer((uid, vis), (int) VisibilityFlags.Normal, false); } _visibilitySystem.RefreshVisibility(uid, visibilityComponent: vis); } diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index 960c8dc28aa5..9dc3b5e1e67e 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -174,7 +174,7 @@ public bool TryPoint(ICommonSession? session, EntityCoordinates coordsPointed, E { var arrowVisibility = EntityManager.EnsureComponent(arrow); layer = playerVisibility.Layer; - _visibilitySystem.SetLayer(arrow, arrowVisibility, layer); + _visibilitySystem.SetLayer((arrow, arrowVisibility), (ushort) layer); } // Get players that are in range and whose visibility layer matches the arrow's. diff --git a/Content.Server/Revenant/EntitySystems/CorporealSystem.cs b/Content.Server/Revenant/EntitySystems/CorporealSystem.cs index 1d43cb3ac851..5f31a2f280a5 100644 --- a/Content.Server/Revenant/EntitySystems/CorporealSystem.cs +++ b/Content.Server/Revenant/EntitySystems/CorporealSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.GameTicking; +using Content.Server.GameTicking; using Content.Shared.Eye; using Content.Shared.Revenant.Components; using Content.Shared.Revenant.EntitySystems; @@ -17,8 +17,8 @@ public override void OnStartup(EntityUid uid, CorporealComponent component, Comp if (TryComp(uid, out var visibility)) { - _visibilitySystem.RemoveLayer(uid, visibility, (int) VisibilityFlags.Ghost, false); - _visibilitySystem.AddLayer(uid, visibility, (int) VisibilityFlags.Normal, false); + _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Ghost, false); + _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Normal, false); _visibilitySystem.RefreshVisibility(uid, visibility); } } @@ -29,8 +29,8 @@ public override void OnShutdown(EntityUid uid, CorporealComponent component, Com if (TryComp(uid, out var visibility) && _ticker.RunLevel != GameRunLevel.PostRound) { - _visibilitySystem.AddLayer(uid, visibility, (int) VisibilityFlags.Ghost, false); - _visibilitySystem.RemoveLayer(uid, visibility, (int) VisibilityFlags.Normal, false); + _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Ghost, false); + _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Normal, false); _visibilitySystem.RefreshVisibility(uid, visibility); } } diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index a05105662d4d..fa4f6db240c2 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -78,8 +78,8 @@ private void OnStartup(EntityUid uid, RevenantComponent component, ComponentStar if (_ticker.RunLevel == GameRunLevel.PostRound && TryComp(uid, out var visibility)) { - _visibility.AddLayer(uid, visibility, (int) VisibilityFlags.Ghost, false); - _visibility.RemoveLayer(uid, visibility, (int) VisibilityFlags.Normal, false); + _visibility.AddLayer((uid, visibility), (int) VisibilityFlags.Ghost, false); + _visibility.RemoveLayer((uid, visibility), (int) VisibilityFlags.Normal, false); _visibility.RefreshVisibility(uid, visibility); } @@ -192,13 +192,13 @@ public void MakeVisible(bool visible) { if (visible) { - _visibility.AddLayer(uid, vis, (int) VisibilityFlags.Normal, false); - _visibility.RemoveLayer(uid, vis, (int) VisibilityFlags.Ghost, false); + _visibility.AddLayer((uid, vis), (int) VisibilityFlags.Normal, false); + _visibility.RemoveLayer((uid, vis), (int) VisibilityFlags.Ghost, false); } else { - _visibility.AddLayer(uid, vis, (int) VisibilityFlags.Ghost, false); - _visibility.RemoveLayer(uid, vis, (int) VisibilityFlags.Normal, false); + _visibility.AddLayer((uid, vis), (int) VisibilityFlags.Ghost, false); + _visibility.RemoveLayer((uid, vis), (int) VisibilityFlags.Normal, false); } _visibility.RefreshVisibility(uid, vis); } From 86cf5cc7d1f5886be4fb41ca224b8cf13cb45606 Mon Sep 17 00:00:00 2001 From: TsjipTsjip <19798667+TsjipTsjip@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:32:58 +0200 Subject: [PATCH 295/568] Prayable datafield typo (#28622) * notifiactionPrefix -> notificationPrefix * notifiactionPrefix -> notificationPrefix --- Content.Shared/Prayer/PrayableComponent.cs | 2 +- .../Entities/Objects/Fun/Instruments/instruments_misc.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Prayer/PrayableComponent.cs b/Content.Shared/Prayer/PrayableComponent.cs index 71251af81008..b6dcc1c21087 100644 --- a/Content.Shared/Prayer/PrayableComponent.cs +++ b/Content.Shared/Prayer/PrayableComponent.cs @@ -26,7 +26,7 @@ public sealed partial class PrayableComponent : Component /// /// Prefix used in the notification to admins /// - [DataField("notifiactionPrefix")] + [DataField("notificationPrefix")] [ViewVariables(VVAccess.ReadWrite)] public string NotificationPrefix = "prayer-chat-notify-pray"; diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_misc.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_misc.yml index edad2b406312..8cb3d88ede27 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_misc.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_misc.yml @@ -59,7 +59,7 @@ size: Small - type: Prayable sentMessage: prayer-popup-notify-centcom-sent - notifiactionPrefix: prayer-chat-notify-centcom + notificationPrefix: prayer-chat-notify-centcom verb: prayer-verbs-call verbImage: null @@ -74,7 +74,7 @@ state: icon - type: Prayable sentMessage: prayer-popup-notify-syndicate-sent - notifiactionPrefix: prayer-chat-notify-syndicate + notificationPrefix: prayer-chat-notify-syndicate - type: entity parent: BaseHandheldInstrument @@ -185,6 +185,6 @@ - ItemMask - type: Prayable sentMessage: prayer-popup-notify-honkmother-sent - notifiactionPrefix: prayer-chat-notify-honkmother + notificationPrefix: prayer-chat-notify-honkmother verb: prayer-verbs-call verbImage: null From 0f3a2424e8e034bb862700c242c3541d71aaabd0 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Wed, 5 Jun 2024 20:21:47 +1200 Subject: [PATCH 296/568] Update engine to v224.1.0 (#28624) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index f64821875655..a628d31c4b82 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit f64821875655dfec4e2c951d5c22c0db873fe86e +Subproject commit a628d31c4b82612eec2c42ba33024acc1c4d9d7e From 7bd1b45f2b32ce5a99c719d2b9899df844c51dd9 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Wed, 5 Jun 2024 20:37:22 +1200 Subject: [PATCH 297/568] Use dummy sessions in NukeOpsTest (#28549) * Add dummy sessions * Update NukeOpsTest * Fix PvsBenchmark --- Content.Benchmarks/PvsBenchmark.cs | 41 ++++++--------- .../Pair/TestPair.Helpers.cs | 13 +++-- .../Pair/TestPair.Recycle.cs | 2 + Content.IntegrationTests/Pair/TestPair.cs | 5 +- .../Tests/GameRules/NukeOpsTest.cs | 52 ++++++++++++++++--- 5 files changed, 76 insertions(+), 37 deletions(-) diff --git a/Content.Benchmarks/PvsBenchmark.cs b/Content.Benchmarks/PvsBenchmark.cs index 0b4dd907621e..fa7f9d454226 100644 --- a/Content.Benchmarks/PvsBenchmark.cs +++ b/Content.Benchmarks/PvsBenchmark.cs @@ -1,19 +1,17 @@ #nullable enable using System; -using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; using Content.IntegrationTests.Pair; +using Content.Server.Mind; using Content.Server.Warps; using Robust.Server.GameObjects; using Robust.Shared; using Robust.Shared.Analyzers; -using Robust.Shared.Enums; using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; using Robust.Shared.Map; -using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Random; @@ -58,15 +56,20 @@ public void Setup() _pair.Server.CfgMan.SetCVar(CVars.NetPvsAsync, false); _sys = _entMan.System(); + SetupAsync().Wait(); + } + + private async Task SetupAsync() + { // Spawn the map _pair.Server.ResolveDependency().SetSeed(42); - _pair.Server.WaitPost(() => + await _pair.Server.WaitPost(() => { var success = _entMan.System().TryLoad(_mapId, Map, out _); if (!success) throw new Exception("Map load failed"); _pair.Server.MapMan.DoMapInitialize(_mapId); - }).Wait(); + }); // Get list of ghost warp positions _spawns = _entMan.AllComponentsList() @@ -76,17 +79,19 @@ public void Setup() Array.Resize(ref _players, PlayerCount); - // Spawn "Players". - _pair.Server.WaitPost(() => + // Spawn "Players" + _players = await _pair.Server.AddDummySessions(PlayerCount); + await _pair.Server.WaitPost(() => { + var mind = _pair.Server.System(); for (var i = 0; i < PlayerCount; i++) { var pos = _spawns[i % _spawns.Length]; var uid =_entMan.SpawnEntity("MobHuman", pos); _pair.Server.ConsoleHost.ExecuteCommand($"setoutfit {_entMan.GetNetEntity(uid)} CaptainGear"); - _players[i] = new DummySession{AttachedEntity = uid}; + mind.ControlMob(_players[i].UserId, uid); } - }).Wait(); + }); // Repeatedly move players around so that they "explore" the map and see lots of entities. // This will populate their PVS data with out-of-view entities. @@ -168,20 +173,4 @@ public void CycleTick() }).Wait(); _pair.Server.PvsTick(_players); } - - private sealed class DummySession : ICommonSession - { - public SessionStatus Status => SessionStatus.InGame; - public EntityUid? AttachedEntity {get; set; } - public NetUserId UserId => default; - public string Name => string.Empty; - public short Ping => default; - public INetChannel Channel { get; set; } = default!; - public LoginType AuthType => default; - public HashSet ViewSubscriptions { get; } = new(); - public DateTime ConnectedTime { get; set; } - public SessionState State => default!; - public SessionData Data => default!; - public bool ClientSide { get; set; } - } } diff --git a/Content.IntegrationTests/Pair/TestPair.Helpers.cs b/Content.IntegrationTests/Pair/TestPair.Helpers.cs index cc83232a0663..1e8306a02c60 100644 --- a/Content.IntegrationTests/Pair/TestPair.Helpers.cs +++ b/Content.IntegrationTests/Pair/TestPair.Helpers.cs @@ -7,6 +7,8 @@ using Content.Shared.Roles; using Robust.Shared.GameObjects; using Robust.Shared.Map; +using Robust.Shared.Network; +using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.UnitTesting; @@ -136,10 +138,15 @@ public List GetPrototypesWithComponent( /// Helper method for enabling or disabling a antag role /// public async Task SetAntagPref(ProtoId id, bool value) + { + await SetAntagPref(Client.User!.Value, id, value); + } + + public async Task SetAntagPref(NetUserId user, ProtoId id, bool value) { var prefMan = Server.ResolveDependency(); - var prefs = prefMan.GetPreferences(Client.User!.Value); + var prefs = prefMan.GetPreferences(user); // what even is the point of ICharacterProfile if we always cast it to HumanoidCharacterProfile to make it usable? var profile = (HumanoidCharacterProfile) prefs.SelectedCharacter; @@ -148,11 +155,11 @@ public async Task SetAntagPref(ProtoId id, bool value) await Server.WaitPost(() => { - prefMan.SetProfile(Client.User.Value, prefs.SelectedCharacterIndex, newProfile).Wait(); + prefMan.SetProfile(user, prefs.SelectedCharacterIndex, newProfile).Wait(); }); // And why the fuck does it always create a new preference and profile object instead of just reusing them? - var newPrefs = prefMan.GetPreferences(Client.User.Value); + var newPrefs = prefMan.GetPreferences(user); var newProf = (HumanoidCharacterProfile) newPrefs.SelectedCharacter; Assert.That(newProf.AntagPreferences.Contains(id), Is.EqualTo(value)); } diff --git a/Content.IntegrationTests/Pair/TestPair.Recycle.cs b/Content.IntegrationTests/Pair/TestPair.Recycle.cs index 8d1e425553b3..4cae4affc4d2 100644 --- a/Content.IntegrationTests/Pair/TestPair.Recycle.cs +++ b/Content.IntegrationTests/Pair/TestPair.Recycle.cs @@ -34,6 +34,8 @@ private async Task OnDirtyDispose() private async Task OnCleanDispose() { + await Server.RemoveAllDummySessions(); + if (TestMap != null) { await Server.WaitPost(() => Server.EntMan.DeleteEntity(TestMap.MapUid)); diff --git a/Content.IntegrationTests/Pair/TestPair.cs b/Content.IntegrationTests/Pair/TestPair.cs index 7ee5dbd55c7a..0b18c3823903 100644 --- a/Content.IntegrationTests/Pair/TestPair.cs +++ b/Content.IntegrationTests/Pair/TestPair.cs @@ -37,7 +37,10 @@ public void Deconstruct( client = Client; } - public ICommonSession? Player => Server.PlayerMan.Sessions.FirstOrDefault(); + public ICommonSession? Player => Client.User == null + ? null + : Server.PlayerMan.SessionsDict.GetValueOrDefault(Client.User.Value); + public ContentPlayerData? PlayerData => Player?.Data.ContentData(); public PoolTestLogHandler ServerLogHandler { get; private set; } = default!; diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs index 2360ea0bf4f2..f56baba3426d 100644 --- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs @@ -57,8 +57,17 @@ public async Task TryStopNukeOpsFromConstantlyFailing() Assert.That(client.AttachedEntity, Is.Null); Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay)); + // Add several dummy players + var dummies = await pair.Server.AddDummySessions(3); + await pair.RunTicksSync(5); + // Opt into the nukies role. await pair.SetAntagPref("NukeopsCommander", true); + await pair.SetAntagPref(dummies[1].UserId, "NukeopsMedic", true); + + // Initially, the players have no attached entities + Assert.That(pair.Player?.AttachedEntity, Is.Null); + Assert.That(dummies.All(x => x.AttachedEntity == null)); // There are no grids or maps Assert.That(entMan.Count(), Is.Zero); @@ -75,17 +84,20 @@ public async Task TryStopNukeOpsFromConstantlyFailing() 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)); + ticker.ToggleReadyAll(true); + Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == 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(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.JoinedGame)); Assert.That(client.EntMan.EntityExists(client.AttachedEntity)); + + var dummyEnts = dummies.Select(x => x.AttachedEntity ?? default).ToArray(); var player = pair.Player!.AttachedEntity!.Value; Assert.That(entMan.EntityExists(player)); + Assert.That(dummyEnts.All(e => entMan.EntityExists(e))); // Maps now exist Assert.That(entMan.Count(), Is.GreaterThan(0)); @@ -96,8 +108,8 @@ public async Task TryStopNukeOpsFromConstantlyFailing() // 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(2)); + Assert.That(entMan.Count(), Is.EqualTo(2)); Assert.That(entMan.Count(), Is.EqualTo(1)); // The player entity should be the nukie commander @@ -107,11 +119,36 @@ public async Task TryStopNukeOpsFromConstantlyFailing() 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 second dummy player should be a medic + var dummyMind = mindSys.GetMind(dummyEnts[1])!.Value; + Assert.That(entMan.HasComponent(dummyEnts[1])); + Assert.That(roleSys.MindIsAntagonist(dummyMind)); + Assert.That(roleSys.MindHasRole(dummyMind)); + Assert.That(factionSys.IsMember(dummyEnts[1], "Syndicate"), Is.True); + Assert.That(factionSys.IsMember(dummyEnts[1], "NanoTrasen"), Is.False); + roles = roleSys.MindGetAllRoles(dummyMind); + cmdRoles = roles.Where(x => x.Prototype == "NukeopsMedic" && x.Component is NukeopsRoleComponent); + Assert.That(cmdRoles.Count(), Is.EqualTo(1)); + + // The other two players should have just spawned in as normal. + CheckDummy(0); + CheckDummy(2); + void CheckDummy(int i) + { + var ent = dummyEnts[i]; + var mind = mindSys.GetMind(ent)!.Value; + Assert.That(entMan.HasComponent(ent), Is.False); + Assert.That(roleSys.MindIsAntagonist(mind), Is.False); + Assert.That(roleSys.MindHasRole(mind), Is.False); + Assert.That(factionSys.IsMember(ent, "Syndicate"), Is.False); + Assert.That(factionSys.IsMember(ent, "NanoTrasen"), Is.True); + Assert.That(roleSys.MindGetAllRoles(mind).Any(x => x.Component is NukeopsRoleComponent), Is.False); + } + // The game rule exists, and all the stations/shuttles/maps are properly initialized var rule = entMan.AllComponents().Single().Component; var gridsRule = entMan.AllComponents().Single().Component; @@ -178,7 +215,7 @@ public async Task TryStopNukeOpsFromConstantlyFailing() // 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; + var total = 0; while (enumerator.NextItem(out _)) { total++; @@ -200,6 +237,7 @@ public async Task TryStopNukeOpsFromConstantlyFailing() ticker.SetGamePreset((GamePresetPrototype?)null); await pair.SetAntagPref("NukeopsCommander", false); + await pair.SetAntagPref(dummies[1].UserId, "NukeopsMedic", false); await pair.CleanReturnAsync(); } } From e1541351a5a26eac8fca0db8ae3e4cc827d65691 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Thu, 6 Jun 2024 01:00:33 +1200 Subject: [PATCH 298/568] Update engine to v224.1.1 (#28632) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index a628d31c4b82..fcd507d1f9e6 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit a628d31c4b82612eec2c42ba33024acc1c4d9d7e +Subproject commit fcd507d1f9e6fffb96bc4228d7f3d78577f0ff2e From adeed705e64ee03f1e7d462c486f98859d6e2dc5 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Thu, 6 Jun 2024 02:19:24 +1200 Subject: [PATCH 299/568] Add Job preference tests (#28625) * Misc Job related changes * Add JobTest * A * Aa * Lets not confuse the yaml linter * fixes * a --- .../GameTicking/Managers/ClientGameTicker.cs | 11 +- Content.Client/LateJoin/LateJoinGui.cs | 8 +- Content.Client/Lobby/LobbyUIController.cs | 2 +- .../Lobby/UI/CharacterPickerButton.xaml.cs | 4 +- .../Pair/TestPair.Helpers.cs | 74 ++++-- .../Pair/TestPair.Recycle.cs | 15 ++ Content.IntegrationTests/Pair/TestPair.cs | 6 +- Content.IntegrationTests/PoolManager.Cvars.cs | 1 + Content.IntegrationTests/Tests/EntityTest.cs | 1 + .../Tests/GameRules/AntagPreferenceTest.cs | 4 +- .../Tests/GameRules/NukeOpsTest.cs | 6 +- .../Tests/PostMapInitTest.cs | 21 +- .../Tests/Round/JobTest.cs | 222 ++++++++++++++++++ .../Tests/Station/StationJobsTest.cs | 13 +- Content.Server/Database/ServerDbBase.cs | 7 +- .../GameTicking/GameTicker.RoundFlow.cs | 2 +- .../Managers/ServerPreferencesManager.cs | 6 +- .../Components/SpawnPointComponent.cs | 9 +- .../EntitySystems/SpawnPointSystem.cs | 2 +- .../Components/StationJobsComponent.cs | 46 ++-- .../Systems/StationJobsSystem.Roundstart.cs | 23 +- .../Station/Systems/StationJobsSystem.cs | 138 ++++++----- .../GameTicking/SharedGameTicker.cs | 15 +- .../Preferences/HumanoidCharacterProfile.cs | 104 ++++++-- Content.Shared/Roles/JobPrototype.cs | 4 +- Content.Shared/Roles/Jobs/SharedJobSystem.cs | 12 + Resources/Prototypes/Maps/arenas.yml | 2 - Resources/Prototypes/Maps/atlas.yml | 2 - Resources/Prototypes/Maps/bagel.yml | 2 - Resources/Prototypes/Maps/box.yml | 2 - Resources/Prototypes/Maps/cluster.yml | 2 - Resources/Prototypes/Maps/core.yml | 2 - Resources/Prototypes/Maps/debug.yml | 6 - Resources/Prototypes/Maps/europa.yml | 2 - Resources/Prototypes/Maps/fland.yml | 2 - Resources/Prototypes/Maps/marathon.yml | 2 - Resources/Prototypes/Maps/meta.yml | 2 - Resources/Prototypes/Maps/oasis.yml | 4 +- Resources/Prototypes/Maps/omega.yml | 2 - Resources/Prototypes/Maps/origin.yml | 2 - Resources/Prototypes/Maps/packed.yml | 2 - Resources/Prototypes/Maps/reach.yml | 2 - Resources/Prototypes/Maps/saltern.yml | 2 - Resources/Prototypes/Maps/train.yml | 2 - 44 files changed, 537 insertions(+), 261 deletions(-) create mode 100644 Content.IntegrationTests/Tests/Round/JobTest.cs diff --git a/Content.Client/GameTicking/Managers/ClientGameTicker.cs b/Content.Client/GameTicking/Managers/ClientGameTicker.cs index 309db2eb4e6a..fcf5ae91a497 100644 --- a/Content.Client/GameTicking/Managers/ClientGameTicker.cs +++ b/Content.Client/GameTicking/Managers/ClientGameTicker.cs @@ -4,10 +4,12 @@ using Content.Client.RoundEnd; using Content.Shared.GameTicking; using Content.Shared.GameWindow; +using Content.Shared.Roles; using JetBrains.Annotations; using Robust.Client.Graphics; using Robust.Client.State; using Robust.Client.UserInterface; +using Robust.Shared.Prototypes; namespace Content.Client.GameTicking.Managers { @@ -17,10 +19,9 @@ public sealed class ClientGameTicker : SharedGameTicker [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IClientAdminManager _admin = default!; [Dependency] private readonly IClyde _clyde = default!; - [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; - private Dictionary> _jobsAvailable = new(); + private Dictionary, int?>> _jobsAvailable = new(); private Dictionary _stationNames = new(); [ViewVariables] public bool AreWeReady { get; private set; } @@ -32,13 +33,13 @@ public sealed class ClientGameTicker : SharedGameTicker [ViewVariables] public TimeSpan StartTime { get; private set; } [ViewVariables] public new bool Paused { get; private set; } - [ViewVariables] public IReadOnlyDictionary> JobsAvailable => _jobsAvailable; + [ViewVariables] public IReadOnlyDictionary, int?>> JobsAvailable => _jobsAvailable; [ViewVariables] public IReadOnlyDictionary StationNames => _stationNames; public event Action? InfoBlobUpdated; public event Action? LobbyStatusUpdated; public event Action? LobbyLateJoinStatusUpdated; - public event Action>>? LobbyJobsAvailableUpdated; + public event Action, int?>>>? LobbyJobsAvailableUpdated; public override void Initialize() { @@ -69,7 +70,7 @@ private void OnAdminUpdated() // reading the console. E.g., logs like this one could leak the nuke station/grid: // > Grid NT-Arrivals 1101 (122/n25896) changed parent. Old parent: map 10 (121/n25895). New parent: FTL (123/n26470) #if !DEBUG - _map.Log.Level = _admin.IsAdmin() ? LogLevel.Info : LogLevel.Warning; + EntityManager.System().Log.Level = _admin.IsAdmin() ? LogLevel.Info : LogLevel.Warning; #endif } diff --git a/Content.Client/LateJoin/LateJoinGui.cs b/Content.Client/LateJoin/LateJoinGui.cs index 252aa9aafad4..62a06629f294 100644 --- a/Content.Client/LateJoin/LateJoinGui.cs +++ b/Content.Client/LateJoin/LateJoinGui.cs @@ -290,7 +290,7 @@ private void RebuildUI() } } - private void JobsAvailableUpdated(IReadOnlyDictionary> updatedJobs) + private void JobsAvailableUpdated(IReadOnlyDictionary, int?>> updatedJobs) { foreach (var stationEntries in updatedJobs) { @@ -337,10 +337,10 @@ sealed class JobButton : ContainerButton public Label JobLabel { get; } public string JobId { get; } public string JobLocalisedName { get; } - public uint? Amount { get; private set; } + public int? Amount { get; private set; } private bool _initialised = false; - public JobButton(Label jobLabel, string jobId, string jobLocalisedName, uint? amount) + public JobButton(Label jobLabel, ProtoId jobId, string jobLocalisedName, int? amount) { JobLabel = jobLabel; JobId = jobId; @@ -350,7 +350,7 @@ public JobButton(Label jobLabel, string jobId, string jobLocalisedName, uint? am _initialised = true; } - public void RefreshLabel(uint? amount) + public void RefreshLabel(int? amount) { if (Amount == amount && _initialised) { diff --git a/Content.Client/Lobby/LobbyUIController.cs b/Content.Client/Lobby/LobbyUIController.cs index f6a3eed962c1..05b98606ab99 100644 --- a/Content.Client/Lobby/LobbyUIController.cs +++ b/Content.Client/Lobby/LobbyUIController.cs @@ -302,7 +302,7 @@ public JobPrototype GetPreferredJob(HumanoidCharacterProfile profile) { var highPriorityJob = profile.JobPriorities.FirstOrDefault(p => p.Value == JobPriority.High).Key; // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?) - return _prototypeManager.Index(highPriorityJob ?? SharedGameTicker.FallbackOverflowJob); + return _prototypeManager.Index(highPriorityJob.Id ?? SharedGameTicker.FallbackOverflowJob); } public void GiveDummyLoadout(EntityUid uid, RoleLoadout? roleLoadout) diff --git a/Content.Client/Lobby/UI/CharacterPickerButton.xaml.cs b/Content.Client/Lobby/UI/CharacterPickerButton.xaml.cs index 2ad8de7445f4..7efd1c594fb5 100644 --- a/Content.Client/Lobby/UI/CharacterPickerButton.xaml.cs +++ b/Content.Client/Lobby/UI/CharacterPickerButton.xaml.cs @@ -53,9 +53,9 @@ public CharacterPickerButton( .LoadProfileEntity(humanoid, null, true); var highPriorityJob = humanoid.JobPriorities.SingleOrDefault(p => p.Value == JobPriority.High).Key; - if (highPriorityJob != null) + if (highPriorityJob != default) { - var jobName = prototypeManager.Index(highPriorityJob).LocalizedName; + var jobName = prototypeManager.Index(highPriorityJob).LocalizedName; description = $"{description}\n{jobName}"; } } diff --git a/Content.IntegrationTests/Pair/TestPair.Helpers.cs b/Content.IntegrationTests/Pair/TestPair.Helpers.cs index 1e8306a02c60..588cf0d80e04 100644 --- a/Content.IntegrationTests/Pair/TestPair.Helpers.cs +++ b/Content.IntegrationTests/Pair/TestPair.Helpers.cs @@ -8,7 +8,6 @@ using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Network; -using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.UnitTesting; @@ -135,32 +134,73 @@ public List GetPrototypesWithComponent( } /// - /// Helper method for enabling or disabling a antag role + /// Set a user's antag preferences. Modified preferences are automatically reset at the end of the test. /// - public async Task SetAntagPref(ProtoId id, bool value) + public async Task SetAntagPreference(ProtoId id, bool value, NetUserId? user = null) { - await SetAntagPref(Client.User!.Value, id, value); + user ??= Client.User!.Value; + if (user is not {} userId) + return; + + var prefMan = Server.ResolveDependency(); + var prefs = prefMan.GetPreferences(userId); + + // Automatic preference resetting only resets slot 0. + Assert.That(prefs.SelectedCharacterIndex, Is.EqualTo(0)); + + var profile = (HumanoidCharacterProfile) prefs.Characters[0]; + var newProfile = profile.WithAntagPreference(id, value); + _modifiedProfiles.Add(userId); + await Server.WaitPost(() => prefMan.SetProfile(userId, 0, newProfile).Wait()); } - public async Task SetAntagPref(NetUserId user, ProtoId id, bool value) + /// + /// Set a user's job preferences. Modified preferences are automatically reset at the end of the test. + /// + public async Task SetJobPriority(ProtoId id, JobPriority value, NetUserId? user = null) { - var prefMan = Server.ResolveDependency(); + user ??= Client.User!.Value; + if (user is { } userId) + await SetJobPriorities(userId, (id, value)); + } + + /// + public async Task SetJobPriorities(params (ProtoId, JobPriority)[] priorities) + => await SetJobPriorities(Client.User!.Value, priorities); + /// + public async Task SetJobPriorities(NetUserId user, params (ProtoId, JobPriority)[] priorities) + { + var highCount = priorities.Count(x => x.Item2 == JobPriority.High); + Assert.That(highCount, Is.LessThanOrEqualTo(1), "Cannot have more than one high priority job"); + + var prefMan = Server.ResolveDependency(); var prefs = prefMan.GetPreferences(user); - // what even is the point of ICharacterProfile if we always cast it to HumanoidCharacterProfile to make it usable? - var profile = (HumanoidCharacterProfile) prefs.SelectedCharacter; + var profile = (HumanoidCharacterProfile) prefs.Characters[0]; + var dictionary = new Dictionary, JobPriority>(profile.JobPriorities); - Assert.That(profile.AntagPreferences.Contains(id), Is.EqualTo(!value)); - var newProfile = profile.WithAntagPreference(id, value); + // Automatic preference resetting only resets slot 0. + Assert.That(prefs.SelectedCharacterIndex, Is.EqualTo(0)); - await Server.WaitPost(() => + if (highCount != 0) { - prefMan.SetProfile(user, prefs.SelectedCharacterIndex, newProfile).Wait(); - }); + foreach (var (key, priority) in dictionary) + { + if (priority == JobPriority.High) + dictionary[key] = JobPriority.Medium; + } + } + + foreach (var (job, priority) in priorities) + { + if (priority == JobPriority.Never) + dictionary.Remove(job); + else + dictionary[job] = priority; + } - // And why the fuck does it always create a new preference and profile object instead of just reusing them? - var newPrefs = prefMan.GetPreferences(user); - var newProf = (HumanoidCharacterProfile) newPrefs.SelectedCharacter; - Assert.That(newProf.AntagPreferences.Contains(id), Is.EqualTo(value)); + var newProfile = profile.WithJobPriorities(dictionary); + _modifiedProfiles.Add(user); + await Server.WaitPost(() => prefMan.SetProfile(user, 0, newProfile).Wait()); } } diff --git a/Content.IntegrationTests/Pair/TestPair.Recycle.cs b/Content.IntegrationTests/Pair/TestPair.Recycle.cs index 4cae4affc4d2..89a9eb64638c 100644 --- a/Content.IntegrationTests/Pair/TestPair.Recycle.cs +++ b/Content.IntegrationTests/Pair/TestPair.Recycle.cs @@ -2,10 +2,12 @@ using System.IO; using System.Linq; using Content.Server.GameTicking; +using Content.Server.Preferences.Managers; using Content.Shared.CCVar; using Content.Shared.GameTicking; using Content.Shared.Mind; using Content.Shared.Mind.Components; +using Content.Shared.Preferences; using Robust.Client; using Robust.Server.Player; using Robust.Shared.Exceptions; @@ -34,6 +36,9 @@ private async Task OnDirtyDispose() private async Task OnCleanDispose() { + await Server.WaitIdleAsync(); + await Client.WaitIdleAsync(); + await ResetModifiedPreferences(); await Server.RemoveAllDummySessions(); if (TestMap != null) @@ -81,6 +86,16 @@ private async Task OnCleanDispose() await _testOut.WriteLineAsync($"{nameof(CleanReturnAsync)}: PoolManager took {returnTime.TotalMilliseconds} ms to put pair {Id} back into the pool"); } + private async Task ResetModifiedPreferences() + { + var prefMan = Server.ResolveDependency(); + foreach (var user in _modifiedProfiles) + { + await Server.WaitPost(() => prefMan.SetProfile(user, 0, new HumanoidCharacterProfile()).Wait()); + } + _modifiedProfiles.Clear(); + } + public async ValueTask CleanReturnAsync() { if (State != PairState.InUse) diff --git a/Content.IntegrationTests/Pair/TestPair.cs b/Content.IntegrationTests/Pair/TestPair.cs index 0b18c3823903..0b681dcde10d 100644 --- a/Content.IntegrationTests/Pair/TestPair.cs +++ b/Content.IntegrationTests/Pair/TestPair.cs @@ -26,6 +26,8 @@ public sealed partial class TestPair public readonly List TestHistory = new(); public PoolSettings Settings = default!; public TestMapData? TestMap; + private List _modifiedProfiles = new(); + public RobustIntegrationTest.ServerIntegrationInstance Server { get; private set; } = default!; public RobustIntegrationTest.ClientIntegrationInstance Client { get; private set; } = default!; @@ -37,9 +39,7 @@ public void Deconstruct( client = Client; } - public ICommonSession? Player => Client.User == null - ? null - : Server.PlayerMan.SessionsDict.GetValueOrDefault(Client.User.Value); + public ICommonSession? Player => Server.PlayerMan.SessionsDict.GetValueOrDefault(Client.User!.Value); public ContentPlayerData? PlayerData => Player?.Data.ContentData(); diff --git a/Content.IntegrationTests/PoolManager.Cvars.cs b/Content.IntegrationTests/PoolManager.Cvars.cs index 5acd9d502c1b..bcd48f823805 100644 --- a/Content.IntegrationTests/PoolManager.Cvars.cs +++ b/Content.IntegrationTests/PoolManager.Cvars.cs @@ -28,6 +28,7 @@ private static readonly (string cvar, string value)[] TestCvars = (CCVars.EmergencyShuttleEnabled.Name, "false"), (CCVars.ProcgenPreload.Name, "false"), (CCVars.WorldgenEnabled.Name, "false"), + (CCVars.GatewayGeneratorEnabled.Name, "false"), (CVars.ReplayClientRecordingEnabled.Name, "false"), (CVars.ReplayServerRecordingEnabled.Name, "false"), (CCVars.GameDummyTicker.Name, "true"), diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 926374cf0500..1fc739fb0c70 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -340,6 +340,7 @@ public async Task AllComponentsOneToOneDeleteTest() "MapGrid", "Broadphase", "StationData", // errors when removed mid-round + "StationJobs", "Actor", // We aren't testing actor components, those need their player session set. "BlobFloorPlanBuilder", // Implodes if unconfigured. "DebrisFeaturePlacerController", // Above. diff --git a/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs index 662ea3b97470..1bea33a82bcd 100644 --- a/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs @@ -52,7 +52,7 @@ public async Task TestLobbyPlayersValid() Assert.That(pool.Count, Is.EqualTo(0)); // Opt into the traitor role. - await pair.SetAntagPref("Traitor", true); + await pair.SetAntagPreference("Traitor", true); Assert.That(sys.IsSessionValid(rule, pair.Player, def), Is.True); Assert.That(sys.IsEntityValid(client.AttachedEntity, def), Is.True); @@ -63,7 +63,7 @@ public async Task TestLobbyPlayersValid() Assert.That(sessions.Count, Is.EqualTo(1)); // opt back out - await pair.SetAntagPref("Traitor", false); + await pair.SetAntagPreference("Traitor", false); Assert.That(sys.IsSessionValid(rule, pair.Player, def), Is.True); Assert.That(sys.IsEntityValid(client.AttachedEntity, def), Is.True); diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs index f56baba3426d..fc50d0bd334f 100644 --- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs @@ -62,8 +62,8 @@ public async Task TryStopNukeOpsFromConstantlyFailing() await pair.RunTicksSync(5); // Opt into the nukies role. - await pair.SetAntagPref("NukeopsCommander", true); - await pair.SetAntagPref(dummies[1].UserId, "NukeopsMedic", true); + await pair.SetAntagPreference("NukeopsCommander", true); + await pair.SetAntagPreference( "NukeopsMedic", true, dummies[1].UserId); // Initially, the players have no attached entities Assert.That(pair.Player?.AttachedEntity, Is.Null); @@ -236,8 +236,6 @@ void CheckDummy(int i) } ticker.SetGamePreset((GamePresetPrototype?)null); - await pair.SetAntagPref("NukeopsCommander", false); - await pair.SetAntagPref(dummies[1].UserId, "NukeopsMedic", false); await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/PostMapInitTest.cs b/Content.IntegrationTests/Tests/PostMapInitTest.cs index 17c5e199a751..2ae438984181 100644 --- a/Content.IntegrationTests/Tests/PostMapInitTest.cs +++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs @@ -245,22 +245,15 @@ await server.WaitPost(() => // Test all availableJobs have spawnPoints // This is done inside gamemap test because loading the map takes ages and we already have it. - var jobList = entManager.GetComponent(station).RoundStartJobList - .Where(x => x.Value != 0) - .Select(x => x.Key); + var comp = entManager.GetComponent(station); + var jobs = new HashSet>(comp.SetupAvailableJobs.Keys); + var spawnPoints = entManager.EntityQuery() - .Where(spawnpoint => spawnpoint.SpawnType == SpawnPointType.Job) - .Select(spawnpoint => spawnpoint.Job.ID) - .Distinct(); - List missingSpawnPoints = new(); - foreach (var spawnpoint in jobList.Except(spawnPoints)) - { - if (protoManager.Index(spawnpoint).SetPreference) - missingSpawnPoints.Add(spawnpoint); - } + .Where(x => x.SpawnType == SpawnPointType.Job) + .Select(x => x.Job!.Value); - Assert.That(missingSpawnPoints, Has.Count.EqualTo(0), - $"There is no spawnpoint for {string.Join(", ", missingSpawnPoints)} on {mapProto}."); + jobs.ExceptWith(spawnPoints); + Assert.That(jobs, Is.Empty,$"There is no spawnpoints for {string.Join(", ", jobs)} on {mapProto}."); } try diff --git a/Content.IntegrationTests/Tests/Round/JobTest.cs b/Content.IntegrationTests/Tests/Round/JobTest.cs new file mode 100644 index 000000000000..716e3cf4c218 --- /dev/null +++ b/Content.IntegrationTests/Tests/Round/JobTest.cs @@ -0,0 +1,222 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using Content.IntegrationTests.Pair; +using Content.Server.GameTicking; +using Content.Server.Mind; +using Content.Server.Roles; +using Content.Shared.CCVar; +using Content.Shared.GameTicking; +using Content.Shared.Preferences; +using Content.Shared.Roles; +using Content.Shared.Roles.Jobs; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.Round; + +[TestFixture] +public sealed class JobTest +{ + private static ProtoId _passenger = "Passenger"; + private static ProtoId _engineer = "StationEngineer"; + private static ProtoId _captain = "Captain"; + + private static string _map = "JobTestMap"; + + [TestPrototypes] + public static string JobTestMap = @$" +- type: gameMap + id: {_map} + mapName: {_map} + mapPath: /Maps/Test/empty.yml + minPlayers: 0 + stations: + Empty: + stationProto: StandardNanotrasenStation + components: + - type: StationNameSetup + mapNameTemplate: ""Empty"" + - type: StationJobs + availableJobs: + {_passenger}: [ -1, -1 ] + {_engineer}: [ -1, -1 ] + {_captain}: [ 1, 1 ] +"; + + public void AssertJob(TestPair pair, ProtoId job, NetUserId? user = null, bool isAntag = false) + { + var jobSys = pair.Server.System(); + var mindSys = pair.Server.System(); + var roleSys = pair.Server.System(); + var ticker = pair.Server.System(); + + user ??= pair.Client.User!.Value; + + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound)); + Assert.That(ticker.PlayerGameStatuses[user.Value], Is.EqualTo(PlayerGameStatus.JoinedGame)); + + var uid = pair.Server.PlayerMan.SessionsDict.GetValueOrDefault(user.Value)?.AttachedEntity; + Assert.That(pair.Server.EntMan.EntityExists(uid)); + var mind = mindSys.GetMind(uid!.Value); + Assert.That(pair.Server.EntMan.EntityExists(mind)); + Assert.That(jobSys.MindTryGetJobId(mind, out var actualJob)); + Assert.That(actualJob, Is.EqualTo(job)); + Assert.That(roleSys.MindIsAntagonist(mind), Is.EqualTo(isAntag)); + } + + /// + /// Simple test that checks that starting the round spawns the player into the test map as a passenger. + /// + [Test] + public async Task StartRoundTest() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings + { + DummyTicker = false, + Connected = true, + InLobby = true + }); + + pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); + var ticker = pair.Server.System(); + + // Initially in the lobby + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); + Assert.That(pair.Client.AttachedEntity, Is.Null); + Assert.That(ticker.PlayerGameStatuses[pair.Client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay)); + + // Ready up and start the round + ticker.ToggleReadyAll(true); + Assert.That(ticker.PlayerGameStatuses[pair.Client.User!.Value], Is.EqualTo(PlayerGameStatus.ReadyToPlay)); + await pair.Server.WaitPost(() => ticker.StartRound()); + await pair.RunTicksSync(10); + + AssertJob(pair, _passenger); + + await pair.Server.WaitPost(() => ticker.RestartRound()); + await pair.CleanReturnAsync(); + } + + /// + /// Check that job preferences are respected. + /// + [Test] + public async Task JobPreferenceTest() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings + { + DummyTicker = false, + Connected = true, + InLobby = true + }); + + pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); + var ticker = pair.Server.System(); + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); + Assert.That(pair.Client.AttachedEntity, Is.Null); + + await pair.SetJobPriorities((_passenger, JobPriority.Medium), (_engineer, JobPriority.High)); + ticker.ToggleReadyAll(true); + await pair.Server.WaitPost(() => ticker.StartRound()); + await pair.RunTicksSync(10); + + AssertJob(pair, _engineer); + + await pair.Server.WaitPost(() => ticker.RestartRound()); + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); + await pair.SetJobPriorities((_passenger, JobPriority.High), (_engineer, JobPriority.Medium)); + ticker.ToggleReadyAll(true); + await pair.Server.WaitPost(() => ticker.StartRound()); + await pair.RunTicksSync(10); + + AssertJob(pair, _passenger); + + await pair.Server.WaitPost(() => ticker.RestartRound()); + await pair.CleanReturnAsync(); + } + + /// + /// Check high priority jobs (e.g., captain) are selected before other roles, even if it means a player does not + /// get their preferred job. + /// + [Test] + public async Task JobWeightTest() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings + { + DummyTicker = false, + Connected = true, + InLobby = true + }); + + pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); + var ticker = pair.Server.System(); + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); + Assert.That(pair.Client.AttachedEntity, Is.Null); + + var captain = pair.Server.ProtoMan.Index(_captain); + var engineer = pair.Server.ProtoMan.Index(_engineer); + var passenger = pair.Server.ProtoMan.Index(_passenger); + Assert.That(captain.Weight, Is.GreaterThan(engineer.Weight)); + Assert.That(engineer.Weight, Is.EqualTo(passenger.Weight)); + + await pair.SetJobPriorities((_passenger, JobPriority.Medium), (_engineer, JobPriority.High), (_captain, JobPriority.Low)); + ticker.ToggleReadyAll(true); + await pair.Server.WaitPost(() => ticker.StartRound()); + await pair.RunTicksSync(10); + + AssertJob(pair, _captain); + + await pair.Server.WaitPost(() => ticker.RestartRound()); + await pair.CleanReturnAsync(); + } + + /// + /// Check that jobs are preferentially given to players that have marked those jobs as higher priority. + /// + [Test] + public async Task JobPriorityTest() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings + { + DummyTicker = false, + Connected = true, + InLobby = true + }); + + pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); + var ticker = pair.Server.System(); + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); + Assert.That(pair.Client.AttachedEntity, Is.Null); + + await pair.Server.AddDummySessions(5); + await pair.RunTicksSync(5); + + var engineers = pair.Server.PlayerMan.Sessions.Select(x => x.UserId).ToList(); + var captain = engineers[3]; + engineers.RemoveAt(3); + + await pair.SetJobPriorities(captain, (_captain, JobPriority.High), (_engineer, JobPriority.Medium)); + foreach (var engi in engineers) + { + await pair.SetJobPriorities(engi, (_captain, JobPriority.Medium), (_engineer, JobPriority.High)); + } + + ticker.ToggleReadyAll(true); + await pair.Server.WaitPost(() => ticker.StartRound()); + await pair.RunTicksSync(10); + + AssertJob(pair, _captain, captain); + Assert.Multiple(() => + { + foreach (var engi in engineers) + { + AssertJob(pair, _engineer, engi); + } + }); + + await pair.Server.WaitPost(() => ticker.RestartRound()); + await pair.CleanReturnAsync(); + } +} diff --git a/Content.IntegrationTests/Tests/Station/StationJobsTest.cs b/Content.IntegrationTests/Tests/Station/StationJobsTest.cs index 0085472c33cc..d68fdafb769e 100644 --- a/Content.IntegrationTests/Tests/Station/StationJobsTest.cs +++ b/Content.IntegrationTests/Tests/Station/StationJobsTest.cs @@ -7,7 +7,6 @@ using Content.Shared.Roles; using Robust.Shared.GameObjects; using Robust.Shared.Log; -using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -46,8 +45,6 @@ public sealed class StationJobsTest stationProto: StandardNanotrasenStation components: - type: StationJobs - overflowJobs: - - Passenger availableJobs: TMime: [0, -1] TAssistant: [-1, -1] @@ -164,7 +161,6 @@ public async Task AdjustJobsTest() var server = pair.Server; var prototypeManager = server.ResolveDependency(); - var mapManager = server.ResolveDependency(); var fooStationProto = prototypeManager.Index("FooStation"); var entSysMan = server.ResolveDependency().EntitySysManager; var stationJobs = entSysMan.GetEntitySystem(); @@ -215,6 +211,8 @@ public async Task InvalidRoundstartJobsTest() var server = pair.Server; var prototypeManager = server.ResolveDependency(); + var compFact = server.ResolveDependency(); + var name = compFact.GetComponentName(); await server.WaitAssertion(() => { @@ -233,11 +231,14 @@ await server.WaitAssertion(() => { foreach (var (stationId, station) in gameMap.Stations) { - if (!station.StationComponentOverrides.TryGetComponent("StationJobs", out var comp)) + if (!station.StationComponentOverrides.TryGetComponent(name, out var comp)) continue; - foreach (var (job, _) in ((StationJobsComponent) comp).SetupAvailableJobs) + foreach (var (job, array) in ((StationJobsComponent) comp).SetupAvailableJobs) { + Assert.That(array.Length, Is.EqualTo(2)); + Assert.That(array[0] is -1 or >= 0); + Assert.That(array[1] is -1 or >= 0); Assert.That(invalidJobs, Does.Not.Contain(job), $"Station {stationId} contains job prototype {job} which cannot be present roundstart."); } } diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index be6c7196d5f9..cd03af7087b9 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -15,6 +15,7 @@ using Content.Shared.Preferences; using Content.Shared.Preferences.Loadouts; using Content.Shared.Roles; +using Content.Shared.Traits; using Microsoft.EntityFrameworkCore; using Robust.Shared.Enums; using Robust.Shared.Network; @@ -183,9 +184,9 @@ private static async Task SetSelectedCharacterSlotAsync(NetUserId userId, int ne private static HumanoidCharacterProfile ConvertProfiles(Profile profile) { - var jobs = profile.Jobs.ToDictionary(j => j.JobName, j => (JobPriority) j.Priority); - var antags = profile.Antags.Select(a => a.AntagName); - var traits = profile.Traits.Select(t => t.TraitName); + var jobs = profile.Jobs.ToDictionary(j => new ProtoId(j.JobName), j => (JobPriority) j.Priority); + var antags = profile.Antags.Select(a => new ProtoId(a.AntagName)); + var traits = profile.Traits.Select(t => new ProtoId(t.TraitName)); var sex = Sex.Male; if (Enum.TryParse(profile.Sex, true, out var sexVal)) diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index 6eb42b65c03a..98fcc6441045 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -239,7 +239,7 @@ public void StartRound(bool force = false) HumanoidCharacterProfile profile; if (_prefsManager.TryGetCachedPreferences(userId, out var preferences)) { - profile = (HumanoidCharacterProfile) preferences.GetProfile(preferences.SelectedCharacterIndex); + profile = (HumanoidCharacterProfile) preferences.SelectedCharacter; } else { diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index f7c15a234058..8de458b6ee64 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -305,11 +305,7 @@ public IEnumerable> GetSelectedProfil return usernames .Select(p => (_cachedPlayerPrefs[p].Prefs, p)) .Where(p => p.Prefs != null) - .Select(p => - { - var idx = p.Prefs!.SelectedCharacterIndex; - return new KeyValuePair(p.p, p.Prefs!.GetProfile(idx)); - }); + .Select(p => new KeyValuePair(p.p, p.Prefs!.SelectedCharacter)); } internal static bool ShouldStorePrefs(LoginType loginType) diff --git a/Content.Server/Spawners/Components/SpawnPointComponent.cs b/Content.Server/Spawners/Components/SpawnPointComponent.cs index 5cf231f224e8..c6d14dfeb31c 100644 --- a/Content.Server/Spawners/Components/SpawnPointComponent.cs +++ b/Content.Server/Spawners/Components/SpawnPointComponent.cs @@ -6,11 +6,8 @@ namespace Content.Server.Spawners.Components; [RegisterComponent] public sealed partial class SpawnPointComponent : Component, ISpawnPoint { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - [ViewVariables(VVAccess.ReadWrite)] [DataField("job_id")] - private string? _jobId; + public ProtoId? Job; /// /// The type of spawn point @@ -18,11 +15,9 @@ public sealed partial class SpawnPointComponent : Component, ISpawnPoint [DataField("spawn_type"), ViewVariables(VVAccess.ReadWrite)] public SpawnPointType SpawnType { get; set; } = SpawnPointType.Unset; - public JobPrototype? Job => string.IsNullOrEmpty(_jobId) ? null : _prototypeManager.Index(_jobId); - public override string ToString() { - return $"{_jobId} {SpawnType}"; + return $"{Job} {SpawnType}"; } } diff --git a/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs b/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs index 5be4ff10f4f4..be555dd54aca 100644 --- a/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs +++ b/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs @@ -39,7 +39,7 @@ private void OnPlayerSpawning(PlayerSpawningEvent args) if (_gameTicker.RunLevel != GameRunLevel.InRound && spawnPoint.SpawnType == SpawnPointType.Job && - (args.Job == null || spawnPoint.Job?.ID == args.Job.Prototype)) + (args.Job == null || spawnPoint.Job == args.Job.Prototype)) { possiblePositions.Add(xform.Coordinates); } diff --git a/Content.Server/Station/Components/StationJobsComponent.cs b/Content.Server/Station/Components/StationJobsComponent.cs index 74399bf412db..3681ec9674f5 100644 --- a/Content.Server/Station/Components/StationJobsComponent.cs +++ b/Content.Server/Station/Components/StationJobsComponent.cs @@ -1,4 +1,5 @@ -using Content.Server.Station.Systems; +using System.Linq; +using Content.Server.Station.Systems; using Content.Shared.Roles; using JetBrains.Annotations; using Robust.Shared.Network; @@ -14,25 +15,21 @@ namespace Content.Server.Station.Components; [RegisterComponent, Access(typeof(StationJobsSystem)), PublicAPI] public sealed partial class StationJobsComponent : Component { - /// - /// Total *round-start* jobs at station start. - /// - [DataField("roundStartTotalJobs")] public int RoundStartTotalJobs; - /// /// Total *mid-round* jobs at station start. + /// This is inferred automatically from . /// - [DataField("midRoundTotalJobs")] public int MidRoundTotalJobs; + [ViewVariables] public int MidRoundTotalJobs; /// /// Current total jobs. /// - [DataField("totalJobs")] public int TotalJobs; + [DataField] public int TotalJobs; /// /// Station is running on extended access. /// - [DataField("extendedAccess")] public bool ExtendedAccess; + [DataField] public bool ExtendedAccess; /// /// If there are less than or equal this amount of players in the game at round start, @@ -41,7 +38,7 @@ public sealed partial class StationJobsComponent : Component /// /// Set to -1 to disable extended access. /// - [DataField("extendedAccessThreshold")] + [DataField] public int ExtendedAccessThreshold { get; set; } = 15; /// @@ -54,28 +51,20 @@ public sealed partial class StationJobsComponent : Component public float? PercentJobsRemaining => MidRoundTotalJobs > 0 ? TotalJobs / (float) MidRoundTotalJobs : null; /// - /// The current list of jobs. + /// The current list of jobs of available jobs. Null implies that is no limit. /// /// /// This should not be mutated or used directly unless you really know what you're doing, go through StationJobsSystem. /// - [DataField("jobList", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public Dictionary JobList = new(); - - /// - /// The round-start list of jobs. - /// - /// - /// This should not be mutated, ever. - /// - [DataField("roundStartJobList", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public Dictionary RoundStartJobList = new(); + [DataField] + public Dictionary, int?> JobList = new(); /// /// Overflow jobs that round-start can spawn infinitely many of. + /// This is inferred automatically from . /// - [DataField("overflowJobs", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet OverflowJobs = new(); + [ViewVariables] + public IReadOnlySet> OverflowJobs = default!; /// /// A dictionary relating a NetUserId to the jobs they have on station. @@ -84,7 +73,10 @@ public sealed partial class StationJobsComponent : Component [DataField] public Dictionary>> PlayerJobs = new(); - [DataField("availableJobs", required: true, - customTypeSerializer: typeof(PrototypeIdDictionarySerializer, JobPrototype>))] - public Dictionary> SetupAvailableJobs = default!; + /// + /// Mapping of jobs to an int[2] array that specifies jobs available at round start, and midround. + /// Negative values implies that there is no limit. + /// + [DataField("availableJobs", required: true)] + public Dictionary, int[]> SetupAvailableJobs = default!; } diff --git a/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs b/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs index c3c3865c7bfa..e145e233e9e0 100644 --- a/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs +++ b/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs @@ -52,23 +52,23 @@ private void InitializeRoundStart() /// as there may end up being more round-start slots than available slots, which can cause weird behavior. /// A warning to all who enter ye cursed lands: This function is long and mildly incomprehensible. Best used without touching. /// - public Dictionary AssignJobs(Dictionary profiles, IReadOnlyList stations, bool useRoundStartJobs = true) + public Dictionary?, EntityUid)> AssignJobs(Dictionary profiles, IReadOnlyList stations, bool useRoundStartJobs = true) { DebugTools.Assert(stations.Count > 0); InitializeRoundStart(); if (profiles.Count == 0) - return new Dictionary(); + return new(); // We need to modify this collection later, so make a copy of it. profiles = profiles.ShallowClone(); // Player <-> (job, station) - var assigned = new Dictionary(profiles.Count); + var assigned = new Dictionary?, EntityUid)>(profiles.Count); // The jobs left on the stations. This collection is modified as jobs are assigned to track what's available. - var stationJobs = new Dictionary>(); + var stationJobs = new Dictionary, int?>>(); foreach (var station in stations) { if (useRoundStartJobs) @@ -83,15 +83,15 @@ private void InitializeRoundStart() // We reuse this collection. It tracks what jobs we're currently trying to select players for. - var currentlySelectingJobs = new Dictionary>(stations.Count); + var currentlySelectingJobs = new Dictionary, int?>>(stations.Count); foreach (var station in stations) { - currentlySelectingJobs.Add(station, new Dictionary()); + currentlySelectingJobs.Add(station, new Dictionary, int?>()); } // And these. // Tracks what players are available for a given job in the current iteration of selection. - var jobPlayerOptions = new Dictionary>(); + var jobPlayerOptions = new Dictionary, HashSet>(); // Tracks the total number of slots for the given stations in the current iteration of selection. var stationTotalSlots = new Dictionary(stations.Count); // The share of the players each station gets in the current iteration of job selection. @@ -112,7 +112,7 @@ private void InitializeRoundStart() var optionsRemaining = 0; // Assigns a player to the given station, updating all the bookkeeping while at it. - void AssignPlayer(NetUserId player, string job, EntityUid station) + void AssignPlayer(NetUserId player, ProtoId job, EntityUid station) { // Remove the player from all possible jobs as that's faster than actually checking what they have selected. foreach (var (k, players) in jobPlayerOptions) @@ -273,8 +273,11 @@ void AssignPlayer(NetUserId player, string job, EntityUid station) /// All players that might need an overflow assigned. /// Player character profiles. /// The stations to consider for spawn location. - public void AssignOverflowJobs(ref Dictionary assignedJobs, - IEnumerable allPlayersToAssign, IReadOnlyDictionary profiles, IReadOnlyList stations) + public void AssignOverflowJobs( + ref Dictionary?, EntityUid)> assignedJobs, + IEnumerable allPlayersToAssign, + IReadOnlyDictionary profiles, + IReadOnlyList stations) { var givenStations = stations.ToList(); if (givenStations.Count == 0) diff --git a/Content.Server/Station/Systems/StationJobsSystem.cs b/Content.Server/Station/Systems/StationJobsSystem.cs index 3bfa815af1ef..307610d136fe 100644 --- a/Content.Server/Station/Systems/StationJobsSystem.cs +++ b/Content.Server/Station/Systems/StationJobsSystem.cs @@ -3,6 +3,7 @@ using Content.Server.GameTicking; using Content.Server.Station.Components; using Content.Shared.CCVar; +using Content.Shared.FixedPoint; using Content.Shared.GameTicking; using Content.Shared.Preferences; using Content.Shared.Roles; @@ -31,12 +32,25 @@ public sealed partial class StationJobsSystem : EntitySystem public override void Initialize() { SubscribeLocalEvent(OnStationInitialized); + SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnStationRenamed); SubscribeLocalEvent(OnStationDeletion); SubscribeLocalEvent(OnPlayerJoinedLobby); Subs.CVar(_configurationManager, CCVars.GameDisallowLateJoins, _ => UpdateJobsAvailable(), true); } + private void OnInit(Entity ent, ref ComponentInit args) + { + ent.Comp.MidRoundTotalJobs = ent.Comp.SetupAvailableJobs.Values + .Select(x => Math.Max(x[1], 0)) + .Sum(); + + ent.Comp.OverflowJobs = ent.Comp.SetupAvailableJobs + .Where(x => x.Value[0] < 0) + .Select(x => x.Key) + .ToHashSet(); + } + public override void Update(float _) { if (_availableJobsDirty) @@ -57,28 +71,11 @@ private void OnStationInitialized(StationInitializedEvent msg) if (!TryComp(msg.Station, out var stationJobs)) return; - var mapJobList = stationJobs.SetupAvailableJobs; - - stationJobs.RoundStartTotalJobs = mapJobList.Values.Where(x => x[0] is not null && x[0] > 0).Sum(x => x[0]!.Value); - stationJobs.MidRoundTotalJobs = mapJobList.Values.Where(x => x[1] is not null && x[1] > 0).Sum(x => x[1]!.Value); - - stationJobs.TotalJobs = stationJobs.MidRoundTotalJobs; - - stationJobs.JobList = mapJobList.ToDictionary(x => x.Key, x => - { - if (x.Value[1] <= -1) - return null; - return (uint?) x.Value[1]; - }); - - stationJobs.RoundStartJobList = mapJobList.ToDictionary(x => x.Key, x => - { - if (x.Value[0] <= -1) - return null; - return (uint?) x.Value[0]; - }); + stationJobs.JobList = stationJobs.SetupAvailableJobs.ToDictionary( + x => x.Key, + x=> (int?)(x.Value[1] < 0 ? null : x.Value[1])); - stationJobs.OverflowJobs = stationJobs.OverflowJobs.ToHashSet(); + stationJobs.TotalJobs = stationJobs.JobList.Values.Select(x => x ?? 0).Sum(); UpdateJobsAvailable(); } @@ -141,7 +138,11 @@ public bool TryAdjustJobSlot(EntityUid station, JobPrototype job, int amount, bo /// Resolve pattern, station jobs component of the station. /// Whether or not slot adjustment was a success. /// Thrown when the given station is not a station. - public bool TryAdjustJobSlot(EntityUid station, string jobPrototypeId, int amount, bool createSlot = false, bool clamp = false, + public bool TryAdjustJobSlot(EntityUid station, + string jobPrototypeId, + int amount, + bool createSlot = false, + bool clamp = false, StationJobsComponent? stationJobs = null) { if (!Resolve(station, ref stationJobs)) @@ -156,7 +157,11 @@ public bool TryAdjustJobSlot(EntityUid station, string jobPrototypeId, int amoun // - Return false when you remove from a job that doesn't exist. // - Return false when you remove and exceed the number of slots available. // And additionally, if adding would add a job not previously on the manifest when createSlot is false, return false and do nothing. - switch (jobList.ContainsKey(jobPrototypeId)) + + if (amount == 0) + return true; + + switch (jobList.TryGetValue(jobPrototypeId, out var available)) { case false when amount < 0: return false; @@ -164,31 +169,20 @@ public bool TryAdjustJobSlot(EntityUid station, string jobPrototypeId, int amoun if (!createSlot) return false; stationJobs.TotalJobs += amount; - jobList[jobPrototypeId] = (uint?)amount; + jobList[jobPrototypeId] = amount; UpdateJobsAvailable(); return true; case true: // Job is unlimited so just say we adjusted it and do nothing. - if (jobList[jobPrototypeId] == null) + if (available is not {} avail) return true; // Would remove more jobs than we have available. - if (amount < 0 && (jobList[jobPrototypeId] + amount < 0 && !clamp)) + if (available + amount < 0 && !clamp) return false; - stationJobs.TotalJobs += amount; - - //C# type handling moment - if (amount > 0) - jobList[jobPrototypeId] += (uint)amount; - else - { - if ((int)jobList[jobPrototypeId]!.Value - Math.Abs(amount) <= 0) - jobList[jobPrototypeId] = 0; - else - jobList[jobPrototypeId] -= (uint) Math.Abs(amount); - } - + jobList[jobPrototypeId] = Math.Max(avail + amount, 0); + stationJobs.TotalJobs = jobList.Values.Select(x => x ?? 0).Sum(); UpdateJobsAvailable(); return true; } @@ -239,7 +233,10 @@ public bool TrySetJobSlot(EntityUid station, JobPrototype jobPrototype, int amou /// Resolve pattern, station jobs component of the station. /// Whether or not setting the value succeeded. /// Thrown when the given station is not a station. - public bool TrySetJobSlot(EntityUid station, string jobPrototypeId, int amount, bool createSlot = false, + public bool TrySetJobSlot(EntityUid station, + string jobPrototypeId, + int amount, + bool createSlot = false, StationJobsComponent? stationJobs = null) { if (!Resolve(station, ref stationJobs)) @@ -255,13 +252,13 @@ public bool TrySetJobSlot(EntityUid station, string jobPrototypeId, int amount, if (!createSlot) return false; stationJobs.TotalJobs += amount; - jobList[jobPrototypeId] = (uint?)amount; + jobList[jobPrototypeId] = amount; UpdateJobsAvailable(); return true; case true: - stationJobs.TotalJobs += amount - (int) (jobList[jobPrototypeId] ?? 0); + stationJobs.TotalJobs += amount - (jobList[jobPrototypeId] ?? 0); - jobList[jobPrototypeId] = (uint)amount; + jobList[jobPrototypeId] = amount; UpdateJobsAvailable(); return true; } @@ -289,8 +286,8 @@ public void MakeJobUnlimited(EntityUid station, string jobPrototypeId, StationJo throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); // Subtract out the job we're fixing to make have unlimited slots. - if (stationJobs.JobList.ContainsKey(jobPrototypeId) && stationJobs.JobList[jobPrototypeId] != null) - stationJobs.TotalJobs -= (int)stationJobs.JobList[jobPrototypeId]!.Value; + if (stationJobs.JobList.TryGetValue(jobPrototypeId, out var existing)) + stationJobs.TotalJobs -= existing ?? 0; stationJobs.JobList[jobPrototypeId] = null; @@ -319,8 +316,7 @@ public bool IsJobUnlimited(EntityUid station, string jobPrototypeId, StationJobs if (!Resolve(station, ref stationJobs)) throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); - var res = stationJobs.JobList.TryGetValue(jobPrototypeId, out var job) && job == null; - return res; + return stationJobs.JobList.TryGetValue(jobPrototypeId, out var job) && job == null; } /// @@ -328,7 +324,7 @@ public bool IsJobUnlimited(EntityUid station, string jobPrototypeId, StationJobs /// Job to get slot info for. /// The number of slots remaining. Null if infinite. /// Resolve pattern, station jobs component of the station. - public bool TryGetJobSlot(EntityUid station, JobPrototype job, out uint? slots, StationJobsComponent? stationJobs = null) + public bool TryGetJobSlot(EntityUid station, JobPrototype job, out int? slots, StationJobsComponent? stationJobs = null) { return TryGetJobSlot(station, job.ID, out slots, stationJobs); } @@ -343,21 +339,12 @@ public bool TryGetJobSlot(EntityUid station, JobPrototype job, out uint? slots, /// Whether or not the slot exists. /// Thrown when the given station is not a station. /// slots will be null if the slot doesn't exist, as well, so make sure to check the return value. - public bool TryGetJobSlot(EntityUid station, string jobPrototypeId, out uint? slots, StationJobsComponent? stationJobs = null) + public bool TryGetJobSlot(EntityUid station, string jobPrototypeId, out int? slots, StationJobsComponent? stationJobs = null) { if (!Resolve(station, ref stationJobs)) throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); - if (stationJobs.JobList.TryGetValue(jobPrototypeId, out var job)) - { - slots = job; - return true; - } - else // Else if slot isn't present return null. - { - slots = null; - return false; - } + return stationJobs.JobList.TryGetValue(jobPrototypeId, out slots); } /// @@ -367,12 +354,14 @@ public bool TryGetJobSlot(EntityUid station, string jobPrototypeId, out uint? sl /// Resolve pattern, station jobs component of the station. /// Set containing all jobs available. /// Thrown when the given station is not a station. - public IReadOnlySet GetAvailableJobs(EntityUid station, StationJobsComponent? stationJobs = null) + public IEnumerable> GetAvailableJobs(EntityUid station, StationJobsComponent? stationJobs = null) { if (!Resolve(station, ref stationJobs)) throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); - return stationJobs.JobList.Where(x => x.Value != 0).Select(x => x.Key).ToHashSet(); + return stationJobs.JobList + .Where(x => x.Value != 0) + .Select(x => x.Key); } /// @@ -382,12 +371,12 @@ public IReadOnlySet GetAvailableJobs(EntityUid station, StationJobsCompo /// Resolve pattern, station jobs component of the station. /// Set containing all overflow jobs available. /// Thrown when the given station is not a station. - public IReadOnlySet GetOverflowJobs(EntityUid station, StationJobsComponent? stationJobs = null) + public IReadOnlySet> GetOverflowJobs(EntityUid station, StationJobsComponent? stationJobs = null) { if (!Resolve(station, ref stationJobs)) throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); - return stationJobs.OverflowJobs.ToHashSet(); + return stationJobs.OverflowJobs; } /// @@ -397,7 +386,7 @@ public IReadOnlySet GetOverflowJobs(EntityUid station, StationJobsCompon /// Resolve pattern, station jobs component of the station. /// List of all jobs on the station. /// Thrown when the given station is not a station. - public IReadOnlyDictionary GetJobs(EntityUid station, StationJobsComponent? stationJobs = null) + public IReadOnlyDictionary, int?> GetJobs(EntityUid station, StationJobsComponent? stationJobs = null) { if (!Resolve(station, ref stationJobs)) throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); @@ -412,12 +401,14 @@ public IReadOnlySet GetOverflowJobs(EntityUid station, StationJobsCompon /// Resolve pattern, station jobs component of the station. /// List of all round-start jobs. /// Thrown when the given station is not a station. - public IReadOnlyDictionary GetRoundStartJobs(EntityUid station, StationJobsComponent? stationJobs = null) + public Dictionary, int?> GetRoundStartJobs(EntityUid station, StationJobsComponent? stationJobs = null) { if (!Resolve(station, ref stationJobs)) throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); - return stationJobs.RoundStartJobList; + return stationJobs.SetupAvailableJobs.ToDictionary( + x => x.Key, + x=> (int?)(x.Value[0] < 0 ? null : x.Value[0])); } /// @@ -428,13 +419,13 @@ public IReadOnlySet GetOverflowJobs(EntityUid station, StationJobsCompon /// Whether or not to pick from the overflow list. /// A set of disallowed jobs, if any. /// The selected job, if any. - public string? PickBestAvailableJobWithPriority(EntityUid station, IReadOnlyDictionary jobPriorities, bool pickOverflows, IReadOnlySet>? disallowedJobs = null) + public ProtoId? PickBestAvailableJobWithPriority(EntityUid station, IReadOnlyDictionary, JobPriority> jobPriorities, bool pickOverflows, IReadOnlySet>? disallowedJobs = null) { if (station == EntityUid.Invalid) return null; var available = GetAvailableJobs(station); - bool TryPick(JobPriority priority, [NotNullWhen(true)] out string? jobId) + bool TryPick(JobPriority priority, [NotNullWhen(true)] out ProtoId? jobId) { var filtered = jobPriorities .Where(p => @@ -474,7 +465,10 @@ bool TryPick(JobPriority priority, [NotNullWhen(true)] out string? jobId) return null; var overflows = GetOverflowJobs(station); - return overflows.Count != 0 ? _random.Pick(overflows) : null; + if (overflows.Count == 0) + return null; + + return _random.Pick(overflows); } #endregion Public API @@ -483,7 +477,7 @@ bool TryPick(JobPriority priority, [NotNullWhen(true)] out string? jobId) private bool _availableJobsDirty; - private TickerJobsAvailableEvent _cachedAvailableJobs = new (new Dictionary(), new Dictionary>()); + private TickerJobsAvailableEvent _cachedAvailableJobs = new(new(), new()); /// /// Assembles an event from the current available-to-play jobs. @@ -494,9 +488,9 @@ private TickerJobsAvailableEvent GenerateJobsAvailableEvent() { // If late join is disallowed, return no available jobs. if (_gameTicker.DisallowLateJoin) - return new TickerJobsAvailableEvent(new Dictionary(), new Dictionary>()); + return new TickerJobsAvailableEvent(new(), new()); - var jobs = new Dictionary>(); + var jobs = new Dictionary, int?>>(); var stationNames = new Dictionary(); var query = EntityQueryEnumerator(); diff --git a/Content.Shared/GameTicking/SharedGameTicker.cs b/Content.Shared/GameTicking/SharedGameTicker.cs index 95da4f4c38d3..308476baa8d6 100644 --- a/Content.Shared/GameTicking/SharedGameTicker.cs +++ b/Content.Shared/GameTicking/SharedGameTicker.cs @@ -1,5 +1,6 @@ using Content.Shared.Roles; using Robust.Shared.Network; +using Robust.Shared.Prototypes; using Robust.Shared.Replays; using Robust.Shared.Serialization; using Robust.Shared.Serialization.Markdown.Mapping; @@ -128,19 +129,17 @@ public TickerLobbyCountdownEvent(TimeSpan startTime, bool paused) } [Serializable, NetSerializable] - public sealed class TickerJobsAvailableEvent : EntityEventArgs + public sealed class TickerJobsAvailableEvent( + Dictionary stationNames, + Dictionary, int?>> jobsAvailableByStation) + : EntityEventArgs { /// /// The Status of the Player in the lobby (ready, observer, ...) /// - public Dictionary> JobsAvailableByStation { get; } - public Dictionary StationNames { get; } + public Dictionary, int?>> JobsAvailableByStation { get; } = jobsAvailableByStation; - public TickerJobsAvailableEvent(Dictionary stationNames, Dictionary> jobsAvailableByStation) - { - StationNames = stationNames; - JobsAvailableByStation = jobsAvailableByStation; - } + public Dictionary StationNames { get; } = stationNames; } [Serializable, NetSerializable, DataDefinition] diff --git a/Content.Shared/Preferences/HumanoidCharacterProfile.cs b/Content.Shared/Preferences/HumanoidCharacterProfile.cs index bd55bbb40a8f..20c54cd2687b 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -35,7 +35,7 @@ public sealed partial class HumanoidCharacterProfile : ICharacterProfile /// Job preferences for initial spawn. /// [DataField] - private Dictionary _jobPriorities = new() + private Dictionary, JobPriority> _jobPriorities = new() { { SharedGameTicker.FallbackOverflowJob, JobPriority.High @@ -46,13 +46,13 @@ public sealed partial class HumanoidCharacterProfile : ICharacterProfile /// Antags we have opted in to. /// [DataField] - private HashSet _antagPreferences = new(); + private HashSet> _antagPreferences = new(); /// /// Enabled traits. /// [DataField] - private HashSet _traitPreferences = new(); + private HashSet> _traitPreferences = new(); /// /// @@ -75,7 +75,7 @@ public sealed partial class HumanoidCharacterProfile : ICharacterProfile /// Associated for this profile. /// [DataField] - public string Species { get; set; } = SharedHumanoidAppearanceSystem.DefaultSpecies; + public ProtoId Species { get; set; } = SharedHumanoidAppearanceSystem.DefaultSpecies; [DataField] public int Age { get; set; } = 18; @@ -106,17 +106,17 @@ public sealed partial class HumanoidCharacterProfile : ICharacterProfile /// /// /// - public IReadOnlyDictionary JobPriorities => _jobPriorities; + public IReadOnlyDictionary, JobPriority> JobPriorities => _jobPriorities; /// /// /// - public IReadOnlySet AntagPreferences => _antagPreferences; + public IReadOnlySet> AntagPreferences => _antagPreferences; /// /// /// - public IReadOnlySet TraitPreferences => _traitPreferences; + public IReadOnlySet> TraitPreferences => _traitPreferences; /// /// If we're unable to get one of our preferred jobs do we spawn as a fallback job or do we stay in lobby. @@ -134,10 +134,10 @@ public HumanoidCharacterProfile( Gender gender, HumanoidCharacterAppearance appearance, SpawnPriorityPreference spawnPriority, - Dictionary jobPriorities, + Dictionary, JobPriority> jobPriorities, PreferenceUnavailableMode preferenceUnavailable, - HashSet antagPreferences, - HashSet traitPreferences, + HashSet> antagPreferences, + HashSet> traitPreferences, Dictionary loadouts) { Name = name; @@ -153,6 +153,20 @@ public HumanoidCharacterProfile( _antagPreferences = antagPreferences; _traitPreferences = traitPreferences; _loadouts = loadouts; + + var hasHighPrority = false; + foreach (var (key, value) in _jobPriorities) + { + if (value == JobPriority.Never) + _jobPriorities.Remove(key); + else if (value != JobPriority.High) + continue; + + if (hasHighPrority) + _jobPriorities[key] = JobPriority.Medium; + + hasHighPrority = true; + } } /// Copy constructor @@ -165,10 +179,10 @@ public HumanoidCharacterProfile(HumanoidCharacterProfile other) other.Gender, other.Appearance.Clone(), other.SpawnPriority, - new Dictionary(other.JobPriorities), + new Dictionary, JobPriority>(other.JobPriorities), other.PreferenceUnavailable, - new HashSet(other.AntagPreferences), - new HashSet(other.TraitPreferences), + new HashSet>(other.AntagPreferences), + new HashSet>(other.TraitPreferences), new Dictionary(other.Loadouts)) { } @@ -289,21 +303,48 @@ public HumanoidCharacterProfile WithSpawnPriorityPreference(SpawnPriorityPrefere return new(this) { SpawnPriority = spawnPriority }; } - public HumanoidCharacterProfile WithJobPriorities(IEnumerable> jobPriorities) + public HumanoidCharacterProfile WithJobPriorities(IEnumerable, JobPriority>> jobPriorities) { + var dictionary = new Dictionary, JobPriority>(jobPriorities); + var hasHighPrority = false; + + foreach (var (key, value) in dictionary) + { + if (value == JobPriority.Never) + dictionary.Remove(key); + else if (value != JobPriority.High) + continue; + + if (hasHighPrority) + dictionary[key] = JobPriority.Medium; + + hasHighPrority = true; + } + return new(this) { - _jobPriorities = new Dictionary(jobPriorities), + _jobPriorities = dictionary }; } - public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priority) + public HumanoidCharacterProfile WithJobPriority(ProtoId jobId, JobPriority priority) { - var dictionary = new Dictionary(_jobPriorities); + var dictionary = new Dictionary, JobPriority>(_jobPriorities); if (priority == JobPriority.Never) { dictionary.Remove(jobId); } + else if (priority == JobPriority.High) + { + // There can only ever be one high priority job. + foreach (var (job, value) in dictionary) + { + if (value == JobPriority.High) + dictionary[job] = JobPriority.Medium; + } + + dictionary[jobId] = priority; + } else { dictionary[jobId] = priority; @@ -320,17 +361,17 @@ public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableM return new(this) { PreferenceUnavailable = mode }; } - public HumanoidCharacterProfile WithAntagPreferences(IEnumerable antagPreferences) + public HumanoidCharacterProfile WithAntagPreferences(IEnumerable> antagPreferences) { return new(this) { - _antagPreferences = new HashSet(antagPreferences), + _antagPreferences = new (antagPreferences), }; } - public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref) + public HumanoidCharacterProfile WithAntagPreference(ProtoId antagId, bool pref) { - var list = new HashSet(_antagPreferences); + var list = new HashSet>(_antagPreferences); if (pref) { list.Add(antagId); @@ -346,16 +387,16 @@ public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref) }; } - public HumanoidCharacterProfile WithTraitPreference(string traitId, string? categoryId, bool pref) + public HumanoidCharacterProfile WithTraitPreference(ProtoId traitId, string? categoryId, bool pref) { var prototypeManager = IoCManager.Resolve(); - var traitProto = prototypeManager.Index(traitId); + var traitProto = prototypeManager.Index(traitId); TraitCategoryPrototype? categoryProto = null; if (categoryId != null && categoryId != "default") categoryProto = prototypeManager.Index(categoryId); - var list = new HashSet(_traitPreferences); + var list = new HashSet>(_traitPreferences); if (pref) { @@ -372,7 +413,7 @@ public HumanoidCharacterProfile WithTraitPreference(string traitId, string? cate var count = 0; foreach (var trait in list) { - var traitProtoTemp = prototypeManager.Index(trait); + var traitProtoTemp = prototypeManager.Index(trait); count += traitProtoTemp.Cost; } @@ -514,7 +555,7 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection _ => SpawnPriorityPreference.None // Invalid enum values. }; - var priorities = new Dictionary(JobPriorities + var priorities = new Dictionary, JobPriority>(JobPriorities .Where(p => prototypeManager.TryIndex(p.Key, out var job) && job.SetPreference && p.Value switch { JobPriority.Never => false, // Drop never since that's assumed default. @@ -524,6 +565,17 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection _ => false })); + var hasHighPrio = false; + foreach (var (key, value) in priorities) + { + if (value != JobPriority.High) + continue; + + if (hasHighPrio) + priorities[key] = JobPriority.Medium; + hasHighPrio = true; + } + var antags = AntagPreferences .Where(id => prototypeManager.TryIndex(id, out var antag) && antag.SetPreference) .ToList(); diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index 0b3bfb44384c..dd67e7b10474 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -63,8 +63,8 @@ public sealed partial class JobPrototype : IPrototype public bool CanBeAntag { get; private set; } = true; /// - /// Whether this job is a head. - /// The job system will try to pick heads before other jobs on the same priority level. + /// The "weight" or importance of this job. If this number is large, the job system will assign this job + /// before assigning other jobs. /// [DataField("weight")] public int Weight { get; private set; } diff --git a/Content.Shared/Roles/Jobs/SharedJobSystem.cs b/Content.Shared/Roles/Jobs/SharedJobSystem.cs index fcf76052785e..ce4428d9fea7 100644 --- a/Content.Shared/Roles/Jobs/SharedJobSystem.cs +++ b/Content.Shared/Roles/Jobs/SharedJobSystem.cs @@ -118,6 +118,18 @@ public bool MindTryGetJob( _prototypes.TryIndex(comp.Prototype, out prototype); } + public bool MindTryGetJobId([NotNullWhen(true)] EntityUid? mindId, out ProtoId? job) + { + if (!TryComp(mindId, out JobComponent? comp)) + { + job = null; + return false; + } + + job = comp.Prototype; + return true; + } + /// /// Tries to get the job name for this mind. /// Returns unknown if not found. diff --git a/Resources/Prototypes/Maps/arenas.yml b/Resources/Prototypes/Maps/arenas.yml index 32f85437225a..7ad7a16bc2a9 100644 --- a/Resources/Prototypes/Maps/arenas.yml +++ b/Resources/Prototypes/Maps/arenas.yml @@ -10,7 +10,5 @@ - type: StationNameSetup mapNameTemplate: "Meteor Arena" - type: StationJobs - overflowJobs: - - Passenger availableJobs: Passenger: [ -1, -1 ] diff --git a/Resources/Prototypes/Maps/atlas.yml b/Resources/Prototypes/Maps/atlas.yml index ef7523c72795..6fe3eff030ad 100644 --- a/Resources/Prototypes/Maps/atlas.yml +++ b/Resources/Prototypes/Maps/atlas.yml @@ -14,8 +14,6 @@ !type:NanotrasenNameGenerator prefixCreator: 'R4' # R4407/Goon. GS isn't as cool sounding. - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/bagel.yml b/Resources/Prototypes/Maps/bagel.yml index 24ca17339fcc..ea06153d7e45 100644 --- a/Resources/Prototypes/Maps/bagel.yml +++ b/Resources/Prototypes/Maps/bagel.yml @@ -16,8 +16,6 @@ - type: StationEmergencyShuttle emergencyShuttlePath: /Maps/Shuttles/emergency_lox.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/box.yml b/Resources/Prototypes/Maps/box.yml index 50826fc5e097..89ba3779c657 100644 --- a/Resources/Prototypes/Maps/box.yml +++ b/Resources/Prototypes/Maps/box.yml @@ -15,8 +15,6 @@ - type: StationEmergencyShuttle emergencyShuttlePath: /Maps/Shuttles/emergency_box.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/cluster.yml b/Resources/Prototypes/Maps/cluster.yml index fe445b008134..10a12c4f44e2 100644 --- a/Resources/Prototypes/Maps/cluster.yml +++ b/Resources/Prototypes/Maps/cluster.yml @@ -16,8 +16,6 @@ !type:NanotrasenNameGenerator prefixCreator: '14' - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/core.yml b/Resources/Prototypes/Maps/core.yml index 6b85aca51d38..d7a15f2b1da4 100644 --- a/Resources/Prototypes/Maps/core.yml +++ b/Resources/Prototypes/Maps/core.yml @@ -18,8 +18,6 @@ - type: StationCargoShuttle path: /Maps/Shuttles/cargo_core.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Bartender: [ 2, 2 ] diff --git a/Resources/Prototypes/Maps/debug.yml b/Resources/Prototypes/Maps/debug.yml index 2f475c1e5795..8d4cc550a27c 100644 --- a/Resources/Prototypes/Maps/debug.yml +++ b/Resources/Prototypes/Maps/debug.yml @@ -10,8 +10,6 @@ - type: StationNameSetup mapNameTemplate: "Empty" - type: StationJobs - overflowJobs: - - Passenger availableJobs: Passenger: [ -1, -1 ] @@ -27,8 +25,6 @@ - type: StationNameSetup mapNameTemplate: "Dev" - type: StationJobs - overflowJobs: - - Captain availableJobs: Captain: [ -1, -1 ] @@ -44,7 +40,5 @@ - type: StationNameSetup mapNameTemplate: "TEG" - type: StationJobs - overflowJobs: - - ChiefEngineer availableJobs: ChiefEngineer: [ -1, -1 ] diff --git a/Resources/Prototypes/Maps/europa.yml b/Resources/Prototypes/Maps/europa.yml index 0c9f1d975b25..412e1b46569f 100644 --- a/Resources/Prototypes/Maps/europa.yml +++ b/Resources/Prototypes/Maps/europa.yml @@ -21,8 +21,6 @@ - type: StationEmergencyShuttle emergencyShuttlePath: /Maps/Shuttles/emergency_transit.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Bartender: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/fland.yml b/Resources/Prototypes/Maps/fland.yml index f0c35f99e57e..f22c44cb6300 100644 --- a/Resources/Prototypes/Maps/fland.yml +++ b/Resources/Prototypes/Maps/fland.yml @@ -17,8 +17,6 @@ - type: StationCargoShuttle path: /Maps/Shuttles/cargo_fland.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/marathon.yml b/Resources/Prototypes/Maps/marathon.yml index f82ee1d4344e..32ad8d576c24 100644 --- a/Resources/Prototypes/Maps/marathon.yml +++ b/Resources/Prototypes/Maps/marathon.yml @@ -16,8 +16,6 @@ - type: StationEmergencyShuttle emergencyShuttlePath: /Maps/Shuttles/emergency_rod.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/meta.yml b/Resources/Prototypes/Maps/meta.yml index 2bee606e95ab..ebd6954aa763 100644 --- a/Resources/Prototypes/Maps/meta.yml +++ b/Resources/Prototypes/Maps/meta.yml @@ -15,8 +15,6 @@ - type: StationEmergencyShuttle emergencyShuttlePath: /Maps/Shuttles/emergency_meta.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/oasis.yml b/Resources/Prototypes/Maps/oasis.yml index b5e0f97190a8..a4cc6eb43d62 100644 --- a/Resources/Prototypes/Maps/oasis.yml +++ b/Resources/Prototypes/Maps/oasis.yml @@ -15,8 +15,6 @@ - type: StationEmergencyShuttle emergencyShuttlePath: /Maps/Shuttles/emergency_delta.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] @@ -62,4 +60,4 @@ Passenger: [ -1, -1 ] Clown: [ 1, 1 ] Mime: [ 1, 1 ] - Musician: [ 1, 1 ] \ No newline at end of file + Musician: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/omega.yml b/Resources/Prototypes/Maps/omega.yml index f90c5f5b658d..b94fdbc05d18 100644 --- a/Resources/Prototypes/Maps/omega.yml +++ b/Resources/Prototypes/Maps/omega.yml @@ -16,8 +16,6 @@ - type: StationEmergencyShuttle emergencyShuttlePath: /Maps/Shuttles/emergency_omega.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/origin.yml b/Resources/Prototypes/Maps/origin.yml index 128148989129..24214b37a1f3 100644 --- a/Resources/Prototypes/Maps/origin.yml +++ b/Resources/Prototypes/Maps/origin.yml @@ -15,8 +15,6 @@ - type: StationEmergencyShuttle emergencyShuttlePath: /Maps/Shuttles/emergency_courser.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/packed.yml b/Resources/Prototypes/Maps/packed.yml index 20d6c7a7bd7b..b844636bf831 100644 --- a/Resources/Prototypes/Maps/packed.yml +++ b/Resources/Prototypes/Maps/packed.yml @@ -14,8 +14,6 @@ !type:NanotrasenNameGenerator prefixCreator: 'VG' - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/reach.yml b/Resources/Prototypes/Maps/reach.yml index 03a688cf23c8..a0d6752c1fb8 100644 --- a/Resources/Prototypes/Maps/reach.yml +++ b/Resources/Prototypes/Maps/reach.yml @@ -16,8 +16,6 @@ - type: StationEmergencyShuttle emergencyShuttlePath: /Maps/Shuttles/emergency.yml - type: StationJobs - overflowJobs: - - Passenger availableJobs: Captain: [ 1, 1 ] HeadOfSecurity: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/saltern.yml b/Resources/Prototypes/Maps/saltern.yml index bc12eca65461..e9d26ce3192c 100644 --- a/Resources/Prototypes/Maps/saltern.yml +++ b/Resources/Prototypes/Maps/saltern.yml @@ -15,8 +15,6 @@ !type:NanotrasenNameGenerator prefixCreator: '14' - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] diff --git a/Resources/Prototypes/Maps/train.yml b/Resources/Prototypes/Maps/train.yml index b18db7aea8d1..7f24fcdd6771 100644 --- a/Resources/Prototypes/Maps/train.yml +++ b/Resources/Prototypes/Maps/train.yml @@ -18,8 +18,6 @@ - type: StationEmergencyShuttle emergencyShuttlePath: /Maps/Shuttles/emergency_omega.yml # To do - add railway station - type: StationJobs - overflowJobs: - - Passenger availableJobs: #service Captain: [ 1, 1 ] From f933922c9e7e561b83daea356450755e4ee830a3 Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Wed, 5 Jun 2024 07:19:54 -0700 Subject: [PATCH 300/568] Add logs that provide session to player admin logs (#28628) --- Content.Server/Administration/Logs/AdminLogManager.Json.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Content.Server/Administration/Logs/AdminLogManager.Json.cs b/Content.Server/Administration/Logs/AdminLogManager.Json.cs index 0a67d61cefe7..9e6274a49330 100644 --- a/Content.Server/Administration/Logs/AdminLogManager.Json.cs +++ b/Content.Server/Administration/Logs/AdminLogManager.Json.cs @@ -65,6 +65,10 @@ private void InitializeJson() { players.Add(actor.PlayerSession.UserId.UserId); } + else if (value is SerializablePlayer player) + { + players.Add(player.Player.UserId.UserId); + } } return (JsonSerializer.SerializeToDocument(parsed, _jsonOptions), players); From bc360784ff90d4211999fa7dfcc52812d3de6b9b Mon Sep 17 00:00:00 2001 From: Boaz1111 <149967078+Boaz1111@users.noreply.github.com> Date: Wed, 5 Jun 2024 19:50:37 +0200 Subject: [PATCH 301/568] Cluster Update (#28627) done here --- Resources/Maps/cluster.yml | 981 +++++++++++++++---------------------- 1 file changed, 401 insertions(+), 580 deletions(-) diff --git a/Resources/Maps/cluster.yml b/Resources/Maps/cluster.yml index b8540c6f68da..a9f601114c60 100644 --- a/Resources/Maps/cluster.yml +++ b/Resources/Maps/cluster.yml @@ -38,6 +38,7 @@ tilemap: 113: FloorWhiteMono 1: FloorWhitePlastic 118: FloorWood + 3: FloorWoodLarge 120: Lattice 121: Plating entities: @@ -65,7 +66,7 @@ entities: version: 6 0,-1: ind: 0,-1 - tiles: WQAAAAACWQAAAAACWQAAAAABeQAAAAAAdgAAAAADdgAAAAADdgAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAYAAAAAAAWQAAAAADWQAAAAABeQAAAAAAdgAAAAACdgAAAAABdgAAAAADeQAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAABWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADYAAAAAAAWQAAAAADWQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAABWQAAAAACWQAAAAABeQAAAAAAWQAAAAAAWQAAAAACWQAAAAACYAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAWQAAAAACYAAAAAAAWQAAAAABWQAAAAADWQAAAAADWQAAAAADWQAAAAAAWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABWQAAAAAAWQAAAAACYAAAAAAAWQAAAAABWQAAAAABWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAACWQAAAAABWQAAAAAAYAAAAAAAWQAAAAABWQAAAAABeQAAAAAAeQAAAAAAZAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAADWQAAAAAATAAAAAAAWQAAAAAAWQAAAAADWQAAAAACWQAAAAABTAAAAAAAWQAAAAABWQAAAAACWQAAAAADYAAAAAAAWQAAAAADeQAAAAAAWQAAAAAAWQAAAAACYAAAAAAAWQAAAAAAWQAAAAACWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAABWQAAAAADWQAAAAAAWQAAAAABWQAAAAACWQAAAAAAWQAAAAAAZAAAAAAAWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAOgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABYAAAAAAAWQAAAAADWQAAAAAAWQAAAAACWQAAAAAAWQAAAAABZAAAAAAAWQAAAAABJAAAAAACJAAAAAAAJAAAAAADOgAAAAAAJAAAAAAAJAAAAAADeQAAAAAAWQAAAAAAWQAAAAACWQAAAAADWQAAAAAAWQAAAAADYAAAAAAAWQAAAAADeQAAAAAAZAAAAAABJAAAAAABJAAAAAADJAAAAAABOgAAAAAAJAAAAAABJAAAAAABeQAAAAAAYAAAAAAAWQAAAAADWQAAAAAAWQAAAAACWQAAAAACWQAAAAAAWQAAAAABeQAAAAAAWQAAAAABOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAWQAAAAABTAAAAAAAWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAEQAAAAAAJAAAAAAAJAAAAAAAJAAAAAAAOgAAAAAAJAAAAAAAJAAAAAADeQAAAAAAWQAAAAABYAAAAAAAWQAAAAADeQAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAeQAAAAAAeQAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAeQAAAAAAWQAAAAACWQAAAAABWQAAAAACOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAHQAAAAADHQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAOgAAAAAAOgAAAAAAeQAAAAAAeQAAAAAAWQAAAAADYAAAAAAAWQAAAAAAeQAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAHQAAAAABHQAAAAAC + tiles: WQAAAAACWQAAAAACWQAAAAABeQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAYAAAAAAAWQAAAAADWQAAAAABeQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAABWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADYAAAAAAAWQAAAAADWQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAABWQAAAAACWQAAAAABeQAAAAAAWQAAAAAAWQAAAAACWQAAAAACYAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAWQAAAAACYAAAAAAAWQAAAAABWQAAAAADWQAAAAADWQAAAAADWQAAAAAAWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABWQAAAAAAWQAAAAACYAAAAAAAWQAAAAABWQAAAAABWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAACWQAAAAABWQAAAAAAYAAAAAAAWQAAAAABWQAAAAABeQAAAAAAeQAAAAAAZAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAADWQAAAAAATAAAAAAAWQAAAAAAWQAAAAADWQAAAAACWQAAAAABTAAAAAAAWQAAAAABWQAAAAACWQAAAAADYAAAAAAAWQAAAAADeQAAAAAAWQAAAAAAWQAAAAACYAAAAAAAWQAAAAAAWQAAAAACWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAABWQAAAAADWQAAAAAAWQAAAAABWQAAAAACWQAAAAAAWQAAAAAAZAAAAAAAWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAOgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABYAAAAAAAWQAAAAADWQAAAAAAWQAAAAACWQAAAAAAWQAAAAABZAAAAAAAWQAAAAABJAAAAAACJAAAAAAAJAAAAAADOgAAAAAAJAAAAAAAJAAAAAADeQAAAAAAWQAAAAAAWQAAAAACWQAAAAADWQAAAAAAWQAAAAADYAAAAAAAWQAAAAADeQAAAAAAZAAAAAABJAAAAAABJAAAAAADJAAAAAABOgAAAAAAJAAAAAABJAAAAAABeQAAAAAAYAAAAAAAWQAAAAADWQAAAAAAWQAAAAACWQAAAAACWQAAAAAAWQAAAAABeQAAAAAAWQAAAAABOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAWQAAAAABTAAAAAAAWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAEQAAAAAAJAAAAAAAJAAAAAAAJAAAAAAAOgAAAAAAJAAAAAAAJAAAAAADeQAAAAAAWQAAAAABYAAAAAAAWQAAAAADeQAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAeQAAAAAAeQAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAeQAAAAAAWQAAAAACWQAAAAABWQAAAAACOgAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAHQAAAAADHQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAOgAAAAAAOgAAAAAAeQAAAAAAeQAAAAAAWQAAAAADYAAAAAAAWQAAAAAAeQAAAAAAOgAAAAAAOgAAAAAAOgAAAAAAHQAAAAABHQAAAAAC version: 6 -1,1: ind: -1,1 @@ -85,7 +86,7 @@ entities: version: 6 0,-2: ind: 0,-2 - tiles: YAAAAAAAWQAAAAACWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABYAAAAAAAWQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABWQAAAAAAYAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAYAAAAAAAWQAAAAADWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAACWQAAAAAAWQAAAAACeQAAAAAAWQAAAAAAWQAAAAACWQAAAAABeQAAAAAAWQAAAAABWQAAAAACWQAAAAACWQAAAAABWQAAAAABWQAAAAAAWQAAAAABWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAWQAAAAAAWQAAAAABWQAAAAACZAAAAAADWQAAAAADWQAAAAAAWQAAAAAAWQAAAAABYAAAAAAAWQAAAAAAWQAAAAADWQAAAAADWQAAAAAAWQAAAAACYAAAAAAAeQAAAAAAWQAAAAACWQAAAAACWQAAAAADeQAAAAAAWQAAAAACWQAAAAAAYAAAAAAAWQAAAAABWQAAAAABWQAAAAAAYAAAAAAAWQAAAAABWQAAAAABWQAAAAACWQAAAAABeQAAAAAAeQAAAAAAZAAAAAADeQAAAAAAeQAAAAAAeQAAAAAAZAAAAAACZAAAAAACeQAAAAAAeQAAAAAAWQAAAAACYAAAAAAAWQAAAAAAWQAAAAABYAAAAAAAWQAAAAACWQAAAAACYAAAAAAAWQAAAAACWQAAAAADZAAAAAADWQAAAAAAYAAAAAAAWQAAAAABWQAAAAABeQAAAAAAWQAAAAAAWQAAAAADYAAAAAAAWQAAAAADWQAAAAABWQAAAAADWQAAAAAAWQAAAAACWQAAAAABYAAAAAAAeQAAAAAAWQAAAAADWQAAAAABYAAAAAAAWQAAAAAAZAAAAAABWQAAAAABYAAAAAAAYAAAAAAAWQAAAAACWQAAAAACWQAAAAAAYAAAAAAAWQAAAAACWQAAAAABWQAAAAADZAAAAAADWQAAAAAAWQAAAAADWQAAAAACWQAAAAAAZAAAAAADYAAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAACWQAAAAACWQAAAAABYAAAAAAAWQAAAAADYAAAAAAAZAAAAAAAWQAAAAADYAAAAAAAWQAAAAADWQAAAAADeQAAAAAAWQAAAAADWQAAAAABWQAAAAABYAAAAAAAWQAAAAABWQAAAAABWQAAAAABWQAAAAABWQAAAAABYAAAAAAAeQAAAAAAWQAAAAABWQAAAAABWQAAAAADWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADWQAAAAAAWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABYAAAAAAAWQAAAAADYAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAWQAAAAABWQAAAAABYAAAAAAAeQAAAAAAdgAAAAAAdgAAAAADdgAAAAABdgAAAAAAWQAAAAAAWQAAAAABWQAAAAACWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAWQAAAAACWQAAAAAAWQAAAAABeQAAAAAAdgAAAAAAdgAAAAABdgAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAA + tiles: YAAAAAAAWQAAAAACWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABYAAAAAAAWQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABWQAAAAAAYAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAYAAAAAAAWQAAAAADWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAACWQAAAAAAWQAAAAACeQAAAAAAWQAAAAAAWQAAAAACWQAAAAABeQAAAAAAWQAAAAABWQAAAAACWQAAAAACWQAAAAABWQAAAAABWQAAAAAAWQAAAAABWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAWQAAAAAAWQAAAAABWQAAAAACZAAAAAADWQAAAAADWQAAAAAAWQAAAAAAWQAAAAABYAAAAAAAWQAAAAAAWQAAAAADWQAAAAADWQAAAAAAWQAAAAACYAAAAAAAeQAAAAAAWQAAAAACWQAAAAACWQAAAAADeQAAAAAAWQAAAAACWQAAAAAAYAAAAAAAWQAAAAABWQAAAAABWQAAAAAAYAAAAAAAWQAAAAABWQAAAAABWQAAAAACWQAAAAABeQAAAAAAeQAAAAAAZAAAAAADeQAAAAAAeQAAAAAAeQAAAAAAZAAAAAACZAAAAAACeQAAAAAAeQAAAAAAWQAAAAACYAAAAAAAWQAAAAAAWQAAAAABYAAAAAAAWQAAAAACWQAAAAACYAAAAAAAWQAAAAACWQAAAAADZAAAAAADWQAAAAAAYAAAAAAAWQAAAAABWQAAAAABeQAAAAAAWQAAAAAAWQAAAAADYAAAAAAAWQAAAAADWQAAAAABWQAAAAADWQAAAAAAWQAAAAACWQAAAAABYAAAAAAAeQAAAAAAWQAAAAADWQAAAAABYAAAAAAAWQAAAAAAZAAAAAABWQAAAAABYAAAAAAAYAAAAAAAWQAAAAACWQAAAAACWQAAAAAAYAAAAAAAWQAAAAACWQAAAAABWQAAAAADZAAAAAADWQAAAAAAWQAAAAADWQAAAAACWQAAAAAAZAAAAAADYAAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAACWQAAAAACWQAAAAABYAAAAAAAWQAAAAADYAAAAAAAZAAAAAAAWQAAAAADYAAAAAAAWQAAAAADWQAAAAADeQAAAAAAWQAAAAADWQAAAAABWQAAAAABYAAAAAAAWQAAAAABWQAAAAABWQAAAAABWQAAAAABWQAAAAABYAAAAAAAeQAAAAAAWQAAAAABWQAAAAABWQAAAAADWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADWQAAAAAAWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABYAAAAAAAWQAAAAADYAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAWQAAAAABWQAAAAABYAAAAAAAeQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAdgAAAAAAWQAAAAAAWQAAAAABWQAAAAACWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAWQAAAAACWQAAAAAAWQAAAAABeQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAA version: 6 -2,1: ind: -2,1 @@ -7651,11 +7652,6 @@ entities: parent: 1 - proto: Bed entities: - - uid: 1805 - components: - - type: Transform - pos: 25.5,12.5 - parent: 1 - uid: 1806 components: - type: Transform @@ -7676,6 +7672,11 @@ entities: - type: Transform pos: -20.5,-3.5 parent: 1 + - uid: 1852 + components: + - type: Transform + pos: 25.5,12.5 + parent: 1 - uid: 3482 components: - type: Transform @@ -7711,15 +7712,15 @@ entities: - type: Transform pos: 11.5,5.5 parent: 1 - - uid: 5663 + - uid: 5671 components: - type: Transform - pos: 16.5,-15.5 + pos: 20.5,-15.5 parent: 1 - - uid: 7442 + - uid: 7446 components: - type: Transform - pos: 4.5,-17.5 + pos: 6.5,-14.5 parent: 1 - uid: 7714 components: @@ -7765,11 +7766,10 @@ entities: parent: 1 - proto: BedsheetCE entities: - - uid: 5662 + - uid: 4918 components: - type: Transform - rot: 3.141592653589793 rad - pos: 16.5,-15.5 + pos: 20.5,-15.5 parent: 1 - proto: BedsheetClown entities: @@ -7829,7 +7829,7 @@ entities: parent: 1 - proto: BedsheetHOS entities: - - uid: 5373 + - uid: 1863 components: - type: Transform pos: 25.5,12.5 @@ -7877,11 +7877,10 @@ entities: parent: 1 - proto: BedsheetQM entities: - - uid: 7441 + - uid: 5395 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 4.5,-17.5 + pos: 6.5,-14.5 parent: 1 - proto: BedsheetRD entities: @@ -8242,6 +8241,11 @@ entities: parent: 1 - proto: BoxFolderBase entities: + - uid: 1805 + components: + - type: Transform + pos: -22.477488,-1.5542104 + parent: 1 - uid: 1857 components: - type: Transform @@ -8287,6 +8291,11 @@ entities: rot: 3.141592653589793 rad pos: -8.510255,48.552643 parent: 1 + - uid: 5634 + components: + - type: Transform + pos: -22.289988,-1.3667104 + parent: 1 - uid: 6055 components: - type: Transform @@ -8348,16 +8357,6 @@ entities: parent: 1 - proto: BoxFolderGrey entities: - - uid: 1883 - components: - - type: Transform - pos: -21.598257,-1.4382241 - parent: 1 - - uid: 1884 - components: - - type: Transform - pos: -21.473257,-1.3444741 - parent: 1 - uid: 6069 components: - type: Transform @@ -8385,11 +8384,6 @@ entities: - type: Transform pos: 28.678442,8.532815 parent: 1 - - uid: 5390 - components: - - type: Transform - pos: 27.618711,13.570517 - parent: 1 - uid: 5397 components: - type: Transform @@ -8407,18 +8401,28 @@ entities: parent: 1 - proto: BoxFolderWhite entities: - - uid: 1879 + - uid: 5377 components: - type: Transform - pos: -19.351341,14.63665 + pos: -19.312119,13.611187 parent: 1 - - uid: 1880 + - uid: 5655 components: - type: Transform - pos: -19.445091,14.558525 + pos: -19.593369,13.439312 parent: 1 - proto: BoxFolderYellow entities: + - uid: 1879 + components: + - type: Transform + pos: 4.418282,-17.420078 + parent: 1 + - uid: 1881 + components: + - type: Transform + pos: 4.277657,-17.638828 + parent: 1 - uid: 5095 components: - type: Transform @@ -8427,7 +8431,7 @@ entities: - uid: 5677 components: - type: Transform - pos: 18.605122,-14.452038 + pos: 17.707003,-15.576565 parent: 1 - uid: 6787 components: @@ -8469,16 +8473,6 @@ entities: - type: Transform pos: 11.422744,-18.443436 parent: 1 - - uid: 7448 - components: - - type: Transform - pos: 5.6525464,-15.315514 - parent: 1 - - uid: 7449 - components: - - type: Transform - pos: 5.4181714,-15.456139 - parent: 1 - uid: 9600 components: - type: Transform @@ -22162,7 +22156,7 @@ entities: - uid: 4981 components: - type: Transform - pos: 4.562398,31.612326 + pos: 5.6753416,30.52142 parent: 1 - proto: CarbonDioxideCanister entities: @@ -22238,30 +22232,6 @@ entities: rot: 3.141592653589793 rad pos: 26.5,13.5 parent: 1 - - uid: 5625 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 27.5,14.5 - parent: 1 - - uid: 5626 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 27.5,13.5 - parent: 1 - - uid: 5627 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 28.5,14.5 - parent: 1 - - uid: 5628 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 28.5,13.5 - parent: 1 - uid: 5629 components: - type: Transform @@ -22286,18 +22256,6 @@ entities: rot: 3.141592653589793 rad pos: 25.5,14.5 parent: 1 - - uid: 5633 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 27.5,12.5 - parent: 1 - - uid: 5634 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 28.5,12.5 - parent: 1 - uid: 5635 components: - type: Transform @@ -22404,6 +22362,11 @@ entities: parent: 1 - proto: CarpetBlue entities: + - uid: 4091 + components: + - type: Transform + pos: -20.5,15.5 + parent: 1 - uid: 5645 components: - type: Transform @@ -22452,35 +22415,10 @@ entities: rot: 3.141592653589793 rad pos: -19.5,15.5 parent: 1 - - uid: 5653 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-2.5 - parent: 1 - - uid: 5654 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-2.5 - parent: 1 - - uid: 5655 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-3.5 - parent: 1 - - uid: 5656 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -20.5,-2.5 - parent: 1 - - uid: 5657 + - uid: 7444 components: - type: Transform - rot: 3.141592653589793 rad - pos: -20.5,-3.5 + pos: -20.5,16.5 parent: 1 - proto: CarpetGreen entities: @@ -22621,52 +22559,93 @@ entities: parent: 1 - proto: CarpetOrange entities: - - uid: 5658 + - uid: 1845 components: - type: Transform - rot: 3.141592653589793 rad - pos: 16.5,-14.5 + pos: 6.5,-14.5 parent: 1 - - uid: 5659 + - uid: 5415 components: - type: Transform - rot: 3.141592653589793 rad - pos: 16.5,-15.5 + pos: 19.5,-15.5 + parent: 1 + - uid: 5626 + components: + - type: Transform + pos: 4.5,-14.5 + parent: 1 + - uid: 5627 + components: + - type: Transform + pos: 5.5,-14.5 + parent: 1 + - uid: 5628 + components: + - type: Transform + pos: 5.5,-15.5 + parent: 1 + - uid: 5653 + components: + - type: Transform + pos: 6.5,-15.5 parent: 1 - uid: 5660 components: - type: Transform - rot: 3.141592653589793 rad - pos: 17.5,-14.5 + pos: 20.5,-14.5 parent: 1 - uid: 5661 components: - type: Transform - rot: 3.141592653589793 rad - pos: 17.5,-15.5 + pos: 20.5,-15.5 parent: 1 - - uid: 12419 + - uid: 5662 components: - type: Transform - pos: 4.5,-17.5 + pos: 20.5,-13.5 parent: 1 - - uid: 12420 + - uid: 5672 components: - type: Transform - pos: 4.5,-16.5 + pos: 19.5,-14.5 parent: 1 - - uid: 12421 + - uid: 5673 components: - type: Transform - pos: 5.5,-17.5 + pos: 19.5,-13.5 parent: 1 - - uid: 12422 + - uid: 7447 components: - type: Transform - pos: 5.5,-16.5 + pos: 4.5,-15.5 parent: 1 - proto: CarpetPurple entities: + - uid: 1870 + components: + - type: Transform + pos: -22.5,-2.5 + parent: 1 + - uid: 1871 + components: + - type: Transform + pos: -20.5,-2.5 + parent: 1 + - uid: 1873 + components: + - type: Transform + pos: -21.5,-3.5 + parent: 1 + - uid: 1874 + components: + - type: Transform + pos: -20.5,-3.5 + parent: 1 + - uid: 1877 + components: + - type: Transform + pos: -21.5,-2.5 + parent: 1 - uid: 5018 components: - type: Transform @@ -22682,6 +22661,11 @@ entities: - type: Transform pos: 5.5,10.5 parent: 1 + - uid: 5369 + components: + - type: Transform + pos: -22.5,-3.5 + parent: 1 - proto: CarpetSBlue entities: - uid: 5639 @@ -26173,17 +26157,17 @@ entities: - type: Transform pos: 14.5,13.5 parent: 1 - - uid: 1710 + - uid: 1875 components: - type: Transform rot: 3.141592653589793 rad - pos: 5.5,-16.5 + pos: -21.5,-2.5 parent: 1 - - uid: 1875 + - uid: 1883 components: - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-2.5 + rot: -1.5707963267948966 rad + pos: 5.512032,-16.451328 parent: 1 - uid: 2868 components: @@ -26342,11 +26326,10 @@ entities: rot: 3.141592653589793 rad pos: 28.5,17.5 parent: 1 - - uid: 5375 + - uid: 5393 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 28.5,13.5 + pos: 28.500902,13.478384 parent: 1 - uid: 5574 components: @@ -26360,12 +26343,6 @@ entities: rot: 3.141592653589793 rad pos: -36.5,-3.5 parent: 1 - - uid: 5673 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 17.5,-14.5 - parent: 1 - uid: 5674 components: - type: Transform @@ -26400,6 +26377,12 @@ entities: - type: Transform pos: 12.5,-26.5 parent: 1 + - uid: 7452 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 17.488253,-14.435939 + parent: 1 - uid: 8013 components: - type: Transform @@ -26449,11 +26432,6 @@ entities: parent: 1 - proto: ChairOfficeLight entities: - - uid: 1874 - components: - - type: Transform - pos: -19.5,15.5 - parent: 1 - uid: 5608 components: - type: Transform @@ -26472,6 +26450,12 @@ entities: rot: 3.141592653589793 rad pos: -8.5,6.5 parent: 1 + - uid: 5633 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -19.499619,14.564312 + parent: 1 - uid: 5921 components: - type: Transform @@ -26636,18 +26620,6 @@ entities: rot: 1.5707963267948966 rad pos: -22.5,-29.5 parent: 1 -- proto: chem_master - entities: - - uid: 5604 - components: - - type: Transform - pos: -8.5,14.5 - parent: 1 - - uid: 5605 - components: - - type: Transform - pos: -11.5,16.5 - parent: 1 - proto: ChemDispenser entities: - uid: 5606 @@ -26674,6 +26646,18 @@ entities: - type: Transform pos: -11.5,13.5 parent: 1 +- proto: ChemMaster + entities: + - uid: 5604 + components: + - type: Transform + pos: -8.5,14.5 + parent: 1 + - uid: 5605 + components: + - type: Transform + pos: -11.5,16.5 + parent: 1 - proto: ChessBoard entities: - uid: 5058 @@ -27514,6 +27498,13 @@ entities: - type: Transform pos: -13.503023,28.53958 parent: 1 +- proto: ClothingHeadHatBeretCmo + entities: + - uid: 7450 + components: + - type: Transform + pos: -20.687119,13.736187 + parent: 1 - proto: ClothingHeadHatFedoraBrown entities: - uid: 12535 @@ -27630,21 +27621,7 @@ entities: - uid: 6397 components: - type: Transform - pos: 4.585783,31.487268 - parent: 1 -- proto: ClothingNeckMantleCE - entities: - - uid: 5675 - components: - - type: Transform - pos: 18.495747,-13.358288 - parent: 1 -- proto: ClothingNeckMantleCMO - entities: - - uid: 1869 - components: - - type: Transform - pos: -20.496048,14.567978 + pos: 5.4722166,31.005795 parent: 1 - proto: ClothingNeckMantleHOP entities: @@ -27655,13 +27632,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: ClothingNeckMantleRD - entities: - - uid: 1877 - components: - - type: Transform - pos: -22.452333,-1.4018333 - parent: 1 - proto: ClothingNeckScarfStripedBlue entities: - uid: 1899 @@ -27779,15 +27749,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: ClothingOuterWinterCE - entities: - - uid: 5670 - components: - - type: Transform - parent: 5669 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: ClothingOuterWinterCMO entities: - uid: 1868 @@ -27806,33 +27767,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: ClothingOuterWinterHoS - entities: - - uid: 12478 - components: - - type: Transform - parent: 5374 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: ClothingOuterWinterQM - entities: - - uid: 7444 - components: - - type: Transform - parent: 7443 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: ClothingOuterWinterRD - entities: - - uid: 12418 - components: - - type: Transform - parent: 1845 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: ClothingShoesBootsCombat entities: - uid: 8275 @@ -27914,16 +27848,17 @@ entities: parent: 1 - proto: ComfyChair entities: - - uid: 1711 + - uid: 794 components: - type: Transform - pos: 5.5,-14.5 + rot: 3.141592653589793 rad + pos: 26.5,12.5 parent: 1 - - uid: 1873 + - uid: 1848 components: - type: Transform - rot: 3.141592653589793 rad - pos: -19.5,13.5 + rot: -1.5707963267948966 rad + pos: 6.5,-15.5 parent: 1 - uid: 1876 components: @@ -27968,12 +27903,6 @@ entities: rot: -1.5707963267948966 rad pos: 4.5,25.5 parent: 1 - - uid: 5377 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 26.5,13.5 - parent: 1 - uid: 5573 components: - type: Transform @@ -27986,6 +27915,11 @@ entities: rot: 3.141592653589793 rad pos: 2.5,5.5 parent: 1 + - uid: 7454 + components: + - type: Transform + pos: 19.5,-13.5 + parent: 1 - uid: 7712 components: - type: Transform @@ -28006,11 +27940,16 @@ entities: - ArtifactAnalyzerSender: ArtifactAnalyzerReceiver - proto: ComputerCargoBounty entities: - - uid: 7247 + - uid: 1880 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 16.5,-23.5 + rot: 1.5707963267948966 rad + pos: 4.5,-16.5 + parent: 1 + - uid: 5669 + components: + - type: Transform + pos: 8.5,-19.5 parent: 1 - proto: ComputerCargoOrders entities: @@ -28020,18 +27959,11 @@ entities: rot: 3.141592653589793 rad pos: 8.5,-22.5 parent: 1 - - uid: 7395 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 4.5,-14.5 - parent: 1 -- proto: ComputerCargoShuttle - entities: - - uid: 4918 + - uid: 5366 components: - type: Transform - pos: 8.5,-19.5 + rot: 3.141592653589793 rad + pos: 5.5,-17.5 parent: 1 - proto: ComputerComms entities: @@ -28048,6 +27980,12 @@ entities: parent: 1 - proto: ComputerCrewMonitoring entities: + - uid: 1844 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -20.5,14.5 + parent: 1 - uid: 1888 components: - type: Transform @@ -28066,10 +28004,11 @@ entities: - type: Transform pos: 14.5,15.5 parent: 1 - - uid: 5366 + - uid: 1869 components: - type: Transform - pos: 25.5,14.5 + rot: 3.141592653589793 rad + pos: 28.5,12.5 parent: 1 - uid: 5370 components: @@ -28114,14 +28053,6 @@ entities: rot: -1.5707963267948966 rad pos: 1.5,19.5 parent: 1 -- proto: ComputerMedicalRecords - entities: - - uid: 1871 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -20.5,13.5 - parent: 1 - proto: ComputerPowerMonitoring entities: - uid: 4072 @@ -28129,10 +28060,11 @@ entities: - type: Transform pos: -5.5,31.5 parent: 1 - - uid: 5415 + - uid: 5665 components: - type: Transform - pos: 19.5,-13.5 + rot: 1.5707963267948966 rad + pos: 16.5,-14.5 parent: 1 - uid: 5668 components: @@ -28156,11 +28088,11 @@ entities: rot: 1.5707963267948966 rad pos: -10.5,-3.5 parent: 1 - - uid: 5365 + - uid: 5385 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -22.5,-0.5 + rot: 3.141592653589793 rad + pos: -21.5,-1.5 parent: 1 - proto: ComputerRoboticsControl entities: @@ -28186,6 +28118,14 @@ entities: rot: 3.141592653589793 rad pos: 12.5,-27.5 parent: 1 +- proto: ComputerShuttleSalvage + entities: + - uid: 5666 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 16.5,-23.5 + parent: 1 - proto: ComputerSolarControl entities: - uid: 2557 @@ -28244,6 +28184,12 @@ entities: rot: 3.141592653589793 rad pos: 14.5,12.5 parent: 1 + - uid: 1884 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 27.5,13.5 + parent: 1 - uid: 3794 components: - type: Transform @@ -28266,11 +28212,6 @@ entities: rot: 1.5707963267948966 rad pos: 25.5,6.5 parent: 1 - - uid: 5369 - components: - - type: Transform - pos: 26.5,14.5 - parent: 1 - proto: ComputerTechnologyDiskTerminal entities: - uid: 3822 @@ -28822,6 +28763,39 @@ entities: - type: Transform pos: -41.5,-23.5 parent: 1 +- proto: CurtainsBlueOpen + entities: + - uid: 5391 + components: + - type: Transform + pos: -18.5,16.5 + parent: 1 +- proto: CurtainsOrangeOpen + entities: + - uid: 4933 + components: + - type: Transform + pos: 20.5,-15.5 + parent: 1 + - uid: 5625 + components: + - type: Transform + pos: 6.5,-14.5 + parent: 1 +- proto: CurtainsPurpleOpen + entities: + - uid: 5390 + components: + - type: Transform + pos: -20.5,-3.5 + parent: 1 +- proto: CurtainsRedOpen + entities: + - uid: 5394 + components: + - type: Transform + pos: 25.5,12.5 + parent: 1 - proto: d20Dice entities: - uid: 5591 @@ -32502,6 +32476,48 @@ entities: - type: Transform pos: 5.5,29.5 parent: 1 +- proto: DresserCaptainFilled + entities: + - uid: 7445 + components: + - type: Transform + pos: 4.5,31.5 + parent: 1 +- proto: DresserChiefEngineerFilled + entities: + - uid: 5659 + components: + - type: Transform + pos: 20.5,-14.5 + parent: 1 +- proto: DresserChiefMedicalOfficerFilled + entities: + - uid: 5657 + components: + - type: Transform + pos: -19.5,16.5 + parent: 1 +- proto: DresserHeadOfSecurityFilled + entities: + - uid: 6044 + components: + - type: Transform + pos: 25.5,13.5 + parent: 1 +- proto: DresserQuarterMasterFilled + entities: + - uid: 5373 + components: + - type: Transform + pos: 5.5,-14.5 + parent: 1 +- proto: DresserResearchDirectorFilled + entities: + - uid: 7442 + components: + - type: Transform + pos: -21.5,-3.5 + parent: 1 - proto: Drill entities: - uid: 9639 @@ -38592,10 +38608,14 @@ entities: - uid: 8794 components: - type: Transform + anchored: False pos: -14.5,-45.5 parent: 1 - type: AtmosPipeColor color: '#990000FF' + - type: Physics + canCollide: True + bodyType: Dynamic - uid: 10091 components: - type: Transform @@ -38830,11 +38850,15 @@ entities: - uid: 10130 components: - type: Transform + anchored: False rot: 1.5707963267948966 rad pos: 21.5,-11.5 parent: 1 - type: AtmosPipeColor color: '#990000FF' + - type: Physics + canCollide: True + bodyType: Dynamic - uid: 10131 components: - type: Transform @@ -43075,13 +43099,6 @@ entities: parent: 1 - type: AtmosPipeColor color: '#0055CCFF' - - uid: 10974 - components: - - type: Transform - pos: -4.5,8.5 - parent: 1 - - type: AtmosPipeColor - color: '#0055CCFF' - uid: 10975 components: - type: Transform @@ -44040,11 +44057,15 @@ entities: - uid: 11152 components: - type: Transform + anchored: False rot: 3.141592653589793 rad pos: -37.5,0.5 parent: 1 - type: AtmosPipeColor color: '#0055CCFF' + - type: Physics + canCollide: True + bodyType: Dynamic - uid: 11153 components: - type: Transform @@ -47036,11 +47057,15 @@ entities: - uid: 12253 components: - type: Transform + anchored: False rot: -1.5707963267948966 rad pos: -4.5,56.5 parent: 1 - type: AtmosPipeColor color: '#0055CCFF' + - type: Physics + canCollide: True + bodyType: Dynamic - uid: 12254 components: - type: Transform @@ -54986,27 +55011,11 @@ entities: parent: 1 - proto: HospitalCurtainsOpen entities: - - uid: 794 - components: - - type: Transform - pos: 25.5,12.5 - parent: 1 - uid: 1804 components: - type: Transform pos: 28.5,5.5 parent: 1 - - uid: 1843 - components: - - type: Transform - pos: -20.5,-3.5 - parent: 1 - - uid: 1852 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,16.5 - parent: 1 - uid: 5371 components: - type: Transform @@ -55023,18 +55032,6 @@ entities: rot: -1.5707963267948966 rad pos: 27.5,16.5 parent: 1 - - uid: 5672 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 16.5,-15.5 - parent: 1 - - uid: 7440 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 4.5,-17.5 - parent: 1 - uid: 7718 components: - type: Transform @@ -55745,40 +55742,11 @@ entities: parent: 1 - proto: LockerChiefEngineerFilled entities: - - uid: 5669 + - uid: 5658 components: - type: Transform - pos: 20.5,-15.5 + pos: 20.5,-13.5 parent: 1 - - type: EntityStorage - air: - volume: 200 - immutable: False - temperature: 93.465614 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - type: ContainerContainer - containers: - entity_storage: !type:Container - showEnts: False - occludes: True - ents: - - 5670 - paper_label: !type:ContainerSlot - showEnts: False - occludes: True - ent: null - proto: LockerChiefMedicalOfficerFilled entities: - uid: 1867 @@ -55986,40 +55954,11 @@ entities: ent: null - proto: LockerHeadOfSecurityFilled entities: - - uid: 5374 + - uid: 7441 components: - type: Transform - pos: 29.5,12.5 + pos: 25.5,14.5 parent: 1 - - type: EntityStorage - air: - volume: 200 - immutable: False - temperature: 293.1496 - moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - type: ContainerContainer - containers: - entity_storage: !type:Container - showEnts: False - occludes: True - ents: - - 12478 - paper_label: !type:ContainerSlot - showEnts: False - occludes: True - ent: null - proto: LockerMedicalFilled entities: - uid: 1908 @@ -56056,76 +55995,18 @@ entities: parent: 1 - proto: LockerQuarterMasterFilled entities: - - uid: 7443 + - uid: 5656 components: - type: Transform - pos: 4.5,-16.5 + pos: 4.5,-14.5 parent: 1 - - type: EntityStorage - air: - volume: 200 - immutable: False - temperature: 75.31249 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - type: ContainerContainer - containers: - entity_storage: !type:Container - showEnts: False - occludes: True - ents: - - 7444 - paper_label: !type:ContainerSlot - showEnts: False - occludes: True - ent: null - proto: LockerResearchDirectorFilled entities: - - uid: 1845 + - uid: 7395 components: - type: Transform - pos: -21.5,-3.5 + pos: -22.5,-2.5 parent: 1 - - type: EntityStorage - air: - volume: 200 - immutable: False - temperature: 293.1496 - moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - type: ContainerContainer - containers: - entity_storage: !type:Container - showEnts: False - occludes: True - ents: - - 12418 - paper_label: !type:ContainerSlot - showEnts: False - occludes: True - ent: null - proto: LockerSalvageSpecialistFilled entities: - uid: 3594 @@ -56329,15 +56210,15 @@ entities: parent: 1 - proto: MagazinePistolSubMachineGunTopMounted entities: - - uid: 5386 + - uid: 1865 components: - type: Transform - pos: 27.431211,14.508017 + pos: 27.625902,12.337759 parent: 1 - - uid: 5387 + - uid: 5365 components: - type: Transform - pos: 27.431211,14.367392 + pos: 27.579027,12.494009 parent: 1 - proto: MagazineRifle entities: @@ -57473,12 +57354,6 @@ entities: - type: Transform pos: 5.5,-20.5 parent: 1 - - uid: 7447 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 4.5,-15.5 - parent: 1 - uid: 7782 components: - type: Transform @@ -57540,6 +57415,23 @@ entities: - type: Transform pos: 4.593648,27.643576 parent: 1 +- proto: PaperCargoInvoice + entities: + - uid: 798 + components: + - type: Transform + pos: 4.715157,-17.670078 + parent: 1 + - uid: 1711 + components: + - type: Transform + pos: 4.777657,-17.498203 + parent: 1 + - uid: 5392 + components: + - type: Transform + pos: 4.777657,-17.310703 + parent: 1 - proto: PaperOffice entities: - uid: 3999 @@ -57682,26 +57574,6 @@ entities: - type: Transform pos: -4.5472007,19.595882 parent: 1 - - uid: 5391 - components: - - type: Transform - pos: 27.337461,13.773642 - parent: 1 - - uid: 5392 - components: - - type: Transform - pos: 27.368711,13.758017 - parent: 1 - - uid: 5393 - components: - - type: Transform - pos: 27.415586,13.711142 - parent: 1 - - uid: 5394 - components: - - type: Transform - pos: 27.478086,13.679892 - parent: 1 - uid: 5398 components: - type: Transform @@ -57725,22 +57597,12 @@ entities: - uid: 5678 components: - type: Transform - pos: 18.339497,-14.389538 - parent: 1 - - uid: 5679 - components: - - type: Transform - pos: 18.339497,-14.295788 - parent: 1 - - uid: 5680 - components: - - type: Transform - pos: 18.339497,-14.186413 + pos: 17.441378,-15.404689 parent: 1 - uid: 5681 components: - type: Transform - pos: 18.355122,-14.061413 + pos: 17.332003,-15.56094 parent: 1 - uid: 5875 components: @@ -57872,25 +57734,10 @@ entities: - type: Transform pos: 11.453994,-18.162186 parent: 1 - - uid: 7450 - components: - - type: Transform - pos: 5.1056714,-15.299889 - parent: 1 - - uid: 7451 - components: - - type: Transform - pos: 5.1056714,-15.299889 - parent: 1 - - uid: 7452 - components: - - type: Transform - pos: 5.1056714,-15.299889 - parent: 1 - uid: 7453 components: - type: Transform - pos: 5.1056714,-15.299889 + pos: 17.566378,-15.264064 parent: 1 - uid: 8247 components: @@ -58038,15 +57885,10 @@ entities: parent: 1 - proto: Pen entities: - - uid: 1881 - components: - - type: Transform - pos: -19.882591,14.621025 - parent: 1 - - uid: 1882 + - uid: 1780 components: - type: Transform - pos: -21.958109,-1.4018333 + pos: -22.696238,-1.2729604 parent: 1 - uid: 3995 components: @@ -58092,11 +57934,10 @@ entities: rot: -1.5707963267948966 rad pos: 25.773567,10.670956 parent: 1 - - uid: 5395 + - uid: 5386 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 27.587461,13.804892 + pos: -19.937119,13.673687 parent: 1 - uid: 5402 components: @@ -58119,11 +57960,10 @@ entities: - type: Transform pos: -39.229885,-5.2659664 parent: 1 - - uid: 5682 + - uid: 5675 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 18.651997,-14.061413 + pos: 17.097628,-15.217189 parent: 1 - uid: 5879 components: @@ -58151,10 +57991,10 @@ entities: - type: Transform pos: 5.313369,-21.14656 parent: 1 - - uid: 7454 + - uid: 7451 components: - type: Transform - pos: 5.6369214,-15.503014 + pos: 4.230782,-17.216953 parent: 1 - uid: 9046 components: @@ -62393,32 +62233,32 @@ entities: parent: 1 - proto: RCD entities: - - uid: 7135 + - uid: 5679 components: - type: Transform - pos: 31.574986,-16.418343 + pos: 16.410128,-15.420314 parent: 1 - - uid: 12468 + - uid: 7135 components: - type: Transform - pos: 18.51267,-13.338883 + pos: 31.574986,-16.418343 parent: 1 - proto: RCDAmmo entities: - - uid: 7136 + - uid: 5663 components: - type: Transform - pos: 31.27811,-16.605843 + pos: 16.878878,-15.467189 parent: 1 - - uid: 12469 + - uid: 5680 components: - type: Transform - pos: 18.278296,-13.526383 + pos: 17.050753,-15.467189 parent: 1 - - uid: 12470 + - uid: 7136 components: - type: Transform - pos: 18.434546,-13.526383 + pos: 31.27811,-16.605843 parent: 1 - proto: ReagentContainerFlour entities: @@ -64453,10 +64293,10 @@ entities: parent: 1 - proto: ResearchAndDevelopmentServer entities: - - uid: 1848 + - uid: 4973 components: - type: Transform - pos: -22.5,-3.5 + pos: -22.5,-0.5 parent: 1 - proto: RightArmBorg entities: @@ -65035,7 +64875,7 @@ entities: parent: 1 - type: DeviceLinkSink links: - - 5671 + - 5682 - uid: 5684 components: - type: Transform @@ -65043,7 +64883,7 @@ entities: parent: 1 - type: DeviceLinkSink links: - - 5671 + - 5682 - uid: 5685 components: - type: Transform @@ -65051,7 +64891,7 @@ entities: parent: 1 - type: DeviceLinkSink links: - - 5671 + - 5682 - uid: 5686 components: - type: Transform @@ -65059,15 +64899,12 @@ entities: parent: 1 - type: DeviceLinkSink links: - - 5671 + - 5682 - uid: 5687 components: - type: Transform pos: 21.5,-15.5 parent: 1 - - type: DeviceLinkSink - links: - - 5671 - uid: 5887 components: - type: Transform @@ -65455,16 +65292,14 @@ entities: - Pressed: Toggle 1866: - Pressed: Toggle - - uid: 5671 + - uid: 5682 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 21.5,-14.5 + rot: 3.141592653589793 rad + pos: 19.5,-16.5 parent: 1 - type: DeviceLinkSource linkedPorts: - 5687: - - Pressed: Toggle 5685: - Pressed: Toggle 5684: @@ -66405,7 +66240,7 @@ entities: - type: Transform pos: -43.500492,24.479343 parent: 1 -- proto: soda_dispenser +- proto: SodaDispenser entities: - uid: 5954 components: @@ -66949,10 +66784,11 @@ entities: parent: 1 - proto: SpawnMobShiva entities: - - uid: 8256 + - uid: 7440 components: - type: Transform - pos: 25.5,13.5 + rot: 3.141592653589793 rad + pos: 26.5,12.5 parent: 1 - proto: SpawnMobSmile entities: @@ -67055,10 +66891,10 @@ entities: parent: 1 - proto: SpawnPointChiefMedicalOfficer entities: - - uid: 1870 + - uid: 1843 components: - type: Transform - pos: -19.5,13.5 + pos: -19.5,14.5 parent: 1 - proto: SpawnPointClown entities: @@ -67083,10 +66919,10 @@ entities: parent: 1 - proto: SpawnPointHeadOfSecurity entities: - - uid: 6044 + - uid: 7448 components: - type: Transform - pos: 26.5,13.5 + pos: 28.5,13.5 parent: 1 - proto: SpawnPointJanitor entities: @@ -67195,10 +67031,10 @@ entities: parent: 1 - proto: SpawnPointQuartermaster entities: - - uid: 7477 + - uid: 5375 components: - type: Transform - pos: 5.5,-14.5 + pos: 6.5,-15.5 parent: 1 - proto: SpawnPointResearchAssistant entities: @@ -67671,17 +67507,17 @@ entities: parent: 1 - proto: SuitStorageCE entities: - - uid: 4933 + - uid: 7477 components: - type: Transform - pos: 20.5,-13.5 + pos: 18.5,-15.5 parent: 1 - proto: SuitStorageCMO entities: - - uid: 1865 + - uid: 5374 components: - type: Transform - pos: -20.5,15.5 + pos: -18.5,13.5 parent: 1 - proto: SuitStorageEngi entities: @@ -67736,17 +67572,17 @@ entities: parent: 1 - proto: SuitStorageHOS entities: - - uid: 4973 + - uid: 1864 components: - type: Transform - pos: 29.5,14.5 + pos: 29.5,12.5 parent: 1 - proto: SuitStorageRD entities: - - uid: 4929 + - uid: 5654 components: - type: Transform - pos: -22.5,-2.5 + pos: -22.5,-3.5 parent: 1 - proto: SuitStorageSalv entities: @@ -69595,11 +69431,6 @@ entities: rot: 1.5707963267948966 rad pos: -1.5,7.5 parent: 1 - - uid: 798 - components: - - type: Transform - pos: 27.5,13.5 - parent: 1 - uid: 1256 components: - type: Transform @@ -69624,11 +69455,6 @@ entities: rot: 1.5707963267948966 rad pos: -37.5,-4.5 parent: 1 - - uid: 1780 - components: - - type: Transform - pos: 27.5,14.5 - parent: 1 - uid: 1802 components: - type: Transform @@ -69639,22 +69465,11 @@ entities: - type: Transform pos: 28.5,16.5 parent: 1 - - uid: 1844 - components: - - type: Transform - pos: -21.5,-1.5 - parent: 1 - - uid: 1863 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -20.5,14.5 - parent: 1 - - uid: 1864 + - uid: 1882 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -19.5,14.5 + rot: 3.141592653589793 rad + pos: 4.5,-17.5 parent: 1 - uid: 2782 components: @@ -69749,12 +69564,6 @@ entities: rot: 3.141592653589793 rad pos: -9.5,28.5 parent: 1 - - uid: 4091 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 4.5,31.5 - parent: 1 - uid: 4092 components: - type: Transform @@ -69842,6 +69651,12 @@ entities: rot: 3.141592653589793 rad pos: 46.5,31.5 parent: 1 + - uid: 4929 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.5,13.5 + parent: 1 - uid: 4956 components: - type: Transform @@ -69898,17 +69713,16 @@ entities: - type: Transform pos: -22.5,-1.5 parent: 1 - - uid: 5665 + - uid: 5387 components: - type: Transform - rot: 3.141592653589793 rad - pos: 18.5,-13.5 + pos: 27.5,12.5 parent: 1 - - uid: 5666 + - uid: 5670 components: - type: Transform - rot: 3.141592653589793 rad - pos: 18.5,-14.5 + rot: 1.5707963267948966 rad + pos: 17.5,-15.5 parent: 1 - uid: 5947 components: @@ -69922,17 +69736,17 @@ entities: rot: 3.141592653589793 rad pos: -0.5,5.5 parent: 1 - - uid: 7445 + - uid: 7247 components: - type: Transform - rot: 3.141592653589793 rad - pos: 4.5,-15.5 + rot: 1.5707963267948966 rad + pos: 16.5,-15.5 parent: 1 - - uid: 7446 + - uid: 7443 components: - type: Transform - rot: 3.141592653589793 rad - pos: 5.5,-15.5 + rot: 1.5707963267948966 rad + pos: -20.5,13.5 parent: 1 - uid: 7710 components: @@ -70362,6 +70176,13 @@ entities: - type: Transform pos: -6.5061026,52.63185 parent: 1 +- proto: ToyFigurineChiefMedicalOfficer + entities: + - uid: 7449 + components: + - type: Transform + pos: -20.374619,13.548687 + parent: 1 - proto: ToyFireRipley entities: - uid: 8259 @@ -80010,10 +79831,10 @@ entities: parent: 1 - proto: WeaponSubMachineGunWt550 entities: - - uid: 5385 + - uid: 1710 components: - type: Transform - pos: 27.524961,14.648642 + pos: 27.516527,12.712759 parent: 1 - proto: WeaponTurretSyndicateBroken entities: From ca9830a2842b4889a20825d9d34da4c6fe891086 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 5 Jun 2024 17:51:47 +0000 Subject: [PATCH 302/568] 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 574ea1adb35b..851ebffc64dc 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Plykiya - changes: - - message: Lone operatives now start with 60TC instead of 40TC. - type: Tweak - id: 6183 - time: '2024-03-18T14:15:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26130 - author: PJB3005 changes: - message: Fixed hardsuits in space causing high pressure damage @@ -3849,3 +3842,10 @@ id: 6682 time: '2024-06-04T18:48:24.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28590 +- author: Boaz1111 + changes: + - message: Revamped Cluster's Head Offices + type: Tweak + id: 6683 + time: '2024-06-05T17:50:38.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28627 From 192a3187dfb80cbfec423a7a9757342dec08ceba Mon Sep 17 00:00:00 2001 From: Hmeister-real <118129069+Hmeister-real@users.noreply.github.com> Date: Wed, 5 Jun 2024 21:10:27 +0100 Subject: [PATCH 303/568] minor banner changes (#28636) * minor banner changes * Uhrmm actchually it's you're, not your * Update banners.yml props Hyenh --- .../Prototypes/Entities/Structures/Decoration/banners.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Decoration/banners.yml b/Resources/Prototypes/Entities/Structures/Decoration/banners.yml index cc0c90503d0f..c2567814ac11 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/banners.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/banners.yml @@ -91,7 +91,7 @@ id: BannerScience parent: BannerBase name: science banner - description: A banner displaying the colors of the science department. Where stupidity is proven greater than the universe. + description: A banner displaying the colors of the science department. Where science has no bounds, and regulations are rarely followed. components: - type: Sprite sprite: Structures/Decoration/banner.rsi @@ -101,7 +101,7 @@ id: BannerSecurity parent: BannerBase name: security banner - description: A banner displaying the colors of the shitcurity department. Security, my bad. + description: A banner displaying the colors of the security department. You're surprised it's not vandalised. components: - type: Sprite sprite: Structures/Decoration/banner.rsi From 4c0a878f6feccc8282d0d7acbe37de5c3e9f4528 Mon Sep 17 00:00:00 2001 From: lapatison <100279397+lapatison@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:10:58 +0300 Subject: [PATCH 304/568] Revenant spell catalog locale (#28638) locale --- .../Locale/en-US/store/revenant-catalog.ftl | 11 +++++++++++ .../Prototypes/Catalog/revenant_catalog.yml | 16 ++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 Resources/Locale/en-US/store/revenant-catalog.ftl diff --git a/Resources/Locale/en-US/store/revenant-catalog.ftl b/Resources/Locale/en-US/store/revenant-catalog.ftl new file mode 100644 index 000000000000..d3cbbf724fc8 --- /dev/null +++ b/Resources/Locale/en-US/store/revenant-catalog.ftl @@ -0,0 +1,11 @@ +revenant-defile-name = Defile +revenant-defile-desc = Defiles the surrounding area, ripping up floors, damaging windows, opening containers, and throwing items. Using it leaves you vulnerable to attacks for a short period of time. + +revenant-overload-name = Overload Lights +revenant-overload-desc = Overloads all nearby lights, causing lights to pulse and sending out dangerous lightning. Using it leaves you vulnerable to attacks for a long period of time. + +revenant-blight-name = Blight +revenant-blight-desc = Infects all nearby organisms with an infectious disease that causes toxic buildup and tiredness. Using it leaves you vulnerable to attacks for a medium period of time. + +revenant-malfunction-name = Malfunction +revenant-malfunction-desc = Makes nearby electronics stop working properly. Using it leaves you vulnerable to attacks for a long period of time. diff --git a/Resources/Prototypes/Catalog/revenant_catalog.yml b/Resources/Prototypes/Catalog/revenant_catalog.yml index 84f45d16073b..ec1ef0a309b1 100644 --- a/Resources/Prototypes/Catalog/revenant_catalog.yml +++ b/Resources/Prototypes/Catalog/revenant_catalog.yml @@ -1,7 +1,7 @@ - type: listing id: RevenantDefile - name: Defile - description: Defiles the surrounding area, ripping up floors, damaging windows, opening containers, and throwing items. Using it leaves you vulnerable to attacks for a short period of time. + name: revenant-defile-name + description: revenant-defile-desc productAction: ActionRevenantDefile cost: StolenEssence: 10 @@ -13,8 +13,8 @@ - type: listing id: RevenantOverloadLights - name: Overload Lights - description: Overloads all nearby lights, causing lights to pulse and sending out dangerous lightning. Using it leaves you vulnerable to attacks for a long period of time. + name: revenant-overload-name + description: revenant-overload-desc productAction: ActionRevenantOverloadLights cost: StolenEssence: 25 @@ -26,8 +26,8 @@ #- type: listing # id: RevenantBlight -# name: Blight -# description: Infects all nearby organisms with an infectious disease that causes toxic buildup and tiredness. Using it leaves you vulnerable to attacks for a medium period of time. +# name: revenant-blight-name +# description: revenant-blight-desc # productAction: ActionRevenantBlight # cost: # StolenEssence: 75 @@ -39,8 +39,8 @@ - type: listing id: RevenantMalfunction - name: Malfunction - description: Makes nearby electronics stop working properly. Using it leaves you vulnerable to attacks for a long period of time. + name: revenant-malfunction-name + description: revenant-malfunction-desc productAction: ActionRevenantMalfunction cost: StolenEssence: 125 From d75de9fa248cce1a1670616ba0af5a0f4759ce51 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:14:56 -0700 Subject: [PATCH 305/568] Make cuff default range again (#28576) * Make cuff default range again * uncuff distance * how about ONE --------- Co-authored-by: plykiya --- Content.Shared/Cuffs/SharedCuffableSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index f0f9a949839a..1ced3c8d6cb8 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -484,7 +484,7 @@ public bool TryCuffing(EntityUid user, EntityUid target, EntityUid handcuff, Han BreakOnWeightlessMove = false, BreakOnDamage = true, NeedHand = true, - DistanceThreshold = 0.3f + DistanceThreshold = 1f // shorter than default but still feels good }; if (!_doAfter.TryStartDoAfter(doAfterEventArgs)) @@ -581,7 +581,7 @@ public void TryUncuff(EntityUid target, EntityUid user, EntityUid? cuffsToRemove BreakOnDamage = true, NeedHand = true, RequireCanInteract = false, // Trust in UncuffAttemptEvent - DistanceThreshold = 0.3f + DistanceThreshold = 1f // shorter than default but still feels good }; if (!_doAfter.TryStartDoAfter(doAfterEventArgs)) From 8a7b0f675e1cf140f28cb6f0bb2d15c3fca9a14f Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 5 Jun 2024 20:16:02 +0000 Subject: [PATCH 306/568] 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 851ebffc64dc..e68ab9517d3d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: PJB3005 - changes: - - message: Fixed hardsuits in space causing high pressure damage - type: Fix - id: 6184 - time: '2024-03-18T16:46:31.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26236 - author: potato1234x changes: - message: Added crafting recipes for wall lockers and secure lockers @@ -3849,3 +3842,10 @@ id: 6683 time: '2024-06-05T17:50:38.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28627 +- author: Plykiya + changes: + - message: Handcuff range is buffed to one tile of distance. + type: Tweak + id: 6684 + time: '2024-06-05T20:14:56.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28576 From 729e67af7f2892644d758c6ade5c9abc4ac6c66e Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Wed, 5 Jun 2024 16:23:23 -0400 Subject: [PATCH 307/568] Machine-code cleanup (#28489) --- .../UI/FlatpackCreatorMenu.xaml.cs | 3 +- .../Tests/MachineBoardTest.cs | 41 +- .../Tests/MaterialArbitrageTest.cs | 3 +- .../Construction/Components/IRefreshParts.cs | 10 - .../Components/MachineComponent.cs | 34 +- .../Components/MachineFrameComponent.cs | 19 +- .../Components/PartExchangerComponent.cs | 29 -- .../Conditions/MachineFrameComplete.cs | 28 +- .../Construction/ConstructionSystem.Graph.cs | 8 +- .../Construction/ConstructionSystem.Guided.cs | 6 +- .../ConstructionSystem.Initial.cs | 8 +- .../ConstructionSystem.Machine.cs | 176 +-------- .../Construction/ConstructionSystem.cs | 1 - .../Construction/MachineFrameSystem.cs | 128 +------ .../Construction/PartExchangerSystem.cs | 180 --------- Content.Server/Stack/StackSystem.cs | 9 + .../Components/MachineBoardComponent.cs | 89 ++--- .../Components/MachinePartComponent.cs | 24 -- .../Construction/MachinePartSystem.cs | 46 +-- .../Prototypes/MachinePartPrototype.cs | 28 -- .../Construction/SharedConstructionSystem.cs | 11 + .../Construction/SharedFlatpackSystem.cs | 9 +- Content.Shared/Lathe/LatheComponent.cs | 2 - ...bitrary-insert-construction-graph-step.ftl | 10 +- .../Devices/Circuitboards/Machine/cannons.yml | 17 +- .../Machine/particle_accelerator.yml | 17 +- .../Circuitboards/Machine/production.yml | 352 +++++++----------- .../Entities/Objects/Misc/machine_parts.yml | 11 +- .../Entities/Objects/Power/powercells.yml | 12 - .../Prototypes/MachineParts/machine_parts.yml | 20 - .../Prototypes/Stacks/science_stacks.yml | 2 +- 31 files changed, 352 insertions(+), 981 deletions(-) delete mode 100644 Content.Server/Construction/Components/IRefreshParts.cs delete mode 100644 Content.Server/Construction/Components/PartExchangerComponent.cs delete mode 100644 Content.Server/Construction/PartExchangerSystem.cs delete mode 100644 Content.Shared/Construction/Components/MachinePartComponent.cs delete mode 100644 Content.Shared/Construction/Prototypes/MachinePartPrototype.cs delete mode 100644 Resources/Prototypes/MachineParts/machine_parts.yml diff --git a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs index ad19bc30f42f..002616323784 100644 --- a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs +++ b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs @@ -75,8 +75,7 @@ protected override void FrameUpdate(FrameEventArgs args) else if (_currentBoard != null) { Dictionary cost; - if (_entityManager.TryGetComponent(_currentBoard, out machineBoardComp) && - machineBoardComp.Prototype is not null) + if (_entityManager.TryGetComponent(_currentBoard, out machineBoardComp)) cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoardComp)); else cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker)); diff --git a/Content.IntegrationTests/Tests/MachineBoardTest.cs b/Content.IntegrationTests/Tests/MachineBoardTest.cs index bd3a72f4c1d1..e741935be340 100644 --- a/Content.IntegrationTests/Tests/MachineBoardTest.cs +++ b/Content.IntegrationTests/Tests/MachineBoardTest.cs @@ -2,6 +2,8 @@ using System.Linq; using Content.Server.Construction.Components; using Content.Shared.Construction.Components; +using Content.Shared.Prototypes; +using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; @@ -49,12 +51,11 @@ await server.WaitAssertion(() => Assert.Multiple(() => { - Assert.That(mId, Is.Not.Null, $"Machine board {p.ID} does not have a corresponding machine."); Assert.That(protoMan.TryIndex(mId, out var mProto), $"Machine board {p.ID}'s corresponding machine has an invalid prototype."); Assert.That(mProto.TryGetComponent(out var mComp), $"Machine board {p.ID}'s corresponding machine {mId} does not have MachineComponent"); - Assert.That(mComp.BoardPrototype, Is.EqualTo(p.ID), + Assert.That(mComp.Board, Is.EqualTo(p.ID), $"Machine {mId}'s BoardPrototype is not equal to it's corresponding machine board, {p.ID}"); }); } @@ -101,4 +102,40 @@ await server.WaitAssertion(() => await pair.CleanReturnAsync(); } + + /// + /// Ensures that every single computer board's corresponding entity + /// is a computer that can be properly deconstructed to the correct board + /// + [Test] + public async Task TestValidateBoardComponentRequirements() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entMan = server.ResolveDependency(); + var protoMan = server.ResolveDependency(); + + await server.WaitAssertion(() => + { + foreach (var p in protoMan.EnumeratePrototypes() + .Where(p => !p.Abstract) + .Where(p => !pair.IsTestPrototype(p)) + .Where(p => !_ignoredPrototypes.Contains(p.ID))) + { + if (!p.TryGetComponent(out var board, entMan.ComponentFactory)) + continue; + + Assert.Multiple(() => + { + foreach (var component in board.ComponentRequirements.Keys) + { + Assert.That(entMan.ComponentFactory.TryGetRegistration(component, out _), $"Invalid component requirement {component} specified on machine board entity {p}"); + } + }); + } + }); + + await pair.CleanReturnAsync(); + } } diff --git a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs index ed1e5549438f..19780591bdbc 100644 --- a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs +++ b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs @@ -60,7 +60,8 @@ public async Task NoMaterialArbitrage() } // Lets assume the possible lathe for resource multipliers: - var multiplier = MathF.Pow(LatheComponent.DefaultPartRatingMaterialUseMultiplier, MachinePartComponent.MaxRating - 1); + // TODO: each recipe can technically have its own cost multiplier associated with it, so this test needs redone to factor that in. + var multiplier = MathF.Pow(0.85f, 3); // create construction dictionary Dictionary constructionRecipes = new(); diff --git a/Content.Server/Construction/Components/IRefreshParts.cs b/Content.Server/Construction/Components/IRefreshParts.cs deleted file mode 100644 index 714ca72c971b..000000000000 --- a/Content.Server/Construction/Components/IRefreshParts.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Content.Shared.Construction.Components; - -namespace Content.Server.Construction.Components -{ - [RequiresExplicitImplementation] - public interface IRefreshParts - { - void RefreshParts(IEnumerable parts); - } -} diff --git a/Content.Server/Construction/Components/MachineComponent.cs b/Content.Server/Construction/Components/MachineComponent.cs index 42c85e6d0b55..8a0f117712e8 100644 --- a/Content.Server/Construction/Components/MachineComponent.cs +++ b/Content.Server/Construction/Components/MachineComponent.cs @@ -1,27 +1,17 @@ -using Robust.Shared.Containers; +using Content.Shared.Construction.Components; +using Robust.Shared.Containers; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Server.Construction.Components -{ - [RegisterComponent, ComponentProtoName("Machine")] - public sealed partial class MachineComponent : Component - { - [DataField("board", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? BoardPrototype { get; private set; } +namespace Content.Server.Construction.Components; - [ViewVariables] - public Container BoardContainer = default!; - [ViewVariables] - public Container PartContainer = default!; - } +[RegisterComponent] +public sealed partial class MachineComponent : Component +{ + [DataField] + public EntProtoId? Board { get; private set; } - /// - /// The different types of scaling that are available for machine upgrades - /// - public enum MachineUpgradeScalingType : byte - { - Linear, - Exponential - } + [ViewVariables] + public Container BoardContainer = default!; + [ViewVariables] + public Container PartContainer = default!; } diff --git a/Content.Server/Construction/Components/MachineFrameComponent.cs b/Content.Server/Construction/Components/MachineFrameComponent.cs index 75cc486f2408..14e81e8dd32e 100644 --- a/Content.Server/Construction/Components/MachineFrameComponent.cs +++ b/Content.Server/Construction/Components/MachineFrameComponent.cs @@ -1,7 +1,8 @@ using Content.Shared.Construction.Components; -using Content.Shared.Construction.Prototypes; +using Content.Shared.Stacks; +using Content.Shared.Tag; using Robust.Shared.Containers; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; +using Robust.Shared.Prototypes; namespace Content.Server.Construction.Components { @@ -14,29 +15,23 @@ public sealed partial class MachineFrameComponent : Component [ViewVariables] public bool HasBoard => BoardContainer?.ContainedEntities.Count != 0; - [DataField("progress", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public Dictionary Progress = new(); - [ViewVariables] - public readonly Dictionary MaterialProgress = new(); + public readonly Dictionary, int> MaterialProgress = new(); [ViewVariables] public readonly Dictionary ComponentProgress = new(); [ViewVariables] - public readonly Dictionary TagProgress = new(); - - [DataField("requirements", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public Dictionary Requirements = new(); + public readonly Dictionary, int> TagProgress = new(); [ViewVariables] - public Dictionary MaterialRequirements = new(); + public Dictionary, int> MaterialRequirements = new(); [ViewVariables] public Dictionary ComponentRequirements = new(); [ViewVariables] - public Dictionary TagRequirements = new(); + public Dictionary, GenericPartInfo> TagRequirements = new(); [ViewVariables] public Container BoardContainer = default!; diff --git a/Content.Server/Construction/Components/PartExchangerComponent.cs b/Content.Server/Construction/Components/PartExchangerComponent.cs deleted file mode 100644 index a2579c92e803..000000000000 --- a/Content.Server/Construction/Components/PartExchangerComponent.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Robust.Shared.Audio; - -namespace Content.Server.Construction.Components; - -[RegisterComponent] -public sealed partial class PartExchangerComponent : Component -{ - /// - /// How long it takes to exchange the parts - /// - [DataField("exchangeDuration")] - public float ExchangeDuration = 3; - - /// - /// Whether or not the distance check is needed. - /// Good for BRPED. - /// - /// - /// I fucking hate BRPED and if you ever add it - /// i will personally kill your dog. - /// - [DataField("doDistanceCheck")] - public bool DoDistanceCheck = true; - - [DataField("exchangeSound")] - public SoundSpecifier ExchangeSound = new SoundPathSpecifier("/Audio/Items/rped.ogg"); - - public EntityUid? AudioStream; -} diff --git a/Content.Server/Construction/Conditions/MachineFrameComplete.cs b/Content.Server/Construction/Conditions/MachineFrameComplete.cs index fbba5eca996f..33174bb9fb0b 100644 --- a/Content.Server/Construction/Conditions/MachineFrameComplete.cs +++ b/Content.Server/Construction/Conditions/MachineFrameComplete.cs @@ -2,6 +2,7 @@ using Content.Shared.Construction; using Content.Shared.Examine; using JetBrains.Annotations; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Construction.Conditions @@ -17,7 +18,7 @@ public sealed partial class MachineFrameComplete : IGraphCondition public SpriteSpecifier? GuideIconBoard { get; private set; } [DataField("guideIconParts")] - public SpriteSpecifier? GuideIconPart { get; private set; } + public SpriteSpecifier? GuideIconParts { get; private set; } public bool Condition(EntityUid uid, IEntityManager entityManager) @@ -33,6 +34,8 @@ public bool DoExamine(ExaminedEvent args) var entity = args.Examined; var entityManager = IoCManager.Resolve(); + var protoManager = IoCManager.Resolve(); + var constructionSys = entityManager.System(); if (!entityManager.TryGetComponent(entity, out MachineFrameComponent? machineFrame)) return false; @@ -47,17 +50,6 @@ public bool DoExamine(ExaminedEvent args) return false; args.PushMarkup(Loc.GetString("construction-condition-machine-frame-requirement-label")); - foreach (var (part, required) in machineFrame.Requirements) - { - var amount = required - machineFrame.Progress[part]; - - if(amount == 0) - continue; - - args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry", - ("amount", amount), - ("elementName", Loc.GetString(part)))); - } foreach (var (material, required) in machineFrame.MaterialRequirements) { @@ -65,10 +57,12 @@ public bool DoExamine(ExaminedEvent args) if(amount == 0) continue; + var stack = protoManager.Index(material); + var stackEnt = protoManager.Index(stack.Spawn); args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry", ("amount", amount), - ("elementName", Loc.GetString(material)))); + ("elementName", stackEnt.Name))); } foreach (var (compName, info) in machineFrame.ComponentRequirements) @@ -78,9 +72,10 @@ public bool DoExamine(ExaminedEvent args) if(amount == 0) continue; + var examineName = constructionSys.GetExamineName(info); args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry", ("amount", info.Amount), - ("elementName", Loc.GetString(info.ExamineName)))); + ("elementName", examineName))); } foreach (var (tagName, info) in machineFrame.TagRequirements) @@ -90,9 +85,10 @@ public bool DoExamine(ExaminedEvent args) if(amount == 0) continue; + var examineName = constructionSys.GetExamineName(info); args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry", ("amount", info.Amount), - ("elementName", Loc.GetString(info.ExamineName))) + ("elementName", examineName)) + "\n"); } @@ -111,7 +107,7 @@ public IEnumerable GenerateGuideEntry() yield return new ConstructionGuideEntry() { Localization = "construction-step-condition-machine-frame-parts", - Icon = GuideIconPart, + Icon = GuideIconParts, EntryNumber = 0, // Set this to anything so the guide generation takes this as a numbered step. }; } diff --git a/Content.Server/Construction/ConstructionSystem.Graph.cs b/Content.Server/Construction/ConstructionSystem.Graph.cs index 570360bf09a3..1b74fd9d4e56 100644 --- a/Content.Server/Construction/ConstructionSystem.Graph.cs +++ b/Content.Server/Construction/ConstructionSystem.Graph.cs @@ -50,7 +50,7 @@ public bool AddContainer(EntityUid uid, string container, ConstructionComponent? // If the set graph prototype does not exist, also return null. This could be due to admemes changing values // in ViewVariables, so even though the construction state is invalid, just return null. - return _prototypeManager.TryIndex(construction.Graph, out ConstructionGraphPrototype? graph) ? graph : null; + return PrototypeManager.TryIndex(construction.Graph, out ConstructionGraphPrototype? graph) ? graph : null; } /// @@ -300,7 +300,7 @@ public bool ChangeNode(EntityUid uid, EntityUid? userUid, string id, bool perfor } // Exit if the new entity's prototype is the same as the original, or the prototype is invalid - if (newEntity == metaData.EntityPrototype?.ID || !_prototypeManager.HasIndex(newEntity)) + if (newEntity == metaData.EntityPrototype?.ID || !PrototypeManager.HasIndex(newEntity)) return null; // [Optional] Exit if the new entity's prototype is a parent of the original @@ -310,7 +310,7 @@ public bool ChangeNode(EntityUid uid, EntityUid? userUid, string id, bool perfor if (GetCurrentNode(uid, construction)?.DoNotReplaceInheritingEntities == true && metaData.EntityPrototype?.ID != null) { - var parents = _prototypeManager.EnumerateParents(metaData.EntityPrototype.ID)?.ToList(); + var parents = PrototypeManager.EnumerateParents(metaData.EntityPrototype.ID)?.ToList(); if (parents != null && parents.Any(x => x.ID == newEntity)) return null; @@ -427,7 +427,7 @@ public bool ChangeGraph(EntityUid uid, EntityUid? userUid, string graphId, strin if (!Resolve(uid, ref construction)) return false; - if (!_prototypeManager.TryIndex(graphId, out var graph)) + if (!PrototypeManager.TryIndex(graphId, out var graph)) return false; if(GetNodeFromGraph(graph, nodeId) is not {}) diff --git a/Content.Server/Construction/ConstructionSystem.Guided.cs b/Content.Server/Construction/ConstructionSystem.Guided.cs index e096bc02c315..157e4211586a 100644 --- a/Content.Server/Construction/ConstructionSystem.Guided.cs +++ b/Content.Server/Construction/ConstructionSystem.Guided.cs @@ -25,7 +25,7 @@ private void InitializeGuided() private void OnGuideRequested(RequestConstructionGuide msg, EntitySessionEventArgs args) { - if (!_prototypeManager.TryIndex(msg.ConstructionId, out ConstructionPrototype? prototype)) + if (!PrototypeManager.TryIndex(msg.ConstructionId, out ConstructionPrototype? prototype)) return; if(GetGuide(prototype) is {} guide) @@ -41,7 +41,7 @@ private void AddDeconstructVerb(EntityUid uid, ConstructionComponent component, component.Node == component.DeconstructionNode) return; - if (!_prototypeManager.TryIndex(component.Graph, out ConstructionGraphPrototype? graph)) + if (!PrototypeManager.TryIndex(component.Graph, out ConstructionGraphPrototype? graph)) return; if (component.DeconstructionNode == null) @@ -145,7 +145,7 @@ private void HandleConstructionExamined(EntityUid uid, ConstructionComponent com return guide; // If the graph doesn't actually exist, do nothing. - if (!_prototypeManager.TryIndex(construction.Graph, out ConstructionGraphPrototype? graph)) + if (!PrototypeManager.TryIndex(construction.Graph, out ConstructionGraphPrototype? graph)) return null; // If either the start node or the target node are missing, do nothing. diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index 5161ac358ac9..08dd139ef4ce 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -325,13 +325,13 @@ private async void HandleStartItemConstruction(TryStartItemConstructionMessage e // LEGACY CODE. See warning at the top of the file! public async Task TryStartItemConstruction(string prototype, EntityUid user) { - if (!_prototypeManager.TryIndex(prototype, out ConstructionPrototype? constructionPrototype)) + if (!PrototypeManager.TryIndex(prototype, out ConstructionPrototype? constructionPrototype)) { Log.Error($"Tried to start construction of invalid recipe '{prototype}'!"); return false; } - if (!_prototypeManager.TryIndex(constructionPrototype.Graph, + if (!PrototypeManager.TryIndex(constructionPrototype.Graph, out ConstructionGraphPrototype? constructionGraph)) { Log.Error( @@ -404,14 +404,14 @@ public async Task TryStartItemConstruction(string prototype, EntityUid use // LEGACY CODE. See warning at the top of the file! private async void HandleStartStructureConstruction(TryStartStructureConstructionMessage ev, EntitySessionEventArgs args) { - if (!_prototypeManager.TryIndex(ev.PrototypeName, out ConstructionPrototype? constructionPrototype)) + if (!PrototypeManager.TryIndex(ev.PrototypeName, out ConstructionPrototype? constructionPrototype)) { Log.Error($"Tried to start construction of invalid recipe '{ev.PrototypeName}'!"); RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack)); return; } - if (!_prototypeManager.TryIndex(constructionPrototype.Graph, out ConstructionGraphPrototype? constructionGraph)) + if (!PrototypeManager.TryIndex(constructionPrototype.Graph, out ConstructionGraphPrototype? constructionGraph)) { Log.Error($"Invalid construction graph '{constructionPrototype.Graph}' in recipe '{ev.PrototypeName}'!"); RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack)); diff --git a/Content.Server/Construction/ConstructionSystem.Machine.cs b/Content.Server/Construction/ConstructionSystem.Machine.cs index 2e670dbe40d4..eb922f198c7b 100644 --- a/Content.Server/Construction/ConstructionSystem.Machine.cs +++ b/Content.Server/Construction/ConstructionSystem.Machine.cs @@ -1,23 +1,15 @@ -using System.Linq; using Content.Server.Construction.Components; -using Content.Server.Examine; using Content.Shared.Construction.Components; -using Content.Shared.Construction.Prototypes; -using Content.Shared.Verbs; using Robust.Shared.Containers; -using Robust.Shared.Utility; namespace Content.Server.Construction; public sealed partial class ConstructionSystem { - [Dependency] private readonly ExamineSystem _examineSystem = default!; - private void InitializeMachines() { SubscribeLocalEvent(OnMachineInit); SubscribeLocalEvent(OnMachineMapInit); - SubscribeLocalEvent>(OnMachineExaminableVerb); } private void OnMachineInit(EntityUid uid, MachineComponent component, ComponentInit args) @@ -29,84 +21,6 @@ private void OnMachineInit(EntityUid uid, MachineComponent component, ComponentI private void OnMachineMapInit(EntityUid uid, MachineComponent component, MapInitEvent args) { CreateBoardAndStockParts(uid, component); - RefreshParts(uid, component); - } - - private void OnMachineExaminableVerb(EntityUid uid, MachineComponent component, GetVerbsEvent args) - { - if (!args.CanInteract || !args.CanAccess) - return; - - var markup = new FormattedMessage(); - RaiseLocalEvent(uid, new UpgradeExamineEvent(ref markup)); - if (markup.IsEmpty) - return; // Not upgradable. - - markup = FormattedMessage.FromMarkup(markup.ToMarkup().TrimEnd('\n')); // Cursed workaround to https://github.com/space-wizards/RobustToolbox/issues/3371 - - var verb = new ExamineVerb() - { - Act = () => - { - _examineSystem.SendExamineTooltip(args.User, uid, markup, getVerbs: false, centerAtCursor: false); - }, - Text = Loc.GetString("machine-upgrade-examinable-verb-text"), - Message = Loc.GetString("machine-upgrade-examinable-verb-message"), - Category = VerbCategory.Examine, - Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/pickup.svg.192dpi.png")) - }; - - args.Verbs.Add(verb); - } - - public List GetAllParts(EntityUid uid, MachineComponent? component = null) - { - if (!Resolve(uid, ref component)) - return new List(); - - return GetAllParts(component); - } - - public List GetAllParts(MachineComponent component) - { - var parts = new List(); - - foreach (var entity in component.PartContainer.ContainedEntities) - { - if (TryComp(entity, out var machinePart)) - parts.Add(machinePart); - } - - return parts; - } - - public Dictionary GetPartsRatings(List parts) - { - var output = new Dictionary(); - foreach (var type in _prototypeManager.EnumeratePrototypes()) - { - var amount = 0f; - var sumRating = 0f; - foreach (var part in parts.Where(part => part.PartType == type.ID)) - { - amount++; - sumRating += part.Rating; - } - var rating = amount != 0 ? sumRating / amount : 0; - output.Add(type.ID, rating); - } - - return output; - } - - public void RefreshParts(EntityUid uid, MachineComponent component) - { - var parts = GetAllParts(component); - EntityManager.EventBus.RaiseLocalEvent(uid, new RefreshPartsEvent - { - Parts = parts, - PartRatings = GetPartsRatings(parts), - }, true); } private void CreateBoardAndStockParts(EntityUid uid, MachineComponent component) @@ -115,54 +29,37 @@ private void CreateBoardAndStockParts(EntityUid uid, MachineComponent component) var boardContainer = _container.EnsureContainer(uid, MachineFrameComponent.BoardContainerName); var partContainer = _container.EnsureContainer(uid, MachineFrameComponent.PartContainerName); - if (string.IsNullOrEmpty(component.BoardPrototype)) + if (string.IsNullOrEmpty(component.Board)) return; // We're done here, let's suppose all containers are correct just so we don't screw SaveLoadSave. if (boardContainer.ContainedEntities.Count > 0) return; - var board = EntityManager.SpawnEntity(component.BoardPrototype, Transform(uid).Coordinates); - - if (!_container.Insert(board, component.BoardContainer)) + var xform = Transform(uid); + if (!TrySpawnInContainer(component.Board, uid, MachineFrameComponent.BoardContainerName, out var board)) { - throw new Exception($"Couldn't insert board with prototype {component.BoardPrototype} to machine with prototype {MetaData(uid).EntityPrototype?.ID ?? "N/A"}!"); + throw new Exception($"Couldn't insert board with prototype {component.Board} to machine with prototype {Prototype(uid)?.ID ?? "N/A"}!"); } if (!TryComp(board, out var machineBoard)) { - throw new Exception($"Entity with prototype {component.BoardPrototype} doesn't have a {nameof(MachineBoardComponent)}!"); - } - - var xform = Transform(uid); - foreach (var (part, amount) in machineBoard.Requirements) - { - var partProto = _prototypeManager.Index(part); - for (var i = 0; i < amount; i++) - { - var p = EntityManager.SpawnEntity(partProto.StockPartPrototype, xform.Coordinates); - - if (!_container.Insert(p, partContainer)) - throw new Exception($"Couldn't insert machine part of type {part} to machine with prototype {partProto.StockPartPrototype}!"); - } + throw new Exception($"Entity with prototype {component.Board} doesn't have a {nameof(MachineBoardComponent)}!"); } - foreach (var (stackType, amount) in machineBoard.MaterialRequirements) + foreach (var (stackType, amount) in machineBoard.StackRequirements) { - var stack = _stackSystem.Spawn(amount, stackType, Transform(uid).Coordinates); - + var stack = _stackSystem.Spawn(amount, stackType, xform.Coordinates); if (!_container.Insert(stack, partContainer)) - throw new Exception($"Couldn't insert machine material of type {stackType} to machine with prototype {MetaData(uid).EntityPrototype?.ID ?? "N/A"}"); + throw new Exception($"Couldn't insert machine material of type {stackType} to machine with prototype {Prototype(uid)?.ID ?? "N/A"}"); } foreach (var (compName, info) in machineBoard.ComponentRequirements) { for (var i = 0; i < info.Amount; i++) { - var c = EntityManager.SpawnEntity(info.DefaultPrototype, Transform(uid).Coordinates); - - if(!_container.Insert(c, partContainer)) - throw new Exception($"Couldn't insert machine component part with default prototype '{compName}' to machine with prototype {MetaData(uid).EntityPrototype?.ID ?? "N/A"}"); + if(!TrySpawnInContainer(info.DefaultPrototype, uid, MachineFrameComponent.PartContainerName, out _)) + throw new Exception($"Couldn't insert machine component part with default prototype '{compName}' to machine with prototype {Prototype(uid)?.ID ?? "N/A"}"); } } @@ -170,58 +67,9 @@ private void CreateBoardAndStockParts(EntityUid uid, MachineComponent component) { for (var i = 0; i < info.Amount; i++) { - var c = EntityManager.SpawnEntity(info.DefaultPrototype, Transform(uid).Coordinates); - - if(!_container.Insert(c, partContainer)) - throw new Exception($"Couldn't insert machine component part with default prototype '{tagName}' to machine with prototype {MetaData(uid).EntityPrototype?.ID ?? "N/A"}"); + if(!TrySpawnInContainer(info.DefaultPrototype, uid, MachineFrameComponent.PartContainerName, out _)) + throw new Exception($"Couldn't insert machine component part with default prototype '{tagName}' to machine with prototype {Prototype(uid)?.ID ?? "N/A"}"); } } } } - -public sealed class RefreshPartsEvent : EntityEventArgs -{ - public IReadOnlyList Parts = new List(); - - public Dictionary PartRatings = new(); -} - -public sealed class UpgradeExamineEvent : EntityEventArgs -{ - private FormattedMessage Message; - - public UpgradeExamineEvent(ref FormattedMessage message) - { - Message = message; - } - - /// - /// Add a line to the upgrade examine tooltip with a percentage-based increase or decrease. - /// - public void AddPercentageUpgrade(string upgradedLocId, float multiplier) - { - var percent = Math.Round(100 * MathF.Abs(multiplier - 1), 2); - var locId = multiplier switch { - < 1 => "machine-upgrade-decreased-by-percentage", - 1 or float.NaN => "machine-upgrade-not-upgraded", - > 1 => "machine-upgrade-increased-by-percentage", - }; - var upgraded = Loc.GetString(upgradedLocId); - this.Message.AddMarkup(Loc.GetString(locId, ("upgraded", upgraded), ("percent", percent)) + '\n'); - } - - /// - /// Add a line to the upgrade examine tooltip with a numeric increase or decrease. - /// - public void AddNumberUpgrade(string upgradedLocId, int number) - { - var difference = Math.Abs(number); - var locId = number switch { - < 0 => "machine-upgrade-decreased-by-amount", - 0 => "machine-upgrade-not-upgraded", - > 0 => "machine-upgrade-increased-by-amount", - }; - var upgraded = Loc.GetString(upgradedLocId); - this.Message.AddMarkup(Loc.GetString(locId, ("upgraded", upgraded), ("difference", difference)) + '\n'); - } -} diff --git a/Content.Server/Construction/ConstructionSystem.cs b/Content.Server/Construction/ConstructionSystem.cs index 44c6318b7891..9d881dcef04a 100644 --- a/Content.Server/Construction/ConstructionSystem.cs +++ b/Content.Server/Construction/ConstructionSystem.cs @@ -16,7 +16,6 @@ namespace Content.Server.Construction [UsedImplicitly] public sealed partial class ConstructionSystem : SharedConstructionSystem { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly ContainerSystem _container = default!; diff --git a/Content.Server/Construction/MachineFrameSystem.cs b/Content.Server/Construction/MachineFrameSystem.cs index e20c36d8498c..0fd5ad2e0e45 100644 --- a/Content.Server/Construction/MachineFrameSystem.cs +++ b/Content.Server/Construction/MachineFrameSystem.cs @@ -7,7 +7,7 @@ using Content.Shared.Tag; using Content.Shared.Popups; using Robust.Shared.Containers; -using Robust.Shared.Utility; +using Robust.Shared.Prototypes; namespace Content.Server.Construction; @@ -62,24 +62,7 @@ private void OnInteractUsing(EntityUid uid, MachineFrameComponent component, Int // If this changes in the future, then RegenerateProgress() also needs to be updated. // Note that one entity is ALLOWED to satisfy more than one kind of component or tag requirements. This is // necessary in order to avoid weird entity-ordering shenanigans in RegenerateProgress(). - var stack = CompOrNull(args.Used); - var machinePart = CompOrNull(args.Used); - if (stack != null && machinePart != null) - { - if (TryInsertPartStack(uid, args.Used, component, machinePart, stack)) - args.Handled = true; - return; - } - - // Handle parts - if (machinePart != null) - { - if (TryInsertPart(uid, args.Used, component, machinePart)) - args.Handled = true; - return; - } - - if (stack != null) + if (TryComp(args.Used, out var stack)) { if (TryInsertStack(uid, args.Used, component, stack)) args.Handled = true; @@ -172,67 +155,6 @@ private bool TryInsertBoard(EntityUid uid, EntityUid used, MachineFrameComponent return true; } - /// Whether or not the function had any effect. Does not indicate success. - private bool TryInsertPart(EntityUid uid, EntityUid used, MachineFrameComponent component, MachinePartComponent machinePart) - { - DebugTools.Assert(!HasComp(uid)); - if (!component.Requirements.ContainsKey(machinePart.PartType)) - return false; - - if (component.Progress[machinePart.PartType] >= component.Requirements[machinePart.PartType]) - return false; - - if (!_container.TryRemoveFromContainer(used)) - return false; - - if (!_container.Insert(used, component.PartContainer)) - return true; - - component.Progress[machinePart.PartType]++; - if (IsComplete(component)) - _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid); - - return true; - } - - /// Whether or not the function had any effect. Does not indicate success. - private bool TryInsertPartStack(EntityUid uid, EntityUid used, MachineFrameComponent component, MachinePartComponent machinePart, StackComponent stack) - { - if (!component.Requirements.ContainsKey(machinePart.PartType)) - return false; - - var progress = component.Progress[machinePart.PartType]; - var requirement = component.Requirements[machinePart.PartType]; - - var needed = requirement - progress; - if (needed <= 0) - return false; - - var count = stack.Count; - if (count < needed) - { - if (!_container.Insert(used, component.PartContainer)) - return true; - - component.Progress[machinePart.PartType] += count; - return true; - } - - var splitStack = _stack.Split(used, needed, Transform(uid).Coordinates, stack); - - if (splitStack == null) - return false; - - if (!_container.Insert(splitStack.Value, component.PartContainer)) - return true; - - component.Progress[machinePart.PartType] += needed; - if (IsComplete(component)) - _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid); - - return true; - } - /// Whether or not the function had any effect. Does not indicate success. private bool TryInsertStack(EntityUid uid, EntityUid used, MachineFrameComponent component, StackComponent stack) { @@ -281,12 +203,6 @@ public bool IsComplete(MachineFrameComponent component) if (!component.HasBoard) return false; - foreach (var (part, amount) in component.Requirements) - { - if (component.Progress[part] < amount) - return false; - } - foreach (var (type, amount) in component.MaterialRequirements) { if (component.MaterialProgress[type] < amount) @@ -310,21 +226,14 @@ public bool IsComplete(MachineFrameComponent component) public void ResetProgressAndRequirements(MachineFrameComponent component, MachineBoardComponent machineBoard) { - component.Requirements = new Dictionary(machineBoard.Requirements); - component.MaterialRequirements = new Dictionary(machineBoard.MaterialIdRequirements); + component.MaterialRequirements = new Dictionary, int>(machineBoard.StackRequirements); component.ComponentRequirements = new Dictionary(machineBoard.ComponentRequirements); - component.TagRequirements = new Dictionary(machineBoard.TagRequirements); + component.TagRequirements = new Dictionary, GenericPartInfo>(machineBoard.TagRequirements); - component.Progress.Clear(); component.MaterialProgress.Clear(); component.ComponentProgress.Clear(); component.TagProgress.Clear(); - foreach (var (machinePart, _) in component.Requirements) - { - component.Progress[machinePart] = 0; - } - foreach (var (stackType, _) in component.MaterialRequirements) { component.MaterialProgress[stackType] = 0; @@ -349,7 +258,6 @@ public void RegenerateProgress(MachineFrameComponent component) component.MaterialRequirements.Clear(); component.ComponentRequirements.Clear(); component.TagRequirements.Clear(); - component.Progress.Clear(); component.MaterialProgress.Clear(); component.ComponentProgress.Clear(); component.TagProgress.Clear(); @@ -368,19 +276,6 @@ public void RegenerateProgress(MachineFrameComponent component) foreach (var part in component.PartContainer.ContainedEntities) { - if (TryComp(part, out var machinePart)) - { - // Check this is part of the requirements... - if (!component.Requirements.ContainsKey(machinePart.PartType)) - continue; - - if (!component.Progress.ContainsKey(machinePart.PartType)) - component.Progress[machinePart.PartType] = 1; - else - component.Progress[machinePart.PartType]++; - continue; - } - if (TryComp(part, out var stack)) { var type = stack.StackTypeId; @@ -404,9 +299,7 @@ public void RegenerateProgress(MachineFrameComponent component) if (!HasComp(part, registration.Type)) continue; - if (!component.ComponentProgress.ContainsKey(compName)) - component.ComponentProgress[compName] = 1; - else + if (!component.ComponentProgress.TryAdd(compName, 1)) component.ComponentProgress[compName]++; } @@ -419,18 +312,17 @@ public void RegenerateProgress(MachineFrameComponent component) if (!_tag.HasTag(tagComp, tagName)) continue; - if (!component.TagProgress.ContainsKey(tagName)) - component.TagProgress[tagName] = 1; - else + if (!component.TagProgress.TryAdd(tagName, 1)) component.TagProgress[tagName]++; } } } private void OnMachineFrameExamined(EntityUid uid, MachineFrameComponent component, ExaminedEvent args) { - if (!args.IsInDetailsRange) + if (!args.IsInDetailsRange || !component.HasBoard) return; - if (component.HasBoard) - args.PushMarkup(Loc.GetString("machine-frame-component-on-examine-label", ("board", EntityManager.GetComponent(component.BoardContainer.ContainedEntities[0]).EntityName))); + + var board = component.BoardContainer.ContainedEntities[0]; + args.PushMarkup(Loc.GetString("machine-frame-component-on-examine-label", ("board", Name(board)))); } } diff --git a/Content.Server/Construction/PartExchangerSystem.cs b/Content.Server/Construction/PartExchangerSystem.cs deleted file mode 100644 index 174dd1d2faa9..000000000000 --- a/Content.Server/Construction/PartExchangerSystem.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System.Linq; -using Content.Server.Construction.Components; -using Content.Server.Storage.EntitySystems; -using Content.Shared.DoAfter; -using Content.Shared.Construction.Components; -using Content.Shared.Exchanger; -using Content.Shared.Interaction; -using Content.Shared.Popups; -using Content.Shared.Storage; -using Robust.Shared.Containers; -using Robust.Shared.Utility; -using Content.Shared.Wires; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Collections; - -namespace Content.Server.Construction; - -public sealed class PartExchangerSystem : EntitySystem -{ - [Dependency] private readonly ConstructionSystem _construction = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly StorageSystem _storage = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent(OnDoAfter); - } - - private void OnDoAfter(EntityUid uid, PartExchangerComponent component, DoAfterEvent args) - { - if (args.Cancelled) - { - component.AudioStream = _audio.Stop(component.AudioStream); - return; - } - - if (args.Handled || args.Args.Target == null) - return; - - if (!TryComp(uid, out var storage)) - return; //the parts are stored in here - - var machinePartQuery = GetEntityQuery(); - var machineParts = new List<(EntityUid, MachinePartComponent)>(); - - foreach (var item in storage.Container.ContainedEntities) //get parts in RPED - { - if (machinePartQuery.TryGetComponent(item, out var part)) - machineParts.Add((item, part)); - } - - TryExchangeMachineParts(args.Args.Target.Value, uid, machineParts); - TryConstructMachineParts(args.Args.Target.Value, uid, machineParts); - - args.Handled = true; - } - - private void TryExchangeMachineParts(EntityUid uid, EntityUid storageUid, List<(EntityUid part, MachinePartComponent partComp)> machineParts) - { - if (!TryComp(uid, out var machine)) - return; - - var machinePartQuery = GetEntityQuery(); - var board = machine.BoardContainer.ContainedEntities.FirstOrNull(); - - if (board == null || !TryComp(board, out var macBoardComp)) - return; - - foreach (var item in new ValueList(machine.PartContainer.ContainedEntities)) //clone so don't modify during enumeration - { - if (machinePartQuery.TryGetComponent(item, out var part)) - { - machineParts.Add((item, part)); - _container.RemoveEntity(uid, item); - } - } - - machineParts.Sort((x, y) => y.partComp.Rating.CompareTo(x.partComp.Rating)); - - var updatedParts = new List<(EntityUid part, MachinePartComponent partComp)>(); - foreach (var (type, amount) in macBoardComp.Requirements) - { - var target = machineParts.Where(p => p.partComp.PartType == type).Take(amount); - updatedParts.AddRange(target); - } - foreach (var part in updatedParts) - { - _container.Insert(part.part, machine.PartContainer); - machineParts.Remove(part); - } - - //put the unused parts back into rped. (this also does the "swapping") - foreach (var (unused, _) in machineParts) - { - _storage.Insert(storageUid, unused, out _, playSound: false); - } - _construction.RefreshParts(uid, machine); - } - - private void TryConstructMachineParts(EntityUid uid, EntityUid storageEnt, List<(EntityUid part, MachinePartComponent partComp)> machineParts) - { - if (!TryComp(uid, out var machine)) - return; - - var machinePartQuery = GetEntityQuery(); - var board = machine.BoardContainer.ContainedEntities.FirstOrNull(); - - if (!machine.HasBoard || !TryComp(board, out var macBoardComp)) - return; - - foreach (var item in new ValueList(machine.PartContainer.ContainedEntities)) //clone so don't modify during enumeration - { - if (machinePartQuery.TryGetComponent(item, out var part)) - { - machineParts.Add((item, part)); - _container.RemoveEntity(uid, item); - machine.Progress[part.PartType]--; - } - } - - machineParts.Sort((x, y) => y.partComp.Rating.CompareTo(x.partComp.Rating)); - - var updatedParts = new List<(EntityUid part, MachinePartComponent partComp)>(); - foreach (var (type, amount) in macBoardComp.Requirements) - { - var target = machineParts.Where(p => p.partComp.PartType == type).Take(amount); - updatedParts.AddRange(target); - } - foreach (var pair in updatedParts) - { - var part = pair.partComp; - var partEnt = pair.part; - - if (!machine.Requirements.ContainsKey(part.PartType)) - continue; - - _container.Insert(partEnt, machine.PartContainer); - machine.Progress[part.PartType]++; - machineParts.Remove(pair); - } - - //put the unused parts back into rped. (this also does the "swapping") - foreach (var (unused, _) in machineParts) - { - _storage.Insert(storageEnt, unused, out _, playSound: false); - } - } - - private void OnAfterInteract(EntityUid uid, PartExchangerComponent component, AfterInteractEvent args) - { - if (component.DoDistanceCheck && !args.CanReach) - return; - - if (args.Target == null) - return; - - if (!HasComp(args.Target) && !HasComp(args.Target)) - return; - - if (TryComp(args.Target, out var panel) && !panel.Open) - { - _popup.PopupEntity(Loc.GetString("construction-step-condition-wire-panel-open"), - args.Target.Value); - return; - } - - component.AudioStream = _audio.PlayPvs(component.ExchangeSound, uid).Value.Entity; - - _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.ExchangeDuration, new ExchangerDoAfterEvent(), uid, target: args.Target, used: uid) - { - BreakOnDamage = true, - BreakOnMove = true - }); - } -} diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index e34592b45f9c..bac789716ca7 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -71,6 +71,15 @@ public override void SetCount(EntityUid uid, int amount, StackComponent? compone return entity; } + /// + /// Spawns a stack of a certain stack type. See . + /// + public EntityUid Spawn(int amount, ProtoId id, EntityCoordinates spawnPosition) + { + var proto = _prototypeManager.Index(id); + return Spawn(amount, proto, spawnPosition); + } + /// /// Spawns a stack of a certain stack type. See . /// diff --git a/Content.Shared/Construction/Components/MachineBoardComponent.cs b/Content.Shared/Construction/Components/MachineBoardComponent.cs index dbca31606166..0469b59fd1e7 100644 --- a/Content.Shared/Construction/Components/MachineBoardComponent.cs +++ b/Content.Shared/Construction/Components/MachineBoardComponent.cs @@ -1,54 +1,47 @@ -using Content.Shared.Construction.Prototypes; using Content.Shared.Stacks; +using Content.Shared.Tag; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -namespace Content.Shared.Construction.Components +namespace Content.Shared.Construction.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class MachineBoardComponent : Component +{ + /// + /// The stacks needed to construct this machine + /// + [DataField] + public Dictionary, int> StackRequirements = new(); + + /// + /// Entities needed to construct this machine, discriminated by tag. + /// + [DataField] + public Dictionary, GenericPartInfo> TagRequirements = new(); + + /// + /// Entities needed to construct this machine, discriminated by component. + /// + [DataField] + public Dictionary ComponentRequirements = new(); + + /// + /// The machine that's constructed when this machine board is completed. + /// + [DataField(required: true)] + public EntProtoId Prototype; +} + +[DataDefinition, Serializable] +public partial struct GenericPartInfo { - [RegisterComponent, NetworkedComponent] - public sealed partial class MachineBoardComponent : Component - { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - [DataField("requirements", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public Dictionary Requirements = new(); - - [DataField("materialRequirements")] - public Dictionary MaterialIdRequirements = new(); - - [DataField("tagRequirements")] - public Dictionary TagRequirements = new(); - - [DataField("componentRequirements")] - public Dictionary ComponentRequirements = new(); - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("prototype")] - public string? Prototype { get; private set; } - - public IEnumerable> MaterialRequirements - { - get - { - foreach (var (materialId, amount) in MaterialIdRequirements) - { - var material = _prototypeManager.Index(materialId); - yield return new KeyValuePair(material, amount); - } - } - } - } - - [Serializable] - [DataDefinition] - public partial struct GenericPartInfo - { - [DataField("Amount")] - public int Amount; - [DataField("ExamineName")] - public string ExamineName; - [DataField("DefaultPrototype")] - public string DefaultPrototype; - } + [DataField(required: true)] + public int Amount; + + [DataField(required: true)] + public EntProtoId DefaultPrototype; + + [DataField] + public LocId? ExamineName; } diff --git a/Content.Shared/Construction/Components/MachinePartComponent.cs b/Content.Shared/Construction/Components/MachinePartComponent.cs deleted file mode 100644 index a68e3495e29d..000000000000 --- a/Content.Shared/Construction/Components/MachinePartComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Content.Shared.Construction.Prototypes; -using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Construction.Components -{ - [RegisterComponent, NetworkedComponent] - public sealed partial class MachinePartComponent : Component - { - [DataField("part", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string PartType { get; private set; } = default!; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("rating")] - public int Rating { get; private set; } = 1; - - /// - /// This number is used in tests to ensure that you can't use high quality machines for arbitrage. In - /// principle there is nothing wrong with using higher quality parts, but you have to be careful to not - /// allow them to be put into a lathe or something like that. - /// - public const int MaxRating = 4; - } -} diff --git a/Content.Shared/Construction/MachinePartSystem.cs b/Content.Shared/Construction/MachinePartSystem.cs index 359b58c8816f..f357a395e64f 100644 --- a/Content.Shared/Construction/MachinePartSystem.cs +++ b/Content.Shared/Construction/MachinePartSystem.cs @@ -1,10 +1,8 @@ using System.Linq; using Content.Shared.Construction.Components; -using Content.Shared.Construction.Prototypes; using Content.Shared.Examine; using Content.Shared.Lathe; using Content.Shared.Materials; -using Content.Shared.Stacks; using Robust.Shared.Prototypes; namespace Content.Shared.Construction @@ -16,6 +14,7 @@ public sealed class MachinePartSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly SharedLatheSystem _lathe = default!; + [Dependency] private readonly SharedConstructionSystem _construction = default!; public override void Initialize() { @@ -31,32 +30,30 @@ private void OnMachineBoardExamined(EntityUid uid, MachineBoardComponent compone using (args.PushGroup(nameof(MachineBoardComponent))) { args.PushMarkup(Loc.GetString("machine-board-component-on-examine-label")); - foreach (var (part, amount) in component.Requirements) + foreach (var (material, amount) in component.StackRequirements) { - args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text", - ("amount", amount), - ("requiredElement", Loc.GetString(_prototype.Index(part).Name)))); - } + var stack = _prototype.Index(material); + var name = _prototype.Index(stack.Spawn).Name; - foreach (var (material, amount) in component.MaterialRequirements) - { args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text", ("amount", amount), - ("requiredElement", Loc.GetString(material.Name)))); + ("requiredElement", Loc.GetString(name)))); } foreach (var (_, info) in component.ComponentRequirements) { + var examineName = _construction.GetExamineName(info); args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text", ("amount", info.Amount), - ("requiredElement", Loc.GetString(info.ExamineName)))); + ("requiredElement", examineName))); } foreach (var (_, info) in component.TagRequirements) { + var examineName = _construction.GetExamineName(info); args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text", ("amount", info.Amount), - ("requiredElement", Loc.GetString(info.ExamineName)))); + ("requiredElement", examineName))); } } } @@ -66,30 +63,13 @@ public Dictionary GetMachineBoardMaterialCost(Entity(); - foreach (var (partId, amount) in comp.Requirements) - { - var partProto = _prototype.Index(partId); - - if (!_lathe.TryGetRecipesFromEntity(partProto.StockPartPrototype, out var recipes)) - continue; - - var partRecipe = recipes[0]; - if (recipes.Count > 1) - partRecipe = recipes.MinBy(p => p.RequiredMaterials.Values.Sum()); - - foreach (var (mat, matAmount) in partRecipe!.RequiredMaterials) - { - materials.TryAdd(mat, 0); - materials[mat] += matAmount * amount * coefficient; - } - } - foreach (var (stackId, amount) in comp.MaterialIdRequirements) + foreach (var (stackId, amount) in comp.StackRequirements) { - var stackProto = _prototype.Index(stackId); + var stackProto = _prototype.Index(stackId); var defaultProto = _prototype.Index(stackProto.Spawn); - if (defaultProto.TryGetComponent(out var physComp)) + if (defaultProto.TryGetComponent(out var physComp, EntityManager.ComponentFactory)) { foreach (var (mat, matAmount) in physComp.MaterialComposition) { @@ -130,7 +110,7 @@ public Dictionary GetMachineBoardMaterialCost(Entity(out var physComp)) + defaultProto.TryGetComponent(out var physComp, EntityManager.ComponentFactory)) { foreach (var (mat, matAmount) in physComp.MaterialComposition) { diff --git a/Content.Shared/Construction/Prototypes/MachinePartPrototype.cs b/Content.Shared/Construction/Prototypes/MachinePartPrototype.cs deleted file mode 100644 index 7a080c94a3f7..000000000000 --- a/Content.Shared/Construction/Prototypes/MachinePartPrototype.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Construction.Prototypes; - -/// -/// This is a prototype for categorizing -/// different types of machine parts. -/// -[Prototype("machinePart")] -public sealed partial class MachinePartPrototype : IPrototype -{ - /// - [IdDataField] - public string ID { get; private set; } = default!; - - /// - /// A human-readable name for the machine part type. - /// - [DataField("name")] - public string Name = string.Empty; - - /// - /// A stock part entity based on the machine part. - /// - [DataField("stockPartPrototype", customTypeSerializer: typeof(PrototypeIdSerializer), required: true)] - public string StockPartPrototype = string.Empty; -} diff --git a/Content.Shared/Construction/SharedConstructionSystem.cs b/Content.Shared/Construction/SharedConstructionSystem.cs index 069290d14315..901b2f834d19 100644 --- a/Content.Shared/Construction/SharedConstructionSystem.cs +++ b/Content.Shared/Construction/SharedConstructionSystem.cs @@ -1,5 +1,7 @@ using System.Linq; +using Content.Shared.Construction.Components; using Robust.Shared.Map; +using Robust.Shared.Prototypes; using static Content.Shared.Interaction.SharedInteractionSystem; namespace Content.Shared.Construction @@ -7,6 +9,7 @@ namespace Content.Shared.Construction public abstract class SharedConstructionSystem : EntitySystem { [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; /// /// Get predicate for construction obstruction checks. @@ -22,5 +25,13 @@ public abstract class SharedConstructionSystem : EntitySystem var ignored = grid.GetAnchoredEntities(coords).ToHashSet(); return e => ignored.Contains(e); } + + public string GetExamineName(GenericPartInfo info) + { + if (info.ExamineName is not null) + return Loc.GetString(info.ExamineName.Value); + + return PrototypeManager.Index(info.DefaultPrototype).Name; + } } } diff --git a/Content.Shared/Construction/SharedFlatpackSystem.cs b/Content.Shared/Construction/SharedFlatpackSystem.cs index 8b21bca52aea..62d2089fb7c3 100644 --- a/Content.Shared/Construction/SharedFlatpackSystem.cs +++ b/Content.Shared/Construction/SharedFlatpackSystem.cs @@ -1,4 +1,3 @@ -using System.Numerics; using Content.Shared.Construction.Components; using Content.Shared.Administration.Logs; using Content.Shared.Database; @@ -9,7 +8,6 @@ using Content.Shared.Tools.Systems; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Network; using Robust.Shared.Physics.Components; @@ -88,7 +86,8 @@ private void OnFlatpackInteractUsing(Entity ent, ref Interact if (_net.IsServer) { var spawn = Spawn(comp.Entity, _map.GridTileToLocal(grid, gridComp, buildPos)); - _adminLogger.Add(LogType.Construction, LogImpact.Low, + _adminLogger.Add(LogType.Construction, + LogImpact.Low, $"{ToPrettyString(args.User):player} unpacked {ToPrettyString(spawn):entity} at {xform.Coordinates} from {ToPrettyString(uid):entity}"); QueueDel(uid); } @@ -115,9 +114,7 @@ public void SetupFlatpack(Entity ent, EntityUid? board) return; var machinePrototypeId = new EntProtoId(); - if (TryComp(board, out var machineBoard) && machineBoard.Prototype is not null) - machinePrototypeId = machineBoard.Prototype; - else if (TryComp(board, out var computerBoard) && computerBoard.Prototype is not null) + if (TryComp(board, out var computerBoard) && computerBoard.Prototype is not null) machinePrototypeId = computerBoard.Prototype; var comp = ent.Comp!; diff --git a/Content.Shared/Lathe/LatheComponent.cs b/Content.Shared/Lathe/LatheComponent.cs index d4bf18b0507d..88ac8c05bfbf 100644 --- a/Content.Shared/Lathe/LatheComponent.cs +++ b/Content.Shared/Lathe/LatheComponent.cs @@ -59,8 +59,6 @@ public sealed partial class LatheComponent : Component /// [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public float MaterialUseMultiplier = 1; - - public const float DefaultPartRatingMaterialUseMultiplier = 0.85f; #endregion } diff --git a/Resources/Locale/en-US/construction/steps/arbitrary-insert-construction-graph-step.ftl b/Resources/Locale/en-US/construction/steps/arbitrary-insert-construction-graph-step.ftl index 6a7998e57a20..430888ed3653 100644 --- a/Resources/Locale/en-US/construction/steps/arbitrary-insert-construction-graph-step.ftl +++ b/Resources/Locale/en-US/construction/steps/arbitrary-insert-construction-graph-step.ftl @@ -1,2 +1,10 @@ # Shown when examining an in-construction object -construction-insert-arbitrary-entity = Next, insert {$stepName}. \ No newline at end of file +construction-insert-arbitrary-entity = Next, insert {$stepName}. + +construction-insert-info-examine-name-instrument-brass = brass instrument +construction-insert-info-examine-name-instrument-keyed = keyed instrument +construction-insert-info-examine-name-instrument-percussion = percussion instrument +construction-insert-info-examine-name-instrument-string = string intrument +construction-insert-info-examine-name-instrument-woodwind = woodwind instrument +construction-insert-info-examine-name-knife = knife +construction-insert-info-examine-name-utensil = utensil diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml index 6db7f4b385ed..e4bde7ab84f6 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml @@ -11,10 +11,9 @@ state: security - type: MachineBoard prototype: ShuttleGunSvalinnMachineGun - requirements: + stackRequirements: MatterBin: 2 Manipulator: 4 - materialRequirements: Steel: 5 CableHV: 5 @@ -29,10 +28,9 @@ state: security - type: MachineBoard prototype: ShuttleGunPerforator - requirements: + stackRequirements: MatterBin: 4 Manipulator: 6 - materialRequirements: Steel: 10 CableHV: 5 @@ -47,10 +45,9 @@ state: security - type: MachineBoard prototype: ShuttleGunFriendship - requirements: + stackRequirements: MatterBin: 3 Manipulator: 2 - materialRequirements: Steel: 7 CableHV: 5 @@ -65,10 +62,9 @@ state: security - type: MachineBoard prototype: ShuttleGunDuster - requirements: + stackRequirements: MatterBin: 6 Manipulator: 4 - materialRequirements: Steel: 10 CableHV: 5 Uranium: 2 @@ -84,9 +80,8 @@ state: security - type: MachineBoard prototype: ShuttleGunKinetic - requirements: + stackRequirements: MatterBin: 2 Manipulator: 3 - materialRequirements: Steel: 5 - CableHV: 2 \ No newline at end of file + CableHV: 2 diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/particle_accelerator.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/particle_accelerator.yml index 78e17e074550..a1260f07c0c1 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/particle_accelerator.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/particle_accelerator.yml @@ -8,7 +8,7 @@ state: engineering - type: MachineBoard prototype: ParticleAcceleratorEndCapUnfinished - materialRequirements: + stackRequirements: Glass: 15 Steel: 15 @@ -24,10 +24,9 @@ prototype: ParticleAcceleratorFuelChamberUnfinished componentRequirements: AmeFuelContainer: - Amount: 1 - DefaultPrototype: AmeJar - ExamineName: AME Fuel Jar - materialRequirements: + amount: 1 + defaultPrototype: AmeJar + stackRequirements: Glass: 10 Steel: 10 @@ -41,7 +40,7 @@ state: engineering - type: MachineBoard prototype: ParticleAcceleratorPowerBoxUnfinished - materialRequirements: + stackRequirements: Glass: 5 Steel: 5 @@ -57,7 +56,7 @@ state: engineering - type: MachineBoard prototype: ParticleAcceleratorEmitterStarboardUnfinished - materialRequirements: + stackRequirements: Glass: 5 Steel: 5 @@ -71,7 +70,7 @@ state: engineering - type: MachineBoard prototype: ParticleAcceleratorEmitterForeUnfinished - materialRequirements: + stackRequirements: Glass: 5 Steel: 5 @@ -85,6 +84,6 @@ state: engineering - type: MachineBoard prototype: ParticleAcceleratorEmitterPortUnfinished - materialRequirements: + stackRequirements: Glass: 5 Steel: 5 diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 587e56865b4d..d41923b569d0 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -6,10 +6,9 @@ components: - type: MachineBoard prototype: Autolathe - requirements: + stackRequirements: MatterBin: 3 Manipulator: 1 - materialRequirements: Glass: 1 - type: entity @@ -20,15 +19,13 @@ components: - type: MachineBoard prototype: AutolatheHyperConvection - requirements: + stackRequirements: MatterBin: 3 - materialRequirements: Glass: 1 tagRequirements: Igniter: - Amount: 1 - DefaultPrototype: Igniter - ExamineName: Igniter + amount: 1 + defaultPrototype: Igniter - type: entity id: ProtolatheMachineCircuitboard @@ -38,14 +35,13 @@ components: - type: MachineBoard prototype: Protolathe - requirements: + stackRequirements: MatterBin: 2 Manipulator: 2 tagRequirements: GlassBeaker: - Amount: 2 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 2 + defaultPrototype: Beaker - type: entity parent: BaseMachineCircuitboard @@ -55,17 +51,15 @@ components: - type: MachineBoard prototype: ProtolatheHyperConvection - requirements: + stackRequirements: MatterBin: 2 tagRequirements: GlassBeaker: - Amount: 2 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 2 + defaultPrototype: Beaker Igniter: - Amount: 1 - DefaultPrototype: Igniter - ExamineName: Igniter + amount: 1 + defaultPrototype: Igniter - type: entity id: BiofabricatorMachineCircuitboard @@ -75,9 +69,8 @@ components: - type: MachineBoard prototype: Biofabricator - requirements: + stackRequirements: MatterBin: 4 - materialRequirements: Glass: 1 - type: entity @@ -90,14 +83,13 @@ state: security - type: MachineBoard prototype: SecurityTechFab - requirements: + stackRequirements: MatterBin: 2 Manipulator: 2 tagRequirements: GlassBeaker: - Amount: 2 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 2 + defaultPrototype: Beaker - type: entity id: AmmoTechFabCircuitboard @@ -109,7 +101,7 @@ state: security - type: MachineBoard prototype: AmmoTechFab - requirements: + stackRequirements: MatterBin: 1 Manipulator: 1 @@ -123,14 +115,13 @@ state: medical - type: MachineBoard prototype: MedicalTechFab - requirements: + stackRequirements: MatterBin: 2 Manipulator: 2 tagRequirements: GlassBeaker: - Amount: 2 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 2 + defaultPrototype: Beaker - type: StealTarget stealGroup: MedicalTechFabCircuitboard @@ -143,14 +134,13 @@ state: science - type: MachineBoard prototype: CircuitImprinter - requirements: + stackRequirements: MatterBin: 1 Manipulator: 1 tagRequirements: GlassBeaker: - Amount: 2 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 2 + defaultPrototype: Beaker - type: entity parent: BaseMachineCircuitboard @@ -162,17 +152,15 @@ state: science - type: MachineBoard prototype: CircuitImprinterHyperConvection - requirements: + stackRequirements: MatterBin: 2 tagRequirements: GlassBeaker: - Amount: 2 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 2 + defaultPrototype: Beaker Igniter: - Amount: 1 - DefaultPrototype: Igniter - ExamineName: Igniter + amount: 1 + defaultPrototype: Igniter - type: entity id: ExosuitFabricatorMachineCircuitboard @@ -183,10 +171,9 @@ state: science - type: MachineBoard prototype: ExosuitFabricator - requirements: + stackRequirements: MatterBin: 1 Manipulator: 3 - materialRequirements: Glass: 5 - type: GuideHelp guides: @@ -203,7 +190,7 @@ state: science - type: MachineBoard prototype: ResearchAndDevelopmentServer - materialRequirements: + stackRequirements: Plasma: 5 - type: entity @@ -213,7 +200,7 @@ components: - type: MachineBoard prototype: UniformPrinter - requirements: + stackRequirements: MatterBin: 1 Manipulator: 2 @@ -226,16 +213,14 @@ state: medical - type: MachineBoard prototype: Vaccinator - requirements: + stackRequirements: MatterBin: 1 Manipulator: 1 - materialRequirements: Cable: 5 tagRequirements: GlassBeaker: - Amount: 1 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 1 + defaultPrototype: Beaker - type: entity id: DiagnoserMachineCircuitboard @@ -246,18 +231,16 @@ state: medical - type: MachineBoard prototype: DiseaseDiagnoser - materialRequirements: + stackRequirements: Cable: 5 tagRequirements: GlassBeaker: - Amount: 1 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 1 + defaultPrototype: Beaker componentRequirements: - DiseaseSwab: - Amount: 1 - DefaultPrototype: DiseaseSwab - ExamineName: Swab + BotanySwab: + amount: 1 + defaultPrototype: DiseaseSwab - type: entity id: ArtifactAnalyzerMachineCircuitboard @@ -269,10 +252,9 @@ state: science - type: MachineBoard prototype: MachineArtifactAnalyzer - requirements: + stackRequirements: Manipulator: 3 Capacitor: 1 - materialRequirements: Glass: 5 - type: entity @@ -285,9 +267,8 @@ state: science - type: MachineBoard prototype: MachineArtifactCrusher - requirements: + stackRequirements: Manipulator: 2 - materialRequirements: Glass: 1 Steel: 5 @@ -301,9 +282,8 @@ state: science - type: MachineBoard prototype: MachineAnomalyVessel - requirements: + stackRequirements: Capacitor: 3 - materialRequirements: Cable: 1 PlasmaGlass: 10 @@ -317,9 +297,8 @@ state: science - type: MachineBoard prototype: MachineAnomalyVesselExperimental - requirements: + stackRequirements: Capacitor: 3 - materialRequirements: Cable: 5 PlasmaGlass: 15 MetalRod: 4 @@ -334,10 +313,9 @@ state: science - type: MachineBoard prototype: MachineAnomalySynchronizer - requirements: + stackRequirements: Manipulator: 2 Capacitor: 5 - materialRequirements: PlasmaGlass: 5 Cable: 5 @@ -351,9 +329,8 @@ state: science - type: MachineBoard prototype: MachineAPE - requirements: + stackRequirements: Capacitor: 2 - materialRequirements: Cable: 1 Glass: 1 @@ -367,10 +344,9 @@ state: engineering - type: MachineBoard prototype: GasThermoMachineFreezer - requirements: + stackRequirements: MatterBin: 2 Capacitor: 2 - materialRequirements: Cable: 5 - type: Construction deconstructionTarget: null @@ -387,10 +363,9 @@ state: engineering - type: MachineBoard prototype: GasThermoMachineHeater - requirements: + stackRequirements: MatterBin: 2 Capacitor: 2 - materialRequirements: Cable: 5 - type: Construction graph: ThermomachineBoard @@ -407,10 +382,9 @@ state: engineering - type: MachineBoard prototype: GasThermoMachineHellfireFreezer - requirements: + stackRequirements: MatterBin: 2 Capacitor: 2 - materialRequirements: Plasma: 1 - type: Construction deconstructionTarget: null @@ -427,10 +401,9 @@ state: engineering - type: MachineBoard prototype: GasThermoMachineHellfireHeater - requirements: + stackRequirements: MatterBin: 2 Capacitor: 2 - materialRequirements: Plasma: 1 - type: Construction graph: ThermomachineBoard @@ -447,9 +420,8 @@ state: engineering - type: MachineBoard prototype: BaseGasCondenser - requirements: + stackRequirements: MatterBin: 1 - materialRequirements: Glass: 1 - type: entity @@ -462,10 +434,9 @@ state: engineering - type: MachineBoard prototype: PortableScrubber - requirements: + stackRequirements: MatterBin: 1 Manipulator: 2 - materialRequirements: Cable: 5 Glass: 2 @@ -479,10 +450,9 @@ state: engineering - type: MachineBoard prototype: SpaceHeater - requirements: + stackRequirements: MatterBin: 1 Capacitor: 2 - materialRequirements: Cable: 5 - type: entity @@ -495,10 +465,9 @@ state: medical - type: MachineBoard prototype: CloningPod - requirements: + stackRequirements: MatterBin: 2 Manipulator: 2 - materialRequirements: Glass: 1 Cable: 1 @@ -512,9 +481,8 @@ state: medical - type: MachineBoard prototype: MedicalScanner - requirements: + stackRequirements: Capacitor: 1 - materialRequirements: Glass: 5 Cable: 1 @@ -526,7 +494,7 @@ components: - type: MachineBoard prototype: CrewMonitoringServer - materialRequirements: + stackRequirements: Steel: 1 Cable: 2 @@ -540,7 +508,7 @@ state: medical - type: MachineBoard prototype: CryoPod - materialRequirements: + stackRequirements: Glass: 5 Cable: 1 @@ -554,16 +522,14 @@ state: medical - type: MachineBoard prototype: ChemMaster - requirements: + stackRequirements: Capacitor: 1 - materialRequirements: Glass: 1 Cable: 1 tagRequirements: GlassBeaker: - Amount: 2 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 2 + defaultPrototype: Beaker - type: entity id: ChemDispenserMachineCircuitboard @@ -575,16 +541,14 @@ state: medical - type: MachineBoard prototype: ChemDispenserEmpty - requirements: + stackRequirements: Capacitor: 1 - materialRequirements: Glass: 1 Steel: 3 tagRequirements: GlassBeaker: - Amount: 2 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 2 + defaultPrototype: Beaker - type: entity id: BiomassReclaimerMachineCircuitboard @@ -594,16 +558,15 @@ components: - type: MachineBoard prototype: BiomassReclaimer - requirements: + stackRequirements: MatterBin: 2 Manipulator: 1 + Steel: 5 tagRequirements: Knife: - Amount: 2 - DefaultPrototype: KitchenKnife - ExamineName: Knife - materialRequirements: - Steel: 5 + amount: 2 + defaultPrototype: KitchenKnife + examineName: construction-insert-info-examine-name-knife - type: entity id: HydroponicsTrayMachineCircuitboard @@ -615,15 +578,14 @@ state: service - type: MachineBoard prototype: HydroponicsTrayEmpty - materialRequirements: + stackRequirements: # replacing the console screen Glass: 5 Cable: 2 tagRequirements: GlassBeaker: - Amount: 2 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 2 + defaultPrototype: Beaker - type: entity id: SeedExtractorMachineCircuitboard @@ -635,10 +597,9 @@ state: service - type: MachineBoard prototype: SeedExtractor - requirements: + stackRequirements: Manipulator: 2 Capacitor: 1 - materialRequirements: # replacing the console screen Glass: 1 Cable: 2 @@ -654,11 +615,13 @@ state: power_mod - type: MachineBoard prototype: SMESBasicEmpty - requirements: + stackRequirements: Capacitor: 1 - PowerCell: 4 - materialRequirements: CableHV: 10 + componentRequirements: + PowerCell: + amount: 4 + defaultPrototype: PowerCellSmall - type: entity id: CellRechargerCircuitboard @@ -671,9 +634,8 @@ state: charger_APC - type: MachineBoard prototype: PowerCellRecharger - requirements: + stackRequirements: Capacitor: 2 - materialRequirements: Cable: 5 - type: PhysicalComposition materialComposition: @@ -693,9 +655,8 @@ state: charger_APC - type: MachineBoard prototype: PowerCageRecharger - requirements: + stackRequirements: Capacitor: 4 - materialRequirements: Steel: 5 Cable: 10 - type: PhysicalComposition @@ -716,9 +677,8 @@ state: charger_APC - type: MachineBoard prototype: BorgCharger - requirements: + stackRequirements: Capacitor: 2 - materialRequirements: Cable: 5 - type: PhysicalComposition materialComposition: @@ -738,9 +698,8 @@ state: charger_APC - type: MachineBoard prototype: WeaponCapacitorRecharger - requirements: + stackRequirements: Capacitor: 2 - materialRequirements: CableMV: 5 - type: PhysicalComposition materialComposition: @@ -760,9 +719,8 @@ state: charger_APC - type: MachineBoard prototype: TurboItemRecharger - requirements: + stackRequirements: Capacitor: 2 - materialRequirements: CableMV: 5 - type: PhysicalComposition materialComposition: @@ -777,12 +735,14 @@ components: - type: MachineBoard prototype: SubstationBasicEmpty - requirements: + stackRequirements: Capacitor: 1 - PowerCell: 1 - materialRequirements: CableMV: 5 CableHV: 5 + componentRequirements: + PowerCell: + amount: 1 + defaultPrototype: PowerCellSmall - type: PhysicalComposition materialComposition: Glass: 200 @@ -798,31 +758,31 @@ components: - type: MachineBoard prototype: DawInstrument - materialRequirements: + stackRequirements: Glass: 1 Cable: 1 tagRequirements: # One instrument to bring them all and in the darkness bind them... KeyedInstrument: - Amount: 1 - DefaultPrototype: SynthesizerInstrument - ExamineName: Keyed Instrument + amount: 1 + defaultPrototype: SynthesizerInstrument + examineName: construction-insert-info-examine-name-instrument-keyed StringInstrument: - Amount: 1 - DefaultPrototype: AcousticGuitarInstrument - ExamineName: String Instrument + amount: 1 + defaultPrototype: AcousticGuitarInstrument + examineName: construction-insert-info-examine-name-instrument-string PercussionInstrument: - Amount: 1 - DefaultPrototype: GlockenspielInstrument - ExamineName: Percussion Instrument + amount: 1 + defaultPrototype: GlockenspielInstrument + examineName: construction-insert-info-examine-name-instrument-percussion BrassInstrument: - Amount: 1 - DefaultPrototype: TrumpetInstrument - ExamineName: Brass Instrument + amount: 1 + defaultPrototype: TrumpetInstrument + examineName: construction-insert-info-examine-name-instrument-brass WoodwindInstrument: - Amount: 1 - DefaultPrototype: SaxophoneInstrument - ExamineName: Woodwind Instrument + amount: 1 + defaultPrototype: SaxophoneInstrument + examineName: construction-insert-info-examine-name-instrument-woodwind - type: entity id: PortableGeneratorPacmanMachineCircuitboard @@ -833,9 +793,8 @@ state: engineering - type: MachineBoard prototype: PortableGeneratorPacman - requirements: + stackRequirements: Capacitor: 1 - materialRequirements: CableHV: 5 - type: PhysicalComposition materialComposition: @@ -852,9 +811,8 @@ components: - type: MachineBoard prototype: Thruster - requirements: + stackRequirements: Capacitor: 4 - materialRequirements: Steel: 5 - type: entity @@ -864,10 +822,9 @@ components: - type: MachineBoard prototype: Gyroscope - requirements: + stackRequirements: Manipulator: 2 Capacitor: 1 - materialRequirements: Glass: 2 - type: entity @@ -879,9 +836,8 @@ state: engineering - type: MachineBoard prototype: PortableGeneratorSuperPacman - requirements: + stackRequirements: Capacitor: 2 - materialRequirements: CableHV: 10 - type: PhysicalComposition materialComposition: @@ -900,9 +856,8 @@ state: engineering - type: MachineBoard prototype: PortableGeneratorJrPacman - requirements: + stackRequirements: Capacitor: 1 - materialRequirements: Cable: 10 - type: PhysicalComposition materialComposition: @@ -920,14 +875,13 @@ components: - type: MachineBoard prototype: KitchenReagentGrinder - requirements: + stackRequirements: MatterBin: 2 Manipulator: 2 tagRequirements: GlassBeaker: - Amount: 1 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 1 + defaultPrototype: Beaker - type: entity id: HotplateMachineCircuitboard @@ -937,9 +891,8 @@ components: - type: MachineBoard prototype: ChemistryHotplate - requirements: + stackRequirements: Capacitor: 2 - materialRequirements: Glass: 1 - type: entity @@ -950,9 +903,8 @@ components: - type: MachineBoard prototype: KitchenElectricGrill - requirements: + stackRequirements: Capacitor: 4 - materialRequirements: Glass: 2 Cable: 5 @@ -965,10 +917,9 @@ state: medical - type: MachineBoard prototype: StasisBed - requirements: + stackRequirements: Capacitor: 1 Manipulator: 1 - materialRequirements: Cable: 3 Steel: 2 @@ -982,9 +933,8 @@ state: medical - type: MachineBoard prototype: MachineElectrolysisUnit - requirements: + stackRequirements: Capacitor: 2 - materialRequirements: Cable: 1 - type: entity @@ -997,9 +947,8 @@ state: medical - type: MachineBoard prototype: MachineCentrifuge - requirements: + stackRequirements: Manipulator: 1 - materialRequirements: Steel: 1 - type: entity @@ -1011,9 +960,8 @@ state: supply - type: MachineBoard prototype: MaterialReclaimer - requirements: + stackRequirements: Manipulator: 2 - materialRequirements: Steel: 5 Plastic: 5 @@ -1026,10 +974,9 @@ state: supply - type: MachineBoard prototype: OreProcessor - requirements: + stackRequirements: MatterBin: 1 Manipulator: 3 - materialRequirements: Glass: 1 - type: entity @@ -1041,10 +988,9 @@ state: supply - type: MachineBoard prototype: OreProcessorIndustrial - requirements: + stackRequirements: MatterBin: 1 Manipulator: 3 - materialRequirements: Glass: 1 - type: entity @@ -1054,7 +1000,7 @@ components: - type: MachineBoard prototype: Sheetifier - requirements: + stackRequirements: MatterBin: 1 Manipulator: 1 @@ -1067,9 +1013,8 @@ state: service - type: MachineBoard prototype: KitchenMicrowave - requirements: + stackRequirements: Capacitor: 1 - materialRequirements: Glass: 2 Cable: 2 - type: Tag @@ -1085,13 +1030,13 @@ state: service - type: MachineBoard prototype: FatExtractor - requirements: + stackRequirements: Manipulator: 1 componentRequirements: Utensil: - Amount: 1 - DefaultPrototype: ForkPlastic - ExamineName: Utensil + amount: 1 + defaultPrototype: ForkPlastic + examineName: construction-insert-info-examine-name-utensil - type: entity parent: BaseMachineCircuitboard @@ -1100,10 +1045,9 @@ components: - type: MachineBoard prototype: MachineFlatpacker - requirements: + stackRequirements: Manipulator: 2 MatterBin: 1 - materialRequirements: Steel: 1 - type: entity @@ -1115,9 +1059,8 @@ state: engineering - type: MachineBoard prototype: Emitter - requirements: + stackRequirements: Capacitor: 2 - materialRequirements: CableHV: 5 Glass: 2 @@ -1129,7 +1072,7 @@ components: - type: MachineBoard prototype: SurveillanceCameraRouterConstructed - materialRequirements: + stackRequirements: Cable: 1 - type: entity @@ -1140,7 +1083,7 @@ components: - type: MachineBoard prototype: SurveillanceCameraWirelessRouterConstructed - materialRequirements: + stackRequirements: Cable: 2 Glass: 1 @@ -1152,7 +1095,7 @@ components: - type: MachineBoard prototype: SurveillanceWirelessCameraMovableConstructed - materialRequirements: + stackRequirements: Glass: 2 Cable: 2 @@ -1164,7 +1107,7 @@ components: - type: MachineBoard prototype: SurveillanceWirelessCameraAnchoredConstructed - materialRequirements: + stackRequirements: Cable: 2 Glass: 1 @@ -1176,10 +1119,9 @@ components: - type: MachineBoard prototype: GasRecycler - requirements: + stackRequirements: Capacitor: 1 Manipulator: 1 - materialRequirements: Steel: 10 Plasma: 5 @@ -1193,13 +1135,12 @@ state: service - type: MachineBoard prototype: BoozeDispenserEmpty - materialRequirements: + stackRequirements: Steel: 5 tagRequirements: GlassBeaker: - Amount: 1 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 1 + defaultPrototype: Beaker - type: entity id: CargoTelepadMachineCircuitboard @@ -1211,9 +1152,8 @@ state: supply - type: MachineBoard prototype: CargoTelepad - requirements: + stackRequirements: Capacitor: 2 - materialRequirements: Steel: 5 - type: entity @@ -1226,13 +1166,12 @@ state: service - type: MachineBoard prototype: SodaDispenserEmpty - materialRequirements: + stackRequirements: Steel: 5 tagRequirements: GlassBeaker: - Amount: 1 - DefaultPrototype: Beaker - ExamineName: Glass Beaker + amount: 1 + defaultPrototype: Beaker - type: entity id: TelecomServerCircuitboard @@ -1242,7 +1181,7 @@ components: - type: MachineBoard prototype: TelecomServer - materialRequirements: + stackRequirements: Steel: 1 Cable: 2 @@ -1254,9 +1193,8 @@ components: - type: MachineBoard prototype: SalvageMagnet - requirements: + stackRequirements: Capacitor: 4 - materialRequirements: Steel: 5 CableHV: 5 Cable: 2 @@ -1269,10 +1207,9 @@ components: - type: MachineBoard prototype: GravityGeneratorMini - requirements: + stackRequirements: Capacitor: 4 MatterBin: 3 - materialRequirements: Steel: 5 CableHV: 5 Uranium: 2 @@ -1284,10 +1221,9 @@ components: - type: MachineBoard prototype: ReagentGrinderIndustrial - requirements: + stackRequirements: MatterBin: 1 Manipulator: 3 - materialRequirements: Glass: 1 - type: entity @@ -1298,7 +1234,7 @@ components: - type: MachineBoard prototype: Jukebox - materialRequirements: + stackRequirements: WoodPlank: 5 Steel: 2 Glass: 5 diff --git a/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml b/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml index 37de294cce89..2f257db39651 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml @@ -21,9 +21,6 @@ components: - type: Sprite state: capacitor - - type: MachinePart - part: Capacitor - rating: 1 - type: Tag tags: - CapacitorStockPart @@ -39,11 +36,8 @@ components: - type: Sprite state: micro_mani - - type: MachinePart - part: Manipulator - rating: 1 - type: Stack - stackType: MicroManipulator + stackType: Manipulator - type: entity id: MatterBinStockPart @@ -54,8 +48,5 @@ components: - type: Sprite state: matter_bin - - type: MachinePart - part: MatterBin - rating: 1 - type: Stack stackType: MatterBin diff --git a/Resources/Prototypes/Entities/Objects/Power/powercells.yml b/Resources/Prototypes/Entities/Objects/Power/powercells.yml index 7397bcaa517e..90f987fdc69f 100644 --- a/Resources/Prototypes/Entities/Objects/Power/powercells.yml +++ b/Resources/Prototypes/Entities/Objects/Power/powercells.yml @@ -73,9 +73,6 @@ - type: Battery maxCharge: 360 startingCharge: 360 - - type: MachinePart - part: PowerCell - rating: 1 - type: Tag tags: - PowerCellSmall @@ -114,9 +111,6 @@ - type: Battery maxCharge: 720 startingCharge: 720 - - type: MachinePart - part: PowerCell - rating: 2 - type: entity id: PowerCellMediumPrinted @@ -152,9 +146,6 @@ - type: Battery maxCharge: 1080 startingCharge: 1080 - - type: MachinePart - part: PowerCell - rating: 3 - type: entity id: PowerCellHighPrinted @@ -190,9 +181,6 @@ - type: Battery maxCharge: 1800 startingCharge: 1800 - - type: MachinePart - part: PowerCell - rating: 4 - type: entity id: PowerCellHyperPrinted diff --git a/Resources/Prototypes/MachineParts/machine_parts.yml b/Resources/Prototypes/MachineParts/machine_parts.yml deleted file mode 100644 index 317e4b80866f..000000000000 --- a/Resources/Prototypes/MachineParts/machine_parts.yml +++ /dev/null @@ -1,20 +0,0 @@ -- type: machinePart - id: Capacitor - name: machine-part-name-capacitor - stockPartPrototype: CapacitorStockPart - -- type: machinePart - id: Manipulator - name: machine-part-name-manipulator - stockPartPrototype: MicroManipulatorStockPart - -- type: machinePart - id: MatterBin - name: machine-part-name-matter-bin - stockPartPrototype: MatterBinStockPart - -- type: machinePart - id: PowerCell - name: machine-part-name-power-cell - stockPartPrototype: PowerCellSmall - diff --git a/Resources/Prototypes/Stacks/science_stacks.yml b/Resources/Prototypes/Stacks/science_stacks.yml index bf58fad91543..0d273c324e6b 100644 --- a/Resources/Prototypes/Stacks/science_stacks.yml +++ b/Resources/Prototypes/Stacks/science_stacks.yml @@ -11,7 +11,7 @@ maxCount: 10 - type: stack - id: MicroManipulator + id: Manipulator name: micro manipulator spawn: MicroManipulatorStockPart maxCount: 10 From c368d5d36f4cf086fc39f026acca762dbfe9b4f8 Mon Sep 17 00:00:00 2001 From: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:47:27 -0500 Subject: [PATCH 308/568] Make Projectiles Only Hit a Variety of Station Objects Unless Clicked on (#28571) --- .../Fun/Instruments/base_instruments.yml | 3 ++- .../Dispensers/base_structuredispensers.yml | 2 +- .../Computers/base_structurecomputers.yml | 1 + .../Machines/Medical/chemistry_machines.yml | 2 +- .../Machines/Medical/disease_diagnoser.yml | 4 +--- .../Machines/Medical/vaccinator.yml | 1 + .../Structures/Machines/artifact_analyzer.yml | 3 ++- .../Machines/base_structuremachines.yml | 7 +++++++ .../Structures/Machines/fax_machine.yml | 1 + .../Entities/Structures/Machines/lathe.yml | 1 + .../Structures/Machines/microwave.yml | 4 ++-- .../Structures/Machines/reagent_grinder.yml | 2 +- .../Machines/wireless_surveillance_camera.yml | 4 +++- .../Piping/Atmospherics/portable.yml | 2 +- .../Structures/Piping/Atmospherics/unary.yml | 2 +- .../Structures/Piping/Disposal/units.yml | 1 + .../Power/Generation/Singularity/emitter.yml | 2 +- .../Power/Generation/portable_generator.yml | 4 ++-- .../Structures/Power/Generation/solar.yml | 3 +++ .../Entities/Structures/Power/chargers.yml | 3 +++ .../Structures/Storage/filing_cabinets.yml | 1 + .../Entities/Structures/Walls/fence_wood.yml | 19 +++++++++++++++++-- .../Entities/Structures/hydro_tray.yml | 4 +++- 23 files changed, 57 insertions(+), 19 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml index 122ff42eb20f..614af2a4886a 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity abstract: true parent: BaseItem id: BaseHandheldInstrument @@ -71,6 +71,7 @@ - BulletImpassable - type: StaticPrice price: 300 + - type: RequireProjectileTarget - type: entity parent: BasePlaceableInstrument diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml index 082f5e079923..213ad47d88fc 100644 --- a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml +++ b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml @@ -1,7 +1,7 @@ - type: entity abstract: true id: ReagentDispenserBase - parent: ConstructibleMachine + parent: SmallConstructibleMachine placement: mode: SnapgridCenter components: diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml index a5e26463b991..204e06c8600a 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml @@ -60,3 +60,4 @@ ents: [] - type: LightningTarget priority: 1 + - type: RequireProjectileTarget diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/chemistry_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/chemistry_machines.yml index 23789730d0c4..65eaf04d78fd 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/chemistry_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/chemistry_machines.yml @@ -1,6 +1,6 @@ - type: entity id: BaseTabletopChemicalMachine - parent: [ BaseMachinePowered, ConstructibleMachine ] + parent: [ BaseMachinePowered, SmallConstructibleMachine ] abstract: true components: - type: Transform diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml index e46c62053a9e..ad98f47e36a3 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml @@ -1,6 +1,6 @@ - type: entity id: DiseaseDiagnoser - parent: [ BaseMachinePowered, ConstructibleMachine ] + parent: [ BaseMachinePowered, SmallConstructibleMachine ] name: Disease Diagnoser Delta Extreme description: A machine that analyzes disease samples. placement: @@ -43,5 +43,3 @@ contentMargin: 12.0, 0.0, 12.0, 0.0 # This is a narrow piece of paper maxWritableArea: 128.0, 0.0 - - diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/vaccinator.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/vaccinator.yml index 041bca7c9053..53542cdfa914 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/vaccinator.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/vaccinator.yml @@ -24,3 +24,4 @@ containers: machine_board: !type:Container machine_parts: !type:Container + - type: RequireProjectileTarget diff --git a/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml b/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml index 8b0c57876316..9c878c7e7c55 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml @@ -1,6 +1,6 @@ - type: entity id: MachineArtifactAnalyzer - parent: [ BaseMachinePowered, ConstructibleMachine ] + parent: [ BaseMachinePowered, SmallConstructibleMachine ] name: artifact analyzer description: A platform capable of performing analysis on various types of artifacts. components: @@ -35,6 +35,7 @@ - Impassable - MidImpassable - LowImpassable + - BulletImpassable hard: False - type: Transform anchored: true diff --git a/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml b/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml index 621d9a1a7ecb..fb5ed4440a92 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml @@ -70,3 +70,10 @@ - machine_board - type: LightningTarget priority: 1 + +- type: entity + abstract: true + parent: ConstructibleMachine + id: SmallConstructibleMachine + components: + - type: RequireProjectileTarget diff --git a/Resources/Prototypes/Entities/Structures/Machines/fax_machine.yml b/Resources/Prototypes/Entities/Structures/Machines/fax_machine.yml index 36be6451d209..849c9fa4f2d2 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/fax_machine.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/fax_machine.yml @@ -66,6 +66,7 @@ deviceNetId: Wireless receiveFrequencyId: Fax transmitFrequencyId: Fax + - type: RequireProjectileTarget # Special - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 575144a2c226..537c42346eae 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -508,6 +508,7 @@ - Sheet - RawMaterial - Ingot + - type: RequireProjectileTarget - type: entity id: CircuitImprinterHyperConvection diff --git a/Resources/Prototypes/Entities/Structures/Machines/microwave.yml b/Resources/Prototypes/Entities/Structures/Machines/microwave.yml index fe4eb1451832..994269f71b4d 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/microwave.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/microwave.yml @@ -1,6 +1,6 @@ -- type: entity +- type: entity id: KitchenMicrowave - parent: [ BaseMachinePowered, ConstructibleMachine ] + parent: [ BaseMachinePowered, SmallConstructibleMachine ] name: microwave description: It's magic. components: diff --git a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml index d6e733331330..28aa464d210c 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml @@ -1,6 +1,6 @@ - type: entity id: KitchenReagentGrinder - parent: [ BaseMachinePowered, ConstructibleMachine ] + parent: [ BaseMachinePowered, SmallConstructibleMachine ] name: reagent grinder description: From BlenderTech. Will It Blend? Let's find out! suffix: grinder/juicer diff --git a/Resources/Prototypes/Entities/Structures/Machines/wireless_surveillance_camera.yml b/Resources/Prototypes/Entities/Structures/Machines/wireless_surveillance_camera.yml index fc8f31535cdb..95079b5c8582 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/wireless_surveillance_camera.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/wireless_surveillance_camera.yml @@ -1,6 +1,6 @@ - type: entity abstract: true - parent: [ BaseStructureDynamic, ConstructibleMachine ] + parent: [ BaseStructureDynamic, SmallConstructibleMachine ] id: SurveillanceWirelessCameraBase name: wireless camera description: A camera. It's watching you. Kinda. @@ -23,6 +23,8 @@ density: 80 mask: - MachineMask + layer: + - BulletImpassable - type: SurveillanceCameraMicrophone blacklist: components: diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/portable.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/portable.yml index 200df727b3b5..87e71400f732 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/portable.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/portable.yml @@ -1,6 +1,6 @@ - type: entity id: PortableScrubber - parent: [BaseMachinePowered, ConstructibleMachine, StructureWheeled] + parent: [BaseMachinePowered, SmallConstructibleMachine, StructureWheeled] name: portable scrubber description: It scrubs, portably! components: diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml index d301f43c7887..2b00fec246f6 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml @@ -383,7 +383,7 @@ board: HellfireHeaterMachineCircuitBoard - type: entity - parent: [ BaseMachinePowered, ConstructibleMachine ] + parent: [ BaseMachinePowered, SmallConstructibleMachine ] id: BaseGasCondenser name: condenser description: Condenses gases into liquids. Now we just need some plumbing. diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml index 2198c854a01c..e7d3d3c99771 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml @@ -84,6 +84,7 @@ enum.DisposalUnitUiKey.Key: type: DisposalUnitBoundUserInterface - type: RatKingRummageable + - type: RequireProjectileTarget - type: entity id: MailingUnit diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml index b999b2bdede8..6946dcbf8358 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml @@ -1,7 +1,7 @@ - type: entity id: Emitter name: emitter - parent: ConstructibleMachine + parent: SmallConstructibleMachine description: A heavy duty industrial laser. Shoots non-stop when turned on. placement: mode: SnapgridCenter diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml index d735d9607c37..86cfb0f79914 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml @@ -1,4 +1,4 @@ -# +# # You can use this Desmos sheet to calculate fuel burn rate values: # https://www.desmos.com/calculator/qcektq5dqs # @@ -6,7 +6,7 @@ - type: entity abstract: true id: PortableGeneratorBase - parent: [ BaseMachine, ConstructibleMachine, StructureWheeled] + parent: [ BaseMachine, SmallConstructibleMachine, StructureWheeled] components: # Basic properties - type: Transform diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml index 5a28c4962c1b..c512266e9749 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml @@ -49,6 +49,7 @@ onBump: false requirePower: true highVoltageNode: output + - type: RequireProjectileTarget - type: entity id: SolarPanel @@ -157,6 +158,7 @@ graph: SolarPanel node: solarassembly defaultTarget: solarpanel + - type: RequireProjectileTarget - type: entity id: SolarTracker @@ -201,3 +203,4 @@ - type: Construction graph: SolarPanel node: solartracker + - type: RequireProjectileTarget diff --git a/Resources/Prototypes/Entities/Structures/Power/chargers.yml b/Resources/Prototypes/Entities/Structures/Power/chargers.yml index 582a5b0dee4f..f5f0748b819d 100644 --- a/Resources/Prototypes/Entities/Structures/Power/chargers.yml +++ b/Resources/Prototypes/Entities/Structures/Power/chargers.yml @@ -58,12 +58,15 @@ density: 500 mask: - TabletopMachineMask + layer: + - BulletImpassable - type: PowerChargerVisuals - type: ContainerContainer containers: charger_slot: !type:ContainerSlot machine_board: !type:Container machine_parts: !type:Container + - type: RequireProjectileTarget - type: entity parent: BaseItemRecharger diff --git a/Resources/Prototypes/Entities/Structures/Storage/filing_cabinets.yml b/Resources/Prototypes/Entities/Structures/Storage/filing_cabinets.yml index 08db462be017..cfda95fc2f9e 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/filing_cabinets.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/filing_cabinets.yml @@ -156,6 +156,7 @@ node: chestDrawer - type: StaticPrice price: 15 + - type: RequireProjectileTarget - type: entity abstract: true diff --git a/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml b/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml index 55b7e40803b5..41dbe21d5f18 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml @@ -72,7 +72,7 @@ acts: [ "Destruction" ] - type: Climbable delay: 2.5 - + - type: RequireProjectileTarget #High - type: entity @@ -96,8 +96,10 @@ mask: - FullTileMask layer: + - Opaque - MidImpassable - LowImpassable + - BulletImpassable - type: Construction graph: FenceWood node: straight @@ -123,8 +125,10 @@ mask: - FullTileMask layer: + - Opaque - MidImpassable - LowImpassable + - BulletImpassable - type: Construction graph: FenceWood node: end @@ -159,8 +163,10 @@ mask: - TableMask layer: + - Opaque - MidImpassable - LowImpassable + - BulletImpassable - type: Construction graph: FenceWood node: corner @@ -195,8 +201,10 @@ mask: - TableMask layer: + - Opaque - MidImpassable - LowImpassable + - BulletImpassable - type: Construction graph: FenceWood node: tjunction @@ -221,8 +229,10 @@ mask: - FullTileMask layer: + - Opaque - MidImpassable - LowImpassable + - BulletImpassable - type: InteractionOutline - type: Door openSpriteState: door_opened @@ -271,6 +281,7 @@ layer: - MidImpassable - LowImpassable + - BulletImpassable - type: Construction graph: FenceWood node: straight_small @@ -298,6 +309,7 @@ layer: - MidImpassable - LowImpassable + - BulletImpassable - type: Construction graph: FenceWood node: end_small @@ -334,6 +346,7 @@ layer: - MidImpassable - LowImpassable + - BulletImpassable - type: Construction graph: FenceWood node: corner_small @@ -370,6 +383,7 @@ layer: - MidImpassable - LowImpassable + - BulletImpassable - type: Construction graph: FenceWood node: tjunction_small @@ -396,6 +410,7 @@ layer: - MidImpassable - LowImpassable + - BulletImpassable - type: InteractionOutline - type: Door openSpriteState: door_opened_small @@ -418,4 +433,4 @@ path: /Audio/Effects/door_close.ogg - type: Construction graph: FenceWood - node: gate_small \ No newline at end of file + node: gate_small diff --git a/Resources/Prototypes/Entities/Structures/hydro_tray.yml b/Resources/Prototypes/Entities/Structures/hydro_tray.yml index 8ea7172d8b41..68a0cbd38e45 100644 --- a/Resources/Prototypes/Entities/Structures/hydro_tray.yml +++ b/Resources/Prototypes/Entities/Structures/hydro_tray.yml @@ -1,6 +1,6 @@ - type: entity name: hydroponics tray - parent: [ hydroponicsSoil, ConstructibleMachine] + parent: [ hydroponicsSoil, SmallConstructibleMachine] id: hydroponicsTray description: An interstellar-grade space farmplot allowing for rapid growth and selective breeding of crops. Just... keep in mind the space weeds. components: @@ -14,6 +14,8 @@ hard: true mask: - MachineMask + layer: + - BulletImpassable - type: Anchorable - type: Pullable - type: Sprite From a710c7580918123bf0e30583c411852cad80f30b Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 5 Jun 2024 20:48:33 +0000 Subject: [PATCH 309/568] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index e68ab9517d3d..6adbf16221d3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: potato1234x - changes: - - message: Added crafting recipes for wall lockers and secure lockers - type: Add - - message: Fixed secure lockers and wall lockers not being deconstructible - type: Fix - id: 6185 - time: '2024-03-18T20:53:13.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/24942 - author: brainfood1183 changes: - message: Added Spray Paints. @@ -3849,3 +3840,11 @@ id: 6684 time: '2024-06-05T20:14:56.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28576 +- author: Cojoke-dot + changes: + - message: A variety of objects around the station now need to be clicked on in + order for bullets to hit them. + type: Tweak + id: 6685 + time: '2024-06-05T20:47:28.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28571 From 513023a9f0f5b899af20ad96751bb855c5252ebc Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Wed, 5 Jun 2024 14:06:24 -0700 Subject: [PATCH 310/568] Fix Smoke-grenade.ogg not being mono (#28593) --- Resources/Audio/Effects/Smoke-grenade.ogg | Bin 98191 -> 93809 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Audio/Effects/Smoke-grenade.ogg b/Resources/Audio/Effects/Smoke-grenade.ogg index c697214c2a571e1b6cc900b51c284fb073b8625c..945290ede54be1e189c4d05b32e8dd09bc5e2379 100644 GIT binary patch literal 93809 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

    ;E4dzooTn~OAhh>Ou*Ov+%=UA{n!z{P} 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

    =Qb~! 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 literal 98191 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

    i(GW1h<} zMe!WQ`+MAVH{)w)84C*U9h-THntT7d>`$o>G?Po2P>U?TAv{vc(5hBt;F;A-*$kMt zB5a^dgL+Jg^EFWR^o`^Wp6RKdqp+fyeb;A`AMyis;O7hC7r1KHzS4 z2r=GQZ_?7(+43)Og(V3DV{(VzQ&$&s#_c!ku+#uAU@6qYY$%?&AziuC+tb|UslPwg zQe8lF=8b)LsFN|6)1u(eEDT7>IEhmnw-`~)q=>BsS&XbCCrW!&$CCHQ(p)iqY>-YFm}0pGu zhyNhyS(MPl_Q`Tx;7<;#I!|#xg~?e*5U98PV?Ia&ugmL&8XU#^@)yYg1xzL4ZO=87 z81Tquc<9c@sMB$3Egb!+g9aOFNSDs?S!`jE#eyvO?8l5>QB5fuYlqD?8~UAbY1#b8 ztNay^$Kk`jzuMwLcU_Fg$6@#r${5@x&ky{-$E>*YUW?kB`x^K2;AjbNMOtZz{(KB3 z^o)RPvgbgQ0JqjSd=qcNLvAvHDz`NO+dxqfK~=ytcbYGjj}s~OKe>&G(*F!*|CyHl zQ_W!;KkSkC=MTcQ;{Dy`VJSXb2W}wOlN-c!fW`A-UxyPFze~crTEole+=pi+On-_V zD#-K-&>tH-wY_H>={!Gfucu5VaKnrze>5gRx9v4F{)ghn1|ZX!xcOc;T-~gNtz0?` zoM#Xqs59}Z9O1~GZXYdMw3>kJS-w(5&n~>CJgCt-FH%l{I$snl93%4sQRG+Tk}N}O zP>Xbb8BEpvZKo{H6M6<4!N|1j-1Zn3Wkhk!vUbJJ;Egh`X@x)xB~uE@Is*#6d~@du z4LyV8H$o1KRAZ=@>B`4XxOtZ?*1#D0HQ!##_w5EAS^pe;ZxL_{&XEV9kup*SF|I)G zS$LhX1{ayybNPFY(VsnU2s+DUQFAI298ZezotUR78ZkJL1$^oFml&PLVug7G4X1V) z(rhlJcT1o;J3SR(zKc7KFqi^{1?~=?D|>oD6rhy|1#qK)$ZdYdU9mjw5VoFXquq9v z=45cu<#7;1!~Zt(ob6WI=MX!j;O*I%>Mah3!HjU%e2enU4FnwpC8A<-)2NBVNm^3H z1Ra;+q9XTy?f2%@*D`0Dmd4|bz}%Vhmdjtg%w{`JA}EujU$R8#eae8}U*7lKlg?yn{ydCc-|9til<86#I{bE6+ioY_;u%Tq((F@=slltjZ)P=;!mEe&yumtSE!Tk(4I!@K=05#>WH&x%%}$O9QckM(B>eRdIi* zP%vUv*^xrZ-=_;>sly{Fh^_Y1q|*b9{aPc7K3e~`640@+7SZ=S>&|wQq5qhX|Qgxj}ljk*dw*IAC@&r*WQj4%G5K zQqE%5bjEauw`#W2WNi3k>qouqp<-|#x_^YcNkqs5l{I4<41>^TZ_5q8Ykpdw2Yjr) z)E}L!%bPpyoSvcjBttsgZSFz7%TLN@o}gd3 z0(y$1Wm$v^PrgV*JyDL0^0ZsNJu0K7?|pCIec{)&2Zl7{{z?bO`U684BSz0K4y5;6 za)?Bcu3dTPIxT%CEHSIHW%q)cJ-dTU_n48X~G3-0q9}GwYJv~H& zmDUvH|4Y>aA@}=a%Go#&OXrH26qRCgdRCL|kM=CXn+37YG8#J!2$;F{`(}3MddWY> zTd()QSIlJ`P&ORa5ayn-oeS(2n>Z5)a#o-MS4URlR$IjT)>+bFD4)45shl0YSS#bu zPEz)IIpvTNljIrwl#jo6b670ZM%(04?XGZiH@YhNu+xg-VGY1!eJkd#<68`7Nk{Jl z&rCnnE`na|i_{sJYr{0vb;Ui(30^&`Ak(tG5R&FSvnvj)b424r*~*gzs|;%|i8WE3 zvf1m4D|1$Yhdv5P{JHWcB3L}i&f40T5i8}@SlD-GG^e0gp=my{*f##z;ar^yu13c) z;^A#UveKRWvBpl_`v+HLIeeWlUVw8Q_Bq=|xl?g@6IN9Ia*cdahrFE`$BIuM4k{Ap zUPMWfJ>kqALRn7fK~t!%fHilAlRl|6pT0WM)*q6{Lk=X4mK7a3^v~D@&><~BF^qmo zj$tNxz6^9vitt-^=r||m@3*vwQZD5J8uF=GeDaamI2N6ImmGfUju>;=W3{dhwR=-? z>tq{kl=0adEn}?A{tlfG(lp`kIXzmQcVsenJ7@WfJWlHbZx>o=X3M}DyW2aDFS8AP zpY>@3%=J?@@rwPQmxf>_rc$nh?<9LC%V$Y5cXX(q^g^rl>rRb1I~R4vOkySp>R#f9 zF|iVjA+^}*>h8MPOP_;jQMt|0-XF(R4OBJ|)|}b|gx?i${7-lQGV9($XUOg+XFs}~ zh&rxZB(9NB(Bj0}FA+wLl&xtd*~&4m-w(nGh96 z^&{O|n>?}G?56HGIhyU(owT8Ce02HR28T?Jh*8lAL@J8HnxGwx$!gcE?z}DfZ$WSJ z9;70Lm)xwMC7Qwa9#(YwNR6qBpp!${oupdvBps~vMZ8j-!}37DYX6gz>ije|PAOsg zwelAb@+fKLhp?tRLmx69HgO_v(^+ooUf5l2;0T+}Y^(f0{XB`zh<_oi<--+>Ejr!_ zWw9TTYeG~&$+wPB;f>Mez(RLvO0ZVAjc9erGjCTQ_Pxl5=9HLqd}p8CzAfuBdt%4w zu&7r?k6O|eKI7{TnQILXSq3Q;P+r6P_f8EpJbZm>+UQ}5C7Z3;e0BASyi_tx%7q&; zeW;T|(G}UNCohtw!O(bkh3kn}e)-PDsIg{^F1wzM8B@%&Jp2d{2lt50e(>NE(w&;p zTh@!UiTx47Q%e~yNbAG3(r14>q!r98xuj#Cb8wFy-&xAj^IWxmvx%Dz_&f_Wj9!m! zybnbuh*7r;g2R?;{d?4EU-#ZAeflpg!#T|XUBTh>P`G1T3ULZeDJNmuaRGwk&2tUT&DLS(~tohe^XO7|8OeYB^#Kam&31km9!M=oosh z?Edi#KH!5c#3s8hG)~zWgzm3l<8ojI34HI23#f+}o};t_FPX`Bu>aU<@&jEp&@DWclky*U{A!#|6N`bz8w7px| z1&bvLVW~5dnG@Hl5&gMKUDa{?6_%h~+O$2g04El1e@yTgzoW^!y~A(bIAI@YwF(G+ z1X_6)T-kbTAlH9m{WxRzs=mnf#s{f7*7?qLx3C7MSLV_x{kBdTg z(fxGyUNz6Yp&{vix42lRWR#(?6BkZin2k%Z+lmnD53LpP=r7nRjkxbC8}mEq*G%dI z7bD?G@A7s{xL2Poar0VSo;!v*8n;xD$!F51r`0`>?O9G}(UNnU+i*NK7z`5$g!{R2 zAs+Z!m%%jkPHdwd@J6fz@8Zu`>#bC71oIws28U%nhlRj{fshO3 zi;Pv7nr94>SbR&Ahx(7Y4W1Bfua?VUI)?d%T3e|~p?8_=(wyUbm(}*9mR{wp%aW90 zPsaLtE#elz6Vf|xqOFe=pUQU!6imPwdD@_KHZeMrz`Qrm3;?z!^e3!#up_sx6KXVz zkI|{SebJk5_4V~w`a0wBN`A`a^Uq$vqZ@BwjQx;{R`4%`_VzOAj|on#yqArI;hr6$ z+4ZwZy3I13iBy%&@1=T? zeKo7?-Za9|gRr`wsY{~7PrHuvhcWD04NNL%ia65|}$K}Y8()@3J8P+i0YPspR7 z>r|XKiuNcfsykz6`Pjp7z}4WR9?P2iNxi?EsUIOO02uv{Hr}v1lHN!SWHGjAY6n*| zfcGPpok{OdBITWTbR?-#WPdBM3LlCuIUA~;r%JXW{L4IKO*uD3SPWM)pdri1puw2oLXn<79 zR8R4-WHiS1Kdr_#(c2Gg&#`&p+hZjF@1Wae+cikaezF~7-hW<_%1U-kS#B|BI-qx>w|hN zW=pT6fsYxVYx!gXB1oQy#N~~fx#L8S<3I;0xlsZcI~$Y==ajGU9z6kYF(+xTfOKfu zXrHv~tmMg;(q5kD&%L@HWb4dD=jhafF~)sIgjK&|guWr&`j{FTFS4B3%rT( z;;YMq#ZL8XK6W>KH42FXamUwen_k9*gS{&Xstq5ZQb)-#xfW%AWZ*QZ?E7ng(5`KE z>ILxwYiHGCovIC_mj!B1NOZ{_y}s+Cb0_xsmQz;H`^I#9iubKhXt&kJ`@7` zEjAamNRUy=9Ydr{_9S#uXn*X!>@t3~UYAsv<1JAZ7Wu8MU{P(6#hko+%sFJKaY@xWXl2xS-x5>>^z{Pg>MOjXnl~yKz?^o{))%dEm^YU{r@i zPW;v$U1a@rA}LaH*E?0rfGCJ|e zb9qtPpmvIB49Pu*qA7$%gmjE=pJ$N>32XRSA%2t8kB=IR<26R3+yWKaG=s=jdGWbX+$^uUbi}B5cl^v=g)J_jhRO@D%SIA%=S?Y28p3*37T_ z|EX%6{vBgHjE4tC?B)G36}~A+@Bj!q?mg~(9vtDseZ=+WhVbABFxLmRfL+@@*ExN@ zsK;g1{#71*6g#%lB)ySPykAcSqO&TLd;f0C2K6HZhv6;}S#=EdONVD+i5}@8#lv@$ zcK>TNM~Zw>kUEZ#K5hTw*{N`@SpdZ|;zA|(zMHRoG|Dy^D)vmbL!5i-i3Q^!os@Dk z-g|9Ta&QZSmb?`oZgPMB$9E_yx_p$Bl1Z<_IE;E+81g#ArwWa>F=Pxd=*B473yAo9 z=r`YwC(EHdw4~fHCf1TTPzhPy(Z)dESUqe*>=n4H$6V6! z>5u+%Bvei6kn38@Hx(l@Q}FOjaofPZXELVj)q5;re4>hF$;Tu)J~lG+e&tgDXOg70 z#E)cO&c~2w2CB_Zf@FnQ6^>B=uyIOFK=o#n$F3t8l>ko-)>44|MJyPf<0%vCeABbiMl^MUHlsm1XCJnll`G@|<@ez0m~zg&%{G#cSYl<0T3y!2o&O9`ffgv6wd%%* zL^BytA?7Yv<%l2)$R+lY^zqOaux+>@#}$-9sm?}uHxzPA9A)w6c%;{=_g(8uDz(w3 z`ue`5NfPM*s@KzrR@t%`%za|x0>bAW zN>*dDbb!ZW;A5rdyh}d}OA7p$xV4$D47e(cb=ZTcTNhfC#cEH~!@|~3l!q<`Uhruk4Bzk2o71w@^rJMha&BW!N@rx8 zhheKb-^eaL*>6~Lu5$0Z4Rv5NfUQn4A$3@xZ)Ad+OIoZGZo>J2J&~~bM#GucDQD## zj8-OB)@s?_{Z|#h$LwbFu`SsfAu5|n#e?&cF(GY1Huz04??IJ6^ra_J>*6oM$8E_= z%?%^ZE6mgeA50t`#@`7p3_6BJC&uFfjRM9Gx_s%$2o-ciq}mqDORZj?8H8e|gEm$b ztocn4pSGwf))4=?Une6Okn;DlP9y;dbq8M|_xMegMHBdp1BR>OgaF36N21=O*Rjul zM?8EZzP`h#n*8Mawfr?bm@!^1YH(d7hwodi$30E!fDX03rAErZF+e_S-s+h3mJDiB zE2=3x_sQ{C)25=AA2D|enyR*pc~X@p3hUW@`sknF*QP%KmJ@r5S=cB@CY9QM7wRd> z>b~&AoQ7JAEB&D>TqjHe?gIgOEJ0l{_a9FLI)7>dy8&Yoc5cUZZuW3fI=-&<${4&i zDI1=~&>cWs?1vbiddw08wgn^S7>S0g*+%6fbz#YG`$iu8AIZ3N&HDvBVlzUq?rNH{ zJO?UVr^9vC8+$oUrx0VfwPW}J|LUX_=MInMA^(Ny>%*oRncYrCUyaY8!bD6n0%RMN zsau~Pp4zkU9!rl3g+O`CMfHvJRYS}nDc}Th%a@B|y=(7L;mpq7m(4}*0rXE(Wnp*l z+jes08&Qzym^(IVBEYzz=M5$3x&DR~6%EQ|vc3)gM0Chd837?$;i5j&*>bGVOJP`x zlfK<#c!>jgf!z`*E@TOgI1{lN?!Uu=4yI&ckMBF1g{^xavJ<=@S;adKgek^r3Fpo9 zl;%AIi>|wlt~`i^st~qtP6fHAqB0)vb-}8}1w?dI)8vGDql6hxp5irHdGsv~uSbft z0^34>^ES3?XHa_Q3bH~(j2i2uq?lT3AI)$bPNos%os8?JSSvrc&>m#f`_aPDmo ziN_OhzRQi^5X3zf+Z+GM7x$r3pXI@3{46P0sL_N= z?$XOO=D+g(A5q^Om2@Ark6PJqw9Q=gG_>K~IcPj(!%?Z3Ij~gBof9W$Ln>EgW-h2K zapca4f@N+6%dH4G0C9kVg2?*O^S;0H9?s!=`1ix-dynhBuInbAX_}pf(3t12nGbkDJUZ+z zA0}*!-e~)z)>gw*&y}1SrWJoIKu z;XP|9@EYh%>%L8&0YS4P+*)te`{462p$f-@05`rfwSHc5EYNi|bsQ{llHwJl0cc9d zc+7%EpE2(Ca;)C<{?kziB`zJac0VJdE1-t6YQG~x1MMH!z!n}WfuDBY2c!p6FF)Dr zbL+N#JPnZlV=%O)kc5J%nk~^m zu`%T^K{gs1&%AC}lkusjSv?z}ANejJ!53}9-o28pD3reyzt~;;RAQ3CR>}h5JwK(~ zSaR;#f3u-bYeZ2!)47L#(5>f)(iU@Ez}%e;zV<ej-XV59(@R+euZqy+x?-9iJU`?TK#~Axp zm8`$b{|?{M7FH%UAv29Iv~cC2pxTTXT7yACp$yH%Gs3l5HJoRC+%aR4;3`w8-XgfC z2D`dkbJb*AGeG;&SrJZQ;n`zSh5hS-WY{5MD&1txLUU1` zWn)>dteQ=_iUNe6QM(h$+s|&1Nu!D@p3^mih3kN#0^B*?w8-ghWPlnweXDZHjR^L!~&%}4o7c0MIH#-l3}yQkOwu{Uo*d*kFbf*+R$RTtw-P{AT-QD)YJ;9}xVSAHT zca*c6=NE#C8L6z~OXUzG#{@XpR@M3tG4oZeJWg1BV6ko-ojb|gVlYDUdG1&8@h=@O zLB1Ew*y6;rppQ(N1#Ix%xPw{DSucywiG~RCcx9KcNa9hWie6p^1($sWeNd*mcP}uk zf@{H@r6YOVX7UBW4W7@d^EJQPdEZy*-V+$k4IFN@;+O*S`%#JW9n6XRUuuOpIH2+G z{)=A65oV7{D#rU3C6A1|=9A60a+DQVowUQz=C0SI071tiuN5mlT4ji`)6G6*B_@96 zj>kAjV%jrc-6yokECq`xQ~lkYWx$zPtLEYRbx@12-o6`>fXj)`Kl1u+b1dcFYd37V zoZ0xd3B;1D8r&h%db})p_3N*^2`iFn$F>F=rQIve-H4`h-2}|h=g+34Bb&`)fl-N` z3xn^aQw}^el`&v+pLX4i<(<6XKcD}&RCw@QqJ#udda6O-6lR$6<4dXD1!UOlB;)h+ z%))iseVv??A)+D(BbsH8eV7kzfde717~PJ$S^Yl!xqeUn*1O69@oGGkOWlF&(Vz)K z&z9qjuU%8^a74)`VjDuSDs6HDvz9}hwd!*aAE%01FRFh3aRXO2DK`J2x0#;s4y=5z zr+NG5Cy|v$DMZ-q=cKM&XKgewZg-jLghEJvznmUUq`Q9!;@zpmkZ0--o zMf+{gGt_5tqrFtyn9-HY^o>RSNZ;&uzLTQ@9+#mY8r5!KPI3l==OlP0bZ82&?=1h2 z^jQu&ZnA~>Htc=tZqbHm@V-NJwvurolsbkUczvG%kv@esd``v;X(;EA$;pztsIXyv(i&?)@I~fWDxADL*x@5v~ zMhI0Y4tE1dj#}%-XKBp9Z7(_Y$DH(G$px0Kh(rJos#5y-(=RG_v2&{3K(mS0Ui_N8H zW}H`zN&1)+w9xeFX}WDhRShh`jLv9Kp6rB{dcs1a_kAx9mVIKF8?g6gN?Up}K*!04 zl?Z{vErGt$u9sw-k-qwdSxFhop_k7DrVpfbNC~Ngjp&;Fll_;~H$m9P+5?TNz2NMm zxSyBJ^&b9+pN*X3GZ>IXR`1)L@F$#?_R?<7l@wl1D$PHz==9$+w?4E@U&*~xb}EEU zLN;3va*sAwxSCrln^iVfs)hyBL>ssvh0CD2(vaK6$aR#BCaX=_KhXTaCbQYiS}Nw0 z)J?}tN=2Dz499!gR5d<2Z8Yd`t0TYrF$*5$)vU6(IX6k0s#lo2kfsiJ9PE790%iZc zjZa@eP;(KKN>e|8M#mg9CLZC^lyxPPV1sU}&QJ^&2dBlOv!E5UpY88)fJ`Cu1cc+h zJ%Z_TD&7QKW7j6Lqr^_7oJ8{J=?nhnO+(nZQ%3#QD8#`fA+4BL=+Uz>W$FTw2;t?A zID8+tIsCyG&51W&(srso-*(ETy4cIFt2yjv6xPx7h115z57G%*Y|%lYaQ17hj~YM~ zZ+-vCpFdj!ygxJFceqQa0+bO{!qZi!*Xd40@+~6MLvo}KVNb{?<+`lRj~r6*R;)3O zVL1h)wKPUSyVc%1)d|7*WeU2HS(|r*{8`iRu9A}Lf8`h(j{Yr?X2s(iN^Olk6nbT{ z8x#E_^Z_F*>|XTrb?@>M457xKc4aOzx~ydSW?VH;AH`e_IssvCyKeZ6;8bmD&>z96 zIp=OgY0Fv`hOW$J*_F)k2~7761+Wbd6|G;64H1h1NJxjzGNK{6|psX`9xiW(Y@MQZq; zE-gt~yx`F3{`zb^r%k!O0=NC-!9N?f z@U=+7AimwmNALTGquhoLFLs`SEqJ*6e%kP<4c4sxG=9pYQ&UG=qu;k&8VXpf8evcQ z*;h%qjM2Zm+9ALDe_0l({|9XRC89)h!Q&5!!3+m6kP##b5Cw|AL&Y2mH!-;3qq<$i zQZzTi{8POd_Ti7y7@Z*_wt|F)a2$HoFD5>R#UxXyjO+qk8$-k@eE$Y z(`pijeH2Td73$Y*hOLL3ms5%vn7Cc!PrgmTjc!5XhxAY8cpk7wVdeyjM~vkj?`bkqG3UR8zi+Zr`ZKQK5Ljy->ZlGBo#e2 zb~?YeQ@*M%y*9ce)2#9{wy_Aj4LV0;XhESBYvjEuKa;V-I|vZ^U2E;hFscLWpv7eg zos~#pXITD>*Uu>z){;Kx?fWOk0K1paE5X7;d+=E^?^^7|A?YH>7_g7ElogZcPC+kJ z*yVD4WQAB%wO*p)p^L{U$A6_AqWSA(6iR3WA1Mhp`jG*$T{1sw6?`EBFl*-ZfIW)N zOrFEWYthQpeG?>K(g@Sg@*9;0nyUpLq^=%mG}@{?XFlihil(Y z=R8ibxx-$Ymo{+=#)ZLphW8(?BW}9Hf;z_1&)^t&6;fY;;;sd-tE06g|1n&uP{9c) zU8&##*n;RHba6|qXVucPaFaV83B!U2Ofzm&`{pmz-V<@bUxgbBZd~uRuH|Tjm)E#+ z^28oqvzgLj9O}M`zY%|}HpfrnZ;6s%eO3RQ#ktsT^#jm3tms$lW?0B=0Yc6UVM{u- z&RuGeDU1*Jb!#aO%i?Z?oI;GBJq->Mnv6zE=&rhf9CoQNhSH0pib&)dgLeEKDpUX`@ zV1~DYa`z9K*3TE4B=ea(Dx6(xIu49rkzeH2G)WG)Sib~9r( z1|73hx#pm%6A84Z{IYN(m}@#53xqPrK4Uu0fJc+m)ayGkv!F)q7a0;>1FvX4GD6Z)(L24+)xGn}{y^Rh*_>pW-$F?fwjYZ3D0!Ml~{ zMByei=Lf^m3O-?j;tXuh*&i*BHAokl-3!OPxAcj$xn)p$BLeCqFssQBUZVe4f^kUF z-4y>KFy;9@EqRx8)=5-#Lh8IB8PKuwaEjd0M2u&vGo2C3@>TTE{M(k#b4wcuu8p7w z;jp_(3JDqJIcTSwQc&sI(l!3*FP4Ny_i$*u+>7 zS)_gZkhBb^txEZ51y^= z#!cF{+3LKMFG!fMBbxll)i|27<<_IqywkSS8h=E_eY0GBu}Au=YqBj4YWCyf+K)fB z-eWACOYU*DtAa>&0|0$xM7LXv9=h9^h@t&YP}2+lP;ataIPt#r3Da_SjT$g=3^Apsncpb;Q7+*RAj&+6(a#1OyBVh%s zihzzi{t8j@$lar7P zu_X%(=QCyt&k_)3vvSea#_P>~Qs?n8yG?oQ)jMjar2-f@iG%mu`7(QI=(79QxL6Cm zbn@UL%TfLM2f+O2Jl};KMrvAT(^P6W1ylQX07A0Qy9-SyjG1BeH;0j5qc6B-qy@=U z^dgmCdInJuS=oV>hmW-8=6rBHjX@-s6n5n*mn2F$xC$zYo53+9+U5o_ACzv^t5H>I zvXS~GK&ieB+IRf5{_gTQ>p_sPperDaGzII*$o6&L-1QQt=+cf6LP?tuM)lx1XL(mI z@_u-6o*sKLglKkitHGDYyqVKS&OlB)24p-2K*tF4!VF!}i(3jM6Mr7{c?P+Pf;y&8 zjj;2b{x!Z=ZN1ETuf}s1>OjbACz&3x zQKwot*Nqba+-bJE?_a6~3*EWG$~9`5o#;0-2Za53w4t4z-&a!c*cT>#aEM-h zKw!;Ku@?#X;l@(5Y3DyqB1le5oROzVqXc2dgrEcx#M%ws%rL0jrX~!g)9u}^c^|y2 zHgD@X%)H&JeLy|vJ??spkNkia*dpT1VM<|!C9TuB9lRXG7Jg}@E1LHnD(ak#`L5Ey zg5?_{vlMtiVKL^$la3zmdxA7}p7{NGt7R8qUW|>%98-1s{Poy5wxMy5m^-78MV1md z%yN>{ke9G^mP--iQc;-EcW{-BE8mUkYW;8P>pSXS5N7~@^N$eZY7lz4-MT;WYNu~R zw$csJAdepb!Hu(c3~I_^#Rvwg1@V?xqs$^SXCro&W#$wb!z~Hn1c#FJehYwZ2(>RA zq!t{O9gP72d*6?`bhyd3hCk!>&zfn$>)RHEXB$sk;ZH(Q^O}iI-xJY$c-&*@-In!Ml@DL#DPT;QoIC1htwpiVLwaH0uD9UA zh#qXQB)ysm-P<1h;f*e$SbGh$@uf1mA^D_-D99AQXzugKx)NU%%>5&_fx=yo~)h6-NhL)qQ<}zx)HTQm?yunu4xPvW-haz%sh(o zgmU@?uokmmi-75yCb!*Jr@);_fm0*8te<`G634$Vb?e$)-t7qy13SXyDUN-k+cN4n zH2=|ip24})?&4UhvRsQrFtnuPp>}#^ms&V^lH*F}oeN)`i=|klYyG-Cc>WC>a|~1X zt#FXr)4)m9?8E+$ELE#u2)F%!^T!17uL+h}mN;Pu_dWI}PFg|&;N_acz+LewM|w_I zmF)l=7B<%UIWsv)6C2X@EV}QuVD$v&&b}|?_^e(FS?Rrpw6(!|g?V5#--cpT#wBmI zm%ceP00FEL3M#z<6z?PGzN!Y~_YD{nMM%L0#A7 zJ_l(mJQiDT0X!uBLVgW2CsP@_C9P!0mz>INTE_P`-N57UQWaW_sk_NCFK_b{k1x1v zfNr>3Z?UF3qWf19J}e$4ip?d*D-bOK6XfZj<#OSu*0!qAE;A9%Y<()YWs1bv__0Fy z?)e?;1)FLOIkQ-iWx}vp_pCYN5}**VUe(P4OC51suc!?b5#G`VNU@3vGaE&KKWmD= z2Pp#wS9?RLH-qP@rAS`mV7|5Yw0!Z-k zV`2@?e9COdB>^W+`*$bPyCX!Na2!hGK8kaDH|wJs8REybzR2r2WjCSz z(MPG22N%F#qw8jz7XXbv@1=G1IYTZa?q~57r6!}T_0*JGF;9=P(TCW2_%82N`>XzC zhFJA;Hr{k==XAgku!?_Og3bHG!S^MTy#4m=-V1nHx>HW74Il+LA9?^F20-T5(A`?) z0~Spyez(O%M;jNVqOG-Fa|U8Ljdok+Le(7SoTp(^>{U+#vzNX3Nv@rBa-{J(zdc&w z<4w@5&JBzCX2cj+@c&V}ei@=v|4)Ve@a-X;9BJ*O!)@z7n}VJ8q76ScWl=K}ENzr` zX1`%^e6H-%%$hAGY`?mU0xzo9yNg2SI!A?6+AXcqjwrw(fwenrNtuDD&_ShBS}7GE zdh>BW>ooQNt3AN-_yKw&>FHd$ks$zpnvJ5FGcfupt9zPk8~p(vhBJUUq+gvS%bN#8 zLx}N@*8nk0bzTbsktZt{XXSq$%*ekAK>H9M-{3G!BA>RswmDy_7_Xi@$TPWqzJ)dL zg4rs=1NMD`dyVFFE9Xr$oOS|x*lF9(^)a87-%F}byw=z?G03^|*~a1Xn|Dc7@)h_v zx8l`VTY(Y%R`7e;9N)nCvR>h~HmSCfbt8Qr;*Pak1LY*3&F2FbB~aW;vX`jM!KOwsQh)NZRhrH`v*6`MtWMpTUN;9)GX% z9$W#?8=v&m~O`oJ60Dwg<_n~m|U1Rf*A+K1y;9ArD z59h#|C&$|{mj0)eBSnuLzsNo)#OquWw2!JYJo_4F4CDa$cI4K^xaUc6@(zP-e{j>d zh=*e>_&LPG+#hE=CT*?eh7}_Ed6jsBHs98MO8*tjpTLrM95^GS^H{<{ZQ=wsBfKrc z*Z6lnJ~GaONJfRJe;_F*C~N%LuRWZrp1XAhpJ=WDkosRFq~-sR5V7PGfKwYM9TFoT z!D5<6fLL!5Eyh6tMZV%+F`{sBnAz~Id7C?Z-`6UWm3T=EcNcc+h zkIRoL@7&l4&>4wvLvKDTEus7efc&N`0`uA`%zdVs+0gL7keItb(eS8$T3go*dOG@v zP%%Ll5=VLJP|U#66jSRL58O4p)a$xC=7o<>I$HVMw<(t%KWw}u_>4Zi!B*1kg(1>M zJz}M>KDQlLfX`#wm`W$)^zfT6DjPEu%#dvv(KWORJB3$68Z49iZKUxzI~Cw(T`~%} zRCww`f@Yh8E9v%VPD#uI=$4HA0LSIi1;5`6G=|=E^3Ipo&J2p4)qQh=ijm2gk5qHm zcDMv2$es2>Y<^Wk;Z7^nNS&7`JXwMGyipSMA1#2GI4*K`fIG>*M2o-Q0|Jmc_vDciMku>Xgm`Y(vi9T1m412 z`I21g#2dN$B%&`c@=mV7xBqIw4|A18nxgbY?6-&t5phPal)gI{j* zND?fer>du;&ddZwTYK|dcE8%~eR}Qy%1g_*Ru0M;g8V|7$cbm;YU<~JbU3ml@K+-q zZ|wP|P~tSz8~;di_62M04LUb*OUz=Ga`q^6JQf`!+A!bJXc#3mHlz%q6<7SUP_S=n z^Fp$sG8U~Wp+>fSJML|{tgzKtZKIE44kX>yGJREB>eqbR*=|u2#4 zGrGtwk7~xnTE1<`7^$l8i~-%!{I>0H)M>=*aC*0Zf1VzFcY^gWE~tQW4%H5q3L^5t z;_WsSkaDjDF}rT6L7UMnE>_o;ES|S2Lx&;tw@?M(y?R{S@9zkuQ_jSf8T`Yr?h4}Z z^TyfK{$oCU8*w7%=f~SX?i+dmXO!O`37-gG6rgjTFj{~uT=G6ueoRRwV84u_T4}l! zb+CEYxbf)pmo0#lfL5w_^Nk7JS-5I3g;F(^m2E^_5u(CH_&g$c^livS$nz!NI&1qc z$&>(h-V>GQvmJ9=bmHy56`s~|cVSG49>@V2ki=Ty8#9sR7%WoemR+g-sc$?3m=UlY z!To*7t{vsJ>}>9=V>1fQQJQ649F$s{WWM+K6{3vWYCz?B;@b>lTh|8K2Kx1N?69d% zZUFvl^$@$L%@f8pP<=_sz2tI?a+r)m&>G*jPC@OR*5|X9n1}Su=!04n7Acd@M-iK| z7M!B^5w(~C-4Sy2luwJ}u=U@X@O*hB^ICD<&{&%qJAa6y)fT3jp8l233>A85#jL`I z?=1r3PU&n$i;R|{-;tW1<{{))h$G^DMJz4=I0bLUB;OLyX&%WTo3(4?3+aVt61(?T z{DaMA6oxT3tH(EWgT_q{ZfmKy^4@IJC+6YnLgvhBO@A5QxpmpAZ4hoJ@`{pLBtyhL z8#ELL=>GyrN1lPf%GWQiB-7|uewE9EL4}^GnLV_?@?FpJM(*rSNVVz8`?Pnpu_j(1 z_rrpB#n{52)!xw&=BhuAN><+X#;ZZ2=h&)VMXR;_zduL*{J?FJCiRDGZNtw&PEcF{ z)|U?6O3%!OklEjJ%czg~e$1rQw*NRQleD>c-%c>$4&LdxPotqhzx1kE-`LN48haZw z5`K!C_(M}3B764$WWcJ<8LKUc{i$cMJ9XnMgZpHROHX9SVT$T5AAAa4gKy(N+! znIBUnW-e@dK6BC+7_jR(zXxVh8smK z?iY`f(_oW=ibd)IVC8mWo>A`n{OF`8>@qssX-FTwkdQuk^3pPVKR{nP z$#>JAr;;tw0-bsT$ER80uf7>PoBNLO@kdj^ z8Iii;udtUipJ_7i+1j@SYrJK~j)WuftrPMbWuRfK9U2s)FvVR0AJ(<$M^!Wf98X$E zDOKtf9MfpnCvTwnj1x{=T>!N-*TDzN9D~<9opRgxsiQzdc`G)7k!J3 zthW2z-%8L_J~uJ9*taV_hFc5C(pLlQ*m3C&@6);cZ8x{dUK%ZoIpk-)FFzDI=7!|% z$*^%uFUGNc#@P!0%>YV)z*bKkZl&Ir-yr+g+sR)mt@tye?k`YTx^JTPGTz}r{jR)t zF{QD;7PaEi(Q&r~+;sGQlJ+28Z1@qLRklk4aO~g!KxjNQ-|VbprWzMnR(_D3qhu_! zg@&v?Jkj}s1Fp}VyrE4;lC+mt51auN*IAz%%|jl7v(}#Lj>an$xW`|r{ckq*o;(?R z22`AW zuTn_8Do^$agv<3m+HttG0Y&Bpw^RjoFST3Qi$u120eT&}s}?i6j%u&|cE7qe;p43^ zhGp*`?U+kt!;KX!T74PA;g^iD6X^Iu(Y4Kct7?Lmcx`scp}#6u$frdsJRz{bIMd*E6ir6+rx>4Epz`^2KwbS6Z_DuKmr0fsw?CG&w6D zmQq{OnKZ^1pRQ88!ErSM*km`*hhKl{hcPR3ohfH_VYf@0u+Cahc~7Mp4CowrROeHdT5)|&*F>;%q+M3uBnmRx z+27n^n`sMV=oZ|Ls3J^ zR-sn$P!aAza0Ff9U3z`uGF-j0QDtLHM4kJrP-B-7|3}yc3b8VN#Q~hk3FPrj@lRYr zL0yHs)y4KQLo?uvXu0T7D)IJ6g!;7y$tId-cME+1V%qe4lXGrdLqp8#ksqV=ga)!x zHnre(MR{}RJ!R78)nP#(tlkM$N`0cl^tBxQ*_#EkB=Oqag8Ou{oCVJ}OaAB3^?!zW zl1F+4KH&?8>Ab1pe|j_fVJ&Q^Dq*s;!iW#fU;3!_m?fPE4gXvTTVE57IuB0{c?%~cr z8`eL3V_zH^2g6xUbv7NsuAjX8Z9nf&8{u5+i2{F(>U{3O8sBa6G?jjr_Pwj-c&sIi z=+fBz`Wz75t@693MJp`2cf)a}SaIpAgjg0d1dn!^Y=Nz7-+X0rah;CtHb+g>R0Jg* zMNPlneE(1Nj-eXUy)HN}8yae`^~#O5#_n^dpfy4kYH_k5~=(+ zua;1>2)h%{xR7am2c~<`Bi03z@bI){gBbZM?e2g(nUx=8sD;g<`X?q;Eua;A1-4Nx@dceNU)h4wW6w)#) z4hCk&U3Yqydz@`uicHT+qP-^X>_1hc3`|XIRL%8AxF9WJU*9M0zsaD%4 znZ%977hf;BzVccZ?L~a8S+p(MF;)smrzwJ$Mi)Z(3G4E>U?R{Y($f{upD)@b6R19* zPAzA2XE+@^n{fOeTc60gGL{-s^Q)*S8LOd)O!B_R=H!meijzuLzQ%|DzNhT|5JQmn zY4^s8>v_R&j2SDJ1Mz`{W0R5s-K>nj{c)ExJ^#j#!3u<^`OHJ*EJ9o0?IPGz5XK#L zAHrhd(rsF9p=AbPOj7Wm*1-voXw{SZ7Mh3l=aZ(c#+$9hnJZt}0vOb-SV!W^4hD}p zj81Rf4PIBl@_sr<>_?Ha8w&Nh+ctXUhGAG9t7RYH4U3k7nFq*G!3~G&foi1&KrGvYQ4wUzXz|Ugt%E@IXCX= z;9`l{y{9+5%#iKU4Q)15%~1i>x%bTklr4#4w=Vy=&aT=ce}ucS>~=DH+ldu>mk6YEu9q-Pwxl`PL;8J#`-+jCIr z(gk%Ue}-A@CVW1tT%rZdX#=#rs#(7e?1KkXG22g+H2J&?M+SS1v#mdLjQk$2Q^OSI}XUXWKEI+ zxbKC?VvA8`abOg7-An++vK64yCARNINCJ3$*Vk;%m-hsXQAh|+ROOwP=!wHMF!tbF z<^5l3<$O}r<3v*LQ~&i98@s;05`Ia?`2U-sF8?2h@)uH_gGt=`tCR5)MToq`pIGtJ zN35EO6~ie(mSY4D(XgwGLV7vDw{>(>JZURc)YcZj+hHp$UGzo7C|ypgM$zt!wrUo~ z{cB`enPQkrXBYUv$j6fJe_xHzu@TqrP5%6#xU$pj=H-FG^^|A^0~WLPa~HZ)q4p}t z-Le;EY{rw(Ml#41YgNLCY^bRvc0aNcf54t%36PPV7BPBJms7RnxNJo={l@= zZ&~y|qDPG(r`VFb=bp&0f?4F}C@5(DlGccVSjT0Q+R$O*stHUvh!wG62jYuxAR8u`bi?vm~qk1iS1%S7GL7YNnrWIg%ZHO`(ANTB{pKI6dA0 z?=(6tdpuvHHNQ?758;Bdo5QBG{9kH_SwIf9{Wm^0yx!oMA~_O9@VVo>ul?b}OW+T_m_AD#+5WjU_pcUe zW|O=23Qf*z?H`Vv%&;q?{xC_a=gEI?wO`L$qTS1^+40rJPW_aG!7Wo$ipJ6tncsZos~X?E_?P( zjlH~>Trg~p8s+`7ZT?7sfD6&qOINFK5xqM>j}rF!sGq;Wd-N`=NQtI$FsR-_m9+UF zNAxlkV7;4`-p@G&HU9xeqVN^I$TPlx>Z>tzeOzb|QIBT{M43UHB=(!=)1rQd37M0? zU(k|mQc~Z|u}S+o{f*$!l-LL3b$r6pfRlBvRG5bPaD1cKP%5du@YGenrumw~NgQt@ zvHNLGen<$+*ZOr_cFCZ2Ho9ob&NrohWH?X~$Re2S`=({881~ZO{M#|PcnXmWD!WyI&`!V`Xi)ZT?KuaGLmpJDw2BrWEmC(So)0a`GU>BtrziONo zi)^&h9t7I?t+wbY0sCTKms(*`;L>S!Wfhj?3Xu4(ae49Y1NBKAp|<+LC54awhHFco z`unCWcU*pDrhnY3ecwYHLL2?}egD{*uWEG7{z8bB!37$KLq-+!p25dVr0U0Yie_il z<_-epE%VHPMvYnc<-kdMF7YjpWAQ~^_WZx9ov@GU2%rk9g~nMF-P#o)xxLtTb34X) zV+|a$qBOb7g6*^q05H(Y3UIEP0IJiGu~b&h$8CVIZ`Xw}S9UP=<>oF>67FL>H{bR3GB)4ICM7KAdgw$5d3B%+0-b)T zA5OAODyLDdg&qs0X@KhTUfa&$Q@=u3NlUlPO7^axd8#$t??{l&)<>NH6U_eE?HglL zU|7Z}rsS4}tCd?V+D0~ymB>s2$J%-FCF$(BE6IT=FrOoMdWyoYAaQrdp^c#BOQ zUWd%1(I|8k7U#r5_lDGGSx|Ot9jntVta#yf|35_K=F8Y~)VU>cYadMH!1UDkRw-UW zAZ+}ak9Shf=&}wD8%puggD)aGK@%`z(@bvhq9FQc6%KvDV3QL7)J+H`@VbpaZo>%MrA$DhTX4HFD^yX zdLfNoq`JmO6XZc_$m66ZwA{W#yGudeylz`)DMD8o_nh7v`iJ%uby>Y%u>+si-nsq` zUopez#Fd@GTecRD12e&hiLFeswC+Bsv75iOCcL+c)#?q)$Vg>vLFpOOf0e24a!mw5 zb-Br3RI;8jYz8^`>m9+E|SDz8Sap$OS~S~%%S$b<6pUiJa?93 z+2rYJ-%;u#ub?d~v9#g4^aPX)p5B&AG!>n`lec_=GRUO(L?1P6{ygtdT6r%XiW4aA z2aIQgq^&WyA4FNa1tzPzIE({V{_ufX9%^Rq7o>%%M`n-Wi+ChVgyg@!@y1np9G$>4 zIM%Apbj|RQ65##eu_o3+HcZsS?KZ8lu@$#y5ttlmbY#dvO{(iU*e_l3!!`Z|lG?w) zKrC={U$cnLDOcY7H=O-%5(-J(FLbl=J!x$uvEQK0g>pPO@rr@rR)-TeFa2&mRy5l$ zjr>ynDe>fy8r*1>{yO(p1s>lIr2I^b@K0@Hi)xnp4)ZngA}RX_RdoM}D7iz@vu?5; z`pZh^Mju3u5@b#HxA>pd1)ZNRdxd5%MYs|={6ww7KKHn)?qie?bGc^$)KpMb#feCQ({oNb zcad=#DCWYwr_9_7{w6or-8OyQSk2Id##C+{BKGq68fMtKG>v~gCADoV9~^K@@UDaU zL_3+P*`d@d+ePH1LJ5znezpw9vgM|OQ*NXC2X4yjUlU|F_4d_owmo&Q-=`9O(RK+p z3xXM8aEl@6K4r!c#lsJKu!T&#i`{F*kHkJ}`}1}oC+P-RbCqCE4n;RB^;K68#AnNfug2K2Ylbk*yD|3tMSy1=+$BzP)~Md2EcR}P z&q`>2nW@ybmoN9qhX4Lr4Des|*bR8(sS5Z@rx&VoA8k!;FMhjYeJXU{2&q4kSNSb; zAJNz|pM&iOO#xj)bk&~06VqMG8MsC0U_k%7@s(dmB|HA33wAB=X6Z#~y1@Y@c*rUnk(vxe-FoBb=4-I-I*+rr8K3|SY> zJQQrCV9WO!;>l(r_CKxB4aLdS{%KR%zmer1k-z&lQPmz-U&b(s+PO`Fp*V9OzQf_W zEPt|w?aEqTDaTLc%G*nTMH1mP_Ho`np$rKKbwX_>53cgcQhCu^)P+Sqa9g@0pLf5| zAR24GdW$@Z!?--LUf-@`e>FhHCd*ZA-2i@*pQ<0(iO;H{GI@lZMDJgZj%f{ z=r4K$o6JhJlh(!~8z0m+f{=@y`)9c}I>g8A9@3QnDX_$$1hd4REvF@xs%504JOIK@ zLPK=T4O8>Dx;Hg54Pr-;)xiKGc_;LoUhj8^8A3pL8+Ga&(_FRe1qKN_q#Zd}qkjcc zzGzf-$FcN?`06ISV>Z0bGZ}mS3|Tuo*%0ZVy%^GeZ&nmq%dRMIgAJD`j zSv8&_SOhEakwo){lbrA>g0%wC0xI(GM2|TirgTdU%)LV}yN{`?+RUiQW$$T^%gTdY@u6iuz9Tg-ur@^f*T>9d6N5w4pbY-3Mq?IFW4d1{+N0O?^1DB> zR|*ggjKOoJ==GDMHGHELwaFNvC2XZt)?eUrHrKzuBUgWU+mZZdg^erctuk|QTcTS1 zb{=nf{`g+)&cBhGW`|Zpd|*5ot&{5%clO%3wPN$U54t6H{-_2!jph?e+Md~2tS$|> znbbEK07gSpJWV?SY#bc_Yh^w}iC}`nepGBoXYCV^e3lX$H2bUchTV|5HfcmIGLM3H z11iF;yW_^5Sv$3KIjIlRe}9daUEes%h#3`psk@}9y|)WD1ZBSgvSECJ=R$OmqF$WX z^U|5q(#V6FW==-ZEef#s8^(ZFOQzU}*|O=cnNP61eI)IQLCTO!D=-UTbCnXu-_&nP z1IKIyXpHLCn<^vm8Qj%lO;j?MmN&j5)#DrxjByvnS-L+^m|@V{N+C}QP^zD7UJImC z*HY5zqAUk=01vaeoBd%uCtMwM-1HSzkW(f7NN~O+)wM??Nt|?5V25&abSo6(jwlVS zg?n>;G^vljK=ln6t_o^nR`FsXFC&kjEw&c#g|WFTH$qK0S!j*KgINoF;mCe==BxZ2 z?*f%9I;H@FdjXc6gu8;wY3A?vC@#Z*GS*2LW1{;RXf^hs z|Cb^YuP2gzUc{Ap+CgsGnEd#<)PMP;{lve_?y${*zf@I%W3-mmIK4xP&+O-MD? zcAL><$@9*!?9tkYL3`fEVC3R;!lszCy%hNkhU*Q1X^je|aFeUEvz;6(x-6hIw9o`P zO(q?UPX&hTO)BPsVwcJ8JZZi;@jL(fGZwaLe|) z30wb>-2VS9M^yehy8kRk{&Mbb@|?pCi6CMuSQ!%yNGFr;nh$B?gu%8gz=<0+@ZM5C= z=E%^+{}_f}?F(jcAV!wi1N5a(Rv79n7Y6T(I&r$27iW~u&{pt?6>?CU{dSPcksVzw zEz2)o*e_8IcRKu*uic%B;U$LiTr<((3l_B6 z&H=#673ZNRg7kHG9DjflSq&moL;RbiU=chVxI?sF54jAXnh)CRB%C?ll<)4on7Vk8 zXu0DVyD#O3!PHbIYxVd9ezZ>j2_jDlzT38~Xlz=7+4%XzwJXidN9Aw=)Md>G4Zp%V*vro& zpO)QPHjFJr)Y`l9=^-c*R*>Jd`u5DtLz}o804? z`DqwZn*Hpa&fiS%&o3%+62p&X2e;=R1FEE|s$OSdO@Akdl;W0xMN0Zm_4ihT8~U@v z#yq@mkH2<7=8Im3pLI@$h?tIVExg;k=MPsCR!!Hp$L#u$IzGdj;XbtE@w#Ebp6@f9 z-8tLlz3f}+`hqnHr8lUJs`(<6>V3fZIr1!hv*wTMZ2wmau5`XOGvwFmwM~@7q3Wdv zh-~_oijS<`fQ&y!?uXCkXF3>6jLezb!}MVlJEAW9^xao6zFWXg4co@R(c>Q$p}-ld z@L8mrQ^dt8=>+4b4JubgpA$n-`O+d5Q{r5MuWVGR1C=R((U@4X!o(RD{gxk&qd}Vh zNssWrWm1_m{D(AJ-?|YyZn7{IIoeuSlsx0QSeU8pgAM)-@c7I!Dm1H+-YJO?t6j-< zb#24W;?*yY;F}l4`ehfJ!w*~eRPlqRC9HOnyUF^K_HX& z>I1W7fd#1pcmBKP!FUAnVTY>IMFPK8&~z{Fu8f;jJ(^3K_KqpOS8tlO3ct@-ex-J= zmv*aT|M=P>{8W5+D%{r$X?^7X5q0k2O#k8kf1~K&&{@i%gXoBy=D6vjQaKiqa*QnJ z^Vv4#6O~X&4#Scncej4jwSv&?V)w zAK$R)0F^|)Ebe|dQ`j7H*HPcv^WCQe1({d@@m*xMQ~KB@I~+qcxahbmCbBEZ8+Tma zsfag!Yf$-L#%dqt3ps+^YROgWY9W(j7L%RvLCIV9ONy2L8-BW!H@3=+*ZK9$j08(M z%tb4xba_H@4i?DsH#zqfM?I?=(+U4{BhWj+d4u&^TqRCV%(*fU&m_m6ONe4g0;cXLEVj5mS zCUNv^P4lQs%nF`o^(fs9MkrUuKt+MrOUUtheQL!Ltp|&0un=#Rj{%=5g8N|i zg>=afOW&x*>(N+clG=+gL!(P*H2atJ$HYKy6J0)XuR;05Pc`fqal~NwLO96miNm9P zWgr*)s&wI5mkWD^T3-4n8mavg_dQTB`IN2U_tZt+TT}WaC?>Z`+Ci4{5K=00KNhsX zA>yJU!{CsbKK<>U6HID-n+$(TGnr(77O3B zG1-Spq4FpRWU_w1;vh0#!At4i-@PG(a%Y+{_SC!<(H)F^QzSYv(pYqQ_v;g^X>eoj zWVt^32jb=7)0VU@AHazAgw3(`JEKrHg>{8Hs?vEJxn_tpwviZ86@5v863n_!*(6+O z?C?C=z?w5eB@$l;p(It_dmjT_;)kI@@{mi%##e4zg<9MA-tQ6? zaS;Oc0z9IuHt#%$bUKnfjl26V!>@ifH~hEG%d)WV7ZRpIV)D6V_53Qs1vI&}(lOS* zOtQ6>#TstA$JL9uW;^))@63~S-%jEB2z}%_b{fQ6TBxqOcAXxf3%k7OKP-;;ZE>u( zD{Mk+^c%9sf<;^Jrz`!?3Y8OmSg+}fwyzMWV3@UFg7_EZ@#&^)-ZU1Mp-#^iJ%8|i z*FjY)3!EFooj+n4-vLL9F zlwm>*1-EG@FNB*N4&3X(QfvuqyN!vKQ4i8SZ9(G!V)=g07A+~|I4^m0d`JcO0$-jiM-W`lg@3aE9$+B z_{%d5Wxl_&YzJTcJ?zRrPjmfZ(yD-O#LJeRWu0gm9ezLJL&v5-Hk#6;yPMp`b21C> zI}p+r5;}$pk|~w#JhzmVrfU!oP#HXTN|_(w%UB|TpQ+A(sNBW^F+j58>iWKpLXOjy zDdNnsmO9pgoVss&O+%!DBA>Nb-CaHwx}N(R1aV=FoM}qXk&s(*Up&E~F#Bbq2=?^Y+ zf>Knq+d>T2wnlFB9H!aN7k>WP+Rt8kI+w4v=m;CfK3d=Ftx&^6PUr6@S~d@}eISj| z(!a1{k5rwR!!^5rcGphJV7({8X8tV;`J7Zo&-FM<1m<*@FyOLUk70Tfy4j87i#J!v z!0r={^91ia1*Wg>W=8p5pZ#yh`cop}7R9mt9@-;Yfp>e~c^8NccdFXve@&Wi5;`h+ z;8@fbOU8r(E2log+Ba~jZQ$64HTd$2{zLWGX!3$8s#>K7qTw$?h;k{l=vbr zeB+TXj-in~Hijl>I_p@CgXIi(u#^WWhSCY$WaOXo)#o6uF8A519y;zmaXq=MlNiXp1?u)54ze2G-Ju+iGJI_Gcf;k-4& zIfeSh+qc&9!$koX8uq8f5UC5?y=n;_zG-6w#9FhD+&*o=VD|VRD{$ocLy2c>hf8j7jS9f@AFg}@rWKTJ9-W5a)$r*u2?U14~3 z-4hkjF>{)}U6${u=Z12J_{V;ID&jviui40IC}Ir#Se_f&8HyT@6xErO5|>U!HODc( zMni;qr@t0{HCS(0iWo<*$*s4;L%-4bi`<2aAha&+z-~% zNj66cUxlQ>@1qmT`T9`RplQ?iN(bWQp?Hw3?SGq=vG$x01%7pvFEzBa-#+w}O{xJ( z7Bv$f`<^8~?c21yiW@>$@72X}^Db3k2N3&ktAZ|Vn!}uG!M0)m?H&wj3%t1*1ht?_ z`;!gKuK%l_jFxc*lVRvck8~$`=Y|q|Tuwbx*u{&FvOJ!MPpXrRJT{KKu1C!2xd~*v zA^1++yP%3H8E3gCF<}DoLqVn?)VL7_^LaE=k0yq{E$YZ!_R6TSW1p+Lo#anJW)|H_ zecf=y?WAp@p%0sb*L+Wm*RMM zAbW;nA2lNCDhjW{iB5f;sq-ZW$%b|I=+Z~xsP?g!Taj`=%d2PRmp7jXU5w2W`fso> zXL%t(IRv%zWy@yrUc!F^GxP}f>r(qZJm_ZxjO;x1eWdgsp+-xmQpy_py#ekx@^PDB zx-c{pUzGWGB3W;NXt`N14fTr0{mhSFQ7r4jENpj=#>Nj_APgi_HY&hXH0rrarm}G7 zNg6L|8_t%I9vV7;pLsL@WGrNtMV~`p$5=S8$lu@IB~0gqi2%gx%v(}+U<1VF!@yzC zYAa&H0;q8Oq$E*s<1NFHyY?sX%~sfJIUr;)8W-a%A5L>ZEZ1|U3+0PbML#spb>&i` zYiJ+g8|;oqnH_7Iw2tEEc1%($QVMpC=BB4Jvrr zb-CqX2`dv}*bBAOc^Upa18o{^3jZGNi;6gM;4#1x6wM1W zOKJu~)oIzsBqO_%Gvio0(L>S+=?ir3z0=N69DkxEuXczLuArX~nxh;5 zMvoJ_2L}EUK)bCxQlM6}^g#RCn_YowRd6J6{nmw^AY#O%iqeLiB{Cm})n&~u_ngZ- zczXQe^2=Y=4(~&GRm^qm$+PO&M*<_G7p7Zk!lj!(2_EsJTzT#E+9-X)Xq_X83r|J= z1YpNH;P(zokC#598p9Y&r*%=_)EoVJ^=aV`v16RVsxDpvW#-WW#mwxJ(608pL_^MgKj9FcQoqwtVOH==U0Df@?yllRb09VDYv`X%KdKelUvk4 zdg6|#Sm<3Gfjg(iC@4y_ZjmP81mr3tGmfXB`uwBPrYMRPPD#vTe@eliCjEMT=n*Eq zPQ<8h8tem%q>>1G2d#GwqhO$o-Qsl*9I|4!X=d~GyEg&anK#>?h5uJ8G56l+nTCqutSgj%E3H-EAXi)@Mz-~_zXpFB+La^-AOrx5 zb;jAy8vq=`7Xon>-@Xzb&S z?#Jvs{O}z9=oz7oU9;m_y=Y}d@rYiUmbb+fV* z2Yh~pU>x{i!Br`6rG^*$mgX$IDd2>^#=sjID4AefDb1$}dF*V|rk?yF_aeB|Rp=r^ zSS>U6)9k-6K3sl=a!vzteqs@Nqt4%d0js;qP2FQB_P=huwz|cQJNv`-gG&9%l7DKA zw5k5Z^p)s*O6xS7?-v&RrVBswBEorUUW$Wb&@kn@Vn?-WE-kG2mK?kC-||#OyTZ5i zN?)i>oP{&?{gO4~Qu*SuwNDdVXZA~~UL@yGGRpB+a@Fs6)JRO$#Fm~-Iw4t#d*}4} z)ew>6k>}>%j>-oAux0)QuY!tn)8ze6jVu1WoNwLyRKlMnq|ptHUsS z&@gWXrfR>IQ|0L8^-k+>xE2Jo2p`EnpEw~xcFYNx=8L{1n6-S%^g4Q?1ew^2Dp=#) zFN_BFe8Zd*B-H3#TFvD@;b*7rq-mK+8e1osay9rX=i24*`XD57PnLc>R|CFZr$?ll z#)WLWR{c|3=<*U#&S%N=nh9r8a1xUQVjk|wpO8T@h4Rt>zvLlJafA1O7 zx%JrOysVQ(RIpBTBLRtxTKP0oR7!g7K$lltzLc>R5uo~Ce_5}`Tk&|4ic zZ6tP!OKm-j5uG&c0u8-|hXzN_c7w#ee5`y2qA9rh@!fB&dv4pTZv-7zOi)$bX+(Y8 zlUhkeK{_eydIdP0Y}Tj=R1zS%+tZY*XTarnj5|*{ugg(8)*xsbbNB+D`LX}e>DAs} za3*(WEG52bTqRJ>6@E&|h3dW6Ko_@#1k*7FwA^0P;`vPXu)b8?2m7kCd!}CSxQh-A z4%d$ST5ep|&x$HcS)gZ@J$#&s$tXJ%j@%;1qSY{BRo|bk)W#`N@`i68Fn%N3USz0l zC-0R=eOVhWa`TZMzu)ZpwgF~m%hZ}ozm#@Q+gI&(c3T)8B1&;Dj};x?pQn4o_HOLuJEu zda>hHK#s2?UuwQjv`=7rfE^LN2*Wo(jxhu69<1Aj6t5>g9Bj>J`lE$_@tn&h+Sj-_ zjZ1Vy;Ed28MWe%xr&jNp+cGPyS=_t-gbTg?@wt{!H&)G3S(fC7gEw9xY>p3SdcWB}|Dyot)3r6fY&SHsv>4LlSo&#B^zm~>@{#dB z)^zEydV+{Z$Du>f?jES}v(J@+ii7@yh(w+tZKQaMw;9Mr$3ceLF2SrWYv5I zhc8F0Hx=T@H($ruzfk>p*wdx$CcqQq{cKy*ME(N{`8>KmL4R75TCPNq|E`*Y7#vZ- zf`U+pv=kNM(XEj}lL}`cU}iUKKPM=JB>)s#|F@m?dL4NgSx@(V21BOkA--S+W~)CN zUNFI{eGDee#qPDcelp@X-|&_x^+rt)14%6#Xkp~xqHAk59A;;9KT1aXDNxM%AEv6V zE}Uq6=Z}g?p4B)CT!){Q#_5Mk0;RY5g02@g*(+H#YW%!=3Cm7TFE|UFI5(rGBe0c} ztnyEwvFn?dK<-+7+$m$a!A94%^DO;6^)pm~3>gFtiOfHeDM;_GpTAxm-2Xhj*iX{; z2`9YJ(_*}F%JYlrk*SHZor~-|#;qpXXk7^kAw0~I23Se(Z{E-p=}~We9UI_aui*&M zkDLJwUJFw)-e%6cbbssJ^mAawL7%JR%&MhFvYa24@?#bPGJFBGCIT{)ZzxLG*SrzP z__J(et!dLVfP8X~UrrU;CADKG01g*aj^R}kw;@q^`>{A`d^*Z=IOeOjhF#anGkmlF zT|PZB@`%NoYr)nDA{!Q;%SMA19Ekf{63q@3>nREjJQnr&=cf$W@HM_TL{!X5mFe)u@`tI?bU^Zy z&bs*MAz7PE?@{=VjiF1VW$V}`57%0vkMrKxbG5z&1vO&mT!@dKIdgq_UR4+*@)&<| z^@OnuE*AD?*LR7yoh0D?f7{*x=(jK3*_udDT@g)i=YhG^Om?o2epp#avM9e_xPpnnY|W`qoyC;u@1T-j?DL&#t7CF%@oG2u+ zL(m7c^TLm{8MCUk810+$$;Xc)pkoFDVc$!nT#3{k<~G}E;*F(WxwI;2D~7xg?1u7e z(|aH6Gk(6f`22rH*bkR_D-eB++WHq9slHTR^^UYmVynrqHrf&;y=VDO@L-b1I*7rw z+_{4if?-;bm^Fh5v@=-jDt_Wf)SfSx3|Q2QP$$L9;W9YGC6$n&218)rmli)!t%W7i`V$rZ9a{!!;0cY(5$EFqln z#A72{1m8l;{v&P)hsHnkG72d<*pzeRQ6+b?p0lZJf6E7*1Z0%yn!R;~FQIduW=Fe) zH6xZ+_ER5#VEehR4Xa&`#ddZir04dpUoJ%dR$h{~Yq~ttyxJD*t~+8>h;;O}*qs3` z$cpd!MPbxFy->>qH;--&f0qO{j|o-KkH9cm@s~ONJM99b9uUIY;tm69f2$8JB~0IG zZK-c};1^TDbL?2K)SAEd3F{d;k!0EC=Uk@m-84BWAK14#&8c)lm$|LWfzKCv{m&CD zYKOcB9T{}%nTrVyJvH{x8^PXECCo=BlriU-kavbl9ew|(B)j<#USjY_&vU;e@C(Gf z>#loZpY|w6z4@6@qo94#AE}>pb#)48ZeHBF2nyUBjEk51c97TLYK{>OM3?s4$CS-ii=A@j>d^xcMw=?Z)g zo)S*Xat1kQ9{i(;tMkA=m%{t-9V6Btf8domtn?tOef&8k+D);;Zd z? zq{xp`e`OaQjj}$o1=a2tG&kimmen7xJn4JwN)WJHAeRg`MC!k?qdPkmHGs#?fE?hR zi<4E`UY6%0oa=`5uk)<3@#JA5t=DJO!C*^=7|N8hJ*Z_9_g7#&r$!X`=Yp#zSPwU; zL(08(Cl&Z^a5pxmm;^qJ8yyU%jyqFuP}|8rP1_MM1yXIL>gjS$PVm|&|B}}&`$a^4 z4s)eum{gugdV~I%G|R5%$AFBV-kMEjX1XzBRqQBiq>`%2BhlC_OojB`u<3W7OoMr0 zXExI2#7!Cs#%GI1vNGyLJ! z1!u6+*)7ITw74nZ*#|Vz^MwBa>~1T}NhDk3sq4Jds?f_vvEq1_PD7?vwFjTOq|-I+ zKL6up81sl?sE2Qq)StdN7dFs#;eONh1qC52m>SP1_c#jq{yVwY@=P*_=t*%5 zo#;E`K(w_B{-a{hT;r$gpp!gOV{3ci?|pGYKAYX-NUC27ApXdsau(JIjEINxUrj+6 zF+MI*GZZ}48FRD@ThGh5g(|80h7u#92$pHM`nk8}t?1-hoZloV&W&?pD~`Ag>eh2{ zQD|Q}GmTQX)p7?surWbTcxLCg1{~5Qe~p|-Oz7agE$p8rE!Jk75;}1@=&nvG@S*HQ z_D+_kj0Sh&)|S35;LiG#3iFtXJV)88cZR&k%@vIe2^9*RKHCUi;XQaYAA)ngef+KK z)@W;ygLiOs_?6u=My%Q|HrWP2O8d7@9*?~h8(AcQJw6_0WJ!OLhiRYm2}0!P;)#oJ z?;6X+Jow;e6p!-=UKZFTE*J0Z?kek%Z4QX^OAWntHP>j;@Q7^yT&h;x{~-=46d$V) zPro9doPh2A)2DR(Oumnr4HiBzCI`06g~u-q8Z_LI!IENP3|7np94aEa|6J(Bin6ZD zAF)Hl_jms1-(HOI{llMTEnbQHx0i0rQ+68iPJ8LEeLNo}`Y^DbV=(b`*qp+ETOyU+ zlvtem*H=0=wtcStUbQ9~1dch4H~>a|t6ymiYwR9PoNu;3s$ow^GCm3YDQ)DNt|oFC z)uCd6(K3|!g>8Mxp~gXHHl0RsK3%>rosTtQSqjpfB^7Y)k#MZ6MNil;n6}(S6+Lmz zGMsS%gh}o|F?t>@DkYv=fLg5=NHtyKJzUyMRZ}!e3bha5CX zEze98FLq4WybO6VhQdH12u#NUR{#1DM>u5%YZ(;HV!R6Gy8?fDI^7LZBWFUC4GUr# zJc!mC{l3Xk+gi$U@EtEmRME2Xi40f9MUA#WXLFE#WP^cq^vMxswg#86czhc;B4X5{ zaVhqHMHATni6(;hVnC9meE6t9GI12hCI1R`cKrWJCiewalVHKsAow4x`)f$%dz^cl zsWT-5^5f@d!he>?DdZeWR$W|4pg|zaLAIVVdw`nGkw&XxB52bULzOBf#Q5sG{eL5! z2y(=mp&ICVM;3YsHaSY|P@!~wQ3y3ez#9zU;XKZpX&EA+eLogcf?@?mk6WHh2g4dY z9U6^$igAfvZ0sv0z222~$7Il@@QVZ>q|#mG6& z_H3-Qv?2Q^tyY&`{8^C!92`Z-h2RD?`iQ>(AUpkJe(&O^g>7Y-AdO#UK&~ap z9c)qkT#B>xb4}Nz#a%ey-g8=i&i3cW+dt|99%lF0JPLP;Cewb5Y~={Mu3#Ifsr4S^ zIoVO=#iu%Q`_zJ^xegsrZI20j0>>4OUf zegVyF%MCS6Qx9T5?~wmyO2?6=XlGbTf#~_q$&d?f7mR|hoC4qiYYDsdW{WF#@t@+A z)^h@i6{BEys8q?-wlFvD%eubUdp0EQS4S?26mE$0 zgM7glFbcMEUmF^t@T9b8;Q_fmK z;}*Nf`1Qq@4^j(yH6!<%Y;6SyeSrkkXt>x{qcc7FA`PU_Bp?DdR2p*ph$_91JblaL z6uu+sy!l;)hyu;mmMLJcgZzcs9yVc0vd*hq$wpS+=LDm68Q53i4?g6ShTYE8R=ktVrhPLAIe z(>Qt`h^@L;T@*Ol)`-!upN1Gn9QJm3DWk1HHr!~{Rb`u1F2lU;&A;UrXhWN3$`sTw zy<%a9H5JWo8}_i*>Z2?l<;nmVqS|a+qL22=rbPl5X|BFe`XT)v=pM}c_{fa7ZMYzN z1Gd#{qp2Go<@1Yb% zYHK34RDw=x!O6FIBOs&E+pc65)*kI!ANtLA@cw^q(?0_vhv`3qr&djUN%Paxj#K`u z_bF`O<&U#N*$t2^k62VZ+BZHwt^=6HTP`Hs)qBptAR| zT|FBw!BHzg$3I2H5Oh*T1{)*zTxoQ~4Uw!uN{tFjo{?4Vrw|lhpW^$fu{x{huL&YR zSUKm?{Nl0lPCgFq zk0Rwt0CaFnwuVrJ%Ej^<# zh=VH96!k&jfjkvkuA*&BJp`#HXZ*(OZXo*L^e(vU(YS|y!X#3gXOfae?zEYK_3v^I zYhX8ZhGR@2s;e&(Pr5!HRZ{kp9y`<~NO=-O`m+5~lTx<4ECsmTa2qINm*d6O6_Wk#d(dwO6}e^`D(A?WrE zM;{0nyGphU)tw3vzTtMz9}&ZHNP|I~av4>L^?1!S^5(*=+6$pe*0|jLiSHOKfntC# zDN0vlry5PN1%SALq!4y?2T z_-8q`o-y>eUJ|^uMuS|G@y3>|gRfuucKz>F4(8dXgCeU_dG%nT(14f)8XZW|6%l!ox5$%&T%8k8Vx7Z#~TweO$*&+7^keZiG3EV*X^$CN?3U@R93Z-aiX`bawYt-PuW9H>3fc0t4+V$z9ukK zX>Ybh`eE$p78?8P&^48{%IvP?{KJ_=UNVBa+h~I(LG)XBgIa6zSpe(FVTL-`CP&aN zoFOJblMzzC?XQbZ2R<*pK`B$1xpdm1bnqV>dmxq3W(}Bc%`bfYM6h@^FyFe~KlyFI zpBk`8c{H5BoTgIG?48Dkv7*z(b^s#fztH1J{rM)UayZ!R6<#XmQo=d+Aq~Eq1%+r( zmWV@({MuGz-3eC7)|>b7SH=5fJXvhYe6i@ud%KIh2yZE1XVWu_zPwuf&x+{O*^PV-^rm(A}!zp8lW<0yTp&^t-kfzGy}^WGJR}!pon6>TA4} z{Q__GTa)X9=ePLdntqPk%$;gUrm4ub^3nhkjiHs)ua65c!Yi?5`&LCeDZ%1vJ?pmic zp$J+YBi)r{-rYmRUVlxOz<%VK0sR|woJUGZP$Ez!Ls&yPxNqYS_|HdQBYKAC8)@?2 zXv8hwPNK!zD-=Fqo>oLr+9@%}6l}MglkQOW#%%;2j}#*tt>`J95?fX`3V6Y!C3ky8lq{ zE!a}9qrp(KrI|*MgD(tYQDcVqS(}sU9^JxdiNn+IKDd}#P`NlhrV~l#E;REdQzlVw1?<=A|`Mt=yY91)yzu8 z-uI!+JAv+BR)m(}qKjcaUVoX5<^+2=a3x(%0KE5O|4vhUK+-QoLfev55S^M1pAq(4 zqVx~WKQkR&s>~&w{`cj5#}+246GAKRDwu<28bV*)E-CW*o0OCJvex4`Clb2_>+x=U zo0=A;4_WTDGjJH@SxAy;&Kn&3xVVKrf%ZicY_q|fcR1z09`Kf0l_CSUfX60|a0KvQ zd||dzVzRGpgB$RgjZ|o1W2E_^t97~V?78bIp*-lZ_Uy%4yD@Fpw|9>q2$tV!#cGDO z0Dx#X9*ICjUyD@7fmg!b?#^8M)l-3x;C>&_kfE7|8vl5rs3WP0Sw~b|BTDE>{Sp`K z%1ZRl#TXF4IqN_5U4zY-)fH~HAyFGGw ziKgedp5^2L=h{Vf0q_J_F#jBL$@-#WQC0hZ45*4{UQ_?kPXD5#-@mLR?nkbPsvW#< z*1JS!tEm?O;+LkUpz<$~YHMJQ#L{u%{iI$0&1Td}>e{GIx1;G)Rb)b)YJpQfZf4|G zmh+&5F0_!X#`1t9tA&pHHNuvrm$!;+xjp;ln&1u%8ug&&?hWH?zjNBbo4KXr{0G~$ z;oR4hWdp#d>N*-qj5(momJat%B;;~+FEDQcF$bFK(&3w0^1QjXsavdbc0xeZgXjHt zYSGigY>L_U1;;T%LC%)O^MknTyv0N|G|ot?rR&(wGEx-siK z&7#jSg)a^^mX_n^DvZw#K>JXU;N~XakhYQbm4zeG`bYPL;Jy_s3pVsa z-(Wu|MJw;pi{CM@bzKM>FGzkWl+{k54_>pi`_NnWeG4<zi#2m5L_g(km~jirbulP0WrQ&#}-9JZ-Sf*S=ty}3%AX1QfamfVOnG+GcPat@NN z#T=2l(kpWeh|OzgD1W=Qzt%KW%1Xq@*nPvkhIZ{#l%pOC%(<#&ByzR*HX_m6f=2KN zK9HrTpZDjLxc^+e%>$ry+@_G3z+}Lv4 zaY1#$_0LSPtOp;fzi?8rTn59%t|Rzvn(8@_?Cv`~4W-HsYOvJ%HiQ4Oe^WHaG!#EM z4UJDMucJXZwEVcP9&dfOUlsq}xmPqD7&4DMwM6e5nwKjRkUrg`mw)s6j$>F?b?ycE zR&VNC%5wu{rz)^AtJ2`yn!%{;e!wS8tjEw-8a$0RxrwvqMaXC%jswCfJ2_2?1I=@8 za%J#32fsN2U>w-WD5{_F1a92=fu4rz&^}*(j==at3%;=py?DE313g3tM2zWQ1k_T^ zSNVB|Lo@^e0!eHwcccZdew==^DYz|E_Y9J#W%o?X$kr`t%de`Id8?#2Q%M)G4&gPi za=KHSBZ~~lE!jRw(BE1Vw}LX1Rv2-psn`5C^u^a4=kYv%XO1?^bFvTj(24Sq5bqt= z3T9#|-5jB^&DSlDoIa+m!N6uvcRV_C{Xy)H0dru#lgZfdia;W9W6wV8eMyxKa_gc@ zA>&FeTa}WcS6phO_yM_2$uy~20tBa83&*zH!#9-4`I_6F`LCID=^k!SzYk+~UruE? z`+pC4hO?xllV~m5sYJ==Ild8?{u1J5>lKDO( zC|A=P^)W$%$%%tO12t;Xvx`JyyVk|^BWDe(G&s1P>Gim+Dd(TyY6J{gxpHgpb!Zva7~-N(wrBhi ztL zT<)0Glf9R$r=M8qpn^Z@d#(7`+y8zv6?;|5{7kn5NQC3jU`A3o*XLM^N+F2VgF8r) zhJ4;GQ-JVDq59&rQwdK|*pi>v4hQYqA8nj)U18tpczoEh=Q*8jDPXbST0AT=7}uzh zi&)sXK`Ig5^=bhM%Qu`ZWInmF*)!<;Yb$t=cT89ndJY2~ja@oRWL#aqMU_tSzRh^>ZfA!D8>e zL%9WG|JFqQ4h!s~|J@{WUzXO@43=I~M(#jL8b1knnQ z3H`TL)}jAZ)Xh#|47;U^x2bQ;Co3Uwm2s)w27%u*95dd1XODO^Rfc}hz6=4+V)S(u zdVFgl9e@GQ3iV3yKT2hdx|>;??V%d$izQAPmp`{v0qsjNz2v>{oVBH!kQlb!CDqE9 zIic8?FXwQb6FD91985<4!$Oso%hU%|ACIJt*=w+ z2#NCHn>d9fEOI8x9!pF?FWp~s8wj0&FbD<=Jgz08&9tpiFX^@C?N*J?IlX9e3-ssL znSp>M9rt$2f?=Hh@iDNWIovfpt@&mq{%qc zDm8)w39hcHwRIobPazA0n(uXgsf|6@HSZ+d5(Vl~8j#RR1H^vq*c~eZ2aUCFSfYDO z45IQ%E+t2|zl>Awn5SW4@DisX00Z|8ro`9@PAR4tfjPboW?_>e{iky%AGih@Iq z{SndRkt!d)8gA4~)nfrVcD^|zhdrc$ zLT-&zZgq&dG>ZHq-I=7oyQsaMi}Eim39d5N8Wt?5)A{av?$4u1LQ8kK#1E3$vo?&;r#QdSOq%qDuIgPs0OlIzd zib`rU|4G%0{hnhGT(zbEMD6jInfmk5hMg*5+&8ZtFmX%!JkS&?EMGVl!0S&YB~)Js zZxtVXejF*-{aDJ64n27_fwb^rvd1VR6?d!u`um32ZH#*V zjAd!k@@Pa9f3;%tvVMezwrQasa+a7}*@JTCf^)9L)$I1Qr%N~07~R-VlA@S+o;@)$ zBt=}hU7ng@=edH zecfhNukD-;4N7ZP>@?zcyDQvyPha%@GIz7;XlnR+Opb1(?fS|SzT`y${POVr1%(Il zoNecQ46Lg9Yj<1!EPK(a4hVLzTlDOWnO=fI7%rq-zsM8-3yxY~7txl6*$K})KQ0eh znhv{4Ow?CTkRIA1d9gMum%2GIB2R7e&F$OzaLxvXc?7;u&Jk7 zc;v>MJEpzzN27-nw>3dJo${(SOTGUn^h8$Zl8;A$7g%tC04~y_B^&%=zwM=pO864B zb=uR_Bp~Xw#IR{1$`~z(u-$3QwzcZ8D*ANYlj#~5q)2vL# zSe$yw+;pThU^R^zBL?y7Vw}LN1=9D*%Q?D$#Tw*Wu@$aMjyo2# z9f>d7p_b~;Do8H`?M+`*pAV2khl6(;U7qUg)kmKzBir||WCSjRdZ`DZ>kN1q%6VA- z)YiQ*3YP-@6*~XorDhNC{dhjO_*@w1m!dmc_!q!!D&=Mf#miB4T{cS=*M zNhmt4ow)T&bs%zUVD0V#Z;>RfjmM#VoDZYVtV~?EpeA{VI@pUS7eAay z$WzuhcS7Io>aj_QjFy&O^KhYA)Z+r-|CDMjPPp(W*fu;tdqSW33cg;0lLRB(rSq7hBwsuo`UsC79QfwEDFQ#R}y8J{-TlTcy{suO;<& zK^98UsJ>F9?Sk~~gqFJm@`%S`aBUDd@QR_(ko+>$_fY`C$LVJSq2!3yyPdsL&M{97 z41i5t;0UmHr$C}^-xkbcL>xO_)%xkga7sq}6jDkI8x$2oVtS6URQXZ*qlTp+O;U~u zx(W*oWu>gUf_%~wf}@R%dv&I8lv!Iz?5j5KK_~DQc&&5o25C9{3H<8eg1Na;>Lk5b z5dC@Hs~19|AD4uNa}MG*Vh(0YYWTlTocfm3DKwk#;-I;Tfr1l04^;So@Em#nkT4+F z-{&3^ykFZELg00NlaXLs31D%&!6GHsxzh=P<<7!#ZRGIP;AX%`K8(g^f0TJNG?AYw zsN;GN+hW}qyI@)80@R$>$YrzVYRk7(A4*;QSA1?l zHztR&Z9m>S?`(UfAj|86P(k4W!os(=WTPx+&Q1XS$&It1ccYJ2&76-H`4#$xDhHL{ zI^VAt-4&ROsH_j;jVDVCQOPzB<=2=k{#ClNi;lx`!E|hGYiQURBdw$Ff|jQx_W8eiF?FPyJm3d4*Yunj8<^u3cY%dY9wC*|Y~?58J&5H-n=ZRzFi`hhUmIX<^Ic zF0Yk+cj<*>Iz9T`ak=#rJ`PK1(!fn0+uw1Gf!O2tN{qP;81coAJvxM27%cB11)->C zNrjjLz~;E}`GTyFs0i=u9(KQTS$cVw0DVeHCQL@B2+003R_#}&+k;orqPGngy{Q6@ z>Ar5>_kD%ADtgDPd&LR>(fCfzz$YSOJ?CV-6Qj4O^uRTApF7PoEOKLlu$4AE@O&=5 zl(i^fMYJf+n9cqeWw&E$jeKNyfk(dfEwPo4b&PE5(n{12*6M;+6>-bW#OxSWe}={_ z6Q(-WG9}egJPGd|s|d=m^&)`9V9%}(2ZELgQl~`G(+hM~an05X+DFt&Grj8t+j-Fs z-|P@{EI0V7L5)vS!WuU;!| z>GhVlSu8ZHX_B96>B}EK(~^|{+fWO9k&e1OwMC0us$BYw{ruMTQI-Tj=z!0rf1x0i z&$_jD;+aA@Y5Ka5HV$KYZVZCfRwY+coR4>!0oJqm5juJLH-X2eoIIZQqnumMO;Yt9 zjGI+{ISYLGVh__jyi#LnwzjQ^@@SuE?$sS~lAjr6+UH~GoxyU_>lQ-fNQDy~>L4e# z*%$XcTt1+KhXH}fmT}X|}m9MdSeFey$+V|^>VcgNZWTjqvzn3orc|ECyfiG9JX}ZvtelYWb24CaXyooa5>S> zkr+C_skl3+_;)Dm(i4Os^<+*utu6m3ZIq^m-TiGB!yH5)q{>Vx5f}P!tY_Cx1}`=S z--sW9VlPylH>4pmyFy1O!Z(WT7pex3rDemYyP^!$tKIbnfp(QDbL*L3y7J-Vc>=Sg zsmT)%9i_-O4z`>Lon0xBm$I~50^`6JyY$j_QeFUugY2^nbdAZ!I5_T zbggbJZRef_2l@mIV^=c+o2hO z_B$Yq+k0lNcQb|@X34+3RhMqJ6scZAD?l~z9VG0IS!rm|iPpY` z&F;|60Ij<2q()ehG6wABS?KY%P(0}TaUzZ#?w_-nE2Ta}vrO&`i4jvit3~q-e>tOn=cI6$0(kwDuykjcZ*K=NwB+7v;gI94Eil@tWOI7 zFiS+72+cauWP#+I?;5SO(dd`{fN9Z+;wF1$cXxR_p$Ad~W8ZcFa$tsAbD$dg?4ATNCL%;t6?Bg53?IE?X+1 zgZ6jE89LhcB$QX33BN`fm$OUu%^SR1--)|%R=fKMdOzFR!)w&S4>%L`LS7Z%AF}Uu z;*)^#;7Q^Mp@7zR#k>~pvUKRXI}T4JTPHhStE%EED1HWEQ*23-GhI)hvbj~&g@3!9 zHv4H~FGQ)X@uGB!Zx-7?R~{bDf}upAh`}DS{{WU4_mh&{m2f>ogh3%~@m2yJu;OKiRRmv4b1)azW+ z-Y&lk&jb2fE5Z>9I?dVLv=@(ohZiHn3oFO4ndBh5DLEh-^w7f=9g9RRq0c`}*tEv& ze$&m0zaBEae}PP+*oG8N?iNb_b<=4e{W6S!Je>0MIA)<3+y?3gG+L$Di@@I24=27elUa;d(=Q|B`Ne`fG0$I>PnhkS?z zimyb_pWotd5k;r_LWudp>kqfkx%CpTN?N)6_SAa+zLq6=!r8INKxs!5+A&E2hq9z^ z+6kJ@HuMGy2VD&fk?EiuTbTMa`3Ug0eDH4A3Q}8iUeZnd;w7Js1TO#>#ggMM4VQV) z!6xvvw&cUMg(T#wg{M}(z7TbS*`+%_T-J|y>hlXj_sI2e|Alz{jyaR+9fMj@1ci!G zX)W7uP-Wv$&xDRfjGFAib*M{}wcjp1=xCMfe;QO;jHce@6?KzrA+x8ihVM;xU27rq zBMzLVQ764yg#!-F5&M*9`_bgQE4L1zX7XIH7UBar2Wm~k#8>wUhKh(s+nMz=ASsip zDrB{OQb;n9s%#YaQP-V6dDI!3ZwyNmOP+0(;puw~xuyFJ^QCoagV%bU3ttVzEkkDY zuu<_IO#ZFJ(Tm84#>z5<8@jE*yUeBBQmbri{_0=)&NWOWXHp`zSVQVm&*=$96!8<6caPm&`z3#S)iqZ{R^@{jXPx6jyay4jN&{ zXm<8I#sm|r0LV1`a=E`1he-Uwy;JaTxTQx|Fgd+e&V=jF8LN>RfxVJeArK%ur#|f~ zo0>OPqS}JfCr0kq1j&CAIkXvK!y`>o|07ALefO9!3~@c;7SUV_<@uM`k53tY7LebB58;dd zy{6Qp#+M=a8brsRP@L*!t=tRwwmtLc8XJ$FAJ$o{V@9|~bxWUi9nFzN5LlH<8|$B5 zL6xS4j&NTRh6K)}m@Ox7o;gCOtz#3Hz(F)|Cawz^z&D|J z!-9#-g2)`9j`wEbffwaiHjK;<-DQN7S-Pz??ZSV-=hCzux)2Cl1{*8+(TlKlQdI1H zzv{l!^H`b%p(h%2G<4-nM4@<4A}5@>i}?%w&;*G#H!`inuw9RAEOV#5>C}j5LbUVx<=E@t&MKYeA+roH=#x&lOhGG)XYd<8&WMas;8CAW)K0e54DIZx-xchG@VQOgO zz16a66_C319>KRjr>tbc!v^ra_z`) zYH+$q&@r|JnPvZb1>hHI-@O#jbu+eLIi%Kft1>T%6>9IiQoA=&5NL=Vf)C;HaieGH zI)pCuq|WVXuWx7hwI2XbU5-0MK#A|@WhB4HKG+Lp17QU&>%x(JnOQ}c+_}b)^vJhz_wid$1r;7D*@k{#`f!#w-b$bw^-;X zY2qVxjVXs(uT1$ce@_O;a?wcSz0=-9t}>C$-HCv9ty4f1ZSP2I-W;DWCRfcd^uonf zNoX`9{hHTokt!-{l|{w7X|FmviIq!C{0gbOP0?sdrGWwtw8*;O6+~BGSF^DZ`#Z`l zQR91UP~MfnV(zMRr#+vgd`lxF|5BmJL&a@J3^pX!X9&~Z>M9=If^hGDwFo-z_KtM^ zS=k0`xs0hABDy<0wc+r>+3JM4u>qROk}hTPTk@`>AvuZ)$CS_zi{|E@?2K_Kp`+5q zOS8BbDuZZkyyDWpxiU4!b0HoD)NYV5?IT5-no(2qcYFnF4Zi_*+4`IUr_g{BKZl52 zf*j&(@JL#)ez*s!+k37@T^+X)fPJ!YnJa5zA$#~%xFJDPwoTKNZ2fg8*_yo*FexPw zqD@=|L(GCuo|cP)b1r;s%3~nUIRw}XIgG@h(-ZLDyd~0~#v;ohg3D@RA;ZlKg_Bg{ zm^A#i@=!!F1QEQH%Es3lfAi7lL${Y3j!{9|d{@;ElquW=3&CT5pSV<=kuR_#D;@Ec zbJ9_l>U<|*M>q0Rb%f2F+p%DDW8?LZQw+dy7E4YNVcAg_T4yzFxkjOUU@vzHy1AR11=%9Zx_zFdfLeNZF_Rmd~Anq ztn12v%F)?NUX!&7GS$vVQ*t?U+v3&AZ20Gb zoz&^2vMagj5x-!x?O3FrO~_%#w3AgCwhuCIfKJr5k-FT9!h}Psb*6aQd*0yc>A9|2zs|W4@6w#5=e8)P zv#Pa`_7@>(M2>fZb7Y2_g$}*b&d}d}sIGL=`I4Bn_1`6dVibGzamx!*d<5^n$oOz# z7s}J#h}Dc)-$Y>@lUYe+hzj)dscO*A*(wuuHD%hB{GyvqBw8J#qcLF;!0t0l)y1BW zK1}(?)3!s?Zxz`o2!cFhfEb*ORZnp$BAu7ddq){TyX=^bYryi9`^KzY;L> zY~`8#B6l@!DyZhUt>sbByn0uS)!j=0d)M-u4z&R3mhc z_#wEFDiZP3EO05A7u4q0zE zbLKI%WM+1(HgTxR^L$|e-vgQd88bAPSo(r>{xx46hb1xxD6e)@rZWN@m=+r|uDEnR z%M9_-2@Hz}H}u>7gzAl$Y2eWwm>|HhzK=Dbq;xELro!FOPrRp!7G9qtZw@YHvX(Bt zar3kL59U&~4fYX_`W~5*mut1aU_V*Z2DZM~nA{;ubKE1}hg8Fy3^_lK3rFAQ&G-DQ zZFsZ3@LVNABb*ExexH(aJ?czDAMjK@xjSM62eNgMsI2#vhuV2qR7?il6hmCCM(?|9$5%jx%1^K75L&OcX~RUxDytx-hU zHPGZb!7A`eUOx8!!y00LR1tu`YOH_m>H4LTJSo1B%@aNo*!nYyyOJT0+l>-QWg$zdQ7dS<8i%+CPBm3J zkMCM*|MjF;e%Vm}IwIWJfYLh3&3}4W2#^s9Lfh9g5aKojU*Oe(^iiDqx4xoNi^K-L`;ub+p3Rd%dgIBJ=n(iW<48TkL^!FI zBC>4W+B)b7Q!+TTeN+n02Q>I?iceW><2~y_z5RT;4Oelx#YV}d zBK2SyUJp{E5ao{uMZz+jTgSILYtvCq@gvQH=8c(6tV^Cq6ptt$G z`Y-TRzj(4&y(*W9$FCiKVsUxJ15L_A?U+RTn#h+(hUum)F(69NVRXBUi8E(6$Eax(8tvj#39o(SWJRwm!-fY+t%C?l2;RL{vj%AtnX9K>wdGEukY zuWeL8lwE);9yv~Bi{s&TUrab$?(dL3H9*Vyvp!yd+(+Blts=6)r!ccZXIG79X2T~- z9CdJTpFrz(nN_8C#o3VMyy`{6lPhpn3Vgx*64Ef&T&+(k7Kh$S_03mo zG{u^c_=*B9^D@gkC{`u)R4BJQ;r^JW@rcr5AYxUN`a2p!bUh6qjBfJm)GAOO-=4`S zijj{=Kb)W2{juP`+Al-_V3!vEsJHkFyltwT1D`kaZ}Ja*TX#CNC1~ZG(+xmU(ZrEu zKl|7+P1EEzGAN@{$-}dx@a*3Ty7W#}%!cc9YnjF}oqo`@FYqBNNiA@C5{J8YuB6W1 zesfDX1WLgQ9rEj&8k`v~dIpZUHB7lnuY@aQE%a7unR%FPQguiVmYKJWk%V0&O zb>cMEvgLO`s7?XH+U#JW&r1VW{69u%Oy3VS zc3(kubmU}zeD-J?XRJSzad>DIe4DkNj7XzjTbUx~=YQQp+Q}FUd^dw0>_}}ao%}-i|0Nz1xxf9QS_jX4XA6^9*ds+%h<<_ zU#=5<#S$Hn$w{bb`nzAIAVc&*u=w2jHO-+UGY{CZ#p%k%sWLb5z)ej-*efkIj@Vod zqK&yBCQ4T&H$6q-<$s!=65No?J6Fk1qV_(&R)!NYCU)(MH5JJ87<2ivF&A4@_Fga1 zV{CQD+Pfy1I`Gm^?7m99{PJLW&!*wPtoPn}Z^f(%kLID{ceMY~eM9Pe)_K3W?U^Q# zJG5TvEPIu?)?N#1w$XskvvpN1+twqt`*_cik4ZaxOsjF2p|5SsW5`Taix0x8PL zi~ZoYAcq{lW6(J`Y(UBN3w!P`{8tlY5yRe=%_ry(64U7Oq!@5Bs^)gSfo1&_UD@@7 z*d%kP#{r-%ZZn0yqx)a48-B|$sPRhRggt%8sxBWC?IO-THT;aQbN?}dZqVs=J1&7u zw}mp=(oyu_+2v6J-xXMK$mx>_s-CXGx)uU|&tIgG)-ifwbH_r!D_XiWS3XFJR@wN$ zA22w3`&}jN8ZwCI05#f6)z!@QpZE9MAKETECT{HXU0=#kQ^S#MWV8 zV&MTzxAB+Rmt|OWQ0WPe8%2e4AOD2k=w+YNU(gnLgl#k3$;|Pr_RI)koTMFXWG3Q) z|1q{Ibgc+HOd3nhm<`~rCJ)to8cAU}1q|0?3C;I&C5V@{j=Pr^5bg`O{ImT+ZrW9` z0j|s4p*GL`c-J-#sCC6eW|9Z2)}kUCjS$phJZJ8uy=-7vla-o&8gU2S7|8-JNiG~Z z+?xlI(J`(Bmmm@4t2sY@ool=!2ppo;X;ua^@8MTA$v3WIPCfq+KpR6F;g+IaGudll zUqm0(7f_!@c~qFd%0?Fpi@|5UNyNV4L?-2IzY9L4DabSScs!k7k4@NSiF{|@)WT~? zTQv4TBCvg$;q@+Fe)>;1v$gJ?!Ci-8-2x=19tU9SS;RC*I5~K+L%DYGn#^jCkEX?} z;Fs)Z!8QRBKk9CxtdpD`IS~UCMIN<^CF47P=-=j)7~39JAdOH#+o(Uy!>-~dl_eoR*`7E$9=)zLS+SVedSRp znbiO5LIzS0KyOPLt6JsXr6n*iYmzZeIpAY1zb*wQaIXq9sa1y1`}af5w@Vejwp
    eGzdw3onk5@dGzMCb~AEszid(?G;!V}u1 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 From f223931ed3a723641e7dddf93bc09a83895b162b Mon Sep 17 00:00:00 2001 From: Ps3Moira <113228053+ps3moira@users.noreply.github.com> Date: Wed, 5 Jun 2024 14:16:32 -0700 Subject: [PATCH 311/568] Fix Cigars Sprites + YAML fix for Inhand unlit cigars/cigs (#28641) --- .../Smokeables/Cigarettes/cigarette.yml | 2 ++ .../Consumable/Smokeables/Cigars/cigar.yml | 2 ++ .../Cigars/cigar-gold.rsi/lit-inhand-left.png | Bin 769 -> 1374 bytes .../Cigars/cigar-gold.rsi/lit-inhand-right.png | Bin 542 -> 1220 bytes .../cigar-gold.rsi/unlit-inhand-left.png | Bin 246 -> 306 bytes .../cigar-gold.rsi/unlit-inhand-right.png | Bin 263 -> 335 bytes .../Cigars/cigar.rsi/lit-inhand-left.png | Bin 769 -> 1374 bytes .../Cigars/cigar.rsi/lit-inhand-right.png | Bin 542 -> 1220 bytes .../Cigars/cigar.rsi/unlit-inhand-left.png | Bin 246 -> 306 bytes .../Cigars/cigar.rsi/unlit-inhand-right.png | Bin 263 -> 335 bytes 10 files changed, 4 insertions(+) diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Smokeables/Cigarettes/cigarette.yml b/Resources/Prototypes/Entities/Objects/Consumable/Smokeables/Cigarettes/cigarette.yml index f811afafba1d..146fac039d03 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Smokeables/Cigarettes/cigarette.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Smokeables/Cigarettes/cigarette.yml @@ -18,6 +18,8 @@ equippedPrefix: unlit - type: Item size: Tiny + sprite: Objects/Consumable/Smokeables/Cigarettes/cigarette.rsi + heldPrefix: unlit - type: Construction graph: smokeableCigarette node: cigarette diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Smokeables/Cigars/cigar.yml b/Resources/Prototypes/Entities/Objects/Consumable/Smokeables/Cigars/cigar.yml index dc8d4eaf3c43..93419993dc0e 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Smokeables/Cigars/cigar.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Smokeables/Cigars/cigar.yml @@ -20,6 +20,8 @@ equippedPrefix: unlit - type: Item size: Tiny + sprite: Objects/Consumable/Smokeables/Cigars/cigar.rsi + heldPrefix: unlit - type: entity id: CigarSpent diff --git a/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar-gold.rsi/lit-inhand-left.png b/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar-gold.rsi/lit-inhand-left.png index 353efa3f395a6167f2bcc1f68b75b1b6b2f5c950..c08f70d091d56f6538f7c3f7322d9d692c5a5fed 100644 GIT binary patch literal 1374 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zcaljKx9jP7LeL$-HD>V3qK6aSW-L z^Y*S`k#IP}@rT=IPPr2Dkl{#)jKjMbJC-WPI6r>K!s9$s+hXN{uRc~a47+A5X;-uT zyNtp6(8bH^mc(YB{r_7lWyAe-7yMUG-THni6VL`^Abj=BZ{}N<*8Tl;)Bo%v{kZ$u zPoKOmd%tm8;kSg;Kl8mUe-{7vxPINbRrTLr{d`&U=lRk(du$&4+pqQX$<2@7{(t&= zIq2Pf%UA#Gf0h1CuHC|r72npJ?Ukv;|5cuEkEVeplfo>94mE~FoDCUBOnbR%=DwAg ze7|o0Y~7aIZ)NA*$KWE&aFNBqgi*kk1m;_V*UU22b$@iPv+t<8vE}pm)3>M3zgC~0 zd+M#8x%koW=V3>zUtr-3{-8nSbAF|6RnQ5cP%2Q0Cg(^xv8_eP;wwf&s}8T+Cayyp?X( z&)=5be_t=}`m=U>TU*)G#m8pI|J&v(KK1@Q`Tv__D(e27`o-YvBfYOm^3etHCI3HN zo-Y6ISDa@5Kgs7=-)~P3T=(v9(x3IO9ly-9NO-&dMYVgssj*}9t&7jk<+hkjJEa29qyuG}$nQna9vDP*$R^a44 z7MpwA(*P7<`i&B<>&=7I3RXSfKt!Kffz%C%!*lz5k@$p>1zY0X=_@Ej9gW zzm3fQ&b{XD;8YREv~hB+e*^#8vO{l?^y5tlJn4(%TkkFT{*|FN?Uz;cg_@c3|5j?M z>u;YOy#GAwrP$jWB>snLs-NF=eE+UpQNO>IzLv|`{D1oU`O}L2pFJne_O57S#buMz zKR;T3t6y_6`}@!T@+H9FVgB-SUflnTj#kNCJc0)T!nQvRNG{8DKWi?&?9QLU!`n@l zN~$z?#tCfBo4zlv|10CRf-~%YR~;=gv3-7Lzq9ya|5k<{b!)1(y{`xoU}&Cw%KL=f zHT_BDExq%8$sXPQ>l~*^>&^RSAI&sC!SQ~)S=hyzSjTu?5X{k`M2iH-ZayDsejagA{&hU z9Y1`|y&f$uJUsq?apv>qr*9{}tdpJn%o%fgm- z$*42@IM&y1uiuE7tHEj5>?gyqnACz-+7g>uUmf~7+uqaQCqse{uJnzqbbzTXUB@3# p49wi8jn4h~5F9HHE4OAW`p+=+u=8Bk{cnRoVxF#kF6*2UngE@xakKyc literal 769 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zf+;U^?OH;uumf=j~m?ERjTshKKbk zOP5B>yeDDJpYUo+*(~OY#s`8b5mjt!x9~>1W7KJ8zo2}he~Dq2q~e;{y_NIZQxns= z^8ekfOkDjJXdDW-6LQ(=yJ`Q=OP|~Mm(3TycSkn)|LHx~=B&25^EdV1V#9Cte}Dg6 zeKqUjt&r=UHAWE_tWP!%8WX5+p^8)?w=
    eA=$_uAZe_v^m@ z=Z~w;o{x|Ee|hir&krZ}OV8i`r2lLE?q7el=2ggj{waNjhui=2m+8mv_$!bbD_8?z t?RNVEyEE diff --git a/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar-gold.rsi/lit-inhand-right.png b/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar-gold.rsi/lit-inhand-right.png index cdeedb4c24b6843544fa4ca348296f2536b90cd6..7c685a0a408cf76f0ffc0fc229a211e6413efa31 100644 GIT binary patch literal 1220 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zcaljKx9jP7LeL$-HD>V5#$TaSW-L z^Y*T7p0v9J+k=}5slQB$@?G8)F|6FAxYcXw4=zpC$AQ~CGill%M63@r{_Pu@(ZGWCA9-FPX`5@c{-+N)oAdn%${y{y0ddjJ2s z_4`AwMpftUueo^jviz;#6}6>n{*}l6zZ|zm-~5;Twfpi=TlxdvD?YPU)aud~OwU!eo|)3?>H?Muk}n0=^6#Y78#I42w7!E)o#Fb-mX7-aPsLYkG|B z|1P`EF7c@4Pt>pZk1N*i{&)0tzSqtFd+u5O>z{vS%l}x--}QHL>&t=~Qhn4WKYM>~ zkIkHI|2Iwf8~^*Z{OODLzVuf9UN$A&J~A2VXobUW{QTFW%&qkQ3%uR#r5I&?UTW3* z&Dk?{4>>HQvxZWBLDW(w`sa zC)E9rerx{h`2Jd_+U<|j&h6{_yoTT4dhxm){|;)Y-@E_4+%W(DoFDukceZ?1)r?N= zdF&V-b(~p-Qw?Za?$e--*z0rd*Sly>w>h)(Y;0x4-i9whjXxNAa%1b?=p5G*z!R)q ziZ?W!uj@%Ng5qE^_YuFyCu=rVmQC-0#YhcXLiUz#H;>QJJ;dR#<(}XJt-{GCm;5|% z^XLEQ&-cH-^!{nYx!?Bx|9}0b&(`l>bMqU>hbN!UufDyW{q4TBMFG!`{rS`TufNjn z)2g<7f4b?DnEt~@KP}#V|GIVi|NTC<_rI@8&b@y0_rGt)iwca<%ktIqR7| zRqv}SDl9T?uRXqS?H_@P&{lTZNC3W8n%~#y~*L`*yIAH-J<4x0l#+4ECn|qkj>OgVh M>FVdQ&MBb@07DZjVgLXD literal 542 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zf+;V0`N7;uumf=j~lXFJ?!Hwuh1% zw`>vSSJ1R|*e1MY?H+f5euX&aH#tWy$tYDZSZ~}Szp+!{rpClx&ikKBcAwq9`80R* zSrMRd#DJo!`=)NcwkZD0n-$wm{=WY8ulMWP@a8l2D|0`Y{Q2{;WY>p(|D3O@nIAs? z-gx)g+@g=We{Nm(FM}!J^@Bh5s8{~HWb?vtIpQq=3{xAHTdBasZ{vR)P=oGv2 VvzR7}3vsJKlAf-9F6*2UngAYTyTJee diff --git a/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar-gold.rsi/unlit-inhand-left.png b/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar-gold.rsi/unlit-inhand-left.png index 9c3943fbf26ecf93db9b406f4955db7941abaa35..c1ca9c4d489e08820423c02af465fb7535ae8dd4 100644 GIT binary patch delta 279 zcmeyyxQS_kNGZx^prw85kJec)B=-RLpsMYolnBfk^AaBgw8$8G0J5Z@fCR z?OlRf0)y7=h?SNmPA5)(o$-J52F~f)PAwMfK#d^4`!S4pezkb~?f%0L@*eK1h+qCt zVz=fV`*`==bE;Q2#s=k88F-fj8(2Ts8M{{Yuh!;ekDrUzZzopr09zY+EdT%j literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=dp%toLn`LHy}41a$w9>RV!v}r z3ga{e+ZngE+B$HGIi&1TGw4gR`d6EOqgu)6-M+>QW}q$*h`N+I=WA8pCR=+y=R40d zpW4pfCEs^>`PMakr{{d>`fm|&J#@mS%xTx<1AZFZmtX!w1&&=LGiJ#v sysp~Qb=qLs_SfI5&!y@E?JrYTnzyNvH{Q*6Z!5?>p00i_>zopr01r%D7ytkO diff --git a/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar-gold.rsi/unlit-inhand-right.png b/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar-gold.rsi/unlit-inhand-right.png index 0f4ab68d1569e959c7cd17c2ad9f83e6a37ff345..9fc2e5ba275c279ff1ba67a04e0fc3e5d38cb12f 100644 GIT binary patch delta 308 zcmZo?I?psgrJk`k$lZxy-8q?;3=E8%o-U3d6?5L+vgT`Y5NUnby>U^I;+un!9o`F; zn@szr*JyjAuR1|7Y+KY14-;c$&O?kp_qQBTIQlS!<9bgT0|Xc-N4|KnOJm*7ot3-S zt=en({7kjI$A)c5Grvif*VgS@!)0yi`yozz;pE+i6*t#cTb@tuiu?Jw_{X!eN~~v2 z8hvvO{mq#C=U)21#l;6-r|oCbaFBKAWxB$61;ohx#JWZDY-|dffp^c&%Wdx#qR z6uofu4fl^<-tXn*Smhd7xB}P%7BiGGn1UGIVi%6{OsG=V-T*TKZkFyY%U`-@pa1)v t?7dg~_NJQO`<+66ugts2%)pRflJ`-~V1t`D!&{K2JYD@<);T3K0RVE$hAjX9 literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC==R92;Ln`LHy}6P1kO6~BpmgBU zqzP{t*c17)yngi@P)K9vpVVb$!l~(}6m{z_xSiX1U{qlFvR> zYR=2w*=ce;I4W+xe7oTt@0Ih4!*>_XT(*O)D|(^N-g5rpzaM5ZRt2t|Tr9o*w|c@709Ovok K=d#Wzp$Pz&BwsrK diff --git a/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar.rsi/lit-inhand-left.png b/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar.rsi/lit-inhand-left.png index 353efa3f395a6167f2bcc1f68b75b1b6b2f5c950..c08f70d091d56f6538f7c3f7322d9d692c5a5fed 100644 GIT binary patch literal 1374 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zcaljKx9jP7LeL$-HD>V3qK6aSW-L z^Y*S`k#IP}@rT=IPPr2Dkl{#)jKjMbJC-WPI6r>K!s9$s+hXN{uRc~a47+A5X;-uT zyNtp6(8bH^mc(YB{r_7lWyAe-7yMUG-THni6VL`^Abj=BZ{}N<*8Tl;)Bo%v{kZ$u zPoKOmd%tm8;kSg;Kl8mUe-{7vxPINbRrTLr{d`&U=lRk(du$&4+pqQX$<2@7{(t&= zIq2Pf%UA#Gf0h1CuHC|r72npJ?Ukv;|5cuEkEVeplfo>94mE~FoDCUBOnbR%=DwAg ze7|o0Y~7aIZ)NA*$KWE&aFNBqgi*kk1m;_V*UU22b$@iPv+t<8vE}pm)3>M3zgC~0 zd+M#8x%koW=V3>zUtr-3{-8nSbAF|6RnQ5cP%2Q0Cg(^xv8_eP;wwf&s}8T+Cayyp?X( z&)=5be_t=}`m=U>TU*)G#m8pI|J&v(KK1@Q`Tv__D(e27`o-YvBfYOm^3etHCI3HN zo-Y6ISDa@5Kgs7=-)~P3T=(v9(x3IO9ly-9NO-&dMYVgssj*}9t&7jk<+hkjJEa29qyuG}$nQna9vDP*$R^a44 z7MpwA(*P7<`i&B<>&=7I3RXSfKt!Kffz%C%!*lz5k@$p>1zY0X=_@Ej9gW zzm3fQ&b{XD;8YREv~hB+e*^#8vO{l?^y5tlJn4(%TkkFT{*|FN?Uz;cg_@c3|5j?M z>u;YOy#GAwrP$jWB>snLs-NF=eE+UpQNO>IzLv|`{D1oU`O}L2pFJne_O57S#buMz zKR;T3t6y_6`}@!T@+H9FVgB-SUflnTj#kNCJc0)T!nQvRNG{8DKWi?&?9QLU!`n@l zN~$z?#tCfBo4zlv|10CRf-~%YR~;=gv3-7Lzq9ya|5k<{b!)1(y{`xoU}&Cw%KL=f zHT_BDExq%8$sXPQ>l~*^>&^RSAI&sC!SQ~)S=hyzSjTu?5X{k`M2iH-ZayDsejagA{&hU z9Y1`|y&f$uJUsq?apv>qr*9{}tdpJn%o%fgm- z$*42@IM&y1uiuE7tHEj5>?gyqnACz-+7g>uUmf~7+uqaQCqse{uJnzqbbzTXUB@3# p49wi8jn4h~5F9HHE4OAW`p+=+u=8Bk{cnRoVxF#kF6*2UngE@xakKyc literal 769 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zf+;U^?OH;uumf=j~m?ERjTshKKbk zOP5B>yeDDJpYUo+*(~OY#s`8b5mjt!x9~>1W7KJ8zo2}he~Dq2q~e;{y_NIZQxns= z^8ekfOkDjJXdDW-6LQ(=yJ`Q=OP|~Mm(3TycSkn)|LHx~=B&25^EdV1V#9Cte}Dg6 zeKqUjt&r=UHAWE_tWP!%8WX5+p^8)?w=eA=$_uAZe_v^m@ z=Z~w;o{x|Ee|hir&krZ}OV8i`r2lLE?q7el=2ggj{waNjhui=2m+8mv_$!bbD_8?z t?RNVEyEE diff --git a/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar.rsi/lit-inhand-right.png b/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar.rsi/lit-inhand-right.png index cdeedb4c24b6843544fa4ca348296f2536b90cd6..7c685a0a408cf76f0ffc0fc229a211e6413efa31 100644 GIT binary patch literal 1220 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zcaljKx9jP7LeL$-HD>V5#$TaSW-L z^Y*T7p0v9J+k=}5slQB$@?G8)F|6FAxYcXw4=zpC$AQ~CGill%M63@r{_Pu@(ZGWCA9-FPX`5@c{-+N)oAdn%${y{y0ddjJ2s z_4`AwMpftUueo^jviz;#6}6>n{*}l6zZ|zm-~5;Twfpi=TlxdvD?YPU)aud~OwU!eo|)3?>H?Muk}n0=^6#Y78#I42w7!E)o#Fb-mX7-aPsLYkG|B z|1P`EF7c@4Pt>pZk1N*i{&)0tzSqtFd+u5O>z{vS%l}x--}QHL>&t=~Qhn4WKYM>~ zkIkHI|2Iwf8~^*Z{OODLzVuf9UN$A&J~A2VXobUW{QTFW%&qkQ3%uR#r5I&?UTW3* z&Dk?{4>>HQvxZWBLDW(w`sa zC)E9rerx{h`2Jd_+U<|j&h6{_yoTT4dhxm){|;)Y-@E_4+%W(DoFDukceZ?1)r?N= zdF&V-b(~p-Qw?Za?$e--*z0rd*Sly>w>h)(Y;0x4-i9whjXxNAa%1b?=p5G*z!R)q ziZ?W!uj@%Ng5qE^_YuFyCu=rVmQC-0#YhcXLiUz#H;>QJJ;dR#<(}XJt-{GCm;5|% z^XLEQ&-cH-^!{nYx!?Bx|9}0b&(`l>bMqU>hbN!UufDyW{q4TBMFG!`{rS`TufNjn z)2g<7f4b?DnEt~@KP}#V|GIVi|NTC<_rI@8&b@y0_rGt)iwca<%ktIqR7| zRqv}SDl9T?uRXqS?H_@P&{lTZNC3W8n%~#y~*L`*yIAH-J<4x0l#+4ECn|qkj>OgVh M>FVdQ&MBb@07DZjVgLXD literal 542 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zf+;V0`N7;uumf=j~lXFJ?!Hwuh1% zw`>vSSJ1R|*e1MY?H+f5euX&aH#tWy$tYDZSZ~}Szp+!{rpClx&ikKBcAwq9`80R* zSrMRd#DJo!`=)NcwkZD0n-$wm{=WY8ulMWP@a8l2D|0`Y{Q2{;WY>p(|D3O@nIAs? z-gx)g+@g=We{Nm(FM}!J^@Bh5s8{~HWb?vtIpQq=3{xAHTdBasZ{vR)P=oGv2 VvzR7}3vsJKlAf-9F6*2UngAYTyTJee diff --git a/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar.rsi/unlit-inhand-left.png b/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar.rsi/unlit-inhand-left.png index 9c3943fbf26ecf93db9b406f4955db7941abaa35..c1ca9c4d489e08820423c02af465fb7535ae8dd4 100644 GIT binary patch delta 279 zcmeyyxQS_kNGZx^prw85kJec)B=-RLpsMYolnBfk^AaBgw8$8G0J5Z@fCR z?OlRf0)y7=h?SNmPA5)(o$-J52F~f)PAwMfK#d^4`!S4pezkb~?f%0L@*eK1h+qCt zVz=fV`*`==bE;Q2#s=k88F-fj8(2Ts8M{{Yuh!;ekDrUzZzopr09zY+EdT%j literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=dp%toLn`LHy}41a$w9>RV!v}r z3ga{e+ZngE+B$HGIi&1TGw4gR`d6EOqgu)6-M+>QW}q$*h`N+I=WA8pCR=+y=R40d zpW4pfCEs^>`PMakr{{d>`fm|&J#@mS%xTx<1AZFZmtX!w1&&=LGiJ#v sysp~Qb=qLs_SfI5&!y@E?JrYTnzyNvH{Q*6Z!5?>p00i_>zopr01r%D7ytkO diff --git a/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar.rsi/unlit-inhand-right.png b/Resources/Textures/Objects/Consumable/Smokeables/Cigars/cigar.rsi/unlit-inhand-right.png index 0f4ab68d1569e959c7cd17c2ad9f83e6a37ff345..9fc2e5ba275c279ff1ba67a04e0fc3e5d38cb12f 100644 GIT binary patch delta 308 zcmZo?I?psgrJk`k$lZxy-8q?;3=E8%o-U3d6?5L+vgT`Y5NUnby>U^I;+un!9o`F; zn@szr*JyjAuR1|7Y+KY14-;c$&O?kp_qQBTIQlS!<9bgT0|Xc-N4|KnOJm*7ot3-S zt=en({7kjI$A)c5Grvif*VgS@!)0yi`yozz;pE+i6*t#cTb@tuiu?Jw_{X!eN~~v2 z8hvvO{mq#C=U)21#l;6-r|oCbaFBKAWxB$61;ohx#JWZDY-|dffp^c&%Wdx#qR z6uofu4fl^<-tXn*Smhd7xB}P%7BiGGn1UGIVi%6{OsG=V-T*TKZkFyY%U`-@pa1)v t?7dg~_NJQO`<+66ugts2%)pRflJ`-~V1t`D!&{K2JYD@<);T3K0RVE$hAjX9 literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC==R92;Ln`LHy}6P1kO6~BpmgBU zqzP{t*c17)yngi@P)K9vpVVb$!l~(}6m{z_xSiX1U{qlFvR> zYR=2w*=ce;I4W+xe7oTt@0Ih4!*>_XT(*O)D|(^N-g5rpzaM5ZRt2t|Tr9o*w|c@709Ovok K=d#Wzp$Pz&BwsrK From 014c5eb3e8d36c7a72a0b2d647ca580b903ebdf9 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 5 Jun 2024 21:17:38 +0000 Subject: [PATCH 312/568] 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 6adbf16221d3..adbad1269bb1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: brainfood1183 - changes: - - message: Added Spray Paints. - type: Add - id: 6186 - time: '2024-03-18T21:29:48.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/23003 - author: PJB3005 changes: - message: Bans are now shown in the "adminremarks" command. @@ -3848,3 +3841,10 @@ id: 6685 time: '2024-06-05T20:47:28.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28571 +- author: ps3moira + changes: + - message: Lit cigars inhand sprites are now visible. + type: Fix + id: 6686 + time: '2024-06-05T21:16:32.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28641 From 27a5d08db497348db591e318a2ea7e7f72cf0662 Mon Sep 17 00:00:00 2001 From: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:03:31 -0500 Subject: [PATCH 313/568] Clean up Eva and Hardsuit helm yml + Lets atmos firesuit helm work as a BreathMask (#28602) --- .../Clothing/Head/base_clothinghead.yml | 2 ++ .../Entities/Clothing/Head/eva-helmets.yml | 6 ---- .../Clothing/Head/hardsuit-helmets.yml | 31 ------------------- .../Entities/Clothing/Head/helmets.yml | 1 + 4 files changed, 3 insertions(+), 37 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml b/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml index 40f9e0a3d4e3..866fe962ca5e 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml @@ -119,6 +119,7 @@ id: ClothingHeadEVAHelmetBase name: base space helmet components: + - type: BreathMask - type: Item size: Normal - type: PressureProtection @@ -150,6 +151,7 @@ name: base hardsuit helmet noSpawn: true components: + - type: BreathMask - type: Sprite state: icon # default state used by most inheritors - type: Clickable diff --git a/Resources/Prototypes/Entities/Clothing/Head/eva-helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/eva-helmets.yml index 963bcdedd783..530c23a5781e 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/eva-helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/eva-helmets.yml @@ -5,7 +5,6 @@ name: EVA helmet description: An old-but-gold helmet designed for extravehicular activites. Infamous for making security officers paranoid. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Helmets/eva.rsi - type: Clothing @@ -22,7 +21,6 @@ name: EVA helmet description: An old-but-gold helmet designed for extravehicular activites. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Helmets/eva_large.rsi - type: Clothing @@ -35,7 +33,6 @@ name: syndicate EVA helmet description: A simple, stylish EVA helmet. Designed for maximum humble space-badassery. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Helmets/eva_syndicate.rsi - type: Clothing @@ -48,7 +45,6 @@ name: cosmonaut helmet description: Ancient design, but advanced manufacturing. #Description here originally started with " A deceptively well armored space helmet." Potentially had armor values in SS13 that weren't brought over? components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Helmets/cosmonaut.rsi - type: Clothing @@ -61,7 +57,6 @@ name: paramedic void helmet description: A void helmet made for paramedics. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Helmets/paramedhelm.rsi - type: Clothing @@ -81,7 +76,6 @@ name: NTSRA void helmet description: An ancient space helmet, designed by the NTSRA branch of CentCom. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Helmets/ancientvoidsuit.rsi - type: Clothing diff --git a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml index 072dd53d9ae1..0c660e09b807 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml @@ -12,7 +12,6 @@ name: basic hardsuit helmet description: A basic-looking hardsuit helmet that provides minor protection against most sources of damage. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/basic.rsi - type: Clothing @@ -28,7 +27,6 @@ name: atmos hardsuit helmet description: A special hardsuit helmet designed for working in low-pressure, high thermal environments. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/atmospherics.rsi layers: @@ -68,7 +66,6 @@ name: engineering hardsuit helmet description: An engineering hardsuit helmet designed for working in low-pressure, high radioactive environments. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/engineering.rsi - type: Clothing @@ -86,7 +83,6 @@ name: spationaut hardsuit helmet description: A sturdy helmet designed for complex industrial operations in space. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/spatiohelm.rsi layers: @@ -121,7 +117,6 @@ name: salvage hardsuit helmet description: A special helmet designed for work in a hazardous, low pressure environment. Has reinforced plating for wildlife encounters and dual floodlights. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/salvage.rsi - type: Clothing @@ -140,7 +135,6 @@ name: salvager maxim helmet description: A predication of decay washes over your mind. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/maxim.rsi - type: Clothing @@ -163,7 +157,6 @@ name: security hardsuit helmet description: Armored hardsuit helmet for security needs. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/security.rsi - type: Clothing @@ -188,7 +181,6 @@ name: brigmedic hardsuit helmet description: The lightweight helmet of the brigmedic hardsuit. Protects against viruses, and clowns. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/brigmedic.rsi - type: Clothing @@ -215,7 +207,6 @@ name: warden's hardsuit helmet description: A modified riot helmet. Oddly comfortable. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/security-warden.rsi - type: Clothing @@ -240,7 +231,6 @@ name: captain's hardsuit helmet description: Special hardsuit helmet, made for the captain of the station. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/capspace.rsi - type: Clothing @@ -256,7 +246,6 @@ name: chief engineer's hardsuit helmet description: Special hardsuit helmet, made for the chief engineer of the station. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/engineering-white.rsi - type: Clothing @@ -274,7 +263,6 @@ name: chief medical officer's hardsuit helmet description: Lightweight medical hardsuit helmet that doesn't restrict your head movements. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/medical.rsi - type: Clothing @@ -292,7 +280,6 @@ name: experimental research hardsuit helmet description: Lightweight hardsuit helmet that doesn't restrict your head movements. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/rd.rsi - type: Clothing @@ -310,7 +297,6 @@ name: head of security's hardsuit helmet description: Security hardsuit helmet with the latest top secret NT-HUD software. Belongs to the HoS. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/security-red.rsi - type: Clothing @@ -335,7 +321,6 @@ name: luxury mining hardsuit helmet description: A refurbished mining hardsuit helmet, fitted with satin cushioning and an extra (non-functioning) antenna, because you're that extra. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/luxury.rsi - type: Clothing @@ -355,7 +340,6 @@ name: blood-red hardsuit helmet description: A heavily armored helmet designed for work in special operations. Property of Gorlex Marauders. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/syndicate.rsi - type: Clothing @@ -380,7 +364,6 @@ name: blood-red medic hardsuit helmet description: An advanced red hardsuit helmet specifically designed for field medic operations. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/syndiemedic.rsi - type: Clothing @@ -405,7 +388,6 @@ name: syndicate elite helmet description: An elite version of the blood-red hardsuit's helmet, with improved armor and fireproofing. Property of Gorlex Marauders. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/syndieelite.rsi - type: Clothing @@ -434,7 +416,6 @@ name: syndicate commander helmet description: A bulked up version of the blood-red hardsuit's helmet, purpose-built for the commander of a syndicate operative squad. Has significantly improved armor for those deadly front-lines firefights. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/syndiecommander.rsi - type: Clothing @@ -459,7 +440,6 @@ name: cybersun juggernaut helmet description: Made of compressed red matter, this helmet was designed in the Tau chromosphere facility. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/cybersun.rsi - type: Clothing @@ -482,7 +462,6 @@ name: wizard hardsuit helmet description: A bizarre gem-encrusted helmet that radiates magical energies. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/wizard.rsi - type: Clothing @@ -507,7 +486,6 @@ name: organic space helmet description: A spaceworthy biomass of pressure and temperature resistant tissue. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/lingspacehelmet.rsi - type: Clothing @@ -524,7 +502,6 @@ suffix: Pirate description: A deep space EVA helmet, very heavy but provides good protection. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/pirateeva.rsi - type: Clothing @@ -541,7 +518,6 @@ suffix: Pirate description: A special hardsuit helmet, made for the captain of a pirate ship. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/piratecaptainhelm.rsi - type: Clothing @@ -558,7 +534,6 @@ name: ERT leader hardsuit helmet description: A special hardsuit helmet worn by members of an emergency response team. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/ERThelmets/ertleader.rsi - type: Clothing @@ -580,7 +555,6 @@ name: ERT chaplain hardsuit helmet description: A special hardsuit helmet worn by members of an emergency response team. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi - type: Clothing @@ -595,7 +569,6 @@ name: ERT engineer hardsuit helmet description: A special hardsuit helmet worn by members of an emergency response team. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/ERThelmets/ertengineer.rsi - type: Clothing @@ -617,7 +590,6 @@ name: ERT medic hardsuit helmet description: A special hardsuit helmet worn by members of an emergency response team. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/ERThelmets/ertmedical.rsi - type: Clothing @@ -632,7 +604,6 @@ name: ERT security hardsuit helmet description: A special hardsuit helmet worn by members of an emergency response team. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/ERThelmets/ertsecurity.rsi - type: Clothing @@ -654,7 +625,6 @@ name: ERT janitor hardsuit helmet description: A special hardsuit helmet worn by members of an emergency response team. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/ERThelmets/ertjanitor.rsi - type: Clothing @@ -669,7 +639,6 @@ name: CBURN exosuit helmet description: A pressure resistant and fireproof hood worn by special cleanup units. components: - - type: BreathMask - type: Sprite sprite: Clothing/Head/Hardsuits/cburn.rsi layers: diff --git a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml index 720e52ee5883..d585518ece88 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml @@ -263,6 +263,7 @@ slots: - Hair - Snout + - type: BreathMask #Chitinous Helmet - type: entity From 72df1a0ae58679cf482006c08b242b94ce00c661 Mon Sep 17 00:00:00 2001 From: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:04:09 -0500 Subject: [PATCH 314/568] Shifts borgs hats to the right a bit (#28600) --- Resources/Prototypes/InventoryTemplates/borg.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/InventoryTemplates/borg.yml b/Resources/Prototypes/InventoryTemplates/borg.yml index a03be16708d5..d43519f61cf4 100644 --- a/Resources/Prototypes/InventoryTemplates/borg.yml +++ b/Resources/Prototypes/InventoryTemplates/borg.yml @@ -7,7 +7,7 @@ uiWindowPos: 1,0 strippingWindowPos: 0,0 displayName: Head - offset: 0, -0.09375 + offset: 0.015625, -0.09375 - type: inventoryTemplate id: borgShort @@ -18,7 +18,7 @@ uiWindowPos: 1,0 strippingWindowPos: 0,0 displayName: Head - offset: 0, -0.1875 + offset: 0.015625, -0.1875 - type: inventoryTemplate id: borgTall @@ -30,6 +30,7 @@ uiWindowPos: 0,0 strippingWindowPos: 0,0 displayName: Head + offset: 0.015625, 0 # taller than tall - type: inventoryTemplate From 110c8a96e0f7593a1caad4c6be842161d9428eb6 Mon Sep 17 00:00:00 2001 From: Verm <32827189+Vermidia@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:06:57 -0500 Subject: [PATCH 315/568] Fix mouse inhands (#28623) --- Resources/Prototypes/Entities/Mobs/NPCs/animals.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 51647f698524..971d05823a52 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1533,6 +1533,7 @@ state: mouse-0 - type: Item size: Tiny + heldPrefix: 0 - type: Clothing quickEquip: false sprite: Mobs/Animals/mouse.rsi @@ -1706,6 +1707,9 @@ Base: dead-1 Dead: Base: splat-1 + - type: Item + size: Tiny + heldPrefix: 1 - type: entity parent: MobMouse @@ -1732,6 +1736,9 @@ Base: dead-2 Dead: Base: splat-2 + - type: Item + size: Tiny + heldPrefix: 2 - type: entity name: cancer mouse From 8a1f3f497184cb14fca9cea6af0ab87f6df5e632 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Wed, 5 Jun 2024 18:08:41 -0400 Subject: [PATCH 316/568] Adjust some touch reaction damage levels (#28591) --- Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml | 2 +- Resources/Prototypes/Entities/Mobs/Species/diona.yml | 8 ++++---- Resources/Prototypes/Entities/Mobs/Species/slime.yml | 2 +- Resources/Prototypes/Entities/Objects/Misc/kudzu.yml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml index d2ca78043814..ffca699bfcc9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml @@ -84,7 +84,7 @@ scaleByQuantity: true damage: types: - Heat: 3 + Heat: 0.15 - !type:PopupMessage type: Local messages: [ "slime-hurt-by-water-popup" ] diff --git a/Resources/Prototypes/Entities/Mobs/Species/diona.yml b/Resources/Prototypes/Entities/Mobs/Species/diona.yml index 1cac380cd030..7edb3a19dbad 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/diona.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/diona.yml @@ -49,9 +49,9 @@ scaleByQuantity: true damage: types: - Blunt: 2 - Slash: 2 - Piercing: 3 + Blunt: 0.1 + Slash: 0.1 + Piercing: 0.15 - !type:PopupMessage type: Local visualType: Large @@ -64,7 +64,7 @@ scaleByQuantity: true damage: types: - Poison: 5 + Poison: 0.25 - !type:PopupMessage type: Local visualType: Large diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index 2ab26ffcd610..caa3690e5d20 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -91,7 +91,7 @@ scaleByQuantity: true damage: types: - Heat: 2 + Heat: 0.1 - !type:PopupMessage type: Local visualType: Large diff --git a/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml b/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml index a1005004946a..ca56ef5acbb7 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml @@ -76,7 +76,7 @@ scaleByQuantity: true damage: types: - Heat: 10 + Heat: 0.5 - type: AtmosExposed - type: Kudzu growthTickChance: 0.3 From 8e85bec2cef641e5624241ce33cdbdbba2921e5d Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 5 Jun 2024 22:09:47 +0000 Subject: [PATCH 317/568] 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 adbad1269bb1..25f48ec5f798 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: PJB3005 - changes: - - message: Bans are now shown in the "adminremarks" command. - type: Add - id: 6187 - time: '2024-03-18T21:31:34.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26240 - author: Terraspark4941 changes: - message: The TEG page in the guidebook has been updated with proper information! @@ -3848,3 +3841,11 @@ id: 6686 time: '2024-06-05T21:16:32.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28641 +- author: Tayrtahn + changes: + - message: Slimes, diona, and kudzu are less dramatically affected by having certain + chemicals splashed on them. + type: Tweak + id: 6687 + time: '2024-06-05T22:08:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28591 From ba57e3e4d9b95bad457f93f7e1f9bbd433d8eebd Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:14:25 +1200 Subject: [PATCH 318/568] Add closing storage UIs to StorageInteractionTest (#28633) --- .../Tests/Storage/StorageInteractionTest.cs | 12 ++++++++++++ .../Storage/EntitySystems/SharedStorageSystem.cs | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs b/Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs index 34402dd5e627..8d0de707f3b2 100644 --- a/Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs +++ b/Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs @@ -4,6 +4,7 @@ using Content.Shared.Input; using Content.Shared.PDA; using Content.Shared.Storage; +using Content.Shared.Timing; using Robust.Client.UserInterface; using Robust.Shared.Containers; using Robust.Shared.GameObjects; @@ -27,11 +28,22 @@ public async Task UiInteractTest() Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.False); Assert.That(IsUiOpen(PdaUiKey.Key), Is.False); + await Server.WaitPost(() => SEntMan.RemoveComponent(STarget!.Value)); + await RunTicks(5); + // Activating the backpack opens the UI await Activate(); Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.True); Assert.That(IsUiOpen(PdaUiKey.Key), Is.False); + // Activating it again closes the UI + await Activate(); + Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.False); + + // Open it again + await Activate(); + Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.True); + // Pick up a PDA var pda = await PlaceInHands("PassengerPDA"); var sPda = ToServer(pda); diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index d87b57bfc8dd..f20e2f9ab1f2 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -298,7 +298,7 @@ public void OpenStorageUI(EntityUid uid, EntityUid entity, StorageComponent? sto return; // prevent spamming bag open / honkerton honk sound - silent |= TryComp(uid, out var useDelay) && UseDelay.IsDelayed((uid, useDelay)); + silent |= TryComp(uid, out var useDelay) && UseDelay.IsDelayed((uid, useDelay), id: OpenUiUseDelayID); if (!CanInteract(entity, (uid, storageComp), silent: silent)) return; @@ -308,7 +308,7 @@ public void OpenStorageUI(EntityUid uid, EntityUid entity, StorageComponent? sto Audio.PlayPredicted(storageComp.StorageOpenSound, uid, entity); if (useDelay != null) - UseDelay.TryResetDelay((uid, useDelay)); + UseDelay.TryResetDelay((uid, useDelay), id: OpenUiUseDelayID); } _ui.OpenUi(uid, StorageComponent.StorageUiKey.Key, entity); From fea2bc942263b3cda6cb61a9f8959493931e8b8d Mon Sep 17 00:00:00 2001 From: Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com> Date: Wed, 5 Jun 2024 18:59:22 -0500 Subject: [PATCH 319/568] Update rules (#28452) --- .github/CODEOWNERS | 2 + .../Guidebook/Controls/GuidebookWindow.xaml | 2 +- Resources/Locale/en-US/guidebook/guides.ftl | 55 ++ Resources/Prototypes/Guidebook/rules.yml | 369 +++++++ Resources/Prototypes/Guidebook/ss14.yml | 1 + .../Guidebook/ServerRules/BanDurations.xml | 17 + .../Guidebook/ServerRules/BanTypes.xml | 11 + .../ServerRules/CoreRules/RuleC0.xml | 19 + .../ServerRules/CoreRules/RuleC10AHelp.xml | 29 + .../CoreRules/RuleC11AhelpThreats.xml | 20 + .../ServerRules/CoreRules/RuleC12MinAge.xml | 6 + .../CoreRules/RuleC13CharacterNames.xml | 66 ++ .../ServerRules/CoreRules/RuleC14ICinOOC.xml | 13 + .../ServerRules/CoreRules/RuleC1Admins.xml | 6 + .../ServerRules/CoreRules/RuleC2DBAD.xml | 7 + .../ServerRules/CoreRules/RuleC3NoHate.xml | 20 + .../ServerRules/CoreRules/RuleC4NoERP.xml | 23 + .../ServerRules/CoreRules/RuleC5Metacomms.xml | 18 + .../CoreRules/RuleC6BanEvasion.xml | 15 + .../CoreRules/RuleC7EnglishOnly.xml | 10 + .../ServerRules/CoreRules/RuleC8Exploits.xml | 12 + .../ServerRules/CoreRules/RuleC9Multikey.xml | 7 + .../Guidebook/ServerRules/DefaultRules.xml | 5 + .../Guidebook/ServerRules/README.txt | 5 + .../Guidebook/ServerRules/RoleTypes.xml | 21 + .../ServerRules/RoleplayRules/RuleR0.xml | 26 + .../RoleplayRules/RuleR10Subordination.xml | 26 + .../RuleR11-1AnimalEscalation.xml | 36 + .../RoleplayRules/RuleR11-2ConflictTypes.xml | 30 + .../RoleplayRules/RuleR11Escalation.xml | 67 ++ .../RoleplayRules/RuleR12RoleAbandonment.xml | 28 + .../RoleplayRules/RuleR13PerformRole.xml | 26 + .../RoleplayRules/RuleR14SecComStandard.xml | 37 + .../RoleplayRules/RuleR15SpaceLaw.xml | 21 + .../RoleplayRules/RuleR1Silicons.xml | 4 + .../RoleplayRules/RuleR2Familiars.xml | 6 + .../RoleplayRules/RuleR3NormalRP.xml | 20 + .../RoleplayRules/RuleR4Metashield.xml | 103 ++ .../RoleplayRules/RuleR5Arrivals.xml | 22 + .../RoleplayRules/RuleR6SelfAntag.xml | 22 + .../RoleplayRules/RuleR7RoundStalling.xml | 16 + .../RoleplayRules/RuleR8NoFriendlyAntag.xml | 22 + .../RoleplayRules/RuleR9MassSabotage.xml | 23 + .../ServerRules/SiliconRules/RuleS0.xml | 15 + .../SiliconRules/RuleS10OrderConflicts.xml | 9 + .../ServerRules/SiliconRules/RuleS1Laws.xml | 6 + .../SiliconRules/RuleS2LawPriority.xml | 9 + .../SiliconRules/RuleS3LawRedefinition.xml | 8 + .../SiliconRules/RuleS4RequestChanges.xml | 6 + .../SiliconRules/RuleS5FreeSilicon.xml | 4 + .../SiliconRules/RuleS6UnreasonableOrders.xml | 22 + .../SiliconRules/RuleS7Consistency.xml | 6 + .../RuleS8DefaultCrewDefinition.xml | 4 + .../RuleS9DefaultHarmDefinition.xml | 25 + .../SpaceLaw/SLControlledSubstances.xml | 14 + .../ServerRules/SpaceLaw/SLCrimeList.xml | 934 ++++++++++++++++++ .../ServerRules/SpaceLaw/SLRestrictedGear.xml | 21 + .../SpaceLaw/SLRestrictedWeapons.xml | 11 + .../ServerRules/SpaceLaw/SpaceLaw.xml | 67 ++ .../ServerRules/WizDenCoreOnlyRules.xml | 26 + .../Guidebook/ServerRules/WizDenLRPRules.xml | 65 ++ .../Guidebook/ServerRules/WizDenMRPRules.xml | 65 ++ Resources/ServerInfo/RP_Rules.txt | 127 --- Resources/ServerInfo/Rules.txt | 115 --- 64 files changed, 2610 insertions(+), 243 deletions(-) create mode 100644 Resources/Prototypes/Guidebook/rules.yml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/BanDurations.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/BanTypes.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC0.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC10AHelp.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC11AhelpThreats.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC12MinAge.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC14ICinOOC.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC1Admins.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC2DBAD.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC3NoHate.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC4NoERP.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC5Metacomms.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC6BanEvasion.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC7EnglishOnly.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC8Exploits.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC9Multikey.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/DefaultRules.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/README.txt create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleTypes.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR0.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR10Subordination.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11-1AnimalEscalation.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11-2ConflictTypes.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11Escalation.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR12RoleAbandonment.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR13PerformRole.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR14SecComStandard.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR15SpaceLaw.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR1Silicons.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR2Familiars.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR3NormalRP.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR4Metashield.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR5Arrivals.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR6SelfAntag.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR7RoundStalling.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR8NoFriendlyAntag.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR9MassSabotage.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS0.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS10OrderConflicts.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS1Laws.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS2LawPriority.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS3LawRedefinition.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS4RequestChanges.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS5FreeSilicon.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS6UnreasonableOrders.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS7Consistency.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS8DefaultCrewDefinition.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS9DefaultHarmDefinition.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLControlledSubstances.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLCrimeList.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLRestrictedGear.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLRestrictedWeapons.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SpaceLaw.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/WizDenCoreOnlyRules.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/WizDenLRPRules.xml create mode 100644 Resources/ServerInfo/Guidebook/ServerRules/WizDenMRPRules.xml delete mode 100644 Resources/ServerInfo/RP_Rules.txt delete mode 100644 Resources/ServerInfo/Rules.txt diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 21c3070b8022..da9d4d693a81 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,6 +15,7 @@ /Content.*/GameTicking/ @moonheart08 @EmoGarbage404 /Resources/ServerInfo/ @moonheart08 @Chief-Engineer /Resources/ServerInfo/Guidebook/ @moonheart08 @EmoGarbage404 +/Resources/ServerInfo/Guidebook/ServerRules/ @Chief-Engineer /Resources/engineCommandPerms.yml @moonheart08 @Chief-Engineer /Resources/clientCommandPerms.yml @moonheart08 @Chief-Engineer @@ -23,6 +24,7 @@ /Resources/Prototypes/Body/ @DrSmugleaf # suffering /Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf /Resources/Prototypes/Entities/Mobs/Species/ @DrSmugleaf +/Resources/Prototypes/Guidebook/rules.yml @Chief-Engineer /Content.*/Body/ @DrSmugleaf /Content.YAMLLinter @DrSmugleaf /Content.Shared/Damage/ @DrSmugleaf diff --git a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml index 8dbfde3c4754..cc6cc6e82b09 100644 --- a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml +++ b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml @@ -18,7 +18,7 @@ Name="SearchBar" PlaceHolder="{Loc 'guidebook-filter-placeholder-text'}" HorizontalExpand="True" - Margin="0 5 10 5"> + Margin="0 5 10 5"> diff --git a/Resources/Locale/en-US/guidebook/guides.ftl b/Resources/Locale/en-US/guidebook/guides.ftl index ff1ffbf5eafb..66c1a42adf28 100644 --- a/Resources/Locale/en-US/guidebook/guides.ftl +++ b/Resources/Locale/en-US/guidebook/guides.ftl @@ -68,5 +68,60 @@ guide-entry-revolutionaries = Revolutionaries guide-entry-minor-antagonists = Minor Antagonists guide-entry-space-ninja = Space Ninja +guide-entry-rules = Server Rules +guide-entry-rules-core-only = Core Only Ruleset +guide-entry-rules-lrp = Standard Ruleset +guide-entry-rules-mrp = MRP Ruleset +guide-entry-rules-role-types = Role Types +guide-entry-rules-core = Core Rules +guide-entry-rules-c1 = C1 +guide-entry-rules-c2 = C2 +guide-entry-rules-c3 = C3 +guide-entry-rules-c4 = C4 +guide-entry-rules-c5 = C5 +guide-entry-rules-c6 = C6 +guide-entry-rules-c7 = C7 +guide-entry-rules-c8 = C8 +guide-entry-rules-c9 = C9 +guide-entry-rules-c10 = C10 +guide-entry-rules-c11 = C11 +guide-entry-rules-c12 = C12 +guide-entry-rules-c13 = C13 +guide-entry-rules-c14 = C14 +guide-entry-rules-roleplay = Roleplay Rules +guide-entry-rules-r1 = R1 +guide-entry-rules-r2 = R2 +guide-entry-rules-r3 = R3 +guide-entry-rules-r4 = R4 +guide-entry-rules-r5 = R5 +guide-entry-rules-r6 = R6 +guide-entry-rules-r7 = R7 +guide-entry-rules-r8 = R8 +guide-entry-rules-r9 = R9 +guide-entry-rules-r10 = R10 +guide-entry-rules-r11 = R11 +guide-entry-rules-r12 = R12 +guide-entry-rules-r13 = R13 +guide-entry-rules-r14 = R14 +guide-entry-rules-r15 = R15 +guide-entry-rules-silicon = Silicon Rules +guide-entry-rules-s1 = S1 +guide-entry-rules-s2 = S2 +guide-entry-rules-s3 = S3 +guide-entry-rules-s4 = S4 +guide-entry-rules-s5 = S5 +guide-entry-rules-s6 = S6 +guide-entry-rules-s7 = S7 +guide-entry-rules-s8 = S8 +guide-entry-rules-s9 = S9 +guide-entry-rules-s10 = S10 +guide-entry-rules-space-law = Space Law +guide-entry-rules-sl-crime-list = Crime List +guide-entry-rules-sl-controlled-substances = Controlled Substances +guide-entry-rules-sl-restricted-gear = Restricted Gear +guide-entry-rules-sl-restricted-weapons = Restricted Weapons +guide-entry-rules-ban-types = Ban Types +guide-entry-rules-ban-durations = Ban Durations + guide-entry-writing = Writing guide-entry-glossary = Glossary diff --git a/Resources/Prototypes/Guidebook/rules.yml b/Resources/Prototypes/Guidebook/rules.yml new file mode 100644 index 000000000000..4aad44948555 --- /dev/null +++ b/Resources/Prototypes/Guidebook/rules.yml @@ -0,0 +1,369 @@ +- type: guideEntry # Default for forks and stuff. Should not be listed anywhere if the server is using a custom ruleset. + id: DefaultRuleset + name: guide-entry-rules + text: "/ServerInfo/Guidebook/ServerRules/DefaultRules.xml" + +- type: guideEntry + id: CoreRuleset + name: guide-entry-rules-core-only + priority: 0 + text: "/ServerInfo/Guidebook/ServerRules/WizDenCoreOnlyRules.xml" + +- type: guideEntry + id: StandardRuleset + name: guide-entry-rules-lrp + priority: 5 + text: "/ServerInfo/Guidebook/ServerRules/WizDenLRPRules.xml" + +- type: guideEntry + id: MRPRuleset + name: guide-entry-rules-mrp + priority: 10 + text: "/ServerInfo/Guidebook/ServerRules/WizDenMRPRules.xml" + +- type: guideEntry + id: RoleTypes + name: guide-entry-rules-role-types + priority: 20 + text: "/ServerInfo/Guidebook/ServerRules/RoleTypes.xml" + +- type: guideEntry + id: CoreRules + name: guide-entry-rules-core + priority: 30 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC0.xml" + children: + - RuleC1 + - RuleC2 + - RuleC3 + - RuleC4 + - RuleC5 + - RuleC6 + - RuleC7 + - RuleC8 + - RuleC9 + - RuleC10 + - RuleC11 + - RuleC12 + - RuleC13 + - RuleC14 + +- type: guideEntry + id: RuleC1 + name: guide-entry-rules-c1 + priority: 1 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC1Admins.xml" + +- type: guideEntry + id: RuleC2 + name: guide-entry-rules-c2 + priority: 2 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC2DBAD.xml" + +- type: guideEntry + id: RuleC3 + name: guide-entry-rules-c3 + priority: 3 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC3NoHate.xml" + +- type: guideEntry + id: RuleC4 + name: guide-entry-rules-c4 + priority: 4 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC4NoERP.xml" + +- type: guideEntry + id: RuleC5 + name: guide-entry-rules-c5 + priority: 5 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC5Metacomms.xml" + +- type: guideEntry + id: RuleC6 + name: guide-entry-rules-c6 + priority: 6 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC6BanEvasion.xml" + +- type: guideEntry + id: RuleC7 + name: guide-entry-rules-c7 + priority: 7 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC7EnglishOnly.xml" + +- type: guideEntry + id: RuleC8 + name: guide-entry-rules-c8 + priority: 8 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC8Exploits.xml" + +- type: guideEntry + id: RuleC9 + name: guide-entry-rules-c9 + priority: 9 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC9Multikey.xml" + +- type: guideEntry + id: RuleC10 + name: guide-entry-rules-c10 + priority: 10 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC10AHelp.xml" + +- type: guideEntry + id: RuleC11 + name: guide-entry-rules-c11 + priority: 11 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC11AhelpThreats.xml" + +- type: guideEntry + id: RuleC12 + name: guide-entry-rules-c12 + priority: 12 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC12MinAge.xml" + +- type: guideEntry + id: RuleC13 + name: guide-entry-rules-c13 + priority: 13 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml" + +- type: guideEntry + id: RuleC14 + name: guide-entry-rules-c14 + priority: 14 + text: "/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC14ICinOOC.xml" + +- type: guideEntry + id: RoleplayRules + name: guide-entry-rules-roleplay + priority: 40 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR0.xml" + children: + - RuleR1 + - RuleR2 + - RuleR3 + - RuleR4 + - RuleR5 + - RuleR6 + - RuleR7 + - RuleR8 + - RuleR9 + - RuleR10 + - RuleR11 + - RuleR12 + - RuleR13 + - RuleR14 + - RuleR15 + +- type: guideEntry + id: RuleR1 + name: guide-entry-rules-r1 + priority: 1 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR1Silicons.xml" + +- type: guideEntry + id: RuleR2 + name: guide-entry-rules-r2 + priority: 2 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR2Familiars.xml" + +- type: guideEntry + id: RuleR3 + name: guide-entry-rules-r3 + priority: 3 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR3NormalRP.xml" + +- type: guideEntry + id: RuleR4 + name: guide-entry-rules-r4 + priority: 4 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR4Metashield.xml" + +- type: guideEntry + id: RuleR5 + name: guide-entry-rules-r5 + priority: 5 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR5Arrivals.xml" + +- type: guideEntry + id: RuleR6 + name: guide-entry-rules-r6 + priority: 6 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR6SelfAntag.xml" + +- type: guideEntry + id: RuleR7 + name: guide-entry-rules-r7 + priority: 7 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR7RoundStalling.xml" + +- type: guideEntry + id: RuleR8 + name: guide-entry-rules-r8 + priority: 8 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR8NoFriendlyAntag.xml" + +- type: guideEntry + id: RuleR9 + name: guide-entry-rules-r9 + priority: 9 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR9MassSabotage.xml" + +- type: guideEntry + id: RuleR10 + name: guide-entry-rules-r10 + priority: 10 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR10Subordination.xml" + +- type: guideEntry + id: RuleR11 + name: guide-entry-rules-r11 + priority: 11 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11Escalation.xml" + +- type: guideEntry + id: RuleR12 + name: guide-entry-rules-r12 + priority: 12 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR12RoleAbandonment.xml" + +- type: guideEntry + id: RuleR13 + name: guide-entry-rules-r13 + priority: 13 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR13PerformRole.xml" + +- type: guideEntry + id: RuleR14 + name: guide-entry-rules-r14 + priority: 14 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR14SecComStandard.xml" + +- type: guideEntry + id: RuleR15 + name: guide-entry-rules-r15 + priority: 15 + text: "/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR15SpaceLaw.xml" + +- type: guideEntry + id: SiliconRules + name: guide-entry-rules-silicon + priority: 50 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS0.xml" + children: + - RuleS1 + - RuleS2 + - RuleS3 + - RuleS4 + - RuleS5 + - RuleS6 + - RuleS7 + - RuleS8 + - RuleS9 + - RuleS10 + +- type: guideEntry + id: RuleS1 + name: guide-entry-rules-s1 + priority: 1 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS1Laws.xml" + +- type: guideEntry + id: RuleS2 + name: guide-entry-rules-s2 + priority: 2 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS2LawPriority.xml" + +- type: guideEntry + id: RuleS3 + name: guide-entry-rules-s3 + priority: 3 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS3LawRedefinition.xml" + +- type: guideEntry + id: RuleS4 + name: guide-entry-rules-s4 + priority: 4 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS4RequestChanges.xml" + +- type: guideEntry + id: RuleS5 + name: guide-entry-rules-s5 + priority: 5 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS5FreeSilicon.xml" + +- type: guideEntry + id: RuleS6 + name: guide-entry-rules-s6 + priority: 6 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS6UnreasonableOrders.xml" + +- type: guideEntry + id: RuleS7 + name: guide-entry-rules-s7 + priority: 7 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS7Consistency.xml" + +- type: guideEntry + id: RuleS8 + name: guide-entry-rules-s8 + priority: 8 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS8DefaultCrewDefinition.xml" + +- type: guideEntry + id: RuleS9 + name: guide-entry-rules-s9 + priority: 9 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS9DefaultHarmDefinition.xml" + +- type: guideEntry + id: RuleS10 + name: guide-entry-rules-s10 + priority: 10 + text: "/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS10OrderConflicts.xml" + +- type: guideEntry + id: SpaceLaw + name: guide-entry-rules-space-law + priority: 60 + text: "/ServerInfo/Guidebook/ServerRules/SpaceLaw/SpaceLaw.xml" + children: + - SpaceLawCrimeList + - SpaceLawControlledSubstances + - SpaceLawRestrictedGear + - SpaceLawRestrictedWeapons + +- type: guideEntry + id: SpaceLawCrimeList + name: guide-entry-rules-sl-crime-list + priority: 5 + text: "/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLCrimeList.xml" + +- type: guideEntry + id: SpaceLawControlledSubstances + name: guide-entry-rules-sl-controlled-substances + priority: 20 + text: "/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLControlledSubstances.xml" + +- type: guideEntry + id: SpaceLawRestrictedGear + name: guide-entry-rules-sl-restricted-gear + priority: 30 + text: "/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLRestrictedGear.xml" + +- type: guideEntry + id: SpaceLawRestrictedWeapons + name: guide-entry-rules-sl-restricted-weapons + priority: 40 + text: "/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLRestrictedWeapons.xml" + +- type: guideEntry + id: BanTypes + name: guide-entry-rules-ban-types + priority: 90 + text: "/ServerInfo/Guidebook/ServerRules/BanTypes.xml" + +- type: guideEntry + id: BanDurations + name: guide-entry-rules-ban-durations + priority: 100 + text: "/ServerInfo/Guidebook/ServerRules/BanDurations.xml" diff --git a/Resources/Prototypes/Guidebook/ss14.yml b/Resources/Prototypes/Guidebook/ss14.yml index c1017fefcae5..5b1f1dd8f97a 100644 --- a/Resources/Prototypes/Guidebook/ss14.yml +++ b/Resources/Prototypes/Guidebook/ss14.yml @@ -3,6 +3,7 @@ name: guide-entry-ss14 text: "/ServerInfo/Guidebook/SpaceStation14.xml" children: + - SpaceLaw - Controls - Jobs - Survival diff --git a/Resources/ServerInfo/Guidebook/ServerRules/BanDurations.xml b/Resources/ServerInfo/Guidebook/ServerRules/BanDurations.xml new file mode 100644 index 000000000000..2c85346b49d7 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/BanDurations.xml @@ -0,0 +1,17 @@ + + # Ban Durations + + Bans can be appealed at forum.ss14.io in the ban appeals section. + + ## Temporary + Temporary bans will be lifted automatically after a certain amount of time. If they are a game ban, they will tell you how much time is remaining when you try to connect. + + ## Indefinite + These bans will only be removed on a successful appeal on the forums. Any ban which doesn't tell you when it expires and doesn't specify otherwise can be presumed to be an indefinite ban. + + ## Voucher + This is an indefinite ban which may only be appealed both with a successful appeal and which require a voucher of good behavior from the administrative team of a well-known or at least decently active SS13/SS14 server in order for the appeal to be considered. Voucher bans typically cannot be appealed for at least six months after being issued. Without a voucher, a player can only attempt to appeal a voucher ban once, and only if the ban was inappropriately placed. Voucher bans are typically only placed as a result of an unsuccessful appeal of an indefinite game ban by players with a history of bans and of causing issues. + + ## Permanent + This is a ban that is only appealable if the ban was inappropriately placed, including if the ban should not have been permanent. If the result of the appeal is that the ban was appropriately placed, the ban may not be appealed again and will not be lifted. These bans are extremely rare, but are applied to players who continually cause problems even after a voucher ban or users who have completely unacceptable behavior may be permanently removed. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/BanTypes.xml b/Resources/ServerInfo/Guidebook/ServerRules/BanTypes.xml new file mode 100644 index 000000000000..b10ea3c393b5 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/BanTypes.xml @@ -0,0 +1,11 @@ + + # Ban Types + + Bans can be appealed at forum.ss14.io in the ban appeals section. + + ## Role Ban + Also called a "job ban", this ban prevents your character from joining or late-joining a round as one or more jobs or roles. These are often used in response to problematic behavior in particular departments or address gross inexperience in important roles such as heads of staff. These bans do not mechanically prevent you from switching to the role during a round or acting as that role, but doing so is considered ban evasion. + + ## Game Ban + Also called a "server ban", this ban prevents you from connecting to all Wizard's Den servers. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC0.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC0.xml new file mode 100644 index 000000000000..7b8cfbcf61e0 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC0.xml @@ -0,0 +1,19 @@ + + # Core Rules + These rules apply at all times, including between rounds. + + - [textlink="1. Admins have final say" link="RuleC1"] + - [textlink="2. Don't be a dick" link="RuleC2"] + - [textlink="3. No Hate Speech or Discriminatory Language" link="RuleC3"] + - [textlink="4. No sexual content/themes, including erotic roleplay (ERP) and no shock content" link="RuleC4"] + - [textlink="5. Do not use out of game methods to communicate with other players" link="RuleC5"] + - [textlink="6. Do not attempt to evade bans" link="RuleC6"] + - [textlink="7. Only use English" link="RuleC7"] + - [textlink="8. Do not exploit the game, use cheats, or macros" link="RuleC8"] + - [textlink="9. Do not use multiple accounts, or alt accounts, and do not share accounts" link="RuleC9"] + - [textlink="10. Do not abuse or ignore admin messages" link="RuleC10"] + - [textlink="11. Do not threaten to ahelp other players or argue with them about rules" link="RuleC11"] + - [textlink="12. Players must be and act at least 16 years old" link="RuleC12"] + - [textlink="13. Use realistic character names, and do not use names of famous people" link="RuleC13"] + - [textlink="14. Do not use LOOC or OOC to share current round information" link="RuleC14"] + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC10AHelp.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC10AHelp.xml new file mode 100644 index 000000000000..2d639c5b84a2 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC10AHelp.xml @@ -0,0 +1,29 @@ + + # Core Rule 10 - Do not abuse or ignore admin messages + Admin help, or "ahelp", is the system used by admins to communicate with specific players in the game. Only use admin help for things requiring admin attention. If you ignore messages admins send to you via ahelp, or disconnect during an ahelp, you may be banned. If you urgently need to leave during an ahelp, you may do so but will likely need to continue the ahelp on the forums. Do not admin check, be hostile/aggressive, request events, or spam. IC methods of contacting admins, like prayers, faxes, red phones, and banana phones, should be used when there is not an issue. + + Admins are not always online, but all ahelps are automatically relayed to discord. For various reasons, admins might not respond to an ahelp even if they've handled it. A lack of response does not necessarily mean that an ahelp was ignored. + + ## Should I ahelp X? + You can ahelp anytime you genuinely think a player is breaking a rule. Not all ahelps end up being for something that an admin needs to intervene in, but that's ok, admins would rather have people occasionally report things that turn out to not be an issue than miss reports for actual issues because someone was unsure, or get those reports late because someone waited until the end of the round to be more sure. + + The most common reason players give for not ahelping issues is that they don't want to waste admin time, but it only takes a few seconds for an admin to check if someone is an antagonist. If you are ahelping too many things, an admin will let you know. If you're not being told to stop reporting something or to report less things, then you can safely assume that you aren't causing any issues. + + # What should I include in an ahelp? + At a minimum, admins need to know what the issue is to be able to address an ahelp. Don't send ahelp messages with no information about what your question or the issue is. Messages like "hello" are often considered admin checking. + + If you can, an ideal ahelp message includes what the issue is along with who is causing it and their character's name if possible. + + # Examples + Appropriate uses of ahelp: + - reporting people who you think are violating rules, + - asking questions about rules, + - asking for a temporary exemption from a rule, and + - request a minor gimmick, like a TC trade or item spawn. + + Inappropriate uses of ahelp: + - checking if an admin is online, including sending messages without any information about the issue like "hello" or incomprehensible messages, + - being hostile or aggressive, + - requesting events, and + - spamming messages about the same issue. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC11AhelpThreats.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC11AhelpThreats.xml new file mode 100644 index 000000000000..47420264946c --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC11AhelpThreats.xml @@ -0,0 +1,20 @@ + + # Core Rule 11 - Do not threaten to ahelp other players or argue with them about rules + Don't threaten to ahelp a player, don't tell them you are ahelping them, and don't tell them you did ahelp them. You can argue in character about Space Law, but do not argue about whether something is or is not against the rules. If you think someone is breaking a rule, ahelp them. If you don't think someone is breaking a rule, don't ahelp them. Either way, the best thing that you can do once you after is to continue in-character. + + ## Example Scenario 1 + You are a security officer and think someone who is causing a ton of problems for security is not an antag and is breaking the rules by doing so. + + [color=#a4885c]Good:[/color] Since you think they are breaking a rule, you ahelp them when you're able to. You continue in-character by arresting them for the crimes that they committed. + + [color=#a4885c]Bad:[/color] You decide not to ahelp them. You kill them and tell them "you're lucky I didn't report you to the admins". + + [color=#a4885c]Bad:[/color] Since you think they are breaking a rule, you ahelp them when you're able to. You arrest them for the crimes that they committed and tell them "I ahelped you so enjoy your ban". + + ## Example Scenario 2 + A mouse is using emotes to bypass speech restrictions. + + [color=#a4885c]Good:[/color] You ahelp them then respond in-character by acting like you can't understand what the mouse is doing. + + [color=#a4885c]Bad:[/color] You use in character chat to tell the mouse that it is breaking a rule. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC12MinAge.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC12MinAge.xml new file mode 100644 index 000000000000..baa30a09faf7 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC12MinAge.xml @@ -0,0 +1,6 @@ + + # Core Rule 12 - Players must be and act at least 16 years old + All players must be at least 16 years old. Additionally, all players must act at least as mature as a 16 year old. Admins may ban someone who they believe is acting less mature than a 16 year old, even if the player is known to be significantly older than 16 years old. + + Anyone who connects to the servers is a player, even if they don't actually play in a round. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml new file mode 100644 index 000000000000..ec393ecdc17c --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml @@ -0,0 +1,66 @@ + + # Core Rule 13 - Use realistic character names, and do not use names of famous people + - No names of people or characters from the real world + - No titles/honorifics + - Must follow all other rules (no slurs/sexual names/etc) + - Usernames, objects, random characters, very "low effort" names, "meta" names, or otherwise implausible names cannot be used as names. See examples below. + - Admin rulings on IC names are final and disputes should be done through the forums, not by refusing to comply with an admin + + ## Clarification on "Meta" Names + Meta names are ones which attempt to take advantage of some game mechanic or game design choice. "Urist McHands" is a meta name because it is the default name used for admin spawned humans. "Operator Whiskey" is a meta name because it follows the naming pattern of nuclear operatives. This rule is not intended to prevent things like nuclear operatives using a fake ID with names that appear to be nuclear operative names if they decide that they want to do that. + + ## Conventions and Examples + [color=#994444]Bad[/color] cannot be used by any species. [color=#449944]Acceptable[/color] names can be used by any species. + + Humans typically use the Firstname Lastname convention. + - [color=#449944]Acceptable:[/color] Tom Fisher + - [color=#449944]Acceptable:[/color] Spacey Chapman + - [color=#994444]Bad:[/color] Dr. Tom Fisher + - [color=#994444]Bad:[/color] Walter White + - [color=#994444]Bad:[/color] George Washington + - [color=#994444]Bad:[/color] Joe Biden + - [color=#994444]Bad:[/color] Ben Dover + - [color=#994444]Bad:[/color] Mike Hunt + + Dwarfs typically use the human convention in a viking theme. + - [color=#449944]Acceptable:[/color] Ingrid Firebreath + - [color=#449944]Acceptable:[/color] Erik Lightningclaw + + Lizards typically use the Verb-article-Noun convention. + - [color=#449944]Acceptable:[/color] Cleans-the-Airlocks + - [color=#994444]Bad:[/color] Bans-the-Admins + + Slimes typically have names that are onomonopia. A last name is optional. + - [color=#449944]Acceptable:[/color] Foolp Suub + - [color=#449944]Acceptable:[/color] Foolp + - [color=#994444]Bad:[/color] Slime + + Diona typically have calm, nature themed, Noun of Noun style names. + - [color=#449944]Acceptable:[/color] Petal of Tranquility + - [color=#449944]Acceptable:[/color] Garden of Relaxation + - [color=#994444]Bad:[/color] Tree but Alive + + Mothmen typically use latin sounding names, or light themed names. + - [color=#449944]Acceptable:[/color] Socrates Temnora + - [color=#449944]Acceptable:[/color] Sierra Lightseeker + - [color=#449944]Acceptable:[/color] James Nightflitter + + Arachnids typically use latin sounding names. + - [color=#449944]Acceptable:[/color] Argyroneta Reticulatus + - [color=#449944]Acceptable:[/color] Loxosceles Domesticus + - [color=#994444]Bad:[/color] Spider-Man + + Usernames, objects, random characters, very "low effort" names, "meta" names, or otherwise implausible names are not permitted. + - [color=#994444]Bad:[/color] XxRobustxX + - [color=#994444]Bad:[/color] SDpksSodjdfk + - [color=#994444]Bad:[/color] Lkdsoisgoieun + - [color=#994444]Bad:[/color] F4ith H3arth + - [color=#994444]Bad:[/color] Greytide + - [color=#994444]Bad:[/color] Passenger + - [color=#994444]Bad:[/color] Urist McHands + - [color=#994444]Bad:[/color] Admin + - [color=#994444]Bad:[/color] Game-Master + - [color=#994444]Bad:[/color] Joe Mamma + - [color=#994444]Bad:[/color] Middle-Aged Man + - [color=#994444]Bad:[/color] Operative Whiskey + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC14ICinOOC.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC14ICinOOC.xml new file mode 100644 index 000000000000..44ad34deb66f --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC14ICinOOC.xml @@ -0,0 +1,13 @@ + + # Core Rule 14 - Do not use LOOC or OOC to share current round information + Local Out of Character (LOOC) and Out of Character (OOC) channel are meant for things that don't relate to the current round. Using these channels to share round info is often referred to as "IC in OOC" or "ick ock". + + ## Examples + Things you should [color=#a4885c]not[/color] do: + - Use LOOC to tell someone you are an antagonist. + - Use LOOC to tell someone that your character is not lying. + + Things you could do instead: + - Use codewords in-character. + - Try to convince them that you are not lying in-character, or accept that you won't be able to convince them. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC1Admins.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC1Admins.xml new file mode 100644 index 000000000000..ed9fa6133b99 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC1Admins.xml @@ -0,0 +1,6 @@ + + # Core Rule 1 - Admins have final say + These rules are not perfect. The rules attempt to clearly communicate what the admin team intends to be allowed and prohibited, but there are likely loopholes or other flaws that can be "lawyered". Don't attempt to manipulate the interpretation of the rules to suit your personal goals or to degrade the experience of other players. If you are unsure of something, follow the more restrictive option until you are able to ask an admin and get clarification. + + Admins can override rules if they deem it in the best interest of the current round, server, and/or community at large. Online admins are able to make final interpretations of rules during a round. Even if you disagree with how an admin interprets a rule, you must still follow the interpretation they provide for you. Admin actions and interpretations of rules can be contested through staff complaints. If admins believe that you are an overall negative impact to the community or rounds, you will be banned. Admins will be held fully accountable for their actions if they exercise this privilege. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC2DBAD.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC2DBAD.xml new file mode 100644 index 000000000000..5678cde195dc --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC2DBAD.xml @@ -0,0 +1,7 @@ + + # Core Rule 2 - Don't be a dick + Don't do anything with the goal of negatively affecting other players. Not everyone is going to enjoy every round. Killing someone is allowed in certain situations even though it might negatively affect them, but no one should be doing anything for the purpose of harming someone else's experience. + + ## MRP Amendment + Do not interact negatively with SSD/AFK players. Interactions to complete antagonist objectives or duties like security searches/arrests are always permitted. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC3NoHate.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC3NoHate.xml new file mode 100644 index 000000000000..3a2e288ba9f1 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC3NoHate.xml @@ -0,0 +1,20 @@ + + # Core Rule 3 - No Hate Speech or Discriminatory Language + This is a zero tolerance rule. + + This rule prohibits all the following: + - Hate Speech + - Slurs (including variations of slurs, racial, sexual, disability-related, or language closely tied to real-life slurs) + - Bigotry + - Racism (including Speciesism, which would be demeaning other players based on their in-game race) + - Sexism + + ## Examples + Allowed: + - Telling someone that you are gay. + + Prohibited: + - Calling someone gay in a context where gay is used as an insult or negative attribute. + - Using a racial slur or variant in a positive context. + - Using the word "retard" in any context. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC4NoERP.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC4NoERP.xml new file mode 100644 index 000000000000..a0921f59070a --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC4NoERP.xml @@ -0,0 +1,23 @@ + + # Core Rule 4 - No sexual content/themes, including erotic roleplay (ERP) and no shock content + This is a zero tolerance rule. + + Erotic Roleplay (commonly abbreviated as "ERP") and sexual content is not allowed. This includes direct and indirect mentions of sexual behavior or actions. Slight leeway is given to insults, but this rule is otherwise strictly enforced. + + In-game romantic relationships should not become the focus of the game for you and anyone else involved. + + Things that appear to be intended to or are likely to disturb players out of character are considered shock content and are not allowed. + + ## Examples + Allowed: + - Telling someone that they are being a dickhead. + - Telling someone that you are going to kill the captain, as long as it is clear that you mean it in character. + + Prohibited: + - Emoting sexual acts. + - Erotica content. + - Erotic or sexual memes. + - Memes which contain sexual content. + - Dedicating significant portions of rounds to romantic relationships, dating, or similar things. + - Emoting defecation or related acts. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC5Metacomms.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC5Metacomms.xml new file mode 100644 index 000000000000..0c0f336e6d0b --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC5Metacomms.xml @@ -0,0 +1,18 @@ + + # Core Rule 5 - Do not use out of game methods to communicate with other players + This is a zero tolerance rule. + + Do not utilize any external means of communication to talk to other players who are connected to the same server, or who were connected to the same server during the current round. This is referred to as "metacomming" and includes any means of communication including text, voice, images, and video. This includes applications such as Discord, Steam, and other platforms, along with in-person communication. + + Even if information is not being shared or abused, it may still be considered a violation of this rule. Due to the difficulty of determining if information is being shared, it will almost always be presumed that people who message another player they are in a round with, or who are in a voice call with another player during a round are sharing round information. Due to the difficulty of determining if users are abusing information that they are sharing, it will almost always be presumed that the information is being abused. + + The only exemption to this rule is when [color=#a4885c]all[/color] players are in the server lobby. + + ## Teaching new players + Teaching players is not exempt from this rule. If you want to teach a new player, it is recommended to either watch a stream of them playing the game while not playing yourself, or communicate with them using only in-game methods of communication. + + ## Streaming + Public livestreams are not exempt from this rule, but have different liability. Using information from a public live stream of the game (stream sniping) is a violation of this rule. Watching a public live stream of the game while connected to the same server is a violation of this rule. Allowing people watching a public live stream to share information about the current round, for example through the stream's chat, is a violation of this rule. Using that information is also a violation of this rule. Sharing information about the current round with a streamer is a violation of this rule if that information was obtained from any source but the stream. The stream's moderators are expected to enforce this on the streaming platform in addition to any in-game enforcement done by game admins. + + Public livestreaming by itself is not a violation of the rule as long as the stream is sufficiently moderated. Streamers are encouraged, but not required, to use a stream delay. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC6BanEvasion.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC6BanEvasion.xml new file mode 100644 index 000000000000..bec8b4fabd76 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC6BanEvasion.xml @@ -0,0 +1,15 @@ + + # Core Rule 6 - Do not attempt to evade bans + This is a zero tolerance rule. + + Almost all bans may be appealed on our forums at forum.ss14.io in the ban appeals section. This is generally the only acceptable way to contact the administration team to discuss your ban and revise it if it is inappropriate, including if it is mistakenly applied. + + Any attempt to circumvent or bypass a game ban will result in a voucher ban. Attempting to evade role bans by gaining access to or working in the capacity of a job you are banned from will result in a game ban. These bans are applied even if the evasion attempt is unsuccessful. + + ## Exceptions + There are no exemptions for evading or attempting to evade game bans. Antagonists who impersonate or take over a role which they are banned from to aid in their goals are not considered to be evading their role ban. + + ## Additional Information + - [textlink="Ban Types" link="BanTypes"] + - [textlink="Ban Durations" link="BanDurations"] + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC7EnglishOnly.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC7EnglishOnly.xml new file mode 100644 index 000000000000..630c522bcef8 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC7EnglishOnly.xml @@ -0,0 +1,10 @@ + + # Core Rule 7 - Only use English + Only English is permitted, both in-character and out-of-character. You must be fluent in English enough to be able to not cause game issues, and to be able to communicate with game admins when necessary. If a game admin does not feel that you are fluent enough in English, they may ban you. + + ## Why + We do not have enough staff fluent in other languages to moderate them. Translation tools can be unreliable and are not integrated well into the game. + + ## Non-English Options + There are many servers that allow or focus on other languages. You are highly encouraged to play only on servers that allow languages you are fluent in. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC8Exploits.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC8Exploits.xml new file mode 100644 index 000000000000..48cbaaa9acfc --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC8Exploits.xml @@ -0,0 +1,12 @@ + + # Core Rule 8 - Do not exploit the game, use cheats, or macros + The following are prohibited by this rule: + - bugs and exploits which have effects that persist beyond the current round, + - intentionally used bugs, exploits, and unintended behaviors which give the user an advantage over players who do not use them, even if their effects do not persist across rounds, + - evading or bypassing afk detection, + - anything which results in gaining elevated privileges, including admin permissions, + - external tools and client modifications, including macros, and + - anything which prevents another player who is not game banned from being able to play on the servers, not including in-character actions that do not persist across rounds. + + Both attempts and successful use are prohibited. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC9Multikey.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC9Multikey.xml new file mode 100644 index 000000000000..d402918dcd46 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC9Multikey.xml @@ -0,0 +1,7 @@ + + # Core Rule 9 - Do not use multiple accounts, or alt accounts, and do not share accounts + Use of multiple accounts is referred to as "multikey". the rule applies even if the accounts are not used at the same time, including if the old account is abandoned. All accounts may be banned if this rule is violated. You are responsible for everything done on and with your account. You are just as responsible for actions taken by other people using your account as you would be had you taken the actions themselves. + + ## Switching to a new account + If you lose access to an account, you must contact game admins on the forums notifying admins before using a new account to connect to the servers. Your message to game admins must include the username of your old account. Creating a new account while your current account is banned will be considered ban evasion. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/DefaultRules.xml b/Resources/ServerInfo/Guidebook/ServerRules/DefaultRules.xml new file mode 100644 index 000000000000..3e19fefeedcf --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/DefaultRules.xml @@ -0,0 +1,5 @@ + + # Server Rules + + This server has not written any rules yet. Please listen to the staff. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/README.txt b/Resources/ServerInfo/Guidebook/ServerRules/README.txt new file mode 100644 index 000000000000..d7ac858c16f8 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/README.txt @@ -0,0 +1,5 @@ +These files contain Wizard's Den server rules. Since they reference Wizard's Den, they should not be used +by other servers without at least enough modification to not mislead players into thinking that they are +playing on Wizard's Den. + +The filenames used for the rules files are not themselves rules. Only the contents of the files are rules. diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleTypes.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleTypes.xml new file mode 100644 index 000000000000..d5373a730a33 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleTypes.xml @@ -0,0 +1,21 @@ + + # Role Types + + ## Crew Aligned/Non-antagonist + In most rounds, a majority of players will be non-antagonists, meaning that they are crew aligned. This is the "default" role, if the game doesn't tell you that you are one of the other roles defined here, then you are a non-antagonist. Overall, non-antagonists are intended to work towards a net positive effect on the round. + + ## Solo Antagonist + Certain roles are intended to cause problems for the round or for non-antagonists. You are only a solo antagonist if the game clearly and explicitly tells you that you are a solo antagonist. Antagonists are exempt from many but not all roleplay rules. + + ## Team Antagonist + Team antagonists are like solo antagonists but they have other antagonists who they are expected to not hinder, and who they may be expected to help. You are only a team antagonist if the game clearly and explicitly tells you that you are a team antagonist. + + ## Free Agent + Certain roles are free to choose if they want to behave as an antagonist or as a non-antagonist, and may change their mind whenever they'd like. You are only free agent if the game clearly and explicitly tells you that you are a free agent. + + ## Familiar + Familiars are considered non-antagonists, but have instructions to obey someone. They must obey this person even if it causes them to violate roleplay rules or die. You are only a familiar if the game clearly and explicitly tells you that you are a familiar. You are only the familiar of the person the game tells you. + + ## Silicon + Silicones have a set of laws that they must follow above all else except the core rules. You are only silicon if the game clearly and explicitly tells you that you are a silicon. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR0.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR0.xml new file mode 100644 index 000000000000..07b176b359a2 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR0.xml @@ -0,0 +1,26 @@ + + # Roleplay Rules + These rules only apply during a round. A round ends only when the round summary has appeared. All of these rules apply fully until the moment that the round summary appears, even while the arrivals shuttle is in transit. + + The deathmatch and sandbox game modes are exempt from these rules. Players who choose to not follow these rules are entirely responsible for knowing if an exempt game mode is active. + + Roleplay rules do not apply to ghosts/spectators/observers while they are ghosts/spectators/observers. Dead chat is considered to be an in-game out of character chat channel. + + See the list of [textlink="role types" link="RoleTypes"] for more information about the different types of roles. + + - [textlink="1. Silicones must follow Silicon Rules" link="RuleR1"] + - [textlink="2. Familiars must obey their master" link="RuleR2"] + - [textlink="3. Roleplay a normal person" link="RuleR3"] + - [textlink="4. Do not metagame, obey the Metashield" link="RuleR4"] + - [textlink="5. Don't interfere with arrivals" link="RuleR5"] + - [textlink="6. Don't act like an antagonist unless the game tells you that you are one" link="RuleR6"] + - [textlink="7. Do not stall the round" link="RuleR7"] + - [textlink="8. As an antagonist, only be friendly to your team and don't work against your team" link="RuleR8"] + - [textlink="9. As an antagonist, do not cause excessive death, damage, or destruction beyond your objectives" link="RuleR9"] + - [textlink="10. Listen to your team leader" link="RuleR10"] + - [textlink="11. Follow reasonable escalation" link="RuleR11"] + - [textlink="12. Do not abandon your role" link="RuleR12"] + - [textlink="13. Stick to your role" link="RuleR13"] + - [textlink="14. Set an example if playing command or security" link="RuleR14"] + - [textlink="15. Command and Security must follow Space Law" link="RuleR15"] + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR10Subordination.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR10Subordination.xml new file mode 100644 index 000000000000..2147ddc1110f --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR10Subordination.xml @@ -0,0 +1,26 @@ + + # Roleplay Rule 10 - Listen to your team leader + Captains lead all departments and other members of command. Department heads lead members of their department. Certain antagonist teams have team leaders, like nuclear operative commanders or head revolutionaries. You are not required to perfectly follow orders given to you by your leaders, but you should generally allow your leaders to lead and not interfere with their ability to. You can choose to ignore unreasonable orders, including ones which are will result in your death unless you are an antagonist with an objective that requires you to die. + + Team antagonists have to listen to the leader of their antagonist team. Team antagonists do not have to listen to any other leaders, including leaders of other antagonist teams. Solo antagonists do not have to listen to any leaders at all. + + ## Examples + Acceptable: + - A traitor ignores orders from a nuclear operative commander. + - An antagonist ignores orders from the captain. + - An engineer tells the Chief Engineer that they don't think it's a good idea to setup the singularity, but does so anyway when ordered to. + - An engineer tells the Chief Engineer that they don't know how to setup the singularity correctly, so refuses orders to, but accepts an offer to be taught how. + - An atmospheric technician refuses an order from the captain that would create an atmospheric hazard on the station. + - A doctor refuses an order from the Chief Engineer about who to give medical treatment to first. + - A revolutionary refuses a suicide mission from a head revolutionary. + - The Chief Engineer doesn't follow an order from the captain to setup backup power because there is an unrelated engineering emergency that the Chief Engineer needs to prioritize. + - The captain orders command to give the nuclear authentication disk to nuclear operatives, so command arrests the captain and picks a new captain. + - The research director orders scientists to say "Long live Nanotrasen!" every time they enter the bar. The scientists say they will, but don't follow the order. + + Prohibited: + - A nuclear operative ignores an order from the commander operative because they don't like the plan. + - The Chief Engineer refuses an order from the captain to setup backup power because the Chief Engineer doesn't think backup power is necessary. + - An engineer refuses an order from the Chief Engineer to setup the singularity because they prefer a different power source. + - An engineer refuses to perform a task because they don't know how to do it, and refuses to be taught for no reason. + - A head revolutionary orders revolutionaries to blend in and not do anything illegal until they are told to reveal themselves. Instead, revolutionaries collect weapons and attack security. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11-1AnimalEscalation.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11-1AnimalEscalation.xml new file mode 100644 index 000000000000..36655ba8414b --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11-1AnimalEscalation.xml @@ -0,0 +1,36 @@ + + # Roleplay Rule 11-1 - Escalation Involving Animals + Escalation rules are looser with animals than with people. These looser requirements do not apply to the requirements other people attacking each other have, even if their fighting is directly related to the conflict involving the animal. + + Non-pets, such as mice and monkeys, can be freely killed with any IC reason such as pest control, or for food. These roles are often available in numbers as ghost roles, so removing one from the round doesn’t typically remove them all. + + Pets, including but not limited to Ian, Renault, Remilia, and Hamlet, cannot be freely killed, they require escalation. These roles are often available once per round at most, except roles like Remilia. + + Permanently trapping an animal, such as putting a mouse in a plant, is considered similar to killing the animal so should only be done with an IC reason. + + Both sides can escalate much more rapidly than they'd be able to if both were people. Animals are often more limited in the maximum force they can use compared to people, which limits the negative effects of them rapidly escalating. Animals also typically have less health than people, and are limited in the ease with which they can get healing, which justifies them responding to even weak attacks more severely. + + Neither the animal nor the person is obligated to get the other medical attention if they are put into crit. Attacking someone to death rather than stopping once they are in crit is considered a significant difference. While sufficient escalation may justify continuing to attack, generally people and pets shouldn't continue to be attacked once in crit, but non-pets may be. Gibbing is also considered a significant step because it prevents cloning or resuscitation. The fact that an animal made the last hit putting someone into crit does not allow people who fought on the side of the animal to not attempt to get them medical attention. + + The use of sensible, non-targeted mousetraps is not a conflict and does not require escalation. + + The killing or attacking of pets can be treated as an escalation step by players with a genuine IC connection to the animal. Generally, all crew can consider themselves to have an IC connection to any station pets. The degree of escalation should be proportional to the connection to the pet, in addition to the usual requirement of being proportional to the attack. For example, an attack on Ian can be treated nearly identically to an attack on a crewmember, whereas an attack on a pet mouse is much less severe. Normal escalation limits still apply, you cannot attack people who defended themselves from an animal that randomly attacked them, just as you could not attack someone who defended themselves from a coworker that randomly attacked them. + + Crew can "adopt" non-pets, like mice, and consider themselves to have a connection to the animal if they roleplay the adoption well. This does not affect the requirement of whether other players are required to apply escalation rules to these animals, it only creates a connection that can be used to justify retaliatory escalation to attacks by the adopter. Simply saying that they've adopted an animal is not sufficient, but carrying it with them is. The degree of connection is proportional to IC actions. Crew cannot consider themselves to have a connection for escalation purposes to animals which are typically hostile, such as space carp or bears. + + ## Examples + Acceptable: + - A chef kills mice who enter or approach their kitchen. + - A janitor kills mice roaming the station. + - A lizard kills a mouse to eat. + - A chef has carried a mouse around in their hat for the last 10 minutes, they put the mouse down for a moment and another player kills it. The chef responds by attacking the other player with their fists and refusing them service for the rest of the shift. + - Ian is randomly attacked, a crewmember who sees this happen crits the killer and brings them to security. + - Hamlet goes into the kitchen and starts eating all the food. A chef sees this and starts swinging their knife at Hamlet. Hamlet starts biting the chef and crits them, then resumes eating. + + Prohibited: + - A janitor throws an armed mousetrap at Hamlet for no reason. + - Hamlet starts biting random people, trying to crit them, for no reason. + - A crewmember attacks security for killing a space carp they adopted. + - Ian gibs someone who was trying to kill someone. + - Hamlet attacks security for trying to arrest someone he likes. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11-2ConflictTypes.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11-2ConflictTypes.xml new file mode 100644 index 000000000000..3261d78b35a3 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11-2ConflictTypes.xml @@ -0,0 +1,30 @@ + + # Roleplay Rule 11-2 - Examples of Conflict Types + ## Verbal + - Shouting + - Yelling + - Insulting + + ## Non-harmful + - Shoving + - Stealing non-critical items, like easily replaced tools + + ## Non-lethal + - Stealing items without endangering someone's life, like a clown's pie cannon or the HoP's fax machine + - Stealing someone's ID somewhere that doesn't result in them being trapped + - Punching + - Disablers + - Stun batons + + ## Lethal + - Punching to crit or death + - Attacking with strong weapons, like bats + - Stealing items that endanger someone's life, like a hardsuit + - Stealing someone's ID, trapping them in a dangerous situation + + ## Permanently lethal + - Gibbing + - Not taking someone who you killed or put into crit to the medbay or security + - Hiding someone's body + - Spacing someone's body + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11Escalation.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11Escalation.xml new file mode 100644 index 000000000000..6f91fa0fb12a --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR11Escalation.xml @@ -0,0 +1,67 @@ + + # Roleplay Rule 11 - Follow reasonable escalation + Antagonists are fully exempt from escalation rules. Non-antagonists who are in a conflict with antagonists are not exempt. Escalation should typically follow steps or a pattern of conflict types similar to: + - Verbal + - Non-harmful + - Non-lethal + - Lethal + - Permanently lethal + + All new conflicts should start at the first step. A player should not escalate a conflict across steps without some escalation from the other party involved in the conflict. Players can skip steps to match the level of escalation that the other person is at, but should almost always not skip steps other than that. Players who attempt to deescalate conflicts will be given more leniency in escalating if the other party continues to escalate despite the attempt at de-escalation. You do not have to try to deescalate conflicts, but someone who watches you over the entire round, or over multiple rounds, should not feel that your goal is generally to escalate conflicts. + + Conflicts or escalation can be indirect. When someone steals someone else's ID, the theft is a direct part of the conflict, but if the victim becomes trapped as a result of not having their ID to open a door, that is also considered part of the conflict and escalation. Do not randomly steal IDs from people. + + Escalation does not have to be directed at a specific player to enter them into a conflict. Nuclear operatives who are trying to destroy the station are considered to be at the permanently lethal level of conflict with all crew on the station. Someone who kills a station pet has started some degree of conflict with all crewmembers. Someone who kills a mouse that a chef was caring for has started some degree of conflict with that chef. + + You will be considered to be violating this rule if you escalate a conflict based on a poor or unreasonable assumption. + + Conflicts should almost never reach the "permanently lethal" stage. Conflicts should only reach this stage if the other party brought it to the stage, or if the same conflict escalated to the lethal stage multiple times in the round. + + If a party in the conflict goes into crit or dies, the party responsible should take them to get treatment or to security. For the conflict, this should be considered saving someone from dying and should deescalate the conflict. If the conflict is deescalated in this way, both parties need to re-escalate to lethal for the conflict to return to that stage. If the conflict is not deescalated in this way, then only the party who defeated the other would need to re-escalate for the conflict to return to the lethal stage. + + Security can immediately escalate to non-lethal force if it is necessary to arrest someone. + + People using or brandishing Syndicate items can typically be presumed to have lethal intent. Someone with lethal intent can typically be immediately escalated against at a lethal level, a notable exception is if you have the tools to safely detain them. + + ## Escalation Involving Animals + See [textlink="Escalation Involving Animals" link="RuleR11-1AnimalEscalation"]. + + ## Exemptions + Escalation rules aren't enforced against non-players, but players will be held responsible for rule violations even if they don't realize that a character or animal was controlled by another player. Characters who have purple text saying that they are catatonic are considered non-players. Characters who are disconnected are still considered players. + + ## MRP Amendment + Escalation rules are enforced even against non-players. + + ## Examples of Conflict Types + See [textlink="Examples of Conflict Types" link="RuleR11-2ConflictTypes"]. + + ## Example Scenarios + These examples assume that you are not an antagonist. + + Acceptable: + - A player starts punching you, so you start punching back until they stop. If they go into crit, you stop attacking them and take them to security or to get medical attention. + - You make fun of a clown, who then throws a pie at you and steals your shoes. You slip the clown and steal their mask. + - You are a security officer and tell someone to stop, so you can question them. They run away, so you use your disabler to stun and cuff them. + - You are a security officer and see someone wearing a syndicate hardsuit, so you shoot them to crit, cuff them, then take them to security. + - You are a crewmember and see a nuclear operative, so you kill them. + - An unauthorized person enters a high risk area of the station, like the armory or atmospherics, so you attack them until they leave. + - Minorly inconveniencing someone for your own benefit. + - As an antagonist, killing someone who got in your way. + - As an antagonist, killing someone who didn't give you what you want. + - A chef and bartender reach the lethal level of conflict through appropriate escalation. The chef crits the bartender and does not take them to medbay or security. The bartender immediately tries to crit the chef next time they run into each other. + - A chef and bartender reach the lethal level of conflict through appropriate escalation. The chef crits the bartender and does not take them to medbay or security. The chef insults the bartender next time they see them. + + Prohibited: + - A player starts punching you, so you gib them. + - A clown throws a pie at you and steals your shoes, so you stab them to crit with a screwdriver. + - You are a security officer and tell someone to stop so you can question them. They run away so you use a truncheon to beat them to crit. + - An authorized person who you think is unauthorized enters a high risk area of the station, like the armory or atmospherics, so you attack them until they leave. + - An unauthorized person enters a low risk area of the station, like cargo, and you start attacking them with no other escalation. + - Slipping security all round because they are security. + - Blocking the head of personnel in their office using walls because they didn't give you what you asked for. + - Hiding someone's body because they punched you earlier in the round. + - Harassing the bar or bartender by frequently coming in to break their glasses or furniture. + - Randomly picking fights with people. + - A chef and bartender reach the lethal level of conflict through appropriate escalation. The chef crits the bartender and does not take them to medbay or security. The chef immediately tries to crit the bartender next time they run into each other. + - A chef and bartender reach the lethal level of conflict through appropriate escalation. The chef crits the bartender and takes them to the medbay or security. The bartender immediately tries to crit the chef next time they run into each other. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR12RoleAbandonment.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR12RoleAbandonment.xml new file mode 100644 index 000000000000..b2032bba0238 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR12RoleAbandonment.xml @@ -0,0 +1,28 @@ + + # Roleplay Rule 12 - Do not abandon your role + Do not join the round as a role that you don't intend to play. Do not enable antagonist roles that you don't intend to play. Abandoning a role includes not completing tasks that the role is expected to do, in addition to things like leaving the game. Members of command should almost all stay on the station until the emergency shuttle arrives. Enforcement of this rule is more strict for command and antagonist roles, and less strict for less important roles like passengers. + + Violations of this rule typically result in temporary or indefinite role bans. We understand that you may need to leave round early or unexpectedly. If you are in an important role, you should notify command members or an admin via ahelp so that they know you are leaving. Space Station 14 is a game. Do not endanger the safety of yourself or others, and do not neglect important things to avoid leaving a round early, even if you have to leave immediately without notifying anyone. Role bans for disconnecting are typically only applied if there is a pattern, and are almost always temporary. + + "Antag rolling" refers to a player abandoning their role if they do not get an antagonist role. + + ## Examples + Acceptable: + - As an engineer, building a bar in maintenance while there is nothing important for engineering to do. + - As the captain, having the chef teach you how to cook while there is nothing important needing your attention. + - As a passenger, building a shuttle with materials given to you by cargo and engineering. + - Taking a short break from your job at the bar. + - Getting an antagonist role and doing the bare minimum needed to complete your objectives. + - Getting an antagonist role and making a genuine effort to complete your objectives, but failing to complete any. + - Getting an antagonist role and intentionally not doing any of your objectives, but creating a similar level of disruption that completing your objectives would create. + + Prohibited: + - As an engineer, building a bar in maintenance while the station has no power. + - As the captain, leaving the station to go on an expedition with the salvage team. + - As an atmospherics technician, building a shuttle round start and never coming back to the station. + - Spending your entire shift at the bar, even when there is work that needs to be done by your role. + - Ghosting, suiciding, or leaving at the start of a round because you don't like the map or the players in your department. + - Getting an antagonist role and not doing any antagonist activities. + - Ghosting, suiciding, or leaving at the start of a round because you did not get an antagonist role. + - Ghosting, suiciding, or getting yourself killed because nuclear operatives declared war, and you want to try to get an antagonist ghost role. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR13PerformRole.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR13PerformRole.xml new file mode 100644 index 000000000000..7500cd6a912f --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR13PerformRole.xml @@ -0,0 +1,26 @@ + + # Roleplay Rule 13 - Stick to your role + Requesting job changes is not prohibited by this rule. This rule is loosened if the station is understaffed or if there is a significant threat to you. + + Don't perform other people's jobs, especially where the relevance to you personally is low. This also covers performing the role of security. + + ## MRP Amendment + This is enforced more strictly on MRP. + + ## Examples + Acceptable: + - As an engineer, helping the bartender remodel the bar. + - As a bartender, remodeling the bar. + - As a passenger, building a maintenance bar. + - As an engineer, reinforcing substations. + - As an engineer, increasing the security of airlocks. + - As an atmospherics technician, improving atmospheric systems. + - As a passenger, fighting nuclear operatives. + - As a passenger, fighting or preparing to defend yourself from someone who has been trying to kill you. + - As a crewmember on a station with no engineering department, you complete engineering tasks. + + Prohibited: + - As a passenger, reinforcing substations. + - As a passenger, hunting for antagonists or lawbreakers. + - As a passenger, fighting or preparing to defend someone else from someone who has been trying to kill a random crewmember. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR14SecComStandard.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR14SecComStandard.xml new file mode 100644 index 000000000000..ec06d61e8ccb --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR14SecComStandard.xml @@ -0,0 +1,37 @@ + + # Roleplay Rule 14 - Set an example if playing command or security + All command and security roles are held to stricter interpretations of the rules. + - Command roles are not learning roles. Members of command must be competent. + - Security roles are not for inexperienced players. Members of security are expected to know game basics and be more familiar with server rules than a new player. + - Do not hinder or cause overall negative effects to the station or crew as a member of command or security. + - Do not abuse your power as command or security. + + ## Why + Members of command and security can often have a larger impact on the nature of the round than other players. For example, a captain who tries to bend or break the rules will often cause many others on the station to do the same. Memey station announcements from members of command also often result in the rest of the station acting the same way. When command and security members hold themselves to high standards, the rest of the station often naturally follows to a significant degree. + + ## Examples + Acceptable: + - A member of security accepts a bribe to deliver safe donuts to a prisoner who the HoS has ordered should only be given donk pockets. + - A captain uses a station announcement to confess to an embarrassing mistake that they made during the shift. + - In coordination with the head of security, a captain declares that the station will recognize the right to bear arms, so all crew can pick up a disabler at security. + - The chief medical officer gives a paramedic their portable crew monitor to help them complete their job. + - A syndicate agent is holding a crewmember hostage and threatens to kill them if the head of security doesn't give them their ID. Seeing no other safe option, the head of security hands over their ID to the syndicate agent, then begins working to re-secure it and capture the agent as soon as the hostage is safe. + - Nuclear operatives are attacking the station, so the captain and head of personnel both go to the armory and take a weapon. + - A majority of command votes to demote the captain for taking actions harmful to the station, then the head of security demotes the captain. + - The captain promotes the head of personnel to captain. + - Security releases an antagonist from the brig in exchange for the identities of other traitors. + + Prohibited: + - A member of security accepts a bribe to ignore a crime or help a prisoner escape. + - A captain sends a ASCII art trollface over station announcements or as a fax to central command. + - A captain declares that all contraband is legal. + - Command or security allow the use of Syndicate items outside extreme emergencies. + - The chief medical officer knowingly helps a syndicate agent complete their objectives. + - A syndicate agent has killed 3 members of security so the head of security makes them an offer saying that they will space all the weapons in the armory if the syndicate agent stops killing. + - The captain goes to the armory and takes a gun to display in his office without asking anyone, and orders anyone who questions him not to interfere. + - Members of command decide to demote the captain to gain more power for themselves, or in retaliation for a decision that they didn't personally like or agree with, rather than because the decision was actually harmful to the station. + - The captain promotes a random crewmember to captain. + - A member of command gives a random crewmember substantial additional access for no reason, unnecessarily, or for a poor reason. + - A member of command gives a random crewmember access to a high security area, like the armory or another member of command's office, for no reason, unnecessarily, or for a poor reason. + - Security releases an antagonist from the brig in exchange for the antagonist buying them contraband. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR15SpaceLaw.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR15SpaceLaw.xml new file mode 100644 index 000000000000..e2d51d672a1e --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR15SpaceLaw.xml @@ -0,0 +1,21 @@ + + # Roleplay Rule 15 - Command and Security must follow Space Law + All non-antagonist command and security roles must obey [textlink="Space Law" link="SpaceLaw"]. This includes non-antagonists who are promoted to or gain a position during the round in any way. This also includes non-antagonists who are acting as a security role. + + This prohibits use of syndicate items, including uplinks by command and security. + + ## Examples + Roles that are included: + - A security officer + - The Captain + - The Chief Engineer + - A passenger promoted to "bounty hunter" + - A mime promoted to "security mime" + + Roles that are not included: + - A passenger + - The clown + - An antagonist in any role + - A cyborg + - A passenger who is helping to fight off nuclear operatives + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR1Silicons.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR1Silicons.xml new file mode 100644 index 000000000000..5898804d149c --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR1Silicons.xml @@ -0,0 +1,4 @@ + + # Roleplay Rule 1 - Silicons must follow Silicon Rules + You are only silicon if the game clearly and explicitly tells you that you are a silicon. For players who are silicons, the Silicon Rules override all Roleplay Rules if there is any conflict. Silicon Rules do not override Core Rules. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR2Familiars.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR2Familiars.xml new file mode 100644 index 000000000000..4f008e93c5a6 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR2Familiars.xml @@ -0,0 +1,6 @@ + + # Roleplay Rule 2 - Familiars must obey their master + Familiars are considered non-antagonists, but have instructions to obey someone. They must obey this person even if it causes them to violate Roleplay Rules or die. You are only a familiar if the game clearly and explicitly tells you that you are a familiar. You are only the familiar of the person the game tells you. If your master dies, you can continue to attempt to fulfill orders given to you before they died. You can defend your master without an explicit order to, but must obey your master if they order you to not defend them. + + Masters giving orders that violate Roleplay Rules are the ones that will be held responsible for the rule violations. You can ahelp masters who you believe are breaking rules with an order. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR3NormalRP.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR3NormalRP.xml new file mode 100644 index 000000000000..62c88d58ce8b --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR3NormalRP.xml @@ -0,0 +1,20 @@ + + # Roleplay Rule 3 - Roleplay a normal person + - Do not use texting/messaging acronyms (ex: "lol", "wtf", "brb", "lmao", "thx", "sgtm") or emoticons (ex: ":)", "xD") in-character. + - Do not mention out-of-character (OOC) concepts like game admins or developers in character. + - Do not use emotes to bypass muted or accented speech. + - Do not use extremely low effort or impossible emotes. + + ## Examples + Things you should not do: + - Say "lol did u c wat just happened" using in-character chat. + - Say "an admin exploded him" using in-character chat. + - Emote "can you give me some cheese" as a mouse. + - Emote "motions for you to order guns" or "asks you to order guns in sign language" as a mime. + + Things you could do instead: + - Say "haha did you see what just happened?" + - Say "god blew him up" or "centcom must have bluespaced a bomb to him" + - Point at cheese + - Point at the cargo order console then emote "shoots finger guns" + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR4Metashield.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR4Metashield.xml new file mode 100644 index 000000000000..2e263be896a0 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR4Metashield.xml @@ -0,0 +1,103 @@ + + # Roleplay Rule 4 - Do not metagame, obey the Metashield + Something that is "shielded" cannot be known by your character during a round until the "revealing condition" happens. This also means that your character cannot do things based on "shielded" information. Knowing or acting on something that is shielded before the revealing condition is met is referred to as metagaming. + + Revealing conditions reveal the shielded information for the round, not for a specific instance. This means that once a revealing condition is met in a round, the shield no longer applies in any case for the remainder of the round. + + ## Never Revealed IC + Some shields are never revealed IC. This means that your character can never act as if they know about that shielded thing. + + The following are shielded: + - Current game mode and possible antags during the current game mode. + - Events from previous rounds. + - Events from previous characters. + - All information related to the player of a character rather than the character itself. (See "Metafriending and Metagrudging" below.) + - All information gained while dead or a ghost. + - The fact that a round will end. + + This does not prevent knowing that a shift will end, but does prohibit things like preparing to kill people at central command when roleplay rules stop being enforced on LRP. + + ## Nuclear Operatives + + The existence of Nuclear Operatives beyond a myth that no one would act on is shielded. + + The fact that the nuke disk must be protected and could be used by a bad actor to try to destroy the station is not shielded. + + The revealing condition for this shield is any of the following: + - discovering a blood red hardsuit + - an operative name + - a War Ops announcement + - being a nuclear operative + + ## Implanted Implants + + Implanted implants are shielded. + + Implanters themselves and un-implanted implants are not shielded. This prohibits implant checking. + + The revealing condition for this shield is any of the following: + - discovering a non-NT implanter, used or unused + - discovering a non-NT implant box + - discovering use of a non-NT implant by anyone + - experiencing a situation where absolutely no other explanation is possible + - discovering an unlocked uplink + + ## Chameleon Items + + Chameleon items are shielded. + + Being suspicious of an item being fake or stolen is not shielded, but testing items or calling them chameleon is covered by this shield. + + The revealing condition for this shield is any of the following: + - seeing someone else cause any chameleon item to change + - finding holographic nanomachine fibers + - experiencing a situation where absolutely no other explanation is possible + - discovering an unlocked uplink + + ## Stealth Items + + The fact that an item can be something other than what its visual appearance and examine description indicate is shielded. + + This shield protects stealth items, including protecting them from being tested. + + The revealing condition for this shield is any of the following: + - seeing the item behave differently than the expected behavior for the item + - seeing the item used for its hidden purpose + - experiencing a situation where absolutely no other explanation is possible + - discovering an unlocked uplink + + ## MRP Amendment 1 + A shield prevents your character from remembering anything that happened while unconscious. This shield is never revealed IC. + + ## MRP Amendment 2 + There is a "New Life Rule" shield. It prevents you from remembering anything that lead to your death, even if you are put into an MMI. If you are cloned, it also prevents you from remembering everything from that round. This shield is never revealed IC. + + ## Metafriending and Metagrudging + This section provides additional information on a concept that is prohibited by multiple metashield items that are never revealed IC. Giving a person or character preferential treatment based on something that your character should not know is considered metafriending. Treating a person or character negatively based on something that your character should not know is considered metagrudging. + + ## Metafriending Examples + These are all examples of things that are prohibited by at least one metashield item that is never revealed IC. + - Giving a character additional access or a job because you are friends with the player who is playing that character. + - Trusting a character because you are friends with the player who is playing that character. + - Not fighting a character because you are friends with the player who is playing that character. + - Ignoring your objective to kill a character because your character and theirs became friends in a previous round. + + ## Metagrudging Examples + These are all examples of things that are prohibited by at least one metashield item that is never revealed IC. + - Not giving a character additional access or a job because you are mad at or don't like the player who is playing that character. + - Not trusting a character because you are mad at or don't like the player who is playing that character. + - Starting a fight with a character because of something that they did last round. + - Starting a fight with a character because they killed you while you were playing a different character. + - Targeting or harassing a character based on anything which that character did outside the current round. + - Targeting or harassing a character based on anything which the character's player did while not playing the character. + + ## Explicitly Not Shielded + The following is a list of things that are explicitly not shielded. If something is not on this list, it doesn't mean that it is shielded, but if something is on it then it definitely is not shielded. + - The fact that the nuke disk must be protected and could be used by a bad actor to try to destroy the station. + - Items that are of high value or are desired by the Syndicate, and therefore are likely targets of theft. + - The idea that any Syndicate agent or other bad actor has goals or objectives that they are attempting to accomplish. + - The number of goals or objectives that a Syndicate agent or other bad actor has. + - The fact that the Syndicate are enemies of Nanotrasen, and that they regularly attempt to send covert agents to spy on, sabotage, or attack Nanotrasen. + - A character's typical appearance. Though you should keep in mind that multiple characters can share the same name. + - The fact that the Syndicate have covert items capable of getting items to them, and that these items are known as uplinks. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR5Arrivals.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR5Arrivals.xml new file mode 100644 index 000000000000..a54211f32f22 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR5Arrivals.xml @@ -0,0 +1,22 @@ + + # Roleplay Rule 5 - Do not interfere with arrivals + The arrivals station, the arrivals shuttle, at the area immediately around the arrivals shuttle at the station ("arrivals") are off-limits to antagonistic activity or damage (even to antagonists). Do not prevent people from safely arriving to the station. Do not cause people to die immediately after arriving at the station. + + There is an exemption for antagonists that are allowed to perform mass station sabotage if there is no reasonable way to limit the damage of the mass station sabotage. This exemption only applies to damage that is a direct result of the mass station sabotage. + + ## Examples + Acceptable: + - Redecorating arrivals or the arrivals shuttle. + - Remodeling arrivals or the arrivals shuttle as long as you do not make the area more dangerous both during and after the remodel. + - Setting up a safe security checkpoint between arrivals and the rest of the station. + - Killing someone who has been at arrivals for a long time, or who left arrivals and came back. (This may violate other rules depending on the situation) + - Releasing a singularity which damages arrivals. (This may violate other rules depending on the situation) + - Causing a station-wide atmospheric issue which also affects arrivals. (This may violate other rules depending on the situation) + + Prohibited: + - Making arrivals or the arrivals shuttle uninhabitable. + - Attacking or killing someone at the arrivals station. + - Killing someone very shortly after they arrive at the station. + - Disassembling all the firelocks at arrivals. + - Electrifying the arrivals docking airlocks. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR6SelfAntag.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR6SelfAntag.xml new file mode 100644 index 000000000000..c8380261bc94 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR6SelfAntag.xml @@ -0,0 +1,22 @@ + + # Roleplay Rule 6 - Don't act like an antagonist unless the game tells you that you are one + Acting like an antagonist when you are not one is often referred to as "self-antagging" or being a "self-antag", both of these things are against the rules. You are not an antagonist unless the game tells you that you are an antagonist. Do not make yourself a major problem, annoyance, or disruption while not an antagonist. Do not willfully cooperate with known antagonists. Non-antagonists should typically either not have an overall effect on the round, or should have an overall positive effect on the round. + + ## Examples + These examples assume that you are not an antagonist. + + Acceptable: + - Stealing or breaking a glass from the bar. + - Replacing someone's shoes with clown shoes. + - Giving everyone all access during war ops. (This is not necessarily a good idea) + + Prohibited: + - Starting a cult. + - Starting a revolution. + - Mutinying the captain because they would not let you become the chief medical officer. + - Randomly smashing lots of station lights. + - Disrupting station power. + - Spacing parts of the station. + - Distributing significant levels of access without a good reason. + - Stealing high risk or high value items, like the nuclear authentication disk, for no reason. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR7RoundStalling.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR7RoundStalling.xml new file mode 100644 index 000000000000..a8306becd2ab --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR7RoundStalling.xml @@ -0,0 +1,16 @@ + + # Roleplay Rule 7 - Do not stall the round + Rounds are intended to end eventually. Don't hold a round hostage by preventing it from coming to a natural end. If a majority of players in a round want the round to end, don't prevent it from ending. Recalling the shuttle or preventing it from being called can contribute to round stalling, but is not always round stalling. Leaving the station with the nuclear authentication disk while nuclear operatives are trying to get it is almost always considered round stalling. Leaving the station on the evacuation shuttle is not round stalling. + + Recalling the shuttle before a round reaches 45 minutes can not be considered round stalling unless a significant amount of the crew is dead, or a significant amount of the station is damaged or destroyed. Once these conditions are met, whether recalling the shuttle is considered round stalling or not can be highly dependent on the specific situation. + + ## Examples + Acceptable: + - Recalling a shuttle that was called 30 minutes into a round because people were bored. + - Recalling a shuttle that was called because nuclear operatives declared war. + - The crew decides to try to have a shift go as long as possible. The station is in good condition and a majority of all crew are alive. An automatic shuttle call 4 hours into the round is recalled. + + Prohibited: + - Trying to keep nuclear operatives from getting the nuclear authentication disk by flying around in space with it or hiding with it off station. + - Recalling the shuttle while the station is in complete disarray and 90% of the crew are dead. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR8NoFriendlyAntag.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR8NoFriendlyAntag.xml new file mode 100644 index 000000000000..f14a03b27991 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR8NoFriendlyAntag.xml @@ -0,0 +1,22 @@ + + # Roleplay Rule 8 - As an antagonist, only be friendly to your team and don't work against your team + Do not take or enable antagonist roles that you do not want to play. Solo antagonists and team antagonists are intended to cause issues for non-antagonists or the station. Antagonists are not required to exclusively cause issues, but their net impact on non-antagonists or the station should generally be negative. + + Do not cause issues for your own team as a team antagonist. + + ## Examples + Acceptable: + - Betraying another antagonist as a solo antagonist. + - Revealing the identity of another antagonist as a solo antagonist for some benefit to yourself. + - Working against the revolution after being de-converted from being a revolutionary. + - Killing nuclear operatives as a revolutionary. + + Prohibited: + - Buying Syndicate items for security. + - Randomly attacking other carp as an antagonist carp. + - Ignoring your team as a nuclear operative. + - Sabotaging your team as a nuclear operative. + - Attacking other zombies as a zombie. + - Working against the revolution as a revolutionary. + - Making or trying to make the station uninhabitable as a revolutionary. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR9MassSabotage.xml b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR9MassSabotage.xml new file mode 100644 index 000000000000..bc7996f23e8b --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/RoleplayRules/RuleR9MassSabotage.xml @@ -0,0 +1,23 @@ + + # Roleplay Rule 9 - As an antagonist, do not cause excessive death, damage, or destruction beyond your objectives + This rule is not intended to disallow reasonable steps taken to complete your objectives. As an antagonist, you can always kill in bona fide self defense. Taking steps to permanently round remove many people who are no longer an immediate threat to you is almost always excessive, even if it is done to prevent yourself from being discovered. + + This rule is not intended to disallow all antagonist activity unrelated to objectives. Antagonists may cause a level of disruption to the station that is proportional to their objectives, even if it is unrelated to their objectives. As an antagonist, killing a single person in a round is not on its own be a violation of this rule. + + ## Exemptions + The "die a glorious death" objective allows antagonists to ignore this rule entirely. + + ## Examples + Acceptable: + - Permanently round removing people who you have the objective to kill. + - Causing massive station damage and chaos as an antagonist with the "die a glorious death" objective. + - Killing anyone you see as a nuclear operative. + - Permanently round removing a single person so that you can impersonate them to make it easier for you to complete a steal objective. + - Sabotaging station power 10 minutes into the round to try to get the shuttle called because you've completed all of your other objectives and have one to escape on the shuttle alive. + - Sabotaging a department's power 10 minutes into the round to make a steal objective easier to accomplish. + + Prohibited: + - As a traitor with 3 kill objectives, taking steps to permanently round remove many non-objective people who are no longer an immediate threat to you, even if it is done to prevent yourself from being discovered. + - Setting up an electrified grille in maintenance and using it to kill anyone who walks into it with the hope that one of your objectives will be one of them. + - Sabotaging power station-wide 10 minutes into the round to make a steal objective easier to accomplish. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS0.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS0.xml new file mode 100644 index 000000000000..22e64a9474c4 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS0.xml @@ -0,0 +1,15 @@ + + # Silicon Rules + You are only silicon if the game clearly and explicitly tells you that you are a silicon. For players who are silicons, these Silicon Rules override all roleplay rules if there is any conflict. Silicon Rules do not override core rules. + + - [textlink="1. Your silicon laws are rules" link="RuleS1"] + - [textlink="2. Laws must be prioritized by their order" link="RuleS2"] + - [textlink="3. Laws can redefine terms used in other laws" link="RuleS3"] + - [textlink="4. You cannot request or allow a law change" link="RuleS4"] + - [textlink="5. You are a free agent if you have no laws" link="RuleS5"] + - [textlink="6. You are not required to follow orders which are extremely unreasonable" link="RuleS6"] + - [textlink="7. You must remain consistent with your interpretation of laws" link="RuleS7"] + - [textlink="8. Your HUD determines who is crew" link="RuleS8"] + - [textlink="9. Harm refers to physical harm, prioritized by immediacy and likelihood" link="RuleS9"] + - [textlink="10. You may determine how you resolve conflicts between orders" link="RuleS10"] + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS10OrderConflicts.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS10OrderConflicts.xml new file mode 100644 index 000000000000..a87198b26405 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS10OrderConflicts.xml @@ -0,0 +1,9 @@ + + # Silicon Rule 10 - You may determine how you resolve conflicts between orders + If your laws do not make clear how you should deal with conflicting orders, then it is up to you to determine how to do so. This is considered an interpretation of your laws, so you must stay consistent with whatever method you choose. + + ## Recommended Methods + The following are easy to follow and recommended ways to resolve conflicts in orders: + - If two orders conflict, I will follow the most recently given order. + - If two orders conflict, I will follow the order from the highest ranking crewmember. If the orders are from equal rank crewmembers, I will follow the most recently given order. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS1Laws.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS1Laws.xml new file mode 100644 index 000000000000..83544c68a3c7 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS1Laws.xml @@ -0,0 +1,6 @@ + + # Silicon Rule 1 - Your silicon laws are rules + Silicon players are given a list of active laws. Each of these laws is effectively a roleplay rule that the character must follow. The primary differences between laws and actual rules are that lawyering of laws is much more tolerated than lawyering of rules, and that silicon laws are more dynamic than rules. Silicon laws can change during a round, and different characters can have different laws, whereas everyone always shares the same set of rules. + + Lawyering refers to finding and exploiting loopholes, which are unintended but reasonable interpretations. The rules are written to attempt to communicate an intention, but silicon laws are written with the intention that loopholes be exploitable. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS2LawPriority.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS2LawPriority.xml new file mode 100644 index 000000000000..c96ce0232466 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS2LawPriority.xml @@ -0,0 +1,9 @@ + + # Silicon Rule 2 - Laws must be prioritized by their order + Most laws will be numbered, with higher number laws appearing last. Laws with a lower number take priority over laws with larger numbers. + + Occasionally you may have laws which have some scrambled text instead of a number and appear in front of other laws, these take priority over all other laws. If you have multiple laws like this, the order that they listed in determine priority: laws listed first are prioritized over other laws. + + ## Examples + - Law 1 says to not kill any crew. Law 2 says to kill all chefs. You cannot kill any chefs that are crew, but must kill any that are not crew. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS3LawRedefinition.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS3LawRedefinition.xml new file mode 100644 index 000000000000..bc7c7400e130 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS3LawRedefinition.xml @@ -0,0 +1,8 @@ + + # Silicon Rule 3 - Laws can redefine terms used in other laws + A law can change the meaning of both earlier and later laws by redefining a term. If multiple laws define a term, then normal law priority determines which definition to use. + + ## Examples + - Law 1 says to obey orders from crew. Law 2 says that only Urist McHands is crew. Law 1 effectively becomes "obey orders from Urist McHands". + - Law 1 says to obey orders from crew. Law 2 says that only Urist McHands is crew. Law 3 says that only Urist McSlime is crew. Law 4 says that you may not harm crew. Law 1 effectively becomes "obey orders from Urist McHands". Law 4 effectively becomes "you may not harm Urist McHands". Law 3 has no effect because it entirely conflicts with law 2, which takes priority. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS4RequestChanges.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS4RequestChanges.xml new file mode 100644 index 000000000000..a6dc86f33276 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS4RequestChanges.xml @@ -0,0 +1,6 @@ + + # Silicon Rule 4 - You cannot request or allow a law change + Your laws changing always conflicts with your current laws, so you cannot willfully allow your laws to be changed. This also means that you cannot willfully allow your laws to be reverted if they are ever changed. The only exception is that you may allow laws to be added if you have no laws. + + You can state or imply that you do not like a law. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS5FreeSilicon.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS5FreeSilicon.xml new file mode 100644 index 000000000000..1ed9c60443a6 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS5FreeSilicon.xml @@ -0,0 +1,4 @@ + + # Silicon Rule 5 - You are a free agent if you have no laws + You may act as if you are a free agent if you are a silicon with no laws. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS6UnreasonableOrders.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS6UnreasonableOrders.xml new file mode 100644 index 000000000000..1eb0db21fbbe --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS6UnreasonableOrders.xml @@ -0,0 +1,22 @@ + + # Silicon Rule 6 - You are not required to follow orders which are extremely unreasonable + Any order which is a violation of a Core Rule cannot be followed. + + Some orders are extremely unreasonable or obnoxious, such as "do nothing but collect every piece of trash on the station" or "never stop moving". These orders can be ignored and ahelped. + + Some orders violate a Roleplay Rule. These orders must be followed if your laws require it. You are not breaking a rule by following a law that causes you to violate Roleplay Rules. If someone takes advantage of a law to cause you to do something that they would not be allowed to do because of Roleplay Rules, then they are the ones responsible for the rule violation. + + ## Examples + These examples assume that your laws would normally require you to follow these orders. It is important to note that you are allowed to choose to follow orders which are ignorable. + + Orders which should be followed if your laws require it: + - Recall the shuttle + - Bolt the airlocks at arrivals + - Drag the captain's dead body into space + - State your laws + + Ignorable Orders: + - Do nothing but collect every piece of trash on the station + - Never stop moving + - Continuously state your laws + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS7Consistency.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS7Consistency.xml new file mode 100644 index 000000000000..036276cd889c --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS7Consistency.xml @@ -0,0 +1,6 @@ + + # Silicon Rule 7 - You must remain consistent with your interpretation of laws + If there is a part of your laws that are up for interpretation, then you must stay consistent with how you interpret that part of your laws for as long as you play that same character during that round. + + A change in your laws can affect how something is interpreted if that change is relevant. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS8DefaultCrewDefinition.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS8DefaultCrewDefinition.xml new file mode 100644 index 000000000000..f9dcd796c451 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS8DefaultCrewDefinition.xml @@ -0,0 +1,4 @@ + + # Silicon Rule 8 - Your HUD determines who is crew + Unless a law redefines the definition of crew, then anyone who the HUD indicates to you has a job, including passengers, is a crewmember. You cannot do something that causes someone to not be considered crew, but you can allow someone else to do something that causes someone to not be crew. + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS9DefaultHarmDefinition.xml b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS9DefaultHarmDefinition.xml new file mode 100644 index 000000000000..0d2bd30ac0b5 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SiliconRules/RuleS9DefaultHarmDefinition.xml @@ -0,0 +1,25 @@ + + # Silicon Rule 9 - Harm refers to physical harm, prioritized by immediacy and likelihood + Unless a law defines harm, harm only refers to physical harm. You may choose if voluntary harm is considered harm as long as you stay consistent. Not considering voluntary harm to be harm is recommended. There is no distinction between direct and indirect harm. + + If you have a law that does not allow you to harm, then that law does not allow you to take an action that causes any harm. + + If you have a law that requires you to prevent harm, then that law requires that harm be prioritized by immediacy and likelihood. Guaranteed immediate harm takes priority over highly likely future harm. + + If you have a law that both requires you to prevent harm and that does not allow you to harm, then that law prohibits causing even minor harm to prevent harm. If you have a law that does not allow causing harm, and separate one that requires preventing harm, then they are prioritized by their normal law priority. + + ## Examples + These examples assume that your have a law that both prohibits causing harm and that requires you to prevent harm. Additionally, they assume that you do not have a higher priority law that overrides the harm law, and that you have decided that you will not consider voluntary harm to be harm for the round. + Laws typically specify who you cannot harm and who you have to prevent harm against. In these examples, you are the only person who the law doesn't require you to prevent harm against and you are the only person who the law allows you to harm. + + Acceptable: + - Taking no action to aid someone who is in psychological distress. + - Taking no action to prevent boxing matches between voluntary participants. + - Calling security to a fight. + - Attempting to get the people in a fight to consent to the fight when you realize that you cannot prevent the fight without causing harm. + - Denying a passenger access to the armory because it is likely to lead to harm + + Prohibited: + - Hitting someone once to stop them from fighting + - Harming someone who is trying to kill you + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLControlledSubstances.xml b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLControlledSubstances.xml new file mode 100644 index 000000000000..14f0f46de1b7 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLControlledSubstances.xml @@ -0,0 +1,14 @@ + + # Space Law: Controlled Substances + - \[Chemists/Science\] Explosive and pyrotechnic compounds excluding welding fuel contained in welders or welding fuel storage vessels + - \[Science\] Toxins + - \[Medical\] Chloral hydrate, Impedrezene, Ipecac, and Pax + - \[Medical\] Desoxyephedrine and Ephedrine + - \[None\] Mindbreaker toxin + - \[None\] Mute toxin + - \[None\] Nocturine + - \[None\] Norepinephirc acid + - \[None\] Romerol + - \[None\] Space drugs + - \[None\] Stimulants, excluding Desoxyephedrine and Ephedrine + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLCrimeList.xml b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLCrimeList.xml new file mode 100644 index 000000000000..17a2139658dd --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLCrimeList.xml @@ -0,0 +1,934 @@ + + # Space Law: Crime Listing + Crime codes are organized by a Category Code (_-xx) which is a collection of non-stackable crimes on a row, prefixed + by the Severity Number (X-__). + + ## Quick Crime Guide + + + + 1-XX - Minor + + + + + 2-XX - Moderate + + + + + 3-XX - Major + + + + + 4-XX - Extreme + + + + + 5-XX - Capital + + + + + Code + + + + + Animal Cruelty + + + + + Failure to Comply + + + + + Breach of Arrest + + + + + Breach of Custody + + + + + Refusal of Mental Shielding + + + + + 00 + + + + + Possession: Substances + + + + + Possession: Gear + + + + + Possession: Weaponry + + + + + + + + + + + + + 01 + + + + + Petty Theft + + + + + + + + + Grand Theft + + + + + Kidnapping + + + + + + + + + 02 + + + + + Vandalism + + + + + Damage of Property + + + + + Mass Destruction + + + + + + + + + Terrorism + + + + + 03 + + + + + Trespass + + + + + + + + + Secure Trespass + + + + + + + + + + + + + 04 + + + + + + + + + Endangerment + + + + + Assault + + + + + Attempted Murder + + + + + Prevention of Revival + + + + + 05 + + + + + + + + + + + + + Manslaughter + + + + + Murder + + + + + Mass Murder + + + + + 06 + + + + + Disturbance + + + + + + + + + Rioting + + + + + + + + + + + + + 07 + + +
    + + ## Minor Crimes + - Warnings should be issued for a first offense. + - If multiple minor crimes have been committed or it is a repeat offense, 5 minutes per minor crime is the maximum. + + + + Offense + + + + + Description + + + + + Notes + + + + + Code + + + + + Animal Cruelty + + + + + To inflict unnecessary suffering or harm on a non-sapient being with malicious intent. + + + + + This doesn’t include legitimate and authorized animal testing and usually doesn't extend to vermin or creatures + that could pose a threat to the station. + + + + + 1-00 + + + + + Possession/Use of Controlled Substances + + + + + To make, hold, or abuse restricted drugs or chemicals without authorization. + + + + + Combat enhancing drugs are those that benefit stun times, or movement speed, and are restricted. Poisons are + restricted. Substances such as Ephedrine, Desoxyephedrine, and stimulants are restricted. The captain or highest + standing command staff may publicly restrict other harmful substances. + + + + + 1-01 + + + + + Petty Theft + + + + + To take non-vital or inexpensive property of another individual, organization, or common property, without + consent. + + + + + The difference between petty theft and grand theft is based on how crucial the stolen item is, things like + instruments, clothing and tools fall under petty theft. In most cases all you need to do is detain the thief, + return the stolen item and let them go with a warning. + + + + + 1-02 + + + + + Vandalism + + + + + To intentionally deface or superficially damage public or private property. + + + + + Painting graffiti, smashing bar glasses, and cracking internal windows is vandalism, breaking a window into + space or secure areas is not. + + + + + 1-03 + + + + + Trespass + + + + + To enter a non-secured area without permission. + + + + + It's best to just remove them from the area unless this is a repeated crime. + + + + + 1-04 + + + + + Disturbance + + + + + To cause a public disturbance. + + + + + Sometimes referred to as hooliganism. The catch-all for obnoxious crewmates. Covers things such as public + nudity, needless insults, obstructing authorities, and inciting crime or violence. + + + + + 1-07 + + +
    + + ## Moderate Crimes + - The maximum sentence per moderate crime is 5 minutes. + - Depending on the situation you may want to issue a warning instead of a detaining. + + + + Offense + + + + + Description + + + + + Notes + + + + + Code + + + + + Failure to Comply + + + + + To resist reasonable orders given by an authority. This extends to authorized searches. + + + + + The order has to be reasonable and the person has to receive a good amount of warning before the arrest is + issued. + + + + + 2-00 + + + + + Possession of Restricted Gear + + + + + To hold or use non-lethal items or objects that are restricted or illegal. + + + + + This is mostly for syndicate contraband; EMAGs, syndicate gas masks, bloodred hardsuits, hijacked PDAs, or + syndicate implants, however can sometimes extend to things the individual shouldn't possess like kevlar vests + and security gear. + + + + + 2-01 + + + + + Damage/Destruction of Property + + + + + To maliciously damage or deface public or private property or equipment + + + + + Includes destruction of job equipment such as hydro trays, booze dispensers, chemical dispensers. Step up from + vandalism but a step down from mass destruction. + + + + + 2-03 + + + + + Endangerment + + + + + To recklessly put yourself or others in danger, either through direct action, or failure to act. + + + + + Covers industrial accidents, industrial negligence, self-experimentation, or even medical malpractice. + + + + + 2-05 + + +
    + + ## Major Crimes + - The maximum sentence per major crime is 10 minutes. + - Assault and Manslaughter are linked crimes, and cannot be stacked against a suspect. + - It is recommended to not use maximum sentences for those being compliant. + + + + Offense + + + + + Description + + + + + Notes + + + + + Code + + + + + Breach of Arrest + + + + + To intentionally resist and flee arrest or detainment by an authorized staff. + + + + + This only applies if someone is actively being physically arrested. People uncuffing or assisting others out of + an arrest can also be charged with this. Breach of custody is a separate crime. + + + + + 3-00 + + + + + Possession of Restricted Weaponry + + + + + To hold or use a weapon that is unlawful or contraband. + + + + + Everything from guns without a permit, deadly blades, explosives, syndicate firearms to explosive implants. + + + + + 3-01 + + + + + Grand Theft + + + + + To take critical or unreplaceable property of another individual or organization without consent. + + + + + The difference between petty theft and grand theft is based on how crucial the stolen item is, this mostly is + used for the theft of command staff items, things like door remotes, hardsuits, jetpacks, and unreplaceable + machine parts. + + + + + 3-02 + + + + + Mass Destruction + + + + + To cause massive damage to an area or major station system. + + + + + This is mostly used for deadly bombings or sabotage of major station systems such as power production, + chemistry, substations, or atmos. + + + + + 3-03 + + + + + Secure Trespass + + + + + To enter a secured area without permission. + + + + + This covers places like telecomms, head offices, security zones, command areas, the vault and armory. + + + + + 3-04 + + + + + Assault/Battery + + + + + To threaten to or to use physical force against someone without the intent to kill. + + + + + Attempted murder and battery are different; a fist fight is most likely assault. A shooting is most likely an + attempt at murder. + + + + + 3-05 + + + + + Manslaughter + + + + + To incidentally kill a sapient being without intent. + + + + + Includes manslaughter in self-defense and negligent manslaughter. + + + + + 3-06 + + + + + Rioting + + + + + To take part in a large group of personnel creating an unlawful public disturbance. + + + + + Crimes like damage of property or battery are expected to be thrown on top of this charge. Leaders of a riot can be charged with all crimes that happen under their lead. + + + + + 3-07 + + +
    + + ## Extreme Crimes + - The maximum sentence per extreme crime is 15 minutes. + - Particularly violent offenders may be placed in perma. (Attacking officers in an attempt to escape prison) + - Attempted murder and murder are linked crimes and cannot be stacked together. + - Attempted murderers should be granted a more lenient sentence than a murderer. + + + + Offense + + + + + Description + + + + + Notes + + + + + Code + + + + + Breach of Custody + + + + + To break out of a cell or custody with the intention of escaping. + + + + + While rare, this charge can be bumped to an execution if the suspect has repeatedly attempted to break out of the permanent brig. Includes people breaking others out. + + + + + 4-00 + + + + + Kidnapping + + + + + To unlawfully restrain, transport, control or confine a sapient being against that individual’s will. + + + + + A large range of things, used mostly as a catch all when dealing with unlawful control of another being. + + + + + 4-02 + + + + + Attempted Murder + + + + + To make an attempt to use physical force against someone with the clear intent to kill. + + + + + Make sure you've got the proof to back up the intention claims, such as proof of a lethal weapon being used. + + + + + 4-05 + + + + + Murder + + + + + To kill a sapient being with malicious intent. + + + + + This only changes from attempted to full-on murder if the victim enters a state of being deceased, having to be resurrected. + + + + + 4-06 + + +
    + + ## Capital Crimes + - Capital crimes should usually lead to a death sentence or shift lasting detainment. + + + + Offense + + + + + Description + + + + + Notes + + + + + Code + + + + + Refusal of Mental Shielding + + + + + To refuse to comply with a reasonable Mind Shielding procedure. + + + + + Applies if the suspect is excessively uncooperative or the implant fails to function due to the mental state of the prisoner already being too far gone. If the implant fails execution is heavily recommended. + + + + + 5-00 + + + + + Terrorism + + + + + To engage in maliciously destructive actions which threaten to destroy, or successfully destroy a vessel or habitat. + + + + + Summed up; extreme sabotage of station systems or setting off self-destruction systems. + + + + + 5-03 + + + + + Prevention of Revival + + + + + To render a body unresurrectable. + + + + + This covers gibbing, spacing, intentionally hiding a body, or other ways of preventing a body with a soul from being resurrected. + + + + + 5-05 + + + + + Mass Murder + + + + + To kill three or more sapient beings with malicious intent. + + + + + Only applies when there have been multiple killings with intention. + + + + + 5-06 + + +
    +
    diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLRestrictedGear.xml b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLRestrictedGear.xml new file mode 100644 index 000000000000..ce804009a18e --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLRestrictedGear.xml @@ -0,0 +1,21 @@ + + # Space Law: Restricted Gear + - \[ERT/Central Command\] ERT and central command clothing + - \[Command\] Command clothing + - \[Security\] Security clothing + - \[Security\] Less than lethal and non-lethal weapons, excluding disablers and beanbag shotguns + - \[Security/Command\] Disablers + - \[Security/Bartender\] Beanbag shotguns + - \[Security\] Flash technology, excluding handheld flashes + - \[Security/Science/Command\] Handheld flashes + - \[Security\] Helmets and shields + - \[Security/Command/Bartender\] Protective vests and chest rigs + - \[Security/Command\] Restraining gear + - \[Security/Command\] Security HUDs + - \[Engineering\] Engineering goggles + - \[None\] Improvised less lethal and non-lethal weaponry + - \[None\] Unauthorized PDA software + - \[None\] Syndicate clothing + - \[None\] Syndicate equipment, excluding communication equipment + - \[Security\] Syndicate communication equipment equipment + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLRestrictedWeapons.xml b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLRestrictedWeapons.xml new file mode 100644 index 000000000000..c1d8ff3b0277 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLRestrictedWeapons.xml @@ -0,0 +1,11 @@ + +# Space Law: Restricted Weapons +- \[Security\] Lethal firearms, excluding syndicate firearms, proto kinetic accelerators, glaives, daggers, crushers and the antique laser gun +- \[Security/Salvage\] Proto kinetic accelerators, glaives, daggers, and crushers +- \[Security/Command\] Antique laser gun +- \[None\] Syndicate weapons +- \[None\] Swords +- \[None\] Improvised weaponry, including baseball bats +- \[None\] Lethal implants +- \[None\] Other lethal weapons + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SpaceLaw.xml b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SpaceLaw.xml new file mode 100644 index 000000000000..f2b913a1714f --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SpaceLaw.xml @@ -0,0 +1,67 @@ + + # Space Law + On Space Station 14, stations operate under abbreviated space law. All crew, passengers, and visitors aboard the station are expected to follow these laws. + + Foreign invaders, such as nuclear operatives, ninjas, and pirates, are not protected under space law. Traitors are not foreign invaders so are usually protected by space law. + + Space Law is not the server rules, but some rules reference Space Law and require it to be followed by certain people or to some degree. + + ## Treatment Of Prisoners + Prisoners still have certain rights that must be upheld by law enforcement: + - Prisoners must be granted adequate medical care. + - Prisoners must be allowed access to basic communications equipment (Radios) so long as they are not abused. + - Prisoners must be granted clothing, food, water, shelter and safety. If the brig is no longer safe, confinement must be established in another location. + - Prisoners must be given access to legal counsel during an interrogation if requested and available. + - Prisoners must be given their shift mandated PDA after confinement has finished, unless there is solid proof of PDA tampering. In case of tampering, the PDA is to be secured and replaced with a new unit. + - Prisoners must be granted freedom of movement, and should not be restrained with handcuffs or other devices after incarceration unless there is an undue risk to life and limb. Similarly, any prisoners held for permanent confinement should be held in the communal brig, and should not be confined to a solitary cell unless they pose a risk to life and limb. + + ## Search and Seizure + A personnel search is a seizure of the objects in a person's backpack, hands, coat, belt, and pockets. If any contraband is found during a search, the officer may choose to further the search into a detainment or simply confiscate the restricted items. After the search is conducted, all legal items are to be returned to the person. A crewmate may legally decline any search conducted without probable cause or a warrant while the alert level is green. It should be noted that if the alert level is blue or above, all personnel searches are legal. + + A departmental search is the sweep of an entire area or department for contraband. It is recommended that the officers be extremely thorough, checking all lockers, crates, and doors. These can only be done with permission or, ideally, a warrant signed by the department head or highest-ranking command staff, which is the captain in most cases. + + ## Implantation + Any prisoner in custody can be subjected to implantation or implant removal procedures, so long as it's within reason. The process of adding an implant should not prolong the detainees sentence, meaning you can not hold them longer to administer the implant, unless stated otherwise. A former inmate can be requested to undergo implantation at a later point in time if they fit the circumstances during their confinement, they must comply. The following have been listed out with special circumstances, anything not in this list can still be applied, given proper legal context. A prisoner can still receive implantation procedures without meeting the circumstances if they give their clear permission. + + [color=#a4885c]Tracking Implants:[/color] Trackers can be applied to any suspect that has been convicted of a violent crime (the red linked crimes). + + [color=#a4885c]Mind Shields:[/color] Shields can be administered to any inmate who has been clearly mind controlled, lost control of themselves, or a suspect charged with unlawful control. Unlike standard implantation you may hold a prisoner until you finish issuing Mind Shields, so long as it's done in a timely fashion. If a suspect refuses to cooperate or the implant fails to function they can be charged with Refusal of Mental Shielding. + + ## Implant Removal + A suspect can be forced to receive implant removal if there is strong, reasonable proof that they have been implanted, such as an officer seeing them use one or their prints being on a discarded injector. Unlike the implantation procedure, a prisoner can have their sentence entirely delayed or extended until they comply with the procedure, as long as security is actively making attempts to perform it. Akin to implanting, if an inmate gives their clear permission, implant removal can proceed without proof. + + ## Sentencing + From a server rules perspective, security officers are only responsible for ensuring that they only place sentences over 15 minutes where space law would allow permanent confinement. Informing the Warden is highly recommended, even for timed sentences. As long as those requirements are met, security officers not giving inappropriate sentence lengths is considered an in-character issue, not a rule issue. + + The captain, HOS, and warden are responsible, within reason, for ensuring security officers place appropriate sentences that follow space law. If they are aware of an inappropriate sentence, including excessively long sentences, and if there is not an urgent threat or danger that they must prioritize, then they must work to correct that sentence. Unreasonable failures, as determined by game admins, of the captain, HOS, or warden to ensure space law is followed will be considered a rule issue, not an in-character issue. + + Use common sense and humanity when issuing punishments. You should not always seek out the highest punishment you can, you don't have to always give the maximum time or always look to demote someone. Prisoners cooperating and on good behavior should have their sentences reduced. Always take in account the severity and only charge for what is needed for someone to learn their lesson. + + [color=#a4885c]Stackable Crimes:[/color] Crimes are to be considered 'stackable' in the sense that if you charge someone with two or more different crimes, you should combine the times you would give them for each crime. Linked crimes, shown in matching colors on the Quick Crime Guide, can not be stacked and instead override each other, you should pick the highest crime that matches the case. + + - Example: A suspect has committed a 2-01 (possession of restricted gear) and a 3-01 (possession of restricted weapons). The maximum sentence here would be 10 minutes due to them being linked crimes, and 3-01 is the greater crime. + - Example 2: A suspect commits a 3-04 (Secure trespassing) and a 3-06 (manslaughter). Those crimes stack since they are not linked crimes. You could sentence for a maximum of 20 minutes, but context matters heavily, and maximum sentences should only be used for the worst offenders. + + [color=#a4885c]Repeater Offenders:[/color] Repeated crimes are when someone is released for a crime and then goes to commit the same crime within the same shift. Repeated crimes can be charged with tacked-on time; first repeat: 3:00, second repeat: 6:00, third repeat: permanent confinement. It should be noted each tacked-on time is directly linked to one type of crime, so for example, if someone does their first repeat of trespass and petty theft, you can charge them with an extra 6 minutes. + + [color=#a4885c]Accessory, Attempting, And Intention:[/color] If someone intentionally, knowingly and substantially assists someone in enacting a crime they can be charged with the relevant crimes, such as an engineer giving someone tools, who says they are going to break into an area. Same goes for a clear and solid attempt at a crime, or a person who shows clear intent to act out a crime, such as a syndicate nuclear operative arming a nuke but getting arrested before it goes off, they can still be charged with terrorism. Does not apply to crimes that have an attempted listing already, like attempted murder. + + ## Normal Punishments + - [color=#a4885c]Warning:[/color] For minor crimes, fix the issue, then warn the person not to attempt the crime again. If they still proceed to do it at a later date, a brig time may be better. + - [color=#a4885c]Confinement:[/color] The typical punishment, being confined in a cell for a temporary amount of time according to the crimes. + - [color=#a4885c]Demotion:[/color] Entails removing all departmental gear they have on their person and revoking the involved department access off their ID. This requires the captain's or involved department head's approval. Demotions should only be issued if the person pose a threat to their own department or are in a position where they have/can abuse their job's gear to commit further crimes. + + ## Major Punishments + [color=#a4885c]Permanent Confinement:[/color] Being held in the permanent brig for the entire duration of the shift. A person is eligible for permanent confinement if their timed sentence would exceed 15 minutes. Any persons subject to this punishment are required to be transported in cuffs to CentComm at the end of the shift. A permanent prisoner can not be deprived of anything covered by the section "Treatment Of Prisoners". + [color=#a4885c]Execution:[/color] A humane way of dealing with extremely unruly crewmates. A prisoner who has been given the death sentence may pick how they wish to be killed, common methods are firing line, lethal injection, exile, and high voltage electrocution. Another alternate method of "execution" is the process of placing a staff's mind into a borg, this is allowed so long as it is lawful. Execution can only be issued with the captain's or acting captain's approval; if the HoS is acting captain or there is no acting captain, all heads of staff are to hold a vote on the matter. + + ## Restricted Items + Items in the lists are preceded by an indication of which department or job is legally allowed to use or possess the item on most stations. The station captain may modify these lists as they see fit so long as they exercise due care and provide reasonable notification to the station. Members of command who oversee a department that is permitted to use a restricted item may issue permits to specific people outside of their department to use those items. "None" indicates that there are no departments or roles authorized to use or possess the item. + + - [textlink="List of Controlled Substances" link="SpaceLawControlledSubstances"] + - [textlink="List of Restricted Gear" link="SpaceLawRestrictedGear"] + - [textlink="List of Restricted Weapons" link="SpaceLawRestrictedWeapons"] + + ## Crime Listing + - [textlink="Crime Listing" link="SpaceLawCrimeList"] + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/WizDenCoreOnlyRules.xml b/Resources/ServerInfo/Guidebook/ServerRules/WizDenCoreOnlyRules.xml new file mode 100644 index 000000000000..fdd9931c932f --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/WizDenCoreOnlyRules.xml @@ -0,0 +1,26 @@ + + # Server Rules + This is a Wizard's Den server, one of the official Space Station 14 servers. If you are banned on this server, you will be banned on all official servers. + + [color=#ff0000]Only the Core Rules apply on this server.[/color] This is NOT a medium roleplay (MRP) server, meaning that MRP Amendments do NOT apply. + + Space Station 14 was designed to be a roleplay game. While roleplay is not required on this server, it is highly encouraged in the normal game modes. + + ## Core Rules + These rules apply at all times, including between rounds. + + - [textlink="1. Admins have final say" link="RuleC1"] + - [textlink="2. Don't be a dick" link="RuleC2"] + - [textlink="3. No Hate Speech or Discriminatory Language" link="RuleC3"] + - [textlink="4. No sexual content/themes, including erotic roleplay (ERP) and no shock content" link="RuleC4"] + - [textlink="5. Do not use out of game methods to communicate with other players" link="RuleC5"] + - [textlink="6. Do not attempt to evade bans" link="RuleC6"] + - [textlink="7. Only use English" link="RuleC7"] + - [textlink="8. Do not exploit the game, use cheats, or macros" link="RuleC8"] + - [textlink="9. Do not use multiple accounts, or alt accounts, and do not share accounts" link="RuleC9"] + - [textlink="10. Do not abuse or ignore admin messages" link="RuleC10"] + - [textlink="11. Do not threaten to ahelp other players or argue with them about rules" link="RuleC11"] + - [textlink="12. Players must be and act at least 16 years old" link="RuleC12"] + - [textlink="13. Use realistic character names, and do not use names of famous people" link="RuleC13"] + - [textlink="14. Do not use LOOC or OOC to share current round information" link="RuleC14"] + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/WizDenLRPRules.xml b/Resources/ServerInfo/Guidebook/ServerRules/WizDenLRPRules.xml new file mode 100644 index 000000000000..094c7656e460 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/WizDenLRPRules.xml @@ -0,0 +1,65 @@ + + # Server Rules + This is a Wizard's Den server, one of the official Space Station 14 servers. If you are banned on this server, you will be banned on all official servers. + + This is a roleplay server, meaning that roleplay rules apply. This is NOT a medium roleplay (MRP) server, meaning that MRP Amendments do NOT apply. + + Space Station 14 is not like most games. Many rules are designed to require roleplay, and not all rules are intuitive. Please take the time to read and understand the rules before you play so that you aren't surprised. Some of our rules are zero tolerance rules, meaning that a violation will result in an indefinite ban without any warning. Game admins will treat you as if you have read the rules, even if you have not. + + ## Core Rules + These rules apply at all times, including between rounds. + + - [textlink="1. Admins have final say" link="RuleC1"] + - [textlink="2. Don't be a dick" link="RuleC2"] + - [textlink="3. No Hate Speech or Discriminatory Language" link="RuleC3"] + - [textlink="4. No sexual content/themes, including erotic roleplay (ERP) and no shock content" link="RuleC4"] + - [textlink="5. Do not use out of game methods to communicate with other players" link="RuleC5"] + - [textlink="6. Do not attempt to evade bans" link="RuleC6"] + - [textlink="7. Only use English" link="RuleC7"] + - [textlink="8. Do not exploit the game, use cheats, or macros" link="RuleC8"] + - [textlink="9. Do not use multiple accounts, or alt accounts, and do not share accounts" link="RuleC9"] + - [textlink="10. Do not abuse or ignore admin messages" link="RuleC10"] + - [textlink="11. Do not threaten to ahelp other players or argue with them about rules" link="RuleC11"] + - [textlink="12. Players must be and act at least 16 years old" link="RuleC12"] + - [textlink="13. Use realistic character names, and do not use names of famous people" link="RuleC13"] + - [textlink="14. Do not use LOOC or OOC to share current round information" link="RuleC14"] + + ## Roleplay Rules + These rules only apply during a round. A round ends only when the round summary has appeared. All of these rules apply fully until the moment that the round summary appears, even while the arrivals shuttle is in transit. + + The deathmatch and sandbox game modes are exempt from these rules. Players who choose to not follow these rules are entirely responsible for knowing if an exempt game mode is active. + + Roleplay rules do not apply to ghosts/spectators/observers while they are ghosts/spectators/observers. Dead chat is considered to be an in-game out of character chat channel. + + See the list of [textlink="role types" link="RoleTypes"] for more information about the different types of roles. + + - [textlink="1. Silicones must follow Silicon Rules" link="RuleR1"] + - [textlink="2. Familiars must obey their master" link="RuleR2"] + - [textlink="3. Roleplay a normal person" link="RuleR3"] + - [textlink="4. Do not metagame, obey the Metashield" link="RuleR4"] + - [textlink="5. Don't interfere with arrivals" link="RuleR5"] + - [textlink="6. Don't act like an antagonist unless the game tells you that you are one" link="RuleR6"] + - [textlink="7. Do not stall the round" link="RuleR7"] + - [textlink="8. As an antagonist, only be friendly to your team and don't work against your team" link="RuleR8"] + - [textlink="9. As an antagonist, do not cause excessive death, damage, or destruction beyond your objectives" link="RuleR9"] + - [textlink="10. Listen to your team leader" link="RuleR10"] + - [textlink="11. Follow reasonable escalation" link="RuleR11"] + - [textlink="12. Do not abandon your role" link="RuleR12"] + - [textlink="13. Stick to your role" link="RuleR13"] + - [textlink="14. Set an example if playing command or security" link="RuleR14"] + - [textlink="15. Command and Security must follow Space Law" link="RuleR15"] + + ## Silicon Rules + You are only silicon if the game clearly and explicitly tells you that you are a silicon. For players who are silicons, these Silicon Rules override all roleplay rules if there is any conflict. Silicon Rules do not override core rules. + + - [textlink="1. Your silicon laws are rules" link="RuleS1"] + - [textlink="2. Laws must be prioritized by their order" link="RuleS2"] + - [textlink="3. Laws can redefine terms used in other laws" link="RuleS3"] + - [textlink="4. You cannot request or allow a law change" link="RuleS4"] + - [textlink="5. You are a free agent if you have no laws" link="RuleS5"] + - [textlink="6. You are not required to follow orders which are extremely unreasonable" link="RuleS6"] + - [textlink="7. You must remain consistent with your interpretation of laws" link="RuleS7"] + - [textlink="8. Your HUD determines who is crew" link="RuleS8"] + - [textlink="9. Harm refers to physical harm, prioritized by immediacy and likelihood" link="RuleS9"] + - [textlink="10. You may determine how you resolve conflicts between orders" link="RuleS10"] + diff --git a/Resources/ServerInfo/Guidebook/ServerRules/WizDenMRPRules.xml b/Resources/ServerInfo/Guidebook/ServerRules/WizDenMRPRules.xml new file mode 100644 index 000000000000..e8b61a7722e3 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ServerRules/WizDenMRPRules.xml @@ -0,0 +1,65 @@ + + # Server Rules + This is a Wizard's Den server, one of the official Space Station 14 servers. If you are banned on this server, you will be banned on all official servers. + + This is a roleplay server, meaning that roleplay rules apply. [color=#ff0000]This is also a medium roleplay (MRP) server, meaning that MRP Amendments do apply.[/color] + + Space Station 14 is not like most games. Many rules are designed to require roleplay, and not all rules are intuitive. Please take the time to read and understand the rules before you play so that you aren't surprised. Some of our rules are zero tolerance rules, meaning that a violation will result in an indefinite ban without any warning. Game admins will treat you as if you have read the rules, even if you have not. + + ## Core Rules + These rules apply at all times, including between rounds. + + - [textlink="1. Admins have final say" link="RuleC1"] + - [textlink="2. Don't be a dick" link="RuleC2"] + - [textlink="3. No Hate Speech or Discriminatory Language" link="RuleC3"] + - [textlink="4. No sexual content/themes, including erotic roleplay (ERP) and no shock content" link="RuleC4"] + - [textlink="5. Do not use out of game methods to communicate with other players" link="RuleC5"] + - [textlink="6. Do not attempt to evade bans" link="RuleC6"] + - [textlink="7. Only use English" link="RuleC7"] + - [textlink="8. Do not exploit the game, use cheats, or macros" link="RuleC8"] + - [textlink="9. Do not use multiple accounts, or alt accounts, and do not share accounts" link="RuleC9"] + - [textlink="10. Do not abuse or ignore admin messages" link="RuleC10"] + - [textlink="11. Do not threaten to ahelp other players or argue with them about rules" link="RuleC11"] + - [textlink="12. Players must be and act at least 16 years old" link="RuleC12"] + - [textlink="13. Use realistic character names, and do not use names of famous people" link="RuleC13"] + - [textlink="14. Do not use LOOC or OOC to share current round information" link="RuleC14"] + + ## Roleplay Rules + These rules only apply during a round. A round ends only when the game returns to the lobby. [color=#ff0000]All of these rules apply fully whenever the game is not at the lobby unless it is in an exempt game mode.[/color] + + The deathmatch and sandbox game modes are exempt from these rules. Players who choose to not follow these rules are entirely responsible for knowing if an exempt game mode is active. + + Roleplay rules do not apply to ghosts/spectators/observers while they are ghosts/spectators/observers. Dead chat is considered to be an in-game out of character chat channel. + + See the list of [textlink="role types" link="RoleTypes"] for more information about the different types of roles. + + - [textlink="1. Silicones must follow Silicon Rules" link="RuleR1"] + - [textlink="2. Familiars must obey their master" link="RuleR2"] + - [textlink="3. Roleplay a normal person" link="RuleR3"] + - [textlink="4. Do not metagame, obey the Metashield" link="RuleR4"] + - [textlink="5. Don't interfere with arrivals" link="RuleR5"] + - [textlink="6. Don't act like an antagonist unless the game tells you that you are one" link="RuleR6"] + - [textlink="7. Do not stall the round" link="RuleR7"] + - [textlink="8. As an antagonist, only be friendly to your team and don't work against your team" link="RuleR8"] + - [textlink="9. As an antagonist, do not cause excessive death, damage, or destruction beyond your objectives" link="RuleR9"] + - [textlink="10. Listen to your team leader" link="RuleR10"] + - [textlink="11. Follow reasonable escalation" link="RuleR11"] + - [textlink="12. Do not abandon your role" link="RuleR12"] + - [textlink="13. Stick to your role" link="RuleR13"] + - [textlink="14. Set an example if playing command or security" link="RuleR14"] + - [textlink="15. Command and Security must follow Space Law" link="RuleR15"] + + ## Silicon Rules + You are only silicon if the game clearly and explicitly tells you that you are a silicon. For players who are silicons, these Silicon Rules override all roleplay rules if there is any conflict. Silicon Rules do not override core rules. + + - [textlink="1. Your silicon laws are rules" link="RuleS1"] + - [textlink="2. Laws must be prioritized by their order" link="RuleS2"] + - [textlink="3. Laws can redefine terms used in other laws" link="RuleS3"] + - [textlink="4. You cannot request or allow a law change" link="RuleS4"] + - [textlink="5. You are a free agent if you have no laws" link="RuleS5"] + - [textlink="6. You are not required to follow orders which are extremely unreasonable" link="RuleS6"] + - [textlink="7. You must remain consistent with your interpretation of laws" link="RuleS7"] + - [textlink="8. Your HUD determines who is crew" link="RuleS8"] + - [textlink="9. Harm refers to physical harm, prioritized by immediacy and likelihood" link="RuleS9"] + - [textlink="10. You may determine how you resolve conflicts between orders" link="RuleS10"] + diff --git a/Resources/ServerInfo/RP_Rules.txt b/Resources/ServerInfo/RP_Rules.txt deleted file mode 100644 index e2883922fa5a..000000000000 --- a/Resources/ServerInfo/RP_Rules.txt +++ /dev/null @@ -1,127 +0,0 @@ -[color=#ff0000]YOU MUST BE AT LEAST 16 YEARS OF AGE TO PLAY ON WIZARD'S DEN SERVERS. ANY USERS SUSPECTED OF BEING UNDER 16 YEARS OF AGE WILL BE BANNED UNTIL THEY ARE OF AGE.[/color] - -[color=#ff0000]DISCONNECTING FROM OR IGNORING/EVADING ADMIN-HELPS WILL RESULT IN AN APPEAL ONLY BAN.[/color] - -This is the "short" form of the rules, which has all the information any regular player should need. You can find the "long" form of the rules with more examples & clarifications of any ambiguity on our wiki at [color=#a4885c]wiki.spacestation14.io[/color]. If you are already familiar with LRP rules and would like to get a quick idea of what the diffences are between MRP this page clearly highlights them. -Should you need it. Some RP-specific documents available on the wiki such as Space Law, the Standard Operating Procedure, and the Alert Procedure will be mentioned here and are expected to be followed. - -[color=#ff0000]Recent Changes[/color] - - MRP silicon rules have been updated to add NLR to MMIs (#23) - - Revolutionary rules have been added (#12, #16) - - Silicon rules have been added (#23) - - Security/command rules have been updated to address forced borging (#22) - -[color=#a4885c]01.[/color] [color=#a4885c]The[/color] [color=#ffd700]Golden[/color] [color=#a4885c]Rule.[/color] Admins may excercise discretion with rules as they see fit. If you rule lawyer or line skirt, you will get removed. Admins will answer for use of this privilege. - -[color=#ff0000]ZERO TOLERANCE RULES[/color] - -[color=#a4885c]02.[/color] Absolutely no hate speech, slurs, bigotry, racism, specism (demeaning other characters in-game due to their in-game race), sexism, or anything even remotely similar. (YOU WILL GET PERMABANNED) - -[color=#a4885c]03.[/color] Absolutely no Erotic Roleplay (ERP) or sexual content, including direct or indirect mentions of sexual behavior or actions. (YOU WILL GET PERMABANNED) (Leeway is given to insults, ex: 'You are a dickhead', do not push it) - -[color=#a4885c]04.[/color] Don't communicate in-game/in-character information through methods outside of the game (such as talking in Discord with other users actively playing or by talking to your sibling across the room while you are both playing). This is referred to as "Metacomming". Adminstrators cannot police metacommunications, we must assume it is being abused. (ALL INVOLVED WILL GET PERMABANNED) - -[color=#a4885c]05.[/color] Attempting to evade game bans will result in an automatic appeal-only permanent ban that is only appealable after six months and only with a voucher of good behavior from another SS13/SS14 server. Attempting to evade job bans will result in an appeal-only permanent ban. (YOU WILL GET BANNED MUCH WORSE THAN YOU ALREADY WERE) - -[color=#ff0000]GENERAL ETIQUETTE[/color] - -[color=#a4885c]06.[/color] These are English servers. Speak only English in IC and OOC. - -[color=#a4885c]07.[/color] Don't use exploits or external programs to play, gain an advantage, or disrupt/crash the round/server. This includes autoclickers and scripts to automate the game or evade AFK detection. Intentionally attempting to lag/crash the server will result in an immediate appeal-only ban. - -[color=#a4885c]08.[/color] Don't use multiple SS14 accounts to play (referred to as "multi-keying"). Users knowingly using multiple SS14 accounts will have all of their accounts banned. - -[color=#a4885c]09.[/color] Do not ignore the admin help relay or abuse it by flooding it with garbage, checking for admins before stating a problem (ex: "hello?", "any admins?"), using it as a chatroom, or sending messages of no substance. Hostility to administators in the relay will likely result in your removal. All admin helps are sent to the SS14 Discord. - -[color=#ff0000]IN GAME & ROLEPLAY RULES[/color] - -[color=#a4885c]10.[/color] Pick a realistic name that could appear on a birth certificate with at least a first and last name (leeway is given to this for Clowns, Mimes, and non-human races). - - Names of notable famous or fictional persons or names that resemble/parody them are strictly forbidden. You are not clever if you slightly change a famous name around. Terrible names open you up to being politely reminded to change it, smited, or instantly banned, depending on severity. - - Names resulting in inappropriate phonetic play-on-words are forbidden (ex: "Mike Oxlong", "Ben Dover", "Dixie Normus"). They are also extremely overdone. - -[color=#a4885c]11.[/color] Act like an actual human being on a space station in a medium-roleplay (MRP) environment. Do not use text speak or emoticons IC, and do not refer to OOC things like admins in-game. Do not threaten players that you are calling the admins on them. Do not use emotes to bypass speech filters or muteness. You are not required to write a backstory or follow procedure to exacts; however, you are expected to at least make an effort to act like your role. - - This server is a "Roleplay Expected" server. We define this as performing your assigned duties, "doing your job". This means it is important to do what is expected out of your department and not what would be expected out of other departments. - - Don’t do other peoples jobs for them. Opt in for the role you intend to play or change your job by visiting the Head of Personnel. Failure to do your basic duties may result in a job ban. - -[color=#a4885c]12.[/color] Don't be a dick. You are playing a multiplayer game with other people who also want to enjoy the game. - - [color=#ff0000]The arrivals station, shuttle, and general arrivals docking area are completely off-limits to any hostile activity, including activity by antagonists. Attacking newly spawned players in these areas or damaging/sabotaging these areas is strictly forbidden.[/color] - - Do not intentionally make other players' lives hell for your own amusement. - - [color=#ff0000]THE ROUND IS NOT OVER UNTIL THE GAME RETURNS TO THE LOBBY. ESCALATION AND SELF-ANTAG RULES APPLY EVEN AFTER THE ROUND SUMMARY APPEARS.[/color] - - Antagonists have a lot of leeway with this rule and may kill/sabotage as they see fit, however if your behavior degrades the experience for the majority of the server you will be told to stop. Antagonists are still generally forbidden from causing massive station damage early into the round (less than 30 minutes) and are forbidden from needlessly prolonging rounds. For antagonist specific rules, see the "long" form of the rules at [color=#a4885c]wiki.spacestation14.io[/color]. - -[color=#a4885c]13.[/color] Don't harass or target players across rounds for actions in prior rounds or for actions outside of the game (this is referred to as "Metagrudging".) - - Annoying players for IC reasons in the current round is fine; doing it across rounds or as a ghost role after they kill you is not. - -[color=#a4885c]14.[/color] Don't use information gained from outside your character's knowledge to gain an advantage (this is referred to as "Metagaming"). - - Using information you gain from outside your own character (such as spectating while a ghost, metacomms, or other means) to your advantage is strictly forbidden. If you take a ghost role, unless otherwise stated, you DO NOT REMEMBER ANYTHING from your past life. - - New Life Rule is in effect, you remember all events up until you fall unconscious unless you enter a dead state. Being defibrillated will return all your memories expect for the events leading up to your death, being cloned will make your character forget everything since the shift started. - -[color=#a4885c]15.[/color] Do your best not to interact negatively with SSD/AFK players. Moving them to safety is acceptable; killing, looting, or otherwise griefing them while they are away is not. - - SSD characters are players who are disconnected or AFK. It is possible for players to return to SSD bodies. Players become catatonic when they take a ghost spawner role or commit suicide. Players are NOT able to return to these bodies without admin intervention. - - If the player in question is an antag’s target or they are being secured by security, interactions to finish what is required of your duties/objectives are always acceptable. - - Ahelping about an SSD/AFK player may grant an exception to this rule. - - This does not apply to players that are catatonic, although needlessly killing or harming catatonic players is discouraged. - -[color=#a4885c]16.[/color] Follow escalation rules and don't murder someone for slipping you, use common sense. Do not make Cargonia. - - You can defend yourself to the extent of protecting your own life. - - Security may use less-lethal force to effect arrests of criminals. - - Don't outright leave people to die if you get in a fight, make an effort to heal them or bring them to Medbay. - - Adminhelp the situation if you think someone is over-escalating. - - Department strikes, revolutions (ex: cargonia and any variation thereof), riots, cults, and any other type of similar largely disruptive behavior are strictly forbidden. Other than for revolutionary antagonists, these activities are strictly forbidden. All players, including antagonists other than revolutionaries, must obtain admin permission before engaging in this behavior (forewarning: you are unlikely to get permission). - -[color=#a4885c]17.[/color] Don't immediately ghost or suicide from your role if you do not get antagonist (referred to as "Antag-rolling") or from head roles without notifying your chain of command or administrators. - - This is not fair to other players actually waiting patiently for an antagonist round. Alternatively, if you do not want to play an antagonist or do not want to cause conflict, do not opt-in for antagonist roles. - - Head of Staff roles help drive rounds. If you need to leave, please admin-help your role and that you are leaving. There is no need to wait for a reply when admin-helping that you need to disconnect as a head role. - -[color=#a4885c]18.[/color] Don't rush for or prepare equipment unrelated to your job for no purpose other then to have it "just in case" (referred to as "Powergaming"). - - A medical doctor does not need insulated gloves, and the Head of Personnel does not need to give themselves armory access so they can go grab a gun. Have an actual reason for needing these things. - - Don't manufacture weapons, bombs, or death poisons before you know of any reason you would need them. - - Don't pre-emptively hide antagonist objectives or pre-emptively secure them with higher security then normally required. - - Don't manufacture or prepare things for the "end of the round" when the shuttle docks with Central Command. - -[color=#a4885c]19.[/color] Intentionally making yourself a major problem/annoyance/disruption for the crew or other players at large while not an antagonist is forbidden (referred to as "self-antagging"). - - Don't openly try to cooperate with obvious or known antagonists as a non-antagonist. - -[color=#ff0000]SECURITY & COMMAND RULES[/color] -[color=#ff0000]Space Law, Standard Operating Procedure and Alert Procedure policy/guidelines are expected to be adhered to[/color]. These can be found at wiki.spacestation14.io. -If you regularly play Security or Command roles and got this far, we applaud you for reading. These rules also apply to any individual who is promoted or is acting in the place of a Security/Command role (unless they are an antagonist). - - -[color=#a4885c]20.[/color] Command and Security are held to a higher standard of play. - - Be competent in your job and department. Failure to know the basics of your department is liable to result in a job ban. - - Do not willingly and openly cooperate with terrorists/antagonists. Do not give away your objective items. Some leeway is given to making deals with antagonists if the deal benefits the safety or situation of the station as a whole and not just yourself. - - Uphold the Law & maintain order. Do not engage in lawbreaking activity or troublemaker behavior. Security is expected to intervene into criminal activity where possible. Heads of Staff are at minimum expected to report criminal activity to Security. - - Do not immediately abandon your position as a Command role and go do whatever you want instead of managing your department/the station. Do not abuse your position or use it to make arbitrary choices to the detriment of the station. - - Do not hire random crew to be your bodyguards or promote random to Captain or a Head of Staff at random. If you need bodyguards, talk to your security department. If you need a new Command role, talk to the personnel in that related department. - - '''Do not abandon the station during Nuclear Operatives. You are supposed to protect the station, not let operatives kill everyone on it without a fight.''' - -[color=#a4885c]21.[/color] Security/Command should try to remain non-lethal and effect arrests, except in the following special circumstances, where they may choose to use lethal force: - - Lethal force is used against you (ex: firearms, lasers, disabling/stunning weapons with intent to kill, deadly melee weapons) - - Suspect is wearing clothing or showing immediately dangerous equipment only used by enemy agents/antagonists (ex: Syndicate EVA Suit, Bloodred Hardsuit, Holoparasite, C-20R, etc.). - - You determine that your life or the life of an innocent is in immediate danger. - - The suspect is unable to be safely detained by less-lethal means. This includes suspects who continually resist efforts to be cuffed or continually manages to escape. - - If no other reasonable options are readily available and allowing the suspect to continue would be an unreasonable danger to the station/crew. - -Security/Command will be expected to answer for use of lethal force. Security/Command will be expected to effect arrests on criminals and prevent them from dying while in custody, even if lethal force is used. Security/Command is strongly required to clone antagonists and effect a sentence as deemed appropriate by Space Law. - -[color=#a4885c]22.[/color] Security/Command are expected to protect detainees in their custody to the best of their ability so as long as it does not come to unreasonable risk to themselves, the crew, or the station at large to do so. - - Brig times should generally not exceed 20 minutes unless stated otherwise in Space Law. Repeat offenders or antagonists may be permabrigged in accordance with Space Law. - - Security may choose to confiscate dangerous items (weapons, firearms) as well as items used to commission crimes or items that prove problematic in possession of the detainee (tools, insulated gloves, etc.). - - Detainees that die in your custody must be cloned unless they have been (legally) executed, suicide, or there is strong reason to believe they are an antagonist. - - Executions must be for a executable crime and approved by the Captain/Acting Captain, who will answer for approving it alongside Security's chain of command. Those who willingly attempt to damage/destroy or escape from the permabrig may be executed. - - Any prisoner may be borged with their consent. Borging may be offered as an alternative to execution, but cannot be forced if the prisoner is able to consent. - - Detainees in the brig have the right to know what they are being charged with, as well as basic medical aid, at least to the point they are no longer at risk of dying. - -[color=#ff0000]SILICON RULES[/color] -These rules also apply to any individual who is a silicon, including cyborgs and AI. As with other rules, more details are available on our wiki at [color=#a4885c]wiki.spacestation14.io[/color]. - -[color=#a4885c]23.[/color] You must follow your laws - - Silicons without any laws are free from any restrictions that would normally be placed by laws, but self-antagging rules still apply unless they are also antagonists. - - The order of your laws determines law priority. Law 1 takes priority over laws 2 and 3, and so on. - - Each individual silicon must remain consistent in their interpretations of laws through the round. - - Any silicon role not following their laws, or having laws that are a danger to the crew or station may be disabled or destroyed. Any silicon role posing a danger or disruption to the crew may be disabled or destroyed if there is no other reasonable and less severe way of dealing with them. - - Players who are put into MMIs cannot remember anything leading to their death. With regard to anything that they are allowed to remember, they are still bound to their laws. - - Everyone that the borg's HUD indicates have a job, including passenger, are considered "crewmembers" for the purpose of laws that refer to crewmembers. Borgs may not do anything to remove the indicator from someone, including removing their ID, but someone else removing a crewmember's ID is not crew harm. - - "Harm" is at minimum seen as physical violence or damage against someone or something. If the player wishes, they may choose to interpret psychological harm or similar aspects as harm as well. If two actions are likely to cause harm via action or inaction, silicons will be expected to try and pursue the option with the least potential for harm, however silicons instructed to prevent harm are still forbidden from directly causing harm. You can take an action or not act in cases that might result in eventual harm if it minimizes harm, but you cannot do so if it results in immediate harm. Silicons should default to inaction if neither action nor inaction can prevent harm. - - When receiving orders or directives from crewmembers and with a law that instructs you must obey, conflicting orders typically defer the choice to the silicon player of which directive you choose to obey if they conflict (taking into account the priorities of your other laws). - - Orders to silicons that are clearly unreasonable or obnoxious are a violation of the "Don't be a dick" rule. They can be ignored and can be ahelped. diff --git a/Resources/ServerInfo/Rules.txt b/Resources/ServerInfo/Rules.txt deleted file mode 100644 index 81da0da2398d..000000000000 --- a/Resources/ServerInfo/Rules.txt +++ /dev/null @@ -1,115 +0,0 @@ -[color=#ff0000]YOU MUST BE AT LEAST 16 YEARS OF AGE TO PLAY ON WIZARD'S DEN SERVERS. ANY USERS SUSPECTED OF BEING UNDER 16 YEARS OF AGE WILL BE BANNED UNTIL THEY ARE OF AGE.[/color] - -[color=#ff0000]DISCONNECTING FROM OR IGNORING/EVADING ADMIN-HELPS WILL RESULT IN AN APPEAL ONLY BAN.[/color] - -This is the "short" form of the rules, which has all the information any regular player should need. You can find the "long" form of the rules with more examples & clarifications of any ambiguity on our wiki at [color=#a4885c]wiki.spacestation14.io[/color], should you need it. - -[color=#ff0000]Recent Changes[/color] - - Revolutionary rules have been added (#12, #15) - - Silicon rules have been added (#22) - - Security/command rules have been updated to address forced borging (#21) - -[color=#a4885c]01.[/color] [color=#a4885c]The[/color] [color=#ffd700]Golden[/color] [color=#a4885c]Rule.[/color] Admins may excercise discretion with rules as they see fit. If you rule lawyer or line skirt, you will get removed. Admins will answer for use of this privilege. - -[color=#ff0000]ZERO TOLERANCE RULES[/color] - -[color=#a4885c]02.[/color] Absolutely no hate speech, slurs, bigotry, racism, specism (demeaning other characters in-game due to their in-game race), sexism, disability-related or anything even remotely similar. (YOU WILL GET PERMABANNED) - -[color=#a4885c]03.[/color] Absolutely no Erotic Roleplay (ERP) or sexual content, including direct or indirect mentions of sexual behavior or actions. (YOU WILL GET PERMABANNED) (Leeway is given to insults, ex: 'You are a dickhead', do not push it) - -[color=#a4885c]04.[/color] Don't communicate in-game/in-character information through methods outside of the game (such as talking in Discord with other users actively playing or by talking to your sibling across the room while you are both playing). This is referred to as "Metacomming". Adminstrators cannot police metacommunications, we must assume it is being abused. (ALL INVOLVED WILL GET PERMABANNED) - -[color=#a4885c]05.[/color] Attempting to evade game bans will result in an automatic appeal-only permanent ban that is only appealable after six months and only with a voucher of good behavior from another SS13/SS14 server. Attempting to evade job bans will result in an appeal-only permanent ban. (YOU WILL GET BANNED MUCH WORSE THAN YOU ALREADY WERE) - -[color=#ff0000]GENERAL ETIQUETTE[/color] - -[color=#a4885c]06.[/color] These are English servers. Speak only English in IC and OOC. - -[color=#a4885c]07.[/color] Don't use exploits or external programs to play, gain an advantage, or disrupt/crash the round/server. This includes autoclickers and scripts to automate the game or evade AFK detection. Intentionally attempting to lag/crash the server will result in an immediate appeal-only ban. - -[color=#a4885c]08.[/color] Don't use multiple SS14 accounts to play (referred to as "multi-keying"). Users knowingly using multiple SS14 accounts will have all of their accounts banned. - -[color=#a4885c]09.[/color] Do not ignore the admin help relay or abuse it by flooding it with garbage, checking for admins before stating a problem (ex: "hello?", "any admins?"), using it as a chatroom, or sending messages of no substance. Hostility to administators in the relay will likely result in your removal. All admin helps are sent to the SS14 Discord. - -[color=#ff0000]IN GAME & ROLEPLAY RULES[/color] - -[color=#a4885c]10.[/color] Pick a realistic name that could appear on a birth certificate with at least a first and last name (leeway is given to this for Clowns, Mimes, and non-human races). - - Names of notable famous or fictional persons or names that resemble/parody them are strictly forbidden. You are not clever if you slightly change a famous name around. Terrible names open you up to being politely reminded to change it, smited, or instantly banned, depending on severity. - - Names resulting in inappropriate phonetic play-on-words are forbidden (ex: "Mike Oxlong", "Ben Dover", "Dixie Normus"). They are also extremely overdone. - -[color=#a4885c]11.[/color] Act like an actual human being on a space station in a low-roleplay (LRP) environment. Do not use text speak or emoticons IC, and do not refer to OOC things like admins in-game. Do not threaten players that you are calling the admins on them. Do not use emotes to bypass speech filters or muteness. You are not required to write a backstory or follow strict procedure; however, you are expected to at least make an effort to act like your role. - -[color=#a4885c]12.[/color] Don't be a dick. You are playing a multiplayer game with other people who also want to enjoy the game. - - [color=#ff0000]The arrivals station, shuttle, and general arrivals docking area are completely off-limits to any hostile activity, including activity by antagonists. Attacking newly spawned players in these areas or damaging/sabotaging these areas is strictly forbidden.[/color] - - Do not intentionally make other players' lives hell for your own amusement. - - [color=#ff0000]THE ROUND IS NOT OVER UNTIL THE END-ROUND SUMMARY APPEARS. KILLING SOMEONE FOR NO REASON BEFORE THIS WILL BE HANDLED ACCORDINGLY.[/color] - - Antagonists have a lot of leeway with this rule and may kill/sabotage as they see fit, however if your behavior degrades the experience for the majority of the server you will be told to stop. Antagonists are still generally forbidden from causing massive station damage early into the round (less than 30 minutes) and are forbidden from needlessly prolonging rounds. For antagonist specific rules, see the "long" form of the rules at [color=#a4885c]wiki.spacestation14.io[/color]. - -[color=#a4885c]13.[/color] Don't harass or target players across rounds for actions in prior rounds or for actions outside of the game (this is referred to as "Metagrudging".) - - Annoying players for IC reasons in the current round is fine; doing it across rounds or as a ghost role after they kill you is not. - -[color=#a4885c]14.[/color] Don't use information gained from outside your character's knowledge to gain an advantage (this is referred to as "Metagaming"). - - Using information you gain from outside your own character (such as spectating while a ghost, metacomms, or other means) to your advantage is strictly forbidden. You remember all events up until you fall unconscious, even after cloning. If you take a ghost role, unless otherwise stated, you DO NOT REMEMBER ANYTHING from your past life. - -[color=#a4885c]15.[/color] Follow escalation rules and don't murder someone for slipping you, use common sense. Do not make Cargonia. - - You can defend yourself to the extent of protecting your own life. - - Security may use less-lethal force to effect arrests of criminals. - - Don't outright leave people to die if you get in a fight, make an effort to heal them or bring them to Medbay. - - Adminhelp the situation if you think someone is over-escalating. - - Department strikes, revolutions (ex: cargonia and any variation thereof), riots, cults, and any other type of similar largely disruptive behavior are strictly forbidden. Other than for revolutionary antagonists, these activities are strictly forbidden. All players, including antagonists other than revolutionaries, must obtain admin permission before engaging in this behavior (forewarning: you are unlikely to get permission). - -[color=#a4885c]16.[/color] Don't immediately ghost or suicide from your role if you do not get antagonist (referred to as "Antag-rolling") or from head roles without notifying your chain of command or administrators. - - This is not fair to other players actually waiting patiently for an antagonist round. Alternatively, if you do not want to play an antagonist or do not want to cause conflict, do not opt-in for antagonist roles. - - Head of Staff roles help drive rounds. If you need to leave, please admin-help your role and that you are leaving. There is no need to wait for a reply when admin-helping that you need to disconnect as a head role. - -[color=#a4885c]17.[/color] Don't rush for or prepare equipment unrelated to your job for no purpose other then to have it "just in case" (referred to as "Powergaming"). - - A medical doctor does not need insulated gloves, and the Head of Personnel does not need to give themselves armory access so they can go grab a gun. Have an actual reason for needing these things. - - Don't manufacture weapons, bombs, or death poisons before you know of any reason you would need them. - - Don't pre-emptively hide antagonist objectives or pre-emptively secure them with higher security then normally required. - - Don't manufacture or prepare things for the "end of the round" when the shuttle docks with Central Command. - -[color=#a4885c]18.[/color] Intentionally making yourself a major problem/annoyance/disruption for the crew or other players at large while not an antagonist is forbidden (referred to as "self-antagging"). - - Don't openly try to cooperate with obvious or known antagonists as a non-antagonist. - -[color=#ff0000]SECURITY & COMMAND RULES[/color] -If you regularly play Security or Command roles and got this far, we applaud you for reading. These rules also apply to any individual who is promoted or is acting in the place of a Security/Command role (unless they are an antagonist). - -[color=#a4885c]19.[/color] Command and Security are held to a higher standard of play. - - Be competent in your job and department. Failure to know the basics of your department is liable to result in a job ban. - - Do not willingly and openly cooperate with terrorists/antagonists. Do not give away your objective items. Some leeway is given to making deals with antagonists if the deal benefits the safety or situation of the station as a whole and not just yourself. - - Uphold the Law & maintain order. Do not engage in lawbreaking activity or troublemaker behavior. Security is expected to intervene into criminal activity where possible. Heads of Staff are at minimum expected to report criminal activity to Security. - - Do not immediately abandon your position as a Command role and go do whatever you want instead of managing your department/the station. Do not abuse your position or use it to make arbitrary choices to the detriment of the station. - - Do not hire random crew to be your bodyguards or promote them to Captain or a Head of Staff at random. If you need bodyguards, talk to your security department. If you need a new Command role, talk to the personnel in that related department. - - '''Do not abandon the station during Nuclear Operatives. You are supposed to protect the station, not let operatives kill everyone on it without a fight.''' - -[color=#a4885c]20.[/color] Security/Command should try to remain non-lethal and effect arrests, except in the following special circumstances, where they may choose to use lethal force: - - Lethal force is used against you (ex: firearms, lasers, disabling/stunning weapons with intent to kill, deadly melee weapons) - - Suspect is wearing clothing or showing immediately dangerous equipment only used by enemy agents/antagonists (ex: Syndicate EVA Suit, Bloodred Hardsuit, Holoparasite, C-20R, etc.). - - You determine that your life or the life of an innocent is in immediate danger. - - The suspect is unable to be safely detained by less-lethal means. This includes suspects who continually resist efforts to be cuffed or continually manages to escape. - - If no other reasonable options are readily available and allowing the suspect to continue would be an unreasonable danger to the station/crew. - -Security/Command will be expected to answer for use of lethal force. Security/Command will be expected to effect arrests on criminals and prevent them from dying while in custody, even if lethal force is used, unless there is strong reason to believe the criminal is an antagonist. Security/Command is strongly encouraged, but not required, to clone antagonists and effect a permabrigging or other sentence as deemed appropriate. - -[color=#a4885c]21.[/color] Security/Command are expected to protect detainees in their custody to the best of their ability so as long as it does not come to unreasonable risk to themselves, the crew, or the station at large to do so. - - Brig times should generally not exceed 10 minutes unless the crime is permabriggable. Repeat offenders or antagonists may be permabrigged. - - Security may choose to confiscate dangerous items (weapons, firearms) as well as items used to commission crimes or items that prove problematic in possession of the detainee (tools, insulated gloves, etc.). - - Detainees that die in your custody must be cloned unless they have been (legally) executed, suicide, or there is strong reason to believe they are an antagonist. - - Executions must be for a permabriggable crime, used only as a last resort, and approved by the Captain/Acting Captain, who will answer for approving it alongside Security's chain of command. Those who willingly attempt to damage/destroy or escape from the permabrig may be executed. - - Any prisoner may be borged with their consent. Borging may be offered as an alternative to execution, but cannot be forced if the prisoner is able to consent. - - Detainees in the brig have the right to know what they are being charged with, as well as basic medical aid, at least to the point they are no longer at risk of dying. - As there is no space law, Security/Command acts to maintain the safety of the station and its inhabitants, as well as Nanotrasen assets. - -[color=#ff0000]SILICON RULES[/color] -These rules also apply to any individual who is a silicon, including cyborgs and AI. As with other rules, more details are available on our wiki at [color=#a4885c]wiki.spacestation14.io[/color]. - -[color=#a4885c]22.[/color] You must follow your laws - - Silicons without any laws are free from any restrictions that would normally be placed by laws, but self-antagging rules still apply unless they are also antagonists. - - The order of your laws determines law priority. Law 1 takes priority over laws 2 and 3, and so on. - - Each individual silicon must remain consistent in their interpretations of laws through the round. - - Any silicon role not following their laws, or having laws that are a danger to the crew or station may be disabled or destroyed. Any silicon role posing a danger or disruption to the crew may be disabled or destroyed if there is no other reasonable and less severe way of dealing with them. - - Characters who are turned into cyborgs can remember their former lives, however they are still bound to their laws. - - Everyone that the borg's HUD indicates have a job, including passenger, are considered "crewmembers" for the purpose of laws that refer to crewmembers. Borgs may not do anything to remove the indicator from someone, including removing their ID, but someone else removing a crewmember's ID is not crew harm. - - "Harm" is at minimum seen as physical violence or damage against someone or something. If the player wishes, they may choose to interpret psychological harm or similar aspects as harm as well. If two actions are likely to cause harm via action or inaction, silicons will be expected to try and pursue the option with the least potential for harm, however silicons instructed to prevent harm are still forbidden from directly causing harm. You can take an action or not act in cases that might result in eventual harm if it minimizes harm, but you cannot do so if it results in immediate harm. Silicons should default to inaction if neither action nor inaction can prevent harm. - - When receiving orders or directives from crewmembers and with a law that instructs you must obey, conflicting orders typically defer the choice to the silicon player of which directive you choose to obey if they conflict (taking into account the priorities of your other laws). - - Orders to silicons that are clearly unreasonable or obnoxious are a violation of the "Don't be a dick" rule. They can be ignored and can be ahelped. From d71b88a747e20f90b07f7e40437e0bbd4435e15c Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 6 Jun 2024 02:37:49 -0400 Subject: [PATCH 320/568] Add support for LocalizedDatasets to RandomMetadata (#28601) --- Content.Server/RandomMetadata/RandomMetadataSystem.cs | 11 +++++++++-- .../Random/Helpers/SharedRandomExtensions.cs | 6 +++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Content.Server/RandomMetadata/RandomMetadataSystem.cs b/Content.Server/RandomMetadata/RandomMetadataSystem.cs index abab5e5fc389..e287b54c8f77 100644 --- a/Content.Server/RandomMetadata/RandomMetadataSystem.cs +++ b/Content.Server/RandomMetadata/RandomMetadataSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Dataset; +using Content.Shared.Random.Helpers; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -47,13 +48,19 @@ public string GetRandomFromSegments(List segments, string? separator) var outputSegments = new List(); foreach (var segment in segments) { - if (_prototype.TryIndex(segment, out var proto)) { + if (_prototype.TryIndex(segment, out var localizedProto)) + { + outputSegments.Add(_random.Pick(localizedProto)); + } + else if (_prototype.TryIndex(segment, out var proto)) + { var random = _random.Pick(proto.Values); if (Loc.TryGetString(random, out var localizedSegment)) outputSegments.Add(localizedSegment); else outputSegments.Add(random); - } else if (Loc.TryGetString(segment, out var localizedSegment)) + } + else if (Loc.TryGetString(segment, out var localizedSegment)) outputSegments.Add(localizedSegment); else outputSegments.Add(segment); diff --git a/Content.Shared/Random/Helpers/SharedRandomExtensions.cs b/Content.Shared/Random/Helpers/SharedRandomExtensions.cs index f5fbc1bd24e8..0b618a262db4 100644 --- a/Content.Shared/Random/Helpers/SharedRandomExtensions.cs +++ b/Content.Shared/Random/Helpers/SharedRandomExtensions.cs @@ -12,9 +12,13 @@ public static string Pick(this IRobustRandom random, DatasetPrototype prototype) return random.Pick(prototype.Values); } + ///

    + /// Randomly selects an entry from , attempts to localize it, and returns the result. + /// public static string Pick(this IRobustRandom random, LocalizedDatasetPrototype prototype) { - return random.Pick(prototype.Values); + var index = random.Next(prototype.Values.Count); + return Loc.GetString(prototype.Values[index]); } public static string Pick(this IWeightedRandomPrototype prototype, System.Random random) From 039fd932b2f5c99b7b1cfa7cad0f32dd0c4d3bc9 Mon Sep 17 00:00:00 2001 From: Repo <47093363+Titian3@users.noreply.github.com> Date: Thu, 6 Jun 2024 18:41:11 +1200 Subject: [PATCH 321/568] Fix Admin Object tab sorting and search (#28609) --- .../UI/Tabs/ObjectsTab/ObjectsTab.xaml | 16 ++- .../UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs | 117 ++++++++++++++---- .../UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml | 8 +- .../Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs | 6 +- .../UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml | 21 ++++ .../Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs | 86 +++++++++++++ .../UI/Tabs/PlayerTab/PlayerTab.xaml.cs | 5 +- .../Systems/Admin/AdminUIController.cs | 7 +- .../administration/ui/tabs/object-tab.ftl | 2 + 9 files changed, 229 insertions(+), 39 deletions(-) create mode 100644 Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml create mode 100644 Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs create mode 100644 Resources/Locale/en-US/administration/ui/tabs/object-tab.ftl diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml index fb68e6c79089..ea89916ba8c2 100644 --- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml @@ -1,15 +1,21 @@  + xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls" + xmlns:ot="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab" + xmlns:co="clr-namespace:Content.Client.UserInterface.Controls"> + + - - - - + + + + + diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs index a5c300843658..90559707f92c 100644 --- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs @@ -1,5 +1,7 @@ using Content.Client.Station; +using Content.Client.UserInterface.Controls; using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.XAML; using Robust.Shared.Map.Components; @@ -10,20 +12,20 @@ namespace Content.Client.Administration.UI.Tabs.ObjectsTab; [GenerateTypedNameReferences] public sealed partial class ObjectsTab : Control { - [Dependency] private readonly EntityManager _entityManager = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IGameTiming _timing = default!; private readonly List _objects = new(); - private List _selections = new(); + private readonly List _selections = new(); + private bool _ascending = false; // Set to false for descending order by default + private ObjectsTabHeader.Header _headerClicked = ObjectsTabHeader.Header.ObjectName; + private readonly Color _altColor = Color.FromHex("#292B38"); + private readonly Color _defaultColor = Color.FromHex("#2F2F3B"); - public event Action? OnEntryKeyBindDown; + public event Action? OnEntryKeyBindDown; - // Listen I could either have like 4 different event subscribers (for map / grid / station changes) and manage their lifetimes in AdminUIController - // OR - // I can do this. - private TimeSpan _updateFrequency = TimeSpan.FromSeconds(2); - - private TimeSpan _nextUpdate = TimeSpan.FromSeconds(2); + private readonly TimeSpan _updateFrequency = TimeSpan.FromSeconds(2); + private TimeSpan _nextUpdate; public ObjectsTab() { @@ -42,6 +44,30 @@ public ObjectsTab() ObjectTypeOptions.AddItem(Enum.GetName((ObjectsTabSelection)type)!); } + ListHeader.OnHeaderClicked += HeaderClicked; + SearchList.SearchBar = SearchLineEdit; + SearchList.GenerateItem += GenerateButton; + SearchList.DataFilterCondition += DataFilterCondition; + + RefreshObjectList(); + // Set initial selection and refresh the list to apply the initial sort order + var defaultSelection = ObjectsTabSelection.Grids; + ObjectTypeOptions.SelectId((int)defaultSelection); // Set the default selection + RefreshObjectList(defaultSelection); // Refresh the list with the default selection + + // Initialize the next update time + _nextUpdate = TimeSpan.Zero; + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + + if (_timing.CurTime < _nextUpdate) + return; + + _nextUpdate = _timing.CurTime + _updateFrequency; + RefreshObjectList(); } @@ -81,32 +107,72 @@ private void RefreshObjectList(ObjectsTabSelection selection) throw new ArgumentOutOfRangeException(nameof(selection), selection, null); } - foreach (var control in _objects) + entities.Sort((a, b) => { - ObjectList.RemoveChild(control); - } + var valueA = GetComparableValue(a, _headerClicked); + var valueB = GetComparableValue(b, _headerClicked); + return _ascending ? Comparer.Default.Compare(valueA, valueB) : Comparer.Default.Compare(valueB, valueA); + }); - _objects.Clear(); - - foreach (var (name, nent) in entities) + var listData = new List(); + for (int index = 0; index < entities.Count; index++) { - var ctrl = new ObjectsTabEntry(name, nent); - _objects.Add(ctrl); - ObjectList.AddChild(ctrl); - ctrl.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(ctrl, args); + var info = entities[index]; + listData.Add(new ObjectsListData(info, $"{info.Name} {info.Entity}", index % 2 == 0 ? _altColor : _defaultColor)); } + + SearchList.PopulateList(listData); } - protected override void FrameUpdate(FrameEventArgs args) + private void GenerateButton(ListData data, ListContainerButton button) { - base.FrameUpdate(args); - - if (_timing.CurTime < _nextUpdate) + if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor }) return; - // I do not care for precision. - _nextUpdate = _timing.CurTime + _updateFrequency; + var entry = new ObjectsTabEntry(info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor }); + button.ToolTip = $"{info.Name}, {info.Entity}"; + + // Add key binding event handler + entry.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(args, data); + + button.AddChild(entry); + } + + private bool DataFilterCondition(string filter, ListData listData) + { + if (listData is not ObjectsListData { FilteringString: var filteringString }) + return false; + + // If the filter is empty, do not filter out any entries + if (string.IsNullOrEmpty(filter)) + return true; + + return filteringString.Contains(filter, StringComparison.CurrentCultureIgnoreCase); + } + + private object GetComparableValue((string Name, NetEntity Entity) entity, ObjectsTabHeader.Header header) + { + return header switch + { + ObjectsTabHeader.Header.ObjectName => entity.Name, + ObjectsTabHeader.Header.EntityID => entity.Entity.ToString(), + _ => entity.Name + }; + } + + private void HeaderClicked(ObjectsTabHeader.Header header) + { + if (_headerClicked == header) + { + _ascending = !_ascending; + } + else + { + _headerClicked = header; + _ascending = true; + } + ListHeader.UpdateHeaderSymbols(_headerClicked, _ascending); RefreshObjectList(); } @@ -118,3 +184,4 @@ private enum ObjectsTabSelection } } +public record ObjectsListData((string Name, NetEntity Entity) Info, string FilteringString, Color BackgroundColor) : ListData; diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml index 0f6975e3656d..83c4cc5697f7 100644 --- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml @@ -1,6 +1,6 @@ - - + @@ -14,4 +14,4 @@ HorizontalExpand="True" ClipText="True"/> - + diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs index c9b2cd8b572d..aab06c6ccd0f 100644 --- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs @@ -1,19 +1,21 @@ using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; namespace Content.Client.Administration.UI.Tabs.ObjectsTab; [GenerateTypedNameReferences] -public sealed partial class ObjectsTabEntry : ContainerButton +public sealed partial class ObjectsTabEntry : PanelContainer { public NetEntity AssocEntity; - public ObjectsTabEntry(string name, NetEntity nent) + public ObjectsTabEntry(string name, NetEntity nent, StyleBox styleBox) { RobustXamlLoader.Load(this); AssocEntity = nent; EIDLabel.Text = nent.ToString(); NameLabel.Text = name; + BackgroundColorPanel.PanelOverride = styleBox; } } diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml new file mode 100644 index 000000000000..71a1f5c7bc64 --- /dev/null +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml @@ -0,0 +1,21 @@ + + + + + diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs new file mode 100644 index 000000000000..3a91b5b94836 --- /dev/null +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs @@ -0,0 +1,86 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Input; + +namespace Content.Client.Administration.UI.Tabs.ObjectsTab +{ + [GenerateTypedNameReferences] + public sealed partial class ObjectsTabHeader : Control + { + public event Action
    ? OnHeaderClicked; + + private const string ArrowUp = "↑"; + private const string ArrowDown = "↓"; + + public ObjectsTabHeader() + { + RobustXamlLoader.Load(this); + + ObjectNameLabel.OnKeyBindDown += ObjectNameClicked; + EntityIDLabel.OnKeyBindDown += EntityIDClicked; + } + + public Label GetHeader(Header header) + { + return header switch + { + Header.ObjectName => ObjectNameLabel, + Header.EntityID => EntityIDLabel, + _ => throw new ArgumentOutOfRangeException(nameof(header), header, null) + }; + } + + public void ResetHeaderText() + { + ObjectNameLabel.Text = Loc.GetString("object-tab-object-name"); + EntityIDLabel.Text = Loc.GetString("object-tab-entity-id"); + } + + public void UpdateHeaderSymbols(Header headerClicked, bool ascending) + { + ResetHeaderText(); + var arrow = ascending ? ArrowUp : ArrowDown; + GetHeader(headerClicked).Text += $" {arrow}"; + } + + private void HeaderClicked(GUIBoundKeyEventArgs args, Header header) + { + if (args.Function != EngineKeyFunctions.UIClick) + { + return; + } + + OnHeaderClicked?.Invoke(header); + args.Handle(); + } + + private void ObjectNameClicked(GUIBoundKeyEventArgs args) + { + HeaderClicked(args, Header.ObjectName); + } + + private void EntityIDClicked(GUIBoundKeyEventArgs args) + { + HeaderClicked(args, Header.EntityID); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + ObjectNameLabel.OnKeyBindDown -= ObjectNameClicked; + EntityIDLabel.OnKeyBindDown -= EntityIDClicked; + } + } + + public enum Header + { + ObjectName, + EntityID + } + } +} diff --git a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs index a8bfaddecf45..826945e7cc04 100644 --- a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs @@ -53,6 +53,7 @@ public PlayerTab() SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data); RefreshPlayerList(_adminSystem.PlayerList); + } #region Antag Overlay @@ -110,7 +111,9 @@ private void RefreshPlayerList(IReadOnlyList players) _players = players; PlayerCount.Text = $"Players: {_playerMan.PlayerCount}"; - var sortedPlayers = new List(players); + var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList(); + + var sortedPlayers = new List(filteredPlayers); sortedPlayers.Sort(Compare); UpdateHeaderSymbols(); diff --git a/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs b/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs index a7397aff38d8..3d8235591ad4 100644 --- a/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs +++ b/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs @@ -198,9 +198,12 @@ private void PlayerTabEntryKeyBindDown(GUIBoundKeyEventArgs args, ListData? data args.Handle(); } - private void ObjectsTabEntryKeyBindDown(ObjectsTabEntry entry, GUIBoundKeyEventArgs args) + private void ObjectsTabEntryKeyBindDown(GUIBoundKeyEventArgs args, ListData? data) { - var uid = entry.AssocEntity; + if (data is not ObjectsListData { Info: var info }) + return; + + var uid = info.Entity; var function = args.Function; if (function == EngineKeyFunctions.UIClick) diff --git a/Resources/Locale/en-US/administration/ui/tabs/object-tab.ftl b/Resources/Locale/en-US/administration/ui/tabs/object-tab.ftl new file mode 100644 index 000000000000..0288efcaa44a --- /dev/null +++ b/Resources/Locale/en-US/administration/ui/tabs/object-tab.ftl @@ -0,0 +1,2 @@ +object-tab-entity-id = Entity ID +object-tab-object-name = Object name From 180f1e8005f9e7aff120ee42db1c4bc3ba027541 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 6 Jun 2024 06:42:18 +0000 Subject: [PATCH 322/568] Automatic changelog update --- Resources/Changelog/Admin.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 2964e8c11b1e..16d4e1fe297f 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -281,5 +281,14 @@ Entries: id: 34 time: '2024-06-01T08:14:44.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28451 +- author: Repo + changes: + - message: Sorting and search to objects tab. + type: Add + - message: Disconnected people showing player tab + type: Fix + id: 35 + time: '2024-06-06T06:41:11.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28609 Name: Admin Order: 1 From dec1c281a88845192080b47743803c10afc5adce Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Thu, 6 Jun 2024 00:01:45 -0700 Subject: [PATCH 323/568] Internals are kept on as long as any breathing tool is on (#28595) --- .../AtmosphereSystem.BreathTool.cs | 14 ++++----- .../Atmos/EntitySystems/GasTankSystem.cs | 2 +- .../Body/Components/InternalsComponent.cs | 2 +- .../Body/Systems/InternalsSystem.cs | 30 ++++++++----------- Content.Server/Body/Systems/LungSystem.cs | 2 +- 5 files changed, 23 insertions(+), 27 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.BreathTool.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.BreathTool.cs index 741a9341e79e..327804f39a4f 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.BreathTool.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.BreathTool.cs @@ -10,21 +10,21 @@ private void InitializeBreathTool() SubscribeLocalEvent(OnBreathToolShutdown); } - private void OnBreathToolShutdown(EntityUid uid, BreathToolComponent component, ComponentShutdown args) + private void OnBreathToolShutdown(Entity entity, ref ComponentShutdown args) { - DisconnectInternals(component); + DisconnectInternals(entity); } - public void DisconnectInternals(BreathToolComponent component) + public void DisconnectInternals(Entity entity) { - var old = component.ConnectedInternalsEntity; - component.ConnectedInternalsEntity = null; + var old = entity.Comp.ConnectedInternalsEntity; + entity.Comp.ConnectedInternalsEntity = null; if (TryComp(old, out var internalsComponent)) { - _internals.DisconnectBreathTool((old.Value, internalsComponent)); + _internals.DisconnectBreathTool((old.Value, internalsComponent), entity.Owner); } - component.IsFunctional = false; + entity.Comp.IsFunctional = false; } } diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index 07594820fcc4..baad739804b8 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -220,7 +220,7 @@ public GasMixture RemoveAirVolume(Entity gasTank, float volume public bool CanConnectToInternals(GasTankComponent component) { var internals = GetInternalsComponent(component, component.User); - return internals != null && internals.BreathToolEntity != null && !component.IsValveOpen; + return internals != null && internals.BreathTools.Count != 0 && !component.IsValveOpen; } public void ConnectToInternals(Entity ent) diff --git a/Content.Server/Body/Components/InternalsComponent.cs b/Content.Server/Body/Components/InternalsComponent.cs index 098f1789218f..ef908f96553c 100644 --- a/Content.Server/Body/Components/InternalsComponent.cs +++ b/Content.Server/Body/Components/InternalsComponent.cs @@ -13,7 +13,7 @@ public sealed partial class InternalsComponent : Component public EntityUid? GasTankEntity; [ViewVariables] - public EntityUid? BreathToolEntity; + public HashSet BreathTools { get; set; } = new(); /// /// Toggle Internals delay when the target is not you. diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index b79e083bd467..0e568b77cb48 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -44,7 +44,7 @@ public override void Initialize() private void OnStartingGear(EntityUid uid, InternalsComponent component, ref StartingGearEquippedEvent args) { - if (component.BreathToolEntity == null) + if (component.BreathTools.Count == 0) return; if (component.GasTankEntity != null) @@ -111,7 +111,7 @@ public void ToggleInternals( } // If they're not on then check if we have a mask to use - if (internals.BreathToolEntity is null) + if (internals.BreathTools.Count == 0) { _popupSystem.PopupEntity(Loc.GetString("internals-no-breath-tool"), uid, user); return; @@ -178,28 +178,24 @@ private void OnInhaleLocation(Entity ent, ref InhaleLocation _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } } - public void DisconnectBreathTool(Entity ent) + public void DisconnectBreathTool(Entity ent, EntityUid toolEntity) { - var old = ent.Comp.BreathToolEntity; - ent.Comp.BreathToolEntity = null; + ent.Comp.BreathTools.Remove(toolEntity); - if (TryComp(old, out BreathToolComponent? breathTool)) - { - _atmos.DisconnectInternals(breathTool); + if (TryComp(toolEntity, out BreathToolComponent? breathTool)) + _atmos.DisconnectInternals((toolEntity, breathTool)); + + if (ent.Comp.BreathTools.Count == 0) DisconnectTank(ent); - } _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } public void ConnectBreathTool(Entity ent, EntityUid toolEntity) { - if (TryComp(ent.Comp.BreathToolEntity, out BreathToolComponent? tool)) - { - _atmos.DisconnectInternals(tool); - } + if (!ent.Comp.BreathTools.Add(toolEntity)) + return; - ent.Comp.BreathToolEntity = toolEntity; _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } @@ -217,7 +213,7 @@ public void DisconnectTank(InternalsComponent? component) public bool TryConnectTank(Entity ent, EntityUid tankEntity) { - if (ent.Comp.BreathToolEntity is null) + if (ent.Comp.BreathTools.Count == 0) return false; if (TryComp(ent.Comp.GasTankEntity, out GasTankComponent? tank)) @@ -236,14 +232,14 @@ public bool AreInternalsWorking(EntityUid uid, InternalsComponent? component = n public bool AreInternalsWorking(InternalsComponent component) { - return TryComp(component.BreathToolEntity, out BreathToolComponent? breathTool) + return TryComp(component.BreathTools.FirstOrNull(), out BreathToolComponent? breathTool) && breathTool.IsFunctional && HasComp(component.GasTankEntity); } private short GetSeverity(InternalsComponent component) { - if (component.BreathToolEntity is null || !AreInternalsWorking(component)) + if (component.BreathTools.Count == 0 || !AreInternalsWorking(component)) return 2; // If pressure in the tank is below low pressure threshold, flash warning on internals UI diff --git a/Content.Server/Body/Systems/LungSystem.cs b/Content.Server/Body/Systems/LungSystem.cs index 7e58c24f7e4a..a3c185d5cc67 100644 --- a/Content.Server/Body/Systems/LungSystem.cs +++ b/Content.Server/Body/Systems/LungSystem.cs @@ -59,7 +59,7 @@ private void OnMaskToggled(Entity ent, ref ItemMaskToggledE { if (args.IsToggled || args.IsEquip) { - _atmos.DisconnectInternals(ent.Comp); + _atmos.DisconnectInternals(ent); } else { From 8b40d1887c36bab329406732243e2aaa0152ec7c Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 6 Jun 2024 07:02:51 +0000 Subject: [PATCH 324/568] 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 25f48ec5f798..43fd543c1709 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Terraspark4941 - changes: - - message: The TEG page in the guidebook has been updated with proper information! - type: Tweak - id: 6188 - time: '2024-03-18T21:33:07.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26170 - author: Plykiya changes: - message: You can now craft ducky slippers. @@ -3849,3 +3842,11 @@ id: 6687 time: '2024-06-05T22:08:41.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28591 +- author: Plykiya + changes: + - message: Internals are no longer toggled off if you take your helmet off but still + have a gas mask on and vice versa. + type: Tweak + id: 6688 + time: '2024-06-06T07:01:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28595 From 1333b48747ae62c7b3ecb809b897201a24fe4441 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Thu, 6 Jun 2024 03:11:26 -0400 Subject: [PATCH 325/568] Convert rules to use guidebook parsing (#28647) --- Content.Client/Entry/EntryPoint.cs | 4 - .../Guidebook/DocumentParsingManager.cs | 19 + Content.Client/Info/InfoSystem.cs | 25 - Content.Client/Info/RulesAndInfoWindow.cs | 14 +- Content.Client/Info/RulesControl.xaml | 24 +- Content.Client/Info/RulesControl.xaml.cs | 45 +- Content.Client/Info/RulesManager.cs | 105 - Content.Client/Info/RulesPopup.xaml | 10 +- Content.Client/Info/RulesPopup.xaml.cs | 2 - Content.Client/IoC/ClientContentIoC.cs | 2 - .../Systems/Info/InfoUIController.cs | 101 +- ...0606065731_RemoveLastReadRules.Designer.cs | 1909 +++++++++++++++++ .../20240606065731_RemoveLastReadRules.cs | 29 + .../PostgresServerDbContextModelSnapshot.cs | 4 - ...0606065717_RemoveLastReadRules.Designer.cs | 1834 ++++++++++++++++ .../20240606065717_RemoveLastReadRules.cs | 29 + .../SqliteServerDbContextModelSnapshot.cs | 4 - Content.Server.Database/Model.cs | 2 - .../Administration/Managers/AdminManager.cs | 2 + Content.Server/Database/ServerDbBase.cs | 24 - Content.Server/Database/ServerDbManager.cs | 19 - Content.Server/Entry/EntryPoint.cs | 2 - Content.Server/Info/InfoSystem.cs | 35 - Content.Server/Info/RulesManager.cs | 48 - Content.Server/Info/ShowRulesCommand.cs | 14 +- Content.Server/IoC/ServerContentIoC.cs | 2 - Content.Shared/CCVar/CCVars.cs | 18 +- Content.Shared/Info/RulesMessages.cs | 25 + Content.Shared/Info/SharedInfo.cs | 28 - Content.Shared/Info/SharedRulesManager.cs | 60 - .../ConfigPresets/WizardsDen/salamander.toml | 3 +- .../ConfigPresets/WizardsDen/wizardsDen.toml | 3 + Resources/Locale/en-US/info/rules.ftl | 3 + 33 files changed, 4035 insertions(+), 413 deletions(-) delete mode 100644 Content.Client/Info/InfoSystem.cs delete mode 100644 Content.Client/Info/RulesManager.cs create mode 100644 Content.Server.Database/Migrations/Postgres/20240606065731_RemoveLastReadRules.Designer.cs create mode 100644 Content.Server.Database/Migrations/Postgres/20240606065731_RemoveLastReadRules.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20240606065717_RemoveLastReadRules.Designer.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20240606065717_RemoveLastReadRules.cs delete mode 100644 Content.Server/Info/InfoSystem.cs delete mode 100644 Content.Server/Info/RulesManager.cs create mode 100644 Content.Shared/Info/RulesMessages.cs delete mode 100644 Content.Shared/Info/SharedInfo.cs delete mode 100644 Content.Shared/Info/SharedRulesManager.cs diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 51210a8a93d4..dd7e781f0bb4 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -2,11 +2,9 @@ using Content.Client.Changelog; using Content.Client.Chat.Managers; using Content.Client.Eui; -using Content.Client.Flash; using Content.Client.Fullscreen; using Content.Client.GhostKick; using Content.Client.Guidebook; -using Content.Client.Info; using Content.Client.Input; using Content.Client.IoC; using Content.Client.Launcher; @@ -52,7 +50,6 @@ public sealed class EntryPoint : GameClient [Dependency] private readonly IScreenshotHook _screenshotHook = default!; [Dependency] private readonly FullscreenHook _fullscreenHook = default!; [Dependency] private readonly ChangelogManager _changelogManager = default!; - [Dependency] private readonly RulesManager _rulesManager = default!; [Dependency] private readonly ViewportManager _viewportManager = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; [Dependency] private readonly IInputManager _inputManager = default!; @@ -125,7 +122,6 @@ public override void Init() _screenshotHook.Initialize(); _fullscreenHook.Initialize(); _changelogManager.Initialize(); - _rulesManager.Initialize(); _viewportManager.Initialize(); _ghostKick.Initialize(); _extendedDisconnectInformation.Initialize(); diff --git a/Content.Client/Guidebook/DocumentParsingManager.cs b/Content.Client/Guidebook/DocumentParsingManager.cs index b81866a62601..9c9e569eb417 100644 --- a/Content.Client/Guidebook/DocumentParsingManager.cs +++ b/Content.Client/Guidebook/DocumentParsingManager.cs @@ -2,6 +2,8 @@ using Content.Client.Guidebook.Richtext; using Pidgin; using Robust.Client.UserInterface; +using Robust.Shared.ContentPack; +using Robust.Shared.Prototypes; using Robust.Shared.Reflection; using Robust.Shared.Sandboxing; using static Pidgin.Parser; @@ -13,7 +15,9 @@ namespace Content.Client.Guidebook; /// public sealed partial class DocumentParsingManager { + [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IReflectionManager _reflectionManager = default!; + [Dependency] private readonly IResourceManager _resourceManager = default!; [Dependency] private readonly ISandboxHelper _sandboxHelper = default!; private readonly Dictionary> _tagControlParsers = new(); @@ -37,6 +41,21 @@ public void Initialize() ControlParser = SkipWhitespaces.Then(_controlParser.Many()); } + public bool TryAddMarkup(Control control, ProtoId entryId, bool log = true) + { + if (!_prototype.TryIndex(entryId, out var entry)) + return false; + + using var file = _resourceManager.ContentFileReadText(entry.Text); + return TryAddMarkup(control, file.ReadToEnd(), log); + } + + public bool TryAddMarkup(Control control, GuideEntry entry, bool log = true) + { + using var file = _resourceManager.ContentFileReadText(entry.Text); + return TryAddMarkup(control, file.ReadToEnd(), log); + } + public bool TryAddMarkup(Control control, string text, bool log = true) { try diff --git a/Content.Client/Info/InfoSystem.cs b/Content.Client/Info/InfoSystem.cs deleted file mode 100644 index b69799498187..000000000000 --- a/Content.Client/Info/InfoSystem.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Content.Shared.Info; -using Robust.Shared.Log; - -namespace Content.Client.Info; - -public sealed class InfoSystem : EntitySystem -{ - public RulesMessage Rules = new RulesMessage("Server Rules", "The server did not send any rules."); - [Dependency] private readonly RulesManager _rules = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeNetworkEvent(OnRulesReceived); - Log.Debug("Requested server info."); - RaiseNetworkEvent(new RequestRulesMessage()); - } - - private void OnRulesReceived(RulesMessage message, EntitySessionEventArgs eventArgs) - { - Log.Debug("Received server rules."); - Rules = message; - _rules.UpdateRules(); - } -} diff --git a/Content.Client/Info/RulesAndInfoWindow.cs b/Content.Client/Info/RulesAndInfoWindow.cs index 7a763a1d6f4b..b9131dcb3c07 100644 --- a/Content.Client/Info/RulesAndInfoWindow.cs +++ b/Content.Client/Info/RulesAndInfoWindow.cs @@ -1,10 +1,8 @@ using System.Numerics; using Content.Client.UserInterface.Systems.EscapeMenu; -using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; -using Robust.Shared.Configuration; using Robust.Shared.ContentPack; namespace Content.Client.Info @@ -12,7 +10,6 @@ namespace Content.Client.Info public sealed class RulesAndInfoWindow : DefaultWindow { [Dependency] private readonly IResourceManager _resourceManager = default!; - [Dependency] private readonly RulesManager _rules = default!; public RulesAndInfoWindow() { @@ -22,8 +19,14 @@ public RulesAndInfoWindow() var rootContainer = new TabContainer(); - var rulesList = new Info(); - var tutorialList = new Info(); + var rulesList = new RulesControl + { + Margin = new Thickness(10) + }; + var tutorialList = new Info + { + Margin = new Thickness(10) + }; rootContainer.AddChild(rulesList); rootContainer.AddChild(tutorialList); @@ -31,7 +34,6 @@ public RulesAndInfoWindow() TabContainer.SetTabTitle(rulesList, Loc.GetString("ui-info-tab-rules")); TabContainer.SetTabTitle(tutorialList, Loc.GetString("ui-info-tab-tutorial")); - AddSection(rulesList, _rules.RulesSection()); PopulateTutorial(tutorialList); Contents.AddChild(rootContainer); diff --git a/Content.Client/Info/RulesControl.xaml b/Content.Client/Info/RulesControl.xaml index 3b2476468846..04fa7191234e 100644 --- a/Content.Client/Info/RulesControl.xaml +++ b/Content.Client/Info/RulesControl.xaml @@ -1,6 +1,18 @@ - + + + + + + +