From 4cd2fbd076ac09d93e5258bf2b822360c77d1ea1 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 29 Mar 2024 05:00:09 -0400 Subject: [PATCH 001/133] Add prediction to Spill Container verb, add dummy TrySpill methods to shared (#25813) * Moved abstract spill methods to shared; added prediction to spill container verb. * Rerun tests * Requested changes * Note Client behavior in Spill method docs --- Content.Client/Fluids/PuddleSystem.cs | 38 +++++++++++- .../Components/PreventSpillerComponent.cs | 7 --- .../EntitySystems/PuddleSystem.Spillable.cs | 62 +------------------ .../Fluids/EntitySystems/PuddleSystem.cs | 27 +++----- .../Components/PreventSpillerComponent.cs | 12 ++++ .../Fluids/Components/SpillableComponent.cs | 6 ++ .../Fluids/SharedPuddleSystem.Spillable.cs | 60 ++++++++++++++++++ Content.Shared/Fluids/SharedPuddleSystem.cs | 53 ++++++++++++++++ 8 files changed, 178 insertions(+), 87 deletions(-) delete mode 100644 Content.Server/Fluids/Components/PreventSpillerComponent.cs create mode 100644 Content.Shared/Fluids/Components/PreventSpillerComponent.cs diff --git a/Content.Client/Fluids/PuddleSystem.cs b/Content.Client/Fluids/PuddleSystem.cs index 54b1d5b86b9..5dbffe0fd2f 100644 --- a/Content.Client/Fluids/PuddleSystem.cs +++ b/Content.Client/Fluids/PuddleSystem.cs @@ -1,7 +1,9 @@ using Content.Client.IconSmoothing; +using Content.Shared.Chemistry.Components; using Content.Shared.Fluids; using Content.Shared.Fluids.Components; using Robust.Client.GameObjects; +using Robust.Shared.Map; namespace Content.Client.Fluids; @@ -21,7 +23,7 @@ private void OnPuddleAppearance(EntityUid uid, PuddleComponent component, ref Ap if (args.Sprite == null) return; - float volume = 1f; + var volume = 1f; if (args.AppearanceData.TryGetValue(PuddleVisuals.CurrentVolume, out var volumeObj)) { @@ -64,4 +66,38 @@ private void OnPuddleAppearance(EntityUid uid, PuddleComponent component, ref Ap args.Sprite.Color *= baseColor; } } + + #region Spill + + // Maybe someday we'll have clientside prediction for entity spawning, but not today. + // Until then, these methods do nothing on the client. + /// + public override bool TrySplashSpillAt(EntityUid uid, EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true, EntityUid? user = null) + { + puddleUid = EntityUid.Invalid; + return false; + } + + /// + public override bool TrySpillAt(EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true) + { + puddleUid = EntityUid.Invalid; + return false; + } + + /// + public override bool TrySpillAt(EntityUid uid, Solution solution, out EntityUid puddleUid, bool sound = true, TransformComponent? transformComponent = null) + { + puddleUid = EntityUid.Invalid; + return false; + } + + /// + public override bool TrySpillAt(TileRef tileRef, Solution solution, out EntityUid puddleUid, bool sound = true, bool tileReact = true) + { + puddleUid = EntityUid.Invalid; + return false; + } + + #endregion Spill } diff --git a/Content.Server/Fluids/Components/PreventSpillerComponent.cs b/Content.Server/Fluids/Components/PreventSpillerComponent.cs deleted file mode 100644 index 37096f1bb3c..00000000000 --- a/Content.Server/Fluids/Components/PreventSpillerComponent.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Content.Server.Fluids.Components; - -[RegisterComponent] -public sealed partial class PreventSpillerComponent : Component -{ - -} diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs index efaca271d3b..ce5b5b36378 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs @@ -1,5 +1,4 @@ using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Fluids.Components; using Content.Server.Nutrition.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; @@ -8,7 +7,6 @@ using Content.Shared.Clothing.Components; using Content.Shared.CombatMode.Pacification; using Content.Shared.Database; -using Content.Shared.DoAfter; using Content.Shared.FixedPoint; using Content.Shared.Fluids.Components; using Content.Shared.IdentityManagement; @@ -16,7 +14,6 @@ using Content.Shared.Popups; using Content.Shared.Spillable; using Content.Shared.Throwing; -using Content.Shared.Verbs; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Player; @@ -24,9 +21,6 @@ namespace Content.Server.Fluids.EntitySystems; public sealed partial class PuddleSystem { - [Dependency] private readonly OpenableSystem _openable = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - protected override void InitializeSpillable() { base.InitializeSpillable(); @@ -34,7 +28,6 @@ protected override void InitializeSpillable() SubscribeLocalEvent(SpillOnLand); // Openable handles the event if it's closed SubscribeLocalEvent(SplashOnMeleeHit, after: [typeof(OpenableSystem)]); - SubscribeLocalEvent>(AddSpillVerb); SubscribeLocalEvent(OnGotEquipped); SubscribeLocalEvent(OnOverflow); SubscribeLocalEvent(OnDoAfter); @@ -134,7 +127,7 @@ private void SpillOnLand(Entity entity, ref LandEvent args) if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution)) return; - if (_openable.IsClosed(entity.Owner)) + if (Openable.IsClosed(entity.Owner)) return; if (args.User != null) @@ -153,7 +146,7 @@ private void SpillOnLand(Entity entity, ref LandEvent args) private void OnAttemptPacifiedThrow(Entity ent, ref AttemptPacifiedThrowEvent args) { // Don’t care about closed containers. - if (_openable.IsClosed(ent)) + if (Openable.IsClosed(ent)) return; // Don’t care about empty containers. @@ -163,57 +156,6 @@ private void OnAttemptPacifiedThrow(Entity ent, ref AttemptP args.Cancel("pacified-cannot-throw-spill"); } - private void AddSpillVerb(Entity entity, ref GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - if (!_solutionContainerSystem.TryGetSolution(args.Target, entity.Comp.SolutionName, out var soln, out var solution)) - return; - - if (_openable.IsClosed(args.Target)) - return; - - if (solution.Volume == FixedPoint2.Zero) - return; - - if (_entityManager.HasComponent(args.User)) - return; - - - Verb verb = new() - { - Text = Loc.GetString("spill-target-verb-get-data-text") - }; - - // TODO VERB ICONS spill icon? pouring out a glass/beaker? - if (entity.Comp.SpillDelay == null) - { - var target = args.Target; - verb.Act = () => - { - var puddleSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume); - TrySpillAt(Transform(target).Coordinates, puddleSolution, out _); - }; - } - else - { - var user = args.User; - verb.Act = () => - { - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, entity.Comp.SpillDelay ?? 0, new SpillDoAfterEvent(), entity.Owner, target: entity.Owner) - { - BreakOnDamage = true, - BreakOnMove = true, - NeedHand = true, - }); - }; - } - verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately. - verb.DoContactInteraction = true; - args.Verbs.Add(verb); - } - private void OnDoAfter(Entity entity, ref SpillDoAfterEvent args) { if (args.Handled || args.Cancelled || args.Args.Target == null) diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index e3481f98da3..923210cc73c 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -46,7 +46,6 @@ public sealed partial class PuddleSystem : SharedPuddleSystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; [Dependency] private readonly AudioSystem _audio = default!; - [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly ReactiveSystem _reactive = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; @@ -551,11 +550,8 @@ public Solution GetOverflowSolution(EntityUid uid, PuddleComponent? puddle = nul #region Spill - /// - /// First splashes reagent on reactive entities near the spilling entity, then spills the rest regularly to a - /// puddle. This is intended for 'destructive' spills, like when entities are destroyed or thrown. - /// - public bool TrySplashSpillAt(EntityUid uid, + /// + public override bool TrySplashSpillAt(EntityUid uid, EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, @@ -600,11 +596,8 @@ public bool TrySplashSpillAt(EntityUid uid, return TrySpillAt(coordinates, solution, out puddleUid, sound); } - /// - /// Spills solution at the specified coordinates. - /// Will add to an existing puddle if present or create a new one if not. - /// - public bool TrySpillAt(EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true) + /// + public override bool TrySpillAt(EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true) { if (solution.Volume == 0) { @@ -622,10 +615,8 @@ public bool TrySpillAt(EntityCoordinates coordinates, Solution solution, out Ent return TrySpillAt(_map.GetTileRef(gridUid.Value, mapGrid, coordinates), solution, out puddleUid, sound); } - /// - /// - /// - public bool TrySpillAt(EntityUid uid, Solution solution, out EntityUid puddleUid, bool sound = true, + /// + public override bool TrySpillAt(EntityUid uid, Solution solution, out EntityUid puddleUid, bool sound = true, TransformComponent? transformComponent = null) { if (!Resolve(uid, ref transformComponent, false)) @@ -637,10 +628,8 @@ public bool TrySpillAt(EntityUid uid, Solution solution, out EntityUid puddleUid return TrySpillAt(transformComponent.Coordinates, solution, out puddleUid, sound: sound); } - /// - /// - /// - public bool TrySpillAt(TileRef tileRef, Solution solution, out EntityUid puddleUid, bool sound = true, + /// + public override bool TrySpillAt(TileRef tileRef, Solution solution, out EntityUid puddleUid, bool sound = true, bool tileReact = true) { if (solution.Volume <= 0) diff --git a/Content.Shared/Fluids/Components/PreventSpillerComponent.cs b/Content.Shared/Fluids/Components/PreventSpillerComponent.cs new file mode 100644 index 00000000000..e396d9faf52 --- /dev/null +++ b/Content.Shared/Fluids/Components/PreventSpillerComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Fluids.Components; + +/// +/// Blocks this entity's ability to spill solution containing entities via the verb menu. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PreventSpillerComponent : Component +{ + +} diff --git a/Content.Shared/Fluids/Components/SpillableComponent.cs b/Content.Shared/Fluids/Components/SpillableComponent.cs index a1b5fa17eb8..428d91f2de7 100644 --- a/Content.Shared/Fluids/Components/SpillableComponent.cs +++ b/Content.Shared/Fluids/Components/SpillableComponent.cs @@ -2,6 +2,12 @@ namespace Content.Shared.Fluids.Components; +/// +/// Makes a solution contained in this entity spillable. +/// Spills can occur when a container with this component overflows, +/// is used to melee attack something, is equipped (see ), +/// lands after being thrown, or has the Spill verb used. +/// [RegisterComponent] public sealed partial class SpillableComponent : Component { diff --git a/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs b/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs index 77730e5afc5..1e9e742a38f 100644 --- a/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs +++ b/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs @@ -1,14 +1,23 @@ +using Content.Shared.Database; +using Content.Shared.DoAfter; using Content.Shared.Examine; +using Content.Shared.FixedPoint; using Content.Shared.Fluids.Components; +using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Spillable; +using Content.Shared.Verbs; using Content.Shared.Weapons.Melee; namespace Content.Shared.Fluids; public abstract partial class SharedPuddleSystem { + [Dependency] protected readonly SharedOpenableSystem Openable = default!; + protected virtual void InitializeSpillable() { SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent>(AddSpillVerb); } private void OnExamined(Entity entity, ref ExaminedEvent args) @@ -21,4 +30,55 @@ private void OnExamined(Entity entity, ref ExaminedEvent arg args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon")); } } + + private void AddSpillVerb(Entity entity, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + if (!_solutionContainerSystem.TryGetSolution(args.Target, entity.Comp.SolutionName, out var soln, out var solution)) + return; + + if (Openable.IsClosed(args.Target)) + return; + + if (solution.Volume == FixedPoint2.Zero) + return; + + if (HasComp(args.User)) + return; + + + Verb verb = new() + { + Text = Loc.GetString("spill-target-verb-get-data-text") + }; + + // TODO VERB ICONS spill icon? pouring out a glass/beaker? + if (entity.Comp.SpillDelay == null) + { + var target = args.Target; + verb.Act = () => + { + var puddleSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume); + TrySpillAt(Transform(target).Coordinates, puddleSolution, out _); + }; + } + else + { + var user = args.User; + verb.Act = () => + { + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, entity.Comp.SpillDelay ?? 0, new SpillDoAfterEvent(), entity.Owner, target: entity.Owner) + { + BreakOnDamage = true, + BreakOnMove = true, + NeedHand = true, + }); + }; + } + verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately. + verb.DoContactInteraction = true; + args.Verbs.Add(verb); + } } diff --git a/Content.Shared/Fluids/SharedPuddleSystem.cs b/Content.Shared/Fluids/SharedPuddleSystem.cs index e4bd61baa8e..f573c042c55 100644 --- a/Content.Shared/Fluids/SharedPuddleSystem.cs +++ b/Content.Shared/Fluids/SharedPuddleSystem.cs @@ -1,12 +1,14 @@ using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; +using Content.Shared.DoAfter; using Content.Shared.DragDrop; using Content.Shared.Examine; using Content.Shared.FixedPoint; using Content.Shared.Fluids.Components; using Content.Shared.Movement.Events; using Content.Shared.StepTrigger.Components; +using Robust.Shared.Map; using Robust.Shared.Prototypes; namespace Content.Shared.Fluids; @@ -15,6 +17,7 @@ public abstract partial class SharedPuddleSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; /// /// The lowest threshold to be considered for puddle sprite states as well as slipperiness of a puddle. @@ -106,4 +109,54 @@ private void HandlePuddleExamined(Entity entity, ref ExaminedEv args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no")); } } + + #region Spill + // These methods are in Shared to make it easier to interact with PuddleSystem in Shared code. + // Note that they always fail when run on the client, not creating a puddle and returning false. + // Adding proper prediction to this system would require spawning temporary puddle entities on the + // client and replacing or merging them with the ones spawned by the server when the client goes to + // replicate those, and I am not enough of a wizard to attempt implementing that. + + /// + /// First splashes reagent on reactive entities near the spilling entity, then spills the rest regularly to a + /// puddle. This is intended for 'destructive' spills, like when entities are destroyed or thrown. + /// + /// + /// On the client, this will always set to and return false. + /// + public abstract bool TrySplashSpillAt(EntityUid uid, + EntityCoordinates coordinates, + Solution solution, + out EntityUid puddleUid, + bool sound = true, + EntityUid? user = null); + + /// + /// Spills solution at the specified coordinates. + /// Will add to an existing puddle if present or create a new one if not. + /// + /// + /// On the client, this will always set to and return false. + /// + public abstract bool TrySpillAt(EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true); + + /// + /// + /// + /// + /// On the client, this will always set to and return false. + /// + public abstract bool TrySpillAt(EntityUid uid, Solution solution, out EntityUid puddleUid, bool sound = true, + TransformComponent? transformComponent = null); + + /// + /// + /// + /// + /// On the client, this will always set to and return false. + /// + public abstract bool TrySpillAt(TileRef tileRef, Solution solution, out EntityUid puddleUid, bool sound = true, + bool tileReact = true); + + #endregion Spill } From 28d05feea729485e934b488b8508f8f1f0d546ee Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 30 Mar 2024 03:03:01 +1100 Subject: [PATCH 002/133] Fix disposals test (#26535) The rounding at 0,0 got memed. --- .../Tests/Disposal/DisposalUnitTest.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs index 976fc2eceb5..9109fdbe4f7 100644 --- a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs +++ b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs @@ -163,7 +163,6 @@ public async Task Test() var entityManager = server.ResolveDependency(); var xformSystem = entityManager.System(); var disposalSystem = entityManager.System(); - await server.WaitAssertion(() => { // Spawn the entities @@ -171,8 +170,7 @@ await server.WaitAssertion(() => human = entityManager.SpawnEntity("HumanDisposalDummy", coordinates); wrench = entityManager.SpawnEntity("WrenchDummy", coordinates); disposalUnit = entityManager.SpawnEntity("DisposalUnitDummy", coordinates); - disposalTrunk = entityManager.SpawnEntity("DisposalTrunkDummy", - entityManager.GetComponent(disposalUnit).MapPosition); + disposalTrunk = entityManager.SpawnEntity("DisposalTrunkDummy", coordinates); // Test for components existing unitUid = disposalUnit; @@ -204,10 +202,10 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - // Move the disposal trunk away - var xform = entityManager.GetComponent(disposalTrunk); var worldPos = xformSystem.GetWorldPosition(disposalTrunk); - xformSystem.SetWorldPosition(xform, worldPos + new Vector2(1, 0)); + + // Move the disposal trunk away + xformSystem.SetWorldPosition(disposalTrunk, worldPos + new Vector2(1, 0)); // Fail to flush with a mob and an item Flush(disposalUnit, unitComponent, false, disposalSystem, human, wrench); @@ -215,10 +213,12 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - // Move the disposal trunk back var xform = entityManager.GetComponent(disposalTrunk); - var worldPos = xformSystem.GetWorldPosition(disposalTrunk); - xformSystem.SetWorldPosition(xform, worldPos - new Vector2(1, 0)); + var worldPos = xformSystem.GetWorldPosition(disposalUnit); + + // Move the disposal trunk back + xformSystem.SetWorldPosition(disposalTrunk, worldPos); + xformSystem.AnchorEntity((disposalTrunk, xform)); // Fail to flush with a mob and an item, no power Flush(disposalUnit, unitComponent, false, disposalSystem, human, wrench); @@ -240,6 +240,7 @@ await server.WaitAssertion(() => // Re-pressurizing Flush(disposalUnit, unitComponent, false, disposalSystem); }); + await pair.CleanReturnAsync(); } } From 355d00a0f2529a5beac6426837fad129d45a41a6 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Fri, 29 Mar 2024 23:46:05 +0000 Subject: [PATCH 003/133] combine same-tile explosions in the same tick (#25664) * combine same-tile explosions in the same tick * ! --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../ExplosionSystem.Processing.cs | 26 +++++- .../EntitySystems/ExplosionSystem.cs | 80 ++++++++++++------- .../Explosion/ExplosionPrototype.cs | 7 ++ 3 files changed, 82 insertions(+), 31 deletions(-) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index 60db6bdf5bb..6e100fe656e 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -38,8 +38,15 @@ public sealed partial class ExplosionSystem /// Queue for delayed processing of explosions. If there is an explosion that covers more than tiles, other explosions will actually be delayed slightly. Unless it's a station /// nuke, this delay should never really be noticeable. + /// This is also used to combine explosion intensities of the same kind. /// - private Queue> _explosionQueue = new(); + private Queue _explosionQueue = new(); + + /// + /// All queued explosions that will be processed in . + /// These always have the same contents. + /// + private HashSet _queuedExplosions = new(); /// /// The explosion currently being processed. @@ -93,10 +100,11 @@ public override void Update(float frameTime) if (MathF.Max(MaxProcessingTime - 1, 0.1f) < Stopwatch.Elapsed.TotalMilliseconds) break; - if (!_explosionQueue.TryDequeue(out var spawnNextExplosion)) + if (!_explosionQueue.TryDequeue(out var queued)) break; - _activeExplosion = spawnNextExplosion(); + _queuedExplosions.Remove(queued); + _activeExplosion = SpawnExplosion(queued); // explosion spawning can be null if something somewhere went wrong. (e.g., negative explosion // intensity). @@ -867,3 +875,15 @@ private void SetTiles() _tileUpdateDict.Clear(); } } + +/// +/// Data needed to spawn an explosion with . +/// +public sealed class QueuedExplosion +{ + public MapCoordinates Epicenter; + public ExplosionPrototype Proto = new(); + public float TotalIntensity, Slope, MaxTileIntensity, TileBreakScale; + public int MaxTileBreak; + public bool CanCreateVacuum; +} diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index 46238fd7c5d..23543895e6f 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -112,6 +112,7 @@ public override void Initialize() private void OnReset(RoundRestartCleanupEvent ev) { _explosionQueue.Clear(); + _queuedExplosions.Clear(); if (_activeExplosion != null) QueueDel(_activeExplosion.VisualEnt); _activeExplosion = null; @@ -297,8 +298,36 @@ public void QueueExplosion(MapCoordinates epicenter, if (addLog) // dont log if already created a separate, more detailed, log. _adminLogger.Add(LogType.Explosion, LogImpact.High, $"Explosion ({typeId}) spawned at {epicenter:coordinates} with intensity {totalIntensity} slope {slope}"); - _explosionQueue.Enqueue(() => SpawnExplosion(epicenter, type, totalIntensity, - slope, maxTileIntensity, tileBreakScale, maxTileBreak, canCreateVacuum)); + // try to combine explosions on the same tile if they are the same type + foreach (var queued in _queuedExplosions) + { + // ignore different types or those on different maps + if (queued.Proto.ID != type.ID || queued.Epicenter.MapId != epicenter.MapId) + continue; + + var dst2 = queued.Proto.MaxCombineDistance * queued.Proto.MaxCombineDistance; + var direction = queued.Epicenter.Position - epicenter.Position; + if (direction.LengthSquared() > dst2) + continue; + + // they are close enough to combine so just add total intensity and prevent queuing another one + queued.TotalIntensity += totalIntensity; + return; + } + + var boom = new QueuedExplosion() + { + Epicenter = epicenter, + Proto = type, + TotalIntensity = totalIntensity, + Slope = slope, + MaxTileIntensity = maxTileIntensity, + TileBreakScale = tileBreakScale, + MaxTileBreak = maxTileBreak, + CanCreateVacuum = canCreateVacuum + }; + _explosionQueue.Enqueue(boom); + _queuedExplosions.Add(boom); } /// @@ -306,32 +335,26 @@ public void QueueExplosion(MapCoordinates epicenter, /// information about the affected tiles for the explosion system to process. It will also trigger the /// camera shake and sound effect. /// - private Explosion? SpawnExplosion(MapCoordinates epicenter, - ExplosionPrototype type, - float totalIntensity, - float slope, - float maxTileIntensity, - float tileBreakScale, - int maxTileBreak, - bool canCreateVacuum) + private Explosion? SpawnExplosion(QueuedExplosion queued) { - if (!_mapManager.MapExists(epicenter.MapId)) + var pos = queued.Epicenter; + if (!_mapManager.MapExists(pos.MapId)) return null; - var results = GetExplosionTiles(epicenter, type.ID, totalIntensity, slope, maxTileIntensity); + var results = GetExplosionTiles(pos, queued.Proto.ID, queued.TotalIntensity, queued.Slope, queued.MaxTileIntensity); if (results == null) return null; var (area, iterationIntensity, spaceData, gridData, spaceMatrix) = results.Value; - var visualEnt = CreateExplosionVisualEntity(epicenter, type.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity); + var visualEnt = CreateExplosionVisualEntity(pos, queued.Proto.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity); // camera shake - CameraShake(iterationIntensity.Count * 4f, epicenter, totalIntensity); + CameraShake(iterationIntensity.Count * 4f, pos, queued.TotalIntensity); //For whatever bloody reason, sound system requires ENTITY coordinates. - var mapEntityCoords = EntityCoordinates.FromMap(_mapManager.GetMapEntityId(epicenter.MapId), epicenter, _transformSystem, EntityManager); + var mapEntityCoords = EntityCoordinates.FromMap(_mapManager.GetMapEntityId(pos.MapId), pos, _transformSystem, EntityManager); // play sound. // for the normal audio, we want everyone in pvs range @@ -339,34 +362,35 @@ public void QueueExplosion(MapCoordinates epicenter, // this is capped to 30 because otherwise really huge bombs // will attempt to play regular audio for people who can't hear it anyway because the epicenter is so far away var audioRange = Math.Min(iterationIntensity.Count * 2, MaxExplosionAudioRange); - var filter = Filter.Pvs(epicenter).AddInRange(epicenter, audioRange); - var sound = iterationIntensity.Count < type.SmallSoundIterationThreshold - ? type.SmallSound - : type.Sound; + var filter = Filter.Pvs(pos).AddInRange(pos, audioRange); + var sound = iterationIntensity.Count < queued.Proto.SmallSoundIterationThreshold + ? queued.Proto.SmallSound + : queued.Proto.Sound; _audio.PlayStatic(sound, filter, mapEntityCoords, true, sound.Params); // play far sound // far sound should play for anyone who wasn't in range of any of the effects of the bomb var farAudioRange = iterationIntensity.Count * 5; - var farFilter = Filter.Empty().AddInRange(epicenter, farAudioRange).RemoveInRange(epicenter, audioRange); - var farSound = iterationIntensity.Count < type.SmallSoundIterationThreshold - ? type.SmallSoundFar - : type.SoundFar; + var farFilter = Filter.Empty().AddInRange(pos, farAudioRange).RemoveInRange(pos, audioRange); + var farSound = iterationIntensity.Count < queued.Proto.SmallSoundIterationThreshold + ? queued.Proto.SmallSoundFar + : queued.Proto.SoundFar; _audio.PlayGlobal(farSound, farFilter, true, farSound.Params); return new Explosion(this, - type, + queued.Proto, spaceData, gridData.Values.ToList(), iterationIntensity, - epicenter, + pos, spaceMatrix, area, - tileBreakScale, - maxTileBreak, - canCreateVacuum, + // TODO: instead of le copy paste fields refactor so it has QueuedExplosion as a field? + queued.TileBreakScale, + queued.MaxTileBreak, + queued.CanCreateVacuum, EntityManager, _mapManager, visualEnt); diff --git a/Content.Shared/Explosion/ExplosionPrototype.cs b/Content.Shared/Explosion/ExplosionPrototype.cs index ec9f2efa1e1..df2fb18360e 100644 --- a/Content.Shared/Explosion/ExplosionPrototype.cs +++ b/Content.Shared/Explosion/ExplosionPrototype.cs @@ -77,6 +77,13 @@ public sealed partial class ExplosionPrototype : IPrototype [DataField("smallSoundIterationThreshold")] public int SmallSoundIterationThreshold = 6; + /// + /// How far away another explosion in the same tick can be and be combined. + /// Total intensity is added to the original queued explosion. + /// + [DataField] + public float MaxCombineDistance = 1f; + [DataField("sound")] public SoundSpecifier Sound = new SoundCollectionSpecifier("Explosion"); From 3a3652a767106b3111f93a0a0d2964d5259c2491 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 29 Mar 2024 23:47:13 +0000 Subject: [PATCH 004/133] 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 5e5c44dd72b..f3495edc010 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Agoichi - changes: - - message: Rebalanced Lobbying Bundle - type: Tweak - id: 5751 - time: '2024-01-20T02:35:44.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24303 - author: Dygon changes: - message: Storage objects can't be opened anymore while stored in a container. @@ -3793,3 +3786,11 @@ id: 6250 time: '2024-03-29T06:30:51.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26290 +- author: deltanedas + changes: + - message: Multiple bombs exploding at once on a tile now combine to have a larger + explosion rather than stacking the same small explosion. + type: Tweak + id: 6251 + time: '2024-03-29T23:46:06.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25664 From 985c4b40163a71737d229aa1c4513a93da8a6d23 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sat, 30 Mar 2024 12:58:12 +1300 Subject: [PATCH 005/133] Fix empty atmos deserialization (#26540) Fix atmos deserialization --- .../Serialization/TileAtmosCollectionSerializer.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Content.Server/Atmos/Serialization/TileAtmosCollectionSerializer.cs b/Content.Server/Atmos/Serialization/TileAtmosCollectionSerializer.cs index 71e4c2d0def..00be83e86d9 100644 --- a/Content.Server/Atmos/Serialization/TileAtmosCollectionSerializer.cs +++ b/Content.Server/Atmos/Serialization/TileAtmosCollectionSerializer.cs @@ -26,7 +26,7 @@ public Dictionary Read(ISerializationManager serializa { node.TryGetValue(new ValueDataNode("version"), out var versionNode); var version = ((ValueDataNode?) versionNode)?.AsInt() ?? 1; - Dictionary tiles; + Dictionary tiles = new(); // Backwards compatability if (version == 1) @@ -36,8 +36,6 @@ public Dictionary Read(ISerializationManager serializa var mixies = serializationManager.Read?>(tile2, hookCtx, context); var unique = serializationManager.Read?>(node["uniqueMixes"], hookCtx, context); - tiles = new Dictionary(); - if (unique != null && mixies != null) { foreach (var (indices, mix) in mixies) @@ -58,15 +56,14 @@ public Dictionary Read(ISerializationManager serializa else { var dataNode = (MappingDataNode) node["data"]; - var tileNode = (MappingDataNode) dataNode["tiles"]; var chunkSize = serializationManager.Read(dataNode["chunkSize"], hookCtx, context); - var unique = serializationManager.Read?>(dataNode["uniqueMixes"], hookCtx, context); - - tiles = new Dictionary(); + dataNode.TryGetValue(new ValueDataNode("uniqueMixes"), out var mixNode); + var unique = mixNode == null ? null : serializationManager.Read?>(mixNode, hookCtx, context); if (unique != null) { + var tileNode = (MappingDataNode) dataNode["tiles"]; foreach (var (chunkNode, valueNode) in tileNode) { var chunkOrigin = serializationManager.Read(chunkNode, hookCtx, context); From 649af483e486e99785e9a1517fa3263c0723034e Mon Sep 17 00:00:00 2001 From: Mephisto72 <66994453+Mephisto72@users.noreply.github.com> Date: Sat, 30 Mar 2024 01:01:39 +0100 Subject: [PATCH 006/133] Increase Ion Storm Weights (#26539) This increases the probability for the following effects caused by Ion Storms: Chance to affect a Station Borg: 50% -> 80% Chance to replace a Law: 10% -> 20% Chance to remove a Law: 10% -> 20% Chance to shuffle Laws: 10% -> 20% --- .../Silicons/Laws/Components/IonStormTargetComponent.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Silicons/Laws/Components/IonStormTargetComponent.cs b/Content.Shared/Silicons/Laws/Components/IonStormTargetComponent.cs index 49ac611ddcb..341767e9cee 100644 --- a/Content.Shared/Silicons/Laws/Components/IonStormTargetComponent.cs +++ b/Content.Shared/Silicons/Laws/Components/IonStormTargetComponent.cs @@ -20,7 +20,7 @@ public sealed partial class IonStormTargetComponent : Component /// Chance for this borg to be affected at all. /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public float Chance = 0.5f; + public float Chance = 0.8f; /// /// Chance to replace the lawset with a random one @@ -32,19 +32,19 @@ public sealed partial class IonStormTargetComponent : Component /// Chance to remove a random law. /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public float RemoveChance = 0.1f; + public float RemoveChance = 0.2f; /// /// Chance to replace a random law with the new one, rather than have it be a glitched-order law. /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public float ReplaceChance = 0.1f; + public float ReplaceChance = 0.2f; /// /// Chance to shuffle laws after everything is done. /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public float ShuffleChance = 0.1f; + public float ShuffleChance = 0.2f; } /// From 86a24db72a2d8208b3e1152a6e03021e3a2fde5d Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 00:02:45 +0000 Subject: [PATCH 007/133] 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 f3495edc010..216ca873049 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Dygon - changes: - - message: Storage objects can't be opened anymore while stored in a container. - type: Fix - id: 5752 - time: '2024-01-20T02:50:14.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24253 - author: FairlySadPanda changes: - message: Lobby restart sound effects no longer cut-off. @@ -3794,3 +3787,10 @@ id: 6251 time: '2024-03-29T23:46:06.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25664 +- author: Mephisto72 + changes: + - message: Ion Storms are now more likely to alter a Borg's laws. + type: Tweak + id: 6252 + time: '2024-03-30T00:01:39.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26539 From 2e9c8f691c6a1e5f8e45899866a9fe443f9f4b50 Mon Sep 17 00:00:00 2001 From: "Alice \"Arimah\" Heurlin" <30327355+arimah@users.noreply.github.com> Date: Sat, 30 Mar 2024 02:25:42 +0100 Subject: [PATCH 008/133] Fix guardian damage transfer (#26541) --- Content.Server/Guardian/GuardianSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs index e58f997b211..97d4eb06803 100644 --- a/Content.Server/Guardian/GuardianSystem.cs +++ b/Content.Server/Guardian/GuardianSystem.cs @@ -256,7 +256,7 @@ private void OnHostStateChange(EntityUid uid, GuardianHostComponent component, M /// private void OnGuardianDamaged(EntityUid uid, GuardianComponent component, DamageChangedEvent args) { - if (args.DamageDelta == null || component.Host == null || component.DamageShare > 0) + if (args.DamageDelta == null || component.Host == null || component.DamageShare == 0) return; _damageSystem.TryChangeDamage( From 72c6a14d593ea6eafc7477292657166968c75401 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 01:26:48 +0000 Subject: [PATCH 009/133] 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 216ca873049..c52c19a50c9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: FairlySadPanda - changes: - - message: Lobby restart sound effects no longer cut-off. - type: Fix - id: 5753 - time: '2024-01-20T03:40:01.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24044 - author: casperr04 changes: - message: Fixed players being able to re-anchor items after fultoning them. @@ -3794,3 +3787,11 @@ id: 6252 time: '2024-03-30T00:01:39.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26539 +- author: arimah + changes: + - message: Holoparasites, holoclowns and other guardians correctly transfer damage + to their hosts again. + type: Fix + id: 6253 + time: '2024-03-30T01:25:43.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26541 From 3b791459c74c6b56c8ae6204a936f6de06674b93 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 30 Mar 2024 02:40:55 +0100 Subject: [PATCH 010/133] Refactor FTL time tracking code to fix a UI bug (#26538) The FTL UI on the shuttle console would reset the FTL progress bar every time you open it. This is because the server only sends "time until completion", not a start/end time. The FTL code now uses a separate start/end time so the exact same progress bar can be preserved. For convenience, I made a StartEndTime record struct that stores the actual tuple. This is now used by the code and has some helpers. --- Content.Client/Shuttles/UI/MapScreen.xaml.cs | 27 ++------ .../Shuttles/Components/FTLComponent.cs | 7 +- .../Shuttles/Systems/ShuttleConsoleSystem.cs | 14 ++-- .../Systems/ShuttleSystem.FasterThanLight.cs | 28 ++++---- .../Shuttles/Systems/ShuttleSystem.cs | 4 +- .../BUIStates/ShuttleMapInterfaceState.cs | 9 +-- Content.Shared/Timing/StartEndTime.cs | 68 +++++++++++++++++++ 7 files changed, 111 insertions(+), 46 deletions(-) create mode 100644 Content.Shared/Timing/StartEndTime.cs diff --git a/Content.Client/Shuttles/UI/MapScreen.xaml.cs b/Content.Client/Shuttles/UI/MapScreen.xaml.cs index 8430699bae1..225d1be14e5 100644 --- a/Content.Client/Shuttles/UI/MapScreen.xaml.cs +++ b/Content.Client/Shuttles/UI/MapScreen.xaml.cs @@ -5,6 +5,7 @@ using Content.Shared.Shuttles.Components; using Content.Shared.Shuttles.Systems; using Content.Shared.Shuttles.UI.MapObjects; +using Content.Shared.Timing; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface; @@ -38,16 +39,11 @@ public sealed partial class MapScreen : BoxContainer private EntityUid? _shuttleEntity; private FTLState _state; - private float _ftlDuration; + private StartEndTime _ftlTime; private List _beacons = new(); private List _exclusions = new(); - /// - /// When the next FTL state change happens. - /// - private TimeSpan _nextFtlTime; - private TimeSpan _nextPing; private TimeSpan _pingCooldown = TimeSpan.FromSeconds(3); private TimeSpan _nextMapDequeue; @@ -114,8 +110,7 @@ public void UpdateState(ShuttleMapInterfaceState state) _beacons = state.Destinations; _exclusions = state.Exclusions; _state = state.FTLState; - _ftlDuration = state.FTLDuration; - _nextFtlTime = _timing.CurTime + TimeSpan.FromSeconds(_ftlDuration); + _ftlTime = state.FTLTime; MapRadar.InFtl = true; MapFTLState.Text = Loc.GetString($"shuttle-console-ftl-state-{_state.ToString()}"); @@ -511,20 +506,8 @@ protected override void FrameUpdate(FrameEventArgs args) MapRebuildButton.Disabled = false; } - var ftlDiff = (float) (_nextFtlTime - _timing.CurTime).TotalSeconds; - - float ftlRatio; - - if (_ftlDuration.Equals(0f)) - { - ftlRatio = 1f; - } - else - { - ftlRatio = Math.Clamp(1f - (ftlDiff / _ftlDuration), 0f, 1f); - } - - FTLBar.Value = ftlRatio; + var progress = _ftlTime.ProgressAt(curTime); + FTLBar.Value = float.IsFinite(progress) ? progress : 1; } protected override void Draw(DrawingHandleScreen handle) diff --git a/Content.Server/Shuttles/Components/FTLComponent.cs b/Content.Server/Shuttles/Components/FTLComponent.cs index d15f65a3555..a3da4855f75 100644 --- a/Content.Server/Shuttles/Components/FTLComponent.cs +++ b/Content.Server/Shuttles/Components/FTLComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Shuttles.Systems; using Content.Shared.Tag; +using Content.Shared.Timing; using Robust.Shared.Audio; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -17,13 +18,13 @@ public sealed partial class FTLComponent : Component public FTLState State = FTLState.Available; [ViewVariables(VVAccess.ReadWrite)] - public float StartupTime = 0f; + public StartEndTime StateTime; [ViewVariables(VVAccess.ReadWrite)] - public float TravelTime = 0f; + public float StartupTime = 0f; [ViewVariables(VVAccess.ReadWrite)] - public float Accumulator = 0f; + public float TravelTime = 0f; /// /// Coordinates to arrive it: May be relative to another grid (for docking) or map coordinates. diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs index f0368ed3a95..a4f2c7b4db3 100644 --- a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs @@ -13,6 +13,7 @@ using Content.Shared.Tag; using Content.Shared.Movement.Systems; using Content.Shared.Shuttles.UI.MapObjects; +using Content.Shared.Timing; using Robust.Server.GameObjects; using Robust.Shared.Collections; using Robust.Shared.GameStates; @@ -257,7 +258,11 @@ private void UpdateState(EntityUid consoleUid, ref DockingInterfaceState? dockSt else { navState = new NavInterfaceState(0f, null, null, new Dictionary>()); - mapState = new ShuttleMapInterfaceState(FTLState.Invalid, 0f, new List(), new List()); + mapState = new ShuttleMapInterfaceState( + FTLState.Invalid, + default, + new List(), + new List()); } if (_ui.TryGetUi(consoleUid, ShuttleConsoleUiKey.Key, out var bui)) @@ -408,12 +413,12 @@ public DockingInterfaceState GetDockState() public ShuttleMapInterfaceState GetMapState(Entity shuttle) { FTLState ftlState = FTLState.Available; - float stateDuration = 0f; + StartEndTime stateDuration = default; if (Resolve(shuttle, ref shuttle.Comp, false) && shuttle.Comp.LifeStage < ComponentLifeStage.Stopped) { ftlState = shuttle.Comp.State; - stateDuration = _shuttle.GetStateDuration(shuttle.Comp); + stateDuration = _shuttle.GetStateTime(shuttle.Comp); } List? beacons = null; @@ -422,7 +427,8 @@ public ShuttleMapInterfaceState GetMapState(Entity shuttle) GetExclusions(ref exclusions); return new ShuttleMapInterfaceState( - ftlState, stateDuration, + ftlState, + stateDuration, beacons ?? new List(), exclusions ?? new List()); } diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index cb322ac3964..51288691039 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -12,6 +12,7 @@ using Content.Shared.Shuttles.Components; using Content.Shared.Shuttles.Systems; using Content.Shared.StatusEffect; +using Content.Shared.Timing; using Content.Shared.Whitelist; using JetBrains.Annotations; using Robust.Shared.Audio; @@ -131,7 +132,7 @@ private EntityUid EnsureFTLMap() return mapUid; } - public float GetStateDuration(FTLComponent component) + public StartEndTime GetStateTime(FTLComponent component) { var state = component.State; @@ -141,9 +142,9 @@ public float GetStateDuration(FTLComponent component) case FTLState.Travelling: case FTLState.Arriving: case FTLState.Cooldown: - return component.Accumulator; + return component.StateTime; case FTLState.Available: - return 0f; + return default; default: throw new NotImplementedException(); } @@ -251,7 +252,9 @@ public void FTLToCoordinates( hyperspace.StartupTime = startupTime; hyperspace.TravelTime = hyperspaceTime; - hyperspace.Accumulator = hyperspace.StartupTime; + hyperspace.StateTime = StartEndTime.FromStartDuration( + _gameTiming.CurTime, + TimeSpan.FromSeconds(hyperspace.StartupTime)); hyperspace.TargetCoordinates = coordinates; hyperspace.TargetAngle = angle; hyperspace.PriorityTag = priorityTag; @@ -282,7 +285,9 @@ public void FTLToDock( var config = _dockSystem.GetDockingConfig(shuttleUid, target, priorityTag); hyperspace.StartupTime = startupTime; hyperspace.TravelTime = hyperspaceTime; - hyperspace.Accumulator = hyperspace.StartupTime; + hyperspace.StateTime = StartEndTime.FromStartDuration( + _gameTiming.CurTime, + TimeSpan.FromSeconds(hyperspace.StartupTime)); hyperspace.PriorityTag = priorityTag; _console.RefreshShuttleConsoles(shuttleUid); @@ -366,7 +371,7 @@ private void UpdateFTLStarting(Entity entity) // Reset rotation so they always face the same direction. xform.LocalRotation = Angle.Zero; _index += width + Buffer; - comp.Accumulator += comp.TravelTime - DefaultArrivalTime; + comp.StateTime = StartEndTime.FromCurTime(_gameTiming, comp.TravelTime - DefaultArrivalTime); Enable(uid, component: body); _physics.SetLinearVelocity(uid, new Vector2(0f, 20f), body: body); @@ -401,7 +406,7 @@ private void UpdateFTLTravelling(Entity entity) { var shuttle = entity.Comp2; var comp = entity.Comp1; - comp.Accumulator += DefaultArrivalTime; + comp.StateTime = StartEndTime.FromCurTime(_gameTiming, DefaultArrivalTime); comp.State = FTLState.Arriving; // TODO: Arrival effects // For now we'll just use the ss13 bubbles but we can do fancier. @@ -504,7 +509,7 @@ private void UpdateFTLArriving(Entity entity) } comp.State = FTLState.Cooldown; - comp.Accumulator += FTLCooldown; + comp.StateTime = StartEndTime.FromCurTime(_gameTiming, FTLCooldown); _console.RefreshShuttleConsoles(uid); _mapManager.SetMapPaused(mapId, false); Smimsh(uid, xform: xform); @@ -519,15 +524,14 @@ private void UpdateFTLCooldown(Entity entity) _console.RefreshShuttleConsoles(entity); } - private void UpdateHyperspace(float frameTime) + private void UpdateHyperspace() { + var curTime = _gameTiming.CurTime; var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp, out var shuttle)) { - comp.Accumulator -= frameTime; - - if (comp.Accumulator > 0f) + if (curTime < comp.StateTime.End) continue; var entity = (uid, comp, shuttle); diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.cs index 7c42753a7de..6dc25e8d766 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.cs @@ -19,6 +19,7 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Server.Shuttles.Systems; @@ -30,6 +31,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly BiomeSystem _biomes = default!; [Dependency] private readonly BodySystem _bobby = default!; [Dependency] private readonly DockingSystem _dockSystem = default!; @@ -68,7 +70,7 @@ public override void Initialize() public override void Update(float frameTime) { base.Update(frameTime); - UpdateHyperspace(frameTime); + UpdateHyperspace(); } private void OnGridFixtureChange(EntityUid uid, FixturesComponent manager, GridFixtureChangeEvent args) diff --git a/Content.Shared/Shuttles/BUIStates/ShuttleMapInterfaceState.cs b/Content.Shared/Shuttles/BUIStates/ShuttleMapInterfaceState.cs index cee0daab4be..3cb7cb6412a 100644 --- a/Content.Shared/Shuttles/BUIStates/ShuttleMapInterfaceState.cs +++ b/Content.Shared/Shuttles/BUIStates/ShuttleMapInterfaceState.cs @@ -1,5 +1,6 @@ using Content.Shared.Shuttles.Systems; using Content.Shared.Shuttles.UI.MapObjects; +using Content.Shared.Timing; using Robust.Shared.Serialization; namespace Content.Shared.Shuttles.BUIStates; @@ -16,9 +17,9 @@ public sealed class ShuttleMapInterfaceState public readonly FTLState FTLState; /// - /// How long the FTL state takes. + /// When the current FTL state starts and ends. /// - public float FTLDuration; + public StartEndTime FTLTime; public List Destinations; @@ -26,12 +27,12 @@ public sealed class ShuttleMapInterfaceState public ShuttleMapInterfaceState( FTLState ftlState, - float ftlDuration, + StartEndTime ftlTime, List destinations, List exclusions) { FTLState = ftlState; - FTLDuration = ftlDuration; + FTLTime = ftlTime; Destinations = destinations; Exclusions = exclusions; } diff --git a/Content.Shared/Timing/StartEndTime.cs b/Content.Shared/Timing/StartEndTime.cs new file mode 100644 index 00000000000..fde9f7341c0 --- /dev/null +++ b/Content.Shared/Timing/StartEndTime.cs @@ -0,0 +1,68 @@ +using Robust.Shared.Timing; + +namespace Content.Shared.Timing; + +/// +/// Represents a range of an "action" in time, as start/end times. +/// +/// +/// Positions in time are represented as s, usually from +/// or . +/// +/// The time the action starts. +/// The time action ends. +[Serializable] +public record struct StartEndTime(TimeSpan Start, TimeSpan End) +{ + /// + /// How long the action takes. + /// + public TimeSpan Length => End - Start; + + /// + /// Get how far the action has progressed relative to a time value. + /// + /// The time to get the current progress value for. + /// If true, clamp values outside the time range to 0 through 1. + /// + /// + /// A progress value. Zero means is at , + /// one means is at . + /// + /// + /// This function returns if and are identical. + /// + /// + public float ProgressAt(TimeSpan time, bool clamp = true) + { + var length = Length; + if (length == default) + return float.NaN; + + var progress = (float) ((time - Start) / length); + if (clamp) + progress = MathHelper.Clamp01(progress); + + return progress; + } + + public static StartEndTime FromStartDuration(TimeSpan start, TimeSpan duration) + { + return new StartEndTime(start, start + duration); + } + + public static StartEndTime FromStartDuration(TimeSpan start, float durationSeconds) + { + return new StartEndTime(start, start + TimeSpan.FromSeconds(durationSeconds)); + } + + public static StartEndTime FromCurTime(IGameTiming gameTiming, TimeSpan duration) + { + return FromStartDuration(gameTiming.CurTime, duration); + } + + public static StartEndTime FromCurTime(IGameTiming gameTiming, float durationSeconds) + { + return FromStartDuration(gameTiming.CurTime, durationSeconds); + } +} From d215419f9ac6108185b724e40185d2cf27f7a749 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 30 Mar 2024 13:28:42 +1100 Subject: [PATCH 011/133] Revert "Fix scram implant's teleportation out of containers" (#25030) * Revert "Fix scram implant's teleportation out of containers (#24827)" This reverts commit d4434dbb5e586544a5dbd067a3fee4a6bd4e9e89. * Remove EntityQuery pass-ins --- Content.Server/Implants/SubdermalImplantSystem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 6b58f6eb092..fd95720732b 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -139,7 +139,6 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, break; } _xform.SetWorldPosition(ent, targetCoords.Position); - _xform.AttachToGridOrMap(ent, xform); _audio.PlayPvs(implant.TeleportSound, ent); args.Handled = true; From bb5ca720fccd035e310c12b266f71081f85edf47 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sat, 30 Mar 2024 15:34:55 +1300 Subject: [PATCH 012/133] Split GasTileOverlaySystem update over two ticks (#26542) Split GasTileOverlaySystem update over ticks --- .../EntitySystems/GasTileOverlaySystem.cs | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs index 6d49feb0181..003eed59e03 100644 --- a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -52,6 +52,8 @@ public sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem new DefaultObjectPool>>( new DefaultPooledObjectPolicy>>(), 64); + private bool _doSessionUpdate; + /// /// Overlay update interval, in seconds. /// @@ -192,7 +194,7 @@ public GasOverlayData GetOverlayData(GasMixture? mixture) /// /// Updates the visuals for a tile on some grid chunk. Returns true if the visuals have changed. /// - private bool UpdateChunkTile(GridAtmosphereComponent gridAtmosphere, GasOverlayChunk chunk, Vector2i index, GameTick curTick) + private bool UpdateChunkTile(GridAtmosphereComponent gridAtmosphere, GasOverlayChunk chunk, Vector2i index) { ref var oldData = ref chunk.TileData[chunk.GetDataIndex(index)]; if (!gridAtmosphere.Tiles.TryGetValue(index, out var tile)) @@ -200,7 +202,7 @@ private bool UpdateChunkTile(GridAtmosphereComponent gridAtmosphere, GasOverlayC if (oldData.Equals(default)) return false; - chunk.LastUpdate = curTick; + chunk.LastUpdate = _gameTiming.CurTick; oldData = default; return true; } @@ -258,11 +260,11 @@ private bool UpdateChunkTile(GridAtmosphereComponent gridAtmosphere, GasOverlayC if (!changed) return false; - chunk.LastUpdate = curTick; + chunk.LastUpdate = _gameTiming.CurTick; return true; } - private void UpdateOverlayData(GameTick curTick) + private void UpdateOverlayData() { // TODO parallelize? var query = EntityQueryEnumerator(); @@ -276,7 +278,7 @@ private void UpdateOverlayData(GameTick curTick) if (!overlay.Chunks.TryGetValue(chunkIndex, out var chunk)) overlay.Chunks[chunkIndex] = chunk = new GasOverlayChunk(chunkIndex); - changed |= UpdateChunkTile(gam, chunk, index, curTick); + changed |= UpdateChunkTile(gam, chunk, index); } if (changed) @@ -291,13 +293,28 @@ public override void Update(float frameTime) base.Update(frameTime); AccumulatedFrameTime += frameTime; - if (AccumulatedFrameTime < _updateInterval) return; - AccumulatedFrameTime -= _updateInterval; + if (_doSessionUpdate) + { + UpdateSessions(); + return; + } + + if (AccumulatedFrameTime < _updateInterval) + return; - var curTick = _gameTiming.CurTick; + AccumulatedFrameTime -= _updateInterval; // First, update per-chunk visual data for any invalidated tiles. - UpdateOverlayData(curTick); + UpdateOverlayData(); + + // Then, next tick we send the data to players. + // This is to avoid doing all the work in the same tick. + _doSessionUpdate = true; + } + + public void UpdateSessions() + { + _doSessionUpdate = false; if (!PvsEnabled) return; @@ -315,11 +332,11 @@ public override void Update(float frameTime) _sessions.Add(player); } - if (_sessions.Count > 0) - { - _updateJob.CurrentTick = curTick; - _parMan.ProcessNow(_updateJob, _sessions.Count); - } + if (_sessions.Count == 0) + return; + + _parMan.ProcessNow(_updateJob, _sessions.Count); + _updateJob.LastSessionUpdate = _gameTiming.CurTick; } public void Reset(RoundRestartCleanupEvent ev) @@ -352,7 +369,7 @@ private record struct UpdatePlayerJob : IParallelRobustJob public ObjectPool> ChunkIndexPool; public ObjectPool>> ChunkViewerPool; - public GameTick CurrentTick; + public GameTick LastSessionUpdate; public Dictionary>> LastSentChunks; public List Sessions; @@ -415,7 +432,7 @@ public void Execute(int index) if (previousChunks != null && previousChunks.Contains(gIndex) && - value.LastUpdate != CurrentTick) + value.LastUpdate > LastSessionUpdate) { continue; } From 6ef592ddba336a41330eaf3760a600c805907e1d Mon Sep 17 00:00:00 2001 From: Boaz1111 <149967078+Boaz1111@users.noreply.github.com> Date: Sat, 30 Mar 2024 03:46:20 +0100 Subject: [PATCH 013/133] Industrial Reagent Grinder (#25020) * all work done * adress whitespace * millions must factorio * conflict fix * i forgot --- .../Circuitboards/Machine/production.yml | 13 ++++++ .../Entities/Structures/Machines/lathe.yml | 1 + .../Structures/Machines/reagent_grinder.yml | 42 +++++++++++++++++++ .../Entities/Structures/Machines/recycler.yml | 2 +- .../Prototypes/Recipes/Lathes/electronics.yml | 9 ++++ .../Prototypes/Research/civilianservices.yml | 1 + 6 files changed, 67 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 093c0524b4a..7a9e60ac566 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -1357,3 +1357,16 @@ materialRequirements: Steel: 5 CableHV: 2 + +- type: entity + parent: BaseMachineCircuitboard + id: ReagentGrinderIndustrialMachineCircuitboard + name: industrial reagent grinder machine board + components: + - type: MachineBoard + prototype: ReagentGrinderIndustrial + requirements: + MatterBin: 1 + Manipulator: 3 + materialRequirements: + Glass: 1 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 28bc62894e5..5fbe84a3fe7 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -451,6 +451,7 @@ - ArtifactCrusherMachineCircuitboard - TelecomServerCircuitboard - MassMediaCircuitboard + - ReagentGrinderIndustrialMachineCircuitboard - type: MaterialStorage whitelist: tags: diff --git a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml index d19e2379972..3bb1458b8c5 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml @@ -56,3 +56,45 @@ inputContainer: !type:Container machine_board: !type:Container machine_parts: !type:Container + +- type: entity + parent: Recycler #too different so different parent + id: ReagentGrinderIndustrial + name: industrial reagent grinder + description: An industrial reagent grinder. + components: + - type: SolutionContainerManager + solutions: + output: + maxVol: 400 #*slaps roof of machine* This baby can fit so much omnizine in it + - type: MaterialReclaimer + whitelist: + components: + - Extractable #same as reagent grinder + blacklist: + tags: + - HighRiskItem #ian meat + efficiency: 0.9 + - type: Sprite + sprite: Structures/Machines/recycling.rsi + layers: + - state: grinder-b0 + - type: Machine + board: ReagentGrinderIndustrialMachineCircuitboard + - type: GenericVisualizer + visuals: + enum.ConveyorVisuals.State: + enum.RecyclerVisualLayers.Main: + Forward: { state: grinder-b1 } + Reverse: { state: grinder-b1 } + Off: { state: grinder-b0 } + - type: ContainerContainer + containers: + machine_board: !type:Container + machine_parts: !type:Container + - type: Construction + graph: Machine + node: machine + containers: + - machine_parts + - machine_board \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Machines/recycler.yml b/Resources/Prototypes/Entities/Structures/Machines/recycler.yml index 814852125a0..138795cbf13 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/recycler.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/recycler.yml @@ -116,4 +116,4 @@ interactSuccessString: petting-success-recycler interactFailureString: petting-failure-generic interactSuccessSound: - path: /Audio/Items/drill_hit.ogg + path: /Audio/Items/drill_hit.ogg \ No newline at end of file diff --git a/Resources/Prototypes/Recipes/Lathes/electronics.yml b/Resources/Prototypes/Recipes/Lathes/electronics.yml index f0c59a0bdf9..c4417f868b5 100644 --- a/Resources/Prototypes/Recipes/Lathes/electronics.yml +++ b/Resources/Prototypes/Recipes/Lathes/electronics.yml @@ -956,3 +956,12 @@ Steel: 100 Glass: 900 Gold: 100 + +- type: latheRecipe + id: ReagentGrinderIndustrialMachineCircuitboard + result: ReagentGrinderIndustrialMachineCircuitboard + completetime: 5 + materials: + Steel: 100 + Glass: 900 + Gold: 100 diff --git a/Resources/Prototypes/Research/civilianservices.yml b/Resources/Prototypes/Research/civilianservices.yml index 61f95894ee6..79dac275101 100644 --- a/Resources/Prototypes/Research/civilianservices.yml +++ b/Resources/Prototypes/Research/civilianservices.yml @@ -14,6 +14,7 @@ - BorgModuleHarvesting - SeedExtractorMachineCircuitboard - HydroponicsTrayMachineCircuitboard + - ReagentGrinderIndustrialMachineCircuitboard - type: technology id: CritterMechs From ac129820492ff0189dd581033848dc9aa3a15b5c Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 02:47:27 +0000 Subject: [PATCH 014/133] 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 c52c19a50c9..420db733015 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: casperr04 - changes: - - message: Fixed players being able to re-anchor items after fultoning them. - type: Fix - - message: Changed which objects can be fultoned. - type: Tweak - id: 5754 - time: '2024-01-20T04:57:05.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/20628 - author: Blackern5000 changes: - message: Crushers can no longer be researched. @@ -3795,3 +3786,11 @@ id: 6253 time: '2024-03-30T01:25:43.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26541 +- author: Boaz1111 + changes: + - message: Added an industrial reagent grinder to the basic hydroponics research. + It grinds things into reagents like a recycler. + type: Add + id: 6254 + time: '2024-03-30T02:46:20.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25020 From 52db14a19dcafa5592be0e2d23a4a628c9ab981e Mon Sep 17 00:00:00 2001 From: SonicHDC <100022571+SonicHDC@users.noreply.github.com> Date: Sat, 30 Mar 2024 15:31:32 +1200 Subject: [PATCH 015/133] Zippable coats (#26494) * Update base_clothingouter.yml * Update coats.yml * Change Flipped to Opened * labcoat * coat * Update meta.json * Update meta.json * Update meta.json * Update meta.json * Update meta.json * Update meta.json * Update meta.json * cmo * gene * rd * robo * sci * viro * Locale zip-unzip * Missing meta * Fix wrong sprites --- .../components/foldable-component.ftl | 5 +- .../OuterClothing/base_clothingouter.yml | 42 ++++++++++++++ .../Entities/Clothing/OuterClothing/coats.yml | 54 +++++++++++++++--- .../OuterClothing/Coats/labcoat.rsi/meta.json | 8 +++ .../Coats/labcoat.rsi/open-inhand-left.png | Bin 0 -> 390 bytes .../Coats/labcoat.rsi/open-inhand-right.png | Bin 0 -> 394 bytes .../Coats/labcoat_chem.rsi/meta.json | 8 +++ .../labcoat_chem.rsi/open-inhand-left.png | Bin 0 -> 408 bytes .../labcoat_chem.rsi/open-inhand-right.png | Bin 0 -> 410 bytes .../Coats/labcoat_cmo.rsi/meta.json | 8 +++ .../labcoat_cmo.rsi/open-inhand-left.png | Bin 0 -> 382 bytes .../labcoat_cmo.rsi/open-inhand-right.png | Bin 0 -> 383 bytes .../Coats/labcoat_gene.rsi/meta.json | 8 +++ .../labcoat_gene.rsi/open-inhand-left.png | Bin 0 -> 408 bytes .../labcoat_gene.rsi/open-inhand-right.png | Bin 0 -> 410 bytes .../Coats/labcoat_rd.rsi/meta.json | 8 +++ .../Coats/labcoat_rd.rsi/open-inhand-left.png | Bin 0 -> 1352 bytes .../labcoat_rd.rsi/open-inhand-right.png | Bin 0 -> 1349 bytes .../Coats/labcoat_robo.rsi/meta.json | 8 +++ .../labcoat_robo.rsi/open-inhand-left.png | Bin 0 -> 395 bytes .../labcoat_robo.rsi/open-inhand-right.png | Bin 0 -> 399 bytes .../Coats/labcoat_sci.rsi/meta.json | 8 +++ .../labcoat_sci.rsi/open-inhand-left.png | Bin 0 -> 408 bytes .../labcoat_sci.rsi/open-inhand-right.png | Bin 0 -> 410 bytes .../Coats/labcoat_viro.rsi/meta.json | 8 +++ .../labcoat_viro.rsi/open-inhand-left.png | Bin 0 -> 408 bytes .../labcoat_viro.rsi/open-inhand-right.png | Bin 0 -> 410 bytes 27 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-right.png diff --git a/Resources/Locale/en-US/foldable/components/foldable-component.ftl b/Resources/Locale/en-US/foldable/components/foldable-component.ftl index 539b4fd9e71..525820920bd 100644 --- a/Resources/Locale/en-US/foldable/components/foldable-component.ftl +++ b/Resources/Locale/en-US/foldable/components/foldable-component.ftl @@ -4,4 +4,7 @@ foldable-deploy-fail = You can't deploy the {$object} here. fold-verb = Fold unfold-verb = Unfold -fold-flip-verb = Flip \ No newline at end of file +fold-flip-verb = Flip + +fold-zip-verb = Zip up +fold-unzip-verb = Unzip diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml index 82df2c21e8f..310661f6ca8 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml @@ -43,6 +43,48 @@ - type: StaticPrice price: 80 +- type: entity + abstract: true + parent: [ClothingOuterStorageBase, BaseFoldable] + id: ClothingOuterStorageFoldableBase + components: + - type: Appearance + - type: Foldable + canFoldInsideContainer: true + unfoldVerbText: fold-zip-verb + foldVerbText: fold-unzip-verb + - type: FoldableClothing + foldedEquippedPrefix: open + foldedHeldPrefix: open + - type: Sprite + layers: + - state: icon + map: [ "unfoldedLayer" ] + - state: icon-open + map: ["foldedLayer"] + visible: false + +- type: entity + abstract: true + parent: ClothingOuterStorageFoldableBase + id: ClothingOuterStorageFoldableBaseOpened + suffix: opened + components: + - type: Foldable + folded: true + - type: Clothing + equippedPrefix: open + - type: Item + heldPrefix: open + - type: Sprite + layers: + - state: icon + map: [ "unfoldedLayer" ] + visible: false + - state: icon-open + map: ["foldedLayer"] + visible: true + - type: entity abstract: true parent: ClothingOuterStorageBase diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml index bb2f598cd0a..ca0e2d4b4d3 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml @@ -114,7 +114,7 @@ Quantity: 20 - type: entity - parent: ClothingOuterStorageBase + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatLab name: lab coat description: A suit that protects against minor chemical spills. @@ -129,7 +129,12 @@ Caustic: 0.75 - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatLab] + id: ClothingOuterCoatLabOpened + name: lab coat + +- type: entity + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatLabChem name: chemist lab coat description: A suit that protects against minor chemical spills. Has an orange stripe on the shoulder. @@ -144,7 +149,12 @@ Caustic: 0.75 - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatLabChem] + id: ClothingOuterCoatLabChemOpened + name: chemist lab coat + +- type: entity + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatLabViro name: virologist lab coat description: A suit that protects against bacteria and viruses. Has an green stripe on the shoulder. @@ -158,9 +168,13 @@ coefficients: Caustic: 0.75 +- type: entity + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatLabViro] + id: ClothingOuterCoatLabViroOpened + name: virologist lab coat - type: entity - parent: ClothingOuterStorageBase + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatLabGene name: geneticist lab coat description: A suit that protects against minor chemical spills. Has an blue stripe on the shoulder. @@ -174,9 +188,13 @@ coefficients: Caustic: 0.75 +- type: entity + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatLabGene] + id: ClothingOuterCoatLabGeneOpened + name: geneticist lab coat - type: entity - parent: ClothingOuterStorageBase + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatLabCmo name: chief medical officer's lab coat description: Bluer than the standard model. @@ -191,7 +209,12 @@ Caustic: 0.75 - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatLabCmo] + id: ClothingOuterCoatLabCmoOpened + name: chief medical officer's lab coat + +- type: entity + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatRnd name: scientist lab coat description: A suit that protects against minor chemical spills. Has a purple stripe on the shoulder. @@ -206,7 +229,12 @@ Caustic: 0.75 - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatRnd] + id: ClothingOuterCoatRndOpened + name: scientist lab coat + +- type: entity + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatRobo name: roboticist lab coat description: More like an eccentric coat than a labcoat. Helps pass off bloodstains as part of the aesthetic. Comes with red shoulder pads. @@ -221,7 +249,12 @@ Caustic: 0.75 - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatRobo] + id: ClothingOuterCoatRoboOpened + name: roboticist lab coat + +- type: entity + parent: ClothingOuterStorageFoldableBase id: ClothingOuterCoatRD name: research director lab coat description: Woven with top of the line technology, this labcoat helps protect against radiation in similar way to the experimental hardsuit. @@ -236,6 +269,11 @@ Caustic: 0.75 Radiation: 0.9 +- type: entity + parent: [ClothingOuterStorageFoldableBaseOpened, ClothingOuterCoatRD] + id: ClothingOuterCoatRDOpened + name: research director lab coat + - type: entity parent: ClothingOuterStorageBase id: ClothingOuterCoatPirate diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/meta.json index 775a3c2151d..02abb078069 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..489f958b9e9e6ae91a0428ecde2b4fc97585c26d GIT binary patch literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1k1^9%xPMkRL|Ns9_pFX{K@nTn3myeH+lao_+cJ`4YM}SIp%?x@4q_|3g z{DOhXAb?@h8J+V$QO*L7$YKTtzC$3)D5~Mr02I9K>Eaj?;r@2oTdrmU9@oqKrM3UX z<1Sx~nk;=&BALUoXVvzbnm>+vKlP4zz;ujNFQHD|BLBwo1#_phR<=7u-`>m6B2r=F z!!}n&!EsGuPWJM9$LDEJXwY~Xa{XV=B<3ksH;Wa{@+(_lazy5+_mqfUDsxXmk3lFrI%5O6Bgvg6U4Xnq~ zc|=(acG~ppH(sUDaC1(NUf@Mn#-4T8nIdI>l_u>LJMrqmdgE)_DItM7X4LlI=Ju*& Zy2#R{xb*nZhd^&Kc)I$ztaD0e0stYfo5}zH literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..54bd14c8aac98862f49d070485b2adb226a9a434 GIT binary patch literal 394 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1k1^9%xPMkRL|Ns9_pFX{K@nTn3myeH+lao_+cJ`4YM}SIp%?x@4q_|3g z{DOhXAb?@h8J+V$QO*L7$YKTtzC$3)D5~Mr02I9M>Eaj?;r@2oO}=IW9+tT+i~s!R zH#cLuCS!T!+rmli9T6w1CRB80{uB`3(NNgPJtLpX%T|pMFtR`{Kn%2t1eP~LtWHU^>RSU*L)2K zR(d^1))kz*&WH1h)K(^g{C9t~MJ_K;EfAWrtVL(W78bvzOH(GWZl2cA8NRIL_GYJj fz5B&=PD?M}(WQ7~>H2>_uQGVL`njxgN@xNA7*wRN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/meta.json index 775a3c2151d..02abb078069 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..57d36185047e17e51ac20411891363e034207aa1 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkRLOd8*RAebQe^y$-!7cX{ob@}-CI5|0GXJ@bQVLNi<2uN)V zmkyBPEeY}q1}TOC2HO`(Z9oam0*}aI1_r)EAj~ML;ne^XeCX-o7!u+BcG_FMLkc{u zjR)k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkRLOd8*RAebQe^y$-!7cX{ob@}-CI5|0GXJ@bQVLNi<2uN)V zmkyBPEeY}q1}TOC2HO`(Z9oam0*}aI1_r)EAj~ML;ne^XeC+As7!u+BcA6(&vjLB* zFWa)B|DVmw*mBxIXb@5-KQZ&#jtlJJ*#R$O4a_+E zzUa-E#o}Hyn_+%b%C3J`nAHpzlbD1R)PA%sxT^8_)xG^jpVtU7eXy`Az1cWLJyIj2$8? oDi(V0xO@7^>mStzH|=43vvS9*=ieN!1HH@O>FVdQ&MBb@0AV+%Pyhe` literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/meta.json index 775a3c2151d..02abb078069 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..31025b7ddad3b10f1804e7ded6722bacd63ff539 GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`031o(uwsw7nv?6@4V`m|xo0!fz;F>6nUg-3ysZ(hlN0a6?#L4LviA%Njt z^WI3H7-xY;WHAE+-$4*&+%YlxEl}{br;B4qg!|iR7x|hMcwD&`{rNAyUUfxmwf8oi z)0>vKD?bb3Zxi?W>l`$JNs(1kV4r%$ypHRR#>(1NZKo>w%9%MZ29Qw^Hdf*=w({5 zRQVRuyw7V7%ZUDtN{DW^)Vc+xvKHxz#4O&hqPBOx_?LeS?DMmmyJ}t(0KLiJ>FVdQ I&MBb@09{g&H~;_u literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_cmo.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..01e8e0fbc1123246578f528efe824e1890af8bff GIT binary patch literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`031o(uwsw7nv?6@4V`m|xo0!fz;F>6nUg-3ysZ(hlN0a6?#L4LviA%Njt z^WI3H7-xY;WHAE+-$4*&+%YlxEl}`|r;B4qg!|iRH-(xNcwF^&eE46VBjnJmbb3qe z3!gr}&H~GAwTc|_Nld~9Y6gs-br0@Qf8U{eC-nQoIo_F)4bir2RL$f75`@}d$aONhU0GU1+9Gza<^ic zrkRFv&DB5mSN%hE>RYvpQcjf{K`VD~M#VC5TG=nO_^ustUFxUdzc_(IjL)>Pn_2!b Ry8->l;OXk;vd$@?2>_OdlcxXx literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/meta.json index 775a3c2151d..02abb078069 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..d95cfab8a7f44f1632971643716f6ae8f98143a0 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkPVcgCLo|NkrRdi(V0(~B1`c6D|6`1m+EIb~;Ot7kVIIdTN3 zwyTu63`p^o1o;I66~h68?F*$gpaf@uM`SSr1K&XqX529``z=uLp{I*uNQC>_X>a)s zDe$;99<-bCy*^H}jOXRsMUg#*FFPA6U-EU<@6n4t!lI_&lfd+-y6HOeU%8S;nVhAm zH7l}fyQ)1$t*!uK6RY%C4bZu6sDw=w{<3f#(vx zglC$F9MocQz7)VRTY$%txxLTM*68NJm+UhRu(WXcILvs!&SconYJQJ(lSV>gIM4Y- z6MiphU^%J!#?_UPd&+*p5Rn5=?47YFTfaTH@U*fo#)*sBB8$IJ92Rx`i1 nz2)!Gv8cf4w_LsciJy#0u{&l}`;`;`JgTe~DWM4fx-*~S literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbbaa54df7255e742a138c820a5e7957ddcd584 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkPVcgCLo|NkrRdi(V0(~B1`c6D|6`1m+EIb~;Ot7kVIIdTN3 zwyTu63`p^o1o;I66~h68?F*$gpaf@uM`SSr1K&XqX529``z=uLv8Rh;NQC>_X`Xz| z20X65Y|D!Ne>O8?%W31u+P&h5`ji<@o*&d}d+O|yz;uK~&0(Kv#`hfcYdecgf@0f> z`Yx+5^yklA|C@hq94jMJ@0O6JqL(cq3`gviO`PWS!eoAbJ6_x2loUL(l#!NRVL^Yh+F zt}A=%8I@f0RD&kG7b=+T-RUL1i=~1$hBGDZ?(cAgBlEludQNyPG%4XpP=U~sT`ejz rc8IK~Sm?du?&&A5e^ejbw1@G{${n+we{;MJ^e%&^tDnm{r-UW|atNy= literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/meta.json index f10d1d80141..5a13fa99f36 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..3a3f3f85b0d6115bb8e3125ef30ff36c984f41b2 GIT binary patch literal 1352 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkO~f+6Go|Nk=?W)?EEJbn7~;>C+yU0pstK2AtCq9nrC$0|8L zS1&OoKPgqOBDVmjnt{Q_zM>#8IXksPAt^OIGtXA({qFrr3YjUkO5vuy2EGN(sTr9b zRYj@6RemAKRoTgwDN6Qsyj(UFRzMSSQ%e#RDspr3imfVamB0pD0ofp7eI*63l9Fs& zr3l{u1?T*tR0T6V13d#JpmqfXo1&C7s~{IQsCFRFRw<*Tq`*pFzr4I$uiRKKzbIYb z(9+UU-@r)U$VeBcLbtdwuOzWTH?LS3VhGe2m(=3qqRfJl%=|nBkgZI5j|wgoG(l0)!-PaNq+|H;`we56>zf9z46H<^l6q5im1FHN8j& zX2iFiE{-7)?r*2P=4)2qaa$R)r{{b9y{$Q1)A)rq>ztl6Phye$*9CtT>eY8}`Y6me zz;ft2%k$2URvB$6Gfw?zTV^z^npI!|zYSab>)%V5SoDe(E{|Al@o`P&BrYKfuVSU^ z9J`h@8O3E!T%Yk`vaE`N{PT)m5A7@^IIj6EnAF^u@R~)-@<6pi(bg{~`9$sA5$;_&p7L|)^X zbyBPfGlF;AaJj3&aIi`@pqEJ?=Q`6>4F<=u!{2W%kY-icar8cSQ5_TWpPlk{ug)z5 O6+xb^elF{r5}E+CH_lD~ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_rd.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..7be0d597256ad965a8844c4be42e510b94b1c30a GIT binary patch literal 1349 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkO~f+6Go|Nk=?W)?EEJbn7~;>C+yU0pstK2AtCq9nrC$0|8L zS1&OoKPgqOBDVmjnt{Q_zM>#8IXksPAt^OIGtXA({qFrr3YjUkO5vuy2EGN(sTr9b zRYj@6RemAKRoTgwDN6Qsyj(UFRzMSSQ%e#RDspr3imfVamB0pD0ofp7eI*63l9Fs& zr3l{u1?T*tR0T6V13d#JpmqfXo1&C7s~{IQsCFRFRw<*Tq`*pFzr4I$uiRKKzbIYb z(9+UU-@r)U$VeBcLbtdwuOzWTH?LS3VhGe2m(=3qqRfJl%=|nBkgZI5j|wgoG(l0)!-PaNq+|H;`we56>zf9z46H<^l6q5im1FHN8j& zX2e&XE{-7)?r*2vER)}TjsoGYdW>?y;Y^wu@BSJ;ICNs^ z1BDf(oEkHBipa2R)njDkp5F9{2dGvg@!6|r@p}D^kDOOrcgoLf&({K#Kc23BF6*2U FngG4m&Uyd< literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/meta.json index 775a3c2151d..02abb078069 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..83d3f527c8c2448a3946b945f51d52cec933bb21 GIT binary patch literal 395 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E1o(uwS}-sdvoKAZIPw4g|4*MjO=e*5WMJPO7pE$%Fw4>L;>C+Vg)1XX z6Mz&?NswPKP#Fv`2;Dz-4k*r9;1OBOz`%D9gc)~C%zg_Lyy5BM7!u+BcG_FMW(6MC zrG6Jb{(HY$Y=6T&Jwt9o@2aGZY0o(RJmh=n9`k_dAgi9hFV0t-zwI;Bt~*&8x7a-U z&K9sF@YZ3E+vcsmC&_ zd0(IERE(IiVC9^WNXMw~O0SZq3|#LXFo|>M6*N9*VrN+;&+W68k86i$+>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E1o(uwS}-sdvoKAZIPw4g|4*MjO=e*5WMJPO7pE$%Fw4>L;>C+Vg)1XX z6Mz&?NswPKP#Fv`2;Dz-4k*r9;1OBOz`%C^gc-B0b#ej)?|8a6hD5l(opzD0S%JrO z>53OW{{NR1UnN$$+Nw6WPz;k3rEElijwVmEH^+ELX2`v8m8?ULpc^BHG e{b#SjG4rgVRV_tr87qPQWbkzLb6Mw<&;$TYIii~Y literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/meta.json index 775a3c2151d..02abb078069 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..5dc3a92fdcdbb9441a1813f956b59cb2ef668d2a GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkQgkfG)O|Nk=?WPNgLYHC*T-p=@w|MyD6+@!WoKjMOTNzfJ$ms+Skx4J5||!UH(h7`D_8O;le09n zW<_?b90Qa2#U=CmlxNB)IO-&>(GH%c{G3UpK=16zHGgDP*)_Dwbr0tn-E6!h@Lb}T z@JthtgIX-kmjYO33-EX{xA)oE8r?kjl6}SjmKIJQhZzspnGE|`&F`^p(nx3w=Q+P< z!tX^5EGJdpxVkcOPuXu6B68qKQFxrXOCZDM;-FnFj$*6^yXJ8pdv&1tc$uEWYUcO0 nxBNXi78Mx%maEr4@sm+0cE_x0zmfu=XBj+Q{an^LB{Ts5GV`69 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_sci.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c28ef522612bf5ae467a6afd3f5f32f1fc41a27f GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkQgkfG)O|Nk=?WyyH`9>pEBdg^MiVAPn~@dn2xZhIqXx-_@1MFZD+AbP;6UK z-(?ks{`|S?fAi0cV`XIO-4fDN^s+^S;fUR`iPOAZn9Of92&t2wn0al-1@`dlfS0ib zW}JOr^k&Rraj%-qFh43~*S{;wY6gr+Ou`CkKUx=D)%g7C-hQLcYXq4-SlE?ue%>3& zb!BfoqmqlBYS4uDLItzEJH5nru~hKJaHhoF{T;4wWS;jy&k3)ECM7%xDiC_It3_qT r4v`fV3%z&TJ^keMkLrV)_AtI#xntJzZ;sc2-evG~^>bP0l+XkK<|nAz literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/meta.json index 775a3c2151d..02abb078069 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/meta.json @@ -28,6 +28,14 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "open-inhand-left", + "directions": 4 + }, + { + "name": "open-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..2dde993a3df6075fcb5df9e01f536fbe0336ddc8 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkPVyq)j=|Nn|Bq@F%~dhz1LuC6X0A0Hc0P<4y1TXg8YJkis694_JvX#P=d3-BeIx*f$ty)Gwzs}{T3+r(9^{+B*Ojew6}bR z6nI=4586%nULU7f#`E&+qR1Y@mz|B3FZnv__vpnRVNp}?NnmwrYjpGAOZFKDSXwxJ9A-RVXEN+(HNVHYNh6^#oag+a z3BMOLu$)wV1 n-tzb8SX5y2TdrRJ#7{=0*d4Q~{Ynaeo@MZK^>bP0l+XkK`uU!i literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..285c57c33f339c5e14303a550e55cf94a630c244 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1E2l#}zPMkPVyq)j=|Nn|Bq@F%~dhz1LuC6X0A0Hc0P<4y1TXg8YJkis694_JvX#P=d3-BeIx*f$ty)Gwzs}{T3+r*we)^B*OjeG*7-} z10Gjjwq-^CKbx7c<+Sl+?OyRjeaeg{&kyRgJ$3d;U^>F0=CDsS<9m+!wVlN#L9uN` zeV0`j`t#?m|II%)j+K$AcS}f9(aRPQh9h>%CQkEuVKTqbAf!%yV&=6S7udtI175}& zm~r-f(VH=g#l31a!~Cd}UH`5ys~IpRF$pWE{b*fqRpaxkd;5((uMuSWU}0Cr`FU?7 z*Ok5Xj7lzgszDRp3l+@v?(`Dh#Zti=!tnw0P)s6gn+t`?OU rJ49AgEcD)S_w Date: Sat, 30 Mar 2024 14:31:47 +1100 Subject: [PATCH 016/133] Fix itemslots swapping (#25634) Fix itemslots prediction --- Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 3463be2e717..83e09b1a6dd 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -279,7 +279,7 @@ public bool CanInsert(EntityUid uid, EntityUid usedUid, EntityUid? user, ItemSlo if (ev.Cancelled) return false; - return _containers.CanInsert(usedUid, slot.ContainerSlot, assumeEmpty: true); + return _containers.CanInsert(usedUid, slot.ContainerSlot, assumeEmpty: swap); } /// From 3af54a286ee3630a00502db20a65f958863e3321 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 03:32:37 +0000 Subject: [PATCH 017/133] 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 420db733015..2a3ec713b39 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Blackern5000 - changes: - - message: Crushers can no longer be researched. - type: Remove - id: 5755 - time: '2024-01-20T05:11:02.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24319 - author: metalgearsloth changes: - message: Fix buckle sound playing twice in some instances. @@ -3794,3 +3787,10 @@ id: 6254 time: '2024-03-30T02:46:20.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25020 +- author: SonicHDC + changes: + - message: Added unzipping for lab coats! + type: Add + id: 6255 + time: '2024-03-30T03:31:32.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26494 From 39753892c2b7a86fb7a10480a32a9dd8252ce80f Mon Sep 17 00:00:00 2001 From: Zealith-Gamer <61980908+Zealith-Gamer@users.noreply.github.com> Date: Fri, 29 Mar 2024 20:35:42 -0700 Subject: [PATCH 018/133] Stop items that are being pulled from spinning (#26504) * Fixed pulled items spinning when moved * edited out others issues * more reverts * requested fix * Removed "Optional:" --- .../Movement/Pulling/Systems/PullingSystem.cs | 2 +- Content.Shared/Throwing/ThrowingSystem.cs | 38 +++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index b347c6da164..acca7aafd05 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -269,7 +269,7 @@ private bool OnRequestMovePulledObject(ICommonSession? session, EntityCoordinate } Dirty(player, pullerComp); - _throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false); + _throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false, doSpin: false); return false; } diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs index 38772eaf340..7d94ada924d 100644 --- a/Content.Shared/Throwing/ThrowingSystem.cs +++ b/Content.Shared/Throwing/ThrowingSystem.cs @@ -49,7 +49,8 @@ public void TryThrow( float pushbackRatio = PushbackDefault, bool recoil = true, bool animated = true, - bool playSound = true) + bool playSound = true, + bool doSpin = true) { var thrownPos = _transform.GetMapCoordinates(uid); var mapPos = _transform.ToMapCoordinates(coordinates); @@ -57,7 +58,7 @@ public void TryThrow( if (mapPos.MapId != thrownPos.MapId) return; - TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, recoil: recoil, animated: animated, playSound: playSound); + TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin); } /// @@ -67,6 +68,7 @@ public void TryThrow( /// A vector pointing from the entity to its destination. /// How much the direction vector should be multiplied for velocity. /// The ratio of impulse applied to the thrower - defaults to 10 because otherwise it's not enough to properly recover from getting spaced + /// Whether spin will be applied to the thrown entity. public void TryThrow(EntityUid uid, Vector2 direction, float strength = 1.0f, @@ -74,7 +76,8 @@ public void TryThrow(EntityUid uid, float pushbackRatio = PushbackDefault, bool recoil = true, bool animated = true, - bool playSound = true) + bool playSound = true, + bool doSpin = true) { var physicsQuery = GetEntityQuery(); if (!physicsQuery.TryGetComponent(uid, out var physics)) @@ -90,7 +93,7 @@ public void TryThrow(EntityUid uid, projectileQuery, strength, user, - pushbackRatio, recoil: recoil, animated: animated, playSound: playSound); + pushbackRatio, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin); } /// @@ -100,6 +103,7 @@ public void TryThrow(EntityUid uid, /// A vector pointing from the entity to its destination. /// How much the direction vector should be multiplied for velocity. /// The ratio of impulse applied to the thrower - defaults to 10 because otherwise it's not enough to properly recover from getting spaced + /// Whether spin will be applied to the thrown entity. public void TryThrow(EntityUid uid, Vector2 direction, PhysicsComponent physics, @@ -110,7 +114,8 @@ public void TryThrow(EntityUid uid, float pushbackRatio = PushbackDefault, bool recoil = true, bool animated = true, - bool playSound = true) + bool playSound = true, + bool doSpin = true) { if (strength <= 0 || direction == Vector2Helpers.Infinity || direction == Vector2Helpers.NaN || direction == Vector2.Zero) return; @@ -147,17 +152,20 @@ public void TryThrow(EntityUid uid, ThrowingAngleComponent? throwingAngle = null; // Give it a l'il spin. - if (physics.InvI > 0f && (!TryComp(uid, out throwingAngle) || throwingAngle.AngularVelocity)) + if (doSpin) { - _physics.ApplyAngularImpulse(uid, ThrowAngularImpulse / physics.InvI, body: physics); - } - else - { - Resolve(uid, ref throwingAngle, false); - var gridRot = _transform.GetWorldRotation(transform.ParentUid); - var angle = direction.ToWorldAngle() - gridRot; - var offset = throwingAngle?.Angle ?? Angle.Zero; - _transform.SetLocalRotation(uid, angle + offset); + if (physics.InvI > 0f && (!TryComp(uid, out throwingAngle) || throwingAngle.AngularVelocity)) + { + _physics.ApplyAngularImpulse(uid, ThrowAngularImpulse / physics.InvI, body: physics); + } + else + { + Resolve(uid, ref throwingAngle, false); + var gridRot = _transform.GetWorldRotation(transform.ParentUid); + var angle = direction.ToWorldAngle() - gridRot; + var offset = throwingAngle?.Angle ?? Angle.Zero; + _transform.SetLocalRotation(uid, angle + offset); + } } var throwEvent = new ThrownEvent(user, uid); From 3fc02edd4ea10d914f280e367c49238ec6126f02 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 03:36:48 +0000 Subject: [PATCH 019/133] 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 2a3ec713b39..b4ec1e0b339 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix buckle sound playing twice in some instances. - type: Fix - id: 5756 - time: '2024-01-20T06:22:19.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24321 - author: tday changes: - message: Added admin log messages for adding and ending game rules, and for the @@ -3794,3 +3787,10 @@ id: 6255 time: '2024-03-30T03:31:32.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26494 +- author: Zealith-Gamer + changes: + - message: Items being pulled no longer spin when being thrown. + type: Fix + id: 6256 + time: '2024-03-30T03:35:43.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26504 From f192d7901fedd134c38a6cab38731f8e93994492 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Fri, 29 Mar 2024 20:59:16 -0700 Subject: [PATCH 020/133] Hyposprays Draw from Jugs (#25544) * Hyposprays Draw from Jugs * Fix last onlyMobs usage in yml * Some Suggested Changes * Remove unnecessary datafield name declarations * Remove unnecessary dirtying of component * Same line parentheses * Added client-side HypospraySystem * Cache UI values and only updates if values change * empty line * Update label * Label change * Reimplement ReactionMixerSystem * Remove DataField from Hypospray Toggle Mode * Change ToggleMode from enum to Bool OnlyAffectsMobs * Add DataField required back since it's required for replays...? * update EligibleEntity and uses of it * Add user argument back * Adds newline Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Guard for dirty entity * Adds summary tag --------- Co-authored-by: Plykiya Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../Components/HyposprayComponent.cs | 16 -- .../EntitySystems/HypospraySystem.cs | 15 ++ .../Chemistry/EntitySystems/InjectorSystem.cs | 13 -- .../Chemistry/UI/HyposprayStatusControl.cs | 48 +++-- .../Body/Components/BloodstreamComponent.cs | 2 +- .../Components/HyposprayComponent.cs | 28 --- .../EntitySystems/ChemistrySystem.cs | 26 --- .../EntitySystems/ChemistrySystemHypospray.cs | 164 --------------- .../EntitySystems/HypospraySystem.cs | 197 ++++++++++++++++++ ...ySystemMixer.cs => ReactionMixerSystem.cs} | 11 +- .../Components/HyposprayComponent.cs | 33 +++ .../Components/SharedHyposprayComponent.cs | 25 --- .../EntitySystems/SharedHypospraySystem.cs | 61 ++++++ .../EntitySystems/SharedInjectorSystem.cs | 3 - .../components/hypospray-component.ftl | 12 +- .../Objects/Specific/Medical/hypospray.yml | 14 +- 16 files changed, 369 insertions(+), 299 deletions(-) delete mode 100644 Content.Client/Chemistry/Components/HyposprayComponent.cs create mode 100644 Content.Client/Chemistry/EntitySystems/HypospraySystem.cs delete mode 100644 Content.Server/Chemistry/Components/HyposprayComponent.cs delete mode 100644 Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs delete mode 100644 Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs create mode 100644 Content.Server/Chemistry/EntitySystems/HypospraySystem.cs rename Content.Server/Chemistry/EntitySystems/{ChemistrySystemMixer.cs => ReactionMixerSystem.cs} (76%) create mode 100644 Content.Shared/Chemistry/Components/HyposprayComponent.cs delete mode 100644 Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs create mode 100644 Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs diff --git a/Content.Client/Chemistry/Components/HyposprayComponent.cs b/Content.Client/Chemistry/Components/HyposprayComponent.cs deleted file mode 100644 index 705b79ad84c..00000000000 --- a/Content.Client/Chemistry/Components/HyposprayComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Content.Shared.Chemistry.Components; -using Content.Shared.FixedPoint; - -namespace Content.Client.Chemistry.Components -{ - [RegisterComponent] - public sealed partial class HyposprayComponent : SharedHyposprayComponent - { - [ViewVariables] - public FixedPoint2 CurrentVolume; - [ViewVariables] - public FixedPoint2 TotalVolume; - [ViewVariables(VVAccess.ReadWrite)] - public bool UiUpdateNeeded; - } -} diff --git a/Content.Client/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Client/Chemistry/EntitySystems/HypospraySystem.cs new file mode 100644 index 00000000000..ee7aa3aafe3 --- /dev/null +++ b/Content.Client/Chemistry/EntitySystems/HypospraySystem.cs @@ -0,0 +1,15 @@ +using Content.Client.Chemistry.UI; +using Content.Client.Items; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; + +namespace Content.Client.Chemistry.EntitySystems; + +public sealed class HypospraySystem : SharedHypospraySystem +{ + public override void Initialize() + { + base.Initialize(); + Subs.ItemStatus(ent => new HyposprayStatusControl(ent, _solutionContainers)); + } +} diff --git a/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs b/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs index 12eb7f3d14d..0131a283c8c 100644 --- a/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs +++ b/Content.Client/Chemistry/EntitySystems/InjectorSystem.cs @@ -1,4 +1,3 @@ -using Content.Client.Chemistry.Components; using Content.Client.Chemistry.UI; using Content.Client.Items; using Content.Shared.Chemistry.Components; @@ -13,17 +12,5 @@ public override void Initialize() { base.Initialize(); Subs.ItemStatus(ent => new InjectorStatusControl(ent, SolutionContainers)); - SubscribeLocalEvent(OnHandleHyposprayState); - Subs.ItemStatus(ent => new HyposprayStatusControl(ent)); - } - - private void OnHandleHyposprayState(EntityUid uid, HyposprayComponent component, ref ComponentHandleState args) - { - if (args.Current is not HyposprayComponentState cState) - return; - - component.CurrentVolume = cState.CurVolume; - component.TotalVolume = cState.MaxVolume; - component.UiUpdateNeeded = true; } } diff --git a/Content.Client/Chemistry/UI/HyposprayStatusControl.cs b/Content.Client/Chemistry/UI/HyposprayStatusControl.cs index bd85cd546cc..4a4d90dc4d5 100644 --- a/Content.Client/Chemistry/UI/HyposprayStatusControl.cs +++ b/Content.Client/Chemistry/UI/HyposprayStatusControl.cs @@ -1,6 +1,8 @@ -using Content.Client.Chemistry.Components; using Content.Client.Message; using Content.Client.Stylesheets; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.FixedPoint; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; @@ -9,34 +11,48 @@ namespace Content.Client.Chemistry.UI; public sealed class HyposprayStatusControl : Control { - private readonly HyposprayComponent _parent; + private readonly Entity _parent; private readonly RichTextLabel _label; + private readonly SharedSolutionContainerSystem _solutionContainers; - public HyposprayStatusControl(HyposprayComponent parent) + private FixedPoint2 PrevVolume; + private FixedPoint2 PrevMaxVolume; + private bool PrevOnlyAffectsMobs; + + public HyposprayStatusControl(Entity parent, SharedSolutionContainerSystem solutionContainers) { _parent = parent; - _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}}; + _solutionContainers = solutionContainers; + _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; AddChild(_label); - - Update(); } protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); - if (!_parent.UiUpdateNeeded) + + if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution)) return; - Update(); - } - public void Update() - { + // only updates the UI if any of the details are different than they previously were + if (PrevVolume == solution.Volume + && PrevMaxVolume == solution.MaxVolume + && PrevOnlyAffectsMobs == _parent.Comp.OnlyAffectsMobs) + return; + + PrevVolume = solution.Volume; + PrevMaxVolume = solution.MaxVolume; + PrevOnlyAffectsMobs = _parent.Comp.OnlyAffectsMobs; - _parent.UiUpdateNeeded = false; + var modeStringLocalized = Loc.GetString(_parent.Comp.OnlyAffectsMobs switch + { + false => "hypospray-all-mode-text", + true => "hypospray-mobs-only-mode-text", + }); - _label.SetMarkup(Loc.GetString( - "hypospray-volume-text", - ("currentVolume", _parent.CurrentVolume), - ("totalVolume", _parent.TotalVolume))); + _label.SetMarkup(Loc.GetString("hypospray-volume-label", + ("currentVolume", solution.Volume), + ("totalVolume", solution.MaxVolume), + ("modeString", modeStringLocalized))); } } diff --git a/Content.Server/Body/Components/BloodstreamComponent.cs b/Content.Server/Body/Components/BloodstreamComponent.cs index d448c4aab21..1d8aa9ffd3d 100644 --- a/Content.Server/Body/Components/BloodstreamComponent.cs +++ b/Content.Server/Body/Components/BloodstreamComponent.cs @@ -11,7 +11,7 @@ namespace Content.Server.Body.Components { - [RegisterComponent, Access(typeof(BloodstreamSystem), (typeof(ChemistrySystem)))] + [RegisterComponent, Access(typeof(BloodstreamSystem), typeof(ReactionMixerSystem))] public sealed partial class BloodstreamComponent : Component { public static string DefaultChemicalsSolutionName = "chemicals"; diff --git a/Content.Server/Chemistry/Components/HyposprayComponent.cs b/Content.Server/Chemistry/Components/HyposprayComponent.cs deleted file mode 100644 index 2a80cec801b..00000000000 --- a/Content.Server/Chemistry/Components/HyposprayComponent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Content.Shared.Chemistry.Components; -using Content.Shared.FixedPoint; -using Robust.Shared.Audio; - -namespace Content.Server.Chemistry.Components -{ - [RegisterComponent] - public sealed partial class HyposprayComponent : SharedHyposprayComponent - { - // TODO: This should be on clumsycomponent. - [DataField("clumsyFailChance")] - [ViewVariables(VVAccess.ReadWrite)] - public float ClumsyFailChance = 0.5f; - - [DataField("transferAmount")] - [ViewVariables(VVAccess.ReadWrite)] - public FixedPoint2 TransferAmount = FixedPoint2.New(5); - - [DataField("injectSound")] - public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg"); - - /// - /// Whether or not the hypo is able to inject only into mobs. On false you can inject into beakers/jugs - /// - [DataField("onlyMobs")] - public bool OnlyMobs = true; - } -} diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs deleted file mode 100644 index c4f22dc63aa..00000000000 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Content.Server.Administration.Logs; -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Interaction; -using Content.Server.Popups; -using Content.Shared.Chemistry; -using Robust.Shared.Audio.Systems; - -namespace Content.Server.Chemistry.EntitySystems; - -public sealed partial class ChemistrySystem : EntitySystem -{ - [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly IEntityManager _entMan = default!; - [Dependency] private readonly InteractionSystem _interaction = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly ReactiveSystem _reactiveSystem = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainers = default!; - - public override void Initialize() - { - // Why ChemMaster duplicates reagentdispenser nobody knows. - InitializeHypospray(); - InitializeMixing(); - } -} diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs deleted file mode 100644 index be8faec9845..00000000000 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs +++ /dev/null @@ -1,164 +0,0 @@ -using Content.Server.Chemistry.Components; -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Components.SolutionManager; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Chemistry.Reagent; -using Content.Shared.Database; -using Content.Shared.FixedPoint; -using Content.Shared.Forensics; -using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Mobs.Components; -using Content.Shared.Timing; -using Content.Shared.Weapons.Melee.Events; -using Robust.Shared.GameStates; -using System.Diagnostics.CodeAnalysis; -using System.Linq; - -namespace Content.Server.Chemistry.EntitySystems -{ - public sealed partial class ChemistrySystem - { - [Dependency] private readonly UseDelaySystem _useDelay = default!; - - private void InitializeHypospray() - { - SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent(OnAttack); - SubscribeLocalEvent(OnSolutionChange); - SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnHypoGetState); - } - - private void OnHypoGetState(Entity entity, ref ComponentGetState args) - { - args.State = _solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out _, out var solution) - ? new HyposprayComponentState(solution.Volume, solution.MaxVolume) - : new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero); - } - - private void OnUseInHand(Entity entity, ref UseInHandEvent args) - { - if (args.Handled) - return; - - TryDoInject(entity, args.User, args.User); - args.Handled = true; - } - - private void OnSolutionChange(Entity entity, ref SolutionContainerChangedEvent args) - { - Dirty(entity); - } - - public void OnAfterInteract(Entity entity, ref AfterInteractEvent args) - { - if (!args.CanReach) - return; - - var target = args.Target; - var user = args.User; - - TryDoInject(entity, target, user); - } - - public void OnAttack(Entity entity, ref MeleeHitEvent args) - { - if (!args.HitEntities.Any()) - return; - - TryDoInject(entity, args.HitEntities.First(), args.User); - } - - public bool TryDoInject(Entity hypo, EntityUid? target, EntityUid user) - { - var (uid, component) = hypo; - - if (!EligibleEntity(target, _entMan, component)) - return false; - - if (TryComp(uid, out UseDelayComponent? delayComp)) - { - if (_useDelay.IsDelayed((uid, delayComp))) - return false; - } - - - string? msgFormat = null; - - if (target == user) - msgFormat = "hypospray-component-inject-self-message"; - else if (EligibleEntity(user, _entMan, component) && _interaction.TryRollClumsy(user, component.ClumsyFailChance)) - { - msgFormat = "hypospray-component-inject-self-clumsy-message"; - target = user; - } - - if (!_solutionContainers.TryGetSolution(uid, component.SolutionName, out var hypoSpraySoln, out var hypoSpraySolution) || hypoSpraySolution.Volume == 0) - { - _popup.PopupCursor(Loc.GetString("hypospray-component-empty-message"), user); - return true; - } - - if (!_solutionContainers.TryGetInjectableSolution(target.Value, out var targetSoln, out var targetSolution)) - { - _popup.PopupCursor(Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target.Value, _entMan))), user); - return false; - } - - _popup.PopupCursor(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message", ("other", target)), user); - - if (target != user) - { - _popup.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target.Value, target.Value); - // TODO: This should just be using melee attacks... - // meleeSys.SendLunge(angle, user); - } - - _audio.PlayPvs(component.InjectSound, user); - - // Medipens and such use this system and don't have a delay, requiring extra checks - // BeginDelay function returns if item is already on delay - if (delayComp != null) - _useDelay.TryResetDelay((uid, delayComp)); - - // Get transfer amount. May be smaller than component.TransferAmount if not enough room - var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume); - - if (realTransferAmount <= 0) - { - _popup.PopupCursor(Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target)), user); - return true; - } - - // Move units from attackSolution to targetSolution - var removedSolution = _solutionContainers.SplitSolution(hypoSpraySoln.Value, realTransferAmount); - - if (!targetSolution.CanAddSolution(removedSolution)) - return true; - _reactiveSystem.DoEntityReaction(target.Value, removedSolution, ReactionMethod.Injection); - _solutionContainers.TryAddSolution(targetSoln.Value, removedSolution); - - var ev = new TransferDnaEvent { Donor = target.Value, Recipient = uid }; - RaiseLocalEvent(target.Value, ref ev); - - // same LogType as syringes... - _adminLogger.Add(LogType.ForceFeed, $"{_entMan.ToPrettyString(user):user} injected {_entMan.ToPrettyString(target.Value):target} with a solution {SolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {_entMan.ToPrettyString(uid):using}"); - - return true; - } - - static bool EligibleEntity([NotNullWhen(true)] EntityUid? entity, IEntityManager entMan, HyposprayComponent component) - { - // TODO: Does checking for BodyComponent make sense as a "can be hypospray'd" tag? - // In SS13 the hypospray ONLY works on mobs, NOT beakers or anything else. - // But this is 14, we dont do what SS13 does just because SS13 does it. - return component.OnlyMobs - ? entMan.HasComponent(entity) && - entMan.HasComponent(entity) - : entMan.HasComponent(entity); - } - } -} diff --git a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs new file mode 100644 index 00000000000..dfbe45c035b --- /dev/null +++ b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs @@ -0,0 +1,197 @@ +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Database; +using Content.Shared.FixedPoint; +using Content.Shared.Forensics; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Mobs.Components; +using Content.Shared.Timing; +using Content.Shared.Weapons.Melee.Events; +using Content.Server.Interaction; +using Content.Server.Body.Components; +using Content.Server.Chemistry.Containers.EntitySystems; +using Robust.Shared.GameStates; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Robust.Server.Audio; + +namespace Content.Server.Chemistry.EntitySystems; + +public sealed class HypospraySystem : SharedHypospraySystem +{ + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly InteractionSystem _interaction = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnAttack); + SubscribeLocalEvent(OnUseInHand); + } + + private void UseHypospray(Entity entity, EntityUid target, EntityUid user) + { + // if target is ineligible but is a container, try to draw from the container + if (!EligibleEntity(target, EntityManager, entity) + && _solutionContainers.TryGetDrawableSolution(target, out var drawableSolution, out _)) + { + TryDraw(entity, target, drawableSolution.Value, user); + } + + TryDoInject(entity, target, user); + } + + private void OnUseInHand(Entity entity, ref UseInHandEvent args) + { + if (args.Handled) + return; + + TryDoInject(entity, args.User, args.User); + args.Handled = true; + } + + public void OnAfterInteract(Entity entity, ref AfterInteractEvent args) + { + if (args.Handled || !args.CanReach || args.Target == null) + return; + + UseHypospray(entity, args.Target.Value, args.User); + args.Handled = true; + } + + public void OnAttack(Entity entity, ref MeleeHitEvent args) + { + if (!args.HitEntities.Any()) + return; + + TryDoInject(entity, args.HitEntities.First(), args.User); + } + + public bool TryDoInject(Entity entity, EntityUid target, EntityUid user) + { + var (uid, component) = entity; + + if (!EligibleEntity(target, EntityManager, component)) + return false; + + if (TryComp(uid, out UseDelayComponent? delayComp)) + { + if (_useDelay.IsDelayed((uid, delayComp))) + return false; + } + + string? msgFormat = null; + + if (target == user) + msgFormat = "hypospray-component-inject-self-message"; + else if (EligibleEntity(user, EntityManager, component) && _interaction.TryRollClumsy(user, component.ClumsyFailChance)) + { + msgFormat = "hypospray-component-inject-self-clumsy-message"; + target = user; + } + + if (!_solutionContainers.TryGetSolution(uid, component.SolutionName, out var hypoSpraySoln, out var hypoSpraySolution) || hypoSpraySolution.Volume == 0) + { + _popup.PopupEntity(Loc.GetString("hypospray-component-empty-message"), target, user); + return true; + } + + if (!_solutionContainers.TryGetInjectableSolution(target, out var targetSoln, out var targetSolution)) + { + _popup.PopupEntity(Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target, EntityManager))), target, user); + return false; + } + + _popup.PopupEntity(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message", ("other", target)), target, user); + + if (target != user) + { + _popup.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target, target); + // TODO: This should just be using melee attacks... + // meleeSys.SendLunge(angle, user); + } + + _audio.PlayPvs(component.InjectSound, user); + + // Medipens and such use this system and don't have a delay, requiring extra checks + // BeginDelay function returns if item is already on delay + if (delayComp != null) + _useDelay.TryResetDelay((uid, delayComp)); + + // Get transfer amount. May be smaller than component.TransferAmount if not enough room + var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume); + + if (realTransferAmount <= 0) + { + _popup.PopupEntity(Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target)), target, user); + return true; + } + + // Move units from attackSolution to targetSolution + var removedSolution = _solutionContainers.SplitSolution(hypoSpraySoln.Value, realTransferAmount); + + if (!targetSolution.CanAddSolution(removedSolution)) + return true; + _reactiveSystem.DoEntityReaction(target, removedSolution, ReactionMethod.Injection); + _solutionContainers.TryAddSolution(targetSoln.Value, removedSolution); + + var ev = new TransferDnaEvent { Donor = target, Recipient = uid }; + RaiseLocalEvent(target, ref ev); + + // same LogType as syringes... + _adminLogger.Add(LogType.ForceFeed, $"{EntityManager.ToPrettyString(user):user} injected {EntityManager.ToPrettyString(target):target} with a solution {SolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {EntityManager.ToPrettyString(uid):using}"); + + return true; + } + + private void TryDraw(Entity entity, Entity target, Entity targetSolution, EntityUid user) + { + if (!_solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, + out var solution) || solution.AvailableVolume == 0) + { + return; + } + + // Get transfer amount. May be smaller than _transferAmount if not enough room, also make sure there's room in the injector + var realTransferAmount = FixedPoint2.Min(entity.Comp.TransferAmount, targetSolution.Comp.Solution.Volume, + solution.AvailableVolume); + + if (realTransferAmount <= 0) + { + _popup.PopupEntity( + Loc.GetString("injector-component-target-is-empty-message", + ("target", Identity.Entity(target, EntityManager))), + entity.Owner, user); + return; + } + + var removedSolution = _solutionContainers.Draw(target.Owner, targetSolution, realTransferAmount); + + if (!_solutionContainers.TryAddSolution(soln.Value, removedSolution)) + { + return; + } + + _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message", + ("amount", removedSolution.Volume), + ("target", Identity.Entity(target, EntityManager))), entity.Owner, user); + } + + private bool EligibleEntity(EntityUid entity, IEntityManager entMan, HyposprayComponent component) + { + // TODO: Does checking for BodyComponent make sense as a "can be hypospray'd" tag? + // In SS13 the hypospray ONLY works on mobs, NOT beakers or anything else. + // But this is 14, we dont do what SS13 does just because SS13 does it. + return component.OnlyAffectsMobs + ? entMan.HasComponent(entity) && + entMan.HasComponent(entity) + : entMan.HasComponent(entity); + } +} diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystemMixer.cs b/Content.Server/Chemistry/EntitySystems/ReactionMixerSystem.cs similarity index 76% rename from Content.Server/Chemistry/EntitySystems/ChemistrySystemMixer.cs rename to Content.Server/Chemistry/EntitySystems/ReactionMixerSystem.cs index 0230671ec98..032374d4a55 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystemMixer.cs +++ b/Content.Server/Chemistry/EntitySystems/ReactionMixerSystem.cs @@ -1,13 +1,20 @@ using Content.Shared.Chemistry.Reaction; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Popups; namespace Content.Server.Chemistry.EntitySystems; -public sealed partial class ChemistrySystem +public sealed partial class ReactionMixerSystem : EntitySystem { - public void InitializeMixing() + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainers = default!; + + public override void Initialize() { + base.Initialize(); + SubscribeLocalEvent(OnAfterInteract); } diff --git a/Content.Shared/Chemistry/Components/HyposprayComponent.cs b/Content.Shared/Chemistry/Components/HyposprayComponent.cs new file mode 100644 index 00000000000..05d202aaaa3 --- /dev/null +++ b/Content.Shared/Chemistry/Components/HyposprayComponent.cs @@ -0,0 +1,33 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Audio; + +namespace Content.Shared.Chemistry.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class HyposprayComponent : Component +{ + [DataField] + public string SolutionName = "hypospray"; + + // TODO: This should be on clumsycomponent. + [DataField] + [ViewVariables(VVAccess.ReadWrite)] + public float ClumsyFailChance = 0.5f; + + [DataField] + [ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 TransferAmount = FixedPoint2.New(5); + + [DataField] + public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg"); + + /// + /// Decides whether you can inject everything or just mobs. + /// When you can only affect mobs, you're capable of drawing from beakers. + /// + [AutoNetworkedField] + [DataField(required: true)] + public bool OnlyAffectsMobs = false; +} diff --git a/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs b/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs deleted file mode 100644 index a8df6be1090..00000000000 --- a/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Content.Shared.FixedPoint; -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared.Chemistry.Components; - -[NetworkedComponent()] -public abstract partial class SharedHyposprayComponent : Component -{ - [DataField("solutionName")] - public string SolutionName = "hypospray"; -} - -[Serializable, NetSerializable] -public sealed class HyposprayComponentState : ComponentState -{ - public FixedPoint2 CurVolume { get; } - public FixedPoint2 MaxVolume { get; } - - public HyposprayComponentState(FixedPoint2 curVolume, FixedPoint2 maxVolume) - { - CurVolume = curVolume; - MaxVolume = maxVolume; - } -} diff --git a/Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs new file mode 100644 index 00000000000..f91e5621f0a --- /dev/null +++ b/Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs @@ -0,0 +1,61 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Timing; +using Content.Shared.Verbs; +using Content.Shared.Popups; +using Robust.Shared.Player; +using Content.Shared.Administration.Logs; + +namespace Content.Shared.Chemistry.EntitySystems; + +public abstract class SharedHypospraySystem : EntitySystem +{ + [Dependency] protected readonly UseDelaySystem _useDelay = default!; + [Dependency] protected readonly SharedPopupSystem _popup = default!; + [Dependency] protected readonly SharedSolutionContainerSystem _solutionContainers = default!; + [Dependency] protected readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] protected readonly ReactiveSystem _reactiveSystem = default!; + + public override void Initialize() + { + SubscribeLocalEvent>(AddToggleModeVerb); + } + + // + // Uses the OnlyMobs field as a check to implement the ability + // to draw from jugs and containers with the hypospray + // Toggleable to allow people to inject containers if they prefer it over drawing + // + private void AddToggleModeVerb(Entity entity, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || args.Hands == null) + return; + + var (_, component) = entity; + var user = args.User; + var verb = new AlternativeVerb + { + Text = Loc.GetString("hypospray-verb-mode-label"), + Act = () => + { + ToggleMode(entity, user); + } + }; + args.Verbs.Add(verb); + } + + private void ToggleMode(Entity entity, EntityUid user) + { + SetMode(entity, !entity.Comp.OnlyAffectsMobs); + string msg = entity.Comp.OnlyAffectsMobs ? "hypospray-verb-mode-inject-mobs-only" : "hypospray-verb-mode-inject-all"; + _popup.PopupClient(Loc.GetString(msg), entity, user); + } + + public void SetMode(Entity entity, bool onlyAffectsMobs) + { + if (entity.Comp.OnlyAffectsMobs == onlyAffectsMobs) + return; + + entity.Comp.OnlyAffectsMobs = onlyAffectsMobs; + Dirty(entity); + } +} diff --git a/Content.Shared/Chemistry/EntitySystems/SharedInjectorSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedInjectorSystem.cs index 7e41cb39bd6..6c43c1d5f06 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedInjectorSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedInjectorSystem.cs @@ -37,10 +37,7 @@ private void AddSetTransferVerbs(Entity entity, ref GetVerbsE if (!args.CanAccess || !args.CanInteract || args.Hands == null) return; - if (!HasComp(args.User)) - return; var user = args.User; - var (_, component) = entity; var min = component.MinimumTransferAmount; diff --git a/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl b/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl index 7acbe8664c4..52dbf9010e3 100644 --- a/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl +++ b/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl @@ -1,13 +1,21 @@ ## UI -hypospray-volume-text = Volume: [color=white]{$currentVolume}/{$totalVolume}[/color] +hypospray-all-mode-text = Only Injects +hypospray-mobs-only-mode-text = Draws and Injects +hypospray-invalid-text = Invalid +hypospray-volume-label = Volume: [color=white]{$currentVolume}/{$totalVolume}u[/color] + Mode: [color=white]{$modeString}[/color] ## Entity hypospray-component-inject-other-message = You inject {$other}. hypospray-component-inject-self-message = You inject yourself. hypospray-component-inject-self-clumsy-message = Oops! You injected yourself. -hypospray-component-empty-message = It's empty! +hypospray-component-empty-message = Nothing to inject. hypospray-component-feel-prick-message = You feel a tiny prick! hypospray-component-transfer-already-full-message = {$owner} is already full! hypospray-cant-inject = Can't inject into {$target}! + +hypospray-verb-mode-label = Toggle Container Draw +hypospray-verb-mode-inject-all = You cannot draw from containers anymore. +hypospray-verb-mode-inject-mobs-only = You can now draw from containers. diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml index 3d28487d68e..abcabd74810 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml @@ -18,7 +18,7 @@ - type: ExaminableSolution solution: hypospray - type: Hypospray - onlyMobs: false + onlyAffectsMobs: false - type: UseDelay delay: 0.5 - type: StaticPrice @@ -49,7 +49,7 @@ - type: ExaminableSolution solution: hypospray - type: Hypospray - onlyMobs: false + onlyAffectsMobs: false - type: UseDelay delay: 0.5 @@ -73,6 +73,7 @@ - type: ExaminableSolution solution: hypospray - type: Hypospray + onlyAffectsMobs: false - type: UseDelay delay: 0.5 @@ -113,6 +114,7 @@ - type: Hypospray solutionName: pen transferAmount: 15 + onlyAffectsMobs: false - type: Appearance - type: SolutionContainerVisuals maxFillLevels: 1 @@ -202,6 +204,7 @@ - type: Hypospray solutionName: pen transferAmount: 20 + onlyAffectsMobs: false - type: SolutionContainerManager solutions: pen: @@ -232,6 +235,7 @@ - type: Hypospray solutionName: pen transferAmount: 20 + onlyAffectsMobs: false - type: SolutionContainerManager solutions: pen: @@ -262,6 +266,7 @@ - type: Hypospray solutionName: pen transferAmount: 20 + onlyAffectsMobs: false - type: SolutionContainerManager solutions: pen: @@ -293,6 +298,7 @@ - type: Hypospray solutionName: pen transferAmount: 30 + onlyAffectsMobs: false - type: SolutionContainerManager solutions: pen: @@ -330,6 +336,7 @@ - type: Hypospray solutionName: pen transferAmount: 30 + onlyAffectsMobs: false - type: StaticPrice price: 500 - type: Tag @@ -389,6 +396,7 @@ - type: Hypospray solutionName: pen transferAmount: 30 + onlyAffectsMobs: false - type: StaticPrice price: 500 - type: Tag @@ -410,7 +418,7 @@ - type: ExaminableSolution solution: hypospray - type: Hypospray - onlyMobs: false + onlyAffectsMobs: false - type: UseDelay delay: 0.5 - type: StaticPrice # A new shitcurity meta From 431c3ad3b893b01b80e18ce0c00daffd2f77b046 Mon Sep 17 00:00:00 2001 From: EdenTheLiznerd <138748328+EdenTheLiznerd@users.noreply.github.com> Date: Fri, 29 Mar 2024 22:00:21 -0600 Subject: [PATCH 021/133] Rebalance amatoxin so it is a slower killer (#25830) * Balancing? Balancing!!! * Additional changes --- Resources/Prototypes/Reagents/toxins.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Reagents/toxins.yml b/Resources/Prototypes/Reagents/toxins.yml index 02e0b995193..6e1ad75a35a 100644 --- a/Resources/Prototypes/Reagents/toxins.yml +++ b/Resources/Prototypes/Reagents/toxins.yml @@ -425,11 +425,12 @@ color: "#D6CE7B" metabolisms: Poison: + metabolismRate: 0.2 effects: - !type:HealthChange damage: types: - Poison: 6 + Poison: 3 - type: reagent id: VentCrud From 299625772d86f28d847cde3104b6bf809033a2e5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 04:00:22 +0000 Subject: [PATCH 022/133] 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 b4ec1e0b339..2ca08113a33 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: tday - changes: - - message: Added admin log messages for adding and ending game rules, and for the - commands to do so. - type: Add - - message: Added admin log messages for secret mode rule selection. - type: Add - id: 5757 - time: '2024-01-20T18:02:13.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24092 - author: Nimfar11 changes: - message: Adds snake kebab and its recipe. @@ -3794,3 +3784,11 @@ id: 6256 time: '2024-03-30T03:35:43.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26504 +- author: Plykiya + changes: + - message: Hyposprays can now be toggled to draw from solution containers like jugs + and beakers. + type: Tweak + id: 6257 + time: '2024-03-30T03:59:17.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25544 From aa96baeb5fd68f7877c60ed20fdfdd3edf0ade02 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 04:01:31 +0000 Subject: [PATCH 023/133] 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 2ca08113a33..8c70fdc6380 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Nimfar11 - changes: - - message: Adds snake kebab and its recipe. - type: Add - id: 5758 - time: '2024-01-20T23:38:11.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24341 - author: Alekshhh changes: - message: Cerberus now has a wideswing that works similarly to spears. @@ -3792,3 +3785,11 @@ id: 6257 time: '2024-03-30T03:59:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25544 +- author: EdenTheLiznerd + changes: + - message: Amanita toxin now kills you slightly slower, providing you time to seek + charcoal before it's too late + type: Tweak + id: 6258 + time: '2024-03-30T04:00:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25830 From 888a3bda515a214733bf748c5d6a7e7c923a301e Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:17:53 +1300 Subject: [PATCH 024/133] Atmos device performance improvements (#26493) * Atmos device performance improvements * AtmosDirection perf improvements * Fix errors * Add GasTileOverlayComponent arguments * Make excite no longer invalidate a tile --- .../Atmos/EntitySystems/AirFilterSystem.cs | 10 +-- .../Atmos/EntitySystems/AtmosExposedSystem.cs | 12 +-- .../EntitySystems/AtmosphereSystem.API.cs | 86 +++++++++++-------- .../AtmosphereSystem.ExcitedGroup.cs | 12 ++- .../AtmosphereSystem.GridAtmosphere.cs | 11 +-- .../EntitySystems/AtmosphereSystem.Hotspot.cs | 14 +-- .../EntitySystems/AtmosphereSystem.LINDA.cs | 18 ++-- .../AtmosphereSystem.Monstermos.cs | 40 +++++---- .../AtmosphereSystem.Processing.cs | 46 ++++++---- .../AtmosphereSystem.Superconductivity.cs | 2 +- .../EntitySystems/AtmosphereSystem.Utils.cs | 12 ++- .../Atmos/EntitySystems/AtmosphereSystem.cs | 2 +- .../Atmos/EntitySystems/GasTankSystem.cs | 2 +- .../EntitySystems/GasTileOverlaySystem.cs | 8 +- .../EntitySystems/HeatExchangerSystem.cs | 20 ++--- .../Monitor/Systems/AtmosMonitoringSystem.cs | 6 +- .../EntitySystems/GasPassiveGateSystem.cs | 6 +- .../EntitySystems/GasPressurePumpSystem.cs | 4 +- .../Binary/EntitySystems/GasRecyclerSystem.cs | 10 +-- .../Binary/EntitySystems/GasValveSystem.cs | 5 +- .../EntitySystems/GasVolumePumpSystem.cs | 7 +- .../Piping/Components/AtmosDeviceComponent.cs | 20 +++-- .../Piping/EntitySystems/AtmosDeviceSystem.cs | 3 +- .../Other/EntitySystems/GasMinerSystem.cs | 4 +- .../Trinary/EntitySystems/GasFilterSystem.cs | 15 ++-- .../Trinary/EntitySystems/GasMixerSystem.cs | 14 +-- .../PressureControlledValveSystem.cs | 6 +- .../Unary/EntitySystems/GasCanisterSystem.cs | 4 +- .../Unary/EntitySystems/GasCondenserSystem.cs | 5 +- .../EntitySystems/GasOutletInjectorSystem.cs | 10 +-- .../EntitySystems/GasPassiveVentSystem.cs | 7 +- .../Unary/EntitySystems/GasPortableSystem.cs | 5 +- .../EntitySystems/GasThermoMachineSystem.cs | 5 +- .../Unary/EntitySystems/GasVentPumpSystem.cs | 12 +-- .../EntitySystems/GasVentScrubberSystem.cs | 19 ++-- .../Atmos/Portable/PortableScrubberSystem.cs | 32 +++---- .../Atmos/Portable/SpaceHeaterSystem.cs | 2 +- .../Electrocution/ElectrocutionNode.cs | 9 +- .../EntitySystems/ExplosionGridTileFlood.cs | 4 +- .../EntitySystems/ExplosionSpaceTileFlood.cs | 2 +- Content.Server/Mech/Systems/MechSystem.cs | 11 ++- Content.Server/Medical/CryoPodSystem.cs | 15 +--- .../EntitySystems/NodeContainerSystem.cs | 74 ++++++++++++++++ .../PneumaticCannon/PneumaticCannonSystem.cs | 2 +- .../Power/Components/CableVisComponent.cs | 4 +- .../Power/EntitySystems/CableVisSystem.cs | 7 +- .../Power/Generator/GasPowerReceiverSystem.cs | 8 +- Content.Server/Spreader/SpreaderSystem.cs | 7 +- Content.Shared/Atmos/AtmosDirection.cs | 24 +++++- .../Tools/Systems/WeldableSystem.cs | 32 +++---- .../Entities/Virtual/electrocution.yml | 2 +- 51 files changed, 373 insertions(+), 324 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs index d947e60b6da..2ab15cfb174 100644 --- a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs @@ -13,6 +13,7 @@ public sealed class AirFilterSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly IMapManager _map = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() { @@ -31,7 +32,7 @@ private void OnIntakeUpdate(EntityUid uid, AirIntakeComponent intake, ref AtmosD if (air.Pressure >= intake.Pressure) return; - var environment = _atmosphere.GetContainingMixture(uid, true, true); + var environment = _atmosphere.GetContainingMixture(uid, args.Grid, args.Map, true, true); // nothing to intake from if (environment == null) return; @@ -63,12 +64,11 @@ private void OnFilterUpdate(EntityUid uid, AirFilterComponent filter, ref AtmosD var oxygen = air.GetMoles(filter.Oxygen) / air.TotalMoles; var gases = oxygen >= filter.TargetOxygen ? filter.Gases : filter.OverflowGases; - var coordinates = Transform(uid).MapPosition; GasMixture? destination = null; - if (_map.TryFindGridAt(coordinates, out _, out var grid)) + if (args.Grid is {} grid) { - var tile = grid.GetTileRef(coordinates); - destination = _atmosphere.GetTileMixture(tile.GridUid, null, tile.GridIndices, true); + var position = _transform.GetGridTilePositionOrDefault(uid); + destination = _atmosphere.GetTileMixture(grid, args.Map, position, true); } if (destination != null) diff --git a/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs b/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs index 4be4d8271f3..9590b9aa548 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs @@ -24,6 +24,8 @@ public AtmosExposedUpdateEvent(EntityCoordinates coordinates, GasMixture mixture /// /// Event that tries to query the mixture a certain entity is exposed to. + /// This is mainly intended for use with entities inside of containers. + /// This event is not raised for entities that are directly parented to the grid. /// [ByRefEvent] public struct AtmosExposedGetAirEvent @@ -31,7 +33,7 @@ public struct AtmosExposedGetAirEvent /// /// The entity we want to query this for. /// - public readonly EntityUid Entity; + public readonly Entity Entity; /// /// The mixture that the entity is exposed to. Output parameter. @@ -39,9 +41,9 @@ public struct AtmosExposedGetAirEvent public GasMixture? Gas = null; /// - /// Whether to invalidate the mixture, if possible. + /// Whether to excite the mixture, if possible. /// - public bool Invalidate = false; + public readonly bool Excite = false; /// /// Whether this event has been handled or not. @@ -49,10 +51,10 @@ public struct AtmosExposedGetAirEvent /// public bool Handled = false; - public AtmosExposedGetAirEvent(EntityUid entity, bool invalidate = false) + public AtmosExposedGetAirEvent(Entity entity, bool excite = false) { Entity = entity; - Invalidate = invalidate; + Excite = excite; } } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs index fb94fe414b0..614d550c2f7 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs @@ -4,6 +4,7 @@ using Content.Server.Atmos.Reactions; using Content.Server.NodeContainer.NodeGroups; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; using Robust.Shared.Map.Components; using Robust.Shared.Utility; @@ -11,41 +12,39 @@ namespace Content.Server.Atmos.EntitySystems; public partial class AtmosphereSystem { - public GasMixture? GetContainingMixture(EntityUid uid, bool ignoreExposed = false, bool excite = false, TransformComponent? transform = null) + public GasMixture? GetContainingMixture(Entity ent, bool ignoreExposed = false, bool excite = false) { - if (!ignoreExposed) - { - // Used for things like disposals/cryo to change which air people are exposed to. - var ev = new AtmosExposedGetAirEvent(uid, excite); - - // Give the entity itself a chance to handle this. - RaiseLocalEvent(uid, ref ev, false); - - if (ev.Handled) - return ev.Gas; + if (!Resolve(ent, ref ent.Comp)) + return null; - // We need to get the parent now, so we need the transform... If the parent is invalid, we can't do much else. - if(!Resolve(uid, ref transform) || !transform.ParentUid.IsValid() || transform.MapUid == null) - return GetTileMixture(null, null, Vector2i.Zero, excite); + return GetContainingMixture(ent, ent.Comp.GridUid, ent.Comp.MapUid, ignoreExposed, excite); + } - // Give the parent entity a chance to handle the event... - RaiseLocalEvent(transform.ParentUid, ref ev, false); + public GasMixture? GetContainingMixture( + Entity ent, + Entity? grid, + Entity? map, + bool ignoreExposed = false, + bool excite = false) + { + if (!Resolve(ent, ref ent.Comp)) + return null; + if (!ignoreExposed && !ent.Comp.Anchored) + { + // Used for things like disposals/cryo to change which air people are exposed to. + var ev = new AtmosExposedGetAirEvent((ent, ent.Comp), excite); + RaiseLocalEvent(ent, ref ev); if (ev.Handled) return ev.Gas; - } - // Oops, we did a little bit of code duplication... - else if(!Resolve(uid, ref transform)) - { - return GetTileMixture(null, null, Vector2i.Zero, excite); - } + // TODO ATMOS: recursively iterate up through parents + // This really needs recursive InContainer metadata flag for performance + // And ideally some fast way to get the innermost airtight container. + } - var gridUid = transform.GridUid; - var mapUid = transform.MapUid; - var position = _transformSystem.GetGridOrMapTilePosition(uid, transform); - - return GetTileMixture(gridUid, mapUid, position, excite); + var position = _transformSystem.GetGridTilePositionOrDefault((ent, ent.Comp)); + return GetTileMixture(grid, map, position, excite); } public bool HasAtmosphere(EntityUid gridUid) => _atmosQuery.HasComponent(gridUid); @@ -84,21 +83,28 @@ public void InvalidateTile(Entity entity, Vector2i til entity.Comp.InvalidatedCoords.Add(tile); } - public GasMixture?[]? GetTileMixtures(Entity? grid, Entity? map, List tiles, bool excite = false) + public GasMixture?[]? GetTileMixtures( + Entity? grid, + Entity? map, + List tiles, + bool excite = false) { GasMixture?[]? mixtures = null; var handled = false; // If we've been passed a grid, try to let it handle it. - if (grid is {} gridEnt && Resolve(gridEnt, ref gridEnt.Comp)) + if (grid is {} gridEnt && Resolve(gridEnt, ref gridEnt.Comp1)) { + if (excite) + Resolve(gridEnt, ref gridEnt.Comp2); + handled = true; mixtures = new GasMixture?[tiles.Count]; for (var i = 0; i < tiles.Count; i++) { var tile = tiles[i]; - if (!gridEnt.Comp.Tiles.TryGetValue(tile, out var atmosTile)) + if (!gridEnt.Comp1.Tiles.TryGetValue(tile, out var atmosTile)) { // need to get map atmosphere handled = false; @@ -108,7 +114,10 @@ public void InvalidateTile(Entity entity, Vector2i til mixtures[i] = atmosTile.Air; if (excite) - gridEnt.Comp.InvalidatedCoords.Add(tile); + { + AddActiveTile(gridEnt.Comp1, atmosTile); + InvalidateVisuals((gridEnt.Owner, gridEnt.Comp2), tile); + } } } @@ -146,15 +155,22 @@ public void InvalidateTile(Entity entity, Vector2i til return GetTileMixture(entity.Comp.GridUid, entity.Comp.MapUid, indices, excite); } - public GasMixture? GetTileMixture(Entity? grid, Entity? map, Vector2i gridTile, bool excite = false) + public GasMixture? GetTileMixture( + Entity? grid, + Entity? map, + Vector2i gridTile, + bool excite = false) { // If we've been passed a grid, try to let it handle it. if (grid is {} gridEnt - && Resolve(gridEnt, ref gridEnt.Comp, false) - && gridEnt.Comp.Tiles.TryGetValue(gridTile, out var tile)) + && Resolve(gridEnt, ref gridEnt.Comp1, false) + && gridEnt.Comp1.Tiles.TryGetValue(gridTile, out var tile)) { if (excite) - gridEnt.Comp.InvalidatedCoords.Add(gridTile); + { + AddActiveTile(gridEnt.Comp1, tile); + InvalidateVisuals((grid.Value.Owner, grid.Value.Comp2), gridTile); + } return tile.Air; } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs index de4c9199cf7..c1b58f7a772 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs @@ -1,5 +1,7 @@ using Content.Server.Atmos.Components; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; +using Robust.Shared.Map.Components; using Robust.Shared.Utility; namespace Content.Server.Atmos.EntitySystems @@ -64,10 +66,12 @@ private void ExcitedGroupResetCooldowns(ExcitedGroup excitedGroup) excitedGroup.DismantleCooldown = 0; } - private void ExcitedGroupSelfBreakdown(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup) + private void ExcitedGroupSelfBreakdown( + Entity ent, + ExcitedGroup excitedGroup) { DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!"); - DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!"); + DebugTools.Assert(ent.Comp1.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!"); var combined = new GasMixture(Atmospherics.CellVolume); var tileSize = excitedGroup.Tiles.Count; @@ -77,7 +81,7 @@ private void ExcitedGroupSelfBreakdown(GridAtmosphereComponent gridAtmosphere, E if (tileSize == 0) { - ExcitedGroupDispose(gridAtmosphere, excitedGroup); + ExcitedGroupDispose(ent.Comp1, excitedGroup); return; } @@ -103,7 +107,7 @@ private void ExcitedGroupSelfBreakdown(GridAtmosphereComponent gridAtmosphere, E continue; tile.Air.CopyFromMutable(combined); - InvalidateVisuals(tile.GridIndex, tile.GridIndices); + InvalidateVisuals(ent, tile); } excitedGroup.BreakdownCooldown = 0; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs index bd45030896f..4b9ef49a406 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs @@ -1,4 +1,3 @@ -using System.Linq; using Content.Server.Atmos.Components; using Content.Server.Atmos.Reactions; using Content.Shared.Atmos; @@ -160,7 +159,7 @@ private void GridReactTile(EntityUid uid, GridAtmosphereComponent component, ref } /// - /// Update array of adjacent tiles and the adjacency flags. Optionally activates all tiles with modified adjacencies. + /// Update array of adjacent tiles and the adjacency flags. /// private void UpdateAdjacentTiles( Entity ent, @@ -195,14 +194,16 @@ private void UpdateAdjacentTiles( if (activate) AddActiveTile(atmos, adjacent); - var oppositeDirection = direction.GetOpposite(); + var oppositeIndex = i.ToOppositeIndex(); + var oppositeDirection = (AtmosDirection) (1 << oppositeIndex); + if (adjBlockDirs.IsFlagSet(oppositeDirection) || blockedDirs.IsFlagSet(direction)) { // Adjacency is blocked by some airtight entity. tile.AdjacentBits &= ~direction; adjacent.AdjacentBits &= ~oppositeDirection; tile.AdjacentTiles[i] = null; - adjacent.AdjacentTiles[oppositeDirection.ToIndex()] = null; + adjacent.AdjacentTiles[oppositeIndex] = null; } else { @@ -210,7 +211,7 @@ private void UpdateAdjacentTiles( tile.AdjacentBits |= direction; adjacent.AdjacentBits |= oppositeDirection; tile.AdjacentTiles[i] = adjacent; - adjacent.AdjacentTiles[oppositeDirection.ToIndex()] = tile; + adjacent.AdjacentTiles[oppositeIndex] = tile; } DebugTools.Assert(!(tile.AdjacentBits.IsFlagSet(direction) ^ diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs index 713d1c4682c..7163b4cd44c 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs @@ -1,10 +1,12 @@ using Content.Server.Atmos.Components; using Content.Server.Atmos.Reactions; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; using Content.Shared.Audio; using Content.Shared.Database; using Robust.Shared.Audio; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Player; namespace Content.Server.Atmos.EntitySystems @@ -18,18 +20,18 @@ public sealed partial class AtmosphereSystem [ViewVariables(VVAccess.ReadWrite)] public string? HotspotSound { get; private set; } = "/Audio/Effects/fire.ogg"; - private void ProcessHotspot(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) + private void ProcessHotspot( + Entity ent, + TileAtmosphere tile) { + var gridAtmosphere = ent.Comp1; if (!tile.Hotspot.Valid) { gridAtmosphere.HotspotTiles.Remove(tile); return; } - if (!tile.Excited) - { - AddActiveTile(gridAtmosphere, tile); - } + AddActiveTile(gridAtmosphere, tile); if (!tile.Hotspot.SkippedFirstProcess) { @@ -44,7 +46,7 @@ private void ProcessHotspot(GridAtmosphereComponent gridAtmosphere, TileAtmosphe || tile.Air == null || tile.Air.GetMoles(Gas.Oxygen) < 0.5f || (tile.Air.GetMoles(Gas.Plasma) < 0.5f && tile.Air.GetMoles(Gas.Tritium) < 0.5f)) { tile.Hotspot = new Hotspot(); - InvalidateVisuals(tile.GridIndex, tile.GridIndices); + InvalidateVisuals(ent, tile); return; } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs index c27e18b55b0..fb2375899d9 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs @@ -1,14 +1,18 @@ using Content.Server.Atmos.Components; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; +using Robust.Shared.Map.Components; using Robust.Shared.Utility; namespace Content.Server.Atmos.EntitySystems { public sealed partial class AtmosphereSystem { - private void ProcessCell(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int fireCount, GasTileOverlayComponent visuals) + private void ProcessCell( + Entity ent, + TileAtmosphere tile, int fireCount) { + var gridAtmosphere = ent.Comp1; // Can't process a tile without air if (tile.Air == null) { @@ -52,11 +56,7 @@ private void ProcessCell(GridAtmosphereComponent gridAtmosphere, TileAtmosphere shouldShareAir = true; } else if (CompareExchange(tile.Air, enemyTile.Air) != GasCompareResult.NoExchange) { - if (!enemyTile.Excited) - { - AddActiveTile(gridAtmosphere, enemyTile); - } - + AddActiveTile(gridAtmosphere, enemyTile); if (ExcitedGroups) { var excitedGroup = tile.ExcitedGroup; @@ -91,7 +91,7 @@ private void ProcessCell(GridAtmosphereComponent gridAtmosphere, TileAtmosphere } else { - ConsiderPressureDifference(gridAtmosphere, enemyTile, direction.GetOpposite(), -difference); + ConsiderPressureDifference(gridAtmosphere, enemyTile, i.ToOppositeDir(), -difference); } } @@ -102,7 +102,7 @@ private void ProcessCell(GridAtmosphereComponent gridAtmosphere, TileAtmosphere if(tile.Air != null) React(tile.Air, tile); - InvalidateVisuals(tile.GridIndex, tile.GridIndices, visuals); + InvalidateVisuals(ent, tile); var remove = true; @@ -146,7 +146,7 @@ private void LastShareCheck(TileAtmosphere tile) /// Tile Atmosphere to be activated. private void AddActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { - if (tile.Air == null) + if (tile.Air == null || tile.Excited) return; tile.Excited = true; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs index dcbc1e86ee2..08193027d67 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs @@ -230,7 +230,7 @@ private void EqualizePressureInZone( if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; _equalizeQueue[queueLength++] = otherTile2; otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; - otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); + otherTile2.MonstermosInfo.CurrentTransferDirection = k.ToOppositeDir(); otherTile2.MonstermosInfo.CurrentTransferAmount = 0; if (otherTile2.MonstermosInfo.MoleDelta < 0) { @@ -296,7 +296,7 @@ private void EqualizePressureInZone( if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; _equalizeQueue[queueLength++] = otherTile2; otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; - otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); + otherTile2.MonstermosInfo.CurrentTransferDirection = k.ToOppositeDir(); otherTile2.MonstermosInfo.CurrentTransferAmount = 0; if (otherTile2.MonstermosInfo.MoleDelta > 0) @@ -338,7 +338,7 @@ private void EqualizePressureInZone( for (var i = 0; i < tileCount; i++) { var otherTile = _equalizeTiles[i]!; - FinalizeEq(gridAtmosphere, otherTile, ent); + FinalizeEq(ent, otherTile); } for (var i = 0; i < tileCount; i++) @@ -473,7 +473,7 @@ private void ExplosivelyDepressurize( if(tile2.Space) continue; - tile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); + tile2.MonstermosInfo.CurrentTransferDirection = j.ToOppositeDir(); tile2.MonstermosInfo.CurrentTransferAmount = 0.0f; tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget; tile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; @@ -549,7 +549,7 @@ private void ExplosivelyDepressurize( otherTile.Air.Temperature = Atmospherics.TCMB; } - InvalidateVisuals(otherTile.GridIndex, otherTile.GridIndices, visuals); + InvalidateVisuals(ent, otherTile); HandleDecompressionFloorRip(mapGrid, otherTile, otherTile.MonstermosInfo.CurrentTransferAmount); } @@ -598,11 +598,13 @@ private void ConsiderFirelocks( UpdateAdjacentTiles(ent, tile); UpdateAdjacentTiles(ent, other); - InvalidateVisuals(tile.GridIndex, tile.GridIndices, ent); - InvalidateVisuals(other.GridIndex, other.GridIndices, ent); + InvalidateVisuals(ent, tile); + InvalidateVisuals(ent, other); } - private void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, GasTileOverlayComponent? visuals) + private void FinalizeEq( + Entity ent, + TileAtmosphere tile) { Span transferDirections = stackalloc float[Atmospherics.Directions]; var hasTransferDirs = false; @@ -629,17 +631,19 @@ private void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere t // Everything that calls this method already ensures that Air will not be null. if (tile.Air!.TotalMoles < amount) - FinalizeEqNeighbors(gridAtmosphere, tile, transferDirections, visuals); + FinalizeEqNeighbors(ent, tile, transferDirections); - otherTile.MonstermosInfo[direction.GetOpposite()] = 0; + otherTile.MonstermosInfo[i.ToOppositeDir()] = 0; Merge(otherTile.Air, tile.Air.Remove(amount)); - InvalidateVisuals(tile.GridIndex, tile.GridIndices, visuals); - InvalidateVisuals(otherTile.GridIndex, otherTile.GridIndices, visuals); - ConsiderPressureDifference(gridAtmosphere, tile, direction, amount); + InvalidateVisuals(ent, tile); + InvalidateVisuals(ent, otherTile); + ConsiderPressureDifference(ent, tile, direction, amount); } } - private void FinalizeEqNeighbors(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, ReadOnlySpan transferDirs, GasTileOverlayComponent? visuals) + private void FinalizeEqNeighbors( + Entity ent, + TileAtmosphere tile, ReadOnlySpan transferDirs) { for (var i = 0; i < Atmospherics.Directions; i++) { @@ -647,7 +651,7 @@ private void FinalizeEqNeighbors(GridAtmosphereComponent gridAtmosphere, TileAtm var amount = transferDirs[i]; // Since AdjacentBits is set, AdjacentTiles[i] wouldn't be null, and neither would its air. if(amount < 0 && tile.AdjacentBits.IsFlagSet(direction)) - FinalizeEq(gridAtmosphere, tile.AdjacentTiles[i]!, visuals); // A bit of recursion if needed. + FinalizeEq(ent, tile.AdjacentTiles[i]!); // A bit of recursion if needed. } } @@ -664,7 +668,9 @@ private void AdjustEqMovement(TileAtmosphere tile, AtmosDirection direction, flo Log.Error($"Encountered null-tile in {nameof(AdjustEqMovement)}. Trace: {Environment.StackTrace}"); return; } - var adj = tile.AdjacentTiles[direction.ToIndex()]; + + var idx = direction.ToIndex(); + var adj = tile.AdjacentTiles[idx]; if (adj == null) { var nonNull = tile.AdjacentTiles.Where(x => x != null).Count(); @@ -673,7 +679,7 @@ private void AdjustEqMovement(TileAtmosphere tile, AtmosDirection direction, flo } tile.MonstermosInfo[direction] += amount; - adj.MonstermosInfo[direction.GetOpposite()] -= amount; + adj.MonstermosInfo[idx.ToOppositeDir()] -= amount; } private void HandleDecompressionFloorRip(MapGridComponent mapGrid, TileAtmosphere tile, float sum) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs index eba398c1821..bd023e8574a 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs @@ -86,7 +86,7 @@ private bool ProcessRevalidate(Entity ent) { + var atmosphere = ent.Comp1; if(!atmosphere.ProcessingPaused) QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.ActiveTiles); var number = 0; while (atmosphere.CurrentRunTiles.TryDequeue(out var tile)) { - ProcessCell(atmosphere, tile, atmosphere.UpdateCounter, visuals); + ProcessCell(ent, tile, atmosphere.UpdateCounter); if (number++ < LagCheckIterations) continue; @@ -337,8 +339,10 @@ private bool ProcessActiveTiles(GridAtmosphereComponent atmosphere, GasTileOverl return true; } - private bool ProcessExcitedGroups(GridAtmosphereComponent gridAtmosphere) + private bool ProcessExcitedGroups( + Entity ent) { + var gridAtmosphere = ent.Comp1; if (!gridAtmosphere.ProcessingPaused) { gridAtmosphere.CurrentRunExcitedGroups.Clear(); @@ -356,7 +360,7 @@ private bool ProcessExcitedGroups(GridAtmosphereComponent gridAtmosphere) excitedGroup.DismantleCooldown++; if (excitedGroup.BreakdownCooldown > Atmospherics.ExcitedGroupBreakdownCycles) - ExcitedGroupSelfBreakdown(gridAtmosphere, excitedGroup); + ExcitedGroupSelfBreakdown(ent, excitedGroup); else if (excitedGroup.DismantleCooldown > Atmospherics.ExcitedGroupsDismantleCycles) DeactivateGroupTiles(gridAtmosphere, excitedGroup); // TODO ATMOS. What is the point of this? why is this only de-exciting the group? Shouldn't it also dismantle it? @@ -411,15 +415,17 @@ private bool ProcessHighPressureDelta(Entity ent) return true; } - private bool ProcessHotspots(GridAtmosphereComponent atmosphere) + private bool ProcessHotspots( + Entity ent) { + var atmosphere = ent.Comp1; if(!atmosphere.ProcessingPaused) QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.HotspotTiles); var number = 0; while (atmosphere.CurrentRunTiles.TryDequeue(out var hotspot)) { - ProcessHotspot(atmosphere, hotspot); + ProcessHotspot(ent, hotspot); if (number++ < LagCheckIterations) continue; @@ -507,8 +513,11 @@ public float RealAtmosTime() return num * AtmosTime; } - private bool ProcessAtmosDevices(GridAtmosphereComponent atmosphere) + private bool ProcessAtmosDevices( + Entity ent, + Entity map) { + var atmosphere = ent.Comp1; if (!atmosphere.ProcessingPaused) { atmosphere.CurrentRunAtmosDevices.Clear(); @@ -521,7 +530,7 @@ private bool ProcessAtmosDevices(GridAtmosphereComponent atmosphere) var time = _gameTiming.CurTime; var number = 0; - var ev = new AtmosDeviceUpdateEvent(RealAtmosTime()); + var ev = new AtmosDeviceUpdateEvent(RealAtmosTime(), (ent, ent.Comp1, ent.Comp2), map); while (atmosphere.CurrentRunAtmosDevices.TryDequeue(out var device)) { RaiseLocalEvent(device, ref ev); @@ -565,12 +574,11 @@ private void UpdateProcessing(float frameTime) var ent = _currentRunAtmosphere[_currentRunAtmosphereIndex]; var (owner, atmosphere, visuals, grid, xform) = ent; - if (!TryComp(owner, out TransformComponent? x) - || x.MapUid == null - || TerminatingOrDeleted(x.MapUid.Value) - || x.MapID == MapId.Nullspace) + if (xform.MapUid == null + || TerminatingOrDeleted(xform.MapUid.Value) + || xform.MapID == MapId.Nullspace) { - Log.Error($"Attempted to process atmos without a map? Entity: {ToPrettyString(owner)}. Map: {ToPrettyString(x?.MapUid)}. MapId: {x?.MapID}"); + Log.Error($"Attempted to process atmos without a map? Entity: {ToPrettyString(owner)}. Map: {ToPrettyString(xform?.MapUid)}. MapId: {xform?.MapID}"); continue; } @@ -585,6 +593,8 @@ private void UpdateProcessing(float frameTime) // We subtract it so it takes lost time into account. atmosphere.Timer -= AtmosTime; + var map = new Entity(xform.MapUid.Value, _mapAtmosQuery.CompOrNull(xform.MapUid.Value)); + switch (atmosphere.State) { case AtmosphereProcessingState.Revalidate: @@ -614,7 +624,7 @@ private void UpdateProcessing(float frameTime) atmosphere.State = AtmosphereProcessingState.ActiveTiles; continue; case AtmosphereProcessingState.ActiveTiles: - if (!ProcessActiveTiles(ent, ent)) + if (!ProcessActiveTiles(ent)) { atmosphere.ProcessingPaused = true; return; @@ -625,7 +635,7 @@ private void UpdateProcessing(float frameTime) atmosphere.State = ExcitedGroups ? AtmosphereProcessingState.ExcitedGroups : AtmosphereProcessingState.HighPressureDelta; continue; case AtmosphereProcessingState.ExcitedGroups: - if (!ProcessExcitedGroups(atmosphere)) + if (!ProcessExcitedGroups(ent)) { atmosphere.ProcessingPaused = true; return; @@ -645,7 +655,7 @@ private void UpdateProcessing(float frameTime) atmosphere.State = AtmosphereProcessingState.Hotspots; continue; case AtmosphereProcessingState.Hotspots: - if (!ProcessHotspots(atmosphere)) + if (!ProcessHotspots(ent)) { atmosphere.ProcessingPaused = true; return; @@ -680,7 +690,7 @@ private void UpdateProcessing(float frameTime) atmosphere.State = AtmosphereProcessingState.AtmosDevices; continue; case AtmosphereProcessingState.AtmosDevices: - if (!ProcessAtmosDevices(atmosphere)) + if (!ProcessAtmosDevices(ent, map)) { atmosphere.ProcessingPaused = true; return; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs index 5c73cf11246..8ed92a9d0eb 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs @@ -16,7 +16,7 @@ private void Superconduct(GridAtmosphereComponent gridAtmosphere, TileAtmosphere if (!directions.IsFlagSet(direction)) continue; - var adjacent = tile.AdjacentTiles[direction.ToIndex()]; + var adjacent = tile.AdjacentTiles[i]; // TODO ATMOS handle adjacent being null. if (adjacent == null || adjacent.ThermalConductivity == 0f) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs index 67c6d5998dd..cf4c73aa2f4 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs @@ -36,9 +36,17 @@ public double GetPrice(GasMixture mixture) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void InvalidateVisuals(EntityUid gridUid, Vector2i tile, GasTileOverlayComponent? comp = null) + public void InvalidateVisuals(Entity grid, Vector2i tile) { - _gasTileOverlaySystem.Invalidate(gridUid, tile, comp); + _gasTileOverlaySystem.Invalidate(grid, tile); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void InvalidateVisuals( + Entity ent, + TileAtmosphere tile) + { + _gasTileOverlaySystem.Invalidate((ent.Owner, ent.Comp2), tile.GridIndices); } /// diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs index a5537665827..44bfa4cc10c 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs @@ -96,7 +96,7 @@ public override void Update(float frameTime) var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out _, out var transform)) { - var air = GetContainingMixture(uid, transform:transform); + var air = GetContainingMixture((uid, transform)); if (air == null) continue; diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index aed00432e1f..80842416e8b 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -168,7 +168,7 @@ public override void Update(float frameTime) private void ReleaseGas(Entity gasTank) { var removed = RemoveAirVolume(gasTank, gasTank.Comp.ValveOutputRate * TimerDelay); - var environment = _atmosphereSystem.GetContainingMixture(gasTank, false, true); + var environment = _atmosphereSystem.GetContainingMixture(gasTank.Owner, false, true); if (environment != null) { _atmosphereSystem.Merge(environment, removed); diff --git a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs index 003eed59e03..c42cfd08da3 100644 --- a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -60,6 +60,7 @@ public sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem private float _updateInterval; private int _thresholds; + private EntityQuery _query; public override void Initialize() { @@ -84,6 +85,7 @@ public override void Initialize() SubscribeLocalEvent(Reset); SubscribeLocalEvent(OnStartup); + _query = GetEntityQuery(); } private void OnStartup(EntityUid uid, GasTileOverlayComponent component, ComponentStartup args) @@ -132,10 +134,10 @@ private void OnPvsToggle(bool value) private void UpdateThresholds(int value) => _thresholds = value; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Invalidate(EntityUid grid, Vector2i index, GasTileOverlayComponent? comp = null) + public void Invalidate(Entity grid, Vector2i index) { - if (Resolve(grid, ref comp)) - comp.InvalidTiles.Add(index); + if (_query.Resolve(grid.Owner, ref grid.Comp)) + grid.Comp.InvalidTiles.Add(index); } private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) diff --git a/Content.Server/Atmos/EntitySystems/HeatExchangerSystem.cs b/Content.Server/Atmos/EntitySystems/HeatExchangerSystem.cs index 286b154e96f..b3644e88b73 100644 --- a/Content.Server/Atmos/EntitySystems/HeatExchangerSystem.cs +++ b/Content.Server/Atmos/EntitySystems/HeatExchangerSystem.cs @@ -40,24 +40,16 @@ private void CacheTileLoss(float val) private void OnAtmosUpdate(EntityUid uid, HeatExchangerComponent comp, ref AtmosDeviceUpdateEvent args) { - if (!TryComp(uid, out NodeContainerComponent? nodeContainer) - || !TryComp(uid, out AtmosDeviceComponent? device) - || !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? outlet)) + // make sure that the tile the device is on isn't blocked by a wall or something similar. + if (args.Grid is {} grid + && _transform.TryGetGridTilePosition(uid, out var tile) + && _atmosphereSystem.IsTileAirBlocked(grid, tile)) { return; } - // make sure that the tile the device is on isn't blocked by a wall or something similar. - var xform = Transform(uid); - if (_transform.TryGetGridTilePosition(uid, out var tile)) - { - // TryGetGridTilePosition() already returns false if GridUid is null, but the null checker isn't smart enough yet - if (xform.GridUid != null && _atmosphereSystem.IsTileAirBlocked(xform.GridUid.Value, tile)) - { - return; - } - } + if (!_nodeContainer.TryGetNodes(uid, comp.InletName, comp.OutletName, out PipeNode? inlet, out PipeNode? outlet)) + return; var dt = args.dt; diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs index 28a0a01c99a..c1a5256fdd5 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs @@ -204,11 +204,7 @@ private void OnAtmosUpdate(EntityUid uid, AtmosMonitorComponent component, ref A if (!this.IsPowered(uid, EntityManager)) return; - // can't hurt - // (in case something is making AtmosDeviceUpdateEvents - // outside the typical device loop) - if (!TryComp(uid, out var atmosDeviceComponent) - || atmosDeviceComponent.JoinedGrid == null) + if (args.Grid == null) return; // if we're not monitoring atmos, don't bother diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs index 77bab4775ce..fced4d79884 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs @@ -26,11 +26,7 @@ public override void Initialize() private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, ref AtmosDeviceUpdateEvent args) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, gate.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, gate.OutletName, out PipeNode? outlet)) + if (!_nodeContainer.TryGetNodes(uid, gate.InletName, gate.OutletName, out PipeNode? inlet, out PipeNode? outlet)) return; var n1 = inlet.Air.TotalMoles; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs index 49b69fc6739..af25d04df92 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs @@ -66,9 +66,7 @@ private void OnExamined(EntityUid uid, GasPressurePumpComponent pump, ExaminedEv private void OnPumpUpdated(EntityUid uid, GasPressurePumpComponent pump, ref AtmosDeviceUpdateEvent args) { if (!pump.Enabled - || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, pump.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, pump.OutletName, out PipeNode? outlet)) + || !_nodeContainer.TryGetNodes(uid, pump.InletName, pump.OutletName, out PipeNode? inlet, out PipeNode? outlet)) { _ambientSoundSystem.SetAmbience(uid, false); return; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs index e14069b8a76..3ebc5094926 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs @@ -41,12 +41,8 @@ private void OnExamined(Entity ent, ref ExaminedEvent args if (!EntityManager.GetComponent(ent).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. return; - if (!EntityManager.TryGetComponent(ent, out NodeContainerComponent? nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? _)) - { + if (!_nodeContainer.TryGetNode(ent.Owner, comp.InletName, out PipeNode? inlet)) return; - } using (args.PushGroup(nameof(GasRecyclerComponent))) { @@ -72,9 +68,7 @@ private void OnExamined(Entity ent, ref ExaminedEvent args private void OnUpdate(Entity ent, ref AtmosDeviceUpdateEvent args) { var comp = ent.Comp; - if (!EntityManager.TryGetComponent(ent, out NodeContainerComponent? nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? outlet)) + if (!_nodeContainer.TryGetNodes(ent.Owner, comp.InletName, comp.OutletName, out PipeNode? inlet, out PipeNode? outlet)) { _ambientSoundSystem.SetAmbience(ent, false); return; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs index 934ce8a7a47..ed7567428e1 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs @@ -59,9 +59,8 @@ private void OnActivate(EntityUid uid, GasValveComponent component, ActivateInWo public void Set(EntityUid uid, GasValveComponent component, bool value) { component.Open = value; - if (TryComp(uid, out NodeContainerComponent? nodeContainer) - && _nodeContainer.TryGetNode(nodeContainer, component.InletName, out PipeNode? inlet) - && _nodeContainer.TryGetNode(nodeContainer, component.OutletName, out PipeNode? outlet)) + + if (_nodeContainer.TryGetNodes(uid, component.InletName, component.OutletName, out PipeNode? inlet, out PipeNode? outlet)) { if (TryComp(uid, out var appearance)) { diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs index 8e478bd2b54..e4767c4061a 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs @@ -71,11 +71,8 @@ private void OnExamined(EntityUid uid, GasVolumePumpComponent pump, ExaminedEven private void OnVolumePumpUpdated(EntityUid uid, GasVolumePumpComponent pump, ref AtmosDeviceUpdateEvent args) { - if (!pump.Enabled - || !TryComp(uid, out NodeContainerComponent? nodeContainer) - || !TryComp(uid, out AtmosDeviceComponent? device) - || !_nodeContainer.TryGetNode(nodeContainer, pump.InletName, out PipeNode? inlet) - || !_nodeContainer.TryGetNode(nodeContainer, pump.OutletName, out PipeNode? outlet)) + if (!pump.Enabled || + !_nodeContainer.TryGetNodes(uid, pump.InletName, pump.OutletName, out PipeNode? inlet, out PipeNode? outlet)) { _ambientSoundSystem.SetAmbience(uid, false); return; diff --git a/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs b/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs index 80461f1bebf..b70262e857b 100644 --- a/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs +++ b/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Atmos.Components; +using Content.Shared.Atmos.Components; namespace Content.Server.Atmos.Piping.Components; @@ -46,18 +47,25 @@ public sealed partial class AtmosDeviceComponent : Component /// Use this for atmos devices instead of . /// [ByRefEvent] -public readonly struct AtmosDeviceUpdateEvent +public readonly struct AtmosDeviceUpdateEvent(float dt, Entity? grid, Entity? map) { /// /// Time elapsed since last update, in seconds. Multiply values used in the update handler /// by this number to make them tickrate-invariant. Use this number instead of AtmosphereSystem.AtmosTime. /// - public readonly float dt; + public readonly float dt = dt; - public AtmosDeviceUpdateEvent(float dt) - { - this.dt = dt; - } + /// + /// The grid that this device is currently on. + /// + public readonly Entity? Grid = grid == null + ? null + : (grid.Value, grid.Value, grid.Value); + + /// + /// The map that the device & grid is on. + /// + public readonly Entity? Map = map; } /// diff --git a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs index c15d31f7d64..3c73a8f64ee 100644 --- a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs +++ b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs @@ -129,9 +129,10 @@ public override void Update(float frameTime) _timer -= _atmosphereSystem.AtmosTime; var time = _gameTiming.CurTime; - var ev = new AtmosDeviceUpdateEvent(_atmosphereSystem.AtmosTime); + var ev = new AtmosDeviceUpdateEvent(_atmosphereSystem.AtmosTime, null, null); foreach (var device in _joinedDevices) { + DebugTools.Assert(!HasComp(Transform(device).GridUid)); RaiseLocalEvent(device, ref ev); device.Comp.LastProcess = time; } diff --git a/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs b/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs index 9853a17f829..aa206dbc686 100644 --- a/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs +++ b/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs @@ -38,9 +38,9 @@ private void OnMinerUpdated(Entity ent, ref AtmosDeviceUpdate private bool CheckMinerOperation(Entity ent, [NotNullWhen(true)] out GasMixture? environment) { var (uid, miner) = ent; - environment = _atmosphereSystem.GetContainingMixture(uid, true, true); - var transform = Transform(uid); + environment = _atmosphereSystem.GetContainingMixture((uid, transform), true, true); + var position = _transformSystem.GetGridOrMapTilePosition(uid, transform); // Space. diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs index faf06a60793..f856946a925 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs @@ -53,11 +53,7 @@ private void OnInit(EntityUid uid, GasFilterComponent filter, ComponentInit args private void OnFilterUpdated(EntityUid uid, GasFilterComponent filter, ref AtmosDeviceUpdateEvent args) { if (!filter.Enabled - || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) - || !EntityManager.TryGetComponent(uid, out AtmosDeviceComponent? device) - || !_nodeContainer.TryGetNode(nodeContainer, filter.InletName, out PipeNode? inletNode) - || !_nodeContainer.TryGetNode(nodeContainer, filter.FilterName, out PipeNode? filterNode) - || !_nodeContainer.TryGetNode(nodeContainer, filter.OutletName, out PipeNode? outletNode) + || !_nodeContainer.TryGetNodes(uid, filter.InletName, filter.OutletName, filter.FilterName, out PipeNode? inletNode, out PipeNode? filterNode, out PipeNode? outletNode) || outletNode.Air.Pressure >= Atmospherics.MaxOutputPressure) // No need to transfer if target is full. { _ambientSoundSystem.SetAmbience(uid, false); @@ -187,16 +183,15 @@ private void OnFilterAnalyzed(EntityUid uid, GasFilterComponent component, GasAn if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) return; - var gasMixDict = new Dictionary(); + args.GasMixtures ??= new Dictionary(); if(_nodeContainer.TryGetNode(nodeContainer, component.InletName, out PipeNode? inlet)) - gasMixDict.Add(Loc.GetString("gas-analyzer-window-text-inlet"), inlet.Air); + args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-inlet"), inlet.Air); if(_nodeContainer.TryGetNode(nodeContainer, component.FilterName, out PipeNode? filterNode)) - gasMixDict.Add(Loc.GetString("gas-analyzer-window-text-filter"), filterNode.Air); + args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-filter"), filterNode.Air); if(_nodeContainer.TryGetNode(nodeContainer, component.OutletName, out PipeNode? outlet)) - gasMixDict.Add(Loc.GetString("gas-analyzer-window-text-outlet"), outlet.Air); + args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-outlet"), outlet.Air); - args.GasMixtures = gasMixDict; args.DeviceFlipped = inlet != null && filterNode != null && inlet.CurrentPipeDirection.ToDirection() == filterNode.CurrentPipeDirection.ToDirection().GetClockwise90Degrees(); } } diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs index fb65c17f610..ba8ebf3c9ae 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs @@ -54,18 +54,8 @@ private void OnMixerUpdated(EntityUid uid, GasMixerComponent mixer, ref AtmosDev { // TODO ATMOS: Cache total moles since it's expensive. - if (!mixer.Enabled) - { - _ambientSoundSystem.SetAmbience(uid, false); - return; - } - - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, mixer.InletOneName, out PipeNode? inletOne) - || !_nodeContainer.TryGetNode(nodeContainer, mixer.InletTwoName, out PipeNode? inletTwo) - || !_nodeContainer.TryGetNode(nodeContainer, mixer.OutletName, out PipeNode? outlet)) + if (!mixer.Enabled + || !_nodeContainer.TryGetNodes(uid, mixer.InletOneName, mixer.InletTwoName, mixer.OutletName, out PipeNode? inletOne, out PipeNode? inletTwo, out PipeNode? outlet)) { _ambientSoundSystem.SetAmbience(uid, false); return; diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs index 2c2f1584a53..1bab2abd8e9 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs @@ -33,11 +33,7 @@ private void OnInit(EntityUid uid, PressureControlledValveComponent comp, Compon private void OnUpdate(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceUpdateEvent args) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) - || !EntityManager.TryGetComponent(uid, out AtmosDeviceComponent? device) - || !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inletNode) - || !_nodeContainer.TryGetNode(nodeContainer, comp.ControlName, out PipeNode? controlNode) - || !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? outletNode)) + if (!_nodeContainer.TryGetNodes(uid, comp.InletName, comp.ControlName, comp.OutletName, out PipeNode? inletNode, out PipeNode? controlNode, out PipeNode? outletNode)) { _ambientSoundSystem.SetAmbience(uid, false); comp.Enabled = false; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index 170586339db..3e4340bf1db 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -60,7 +60,7 @@ public void PurgeContents(EntityUid uid, GasCanisterComponent? canister = null, if (!Resolve(uid, ref canister, ref transform)) return; - var environment = _atmos.GetContainingMixture(uid, false, true); + var environment = _atmos.GetContainingMixture((uid, transform), false, true); if (environment is not null) _atmos.Merge(environment, canister.Air); @@ -168,7 +168,7 @@ private void OnCanisterUpdated(EntityUid uid, GasCanisterComponent canister, ref } else { - var environment = _atmos.GetContainingMixture(uid, false, true); + var environment = _atmos.GetContainingMixture(uid, args.Grid, args.Map, false, true); _atmos.ReleaseGasTo(canister.Air, environment, canister.ReleasePressure); } } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCondenserSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCondenserSystem.cs index 852542ec6cd..e903ceedafa 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCondenserSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCondenserSystem.cs @@ -30,9 +30,8 @@ public override void Initialize() private void OnCondenserUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) { - if (!(_power.IsPowered(entity) && TryComp(entity, out var receiver)) - || !TryComp(entity, out var nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, entity.Comp.Inlet, out PipeNode? inlet) + if (!(TryComp(entity, out var receiver) && _power.IsPowered(entity, receiver)) + || !_nodeContainer.TryGetNode(entity.Owner, entity.Comp.Inlet, out PipeNode? inlet) || !_solution.ResolveSolution(entity.Owner, entity.Comp.SolutionId, ref entity.Comp.Solution, out var solution)) { return; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs index 8029a095565..834a1dfb0b7 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs @@ -50,16 +50,10 @@ private void OnOutletInjectorUpdated(EntityUid uid, GasOutletInjectorComponent i if (!injector.Enabled) return; - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) + if (!_nodeContainer.TryGetNode(uid, injector.InletName, out PipeNode? inlet)) return; - if (!TryComp(uid, out AtmosDeviceComponent? device)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, injector.InletName, out PipeNode? inlet)) - return; - - var environment = _atmosphereSystem.GetContainingMixture(uid, true, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map, true, true); if (environment == null) return; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs index c8fd23d466a..72812cb5237 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs @@ -24,15 +24,12 @@ public override void Initialize() private void OnPassiveVentUpdated(EntityUid uid, GasPassiveVentComponent vent, ref AtmosDeviceUpdateEvent args) { - var environment = _atmosphereSystem.GetContainingMixture(uid, true, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map, true, true); if (environment == null) return; - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, vent.InletName, out PipeNode? inlet)) + if (!_nodeContainer.TryGetNode(uid, vent.InletName, out PipeNode? inlet)) return; var inletAir = inlet.Air.RemoveRatio(1f); diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs index 4ddd19dd45e..7cb8102a388 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs @@ -39,10 +39,7 @@ private void OnPortableAnchorAttempt(EntityUid uid, GasPortableComponent compone private void OnAnchorChanged(EntityUid uid, GasPortableComponent portable, ref AnchorStateChangedEvent args) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, portable.PortName, out PipeNode? portableNode)) + if (!_nodeContainer.TryGetNode(uid, portable.PortName, out PipeNode? portableNode)) return; portableNode.ConnectionsEnabled = args.Anchored; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs index d4ddd65a8ec..9b61044f03e 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs @@ -110,7 +110,7 @@ private void OnThermoMachineUpdated(EntityUid uid, GasThermoMachineComponent the _atmosphereSystem.AddHeat(heatExchangeGasMixture, dQPipe); thermoMachine.LastEnergyDelta = dQPipe; - if (dQLeak != 0f && _atmosphereSystem.GetContainingMixture(uid, excite: true) is { } containingMixture) + if (dQLeak != 0f && _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map, excite: true) is { } containingMixture) _atmosphereSystem.AddHeat(containingMixture, dQLeak); } @@ -130,8 +130,7 @@ private void GetHeatExchangeGasMixture(EntityUid uid, GasThermoMachineComponent } else { - if (!TryComp(uid, out var nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, thermoMachine.InletName, out PipeNode? inlet)) + if (!_nodeContainer.TryGetNode(uid, thermoMachine.InletName, out PipeNode? inlet)) return; heatExchangeGasMixture = inlet.Air; } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs index 3a3ccf75234..a986385f5e9 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs @@ -67,15 +67,12 @@ private void OnGasVentPumpUpdated(EntityUid uid, GasVentPumpComponent vent, ref _ => throw new ArgumentOutOfRangeException() }; - if (!vent.Enabled - || !TryComp(uid, out AtmosDeviceComponent? device) - || !TryComp(uid, out NodeContainerComponent? nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, nodeName, out PipeNode? pipe)) + if (!vent.Enabled || !_nodeContainer.TryGetNode(uid, nodeName, out PipeNode? pipe)) { return; } - var environment = _atmosphereSystem.GetContainingMixture(uid, true, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map, true, true); // We're in an air-blocked tile... Do nothing. if (environment == null) @@ -295,9 +292,6 @@ private void OnExamine(EntityUid uid, GasVentPumpComponent component, ExaminedEv /// private void OnAnalyzed(EntityUid uid, GasVentPumpComponent component, GasAnalyzerScanEvent args) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; - var gasMixDict = new Dictionary(); // these are both called pipe, above it switches using this so I duplicated that...? @@ -307,7 +301,7 @@ private void OnAnalyzed(EntityUid uid, GasVentPumpComponent component, GasAnalyz VentPumpDirection.Siphoning => component.Outlet, _ => throw new ArgumentOutOfRangeException() }; - if (_nodeContainer.TryGetNode(nodeContainer, nodeName, out PipeNode? pipe)) + if (_nodeContainer.TryGetNode(uid, nodeName, out PipeNode? pipe)) gasMixDict.Add(nodeName, pipe.Air); args.GasMixtures = gasMixDict; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs index 5afa007e5e1..b27689ed586 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs @@ -49,27 +49,18 @@ public override void Initialize() private void OnVentScrubberUpdated(EntityUid uid, GasVentScrubberComponent scrubber, ref AtmosDeviceUpdateEvent args) { if (_weldable.IsWelded(uid)) - { - return; - } - - if (!TryComp(uid, out AtmosDeviceComponent? device)) return; var timeDelta = args.dt; - if (!scrubber.Enabled - || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, scrubber.OutletName, out PipeNode? outlet)) + if (!scrubber.Enabled || !_nodeContainer.TryGetNode(uid, scrubber.OutletName, out PipeNode? outlet)) return; - var xform = Transform(uid); - - if (xform.GridUid == null) + if (args.Grid is not {} grid) return; - var position = _transformSystem.GetGridTilePositionOrDefault((uid,xform)); - var environment = _atmosphereSystem.GetTileMixture(xform.GridUid, xform.MapUid, position, true); + var position = _transformSystem.GetGridTilePositionOrDefault(uid); + var environment = _atmosphereSystem.GetTileMixture(grid, args.Map, position, true); Scrub(timeDelta, scrubber, environment, outlet); @@ -77,7 +68,7 @@ private void OnVentScrubberUpdated(EntityUid uid, GasVentScrubberComponent scrub return; // Scrub adjacent tiles too. - var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true); + var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(grid, position, false, true); while (enumerator.MoveNext(out var adjacent)) { Scrub(timeDelta, scrubber, adjacent, outlet); diff --git a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs index e986f8f991d..f9043c091a8 100644 --- a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs +++ b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs @@ -47,17 +47,13 @@ private bool IsFull(PortableScrubberComponent component) private void OnDeviceUpdated(EntityUid uid, PortableScrubberComponent component, ref AtmosDeviceUpdateEvent args) { - if (!TryComp(uid, out AtmosDeviceComponent? device)) - return; - var timeDelta = args.dt; if (!component.Enabled) return; // If we are on top of a connector port, empty into it. - if (TryComp(uid, out var nodeContainer) - && _nodeContainer.TryGetNode(nodeContainer, component.PortName, out PortablePipeNode? portableNode) + if (_nodeContainer.TryGetNode(uid, component.PortName, out PortablePipeNode? portableNode) && portableNode.ConnectionsEnabled) { _atmosphereSystem.React(component.Air, portableNode); @@ -71,13 +67,11 @@ private void OnDeviceUpdated(EntityUid uid, PortableScrubberComponent component, return; } - var xform = Transform(uid); - - if (xform.GridUid == null) + if (args.Grid is not {} grid) return; - var position = _transformSystem.GetGridTilePositionOrDefault((uid,xform)); - var environment = _atmosphereSystem.GetTileMixture(xform.GridUid, xform.MapUid, position, true); + var position = _transformSystem.GetGridTilePositionOrDefault(uid); + var environment = _atmosphereSystem.GetTileMixture(grid, args.Map, position, true); var running = Scrub(timeDelta, component, environment); @@ -85,8 +79,9 @@ private void OnDeviceUpdated(EntityUid uid, PortableScrubberComponent component, // We scrub once to see if we can and set the animation if (!running) return; + // widenet - var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true); + var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(grid, position, false, true); while (enumerator.MoveNext(out var adjacent)) { Scrub(timeDelta, component, adjacent); @@ -98,10 +93,7 @@ private void OnDeviceUpdated(EntityUid uid, PortableScrubberComponent component, /// private void OnAnchorChanged(EntityUid uid, PortableScrubberComponent component, ref AnchorStateChangedEvent args) { - if (!TryComp(uid, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, component.PortName, out PipeNode? portableNode)) + if (!_nodeContainer.TryGetNode(uid, component.PortName, out PipeNode? portableNode)) return; portableNode.ConnectionsEnabled = (args.Anchored && _gasPortableSystem.FindGasPortIn(Transform(uid).GridUid, Transform(uid).Coordinates, out _)); @@ -159,14 +151,10 @@ private void UpdateAppearance(EntityUid uid, bool isFull, bool isRunning) /// private void OnScrubberAnalyzed(EntityUid uid, PortableScrubberComponent component, GasAnalyzerScanEvent args) { - var gasMixDict = new Dictionary { { Name(uid), component.Air } }; + args.GasMixtures ??= new Dictionary { { Name(uid), component.Air } }; // If it's connected to a port, include the port side - if (TryComp(uid, out NodeContainerComponent? nodeContainer)) - { - if (_nodeContainer.TryGetNode(nodeContainer, component.PortName, out PipeNode? port)) - gasMixDict.Add(component.PortName, port.Air); - } - args.GasMixtures = gasMixDict; + if (_nodeContainer.TryGetNode(uid, component.PortName, out PipeNode? port)) + args.GasMixtures.Add(component.PortName, port.Air); } } } diff --git a/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs b/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs index 16311328218..fff15f696c4 100644 --- a/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs +++ b/Content.Server/Atmos/Portable/SpaceHeaterSystem.cs @@ -71,7 +71,7 @@ private void OnDeviceUpdated(EntityUid uid, SpaceHeaterComponent spaceHeater, re // If in automatic temperature mode, check if we need to adjust the heat exchange direction if (spaceHeater.Mode == SpaceHeaterMode.Auto) { - var environment = _atmosphereSystem.GetContainingMixture(uid); + var environment = _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map); if (environment == null) return; diff --git a/Content.Server/Electrocution/ElectrocutionNode.cs b/Content.Server/Electrocution/ElectrocutionNode.cs index 7abcba76666..c8e437d3532 100644 --- a/Content.Server/Electrocution/ElectrocutionNode.cs +++ b/Content.Server/Electrocution/ElectrocutionNode.cs @@ -9,7 +9,7 @@ namespace Content.Server.Electrocution public sealed partial class ElectrocutionNode : Node { [DataField("cable")] - public EntityUid CableEntity; + public EntityUid? CableEntity; [DataField("node")] public string? NodeName; @@ -19,12 +19,11 @@ public override IEnumerable GetReachableNodes(TransformComponent xform, MapGridComponent? grid, IEntityManager entMan) { - var _nodeContainer = entMan.System(); - - if (!nodeQuery.TryGetComponent(CableEntity, out var nodeContainer)) + if (CableEntity == null || NodeName == null) yield break; - if (_nodeContainer.TryGetNode(nodeContainer, NodeName, out Node? node)) + var _nodeContainer = entMan.System(); + if (_nodeContainer.TryGetNode(CableEntity.Value, NodeName, out Node? node)) yield return node; } } diff --git a/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs b/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs index 8d2a699de27..7db1f513f7c 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs @@ -271,7 +271,7 @@ private void AddNewAdjacentTiles(int iteration, IEnumerable tiles, boo var direction = (AtmosDirection) (1 << i); if (ignoreTileBlockers || !blockedDirections.IsFlagSet(direction)) { - ProcessNewTile(iteration, tile.Offset(direction), direction.GetOpposite()); + ProcessNewTile(iteration, tile.Offset(direction), i.ToOppositeDir()); } } @@ -300,7 +300,7 @@ private void AddNewAdjacentTiles(int iteration, IEnumerable tiles, boo var direction = (AtmosDirection) (1 << i); if (blockedDirections.IsFlagSet(direction)) { - list.Add((tile.Offset(direction), direction.GetOpposite())); + list.Add((tile.Offset(direction), i.ToOppositeDir())); } } } diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs b/Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs index f8c917c1cd6..313b03e03a6 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs @@ -97,7 +97,7 @@ private void AddNewAdjacentTiles(int iteration, IEnumerable tiles) if (!unblockedDirections.IsFlagSet(direction)) continue; // explosion cannot propagate in this direction. Ever. - ProcessNewTile(iteration, tile.Offset(direction), direction.GetOpposite()); + ProcessNewTile(iteration, tile.Offset(direction), i.ToOppositeDir()); } } } diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index 78034e0fc3d..9e546dc33fe 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -404,14 +404,17 @@ private void OnExpose(EntityUid uid, MechPilotComponent component, ref AtmosExpo if (args.Handled) return; - if (!TryComp(component.Mech, out var mech) || - !TryComp(component.Mech, out var mechAir)) + if (!TryComp(component.Mech, out MechComponent? mech)) + return; + + if (mech.Airtight && TryComp(component.Mech, out MechAirComponent? air)) { + args.Handled = true; + args.Gas = air.Air; return; } - args.Gas = mech.Airtight ? mechAir.Air : _atmosphere.GetContainingMixture(component.Mech); - + args.Gas = _atmosphere.GetContainingMixture(component.Mech, excite: args.Excite); args.Handled = true; } diff --git a/Content.Server/Medical/CryoPodSystem.cs b/Content.Server/Medical/CryoPodSystem.cs index a7d12d9f0fd..a949d980bef 100644 --- a/Content.Server/Medical/CryoPodSystem.cs +++ b/Content.Server/Medical/CryoPodSystem.cs @@ -257,10 +257,7 @@ private void OnPowerChanged(Entity entity, ref PowerChangedEve private void OnCryoPodUpdateAtmosphere(Entity entity, ref AtmosDeviceUpdateEvent args) { - if (!TryComp(entity, out NodeContainerComponent? nodeContainer)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, entity.Comp.PortName, out PortablePipeNode? portNode)) + if (!_nodeContainer.TryGetNode(entity.Owner, entity.Comp.PortName, out PortablePipeNode? portNode)) return; if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir)) @@ -279,14 +276,10 @@ private void OnGasAnalyzed(Entity entity, ref GasAnalyzerScanE if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir)) return; - var gasMixDict = new Dictionary { { Name(entity.Owner), cryoPodAir.Air } }; + args.GasMixtures ??= new Dictionary { { Name(entity.Owner), cryoPodAir.Air } }; // If it's connected to a port, include the port side - if (TryComp(entity, out NodeContainerComponent? nodeContainer)) - { - if (_nodeContainer.TryGetNode(nodeContainer, entity.Comp.PortName, out PipeNode? port)) - gasMixDict.Add(entity.Comp.PortName, port.Air); - } - args.GasMixtures = gasMixDict; + if (_nodeContainer.TryGetNode(entity.Owner, entity.Comp.PortName, out PipeNode? port)) + args.GasMixtures.Add(entity.Comp.PortName, port.Air); } private void OnEjected(Entity cryoPod, ref EntRemovedFromContainerMessage args) diff --git a/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs b/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs index 99d18aeb3f9..19b811a287f 100644 --- a/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs +++ b/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs @@ -14,6 +14,7 @@ namespace Content.Server.NodeContainer.EntitySystems public sealed class NodeContainerSystem : EntitySystem { [Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!; + private EntityQuery _query; public override void Initialize() { @@ -26,6 +27,8 @@ public override void Initialize() SubscribeLocalEvent(OnReAnchor); SubscribeLocalEvent(OnMoveEvent); SubscribeLocalEvent(OnExamine); + + _query = GetEntityQuery(); } public bool TryGetNode(NodeContainerComponent component, string? identifier, [NotNullWhen(true)] out T? node) where T : Node @@ -46,6 +49,77 @@ public bool TryGetNode(NodeContainerComponent component, string? identifier, return false; } + public bool TryGetNode(Entity ent, string identifier, [NotNullWhen(true)] out T? node) where T : Node + { + if (_query.Resolve(ent, ref ent.Comp, false) + && ent.Comp.Nodes.TryGetValue(identifier, out var n) + && n is T t) + { + node = t; + return true; + } + + node = null; + return false; + } + + public bool TryGetNodes( + Entity ent, + string id1, + string id2, + [NotNullWhen(true)] out T1? node1, + [NotNullWhen(true)] out T2? node2) + where T1 : Node + where T2 : Node + { + if (_query.Resolve(ent, ref ent.Comp, false) + && ent.Comp.Nodes.TryGetValue(id1, out var n1) + && n1 is T1 t1 + && ent.Comp.Nodes.TryGetValue(id2, out var n2) + && n2 is T2 t2) + { + node1 = t1; + node2 = t2; + return true; + } + + node1 = null; + node2 = null; + return false; + } + + public bool TryGetNodes( + Entity ent, + string id1, + string id2, + string id3, + [NotNullWhen(true)] out T1? node1, + [NotNullWhen(true)] out T2? node2, + [NotNullWhen(true)] out T3? node3) + where T1 : Node + where T2 : Node + where T3 : Node + { + if (_query.Resolve(ent, ref ent.Comp, false) + && ent.Comp.Nodes.TryGetValue(id1, out var n1) + && n1 is T1 t1 + && ent.Comp.Nodes.TryGetValue(id2, out var n2) + && n2 is T2 t2 + && ent.Comp.Nodes.TryGetValue(id3, out var n3) + && n2 is T3 t3) + { + node1 = t1; + node2 = t2; + node3 = t3; + return true; + } + + node1 = null; + node2 = null; + node3 = null; + return false; + } + private void OnInitEvent(EntityUid uid, NodeContainerComponent component, ComponentInit args) { foreach (var (key, node) in component.Nodes) diff --git a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs index 60f0603074d..6e0e0c503a9 100644 --- a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs +++ b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs @@ -94,7 +94,7 @@ private void OnShoot(Entity cannon, ref GunShotEvent a return; // this should always be possible, as we'll eject the gas tank when it no longer is - var environment = _atmos.GetContainingMixture(cannon, false, true); + var environment = _atmos.GetContainingMixture(cannon.Owner, false, true); var removed = _gasTank.RemoveAir(gas.Value, component.GasUsage); if (environment != null && removed != null) { diff --git a/Content.Server/Power/Components/CableVisComponent.cs b/Content.Server/Power/Components/CableVisComponent.cs index bd9c62ba808..51d68b99fdc 100644 --- a/Content.Server/Power/Components/CableVisComponent.cs +++ b/Content.Server/Power/Components/CableVisComponent.cs @@ -4,7 +4,7 @@ public sealed partial class CableVisComponent : Component { [ViewVariables(VVAccess.ReadWrite)] - [DataField("node")] - public string? Node; + [DataField("node", required:true)] + public string Node; } } diff --git a/Content.Server/Power/EntitySystems/CableVisSystem.cs b/Content.Server/Power/EntitySystems/CableVisSystem.cs index ec08523d447..1a68e87ad61 100644 --- a/Content.Server/Power/EntitySystems/CableVisSystem.cs +++ b/Content.Server/Power/EntitySystems/CableVisSystem.cs @@ -23,10 +23,7 @@ public override void Initialize() private void UpdateAppearance(EntityUid uid, CableVisComponent cableVis, ref NodeGroupsRebuilt args) { - if (!TryComp(uid, out NodeContainerComponent? nodeContainer) || !TryComp(uid, out AppearanceComponent? appearance)) - return; - - if (!_nodeContainer.TryGetNode(nodeContainer, cableVis.Node, out var node)) + if (!_nodeContainer.TryGetNode(uid, cableVis.Node, out CableNode? node)) return; var transform = Transform(uid); @@ -55,7 +52,7 @@ private void UpdateAppearance(EntityUid uid, CableVisComponent cableVis, ref Nod }; } - _appearance.SetData(uid, WireVisVisuals.ConnectedMask, mask, appearance); + _appearance.SetData(uid, WireVisVisuals.ConnectedMask, mask); } } } diff --git a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs index 76cf90c3693..5f79906c995 100644 --- a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs +++ b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs @@ -26,12 +26,8 @@ private void OnDeviceUpdated(EntityUid uid, GasPowerReceiverComponent component, { var timeDelta = args.dt; - if (!HasComp(uid) - || !TryComp(uid, out var nodeContainer) - || !_nodeContainer.TryGetNode(nodeContainer, "pipe", out var pipe)) - { + if (!_nodeContainer.TryGetNode(uid, "pipe", out PipeNode? pipe)) return; - } // if we're below the max temperature, then we are simply consuming our target gas if (pipe.Air.Temperature <= component.MaxTemperature) @@ -57,7 +53,7 @@ private void OnDeviceUpdated(EntityUid uid, GasPowerReceiverComponent component, if (component.OffVentGas) { // eject the gas into the atmosphere - var mix = _atmosphereSystem.GetContainingMixture(uid, false, true); + var mix = _atmosphereSystem.GetContainingMixture(uid, args.Grid, args.Map, false, true); if (mix is not null) _atmosphereSystem.Merge(res, mix); } diff --git a/Content.Server/Spreader/SpreaderSystem.cs b/Content.Server/Spreader/SpreaderSystem.cs index 671c281d1f4..fe14d86aa1d 100644 --- a/Content.Server/Spreader/SpreaderSystem.cs +++ b/Content.Server/Spreader/SpreaderSystem.cs @@ -231,10 +231,9 @@ public void GetNeighbors(EntityUid uid, TransformComponent comp, ProtoId + /// This returns the index that corresponds to the opposite direction of some other direction index. + /// I.e., 1<<OppositeIndex(i) == (1<<i).GetOpposite() + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ToOppositeIndex(this int index) + { + return index ^ 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static AtmosDirection ToOppositeDir(this int index) + { + return (AtmosDirection) (1 << (index ^ 1)); + } + public static Direction ToDirection(this AtmosDirection direction) { return direction switch @@ -119,10 +138,11 @@ public static AtmosDirection ToAtmosDirection(this Angle angle) return angle.GetDir().ToAtmosDirection(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ToIndex(this AtmosDirection direction) { // This will throw if you pass an invalid direction. Not this method's fault, but yours! - return (int) Math.Log2((int) direction); + return BitOperations.Log2((uint)direction); } public static AtmosDirection WithFlag(this AtmosDirection direction, AtmosDirection other) diff --git a/Content.Shared/Tools/Systems/WeldableSystem.cs b/Content.Shared/Tools/Systems/WeldableSystem.cs index f887ed3049f..b0ea68f713f 100644 --- a/Content.Shared/Tools/Systems/WeldableSystem.cs +++ b/Content.Shared/Tools/Systems/WeldableSystem.cs @@ -15,14 +15,7 @@ public sealed class WeldableSystem : EntitySystem [Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - - public bool IsWelded(EntityUid uid, WeldableComponent? component = null) - { - if (!Resolve(uid, ref component, false)) - return false; - - return component.IsWelded; - } + private EntityQuery _query; public override void Initialize() { @@ -31,6 +24,13 @@ public override void Initialize() SubscribeLocalEvent(OnWeldFinished); SubscribeLocalEvent(OnWeldChanged); SubscribeLocalEvent(OnExamine); + + _query = GetEntityQuery(); + } + + public bool IsWelded(EntityUid uid, WeldableComponent? component = null) + { + return _query.Resolve(uid, ref component, false) && component.IsWelded; } private void OnExamine(EntityUid uid, WeldableComponent component, ExaminedEvent args) @@ -49,7 +49,7 @@ private void OnInteractUsing(EntityUid uid, WeldableComponent component, Interac private bool CanWeld(EntityUid uid, EntityUid tool, EntityUid user, WeldableComponent? component = null) { - if (!Resolve(uid, ref component)) + if (!_query.Resolve(uid, ref component)) return false; // Other component systems @@ -63,7 +63,7 @@ private bool CanWeld(EntityUid uid, EntityUid tool, EntityUid user, WeldableComp private bool TryWeld(EntityUid uid, EntityUid tool, EntityUid user, WeldableComponent? component = null) { - if (!Resolve(uid, ref component)) + if (!_query.Resolve(uid, ref component)) return false; if (!CanWeld(uid, tool, user, component)) @@ -115,17 +115,13 @@ private void OnWeldChanged(EntityUid uid, LayerChangeOnWeldComponent component, private void UpdateAppearance(EntityUid uid, WeldableComponent? component = null) { - if (!Resolve(uid, ref component)) - return; - - if (!TryComp(uid, out AppearanceComponent? appearance)) - return; - _appearance.SetData(uid, WeldableVisuals.IsWelded, component.IsWelded, appearance); + if (_query.Resolve(uid, ref component)) + _appearance.SetData(uid, WeldableVisuals.IsWelded, component.IsWelded); } public void SetWeldedState(EntityUid uid, bool state, WeldableComponent? component = null) { - if (!Resolve(uid, ref component)) + if (!_query.Resolve(uid, ref component)) return; if (component.IsWelded == state) @@ -141,7 +137,7 @@ public void SetWeldedState(EntityUid uid, bool state, WeldableComponent? compone public void SetWeldingTime(EntityUid uid, TimeSpan time, WeldableComponent? component = null) { - if (!Resolve(uid, ref component)) + if (!_query.Resolve(uid, ref component)) return; if (component.WeldingTime.Equals(time)) diff --git a/Resources/Prototypes/Entities/Virtual/electrocution.yml b/Resources/Prototypes/Entities/Virtual/electrocution.yml index 497071ee939..ac65245191e 100644 --- a/Resources/Prototypes/Entities/Virtual/electrocution.yml +++ b/Resources/Prototypes/Entities/Virtual/electrocution.yml @@ -1,7 +1,7 @@ # Special entity used to attach to power networks as load when somebody gets electrocuted. - type: entity id: VirtualElectrocutionLoadBase - noSpawn: true + abstract: true components: - type: Electrocution - type: Icon From 9ddfe38668a263d0d9dc7230727cb55b9ab9e283 Mon Sep 17 00:00:00 2001 From: liltenhead <104418166+liltenhead@users.noreply.github.com> Date: Fri, 29 Mar 2024 21:36:33 -0700 Subject: [PATCH 025/133] Adjust syndicate hardbomb damage (#26548) hardbomb tweaks --- Resources/Prototypes/explosion.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/explosion.yml b/Resources/Prototypes/explosion.yml index a768dead6da..4f720673765 100644 --- a/Resources/Prototypes/explosion.yml +++ b/Resources/Prototypes/explosion.yml @@ -110,9 +110,9 @@ Blunt: 15 Piercing: 6 Structural: 40 - tileBreakChance: [ 0.75, 0.95, 1 ] - tileBreakIntensity: [ 1, 10, 15 ] - tileBreakRerollReduction: 30 + tileBreakChance: [ 0, 0.5, 1 ] + tileBreakIntensity: [ 0, 10, 30 ] + tileBreakRerollReduction: 10 intensityPerState: 20 lightColor: Orange texturePath: /Textures/Effects/fire.rsi From 0f6c7c9d51f81c01dba3a5047ce5e78acdc5bba5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 04:37:39 +0000 Subject: [PATCH 026/133] 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 8c70fdc6380..4429f2be05f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Alekshhh - changes: - - message: Cerberus now has a wideswing that works similarly to spears. - type: Tweak - id: 5759 - time: '2024-01-20T23:38:27.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24328 - author: TheShuEd changes: - message: Added new Floral anomaly! @@ -3793,3 +3786,11 @@ id: 6258 time: '2024-03-30T04:00:21.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25830 +- author: liltenhead + changes: + - message: Changed the syndicate hardbomb to have less of a chance to completely + destroy tiles. + type: Tweak + id: 6259 + time: '2024-03-30T04:36:33.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26548 From 97b390d35f503dd9bda35e0b3a6192b2a502d120 Mon Sep 17 00:00:00 2001 From: takemysoult <143123247+takemysoult@users.noreply.github.com> Date: Sat, 30 Mar 2024 09:52:27 +0300 Subject: [PATCH 027/133] up stimulants (no sleep) (#25886) * up stimulants (no sleep) * Update SharedSleepingSystem.cs --- Content.Shared/Bed/Sleep/SharedSleepingSystem.cs | 2 +- Resources/Prototypes/Reagents/narcotics.yml | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs b/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs index 4e4bc2c574b..c6248c88f77 100644 --- a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs +++ b/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs @@ -37,7 +37,7 @@ private void OnMapInit(EntityUid uid, SleepingComponent component, MapInitEvent _actionsSystem.AddAction(uid, ref component.WakeAction, WakeActionId, uid); // TODO remove hardcoded time. - _actionsSystem.SetCooldown(component.WakeAction, _gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(15)); + _actionsSystem.SetCooldown(component.WakeAction, _gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(2f)); } private void OnShutdown(EntityUid uid, SleepingComponent component, ComponentShutdown args) diff --git a/Resources/Prototypes/Reagents/narcotics.yml b/Resources/Prototypes/Reagents/narcotics.yml index d05cc29ab02..cefc8043b08 100644 --- a/Resources/Prototypes/Reagents/narcotics.yml +++ b/Resources/Prototypes/Reagents/narcotics.yml @@ -116,6 +116,13 @@ damage: types: Poison: 1 + - !type:AdjustReagent + conditions: + - !type:ReagentThreshold + reagent: ChloralHydrate + min: 1 + reagent: ChloralHydrate + amount: -10 - !type:GenericStatusEffect key: Stun time: 3 @@ -129,6 +136,10 @@ component: StaminaModifier time: 3 type: Add + - !type:GenericStatusEffect + key: ForcedSleep + time: 3 + type: Remove Medicine: metabolismRate: 1.0 effects: From 1e51febc67fdd010a286f170bf47d6b7e7445a4e Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 Mar 2024 06:53:32 +0000 Subject: [PATCH 028/133] 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 4429f2be05f..136f7054ef8 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: TheShuEd - changes: - - message: Added new Floral anomaly! - type: Add - id: 5760 - time: '2024-01-21T01:31:12.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24351 - author: Menshin changes: - message: The PA control box should now properly detect the PA parts in all situations @@ -3794,3 +3787,10 @@ id: 6259 time: '2024-03-30T04:36:33.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26548 +- author: takemysoult + changes: + - message: stimulants removes chloral hydrate from body + type: Tweak + id: 6260 + time: '2024-03-30T06:52:27.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25886 From 1ffa5c28d8b50368884177abacf1625f6d37b2d1 Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:38:38 +0100 Subject: [PATCH 029/133] Make BaseMedicalPDA abstract (#26567) --- Resources/Prototypes/Entities/Objects/Devices/pda.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 0056f965a5c..73976b48e03 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -108,6 +108,7 @@ - type: entity parent: BasePDA id: BaseMedicalPDA + abstract: true components: - type: HealthAnalyzer scanDelay: 1 From 7638252df3b0c7e958e988fc7e491d81c1e8a656 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Sat, 30 Mar 2024 11:11:44 -0700 Subject: [PATCH 030/133] Fix GasMixers/Filters not working (#26568) * Fix GasMixers/Filters not working * OKAY GAS FILTERS TOO --------- Co-authored-by: Plykiya --- .../Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs | 2 +- .../NodeContainer/EntitySystems/NodeContainerSystem.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs index f856946a925..fbd42604694 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs @@ -53,7 +53,7 @@ private void OnInit(EntityUid uid, GasFilterComponent filter, ComponentInit args private void OnFilterUpdated(EntityUid uid, GasFilterComponent filter, ref AtmosDeviceUpdateEvent args) { if (!filter.Enabled - || !_nodeContainer.TryGetNodes(uid, filter.InletName, filter.OutletName, filter.FilterName, out PipeNode? inletNode, out PipeNode? filterNode, out PipeNode? outletNode) + || !_nodeContainer.TryGetNodes(uid, filter.InletName, filter.FilterName, filter.OutletName, out PipeNode? inletNode, out PipeNode? filterNode, out PipeNode? outletNode) || outletNode.Air.Pressure >= Atmospherics.MaxOutputPressure) // No need to transfer if target is full. { _ambientSoundSystem.SetAmbience(uid, false); diff --git a/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs b/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs index 19b811a287f..c0603949be5 100644 --- a/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs +++ b/Content.Server/NodeContainer/EntitySystems/NodeContainerSystem.cs @@ -106,7 +106,7 @@ public bool TryGetNodes( && ent.Comp.Nodes.TryGetValue(id2, out var n2) && n2 is T2 t2 && ent.Comp.Nodes.TryGetValue(id3, out var n3) - && n2 is T3 t3) + && n3 is T3 t3) { node1 = t1; node2 = t2; From c9e19445b49ca60b1096fbd48a9ede022fa8b166 Mon Sep 17 00:00:00 2001 From: Boaz1111 <149967078+Boaz1111@users.noreply.github.com> Date: Sat, 30 Mar 2024 21:25:50 +0100 Subject: [PATCH 031/133] Industrial Reagent Grinder Hotfix (#26571) fixed --- .../Entities/Structures/Machines/reagent_grinder.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml index 3bb1458b8c5..773112f6f36 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml @@ -97,4 +97,8 @@ node: machine containers: - machine_parts - - machine_board \ No newline at end of file + - machine_board + - type: DrainableSolution + solution: output + - type: ExaminableSolution + solution: output \ No newline at end of file From b8363cd82a4814bce79d08b258566336d3dac95b Mon Sep 17 00:00:00 2001 From: keronshb <54602815+keronshb@users.noreply.github.com> Date: Sat, 30 Mar 2024 19:25:36 -0400 Subject: [PATCH 032/133] Give stores the ability to check for owner only (#26573) adds a check if the store belongs to the user --- .../Store/Components/StoreComponent.cs | 7 +++++++ Content.Server/Store/Systems/StoreSystem.cs | 17 +++++++++++++++++ Resources/Locale/en-US/store/store.ftl | 2 ++ 3 files changed, 26 insertions(+) diff --git a/Content.Server/Store/Components/StoreComponent.cs b/Content.Server/Store/Components/StoreComponent.cs index 063e25fbf9f..0b7dbbea094 100644 --- a/Content.Server/Store/Components/StoreComponent.cs +++ b/Content.Server/Store/Components/StoreComponent.cs @@ -78,6 +78,13 @@ public sealed partial class StoreComponent : Component [ViewVariables, DataField] public bool RefundAllowed; + /// + /// Checks if store can be opened by the account owner only. + /// Not meant to be used with uplinks. + /// + [ViewVariables(VVAccess.ReadWrite), DataField] + public bool OwnerOnly; + /// /// The map the store was originally from, used to block refunds if the map is changed /// diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index 8ce1f9bb83a..56426e04045 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -10,6 +10,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using System.Linq; +using Robust.Shared.Utility; namespace Content.Server.Store.Systems; @@ -26,6 +27,7 @@ public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnStoreOpenAttempt); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(BeforeActivatableUiOpen); @@ -65,6 +67,21 @@ private void OnShutdown(EntityUid uid, StoreComponent component, ComponentShutdo RaiseLocalEvent(uid, ref ev, true); } + private void OnStoreOpenAttempt(EntityUid uid, StoreComponent component, ActivatableUIOpenAttemptEvent args) + { + if (!component.OwnerOnly) + return; + + component.AccountOwner ??= args.User; + DebugTools.Assert(component.AccountOwner != null); + + if (component.AccountOwner == args.User) + return; + + _popup.PopupEntity(Loc.GetString("store-not-account-owner", ("store", uid)), uid, args.User); + args.Cancel(); + } + private void OnAfterInteract(EntityUid uid, CurrencyComponent component, AfterInteractEvent args) { if (args.Handled || !args.CanReach) diff --git a/Resources/Locale/en-US/store/store.ftl b/Resources/Locale/en-US/store/store.ftl index 7af2b055338..d663cc61f71 100644 --- a/Resources/Locale/en-US/store/store.ftl +++ b/Resources/Locale/en-US/store/store.ftl @@ -6,3 +6,5 @@ store-ui-traitor-flavor = Copyright (C) NT -30643 store-ui-traitor-warning = Operatives must lock their uplinks after use to avoid detection. store-withdraw-button-ui = Withdraw {$currency} + +store-not-account-owner = This {$store} is not bound to you! From a23ff527d450e02ea4ba71e3e49edc65a4d2eb62 Mon Sep 17 00:00:00 2001 From: "Wrexbe (Josh)" <81056464+wrexbe@users.noreply.github.com> Date: Sat, 30 Mar 2024 16:50:29 -0700 Subject: [PATCH 033/133] Fix round start crash (causing instant restart) (#26579) * Fix round start crash * Make `TryCreateObjective` more error tolerant --- Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs | 5 +++++ Resources/Prototypes/Objectives/objectiveGroups.yml | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs index 2e1bdc43831..07032a00ce9 100644 --- a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs +++ b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs @@ -1,6 +1,7 @@ using Content.Shared.Mind; using Content.Shared.Objectives; using Content.Shared.Objectives.Components; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Shared.Objectives.Systems; @@ -11,6 +12,7 @@ namespace Content.Shared.Objectives.Systems; public abstract class SharedObjectivesSystem : EntitySystem { [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly IPrototypeManager _protoMan = default!; private EntityQuery _metaQuery; @@ -55,6 +57,9 @@ public bool CanBeAssigned(EntityUid uid, EntityUid mindId, MindComponent mind, O /// public EntityUid? TryCreateObjective(EntityUid mindId, MindComponent mind, string proto) { + if (!_protoMan.HasIndex(proto)) + return null; + var uid = Spawn(proto); if (!TryComp(uid, out var comp)) { diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index 83921ec983c..ff126eb5d16 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -72,7 +72,6 @@ TechnologyDiskStealCollectionObjective: 1 #rnd FigurineStealCollectionObjective: 0.3 #service IDCardsStealCollectionObjective: 1 - CannabisStealCollectionObjective: 1 LAMPStealCollectionObjective: 2 #only for moth - type: weightedRandom From 69cacf6dc8e844bd7e3eb09722dd04c784568fbd Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 31 Mar 2024 14:18:50 +1300 Subject: [PATCH 034/133] Update engine to v217.1.0 (#26588) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 4002cbddb9c..b28b5ed09b3 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 4002cbddb9c9de9030a81480b45b13d978b87526 +Subproject commit b28b5ed09b361c4f2da5dd9c3e392b79e6b23c51 From 0edd0a74f426f19fc1fb7519656c62f70d02cd1f Mon Sep 17 00:00:00 2001 From: "Wrexbe (Josh)" <81056464+wrexbe@users.noreply.github.com> Date: Sat, 30 Mar 2024 18:20:45 -0700 Subject: [PATCH 035/133] Fix initial infected icon hiding (#26585) --- Content.Client/Zombies/ZombieSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Client/Zombies/ZombieSystem.cs b/Content.Client/Zombies/ZombieSystem.cs index 7c1fb38e744..49b5d6aec18 100644 --- a/Content.Client/Zombies/ZombieSystem.cs +++ b/Content.Client/Zombies/ZombieSystem.cs @@ -46,7 +46,7 @@ private void OnCanDisplayStatusIcons(EntityUid uid, ZombieComponent component, r args.Cancelled = true; } - private void OnCanDisplayStatusIcons(EntityUid uid, InitialInfectedComponent component, CanDisplayStatusIconsEvent args) + private void OnCanDisplayStatusIcons(EntityUid uid, InitialInfectedComponent component, ref CanDisplayStatusIconsEvent args) { if (HasComp(args.User) && !HasComp(args.User)) return; From 7130d1ca2f7ad3793d7a4304865506a640808369 Mon Sep 17 00:00:00 2001 From: "Wrexbe (Josh)" <81056464+wrexbe@users.noreply.github.com> Date: Sat, 30 Mar 2024 18:24:26 -0700 Subject: [PATCH 036/133] Fix Meta evac shuttle name (#26587) --- Resources/Maps/Shuttles/emergency_meta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Maps/Shuttles/emergency_meta.yml b/Resources/Maps/Shuttles/emergency_meta.yml index 57e9f98f402..385f5e3bbf3 100644 --- a/Resources/Maps/Shuttles/emergency_meta.yml +++ b/Resources/Maps/Shuttles/emergency_meta.yml @@ -21,7 +21,7 @@ entities: - uid: 1 components: - type: MetaData - name: grid + name: NT Evac Meta - type: Transform pos: -2.4375,0.859375 parent: invalid From 8676aad583ad7104151bf22932da86a87cb52c5a Mon Sep 17 00:00:00 2001 From: "Wrexbe (Josh)" <81056464+wrexbe@users.noreply.github.com> Date: Sat, 30 Mar 2024 18:26:41 -0700 Subject: [PATCH 037/133] Make timer ignore client predict setting (#26554) * Make timer ignore client predict setting * making tests run --------- Co-authored-by: wrexbe --- Content.Client/TextScreen/TextScreenSystem.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Content.Client/TextScreen/TextScreenSystem.cs b/Content.Client/TextScreen/TextScreenSystem.cs index a736933d0fa..b4d67f5f218 100644 --- a/Content.Client/TextScreen/TextScreenSystem.cs +++ b/Content.Client/TextScreen/TextScreenSystem.cs @@ -62,6 +62,8 @@ public override void Initialize() SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnTimerInit); + + UpdatesOutsidePrediction = true; } private void OnInit(EntityUid uid, TextScreenVisualsComponent component, ComponentInit args) From 175f8205c0c43ae12438bc1d9019a72d9fcca341 Mon Sep 17 00:00:00 2001 From: "Wrexbe (Josh)" <81056464+wrexbe@users.noreply.github.com> Date: Sat, 30 Mar 2024 18:34:31 -0700 Subject: [PATCH 038/133] Make advertise system survive no map inits (#26553) * Make advertise system survive no map inits * Add comment to try prevent future bugs --- Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs b/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs index bb254d11ef0..b326321d546 100644 --- a/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs +++ b/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs @@ -23,7 +23,7 @@ public sealed class AdvertiseSystem : EntitySystem /// /// The next time the game will check if advertisements should be displayed /// - private TimeSpan _nextCheckTime = TimeSpan.MaxValue; + private TimeSpan _nextCheckTime = TimeSpan.MinValue; public override void Initialize() { @@ -33,8 +33,8 @@ public override void Initialize() SubscribeLocalEvent(OnPowerReceiverEnableChangeAttempt); SubscribeLocalEvent(OnVendingEnableChangeAttempt); - // The component inits will lower this. - _nextCheckTime = TimeSpan.MaxValue; + // Force it to check on the next update. + _nextCheckTime = TimeSpan.MinValue; } private void OnMapInit(EntityUid uid, AdvertiseComponent advertise, MapInitEvent args) @@ -110,6 +110,8 @@ public override void Update(float frameTime) if (_nextCheckTime > curTime) return; + // Note that as _nextCheckTime currently starts at TimeSpan.MinValue, so this has to SET the value, not just + // increment it. _nextCheckTime = curTime + _maximumNextCheckDuration; var query = EntityQueryEnumerator(); From 602d30c908e44ea5061842bc8015cf9361a80258 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 31 Mar 2024 12:58:47 +1100 Subject: [PATCH 039/133] Update Credits (#26589) 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 f918d9320f6..f201c036304 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, africalimedrop, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUmAndXGabriel08X, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, artak10t, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, avghdev, AzzyIsNotHere, BananaFlambe, BasedUser, beck-thompson, BGare, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CakeQ, CaptainSqrBeard, Carbonhell, casperr04, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, CrafterKolyan, 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, DmitriyMX, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, 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, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, Kadeo64, KaiShibaa, kalane15, kalanosh, KEEYNy, Keikiru, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Kmc2000, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, LightVillet, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, LudwigVonChesterfield, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, M3739, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, matthst, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, MishaUnity, MisterMecky, Mith-randalf, MjrLandWhale, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nails-n-Tape, Nairodian, Naive817, NakataRin, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, Nylux, OctoRocket, OldDanceJacket, OliverOtter, onoira, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, Phill101, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, PotentiallyTom, ProfanedBane, ProPandaBear, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, Putnam3145, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, Ranger6012, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, renodubois, RiceMar1244, RieBi, RIKELOLDABOSS, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, Saakra, Samsterious, SamV522, SaphireLattice, ScalyChimp, scrato, Scribbles0, ScumbagDog, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, SirDragooon, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stanislav4ix, Stealthbomber16, StrawberryMoses, Subversionary, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, tom-leys, tomasalves8, Tomeno, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Verslebas, VigersRay, Visne, volundr-, Vordenburg, vulppine, wafehling, waylon531, weaversam8, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, youarereadingthis, YuriyKiss, zach-hill, Zandario, Zap527, 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, africalimedrop, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUmAndXGabriel08X, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BGare, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, casperr04, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, CrafterKolyan, 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, DmitriyMX, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, 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, 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, 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, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, Kadeo64, KaiShibaa, kalane15, kalanosh, KEEYNy, Keikiru, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Kmc2000, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, LightVillet, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, LudwigVonChesterfield, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, M3739, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, matthst, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, MishaUnity, MisterMecky, Mith-randalf, MjrLandWhale, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nails-n-Tape, Nairodian, Naive817, NakataRin, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, Nylux, OctoRocket, OldDanceJacket, OliverOtter, onoira, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, Phill101, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, PotentiallyTom, ProfanedBane, ProPandaBear, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, Putnam3145, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, Ranger6012, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, renodubois, RiceMar1244, RieBi, RIKELOLDABOSS, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, SirDragooon, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, Subversionary, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, tom-leys, tomasalves8, Tomeno, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Vordenburg, vulppine, wafehling, waylon531, weaversam8, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem From e7af28d22a02b356265a147f0a757739a98bda23 Mon Sep 17 00:00:00 2001 From: "Wrexbe (Josh)" <81056464+wrexbe@users.noreply.github.com> Date: Sat, 30 Mar 2024 18:59:06 -0700 Subject: [PATCH 040/133] Fix fox spawn on reach (#26584) Co-authored-by: wrexbe --- Resources/Maps/reach.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Resources/Maps/reach.yml b/Resources/Maps/reach.yml index cd1389c31b2..f84136e9ec0 100644 --- a/Resources/Maps/reach.yml +++ b/Resources/Maps/reach.yml @@ -11786,12 +11786,14 @@ entities: - uid: 1030 components: - type: Transform - pos: 21.5,-3.5 + pos: 18.5,-3.5 parent: 407 +- proto: SpawnMobFoxRenault + entities: - uid: 1826 components: - type: Transform - pos: 18.5,-3.5 + pos: 21.5,-3.5 parent: 407 - proto: SpawnPointAtmos entities: From 1f3f1d7d972bfd7671a5023779198e0ab776f813 Mon Sep 17 00:00:00 2001 From: Flareguy <78941145+Flareguy@users.noreply.github.com> Date: Sat, 30 Mar 2024 21:01:28 -0500 Subject: [PATCH 041/133] Removes SCAF armor (#26566) * removes scaf armor * replace maint loot spawner spot with basic helmet --- .../Entities/Clothing/Head/helmets.yml | 23 ---------------- .../Entities/Clothing/OuterClothing/armor.yml | 26 ------------------ .../Markers/Spawners/Random/maintenance.yml | 2 +- .../Head/Helmets/scaf.rsi/equipped-HELMET.png | Bin 890 -> 0 bytes .../Clothing/Head/Helmets/scaf.rsi/icon.png | Bin 569 -> 0 bytes .../Head/Helmets/scaf.rsi/inhand-left.png | Bin 753 -> 0 bytes .../Head/Helmets/scaf.rsi/inhand-right.png | Bin 774 -> 0 bytes .../Clothing/Head/Helmets/scaf.rsi/meta.json | 26 ------------------ .../Armor/scaf.rsi/equipped-OUTERCLOTHING.png | Bin 1487 -> 0 bytes .../OuterClothing/Armor/scaf.rsi/icon.png | Bin 667 -> 0 bytes .../Armor/scaf.rsi/inhand-left.png | Bin 691 -> 0 bytes .../Armor/scaf.rsi/inhand-right.png | Bin 680 -> 0 bytes .../OuterClothing/Armor/scaf.rsi/meta.json | 26 ------------------ Resources/migration.yml | 5 ++++ 14 files changed, 6 insertions(+), 102 deletions(-) delete mode 100644 Resources/Textures/Clothing/Head/Helmets/scaf.rsi/equipped-HELMET.png delete mode 100644 Resources/Textures/Clothing/Head/Helmets/scaf.rsi/icon.png delete mode 100644 Resources/Textures/Clothing/Head/Helmets/scaf.rsi/inhand-left.png delete mode 100644 Resources/Textures/Clothing/Head/Helmets/scaf.rsi/inhand-right.png delete mode 100644 Resources/Textures/Clothing/Head/Helmets/scaf.rsi/meta.json delete mode 100644 Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/equipped-OUTERCLOTHING.png delete mode 100644 Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/icon.png delete mode 100644 Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/inhand-left.png delete mode 100644 Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/inhand-right.png delete mode 100644 Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/meta.json diff --git a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml index 48aa43fa55d..2c5cb54094c 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml @@ -147,29 +147,6 @@ Piercing: 0.9 Heat: 0.9 -#SCAF Helmet -- type: entity - parent: ClothingHeadBase - id: ClothingHeadHelmetScaf - name: scaf helmet - description: A robust, strong helmet. - components: - - type: Sprite - sprite: Clothing/Head/Helmets/scaf.rsi - - type: Clothing - sprite: Clothing/Head/Helmets/scaf.rsi - - type: IngestionBlocker - - type: Armor - modifiers: - coefficients: - Blunt: 0.9 - Slash: 0.9 - Piercing: 0.8 - - type: Tag - tags: - - HidesHair - - WhitelistChameleon - #Space Ninja Helmet - type: entity parent: ClothingHeadEVAHelmetBase diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index fdaee45ccc8..9a1f1427402 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -183,32 +183,6 @@ - type: Clothing sprite: Clothing/OuterClothing/Armor/magusred.rsi -- type: entity - parent: ClothingOuterBaseLarge - id: ClothingOuterArmorScaf - name: scaf suit - description: A green and brown tactical suit for combat situations. - components: - - type: Sprite - sprite: Clothing/OuterClothing/Armor/scaf.rsi - - type: Clothing - sprite: Clothing/OuterClothing/Armor/scaf.rsi - - type: Armor - modifiers: - coefficients: - Blunt: 0.7 - Slash: 0.7 - Piercing: 0.4 - Heat: 0.9 - Caustic: 0.9 - - type: ExplosionResistance - damageCoefficient: 0.8 - - type: GroupExamine - - type: Tag - tags: - - Hardsuit - - WhitelistChameleon - - type: entity parent: ClothingOuterBaseLarge id: ClothingOuterArmorCaptainCarapace diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml index df61726a996..450b501d1aa 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml @@ -77,7 +77,7 @@ - ClothingHeadHatCone - ClothingNeckBling - ClothingHeadHelmetCosmonaut - - ClothingHeadHelmetScaf + - ClothingHeadHelmetBasic - ClothingShoeSlippersDuck - ClothingUnderSocksBee - ClothingUnderSocksCoder diff --git a/Resources/Textures/Clothing/Head/Helmets/scaf.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Helmets/scaf.rsi/equipped-HELMET.png deleted file mode 100644 index ec1c408550ddbd67c5dce0f1b60bc6193fd5275a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 890 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV0QF$aSW-L^LEzxY+*-{>@u?EHH>ssvg1t2TzL_}|RJFEkJ;*Z@| ztr$FxiyJktpMO8UCw8{43$yt{ozINw(j}(_`t(a!q@JqT$(LQ+bU68X$!hIQQ$%`; zgk2f#u)KB$opOD_ZQKqg)L8#czcVCCsa*1opST; z@{hshXR{l9#Jofu58V9Xnsl@F$#?&P_y4{w-17X4+Mr-E5+ zPZeWCoYay(?ftNQ81tNKf3{xwy)bNN z?G4xSZx)1da&LXP#OT1L-!G7_dUj(yHXcKtlb@XVbkFm zhDWb|d26^r)awG5>Y5|{chpd`QdN$<;Y!Mk>7BP91Eb~ROCE(EmW=Onnj|l(&GQKSOiY-fh5oZk(jSE5KX>Rg T#`x_4<|zhGS3j3^P6=G`P)3`CH9;`SSPy~(>A?;iym;smbn6#Hf1rbS3PFnKAW{h7sXx(Ah#>3|vA0+P zp*C2A4lcVJ3W2q;9dy=l%h_2gjPSiU&Wzvt%=dlH2=#g$lhn*a-KPLkfWH7B@@}lu ziVn}dkRCzlxGeC-75wf2VjN2?a0N6~D-Z;;1eIFRiKP~}%uUsTsagPlH|Yh{y+f{y zjm!WsUtL;c22eD^HKrBPBhWiGPEL=QnPB!n=cbG6nGf+A%3;BH63CEMLbD7=`9bEP--tcB&lxZ7&F=2w>w{n=S&?1^7Qz5wBP zGH_5uGky-B+iG$J;@#&JR}YAC!>zq2H{6D#%@gT1LV5(I+6F-X8@|zm)xAS!dnW;) z3*Z(2pkxi)%YzfYqu2TC8)+z-fk(INd!OIBhyYOC47bBg^$xKteM`pOy3a4a0sx*L z+1<|tpm%J{OM~yGYC-9PkTJbRM9lq8?+fsiOx61IIC;%^jEQ`o<6>doA!#Kd4-GvZ zV ztd}BQN|8{ZS;0#$i@U<2U?WTqn=x+OWYwL;U3?!9f9)IJeCNFx+4lf~AP9mW2!bF8 zf*_WSfYSjaNfO}3{A4m2NA^K6ZkRbaJ!1T|Dr*3M!~N3KVsm?LJFERH2#42vO+0z=7pkmT=5sNZXRqF+EGNzPf_OSMkqVyN1Oa2xv!LihRL?K*`px5!2;-Yz zJRO57YnIXF1YrV%ln9Up5~~;J)PbGtn~?wZeFcft3ji_Wx_idpA^-r^6DBbX+o33z z0SGtDG@ZsfP#y(ry_=k`ZnX2v14$r3mV2IGF^w6sG&~1f|F@nVq70)5jLVD<-H+xcq9x8-}^uH*cf;4D@hhPnYcxa{nMMK+54;Il& zkz6IT2UqB&q%2tjqOeie9yYVx?XESrYhAPN2LrM=KkVQGOxB4LY=S+S~r)$HNBkFRC+S>$^FfTM#lXX%&zBTPTj4oud+VmA5VW(aJ= zlTidC0hF?N*=zx)q1Df{|4aXh+2m%uqZ=aMgFohVya`Od8KdyfRKIQuhEF)s?0 zj3cw4l+DZWWE6lBxHfoJ3(T_Ev;6uhbPmX*b20#g{l>%=`byyCYEh@(a1hF*bMp3+dxrGs)&_>j zfU^vk1;aU@TrG0l+5lJGHFZo1{w`oz1n+9FW*9QSZIA(r7O-esZva6M1VIo4K@bE% z5bk5@%g<%MKQN7nt+Cs}dWFGAKz;V&*{h{_t8B@DQZ_FKV@lxKwluq-unb1bYyfMc zF|^%SX2y*XjyA#a7BK6;HZ{+j&P)bm(m8o!`x^Zgpxpw^dI!IDo`ZY(008iY+u*)@ zO4ufL9$3l%p+Clm9|8c{xM{KPXzmuI9$VEncPwuK!-8ku0Vwwi3YGVcuK_bPM#|M9 z=Mj7e`&V@x(vK82QK;(NkIw*5aKM2{<8aq5W z<~)K=&4g}kurwDdZL0x)U!7SNT;Cie0~Re{(YW4#-)#mij&_gLvH$=807*qoM6N<$ Eg2Gc?oB#j- diff --git a/Resources/Textures/Clothing/Head/Helmets/scaf.rsi/meta.json b/Resources/Textures/Clothing/Head/Helmets/scaf.rsi/meta.json deleted file mode 100644 index e0ee93d642c..00000000000 --- a/Resources/Textures/Clothing/Head/Helmets/scaf.rsi/meta.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from cev-eris at commit https://github.com/discordia-space/CEV-Eris/commit/a75dee2e6d236612dbd403dd5f8687ca930c01f1", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "equipped-HELMET", - "directions": 4 - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - } - ] -} diff --git a/Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/equipped-OUTERCLOTHING.png deleted file mode 100644 index e93c024893a80e649601e9a3b17bd37bd7d10537..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1487 zcmV;=1u*)FP)1XK-V7#%hG7Rr#J7-Jk5Kq z#t&tD%J_`80K73)0B-?!W2^w)0`SII0lWp^4L5DWZ+`n)1AsSc?_74;*a7L^3as2z z7LXQGS|Yh%oDXUP0Ey(nz&3V3`kanUVLCR2L~_A6$TchZ27$@~T7rtqVF`(?1)QE- zAT6e}gOq4!IxVKOmY^b$TtMcqgqEPP$iReZz6Jm|ZU5SZizXC)<@#RB6)I4&P5755 zP|erik$uMfmY`xfHf7vjip=0U?Z*dmgKqw%$V^Y0?)$X-_qqV_yQRnsT7n8C+r)Hi z3M*T4#z9`bC8&mQH2zA1e*C#YrRNxypUMStg$mU*tLt{a(kCmwr3JkFkkVc(K1T4e ziE6%vy5t?`-z`B!UGk!uuOWEZ#EZqpc=^HJWH<_bs#PE!3v~E=wQB-0|M$o~#A5-P zZmH3aU&%I&!wx@n96(T)yb%0uRMAN)D>tlNF+=dXAxq@EQzmks6_!t|CF1~qe+qvg z9MYSILuhN*q~%56Jl{aEjCMO1fw6R zI4l)FmXu$QuMyptgd#Tqz)%7~(G*Hd$EF}1Un7xhuhllDK@#27rEC3#Wb;V(=MP3d zqAq!jXYT{G(@4kH5d3b#!~uO2&hri9TCuFRdnic9?H7l)J@@pEzAnd|6%cD7s|I$g zIqr=Fcdxd!YQBc(qr2Ia_!vE#% zhKLg*J!*cY$bpP>vCC_^XJx101V6O}?5&Ef`qK&2pH849s7B-`D>r%^-oEFQRXD}Z zWC2Q6j|1w8x8r(U^1{=O5nv3$2Xlz&k$pyt!pcv*3jAO3$?pn}6~J2n-WV%@ zw*b5`Rse4Scw?*p-U9H(SOL5R;0@-DbaVRIoFV23J2Ej@!215C))G|smnX2YHRnhj z04vgBN?X~QgMV57OeTAXv|x4tkL*J=U&HCi1$QNomp?tZ=tn4l$D7HWb_=5Lel-aIkU1hiv2Gn%JGKhES$pUD>F-^AbWJr0@mRo+i#1Edv@4tzQ<{Hy0zl_Tm~(SSXAQ@e z!nByuNFI{BJeh+?I$X&%nfRG4pkK{^IWyzrq}UQvoagm(k{#-!^Lt8*cO#mkh ps#jpaxITcl0K73)0B-?!<3Eg!FlCpy$bS8ZIOuiNO;WaSt7Gc+jOJQy|UKv1_;d6&cz+V5ALau_UC9!4eiGzduzGKT4 zs+OdXt6&!N|Zvb!u(cj@8pS3Hw{3P)D;!?>Pe!3hm{uYLYT@30quBJ+_x}fKVbV%Oq+396 z2>M~@LFD;48hD-7%=>Y>@+Xkh(g6G_{bYT)W4^vjH^ROsQ*cvkQLfUpgQfTyOYt@E z;Wd)L7yUc%C~<%=v#UaXnQJGCkqUftK1&~dymuB7e@MK1oZUCY z))U*b5+SX0>huST>r$QLjT6a_fUg3_8z<6f_RP~!T8Z>zFIRc8>`t?1l8AF50{?=m zz~FllOm!eR+Rh6_ISOMXA~(|Vz+Gl=Rg42+1&+4!lCc@W*bHIIRxk6_%=^wh(5)L+ zm9Z2suR3|aFoT|QLk|+@<_LTe&mg4P-SEFB@D~W0__-qyB^dw!002ovPDHLkV1kcE BHk$wd diff --git a/Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/inhand-left.png deleted file mode 100644 index ce718ad3009dccde289173a2e65180ea65f811c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 691 zcmV;k0!;mhP)Vr7Mmm`QAe~GQF2&f|-cY*x3J}uW;PFM7w%KGl zH3cbv2oAY<53LD!@p2IWGae$hFhjr`Cb&77Ce8KDZPQJ|PP4&+B+FXRAlWPDjujsoTom^9!i|FhwFoIpc zb}W@!n4##_SxilIub;mE9Oz4KJzr6;UnTyE-^rIb{PsnAf3@!dy@TwXwu5eCy@TwH zAI0Ed1PK7Ir6<_jdh3RC1kMDo@sveD`GBj$oW2yL3gvLP& z;9h430-1o0w%KIzyhX-{c1!TX3SzN0w~=4qH)mjoq#~j0HAyFWb)kq;32>XfIsI5?jx`XXq(Nxa|lBC Z^BcM)3w1J!rL+J5002ovPDHLkV1mIOQ#Akp diff --git a/Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Armor/scaf.rsi/inhand-right.png deleted file mode 100644 index 6bfef738a454fd7a42912bc9b1233628db04c081..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 680 zcmV;Z0$2TsP)ho@Oe_jsthXFm1!2R%dQj-8z3ke5r2jzAz4`|fwnKB+NwD=$ zdU0OXrc69ckQjqdkhWxp5Xmno%d{l;d@do$8+hJ__e}!N0}w(8A%qY@2qA!N)^}SSnIWLs!-ywTuTFl7hQub;;rXpYe7KH@8m!fc^lsgf1zcP;Bb}at zcfY4dC&OKQ^t}Zp?I{Ymb@T^Vy%k^zTi<{e--Ul`-@w^ Date: Sat, 30 Mar 2024 19:01:52 -0700 Subject: [PATCH 042/133] Update Patrons.yml (#26578) --- Resources/Credits/Patrons.yml | 36 ++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/Resources/Credits/Patrons.yml b/Resources/Credits/Patrons.yml index 193caa7350a..f7e8df9bc0a 100644 --- a/Resources/Credits/Patrons.yml +++ b/Resources/Credits/Patrons.yml @@ -1,13 +1,9 @@ -- Name: "Tomeno" - Tier: Revolutionary - Name: "Daniel Thompson" Tier: Revolutionary - Name: "Farewell Fire" Tier: Syndicate Agent - Name: "CPM311" Tier: Revolutionary -- Name: "Bobberunio" - Tier: Revolutionary - Name: "vifs_vestige" Tier: Syndicate Agent - Name: "Anthony Fleck" @@ -30,8 +26,6 @@ Tier: Revolutionary - Name: "clyf" Tier: Nuclear Operative -- Name: "spinnermaster" - Tier: Syndicate Agent - Name: "Will M." Tier: Revolutionary - Name: "The Hateful Flesh" @@ -78,8 +72,6 @@ Tier: Syndicate Agent - Name: "Saphire" Tier: Revolutionary -- Name: "DubzyVEVO" - Tier: Revolutionary - Name: "Never Solus" Tier: Syndicate Agent - Name: "Gnomo" @@ -100,8 +92,6 @@ Tier: Revolutionary - Name: "tokie" Tier: Nuclear Operative -- Name: "BlueisDumb" - Tier: Nuclear Operative - Name: "Enricoc3l" Tier: Revolutionary - Name: "DadNotTheBelt" @@ -138,14 +128,10 @@ Tier: Revolutionary - Name: "Adam Smedstad" Tier: Revolutionary -- Name: "oBerry" - Tier: Nuclear Operative - Name: "DireBoar" Tier: Revolutionary - Name: "Ignoramis" Tier: Revolutionary -- Name: "Repo" - Tier: Nuclear Operative - Name: "612" Tier: Revolutionary - Name: "Higgtastic" @@ -208,8 +194,6 @@ Tier: Revolutionary - Name: "Georgia Partyka" Tier: Syndicate Agent -- Name: "YuNii" - Tier: Syndicate Agent - Name: "Nojan Niaki" Tier: Revolutionary - Name: "nokko" @@ -284,5 +268,23 @@ Tier: Revolutionary - Name: "Black Rose" Tier: Revolutionary -- Name: "Dourbii" +- Name: "Kim" + Tier: Nuclear Operative +- Name: "Kyu, The Meme Fairy" + Tier: Syndicate Agent +- Name: "Russian TS" + Tier: Nuclear Operative +- Name: "Andrew" + Tier: Revolutionary +- Name: "Jack" + Tier: Syndicate Agent +- Name: "Brandon Roughley" + Tier: Syndicate Agent +- Name: "Sean Lilly" + Tier: Syndicate Agent +- Name: "Dieselmohawk D." + Tier: Revolutionary +- Name: "Hannah Dawson" + Tier: Syndicate Agent +- Name: "Godfiend" Tier: Revolutionary From c1b5576cc2d5c5d74a33ef7cc71eadd9f6a1aea4 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 02:02:35 +0000 Subject: [PATCH 043/133] 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 136f7054ef8..302aea15ea5 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: Menshin - changes: - - message: The PA control box should now properly detect the PA parts in all situations - (notably on Origin) - type: Fix - - message: The PA control box now automatically face the control box on part scanning - type: Tweak - id: 5761 - time: '2024-01-21T09:17:17.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24356 - author: metalgearsloth changes: - message: Fix revolver prediction. @@ -3794,3 +3784,10 @@ id: 6260 time: '2024-03-30T06:52:27.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25886 +- author: Flareguy + changes: + - message: Removed SCAF armor. + type: Remove + id: 6261 + time: '2024-03-31T02:01:28.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26566 From 87a56b25c3f06ea98661684b8e62f8acb0afee85 Mon Sep 17 00:00:00 2001 From: Simon <63975668+Simyon264@users.noreply.github.com> Date: Sun, 31 Mar 2024 04:05:44 +0200 Subject: [PATCH 044/133] Make aghost command work on other players using optional argument (#26546) * Translations * Make aghost command work on other players using optional argument * Reviews --- .../Administration/Commands/AGhost.cs | 140 ++++++++++++------ .../en-US/administration/commands/aghost.ftl | 3 + Resources/Locale/en-US/shell.ftl | 1 + 3 files changed, 99 insertions(+), 45 deletions(-) create mode 100644 Resources/Locale/en-US/administration/commands/aghost.ftl diff --git a/Content.Server/Administration/Commands/AGhost.cs b/Content.Server/Administration/Commands/AGhost.cs index d541b1c8840..935114e7a68 100644 --- a/Content.Server/Administration/Commands/AGhost.cs +++ b/Content.Server/Administration/Commands/AGhost.cs @@ -1,72 +1,122 @@ -using Content.Server.GameTicking; +using System.Linq; +using Content.Server.GameTicking; +using Content.Server.Ghost; +using Content.Server.Mind; using Content.Shared.Administration; using Content.Shared.Ghost; using Content.Shared.Mind; +using Robust.Server.GameObjects; +using Robust.Server.Player; using Robust.Shared.Console; -namespace Content.Server.Administration.Commands +namespace Content.Server.Administration.Commands; + +[AdminCommand(AdminFlags.Admin)] +public sealed class AGhost : LocalizedCommands { - [AdminCommand(AdminFlags.Admin)] - public sealed class AGhost : IConsoleCommand + [Dependency] private readonly IEntityManager _entities = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + + public override string Command => "aghost"; + public override string Description => LocalizationManager.GetString("aghost-description"); + public override string Help => "aghost"; + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) { - [Dependency] private readonly IEntityManager _entities = default!; + if (args.Length == 1) + { + var names = _playerManager.Sessions.OrderBy(c => c.Name).Select(c => c.Name).ToArray(); + return CompletionResult.FromHintOptions(names, LocalizationManager.GetString("shell-argument-username-optional-hint")); + } - public string Command => "aghost"; - public string Description => "Makes you an admin ghost."; - public string Help => "aghost"; + return CompletionResult.Empty; + } - public void Execute(IConsoleShell shell, string argStr, string[] args) + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length > 1) { - var player = shell.Player; - if (player == null) + shell.WriteError(LocalizationManager.GetString("shell-wrong-arguments-number")); + return; + } + + var player = shell.Player; + var self = player != null; + if (player == null) + { + // If you are not a player, you require a player argument. + if (args.Length == 0) { - shell.WriteLine("Nah"); + shell.WriteError(LocalizationManager.GetString("shell-need-exactly-one-argument")); return; } - var mindSystem = _entities.System(); - if (!mindSystem.TryGetMind(player, out var mindId, out var mind)) + var didFind = _playerManager.TryGetSessionByUsername(args[0], out player); + if (!didFind) { - shell.WriteLine("You can't ghost here!"); + shell.WriteError(LocalizationManager.GetString("shell-target-player-does-not-exist")); return; } + } - var metaDataSystem = _entities.System(); - - if (mind.VisitingEntity != default && _entities.TryGetComponent(mind.VisitingEntity, out var oldGhostComponent)) + // If you are a player and a username is provided, a lookup is done to find the target player. + if (args.Length == 1) + { + var didFind = _playerManager.TryGetSessionByUsername(args[0], out player); + if (!didFind) { - mindSystem.UnVisit(mindId, mind); - // If already an admin ghost, then return to body. - if (oldGhostComponent.CanGhostInteract) - return; + shell.WriteError(LocalizationManager.GetString("shell-target-player-does-not-exist")); + return; } + } - var canReturn = mind.CurrentEntity != null - && !_entities.HasComponent(mind.CurrentEntity); - var coordinates = player.AttachedEntity != null - ? _entities.GetComponent(player.AttachedEntity.Value).Coordinates - : EntitySystem.Get().GetObserverSpawnPoint(); - var ghost = _entities.SpawnEntity(GameTicker.AdminObserverPrototypeName, coordinates); - _entities.GetComponent(ghost).AttachToGridOrMap(); + var mindSystem = _entities.System(); + var metaDataSystem = _entities.System(); + var ghostSystem = _entities.System(); + var transformSystem = _entities.System(); + var gameTicker = _entities.System(); - if (canReturn) - { - // TODO: Remove duplication between all this and "GamePreset.OnGhostAttempt()"... - if (!string.IsNullOrWhiteSpace(mind.CharacterName)) - metaDataSystem.SetEntityName(ghost, mind.CharacterName); - else if (!string.IsNullOrWhiteSpace(mind.Session?.Name)) - metaDataSystem.SetEntityName(ghost, mind.Session.Name); + if (!mindSystem.TryGetMind(player, out var mindId, out var mind)) + { + shell.WriteError(self + ? LocalizationManager.GetString("aghost-no-mind-self") + : LocalizationManager.GetString("aghost-no-mind-other")); + return; + } - mindSystem.Visit(mindId, ghost, mind); - } - else - { - metaDataSystem.SetEntityName(ghost, player.Name); - mindSystem.TransferTo(mindId, ghost, mind: mind); - } + if (mind.VisitingEntity != default && _entities.TryGetComponent(mind.VisitingEntity, out var oldGhostComponent)) + { + mindSystem.UnVisit(mindId, mind); + // If already an admin ghost, then return to body. + if (oldGhostComponent.CanGhostInteract) + return; + } + + var canReturn = mind.CurrentEntity != null + && !_entities.HasComponent(mind.CurrentEntity); + var coordinates = player!.AttachedEntity != null + ? _entities.GetComponent(player.AttachedEntity.Value).Coordinates + : gameTicker.GetObserverSpawnPoint(); + var ghost = _entities.SpawnEntity(GameTicker.AdminObserverPrototypeName, coordinates); + transformSystem.AttachToGridOrMap(ghost, _entities.GetComponent(ghost)); + + if (canReturn) + { + // TODO: Remove duplication between all this and "GamePreset.OnGhostAttempt()"... + if (!string.IsNullOrWhiteSpace(mind.CharacterName)) + metaDataSystem.SetEntityName(ghost, mind.CharacterName); + else if (!string.IsNullOrWhiteSpace(mind.Session?.Name)) + metaDataSystem.SetEntityName(ghost, mind.Session.Name); - var comp = _entities.GetComponent(ghost); - EntitySystem.Get().SetCanReturnToBody(comp, canReturn); + mindSystem.Visit(mindId, ghost, mind); } + else + { + metaDataSystem.SetEntityName(ghost, player.Name); + mindSystem.TransferTo(mindId, ghost, mind: mind); + } + + var comp = _entities.GetComponent(ghost); + ghostSystem.SetCanReturnToBody(comp, canReturn); } } diff --git a/Resources/Locale/en-US/administration/commands/aghost.ftl b/Resources/Locale/en-US/administration/commands/aghost.ftl new file mode 100644 index 00000000000..4de0639981e --- /dev/null +++ b/Resources/Locale/en-US/administration/commands/aghost.ftl @@ -0,0 +1,3 @@ +aghost-description = Makes you an admin ghost. +aghost-no-mind-self = You can't ghost here! +aghost-no-mind-other = They can't ghost here! diff --git a/Resources/Locale/en-US/shell.ftl b/Resources/Locale/en-US/shell.ftl index 7a66fbc794d..34460f001a0 100644 --- a/Resources/Locale/en-US/shell.ftl +++ b/Resources/Locale/en-US/shell.ftl @@ -47,3 +47,4 @@ shell-argument-number-invalid = Argument {$index} must be a valid number! # Hints shell-argument-username-hint = +shell-argument-username-optional-hint = [username] From ef8b16af9771fdd785251dc394aa40db30c1c224 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 02:06:49 +0000 Subject: [PATCH 045/133] Automatic changelog update --- Resources/Changelog/Admin.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index b4b7f699fe1..8acf404ad38 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -136,5 +136,13 @@ Entries: id: 18 time: '2024-03-29T05:03:34.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26500 +- author: Simyon + changes: + - message: The aghost command now accepts a optional username argument to aghost + other people. + type: Tweak + id: 19 + time: '2024-03-31T02:05:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26546 Name: Admin Order: 1 From daaa7c6de0d21c5b8d9c0b659eb62878a0410d3d Mon Sep 17 00:00:00 2001 From: blueDev2 <89804215+blueDev2@users.noreply.github.com> Date: Sat, 30 Mar 2024 22:20:44 -0400 Subject: [PATCH 046/133] Add new component to Make sound on interact (#26523) * Adds new Component: EmitSoundOnInteractUsing * Missed an import * File-scoping * Replace ID check with Prototype check * Moved component and system to shared. Set prediction to true. * Removed impoper imports and changed namespace of component to reflect changed folder. * Following function naming theme * All this code is basically deltanedas's, but it was a learning experience for me * Update Content.Shared/Sound/Components/EmitSoundOnInteractUsingComponent.cs * Update Content.Shared/Sound/Components/EmitSoundOnInteractUsingComponent.cs --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../EmitSoundOnInteractUsingComponent.cs | 15 +++++++++++++++ Content.Shared/Sound/SharedEmitSoundSystem.cs | 8 ++++++++ 2 files changed, 23 insertions(+) create mode 100644 Content.Shared/Sound/Components/EmitSoundOnInteractUsingComponent.cs diff --git a/Content.Shared/Sound/Components/EmitSoundOnInteractUsingComponent.cs b/Content.Shared/Sound/Components/EmitSoundOnInteractUsingComponent.cs new file mode 100644 index 00000000000..49118d97999 --- /dev/null +++ b/Content.Shared/Sound/Components/EmitSoundOnInteractUsingComponent.cs @@ -0,0 +1,15 @@ +using Content.Shared.Whitelist; +using Robust.Shared.Prototypes; +using Robust.Shared.GameStates; + +namespace Content.Shared.Sound.Components; + +/// +/// Whenever this item is used upon by an entity, with a tag or component within a whitelist, in the hand of a user, play a sound +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class EmitSoundOnInteractUsingComponent : BaseEmitSoundComponent +{ + [DataField(required: true)] + public EntityWhitelist Whitelist = new(); +} diff --git a/Content.Shared/Sound/SharedEmitSoundSystem.cs b/Content.Shared/Sound/SharedEmitSoundSystem.cs index cd7828fc6b3..329626964ef 100644 --- a/Content.Shared/Sound/SharedEmitSoundSystem.cs +++ b/Content.Shared/Sound/SharedEmitSoundSystem.cs @@ -41,6 +41,7 @@ public override void Initialize() SubscribeLocalEvent(OnEmitSoundOnActivateInWorld); SubscribeLocalEvent(OnEmitSoundOnPickup); SubscribeLocalEvent(OnEmitSoundOnDrop); + SubscribeLocalEvent(OnEmitSoundOnInteractUsing); SubscribeLocalEvent(OnEmitSoundOnCollide); } @@ -102,6 +103,13 @@ private void OnEmitSoundOnDrop(EntityUid uid, EmitSoundOnDropComponent component TryEmitSound(uid, component, args.User); } + private void OnEmitSoundOnInteractUsing(Entity ent, ref InteractUsingEvent args) + { + if (ent.Comp.Whitelist.IsValid(args.Used, EntityManager)) + { + TryEmitSound(ent, ent.Comp, args.User); + } + } protected void TryEmitSound(EntityUid uid, BaseEmitSoundComponent component, EntityUid? user=null, bool predict=true) { if (component.Sound == null) From b38547df530705741e0ba0789b051ffee9d6d0cf Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Sun, 31 Mar 2024 04:21:31 +0200 Subject: [PATCH 047/133] Increase syndi duffelbag storage (#26565) * Increase syndi duffelbag storage * weh --- Resources/Prototypes/Entities/Clothing/Back/duffel.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/Prototypes/Entities/Clothing/Back/duffel.yml b/Resources/Prototypes/Entities/Clothing/Back/duffel.yml index 2ac53effe63..101599d5adc 100644 --- a/Resources/Prototypes/Entities/Clothing/Back/duffel.yml +++ b/Resources/Prototypes/Entities/Clothing/Back/duffel.yml @@ -167,6 +167,9 @@ sprite: Clothing/Back/Duffels/syndicate.rsi - type: ExplosionResistance damageCoefficient: 0.1 + - type: Storage + grid: + - 0,0,8,4 - type: entity parent: ClothingBackpackDuffelSyndicate From 9d1d5de4a7d5440f2ba457348fc560d2415d060d Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 02:22:37 +0000 Subject: [PATCH 048/133] 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 302aea15ea5..9adf6658423 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix revolver prediction. - type: Fix - id: 5762 - time: '2024-01-21T11:16:46.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/19649 - author: metalgearsloth changes: - message: Fix shuttle docking highlights being inaccurate. @@ -3791,3 +3784,10 @@ id: 6261 time: '2024-03-31T02:01:28.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26566 +- author: lzk228 + changes: + - message: Syndicate duffelbag storage increased from 8x5 to 9x5. + type: Tweak + id: 6262 + time: '2024-03-31T02:21:31.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26565 From 48e5c3cc8de5f830a74de5cd7671fa42c2995edd Mon Sep 17 00:00:00 2001 From: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Date: Sat, 30 Mar 2024 21:24:38 -0500 Subject: [PATCH 049/133] Adds construction/decon graphs for plastic flaps (#26341) * Adds construction/decon graphs for plastic flaps * Dang arbitrage * undo conflict --------- Co-authored-by: Velcroboy --- .../Entities/Structures/plastic_flaps.yml | 17 ++++- .../Graphs/structures/plastic_flaps.yml | 71 +++++++++++++++++++ .../Recipes/Construction/structures.yml | 53 +++++++++++++- 3 files changed, 138 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/plastic_flaps.yml b/Resources/Prototypes/Entities/Structures/plastic_flaps.yml index bf49eb1be35..c4ee507395f 100644 --- a/Resources/Prototypes/Entities/Structures/plastic_flaps.yml +++ b/Resources/Prototypes/Entities/Structures/plastic_flaps.yml @@ -66,12 +66,15 @@ - Opaque - MidImpassable - type: Occluder + - type: Construction + graph: PlasticFlapsGraph + node: opaqueFlaps - type: entity id: PlasticFlapsAirtightClear parent: PlasticFlapsClear name: airtight plastic flaps - suffix: Airtight Clear + suffix: Airtight, Clear description: Heavy duty, slightly stronger, airtight plastic flaps. Definitely can't get past those. No way. components: - type: Destructible @@ -83,12 +86,17 @@ - !type:DoActsBehavior acts: ["Destruction"] - type: Airtight + - type: Construction + graph: PlasticFlapsGraph + node: airtightFlaps + - type: StaticPrice + price: 100 - type: entity id: PlasticFlapsAirtightOpaque parent: PlasticFlapsOpaque name: airtight plastic flaps - suffix: Airtight Opaque + suffix: Airtight, Opaque description: Heavy duty, slightly stronger, airtight plastic flaps. Definitely can't get past those. No way. components: - type: Destructible @@ -100,3 +108,8 @@ - !type:DoActsBehavior acts: ["Destruction"] - type: Airtight + - type: Construction + graph: PlasticFlapsGraph + node: airtightopaqueFlaps + - type: StaticPrice + price: 100 diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/structures/plastic_flaps.yml b/Resources/Prototypes/Recipes/Construction/Graphs/structures/plastic_flaps.yml index 9f8497ac98a..776c1491a63 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/structures/plastic_flaps.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/structures/plastic_flaps.yml @@ -13,6 +13,7 @@ - material: Plastic amount: 5 doAfter: 10 + - node: plasticFlaps entity: PlasticFlapsClear edges: @@ -24,3 +25,73 @@ steps: - tool: Anchoring doAfter: 10 + + - to: opaqueFlaps + completed: + - !type:SnapToGrid { } + steps: + - tool: Welding + doAfter: 5 + + - to: airtightFlaps + completed: + - !type:SnapToGrid { } + steps: + - material: Plastic + amount: 5 + doAfter: 5 + - tool: Screwing + doAfter: 5 + + - node: opaqueFlaps + entity: PlasticFlapsOpaque + edges: + - to: start + completed: + - !type:SpawnPrototype + prototype: SheetPlastic + amount: 5 + steps: + - tool: Anchoring + doAfter: 10 + + - to: airtightopaqueFlaps + completed: + - !type:SnapToGrid { } + steps: + - material: Plastic + amount: 5 + doAfter: 5 + - tool: Screwing + doAfter: 5 + + - node: airtightFlaps + entity: PlasticFlapsAirtightClear + edges: + - to: plasticFlaps + completed: + - !type:SpawnPrototype + prototype: SheetPlastic + amount: 5 + steps: + - tool: Screwing + doAfter: 10 + + - to: airtightopaqueFlaps #test + completed: + - !type:SnapToGrid { } + steps: + - tool: Welding + doAfter: 5 + + - node: airtightopaqueFlaps + entity: PlasticFlapsAirtightOpaque + edges: + - to: opaqueFlaps + completed: + - !type:SpawnPrototype + prototype: SheetPlastic + amount: 5 + steps: + - tool: Screwing + doAfter: 10 diff --git a/Resources/Prototypes/Recipes/Construction/structures.yml b/Resources/Prototypes/Recipes/Construction/structures.yml index 963a289babf..bea97beaaa5 100644 --- a/Resources/Prototypes/Recipes/Construction/structures.yml +++ b/Resources/Prototypes/Recipes/Construction/structures.yml @@ -86,7 +86,7 @@ canRotate: false canBuildInImpassable: false conditions: - - !type:TileNotBlocked + - !type:TileNotBlocked - type: construction name: clock wall @@ -1590,6 +1590,57 @@ conditions: - !type:TileNotBlocked +- type: construction + name: airtight plastic flaps + id: PlasticFlapsAirtight + graph: PlasticFlapsGraph + startNode: start + targetNode: airtightFlaps + category: construction-category-structures + placementMode: SnapgridCenter + description: An airtight plastic flap to let items through and keep people out. + objectType: Structure + canBuildInImpassable: false + icon: + sprite: Structures/plastic_flaps.rsi + state: plasticflaps + conditions: + - !type:TileNotBlocked + +- type: construction + name: opaque plastic flaps + id: PlasticFlapsOpaque + graph: PlasticFlapsGraph + startNode: start + targetNode: opaqueFlaps + category: construction-category-structures + placementMode: SnapgridCenter + description: An opaque plastic flap to let items through and keep people out. + objectType: Structure + canBuildInImpassable: false + icon: + sprite: Structures/plastic_flaps.rsi + state: plasticflaps + conditions: + - !type:TileNotBlocked + +- type: construction + name: airtight opaque plastic flaps + id: PlasticFlapsAirtightOpaque + graph: PlasticFlapsGraph + startNode: start + targetNode: airtightopaqueFlaps + category: construction-category-structures + placementMode: SnapgridCenter + description: An opaque, airtight plastic flap to let items through and keep people out. + objectType: Structure + canBuildInImpassable: false + icon: + sprite: Structures/plastic_flaps.rsi + state: plasticflaps + conditions: + - !type:TileNotBlocked + - type: construction name: bananium clown statue id: BananiumClownStatue From 950a6448bb0c673076c740609ad68bc6315fd058 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 02:25:44 +0000 Subject: [PATCH 050/133] 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 9adf6658423..05854cf2a2e 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix shuttle docking highlights being inaccurate. - type: Fix - id: 5763 - time: '2024-01-21T12:14:47.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24369 - author: icekot8 changes: - message: Increased the size of flatpacks made by the Flatpacker 1001. @@ -3791,3 +3784,10 @@ id: 6262 time: '2024-03-31T02:21:31.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26565 +- author: Velcroboy + changes: + - message: Changed plastic flaps to be completely constructable/deconstructable + type: Tweak + id: 6263 + time: '2024-03-31T02:24:39.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26341 From a98d0cfe2e120f90c269e810f1365dad404d645a Mon Sep 17 00:00:00 2001 From: Flareguy <78941145+Flareguy@users.noreply.github.com> Date: Sat, 30 Mar 2024 22:00:45 -0500 Subject: [PATCH 051/133] Makes secglasses roundstart (#26487) * makes secglasses roundstart * fix epic fail * fix tests questionmark? * Update Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --------- Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --- .../Catalog/Fills/Lockers/heads.yml | 6 ++--- .../Catalog/Fills/Lockers/security.yml | 13 ++++------ .../Entities/Clothing/Eyes/glasses.yml | 8 ++++++ .../Entities/Structures/Machines/lathe.yml | 1 - .../Graphs/clothing/glasses_sechud.yml | 25 +++++++++++++++++++ .../Recipes/Construction/clothing.yml | 11 ++++++++ .../Prototypes/Recipes/Lathes/security.yml | 8 ------ Resources/Prototypes/Research/arsenal.yml | 1 - .../Roles/Jobs/Security/detective.yml | 2 +- .../Roles/Jobs/Security/head_of_security.yml | 2 +- .../Roles/Jobs/Security/security_officer.yml | 2 +- .../Prototypes/Roles/Jobs/Security/warden.yml | 2 +- Resources/Prototypes/tags.yml | 3 +++ 13 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 Resources/Prototypes/Recipes/Construction/Graphs/clothing/glasses_sechud.yml diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml index 25c3fde0bd6..ae5917bc510 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml @@ -255,7 +255,6 @@ components: - type: StorageFill contents: - - id: ClothingEyesHudSecurity - id: WeaponDisabler - id: ClothingOuterCoatHoSTrench - id: ClothingMaskNeckGaiter @@ -263,7 +262,7 @@ - id: ClothingMaskGasSwat - id: ClothingBeltSecurityFilled - id: ClothingHeadsetAltSecurity - - id: ClothingEyesGlassesSunglasses + - id: ClothingEyesGlassesSecurity - id: ClothingShoesBootsJack - id: CigarGoldCase prob: 0.50 @@ -282,13 +281,12 @@ components: - type: StorageFill contents: - - id: ClothingEyesHudSecurity - id: WeaponDisabler - id: ClothingOuterCoatHoSTrench - id: ClothingMaskNeckGaiter - id: ClothingBeltSecurityFilled - id: ClothingHeadsetAltSecurity - - id: ClothingEyesGlassesSunglasses + - id: ClothingEyesGlassesSecurity - id: ClothingShoesBootsJack - id: CigarGoldCase prob: 0.50 diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/security.yml b/Resources/Prototypes/Catalog/Fills/Lockers/security.yml index 4bb300b1589..2896494978a 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/security.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/security.yml @@ -9,7 +9,7 @@ - id: WeaponDisabler - id: ClothingBeltSecurityFilled - id: Flash - - id: ClothingEyesGlassesSunglasses + - id: ClothingEyesGlassesSecurity - id: ClothingHeadsetAltSecurity - id: ClothingHandsGlovesCombat - id: ClothingShoesBootsJack @@ -19,7 +19,6 @@ - id: DoorRemoteArmory - id: ClothingOuterHardsuitWarden - id: HoloprojectorSecurity - - id: ClothingEyesHudSecurity - type: entity id: LockerWardenFilled @@ -32,7 +31,7 @@ - id: WeaponDisabler - id: ClothingBeltSecurityFilled - id: Flash - - id: ClothingEyesGlassesSunglasses + - id: ClothingEyesGlassesSecurity - id: ClothingHeadsetAltSecurity - id: ClothingHandsGlovesCombat - id: ClothingShoesBootsJack @@ -41,7 +40,6 @@ - id: RubberStampWarden - id: DoorRemoteArmory - id: HoloprojectorSecurity - - id: ClothingEyesHudSecurity - type: entity id: LockerSecurityFilled @@ -60,13 +58,12 @@ - id: ClothingBeltSecurityFilled - id: Flash prob: 0.5 - - id: ClothingEyesGlassesSunglasses + - id: ClothingEyesGlassesSecurity - id: ClothingHeadsetSecurity - id: ClothingHandsGlovesColorBlack - id: ClothingShoesBootsJack - id: WeaponMeleeNeedle prob: 0.1 - - id: ClothingEyesHudSecurity - id: HoloprojectorSecurity prob: 0.6 @@ -77,7 +74,7 @@ components: - type: StorageFill contents: - - id: ClothingEyesHudSecurity + - id: ClothingEyesGlassesSecurity - id: WeaponDisabler - id: TrackingImplanter amount: 2 @@ -112,7 +109,7 @@ components: - type: StorageFill contents: - - id: ClothingEyesHudSecurity + - id: ClothingEyesGlassesSecurity prob: 0.3 - id: ClothingHeadHatFedoraBrown - id: ClothingNeckTieDet diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml index e8cf01cb100..24944f02dd5 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml @@ -149,6 +149,11 @@ - type: FlashImmunity - type: EyeProtection protectionTime: 5 + - type: Tag + tags: + - Sunglasses + - HamsterWearable + - WhitelistChameleon - type: entity parent: ClothingEyesBase @@ -163,6 +168,9 @@ - type: FlashImmunity - type: EyeProtection protectionTime: 5 + - type: Construction + graph: GlassesSecHUD + node: glassesSec - type: Tag tags: - HamsterWearable diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 5fbe84a3fe7..5a32839a47c 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -683,7 +683,6 @@ - CartridgeMagnumUranium - CartridgePistolUranium - CartridgeRifleUranium - - ClothingEyesGlassesSecurity - ExplosivePayload - FlashPayload - HoloprojectorSecurity diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/clothing/glasses_sechud.yml b/Resources/Prototypes/Recipes/Construction/Graphs/clothing/glasses_sechud.yml new file mode 100644 index 00000000000..de264dbd588 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/clothing/glasses_sechud.yml @@ -0,0 +1,25 @@ +- type: constructionGraph + id: GlassesSecHUD + start: start + graph: + - node: start + edges: + - to: glassesSec + steps: + - tag: Sunglasses + name: sun glasses + icon: + sprite: Clothing/Eyes/Glasses/sunglasses.rsi + state: icon + doAfter: 5 + - tag: HudSecurity + name: security hud + icon: + sprite: Clothing/Eyes/Hud/sec.rsi + state: icon + doAfter: 5 + - material: Cable + amount: 5 + doAfter: 5 + - node: glassesSec + entity: ClothingEyesGlassesSecurity diff --git a/Resources/Prototypes/Recipes/Construction/clothing.yml b/Resources/Prototypes/Recipes/Construction/clothing.yml index 89f22cdf305..8907a47c6b7 100644 --- a/Resources/Prototypes/Recipes/Construction/clothing.yml +++ b/Resources/Prototypes/Recipes/Construction/clothing.yml @@ -96,3 +96,14 @@ description: Comfy, yet haunted by the ghosts of ducks you fed bread to as a child. icon: { sprite: Clothing/Shoes/Misc/duck-slippers.rsi, state: icon } objectType: Item + +- type: construction + name: security glasses + id: ClothingEyesGlassesSecurity + graph: GlassesSecHUD + startNode: start + targetNode: glassesSec + category: construction-category-clothing + description: A pair of sunglasses, modified to have a built-in security HUD. + icon: { sprite: Clothing/Eyes/Glasses/secglasses.rsi, state: icon } + objectType: Item \ No newline at end of file diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index 432594d4b5a..b3100ed70bf 100644 --- a/Resources/Prototypes/Recipes/Lathes/security.yml +++ b/Resources/Prototypes/Recipes/Lathes/security.yml @@ -87,14 +87,6 @@ materials: Plastic: 100 -- type: latheRecipe - id: ClothingEyesGlassesSecurity - result: ClothingEyesGlassesSecurity - completetime: 2 - materials: - Steel: 300 - Glass: 200 - - type: latheRecipe id: ClothingEyesHudSecurity result: ClothingEyesHudSecurity diff --git a/Resources/Prototypes/Research/arsenal.yml b/Resources/Prototypes/Research/arsenal.yml index b17fb0ce008..3b7284c7497 100644 --- a/Resources/Prototypes/Research/arsenal.yml +++ b/Resources/Prototypes/Research/arsenal.yml @@ -76,7 +76,6 @@ tier: 1 cost: 8000 recipeUnlocks: - - ClothingEyesGlassesSecurity - Truncheon - TelescopicShield - HoloprojectorSecurity diff --git a/Resources/Prototypes/Roles/Jobs/Security/detective.yml b/Resources/Prototypes/Roles/Jobs/Security/detective.yml index 21458779a02..488410949a3 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/detective.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/detective.yml @@ -27,7 +27,7 @@ jumpsuit: ClothingUniformJumpsuitDetective back: ClothingBackpackSecurityFilledDetective shoes: ClothingShoesBootsCombatFilled - eyes: ClothingEyesGlassesSunglasses + eyes: ClothingEyesGlassesSecurity head: ClothingHeadHatFedoraBrown outerClothing: ClothingOuterVestDetective id: DetectivePDA diff --git a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml index 52233573799..ef9a74fcb4a 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml @@ -46,7 +46,7 @@ back: ClothingBackpackHOSFilled shoes: ClothingShoesBootsCombatFilled outerClothing: ClothingOuterCoatHoSTrench - eyes: ClothingEyesGlassesSunglasses + eyes: ClothingEyesGlassesSecurity head: ClothingHeadHatBeretHoS id: HoSPDA gloves: ClothingHandsGlovesCombat diff --git a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml index 686d140447e..83b439fcaba 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml @@ -28,7 +28,7 @@ jumpsuit: ClothingUniformJumpsuitSec back: ClothingBackpackSecurityFilled shoes: ClothingShoesBootsCombatFilled - eyes: ClothingEyesGlassesSunglasses + eyes: ClothingEyesGlassesSecurity head: ClothingHeadHelmetBasic outerClothing: ClothingOuterArmorBasic id: SecurityPDA diff --git a/Resources/Prototypes/Roles/Jobs/Security/warden.yml b/Resources/Prototypes/Roles/Jobs/Security/warden.yml index 7d509651b6b..dbefd46a6f7 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/warden.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/warden.yml @@ -31,7 +31,7 @@ jumpsuit: ClothingUniformJumpsuitWarden back: ClothingBackpackSecurityFilled shoes: ClothingShoesBootsCombatFilled - eyes: ClothingEyesGlassesSunglasses + eyes: ClothingEyesGlassesSecurity outerClothing: ClothingOuterCoatWarden id: WardenPDA ears: ClothingHeadsetSecurity diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index ae1f8c0d19e..fe8c5a3cc17 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -1180,6 +1180,9 @@ - type: Tag id: SuitEVA + +- type: Tag + id: Sunglasses - type: Tag id: SurgeryTool From 80c4d3ea0fd2cd18e4070f7cf714067db591228c Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 03:01:52 +0000 Subject: [PATCH 052/133] 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 05854cf2a2e..dbfd5c37db9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: icekot8 - changes: - - message: Increased the size of flatpacks made by the Flatpacker 1001. - type: Tweak - id: 5764 - time: '2024-01-21T17:07:59.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24367 - author: Nairodian changes: - message: All vent critter events now have a small delay and alert message before @@ -3791,3 +3784,13 @@ id: 6263 time: '2024-03-31T02:24:39.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26341 +- author: Flareguy + changes: + - message: Security glasses have been moved from research to roundstart gear. All + officers now start with them instead of sunglasses by default. + type: Tweak + - message: You can now craft security glasses. + type: Add + id: 6264 + time: '2024-03-31T03:00:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26487 From 5f063d2d6d2a41702b60d11f779e23c34fa74410 Mon Sep 17 00:00:00 2001 From: brainfood1183 <113240905+brainfood1183@users.noreply.github.com> Date: Sun, 31 Mar 2024 04:21:18 +0100 Subject: [PATCH 053/133] Toilet Upgrade (needs review) (#22133) * Toilet Draft * fixes * toilets now have secret stash to place items in cistern. * fixes * plungers now unblock toilets. * fix sprite * new sprites and fix * fixes * improve seat sprites. * fix * removed visualisersystem changed to genericvisualizers * flush sound for toilets and copyright for toilet sprites. * fix atrributions * fixes * fix datafield flushtime * sprite improvements * fixes * multiple changes * fix * fix * fixes remove vv * moved stash related functions to secret stash system from toilet. * fix * fix * changes for recent review. * fix * fix --- .../Disposal/Systems/DisposalUnitSystem.cs | 24 +- Content.Client/Toilet/ToiletVisualsSystem.cs | 25 -- .../Conditions/ToiletLidClosed.cs | 39 --- .../Unit/EntitySystems/DisposalUnitSystem.cs | 7 +- .../Resist/EscapeInventorySystem.cs | 2 +- .../Components/SecretStashComponent.cs | 39 --- Content.Server/Toilet/ToiletSystem.cs | 197 +-------------- .../Buckle/Components/StrapComponent.cs | 11 +- .../Buckle/SharedBuckleSystem.Buckle.cs | 4 +- .../Components/SharedDisposalUnitComponent.cs | 50 ++-- .../Disposal/SharedDisposalUnitSystem.cs | 17 +- .../Plants}/PottedPlantHideComponent.cs | 5 +- .../Plants}/PottedPlantHideSystem.cs | 13 +- .../Plunger/Components/PlungerComponent.cs | 18 ++ .../Plunger/Components/PlungerUseComponent.cs | 42 ++++ Content.Shared/Plunger/PlungerDoAfterEvent.cs | 10 + .../Plunger/Systems/PlungerSystem.cs | 79 ++++++ .../Components/SecretStashComponent.cs | 90 +++++++ .../EntitySystems/SecretStashSystem.cs | 107 +++++++- .../Toilet/Components/ToiletComponent.cs | 40 +++ .../Toilet/Systems/SharedToiletSystem.cs | 109 ++++++++ Content.Shared/Toilet/ToiletComponent.cs | 32 --- Content.Shared/Toilet/ToiletVisuals.cs | 10 - .../Audio/Effects/Fluids/attributions.yml | 10 + Resources/Audio/Effects/Fluids/flush.ogg | Bin 0 -> 132202 bytes Resources/Audio/Effects/Fluids/splash.ogg | Bin 0 -> 44679 bytes Resources/Audio/Weapons/glug.ogg | Bin 0 -> 27517 bytes .../components/secret-stash-component.ftl | 1 + .../Locale/en-US/toilet/toilet-component.ftl | 2 + .../Objects/Specific/Janitorial/janitor.yml | 9 + .../Entities/Structures/Furniture/toilet.yml | 100 ++++++-- .../Structures/Piping/Disposal/units.yml | 20 +- .../Construction/Graphs/furniture/toilet.yml | 1 - .../Recipes/Construction/furniture.yml | 2 +- .../toilet.rsi/closed_toilet_seat_down.png | Bin 1445 -> 0 bytes .../toilet.rsi/closed_toilet_seat_up.png | Bin 1553 -> 0 bytes .../Furniture/toilet.rsi/condisposal.png | Bin 0 -> 31043 bytes .../toilet.rsi/disposal-charging.png | Bin 0 -> 31052 bytes .../Furniture/toilet.rsi/disposal-closed.png | Bin 0 -> 29675 bytes .../Furniture/toilet.rsi/disposal-down.png | Bin 0 -> 29554 bytes .../Furniture/toilet.rsi/disposal-flush.png | Bin 0 -> 25461 bytes .../Furniture/toilet.rsi/disposal-open.png | Bin 0 -> 28502 bytes .../Furniture/toilet.rsi/disposal-up.png | Bin 0 -> 29203 bytes .../Furniture/toilet.rsi/disposal.png | Bin 0 -> 31095 bytes .../Furniture/toilet.rsi/dispover-charge.png | Bin 0 -> 28316 bytes .../Furniture/toilet.rsi/dispover-full.png | Bin 0 -> 17416 bytes .../Furniture/toilet.rsi/dispover-handle.png | Bin 0 -> 17416 bytes .../Furniture/toilet.rsi/dispover-ready.png | Bin 0 -> 28318 bytes .../Structures/Furniture/toilet.rsi/meta.json | 238 +++++++++++++++++- .../toilet.rsi/open_toilet_seat_down.png | Bin 1437 -> 0 bytes .../toilet.rsi/open_toilet_seat_up.png | Bin 1535 -> 0 bytes 51 files changed, 889 insertions(+), 464 deletions(-) delete mode 100644 Content.Client/Toilet/ToiletVisualsSystem.cs delete mode 100644 Content.Server/Construction/Conditions/ToiletLidClosed.cs delete mode 100644 Content.Server/Storage/Components/SecretStashComponent.cs rename {Content.Server/Plants/Components => Content.Shared/Plants}/PottedPlantHideComponent.cs (80%) rename {Content.Server/Plants/Systems => Content.Shared/Plants}/PottedPlantHideSystem.cs (86%) create mode 100644 Content.Shared/Plunger/Components/PlungerComponent.cs create mode 100644 Content.Shared/Plunger/Components/PlungerUseComponent.cs create mode 100644 Content.Shared/Plunger/PlungerDoAfterEvent.cs create mode 100644 Content.Shared/Plunger/Systems/PlungerSystem.cs create mode 100644 Content.Shared/Storage/Components/SecretStashComponent.cs rename {Content.Server => Content.Shared}/Storage/EntitySystems/SecretStashSystem.cs (55%) create mode 100644 Content.Shared/Toilet/Components/ToiletComponent.cs create mode 100644 Content.Shared/Toilet/Systems/SharedToiletSystem.cs delete mode 100644 Content.Shared/Toilet/ToiletComponent.cs delete mode 100644 Content.Shared/Toilet/ToiletVisuals.cs create mode 100644 Resources/Audio/Effects/Fluids/flush.ogg create mode 100644 Resources/Audio/Effects/Fluids/splash.ogg create mode 100644 Resources/Audio/Weapons/glug.ogg delete mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_down.png delete mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_up.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/condisposal.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/disposal-charging.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/disposal-closed.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/disposal-down.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/disposal-flush.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/disposal-open.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/disposal-up.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/disposal.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/dispover-charge.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/dispover-full.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/dispover-handle.png create mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/dispover-ready.png delete mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/open_toilet_seat_down.png delete mode 100644 Resources/Textures/Structures/Furniture/toilet.rsi/open_toilet_seat_up.png diff --git a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs index 8c40c784219..b9e4a386604 100644 --- a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs +++ b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs @@ -96,24 +96,22 @@ private void OnAppearanceChange(EntityUid uid, SharedDisposalUnitComponent unit, private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, SpriteComponent sprite, AppearanceComponent appearance) { if (!_appearanceSystem.TryGetData(uid, Visuals.VisualState, out var state, appearance)) - { return; - } sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == VisualState.UnAnchored); sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == VisualState.Anchored); - sprite.LayerSetVisible(DisposalUnitVisualLayers.BaseFlush, state is VisualState.Flushing or VisualState.Charging); + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFlush, state is VisualState.OverlayFlushing or VisualState.OverlayCharging); var chargingState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseCharging, out var chargingLayer) ? sprite.LayerGetState(chargingLayer) : new RSI.StateId(DefaultChargeState); // This is a transient state so not too worried about replaying in range. - if (state == VisualState.Flushing) + if (state == VisualState.OverlayFlushing) { if (!_animationSystem.HasRunningAnimation(uid, AnimationKey)) { - var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseFlush, out var flushLayer) + var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer) ? sprite.LayerGetState(flushLayer) : new RSI.StateId(DefaultFlushState); @@ -125,7 +123,7 @@ private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, Sprite { new AnimationTrackSpriteFlick { - LayerKey = DisposalUnitVisualLayers.BaseFlush, + LayerKey = DisposalUnitVisualLayers.OverlayFlush, KeyFrames = { // Play the flush animation @@ -154,26 +152,18 @@ private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, Sprite _animationSystem.Play(uid, anim, AnimationKey); } } - else if (state == VisualState.Charging) - { - sprite.LayerSetState(DisposalUnitVisualLayers.BaseFlush, chargingState); - } + else if (state == VisualState.OverlayCharging) + sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, new RSI.StateId("disposal-charging")); else - { _animationSystem.Stop(uid, AnimationKey); - } if (!_appearanceSystem.TryGetData(uid, Visuals.Handle, out var handleState, appearance)) - { handleState = HandleState.Normal; - } sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != HandleState.Normal); if (!_appearanceSystem.TryGetData(uid, Visuals.Light, out var lightState, appearance)) - { lightState = LightStates.Off; - } sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging, (lightState & LightStates.Charging) != 0); @@ -189,7 +179,7 @@ public enum DisposalUnitVisualLayers : byte Unanchored, Base, BaseCharging, - BaseFlush, + OverlayFlush, OverlayCharging, OverlayReady, OverlayFull, diff --git a/Content.Client/Toilet/ToiletVisualsSystem.cs b/Content.Client/Toilet/ToiletVisualsSystem.cs deleted file mode 100644 index 5367772ab08..00000000000 --- a/Content.Client/Toilet/ToiletVisualsSystem.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Content.Shared.Toilet; -using Robust.Client.GameObjects; - -namespace Content.Client.Toilet; - -public sealed class ToiletVisualsSystem : VisualizerSystem -{ - protected override void OnAppearanceChange(EntityUid uid, ToiletComponent component, ref AppearanceChangeEvent args) - { - if (args.Sprite == null) return; - - AppearanceSystem.TryGetData(uid, ToiletVisuals.LidOpen, out var lidOpen, args.Component); - AppearanceSystem.TryGetData(uid, ToiletVisuals.SeatUp, out var seatUp, args.Component); - - var state = (lidOpen, seatUp) switch - { - (false, false) => "closed_toilet_seat_down", - (false, true) => "closed_toilet_seat_up", - (true, false) => "open_toilet_seat_down", - (true, true) => "open_toilet_seat_up" - }; - - args.Sprite.LayerSetState(0, state); - } -} diff --git a/Content.Server/Construction/Conditions/ToiletLidClosed.cs b/Content.Server/Construction/Conditions/ToiletLidClosed.cs deleted file mode 100644 index 40a3be006bc..00000000000 --- a/Content.Server/Construction/Conditions/ToiletLidClosed.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Content.Shared.Construction; -using Content.Shared.Examine; -using Content.Shared.Toilet; -using JetBrains.Annotations; - -namespace Content.Server.Construction.Conditions -{ - [UsedImplicitly] - [DataDefinition] - public sealed partial class ToiletLidClosed : IGraphCondition - { - public bool Condition(EntityUid uid, IEntityManager entityManager) - { - if (!entityManager.TryGetComponent(uid, out ToiletComponent? toilet)) - return false; - - return !toilet.LidOpen; - } - - public bool DoExamine(ExaminedEvent args) - { - var entity = args.Examined; - - if (!IoCManager.Resolve().TryGetComponent(entity, out ToiletComponent? toilet)) return false; - if (!toilet.LidOpen) return false; - - args.PushMarkup(Loc.GetString("construction-examine-condition-toilet-lid-closed") + "\n"); - return true; - } - - public IEnumerable GenerateGuideEntry() - { - yield return new ConstructionGuideEntry() - { - Localization = "construction-step-condition-toilet-lid-closed" - }; - } - } -} diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index a03ba5d2313..8a7ea438be2 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -135,8 +135,7 @@ private void AddClimbInsideVerb(EntityUid uid, SharedDisposalUnitComponent compo { // This is not an interaction, activation, or alternative verb type because unfortunately most users are // unwilling to accept that this is where they belong and don't want to accidentally climb inside. - if (!component.MobsCanEnter || - !args.CanAccess || + if (!args.CanAccess || !args.CanInteract || component.Container.ContainedEntities.Contains(args.User) || !_actionBlockerSystem.CanMove(args.User)) @@ -630,10 +629,10 @@ public void UpdateVisualState(EntityUid uid, SharedDisposalUnitComponent compone switch (state) { case DisposalsPressureState.Flushed: - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Flushing, appearance); + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.OverlayFlushing, appearance); break; case DisposalsPressureState.Pressurizing: - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Charging, appearance); + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.OverlayCharging, appearance); break; case DisposalsPressureState.Ready: _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Anchored, appearance); diff --git a/Content.Server/Resist/EscapeInventorySystem.cs b/Content.Server/Resist/EscapeInventorySystem.cs index 6bce38fbacf..35c7d0bc65b 100644 --- a/Content.Server/Resist/EscapeInventorySystem.cs +++ b/Content.Server/Resist/EscapeInventorySystem.cs @@ -1,5 +1,5 @@ using Content.Server.Popups; -using Content.Server.Storage.Components; +using Content.Shared.Storage.Components; using Content.Shared.ActionBlocker; using Content.Shared.DoAfter; using Content.Shared.Hands.EntitySystems; diff --git a/Content.Server/Storage/Components/SecretStashComponent.cs b/Content.Server/Storage/Components/SecretStashComponent.cs deleted file mode 100644 index a63cb074ad2..00000000000 --- a/Content.Server/Storage/Components/SecretStashComponent.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Content.Server.Storage.EntitySystems; -using Content.Shared.Containers.ItemSlots; -using Content.Shared.Item; -using Content.Shared.Toilet; -using Robust.Shared.Containers; -using Robust.Shared.Prototypes; - -namespace Content.Server.Storage.Components -{ - /// - /// Logic for a secret slot stash, like plant pot or toilet cistern. - /// Unlike it doesn't have interaction logic or verbs. - /// Other classes like should implement it. - /// - [RegisterComponent] - [Access(typeof(SecretStashSystem))] - public sealed partial class SecretStashComponent : Component - { - /// - /// Max item size that can be fitted into secret stash. - /// - [DataField("maxItemSize")] - public ProtoId MaxItemSize = "Small"; - - /// - /// IC secret stash name. For example "the toilet cistern". - /// If empty string, will replace it with entity name in init. - /// - [DataField("secretPartName", readOnly: true)] - public string SecretPartName { get; set; } = ""; - - /// - /// Container used to keep secret stash item. - /// - [ViewVariables] - public ContainerSlot ItemContainer = default!; - - } -} diff --git a/Content.Server/Toilet/ToiletSystem.cs b/Content.Server/Toilet/ToiletSystem.cs index 8bf8457e075..e184ddf0d5b 100644 --- a/Content.Server/Toilet/ToiletSystem.cs +++ b/Content.Server/Toilet/ToiletSystem.cs @@ -1,197 +1,8 @@ -using Content.Server.Body.Systems; -using Content.Shared.Buckle; -using Content.Server.Popups; -using Content.Server.Storage.Components; -using Content.Server.Storage.EntitySystems; -using Content.Shared.Audio; -using Content.Shared.Body.Components; -using Content.Shared.Body.Part; -using Content.Shared.Buckle.Components; -using Content.Shared.Examine; -using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Popups; -using Content.Shared.Toilet; -using Content.Shared.Tools; -using Content.Shared.Tools.Components; -using Content.Shared.Verbs; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using Robust.Shared.Random; -using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem; +using Content.Shared.Toilet.Systems; -namespace Content.Server.Toilet -{ - public sealed class ToiletSystem : EntitySystem - { - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly BodySystem _body = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SecretStashSystem _secretStash = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly SharedToolSystem _tool = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnInteractUsing); - SubscribeLocalEvent(OnInteractHand); - SubscribeLocalEvent(OnExamine); - SubscribeLocalEvent(OnSuicide); - SubscribeLocalEvent(OnToiletPried); - SubscribeLocalEvent>(OnToggleSeatVerb); - } - - private void OnSuicide(EntityUid uid, ToiletComponent component, SuicideEvent args) - { - if (args.Handled) - return; - - // Check that victim has a head - // FIXME: since suiciding turns you into a ghost immediately, both messages are seen, not sure how this can be fixed - if (TryComp(args.Victim, out var body) && - _body.BodyHasPartType(args.Victim, BodyPartType.Head, body)) - { - var othersMessage = Loc.GetString("toilet-component-suicide-head-message-others", - ("victim", Identity.Entity(args.Victim, EntityManager)), ("owner", uid)); - _popup.PopupEntity(othersMessage, uid, Filter.PvsExcept(args.Victim), true, PopupType.MediumCaution); - - var selfMessage = Loc.GetString("toilet-component-suicide-head-message", - ("owner", uid)); - _popup.PopupEntity(selfMessage, uid, args.Victim, PopupType.LargeCaution); - - args.SetHandled(SuicideKind.Asphyxiation); - } - else - { - var othersMessage = Loc.GetString("toilet-component-suicide-message-others", - ("victim", Identity.Entity(args.Victim, EntityManager)), ("owner", uid)); - _popup.PopupEntity(othersMessage, uid, Filter.PvsExcept(uid), true, PopupType.MediumCaution); - - var selfMessage = Loc.GetString("toilet-component-suicide-message", - ("owner", uid)); - _popup.PopupEntity(selfMessage, uid, args.Victim, PopupType.LargeCaution); - - args.SetHandled(SuicideKind.Blunt); - } - } - - private void OnInit(EntityUid uid, ToiletComponent component, ComponentInit args) - { - EnsureComp(uid); - } - - private void OnMapInit(EntityUid uid, ToiletComponent component, MapInitEvent args) - { - // roll is toilet seat will be up or down - component.IsSeatUp = _random.Prob(0.5f); - UpdateSprite(uid, component); - } - - private void OnInteractUsing(EntityUid uid, ToiletComponent component, InteractUsingEvent args) - { - if (args.Handled) - return; - - // are player trying place or lift of cistern lid? - if (_tool.UseTool(args.Used, args.User, uid, component.PryLidTime, component.PryingQuality, new ToiletPryDoAfterEvent())) - { - args.Handled = true; - } - // maybe player trying to hide something inside cistern? - else if (component.LidOpen) - { - args.Handled = true; - _secretStash.TryHideItem(uid, args.User, args.Used); - } - } - - private void OnInteractHand(EntityUid uid, ToiletComponent component, InteractHandEvent args) - { - if (args.Handled) - return; +namespace Content.Server.Toilet; - // trying get something from stash? - if (component.LidOpen) - { - var gotItem = _secretStash.TryGetItem(uid, args.User); - if (gotItem) - { - args.Handled = true; - return; - } - } - - args.Handled = true; - } - - private void OnToggleSeatVerb(EntityUid uid, ToiletComponent component, GetVerbsEvent args) - { - if (!args.CanInteract || !args.CanAccess || !CanToggle(uid)) - return; - - var alterToiletSeatText = component.IsSeatUp ? Loc.GetString("toilet-seat-close") : Loc.GetString("toilet-seat-open"); - - var verb = new AlternativeVerb() - { - Act = () => { - if (CanToggle(uid)) - ToggleToiletSeat(uid, component); - }, - Text = alterToiletSeatText - }; - - args.Verbs.Add(verb); - } - - private void OnExamine(EntityUid uid, ToiletComponent component, ExaminedEvent args) - { - if (args.IsInDetailsRange && component.LidOpen) - { - if (_secretStash.HasItemInside(uid)) - { - var msg = Loc.GetString("toilet-component-on-examine-found-hidden-item"); - args.PushMarkup(msg); - } - } - } - - private void OnToiletPried(EntityUid uid, ToiletComponent toilet, ToiletPryDoAfterEvent args) - { - if (args.Cancelled) - return; - - toilet.LidOpen = !toilet.LidOpen; - UpdateSprite(uid, toilet); - } - - public bool CanToggle(EntityUid uid) - { - return TryComp(uid, out var strap) && strap.BuckledEntities.Count == 0; - } - - public void ToggleToiletSeat(EntityUid uid, ToiletComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - component.IsSeatUp = !component.IsSeatUp; - _audio.PlayPvs(component.ToggleSound, uid, AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation)); - UpdateSprite(uid, component); - } - - private void UpdateSprite(EntityUid uid, ToiletComponent component) - { - if (!TryComp(uid, out var appearance)) - return; +public sealed class ToiletSystem : SharedToiletSystem +{ - _appearance.SetData(uid, ToiletVisuals.LidOpen, component.LidOpen, appearance); - _appearance.SetData(uid, ToiletVisuals.SeatUp, component.IsSeatUp, appearance); - } - } } diff --git a/Content.Shared/Buckle/Components/StrapComponent.cs b/Content.Shared/Buckle/Components/StrapComponent.cs index f25e1b03741..72c92ebf84b 100644 --- a/Content.Shared/Buckle/Components/StrapComponent.cs +++ b/Content.Shared/Buckle/Components/StrapComponent.cs @@ -22,9 +22,14 @@ public sealed partial class StrapComponent : Component /// Entities that this strap accepts and can buckle /// If null it accepts any entity /// - [DataField] - [ViewVariables] - public EntityWhitelist? AllowedEntities; + [DataField, ViewVariables(VVAccess.ReadWrite)] + public EntityWhitelist? Whitelist; + + /// + /// Entities that this strap does not accept and cannot buckle. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public EntityWhitelist? Blacklist; /// /// The change in position to the strapped mob diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index 3d1fbf2b696..0d67473ffee 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -221,8 +221,8 @@ private bool CanBuckle( } // Does it pass the Whitelist - if (strapComp.AllowedEntities != null && - !strapComp.AllowedEntities.IsValid(userUid, EntityManager)) + if (strapComp.Whitelist != null && + !strapComp.Whitelist.IsValid(buckleUid, EntityManager) || strapComp.Blacklist?.IsValid(buckleUid, EntityManager) == true) { if (_netManager.IsServer) _popup.PopupEntity(Loc.GetString("buckle-component-cannot-fit-message"), userUid, buckleUid, PopupType.Medium); diff --git a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs b/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs index 72586be1ec6..4948cb6640e 100644 --- a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs +++ b/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs @@ -1,4 +1,5 @@ using Robust.Shared.Audio; +using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Serialization; @@ -17,6 +18,18 @@ public abstract partial class SharedDisposalUnitComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField("soundFlush")] public SoundSpecifier? FlushSound = new SoundPathSpecifier("/Audio/Machines/disposalflush.ogg"); + /// + /// Blacklists (prevents) entities listed from being placed inside. + /// + [DataField] + public EntityWhitelist? Blacklist; + + /// + /// Whitelists (allows) entities listed from being placed inside. + /// + [DataField] + public EntityWhitelist? Whitelist; + /// /// Sound played when an object is inserted into the disposal unit. /// @@ -33,20 +46,20 @@ public abstract partial class SharedDisposalUnitComponent : Component /// /// State for this disposals unit. /// - [DataField("state")] + [DataField] public DisposalsPressureState State; // TODO: Just make this use vaulting. /// /// We'll track whatever just left disposals so we know what collision we need to ignore until they stop intersecting our BB. /// - [ViewVariables, DataField("recentlyEjected")] + [ViewVariables, DataField] public List RecentlyEjected = new(); /// /// Next time the disposal unit will be pressurized. /// - [DataField("nextPressurized", customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField(customTypeSerializer:typeof(TimeOffsetSerializer))] public TimeSpan NextPressurized = TimeSpan.Zero; /// @@ -58,63 +71,60 @@ public abstract partial class SharedDisposalUnitComponent : Component /// /// How long it takes from the start of a flush animation to return the sprite to normal. /// - [DataField("flushDelay")] + [DataField] public TimeSpan FlushDelay = TimeSpan.FromSeconds(3); - [DataField("mobsCanEnter")] - public bool MobsCanEnter = true; - /// /// Removes the pressure requirement for flushing. /// - [DataField("disablePressure"), ViewVariables(VVAccess.ReadWrite)] + [DataField, ViewVariables(VVAccess.ReadWrite)] public bool DisablePressure; /// - /// Last time that an entity tried to exit this disposal unit. + /// Last time that an entity tried to exit this disposal unit. /// [ViewVariables] public TimeSpan LastExitAttempt; - [DataField("autoEngageEnabled")] + [DataField] public bool AutomaticEngage = true; [ViewVariables(VVAccess.ReadWrite)] - [DataField("autoEngageTime")] + [DataField] public TimeSpan AutomaticEngageTime = TimeSpan.FromSeconds(30); /// - /// Delay from trying to enter disposals ourselves. + /// Delay from trying to enter disposals ourselves. /// [ViewVariables(VVAccess.ReadWrite)] - [DataField("entryDelay")] + [DataField] public float EntryDelay = 0.5f; /// - /// Delay from trying to shove someone else into disposals. + /// Delay from trying to shove someone else into disposals. /// [ViewVariables(VVAccess.ReadWrite)] public float DraggedEntryDelay = 2.0f; /// - /// Container of entities inside this disposal unit. + /// Container of entities inside this disposal unit. /// [ViewVariables] public Container Container = default!; // TODO: Network power shit instead fam. - [ViewVariables, DataField("powered")] + [ViewVariables, DataField] public bool Powered; /// /// Was the disposals unit engaged for a manual flush. /// - [ViewVariables(VVAccess.ReadWrite), DataField("engaged")] + [ViewVariables(VVAccess.ReadWrite), DataField] public bool Engaged; /// /// Next time this unit will flush. Is the lesser of and /// - [ViewVariables, DataField("nextFlush", customTypeSerializer:typeof(TimeOffsetSerializer))] + [ViewVariables, DataField(customTypeSerializer:typeof(TimeOffsetSerializer))] public TimeSpan? NextFlush; [Serializable, NetSerializable] @@ -130,8 +140,8 @@ public enum VisualState : byte { UnAnchored, Anchored, - Flushing, - Charging + OverlayFlushing, + OverlayCharging } [Serializable, NetSerializable] diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs index 9afd683cbdc..c39139f9a57 100644 --- a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs +++ b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs @@ -1,12 +1,10 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Content.Shared.Body.Components; using Content.Shared.Disposal.Components; using Content.Shared.DoAfter; using Content.Shared.DragDrop; using Content.Shared.Emag.Systems; using Content.Shared.Item; -using Content.Shared.Mobs.Components; -using Content.Shared.Mobs.Systems; using Content.Shared.Throwing; using Robust.Shared.Audio; using Robust.Shared.Physics.Components; @@ -26,7 +24,6 @@ public abstract class SharedDisposalUnitSystem : EntitySystem { [Dependency] protected readonly IGameTiming GameTiming = default!; [Dependency] protected readonly MetaDataSystem Metadata = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] protected readonly SharedJointSystem Joints = default!; protected static TimeSpan ExitAttemptDelay = TimeSpan.FromSeconds(0.5); @@ -112,19 +109,21 @@ public virtual bool CanInsert(EntityUid uid, SharedDisposalUnitComponent compone if (!Transform(uid).Anchored) return false; - // TODO: Probably just need a disposable tag. var storable = HasComp(entity); if (!storable && !HasComp(entity)) return false; - //Check if the entity is a mob and if mobs can be inserted - if (TryComp(entity, out var damageState) && !component.MobsCanEnter) + if (component.Blacklist?.IsValid(entity, EntityManager) == true) return false; - if (TryComp(entity, out var physics) && (physics.CanCollide || storable)) + if (component.Whitelist != null && component.Whitelist?.IsValid(entity, EntityManager) != true) + return false; + + if (TryComp(entity, out var physics) && (physics.CanCollide) || storable) return true; + else + return false; - return damageState != null && (!component.MobsCanEnter || _mobState.IsDead(entity, damageState)); } public abstract void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null); diff --git a/Content.Server/Plants/Components/PottedPlantHideComponent.cs b/Content.Shared/Plants/PottedPlantHideComponent.cs similarity index 80% rename from Content.Server/Plants/Components/PottedPlantHideComponent.cs rename to Content.Shared/Plants/PottedPlantHideComponent.cs index bc35bbe44f9..2e02272494f 100644 --- a/Content.Server/Plants/Components/PottedPlantHideComponent.cs +++ b/Content.Shared/Plants/PottedPlantHideComponent.cs @@ -1,8 +1,7 @@ -using Content.Server.Plants.Systems; -using Content.Server.Storage.Components; +using Content.Shared.Storage.Components; using Robust.Shared.Audio; -namespace Content.Server.Plants.Components +namespace Content.Shared.Plants { /// /// Interaction wrapper for . diff --git a/Content.Server/Plants/Systems/PottedPlantHideSystem.cs b/Content.Shared/Plants/PottedPlantHideSystem.cs similarity index 86% rename from Content.Server/Plants/Systems/PottedPlantHideSystem.cs rename to Content.Shared/Plants/PottedPlantHideSystem.cs index 09571c60a13..fd256fd9263 100644 --- a/Content.Server/Plants/Systems/PottedPlantHideSystem.cs +++ b/Content.Shared/Plants/PottedPlantHideSystem.cs @@ -1,18 +1,15 @@ -using Content.Server.Plants.Components; -using Content.Server.Popups; -using Content.Server.Storage.Components; -using Content.Server.Storage.EntitySystems; -using Content.Shared.Audio; +using Content.Shared.Popups; +using Content.Shared.Storage.Components; +using Content.Shared.Storage.EntitySystems; using Content.Shared.Interaction; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -namespace Content.Server.Plants.Systems +namespace Content.Shared.Plants { public sealed class PottedPlantHideSystem : EntitySystem { - [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SecretStashSystem _stashSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; diff --git a/Content.Shared/Plunger/Components/PlungerComponent.cs b/Content.Shared/Plunger/Components/PlungerComponent.cs new file mode 100644 index 00000000000..101121fe4c4 --- /dev/null +++ b/Content.Shared/Plunger/Components/PlungerComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Plunger.Components +{ + /// + /// Allows entity to unblock target entity with PlungerUseComponent. + /// + [RegisterComponent, NetworkedComponent,AutoGenerateComponentState] + public sealed partial class PlungerComponent : Component + { + /// + /// Duration of plunger doafter event. + /// + [DataField] + [AutoNetworkedField] + public float PlungeDuration = 2f; + } +} diff --git a/Content.Shared/Plunger/Components/PlungerUseComponent.cs b/Content.Shared/Plunger/Components/PlungerUseComponent.cs new file mode 100644 index 00000000000..e886a4ef7ad --- /dev/null +++ b/Content.Shared/Plunger/Components/PlungerUseComponent.cs @@ -0,0 +1,42 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Content.Shared.Random; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Plunger.Components +{ + /// + /// Entity can interact with plungers. + /// + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] + public sealed partial class PlungerUseComponent : Component + { + /// + /// If true entity has been plungered. + /// + [DataField] + [AutoNetworkedField] + public bool Plunged; + + /// + /// If true entity can interact with plunger. + /// + [DataField] + [AutoNetworkedField] + public bool NeedsPlunger = false; + + /// + /// A weighted random entity prototype containing the different loot that rummaging can provide. + /// + [DataField] + [AutoNetworkedField] + public ProtoId PlungerLoot = "PlungerLoot"; + + + /// + /// Sound played on rummage completion. + /// + [DataField] + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Effects/Fluids/glug.ogg"); + } +} diff --git a/Content.Shared/Plunger/PlungerDoAfterEvent.cs b/Content.Shared/Plunger/PlungerDoAfterEvent.cs new file mode 100644 index 00000000000..b803f5136f6 --- /dev/null +++ b/Content.Shared/Plunger/PlungerDoAfterEvent.cs @@ -0,0 +1,10 @@ + +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.Plunger; + +[Serializable, NetSerializable] +public sealed partial class PlungerDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/Plunger/Systems/PlungerSystem.cs b/Content.Shared/Plunger/Systems/PlungerSystem.cs new file mode 100644 index 00000000000..57bd77a7d95 --- /dev/null +++ b/Content.Shared/Plunger/Systems/PlungerSystem.cs @@ -0,0 +1,79 @@ +using Content.Shared.DoAfter; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Content.Shared.Plunger.Components; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Timing; +using Content.Shared.Random.Helpers; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Content.Shared.Random; + +namespace Content.Shared.Plunger.Systems; + +/// +/// Plungers can be used to unblock entities with PlungerUseComponent. +/// +public sealed class PlungerSystem : EntitySystem +{ + [Dependency] protected readonly IPrototypeManager _proto = default!; + [Dependency] protected readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnInteract); + SubscribeLocalEvent(OnDoAfter); + } + + private void OnInteract(EntityUid uid, PlungerComponent component, AfterInteractEvent args) + { + if (args.Handled) + return; + + if (!args.CanReach || args.Target is not { Valid: true } target) + return; + + if (!TryComp(args.Target, out var plunger)) + return; + + if (plunger.NeedsPlunger) + return; + + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.PlungeDuration, new PlungerDoAfterEvent(), uid, target, uid) + { + BreakOnMove = true, + BreakOnDamage = true, + MovementThreshold = 1.0f, + }); + args.Handled = true; + } + + private void OnDoAfter(EntityUid uid, PlungerComponent component, DoAfterEvent args) + { + if (args.Cancelled || args.Handled || args.Args.Target == null) + return; + + if (args.Target is not { Valid: true } target) + return; + + if (!TryComp(target, out PlungerUseComponent? plunge)) + return; + + _popup.PopupClient(Loc.GetString("plunger-unblock", ("target", target)), args.User, args.User, PopupType.Medium); + plunge.Plunged = true; + + var spawn = _proto.Index(plunge.PlungerLoot).Pick(_random); + + _audio.PlayPredicted(plunge.Sound, uid, uid); + Spawn(spawn, Transform(target).Coordinates); + RemComp(target); + Dirty(target, plunge); + + args.Handled = true; + } +} + diff --git a/Content.Shared/Storage/Components/SecretStashComponent.cs b/Content.Shared/Storage/Components/SecretStashComponent.cs new file mode 100644 index 00000000000..8595f79ca57 --- /dev/null +++ b/Content.Shared/Storage/Components/SecretStashComponent.cs @@ -0,0 +1,90 @@ +using Content.Shared.Storage.EntitySystems; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Item; +using Robust.Shared.Containers; +using Robust.Shared.Prototypes; +using Content.Shared.Tools; +using Robust.Shared.GameStates; +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.Storage.Components +{ + /// + /// Logic for a secret slot stash, like plant pot or toilet cistern. + /// Unlike it doesn't have interaction logic or verbs. + /// Other classes like should implement it. + /// + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] + [Access(typeof(SecretStashSystem))] + public sealed partial class SecretStashComponent : Component + { + /// + /// Max item size that can be fitted into secret stash. + /// + [DataField("maxItemSize")] + public ProtoId MaxItemSize = "Small"; + + /// + /// If stash has way to open then this will switch between open and closed. + /// + [DataField, AutoNetworkedField] + public bool ToggleOpen; + + /// + /// Prying the door. + /// + [DataField] + public float PryDoorTime = 1f; + + [DataField] + public ProtoId PryingQuality = "Prying"; + + /// + /// Is stash openable?. + /// + [DataField, AutoNetworkedField] + public bool OpenableStash = false; + + /// + /// IC secret stash name. For example "the toilet cistern". + /// If empty string, will replace it with entity name in init. + /// + [DataField] + public string SecretPartName { get; set; } = ""; + + [DataField, AutoNetworkedField] + public string ExamineStash = "comp-secret-stash-on-examine-found-hidden-item"; + + /// + /// Container used to keep secret stash item. + /// + [ViewVariables] + public ContainerSlot ItemContainer = default!; + + } + + /// + /// Simple pry event for prying open a stash door. + /// + [Serializable, NetSerializable] + public sealed partial class StashPryDoAfterEvent : SimpleDoAfterEvent + { + } + + /// + /// Visualizers for handling stash open closed state if stash has door. + /// + [Serializable, NetSerializable] + public enum StashVisuals : byte + { + DoorVisualState, + } + + [Serializable, NetSerializable] + public enum DoorVisualState : byte + { + DoorOpen, + DoorClosed + } +} diff --git a/Content.Server/Storage/EntitySystems/SecretStashSystem.cs b/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs similarity index 55% rename from Content.Server/Storage/EntitySystems/SecretStashSystem.cs rename to Content.Shared/Storage/EntitySystems/SecretStashSystem.cs index 49be0a2f880..9aee1b982e0 100644 --- a/Content.Server/Storage/EntitySystems/SecretStashSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs @@ -1,25 +1,37 @@ -using Content.Server.Popups; -using Content.Server.Storage.Components; +using Content.Shared.Popups; +using Content.Shared.Storage.Components; using Content.Shared.Destructible; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Item; using Robust.Shared.Containers; +using Content.Shared.Interaction; +using Content.Shared.Tools.Systems; +using Content.Shared.Examine; -namespace Content.Server.Storage.EntitySystems +namespace Content.Shared.Storage.EntitySystems { + /// + /// Secret Stash allows an item to be hidden within. + /// public sealed class SecretStashSystem : EntitySystem { - [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedItemSystem _item = default!; + [Dependency] private readonly SharedToolSystem _tool = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnDestroyed); + SubscribeLocalEvent(OnSecretStashPried); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnInteractHand); + SubscribeLocalEvent(OnExamine); } private void OnInit(EntityUid uid, SecretStashComponent component, ComponentInit args) @@ -42,6 +54,73 @@ public bool HasItemInside(EntityUid uid, SecretStashComponent? component = null) return component.ItemContainer.ContainedEntity != null; } + private void OnInteractUsing(EntityUid uid, SecretStashComponent component, InteractUsingEvent args) + { + if (args.Handled) + return; + + if (!component.OpenableStash) + return; + + // is player trying place or lift off cistern lid? + if (_tool.UseTool(args.Used, args.User, uid, component.PryDoorTime, component.PryingQuality, new StashPryDoAfterEvent())) + args.Handled = true; + // maybe player is trying to hide something inside cistern? + else if (component.ToggleOpen) + { + TryHideItem(uid, args.User, args.Used); + args.Handled = true; + } + } + + private void OnInteractHand(EntityUid uid, SecretStashComponent component, InteractHandEvent args) + { + if (args.Handled) + return; + + if (!component.OpenableStash) + return; + + // trying to get something from stash? + if (component.ToggleOpen) + { + var gotItem = TryGetItem(uid, args.User); + if (gotItem) + { + args.Handled = true; + return; + } + } + args.Handled = true; + } + + private void OnSecretStashPried(EntityUid uid, SecretStashComponent component, StashPryDoAfterEvent args) + { + if (args.Cancelled) + return; + + ToggleOpen(uid, component); + } + + public void ToggleOpen(EntityUid uid, SecretStashComponent? component = null, MetaDataComponent? meta = null) + { + if (!Resolve(uid, ref component)) + return; + + component.ToggleOpen = !component.ToggleOpen; + + UpdateAppearance(uid, component); + Dirty(uid, component, meta); + } + + private void UpdateAppearance(EntityUid uid, SecretStashComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + _appearance.SetData(uid, StashVisuals.DoorVisualState, component.ToggleOpen ? DoorVisualState.DoorOpen : DoorVisualState.DoorClosed); + } + /// /// Tries to hide item inside secret stash from hands of user. /// @@ -62,7 +141,7 @@ public bool TryHideItem(EntityUid uid, EntityUid userUid, EntityUid itemToHideUi if (container.ContainedEntity != null) { var msg = Loc.GetString("comp-secret-stash-action-hide-container-not-empty"); - _popupSystem.PopupEntity(msg, uid, userUid); + _popupSystem.PopupClient(msg, uid, userUid); return false; } @@ -71,7 +150,7 @@ public bool TryHideItem(EntityUid uid, EntityUid userUid, EntityUid itemToHideUi { var msg = Loc.GetString("comp-secret-stash-action-hide-item-too-big", ("item", itemToHideUid), ("stash", GetSecretPartName(uid, component))); - _popupSystem.PopupEntity(msg, uid, userUid); + _popupSystem.PopupClient(msg, uid, userUid); return false; } @@ -84,7 +163,7 @@ public bool TryHideItem(EntityUid uid, EntityUid userUid, EntityUid itemToHideUi // all done, show success message var successMsg = Loc.GetString("comp-secret-stash-action-hide-success", ("item", itemToHideUid), ("this", GetSecretPartName(uid, component))); - _popupSystem.PopupEntity(successMsg, uid, userUid); + _popupSystem.PopupClient(successMsg, uid, userUid); return true; } @@ -113,11 +192,23 @@ public bool TryGetItem(EntityUid uid, EntityUid userUid, SecretStashComponent? c // show success message var successMsg = Loc.GetString("comp-secret-stash-action-get-item-found-something", ("stash", GetSecretPartName(uid, component))); - _popupSystem.PopupEntity(successMsg, uid, userUid); + _popupSystem.PopupClient(successMsg, uid, userUid); return true; } + private void OnExamine(EntityUid uid, SecretStashComponent component, ExaminedEvent args) + { + if (args.IsInDetailsRange && component.ToggleOpen) + { + if (HasItemInside(uid)) + { + var msg = Loc.GetString(component.ExamineStash); + args.PushMarkup(msg); + } + } + } + private string GetSecretPartName(EntityUid uid, SecretStashComponent stash) { if (stash.SecretPartName != "") diff --git a/Content.Shared/Toilet/Components/ToiletComponent.cs b/Content.Shared/Toilet/Components/ToiletComponent.cs new file mode 100644 index 00000000000..5de74e08f6e --- /dev/null +++ b/Content.Shared/Toilet/Components/ToiletComponent.cs @@ -0,0 +1,40 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Toilet.Components +{ + /// + /// Toilets that can be flushed, seats toggled up and down, items hidden in cistern. + /// + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] + public sealed partial class ToiletComponent : Component + { + /// + /// Toggles seat state. + /// + [DataField, AutoNetworkedField] + public bool ToggleSeat; + + + /// + /// Sound to play when toggling toilet seat. + /// + [DataField] + public SoundSpecifier SeatSound = new SoundPathSpecifier("/Audio/Effects/toilet_seat_down.ogg"); + } + + [Serializable, NetSerializable] + public enum ToiletVisuals : byte + { + SeatVisualState, + } + + [Serializable, NetSerializable] + public enum SeatVisualState : byte + { + SeatUp, + SeatDown + } +} + diff --git a/Content.Shared/Toilet/Systems/SharedToiletSystem.cs b/Content.Shared/Toilet/Systems/SharedToiletSystem.cs new file mode 100644 index 00000000000..87df69e88da --- /dev/null +++ b/Content.Shared/Toilet/Systems/SharedToiletSystem.cs @@ -0,0 +1,109 @@ +using Content.Shared.Buckle.Components; +using Content.Shared.Interaction; +using Content.Shared.Verbs; +using Content.Shared.Plunger.Components; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Random; +using Robust.Shared.Utility; +using Content.Shared.Toilet.Components; + +namespace Content.Shared.Toilet.Systems +{ + /// + /// Handles sprite changes for both toilet seat up and down as well as for lid open and closed. Handles interactions with hidden stash + /// + + public abstract class SharedToiletSystem : EntitySystem + { + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent>(OnToggleSeatVerb); + SubscribeLocalEvent(OnActivateInWorld); + } + + private void OnMapInit(EntityUid uid, ToiletComponent component, MapInitEvent args) + { + if (_random.Prob(0.5f)) + component.ToggleSeat = true; + + if (_random.Prob(0.3f)) + { + TryComp(uid, out var plunger); + + if (plunger == null) + return; + + plunger.NeedsPlunger = true; + } + + UpdateAppearance(uid); + Dirty(uid, component); + } + + public bool CanToggle(EntityUid uid) + { + return TryComp(uid, out var strap) && strap.BuckledEntities.Count == 0; + } + + private void OnToggleSeatVerb(EntityUid uid, ToiletComponent component, GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess || !CanToggle(uid) || args.Hands == null) + return; + + AlternativeVerb toggleVerb = new() + { + Act = () => ToggleToiletSeat(uid, args.User, component) + }; + + if (component.ToggleSeat) + { + toggleVerb.Text = Loc.GetString("toilet-seat-close"); + toggleVerb.Icon = + new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/close.svg.192dpi.png")); + } + else + { + toggleVerb.Text = Loc.GetString("toilet-seat-open"); + toggleVerb.Icon = + new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/open.svg.192dpi.png")); + } + args.Verbs.Add(toggleVerb); + } + + private void OnActivateInWorld(EntityUid uid, ToiletComponent comp, ActivateInWorldEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + ToggleToiletSeat(uid, args.User, comp); + } + + public void ToggleToiletSeat(EntityUid uid, EntityUid? user = null, ToiletComponent? component = null, MetaDataComponent? meta = null) + { + if (!Resolve(uid, ref component)) + return; + + component.ToggleSeat = !component.ToggleSeat; + + _audio.PlayPredicted(component.SeatSound, uid, uid); + UpdateAppearance(uid, component); + Dirty(uid, component, meta); + } + + private void UpdateAppearance(EntityUid uid, ToiletComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + _appearance.SetData(uid, ToiletVisuals.SeatVisualState, component.ToggleSeat ? SeatVisualState.SeatUp : SeatVisualState.SeatDown); + } + } +} diff --git a/Content.Shared/Toilet/ToiletComponent.cs b/Content.Shared/Toilet/ToiletComponent.cs deleted file mode 100644 index 161bf81c992..00000000000 --- a/Content.Shared/Toilet/ToiletComponent.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Content.Shared.DoAfter; -using Content.Shared.Tools; -using Robust.Shared.Audio; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Toilet -{ - [RegisterComponent] - public sealed partial class ToiletComponent : Component - { - [DataField("pryLidTime")] - public float PryLidTime = 1f; - - [DataField("pryingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] - public string PryingQuality = "Prying"; - - [DataField("toggleSound")] - public SoundSpecifier ToggleSound = new SoundPathSpecifier("/Audio/Effects/toilet_seat_down.ogg"); - - [DataField("lidOpen")] - public bool LidOpen = false; - - [DataField("isSeatUp")] - public bool IsSeatUp = false; - } - - [Serializable, NetSerializable] - public sealed partial class ToiletPryDoAfterEvent : SimpleDoAfterEvent - { - } -} diff --git a/Content.Shared/Toilet/ToiletVisuals.cs b/Content.Shared/Toilet/ToiletVisuals.cs deleted file mode 100644 index c5992bc0be1..00000000000 --- a/Content.Shared/Toilet/ToiletVisuals.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Toilet; - -[Serializable, NetSerializable] -public enum ToiletVisuals -{ - LidOpen, - SeatUp -} diff --git a/Resources/Audio/Effects/Fluids/attributions.yml b/Resources/Audio/Effects/Fluids/attributions.yml index 4e28c992528..aebe3e3c3ff 100644 --- a/Resources/Audio/Effects/Fluids/attributions.yml +++ b/Resources/Audio/Effects/Fluids/attributions.yml @@ -12,3 +12,13 @@ license: "CC0-1.0" copyright: "Created by brittmosel" source: "https://freesound.org/people/brittmosel/sounds/529300/" + +- files: ["splash.ogg"] + license: "CC0-1.0" + copyright: "Created by deadrobotmusic" + source: "https://freesound.org/people/deadrobotmusic/sounds/609953/" + +- files: ["flush.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Created by the_toilet_guy" + source: "https://freesound.org/people/the_toilet_guy/sounds/98770/" diff --git a/Resources/Audio/Effects/Fluids/flush.ogg b/Resources/Audio/Effects/Fluids/flush.ogg new file mode 100644 index 0000000000000000000000000000000000000000..f4ef31c2ab17c405f90f919d5d2449b4eb6ffc7b GIT binary patch literal 132202 zcmb@tby$_n_cyu~6;K3Jq*FjZa?>Co4V%(Uw{&berBS-OOOOrH-KBJQcSv_P`@O;E z`ToA=J?DDgf6jdkbIr_Jvu0L&X67F5Eo*2f2YLwl=h-&+TR8ZQMf8C9fxU&bjb8s2$^?#P3S2O#X3E{;jyWLEyH3Gg4N)*C5a%kOwtt z$d6SClRhp(T&fW3?_9<*oLQvE0iPDQP@7KP>Y8VkWx~w+Xed#ht2_n)R{bDO=8Xlt z%XIw6d~kuxm(aR{EGxARobzddE{AoY}D-pGiQ;okW9BZPaCx@%-fC5@DplTU6W$tU-rBf6|D*rxe7%nsKoPQJgBTMJ6qYdUd$V`|VsX<@gwXzu1Tgre{UJ>lNI2@{LQh!v zo{g0_{M%nXk*Yd7F7e)jrhRzJfG=sf;#Yf0#B zc-jD;@lXPC_M|eif;L2=wruN`zITY5=7R zy`KgJy2a7C|ERxtZ6g~1gxPvBxI5DYiM4lZ9! zu_@pZhr!FoRB7Q+PzVkDFQNdalIn#CJi)sMiBFt$7flv`ivM=pK1KfvqQU=4{Rf1f zUQz;SkeObNg;zmdLCM}sCDv}b(pzJ~ZhqW#emqzU$LD_!tp6Yf1c1it9+OczLE!cz zPZ>efzYhKvIaXNh;e?&x6p|$r(nEB;hpe(ktb?yaKCw#6z1A3fZ8waqFUPJi%%(rA zpg(G_KUtx#R_Urz@eg4B!e)Nl?!S<82az{iAE~EI77%KA}CZM-a5t@ z`X^+^r)*@WKmFg3V-%1T7m(!_u;v#+?H^<8pO90XZrW9_Rr7yM|3QwJH3g6WI1|5 z!qwGi!{4-MqjM|B(EJow89eQrYGB5D7q(szi4Ah-!X*IqrKD8t=PVmBpwA zfZ^D>)bRVk;lSYK6?cP|$wQ6rdYI(50fT1)3c|n@V7_V!@cI%z^uvv0(~1`lKWZF13jejTk#?HqJY82RD<)&@#P?Zf+kYgz*gE*UXE)6 z^tca} z4II}7U9b%(K|1eBG(=u6@BD%Zp`ih`<&O)%UjErLvtlLNb!?i{V z_`oX+8pO3m=mmuaU_t}HYe}9TA&_XO6nOQn%_JKN0k_?^W$OpswP6C1%Ym&w$rDqM zhNgTB=xYm%28T~@1AT!Z0ehHdPpKctS=8&QPKQ5~9%}6Glzm z$lvcn77MTxnhqM+C&>nr8715XO%R#P8VH08J*c2D8C(h?PMLwt1;iPYnI;YX5&D)g zLtd2z1lk77yZuX$npKYy6ow4!UKv=xp~fc)Fhd)m5CpKYZV(XDK-*dr6i_+N2FM73 z{Q@}m0M;Ya?E%bX69U)*XluVCtR?CG-AzzIpm&zQ=3Deh&*oeBcL1IbLD|4A0fyY$ z72g{d;6{P7ii1~yNkg*#R->SQt2C5%R5HLjcT_B7r`51iwjq z74+!!osKAgtAn)f!#@o2BkJD3(Te!+B+r+35e+O*lyD&O=_`7;XlSAVRp{+s0#zVL z^NN9$3zl7uts6`PtY!i9ZTL1jfN21IfcOL!!3lt6EJ29ojs;Lu9q0xSddt`D3<8w$ ztF!K;jH%n~@!m-(Urllb%n--J3Hm@e!PyKMQdu;zytl&)2qa1aXn_8Yr=tJD^snRI z0f@g3iMy)a9e!>95(GQ`i{LeI2kd*oM?o4m&;$A2>119XA^#FyLa5#I zgyP@+){x>AcVdgTP?77 zRTYMumI17wcL7#V?->Sx<*(rC7ICdj2~g9NPv``(jjKANXO{s2%1_l{yW+v7b;@iYemkv#DGjzypf7uL^pM(BmkeNK!9>Ik+3eNbnL z2;=T98uBBM{e}4zSYMuk{D2KD2v~r;{`y2G;4jqw3(UYv!dp(@0IvMIpl2v2cqvon zFdgGz>cES{aI#n^IP#T1=*Y*w=+C~2mYz5s_Zu4UmV@aBSi~j-Gipp{r&=DuWm3Er zMfNSJEJ?BxO+ZE!Mz7aFO$DyNV)nu+n^N5XmUErlV%s3FM0RToWDPjKgT8@4NH3UQ zpZ%nkwx`1N4=`?ZpS)9|U^Wg>aB zKK5||bqq|1*H(EjAgK?uY9I@u2x5LyCj9UKm6LPPhiY!$iy-N{oc!k9ICt;W3xE|S zL2uII&q1g!@W^PuTmllGfaMr?NI~boBBhOX zsPZIqvxnc>B8Fz9)rl>De=*C`)=b6XtpO zFU)`X8)f5MdQFT zEDTIsWZ~ILisrNx3ECKcCXqYv$(7@;;X`>JHDIyAeqg1=;DyuIlxc8yJ9+%E*-S$| z`y%;9Mqpx;M8WG3=(?W0AT1?1*5=o#17CCqp3f$)5?0~-t@&+F8KUHML1y>l{+5B& z&bG+QgwORC_8S@c{bz79eY4sFf0>j}@`)TV6 zBNL2ecm)oIP>8kUTk}UOETFun?vRf=1+l7*1Y?Y9Bq^)mxLiCxEwtX-hb%croN{+t zaW9>+5asZ3?ZLKiIJD3N4fzsHBO4J1YwH1Tk1!WmRy@@&@in{KUs0(H6jp@FSOn2s*@%=@~Q6VfgL*M2bjJcV*3yp{&LRp2tQWIvz zm9;Mgb7wBKj!#TC2!wvt2mguJH)iio#A&!Ke$gtYNp4=6k{J7N8&)-AR68oh`ZR8-NLw3%v3)bFXlj4s#=&+9*?hEJT&}N;(21^ z6+K2aS6j9wx&`}Mx=C#QPYap`QZ+RtaMrNgkd?J^5Lze+-tKCv%oi#idt3X6L&Dg# z&YS7l%Rihxrr&DbW3|~EC%tW{vT^3mnX4fxJ!0B5mL@M@BOIS0tR*>GTpFyT>zAGw zdi)Zzrr-DI@cA|dzukjA^*WR!-~O!GQAK{U!}_=H`B=-N33XAI7pHV~m?nk~6AhIN z$TbNH1}%OYI=w(dt3hA*wRBfMkXo=O#guxdH(qv%&7u?HpwKMq!8vpF9(m3a`t3y7 zFyEO~P@kEp$$%4L6Yu}s`x`m=ICn+CD-1Gm`KqNkS)brAA29! zx|n0sVx`zqO|t(D)uM~8iVsm%}{m2>CZ;Q!b%7x=DbH% zLOPKEEBD0-g9*diAa2$T4hI4iSEI(CtJ5Ffo|hNdX{yshkrH*R7TBE5`kjyF*18K7 z5i7SDh30Z3J4cq22Kr9=g}t`X0{m#P%P{^id)|)zn>y8LE9HW=uD(AbG$K`TdG1sY zl%q&P9Wmn_!rqrst0@syNh!Or&UW1d?lunM&T$ttnB?tAJ~OZS>Bh$4qF)f!O>_4B zdBi=jLl*=3n>&G%oHj){yC&`Bne)z7B&Ty8aC#S(T8k@EXz{y&85cL zOcTZzi15r^=32916~=&A=NNj-OHXs3N!l4-#J11P-fV9`4&IQEpUmKFGv_c?)`Utc zVFYa$m*`?5haz@MstMer6?QNfS<2WfpA3!t;IlZqs+M$YvqN?kH1H$%aa@e0wtcPA zZQ|BAIFr;`>$tTQ#M4i=BmV~ZHqyQ|~<48h^+6{njFU)wWx%jK%APqhwO zL%&>9_BYPn;Earp7T2b@It)fj7$U|kL|u<3`z6hCLQ@eXIw-86mW{sWx9Jf%yB+t}x+U0^~RC7fH#x`3#a!C1+Xh6$5*_f=n z(k}Kg(REl;_m$eP{v=n$MexPw0PE1p9ZIjw6hUkso6Eo}uE7IwhUX`nvRlC1YhD)Dlzs(x8M z2liwnTW(`A*}kL2&>uFrM=;WoPn|{N%zjIW&0OnzN61p$Dgn>!n~Ko3k6xVxPKwF%-EV41X=bh6M#FTFl9 zmPN~L!@ZPmr+0G;{f%|;kZD0n=UdIX#=Ew<|n6c-&*L)J*zTMS+3MGgF&RSpBo^QjOnW6aT-RAJrwUbp6m^R&p%c^S~x zQ*F9gPq|vE>+-Dk%trUbCQq#EO&dCcwIomdZyOt_ylVBubiL-TEqVe(v6Dw@9a&-v zPG=eB+2mGxC#0cLS+nef*q+7wwtL#DY-=j&v%0qlWk4$BnO5~6Iali!0Dj_OrDHeSk=aEWop}%K?^F&ZCNu4y0%Nu zK&kTEQl718ox+@5N&UJsB0p6b8@XVrUmA?Ht}z%5I!QG#T#4-TP9LQ}Poy@@Bl90( zpU<8oPe%kdXxExC5|Eh5=GfJ;Rc~=$3=mVeYBH{ozNflcW`>lv^>W$@=z3M|=AKfU=DlTOg z$1^wAb6x4%)mt`ej@q``-c$_=cp&Aa&5oq6Kq+Athrt>~(^ko4fspDFV&5a0+s)(9(Cc~^^a=%SevJi828^%6#}u5VSWGKK~-uU#A24X+eef6HzOoRx1h;6#oO zlkr-qI+a_`JSDiY?fqKNuR=Z?)^APId1Y#TYTjtA5zQZBa^+g)JxCxME>4O(v zbLdsLiY0$9D-qD#xO~r5Z^Rj+lPgp&bY5GRNAKF}FCV)xp66pMRG~U_6dR2K1$8{6 zxU8r*l%85msFOLJ8%0!)4#W5h;D`kcY_scum_$j54ZDorjyJ8}8@KGl+uOBNgHsY# zjgvi>mnxW$`5De>r(fgJD4I8a_wR>F|&6jx6950RM5E7)}}guI8kd zUdSSfz)VKN{B<1e62+11?T>_-0t4KTbXqbL7m8FHEueB&xV}RK$-3 z*p!jXM|l!VE%0#Chvu}?XRQq0SXLIga0Z@s5z?#I=F&Tl)e__fQa zQyt5SnEpK137M9DSFk z3;X6x!vzWh>frptwE08Dc^~#S8C~GY_TN(uUmq{!@mw7LsEFJRt+|odUPt1NjWcQ- z(9$Ub4rdBHtel8av}a;BI=Z@QM1H?Xw`Oii6>JA4q9;EI<@#i46^>3*bUfT4>f0-? zZ%wEtK5b+^kPfI;mLR!B{c7DltI|6%DIVT95*WVqJUwmDvnU=3tNxpNW}Otk z_oCg%4=(bWD!Ug~&8W4VHvO>#Ym5%YCOTbHIvjBJp(K)_#)Q4;x7?rV3fow*X#%;k z*0%a%v0;^SwFkfFXSyYgcaB$M5<{^$)?)qY-H6E(hl{Q~FY+KE zL~HrP9NaBgJQB%fyZV}hs=aD$P}Ux8r=D`!(H|^bOysz1F^c8E_PnJi$fK&MiGv8N zqvNj2U`PA+7Yl6C-Rzepx$lkG#z><`(~1xu93<4jYOenLF8^%C1WSD4_Ahka+ zYyL<|rYV1RdTaJkKHLMcR!yS#L$TD>x^!;NaQ0{@zFp8psx~l$2{$DF_#`=4tFpIJ za!7mVr)(^?RBVbiLaXvon2=(6^=39vR^3VuIBT86isTQHKG&|Nh8ySVZDZll!ieRU zKtolNgspwEiJ&)=4{}j2_F+*~THZG=6(tEe2!`2I+20-`BKgNxSM6=XcE1>hOrAf? z?b}(*t!#Q7*hxP-TZ{fJP9O34CpKF6a>{#xLXzeBrET}R(%1R5Z5O%Y74nR1rHM^L zC6P#KpD5~@s&9o1t&Q5p)RMDt3OBibl+G7+=R~@QKmC5Kx<#8_67SWCiTvB5ERRbHDvBX8QQkJuGan?#?j#W8m-POYyl>xG!+4(_GWZ}AZC9}fY;1qnFTG&h9 za79x)S%RoaV6?3yMBlaI&f4jxc&~3ZWbchT+PqnEu>)sO>^U~K z8Qt%JTx1id9w}XI!fOdeDIe5LBL@m5Fb$#!&%Rn9JXQ`Xd*}DDP({^w1!r0t^w0GV z7>HHixx4tP&hJqAwuXknqW=BmnD`laH#ESuTjhmR#kJweL8o`yxGDlVIGw*AN@_am;`QJl8OWyHaki6%+4Xb{%a(T-5R0 zCYArT`^qDN>`7K_muRHrRsTWVc)%8xbgOQ}in;6$;ip@ySBftJQe!-H0j-icG7J*Y z$#(0bFBsd0#;z=GY}Fg<`OZ~B9So~U2H&i^tir7}9wfFOwi@Zk%&RNoz>sKkb(+~( z@qQ2(B;^d4$js>}=ms}NL`nOf!}^F4rdZx2gyj-DQBFU5$}f$_6KQ`XmqPvkABXnW z!|++qK4WYwe)WD-tey5j_wr}gTN7Yi3M=HWZf2XBk+XIkEbZVU=<4rx(3<2N6O%qG zmFr)W<(`^KCt2;dxuMj&z3sU0?&q$At$}C5hU9%P2K%NN*S2)~cU*7-nMvI1!?#Uq zbJlkmn^UTlxt}s-J_{ujkVW^gQ@=WQida|_R_GUshvk;IWpAyt;8F>oYnx?yp0>nw zJe=*~p!aNUs!|n}>)Ph~TC>}Ni_?2zdHBJT*!rUkt88#S>0tk^4SUYfch-Dy7Pe?Y z$}*Uqq*z3-F}15`6HoL>E33GLpPYpbR{I23>0CwQ&kw`)C&|gFMDcUn@n!1+@z29^ zMm{+L-!YI3O)}4N1j5oE#8*cwcZZ^0%wBVwL)?XYQ&NLUW8=tA*;4IpjkbwVvC|$a zx*hj@eJ{f4{=%M0-E+&I>iS4xN1#i_p8PgjJ$Lo&@>Vamkn^A;M%Z>f&#-p3^OLgL z@HLbSZ<`_rCVxekva)w(R9TJOZ&)U%wnA_*lXF2XN6OQxl3RtRe0FF~_Co!UIU|OK zxd`!K;z%J*CP^ifL58$_&fhRP%Qr69zB*EkQ>#_;6$c-!QEy#MEI)}$^t7{&e)>7S zS<1(v*ZTX9O>zz@8uJ+Oi{`6GyD%N}ou@GjMGarezbvZ^+e7rPJE@<$kg^jJo_oDv zB+{*xEN%+bsSn0Su-Hl3Uehrgc5YS+O>?D4WUtBZy2r6-@ExLbiWm!$DifqXV=4U8 z6bw3Zx0&B$JCbAj+Wpab#GA0aW5||QIFS#BqLPTvjh$7Fg}0# z;{5tdsl_#=MfNjYwhM+BvR-GI0)(ot{};VjAI6rLq5Zf*LUFx@qUYk)(UsM0A8_HKfCk6*aOH11y%qnU9K0gGx;i`Rqm;eMbuN8P| zkc-_caUD_jYRSCp0@r{>Q6lQAkLQW6;$z3OT&Q({(xc(?qn(i;J;rZdpWCNmIBSlT z4l|S=lCIw7g!P4>~$G^DVp6yJnIe zNzZx>8GdvAq3m7p4lL_}u;e@b z(&f_ZnkI{*{s!8K(&>64gT9e`OhjqX+P-m0{+Tfxt;jGyqcOqPQM7K;uX4&lRG4uy zXiqXI9sGK&zpX0K0L6jlOYnh2mlXa& zOS?x@_K4Auyz4zqVg{0MIZCMMpb>KUOR#7^Uzm6C1sfvJxqGwa>eNmz+!rxE*6=~* zbpOiyiZ_XanJu`iZbgwGDB|KLvZ2+j0b4jqO2JEu@f!pu|ISMkoE~fzN8z5v&Jkw! z^6RTNEIJ5!+6K8~)0xQLs};7K^zG$|9I8RN=ZJ|&e)mTf>Q-Y|gDkzg$xSl|1O=;6 zggnj`*@~0muvJ^v6orMnEBj{RtJ^2fEUzVar(CBNb^Egluk>#^9?GPlt+b$gjZylp z+eHgjNF+;Hyx{2i2n&wp-TuDV%hJAmtYku4$XJERxbkR*|I-}dmLy%A^QOR4_KSf| zR5qoV)QL2Gir5&L%P+wnL7KQ~VGngmb>T?!IN)RX{_iBo#v!%%{B|NOv9>Nc=*y+I z+~xFR#Z1}yb@!&=z%fWijS_~hs2L;yrdq9&)Luh^wdrwn8;hFl)eR9;etioN=P1L zSC%{g5#t^`@UR^$#$9OW&l{9IU2W>Ax>Yo7n#eR~IqZoW`VqfHiLk7QjN4O`^*p$- zrl=U47nza3J|WDOaFEXQUnMo_I1qC9OHad8s7H8NtHAjmArM2qZhfMD z%+7~P%sdv9S=e&VFN>{dV%Y@VDg=z)sAnwOA6bk%Kd?bPDAV8Rr9uGSsdM91{e<(` zS05KcZMn%nQrs`8V_!qi7@vq>K3v96)TMHn&$UYI|;uOT^E4bH%6cls89rvWYafB-UNq80FKWeTe(@O%}&(`bG2;i`R$I#Tq2IQhc?z7q6X1uH=#Ff6O z!y7rZ6&xNm2ceRnThqCDgQq@5QL`5%CGXrbbzdb?k!uv{MLkY&4-8Q3F>sgu2rp-kgY*$a5XsPojIbT|b@UGQ9{MRHG z!p`ycdC}3ZcdERx_R8yJEIfII7$!?gn7fV3GWeaJk{+Ll^Ec!mo;~!doSi!UoRmBn zKe~2u+Rtkn$>Aw0E71QLdcb_pQBqS?UI3?ct;Meda@WGQI zBwGVe9+tg#ZrzdnAO@wGKM{8q|Yh(L^Iy=?N;%#E6dV|#( zDsOfT*{Xt5H;L!mVaFeHwAk0dc3sqI%M@zmXq z?3?CYTk}s8tDyLK@t0mw?EG=R40yJ-Jvg02d#*}Y_~bI?4OiV4i;p%rpWFFh zL*`v`^+#{72l8UKTIWgPCor17YWR#t%tt1^Dj|PwL~Hcb=DAWxB1U)Tb1N5~oad}U zq@z)0$Wq(0k=%>t&evw?Lul>!A%i5XKmOp%^)AlQjSxF+$H{AoDSMW5RfR4XfI`a?}hbdr*;0>^bjSl`Hw4Cn?Vu789H}6vOLayjnG`WMZHfZkyd}%l8jf0h zJG!hbc63q8?z_^9L-X#}@?ngxd{1%d#_xl%*|$$K5#v`-J&x^`!cjK-;CAbm?VUIU}@tx5vWKX5DH*DmFP4aHEG))%GHECq>M?%}W z8&8F;&JsDgFJr9ojZu%~MyJrNbBu38ArG`H z4qg7ZHn(!Qwf1v|?3OBxn6L%teRQiJOA42LYd`9*F+ot7S2oM3YhsBzzD2z}gFS(# zWw^`MvtOaIXcFV=iea(Wzq6qI#r;>ZuU|LvbXMPyw-5gh_V^EzNUFzss{HwqWBGV8 zF*a74y$>S&9E)Y50sPw2PE?H;<)O z_Yxi7(gyA=NK{%SD5losAMH`SNYv9dfmtFD>Dn6FFqny{fw8u>j=rG@ ztbvn&>nRN{XkgYXDcy!tH2KYpq0;=1gyoB!@igk1aXwEAowFfju*0%N3zKm56k)C5 z7jaDp531R>!JR=q3Q3%{t@3<<>CH^>Qd>2pU2IXGk=>M2_5A)eI<(+JFN1-}cezhQ z*wv_}I;O0CXSIjA_9n5h_LYO-D=P!;_NE5UiGq8^*G?>>@f}8dp7btx>J>2s(TiqQ z+~fv0eoi{c_$9uNJFzVfWl-ibeZe-KeiI$9>ba_#^Xw&?PFZo_?Uude%qXAZ(D;;e z{Rkod5jbgQr~G&NOix;lYLcV zcF$tUubED3>gqRUC57TbD$-^mzRPSgK$dttQVs4PO}zT za9@tf%Ap79(kt|a+HR0!IjmO=oILegUq;jm4%_-x*ZOvUYSaZPJlshC<)B72+Pcp{ z_xn#rd!&{r@${TPDiPzc!X6GrqlQBAqPdDRh>PwSdS>42h@~;F$5{=s7jn$BUie0LQwj5IM;b`XtIBgXw0da|M^iyszXLkbkZ zNzd#q@@;LC1D_M+pXG(0O`YkG?>%-q8~t@`mz=lJR8G|{#!B<7CQW^mFD{+w`JeSD)UF|6Gt5)`ApQWmi9P!tvc9m)uiQ0+=*kq+ zjyTQ|2?W9IIYiZlYvg;s3@W>%%L-5+;fK zI8!Op5+P=1mE0!fo#f+cSu2o<7bcd<33aTl?wX}Nrxl#vJ3W&Z=TfJCShG6zYNBo> zeH>ReL+1M8WM~p7%cfxiBUi!3bHg_leP~D00u~f|Ke5hKr=S^$lD1|13>iJgZK8fs zDoIKo-Gy2^0(*_b*`2lj&&Uwu!fGz|Al2I;)C;b2vXM9H_Xnqc>k*PRCt?QXL6}Fo z4I%FMljZ=yxwRfEa(pdSvuiUn1!bBF%O^&@Hb0Erw=0etlGuh~5f%0RRGhjL# z&5PZ_ZqhEYsN;n*&x-vESiGVB#h|n;Rl6#MRe%c#{WNI-z7Yd!E&ud*p-nszW;tpy zCtZlkg5H{VV}j+oo8RXBcz@2FzdfXLuN$J*Hel_DnH<|W%m(o?F_m-^mVK)t2 z^``I7i+8*^UoEfATBxZ!yD?h&M`wGj#w#2stLR!`9D#!PTPZh-Q)iLO9dYXBdt#1j zvG$$s3q7kB<9%`6-Ww-E+p^2`++UEV-!PsfmZeIEhD>1(shORWkhfIS+#1?&788%B zvz#4^<2u9m&v{EK`iY_>%w5=|tiNBadaA5%?wLZK_h;{wAQAW#l6<0^PmKG5G08~~ z*!s&fwzuvoPBgH&4LxzOef+fE=B0C)gc}Kwb`n$m#yd@0y1s^I@jt_Uz_l!%@5iOT zA=6DDM?b2I9{mVnZBSM~6{3cS=qf9b^lrWnu#yfQSCnQ*Eq}|8I1u@AxsokC_ilr4 zl4`HGoMgPq(&2n#nx1msJ_qht%Vk+gCU)3wmCdu+d(|>@qOX?4F;pM_SxeWKKAyJ6 zat`??>v}fQpYpOt-!gQeHD`sDCrl--T;~%(1ZQ8D|6FC9o|%P^1UJ;OWOeKI$vafY z%D}sjm0rz%GsK~kQ^FJ;KbUcKsNY4T8L>(159a??A#o=d(fH_G8wj@@s#2e{tF&

SqLegnZmTa&-8*NBFjU*LS3UT+syTcSwI-&wn`B-2uBBHKu4qy~BJnCOx%Vnp z@9Hem_cbHq0Ey&)hM24O*#0qZo|C!UaRG6GIjcWOlHkL5wUi^V#oY)3XgredfD<-U zJS6C`{LnijZ0d|ZsfaDa?fljF^-1qsa7yjPFV}H>WaomTT4|_d&7ADmVxU#;kj`h* ztOyqqKeeH6m1ge|0)_&?j}Lv7QbL`P-SlTuIx3@vvNgB`#MX3RGXt#1&=yDQy%5!Y`A@d%K0MBR{u| zU?!R5kp50G)h~622;>))|Lbd9%B-uWw<1(^GpA3;d8JMJAN9iu#7= z#>Q06zA;tw>S%O*o9=!qGxOAj2e^yN{=tcQQ4UN-p!7b?I-q!}|X34zF zlenkrWYbq9?(3QOj(t4ihEm_c*<$k@V+z(BPMAyPE@#JPQWFXkmXJ?p1c(E2-%LtH zTDkZWli?1BC&fL_FDkBm{q)RVvOkqEBTw^c&zLpoa%aNPE^KfU?oR-7JLXbn_4Gy- zBlHAwfwOX=-0V4;UzEP3TBEsv^u3yF!x=7J>>P;b7_u(Kl5Gp}Dd4a@T@c>s)t(Sa zMnknAFC2;EkysCwT9V7LNm=D_ba*JE^gk8woZkr zF#j)4^4My=be&0-dN-_@w#j9cIA8b23slTqKT3c%6zSxr{ zSrgGeo>j@n4=hQM6?(h33z0!R1}8@Ojt=O4V|M3eGb|r9*Vl=h9mEFHmj31%1-`lF zu`8}JfB7Jo@!dgn|8I(^C7Vj%^9PCR=(2T9Xr4Q^>60k2 zlN*j)^`G9<@SJ)K>xB;bIK2Wvbmvb5>uN?5o3$q0$__@)IYaT#*=xq^^AfDs%on3? z@!69Yf0SFe70K9g))P88WOXw+OmDe|>YE+D*7PdMHrh_ma2X8bL36#4uoha5^oYy`CAiKkH?f*Icf0IYXKVM5vCPGj>pGYfu`nI}qm8 zD$9`!XD?^%|Cqzyd;;Q``X-0s|Fp~Eq}?IVA=5DmQoB9lBqG{yJz#a@!lAtFcvu*Q zE|v_>Gd#`6kEC^85Pfzn0&(};BS zeAY*aS;h#G+Lc09AbuOyaJV9eUVP%>D5kcT`DMDgUWxovEawxkbnhT%wTa1G%R-NWbyyZfK+tt7abNGDjcC#ej67^KiP-S^^6J#ui?A7$DoVe^sxvpFz*^|%b zFxU%~@=ikAvERdH&cZ({nJ6q%{c*j)ND0Rr4li!*z0{IIF!+twg5`)A5rR zVr1H3ujg5Js6MirOSHRt(R}L(gE|W%yF-b}RWB2cY~yqG+2P~n&3St&UX|BuS=xf! zY?mp=972r6N$uCdv}mh0hKR!FkOPdIQpNeruZ`P>_-d^#78&`*!fXeNcrWy(N<61Z zl9HMiQe&!}HuNnT?dRwUoab08>n?ay7yYwEcM8{2%F%}_f`jUq+v<|ICEGLJtEL?` zgb$8Vzh<#ZCLAd2o^)3mYTTs`4R?XNXLk`NZojq_<*s-!${bA+(naim>n4$H@=9;@ z+vBdh=C%{We6Z{x(q5IiE40w9?!EJzzH{MhqRPnnI<5rG1b^Ho3}**jCBEaH=CS;f zw1S%sIBT(m<3NMc*Og>^g`{-zG*q^a)^z%_{E)G^I))cd_QVW--gw^ZB|alUrK%A@ z<7o&wsPH(OdUaXoHs;7;y_37VjL1i}*fV1&-|`n3>?#70-1#f_TeO+~+36jEl z;rY?r&%I7hm4kE$-aMLj|H3xDG9O#!O|p$Rh`$`}I$7@Ir{CdLJ*mzz!?uyDN$T<~ z?gKL{+pFbm)5J>iVLPH8Y_7^%Xno(SW)Ys9wwUS|xwhibFd4cmk_@x#OGTF_OHuJD&-h3Pz-ve1B7@6x*p$1DlB$Rkp zx*ylaWgdUe!M|!b!Ft8TTEUdbm8xg4kXOBn?|y7Urhlf@8@pm1Pof0N;whE|DF9z} z<=99KR6+EDta=}e!5nN1-@UEFGo4m(WL3=PshrfL6Fuxwgs#{YH2d?Nn0mN1;<02J z*>atkByiVx{kVR=j<2_j-(?6D+ZfUqSySl!(a#^-KA*YIt!X3JO-&9`KZM$4QMOFS zJG#4*ze~wk8$KASciXx6+-kRY9wyB#?JLsv8&I9c_6MQ zJVPPh7_D1Osy!wxn!G%>#s<3>#PuGdO>XNs44u(Uf!B3}JY#Lb>?GaUN>A3gjQ>#1l z0;}3pYTUzl#0LKMnQFgkPvdlIcVuqk{7)C|T;XrZPmtc&PU$!dd)vPizSPoO|D1zC z*un1_rC2s^@uw2Mm*;HjFgqe=qpsUBUfi-79b@_Ze<(E;xrVkh9YbI-zGeeK1HfxZYq@v~hZ!WCf>$?iy z>c{evxkV`#x}DEEg|J9&(`|JA?l5s&%L_8RvR~S--l656+&XKTio?&ERkfa!X*6A_ zp{;h=miwvYIMbJ}bQ+w}Qa%>-UbH{LX2Ds_*>HD~N>^vWJxFiP=k;6L#LnJ4AB)|p z;KZd)CgvG*ezp022Gu7}r;QZ0mzTQE`Z{#F+VPgxBgHYQ6vV#rSOFYS8u6}2nZfi;fiFDrH8nJ~b&L&J$0C>$b44Ft z8YnbBLl3ET=(j0vGLk)++4-o-j_K=sWjs&ky(aBvhhHxhs-&T^{?ag1|MnI=7WI*QcJy=A{E#o}+8-d_%-qA_e2CQ2(3qyScK6}ebUb}82PGfl z7~QnXtMY4|aOLl{$F67lb_zhDnqE3**I8m_T{k11^ekxvKNx62RbNXw5xJ^M$B!~N z#_y-Dt(;y*IOk(uN)sww2BfBQ^}OeCuoCOn^RSBdRnC&;dMw-&F~X&qN;q|URY~Xm zr3^PPq;s%+@j`Lq6z8NGk0zg;$gBS4oZTwwa;t3!o%*SE%@cYvy!9~>w@*#N2J(`P zh2K8K${no|eJ^`gDu6yxc}&J-Aox9dHcjzsc}6aXbxOxFq02soMNT{cNzc$Zmai{7 zWV9Dm5m(j0yy5-rhO>=is1SAmU2Fxb(W^`8JP$s~#1{_!B19>VIhyua-46%%(!^tf zRlCY&b6C1!n+DqaXx@Wt#WnHj~ae44q=R`sOi>R}3Yr+fPK4PGPAR-<6DhMJa z-OQvzT5@!a?w$!sDhMd4(p{1RCLMzz4TBBI0i(w@#@P1yzVGk4et*L`*K^KwKllB) z9}J`iim=hk z&|Ip=-=aFr;Ppd@$!^qRkjRNP8f|i_#=AuNIg2^iu59yeyLmxqw(3&e*&a&?b=>M} zSUwA25tdR?C1O#&SM{@LAzUxSL&1(VxKL*;p zUu#)j^krvn=RSLLF?Vj(@o>&>(x(?F5)0^(mYv9z0r(m=1flLl^WLG;pL`(nvLTw^ zp|(~FwY*;_y3E%AQ*8|^3qA6S7VdXy0TpC*B~{MWdPP>~SzaRu7?4nE$)ej0Z<=!A znhm<$d}^Z2tKvNAF<}Yv8zzzR9vA{O3lPTPuIW*IR47IJ77~`k{c>mxDNbj$Df1cs zl4>Dq=1jw^RJNnq(U3wWgK?Jr=8LJzyT^y$Eou6}N9~R^?^1*2K9|M=k?OOC=85Yc z4t_n?yA@6nbCqgZeL&<;=cxl;iJB=E6+x07;O8iO>F>8QQJL7Oqc@uY!HZYTCf`gv z$5ipKE&{X8s)B(9gTv`~Oz*W!a3%Iwx;fbJts*%m&Y-z-eZb+T->u$v&;RVSCY>ao zL1Yp$$}J0@D90Fs3=}|ICmoQdgD9uwUvHLk`dNNo(i%9p>3tR_-Bz~Tw?Xr%1WN54 z>_y^Fmemg^G17BU3gl^))M)u{%!Yd%o??#Ww7??M@E?t0AfnBZD{0Gd$BHN;nm-Rn zIlf{eCKsvbpj#jR%c%dyygAcEL|b$^iC3%n!6g}1+kNH>E~f^bQ(mWyRWH7t9Xl;C zZ=f(;>R)z|iJiE!&{5(7SW+Thm%I+f&Hdm7)*cz?@y?3}d_5R=oB7e9&9ckRJI84* zsHJr1cYCRim|s!5ZqJ8)kg-pGsMdz}7dtpT($i+#?A!HABX@5U>4BDNoO)~3%DWJ% zKK*%mEP_PzS(m3mBJtnp;ffOOIhrF57_aafTf`14#MK-pgb55utm9WAxxDN&xP@Ck zpuySRM#0#SGJcx zf#i+8!X!-4c)0J*L%z(>d?%W1zD%I5aU%h4@_mF>f||XQ*usVLrrVbutY0>tUsmhytYj}G z9Zq96vFD_asMzhmEa+AhNO9xiwXn8=E1ZcF|On8nTxY0Iiyo zm=rg7S@&nBAn=o~RKne&m0-IJ`TiRU&>#|vF4f^~D%;C%7x+f#FFwFBe5N_Zs-0iE zH)g{C=u%$_$(L#&-Uq-Z_&787Zl|I^E`^adMn;$CKcaF@giR-Y=)Q}3BJlT(Phs7y z^!;h}B9?<=o_KkBy&Gfx>SE{LJR>~7#Z%&-n3p~`QqzHy z7LfCI?g6~{RC}13?qJtErHG7C)ChMgjPs3{lTF%!yoNM3PVqjejW{@8{TU{Zu+huu zx2LS3JMJoPE_*9Uv)*ol?cSyKN0)mfguixHviP+hw5IHn)s&w%HU>3hOSSiVZI~-z zP6q0fuq#dETbCTe-pY5EXzPS6);LMmguNgAU{hFfpS%$99v6y(zlKAf$4GtNy|D0! zTUM{u>{956(*uuZ?b9!UEGF`I_fbX-XWc}fk^Sn*0Tzz&JWb7}w!RHbzuKM$C;EgLS+#Ty;`PK5Uk{N#;<0F{F?yOn8xNOF+UooDnFWin>Yf2vt zar8a2q&GuGPD-aR`0XO#l3SniH`6y%n=F#d4XE4pk#6a8%RY9+x-H72fzP3v-2~wy zJ~7{V6gskqY)X6|d7#+U7FhQ;9pDF-RN&H&g7DcTcnjSJe&y?g3a%+_{hL&q;)0$^ z{kt^#YU(@);Za+#cef*5gICdh@)sE6v!6qaIU-eG2kHw3Je#UlGkU}+on_E0UpAqv zpHUnAS~THD5->u_rf)P-NbGtx%ov=Rn>{)9CX@2f5hT)f||DD$ACiq`KNLA9qlouNwU3lZGHe9d5M+mvX$buGn$=f*TkawA&2Bl_7fTSR{>Jd zv~On%UR9C9P%ZN5j=Y^?|MHBLUrg!fOEZq`%$T3)ZcR%SEG1Bt1l#;;x7&aA(0%$# z0kK=)=4vVUFa{QAYit*G9To`*wFd!b8@YmVb~;Kl*P0$m?$X=>R74U!-#Z@y&u=R5 z*^|nv(c@545cA#rYbHI;WVcV02chaECs!T)2wgUTtv=I58*mOQe58R2nEs^tK30zX zE?dz)qL=|6d>;tJY8}|$%dg?@&)1UQT*KcXmfi`|5N>dF=8+dG=F$w~FvKU=J>~Tw zxo3Igv!YLE@OSPC2RpI0Z>?0R{WEazGTRrTQQ=>7f+;uiGxr%AAYX z0ccyBDXoWQkgxVNVd}Vg_yNMXLDg$v9GQ4IBtYf({X%1B2(!l`^+{F^CG*PSgf`yN zr^_?~wcfEZ?4)QC0{=aTI}$RryoUG|7_fxJJbrrPaBq#m(6EO2vp5`(MyKENX8oy~ z^~UAT8$?>^E~yY>=iVAq)mBpjq?C;JnQQdRC8%uJ81Ua3n=|;s-$kk7X6^_+mn?3&5E!m{ zM^EvC(GO-_3&bfTSF&{|5sp!1PHf!cY@6bct#wV62%P*pzOg+`ISC@17hozwkJRLw z_7&%{oG%$f^w9mNR;(jnFb*fFbac^?I-v&lnao$l&#q<(b_FKS8HKS}y(r48g zV`nuuJQ*CKMw*cmE~(rOAjq0RvP*`i!u0pnP(HL{!Lz1NKs!P_Z)M(3<0fH07R-ez z=Xl%X%HM*)*EQV^7I`^#vW3xW3gWaVfkQ)m_dm$TKY;s`g7R40tqkKOypOwFvwQn> zxEIMHlSQFGyZofkIIYz262np}3Dnq1+b~7s>7D{68w%APFNa(I7@M4=Zfpmwo3SXG zNMb}()jsf>u)Yf&5xVn-J1hrx> z-ij%?aVshv5ZR8hvD=t@7{uEge5fnpz;HuZYP?Dm@)%Q;f3$Kw=F4PCqF_$vZ#%jIu15qx9peLJ;Vr&iH;0zRdW{UVJ{a&egnIa1|< zSO={`=JI?fOl@oTE!?<%8Q9*$Zscf_?#;T5i7KCU!_orX9dao0c<%&)U~-2!trE3# z_|4>=ZqpM3Hc^^iOY!wKiCwrVb}evlZQ<{U6URn)Uq$}3Fv^zyM#%Id_5CV5QYE~- z|L_Z~otbI%2w|<7p1b^;aD9UU8yiS}BD)axf(qXH8K?hTV2ieEI89mC{VNhyRYeoG zbA9X@YW)$0WZ4k*ot^W7{y^{Htu^!QQhc2?z=e7}$^Bu3`+Nc|R{-$qGXo4S7JuxO zuzD%8JR1pRNW5{)L$1kN>|Jx%!}tdEf^k0tkQcOthUT-}(ipv8rD~TfqJkHa~Aw(&vqc=C6v-A$|BpvSHVe%;nT8^AsV))vwE^dncS}<$>IE;%F1P2k!CEEr@b~ zd#~uY_iloEg8@s5pyXRngS6;g4*nUeM0!W5oTpfaWls4ZPTCj=S)F zqLF}t9)~m4de&9_s;XN8r!$k2|3_@VAN++|YrKXp4?es|-86V6LE4XEUkPHz_a{^G zp>`8%m$B!cUvYKmwy@n0u9arI)b-|)mAg!d6e(fsPMo?gyl^wmY)sMaWi0?4CR=Ql zZv;kLQMUEtQZ(+jj`|WUa}IrcAFG%?O4@tBm)==+Ve5O!ZUfxe8c~Mzk-(!DF+t4l ziDE%iA>Vg;&51&MYCX(XFE)iS%4`6sxv9PplH==^#ghRwJ_>`h-LnRoWqj1(<{fRW zpVWLyt7hNDzTe(=dE{?^k&auIW z4>fFy4F&%UD_nnVSCJ!?n}J!_mnW6ufW$buNP?tU9+Ig1Z&4c6V`3k*H@^W08`=MZ5b{A>_z5ka`}pJ&QbY zc^IP)+*?o?d&GizE1Vh}CU)n%&cfb6Be{q_htObqn!0*#bqkzlUu(S`_OSh1;q#L> zA6n>GxB2MwX9-+a6@2e13!%a=c5~^cbAuC8D-o>oJRQdeHc_A+M#HU@-$H9AR!6M;-^g5#ev&0DwxXCl{7w5&{k7fhIAa2g|(K_;hsw{?h|dzX<(uj=+^iR z>V)2b|M#tp_F|*H=8R0s+jDNql7O`<6&ntXSRicmsrhe&+TO}t)}M5{oMdYM(9X^x zrrTo=Y{k8(5hmz1Oc0A{@D96~G&2XDBbNTu@cZR4G&{=Zyu-NvLL{Cu$93=I%RJfQ zq|d!@Ab9I9&a+Ym!R4>7+c#zJhm*L{+oAgVRu7BNq?38vqwP0Cp$DzdhsnPhBV?On z_WRmTqZi5v>`cnL&|lu}f9K@}$8>!1(KYS#?%(&3PADC@fzPRRkiPLK;Dc*{9 z2AX`Il#K~No+)^z=0m+Y@4L@0favr$t!K-H%|q=^V4T5($F;g$HIi%ye#$8@eQ455QEJHC z{GQBJucB)|x=5YT5X4*o(m9vvrv;NpQkx|7f?aJGC;^4vUp_lJYv{j6x;B+j z{LRM`iDbYeD*b&ftE9jYTRHOi`>y_jmO;e|gSUpMTfUBRxM0|uJ&r5HR`tosQ&^g< zoZd=pa`N=~t@HBIQ83c?;SHhL2ae)BM)5p^dxQI2Z369*nI(X(T-1I^X{DyLViWlA ztA|WXa&oj7qr8j1n8KI0;-o&TK2CpjZwhig zpjOS7W)z6S$9VY0?MB4phJ;Z(F9e@$UN8Cnb`B!OjgayCi2;h~V)v1Hc1FVk!tc4a zj!12iCGEu_gt{x7&d#M1Ov&$4x@J++A2udr&tM_pAt!46rl?c(v-8d0X`gxXnnKo$ zWmCPr*$esOLP8rfo6gzq@YYSyyAUJ?=>A@A0rJrzcr@dd_CV_PVW{mfC&a!9aHluk z&1I~@dH1c&V3f2PcgO3heg8mlkFMP z6>5#r8P+}d>~*aF7?u1iaT)TP6q4j+^SzP#8b>->mN2|=E@30#_#i7{ao%SkH)g>{ zV_Gfb#vZT!swVv_-Y+#S(+9<>TD!|etJ;w=b0a*~d>h{>O3O9UvEzM8A+s&kZ_lMaU4eG9HhDK{bbk9VtgCTIZ?-8c z;-#rkHO=Hu=Qal_Qjb~OY>ps3oZIojMbkh2tIrD?lhH0^vA0^=zrn*t=tL;25*NDd zsVR%@W_EnLXjklYVGaq9G%d3itcYze7l%l|o;v&83Gp7-R^y;kTXl1ff299s)`pup z)=T`^*rlo-8wylFw3ln-AD6rBeZ2OzdiGIXqs@E`e^GIJ1t=Z?mI#`wxLHvF9rQ(P zxL+#K$FkzIbRuf%lxv*#-2#Ux%Tn16HL--gf73*AlG%Jm%t{I8tx%9Hwyee&lg&+ASEPWd9ox&b{h+J)>fG(_ZDw_x)xX)=6}WS z0N@ladXqVP=0zfrh?kFviH6;-9Ba}xXg(>-vWbgUTex2OVrCC zia#ib=!~5dKWGJ3Z@flBbk0Fe8uroe4HmldMDokMv(0=jGc^7>Tds!uwa$SZOM_cL*WopgOx@M_%myy zM`xbW4Ij(#FmitFnWtQq*~zbR8^2lhCRO!c09;|n8z$z9B-npC{<;@Z zD4}%o`_VG)J`FXYB(wQ2FF_hu0eG%yQAJrf8%S5_4rjI|@<-GsIfSS$iz3C$*&k($}k z@>$!{PzB1qEng;ZmzUED&VD zIo6Z%{CqD%=6HJ4>RbIPnzrvL^;lG;1TAo0P`itmx$dz_5uaQIGwnZG;X2qD>VVreeIWlxT6?g*n&gMP|iqgkf_no zlwU0_3er?RzW+MwHf!)pP9e*7H{Xv8A441puacAv_>hrFy8s$HbusROp23sQ<)}J=($jwzv&N?b((CHRG-24_ zGI4=@owdhizS?Z;c{p#3#(V|SlI|Xw$uP8zC^@$wHX+FO;1}&Poy!PelBdef=c(ah zoLBgA|4uI}<|#FhHGjG0kMfJa41}-NeU`{?MlEaX4!TmL_n=COgmy`o? z#WVKSVha+AJ0QiTQ+}~GAt|)j#c&B_zSm!iob1Z!&C6Ky()P5;?)o@qO*H-t4 zj0sa&BHzW%b?gGhC?X+iC|GDp=S0lGOXn@j;#1FR(rR!}HMVc6D4nGdf3vy9K+vpQ z5SCi79No)0RV(mwyIE>$YA(A5a@5*c*j&k6P$3s-UYqC2X3)>N_@KAsow1M$TJpDy z$v5=Es{=@;y_T=R0`3r-8rICkJ626-RI0tA1Ck?SX1_~vU_Zv-ytIfD-A6Gue8ln* z0nVh)5E`rw%eg7+n*Q!|rJ+a19HfJVsokVm; zbbCYg-~D1HfM6V(s#ieGyUQ%*u&M&ig;-+dqI4YgS_GIzw7BPC2bD{i%}oMp*pp>C zXEnN>nav7a#lQq1yA|YvF}fgpq;aR(Hq3Gf(OCw>ec2= zOnuDYlZ6*+!7mB5g^VrkGyD90XxPWMuFai{%#0iN_K=H2d1%*{Bk#THPD3ihh|T$^ z>?YeF8c=Emuu2%zk(YUTLFj9StXjY7gkhZ z7@fkN&2+k|E5p0+nb8S$RYJ~q?o;x3>^lB|JIhMby8=b?= zP%%Kt%F(vkW_DcmUe zkEN$})I)D^;GxWgH?2){`e$Vo0%n_Um-)Ug0AHRTyJV9Kpwb!p72Zd(YZJJ{24IPv zZam9i&QSo7)pCQM@aIZ@#Gum4tvJq@$Ni!7ZDpEYs@27Y%( zyXDk#qXAKkyrs@Ps>(&ZTnWZ>F2}7w7@eiOB_}Ut|53}J!f<8powJ~dQc**d;*s!8 z+z%wg7PVLjb8)xxTbI9Foj}eFv^hP2sUeEP?{zhOwK^A!c6l`TxlK=;y-6Q@`>Lc} zUlvf?R^!G+B>|I)uvFSg!$a5_k<3VEwpt}$oY28+UI(L*f72$kiINMw zq2tTHI<@WvlUgO$-hR^rb*k>Ih!YZp{4Oz~MPS!Rjs&+=s zLc^Ign+gPbt-8`L+fELxUi|Jrd?%txsXyy582jc{4nLI}GTgyW__yzMyily(gJGfu zJ?@okS_i8KYvzHOz5BiQ3wt(1O~LPfoFOLNZkSh6Hq%CV((lAPIYF;;pFkbO-`@_R z(?|G+`adi6L_uew4p4{?xRjk@^_S!-T1-YHd{f^xCWDoerkkxYpqcq0SNH;Aju>G(%`Ny>zkd zzM8(Dc(D)>Vr>GUp$o8FAIJLG7NGeC-jkJTH6qdPBWq~kjRHlb!)^+PenqXc>pfy^ z#L4NF8Pv8d*Y|AX&mU%XZD{%6Z=*fyf1I^D{XQe!yuDjRueM`rdO_bG3Rxn(=88Lt zQ*GF>Zt{yvDJ4v;ehTXp$ew-NY^iyWm3j63uZwnH{9*X=pg~IMJ#zKsluCvYzQu;~ zICa@`if(wR)EX}+80&KgxHl^#9WTQzhkU?bA@cBr(K4XguhMVFvo*r5tTMuWrwOq* zmT>%)Kepk5fis?TWC#Ttf2*Lb9_-3=#eV zk^Fb(J_C^I#(4^22vnX3qP3g8+U?4@DzuvWp2_YsizzRhwi@2OB5x#!+Ejc-D4P0p z3bBAX)s`Q>1BRj^OLFGVn#O8o$Zp3v)SFoR{(Fn3chlq*Sfmd-2YPM()OH$Y`+!<( z;lyLN3MTYdp@KizJU!O!^ezBJwA}NzvWN@6I`2EjpG-P>GapV(8ljG&yqawz$2<1F zvS3ROLXPANfhGU!{DU4g{mKmnx=;-;dN`>Kc5}O^#GRw$k&a@e%~P#=&|!AfczV|H zhu&%JCi&Hfv3Qi5_ZpcB%+1ePplj03RrS0{XNL9;JfJtzrcyVIk2l|pokfuH@;0v> z1QOvdgO4%hYvkvPRep-5P7oge6te>H#vD}p=1W@WY*<^2|hfsDfOXsP~llRh-l<++Z(KL(l)sF{U%7T92Yfix~`%eD(Hpl0zW|h=2 z5`vQ%*JU0{S~)!f4+1#XWJST#9UwJBsald#lL4*2R4u?<*DkZIv3AX%LfsVzTWolK z1r0mW>{nwn^6}*vB7ZFruE)!y3n(#Y?$0*pl3JTCi4IV7&8qj26yUpEZan@Oz#jZJ$-1vJD#|)#? zOd3xV2Xtba_31_>57IZK8=t{(*Q~kFJmG9_ zA1nOQ8v8E2s(LzBDjJ(*VX~bc>;D=eTwr?#KXXq2*ZAYaW6sCr0jcz`TTBf3j*)OF1Rw|MgNtuFl6%l5OQ9nu15yod9e-maEQ z?0oLwgWUEn3m|*$5_89ZV5;nr(WW+zabN&j+uRHbv2zvSpRKK;+1;M{FS#$6;N`#Q z)nPdH(VIq^xUCqE|N9V!|B-S4f~-}byKLl4VzZLgB-aFLYe%7m!~^9HpD~*Pk)H*} z!F~*N`mIxIg%$#QmoTaS3kBWSdILXL}Q^|R5f#XdF8CZO^bDjW-uQ2PJs#2V1DSJ@@!OL%o zyDcZU;*XeYg{+n?)-Rn9RV-v0CE({{->EdnoO6(oVD}MWvkpj<0Shk zabxiBt{t?OZ!ach?@e!T25HG5K|LL@Rhm4G3TkgHcP_9H{t*phz|{0F=6mhT*6r4& z-#sPX{-`VpY)fSC9Ke(`qP$w$IZtT4`F=eA(S5n)H+v`dTqj4*0|b$sRU16t+Qk28 zB)Y-Okl$KJIark^G`kQ_^{An?jNYm4LCI5XMWFLJhM_4 zh=L=T%|}#YXJcm#W8eE>DvIc<6mHpGiJD}6lN9w~lP+~nC7ut$Lui+%QQO5$43_L! zc`TJ|ZwzF+iMZ78lJ-*w7i0apIl9o{d3ud9C?>c;#9vlkixYfx0EqR!@AfzYQ(0w^ zbKAVTH!)|;^IaH6`2)!f=TjDAx_4KC0_{+HQiJU)Qem;kZBa&dW+y2-;*RR-|KliTQB~C)}uD(m6%ysR%kP}nhMEPX z^Ic_^j0hwNEF}kjF>*4w)5}g5ydUU4Xr|uFPTrn;3ebQnb>V+tlqH%LX+bo-%;gV9 zbLzJnG+-2}@2r%)go^vGu@)Z>yTYG{oqmoMg5&ib)y|>prWExI$w^lEC5qLkYBwzD z$Lz|VlI3+O%bL?Fi7>(;`T86)ioBA0wz=N^q^5Hg)VPwz@vw%i8%Oc1(53=n;fc(PH3XSf|bpJjQ##?`X&yYJRD-pfr>w2@a@V|KK@43{x zPQtIjFSEuwy-NdEaj3Jchs9Q^dOaf;W=Y!nUGF>pZhHa|o8vAWC%v)UGDr;d7NFp_=!<7AlY9(t{|7P~e`902wE=w=9t2QIJn*dh zbwSH`*e()j=;J=Rpd~+{Dw+C^c3eVUT~WUgl&lbOWXh+wqYZGsVhuOLd6SrQO^Pu%BV8gGSwldPPr%{Jwa&>{Y#6U z8Kf*@_q8Jx+*%J)R)?ZVW_&xJdl#o#9{Nl8=Bsbsolza%tb)~m$Mk|uPnMl&B%J{D z62F5iNjz#!ji0ih&0@MYK2g=0(4P+IQnMpkmdA6Fm-kZ?{bs|KCG21l8=^kW8e47F zdL>2NgS?!$C1Y&6=RYR;*(m-#kgdS?mI#A$=}#>r!yr-BGWKL)_E&O{N5$|=>=P4B zm9j>`qW&du!)b={0B^DJ#DuXy&#=gQYo$#adyZs2?j;TZS&>qCZtrgrhK4husQBS` z@E1YU>K085JBw6xc$kX1%+(ge^t&OjJNj3Kg{g~|MdM3QzgmPzyQ>CY zj`UItezQ5it)Ptbl)JslL>>;?U|i-jiq-x!?eTsGe*%t6`5OEwLrX0#Xrwqj9M%a4 zampj3IzRYfF+3HrW94cuJdfFr((+nrRd;qnM%~*!Rwl$4t>pK)d8Ybq2IC9DIKL_S zh)~m8`j`CuJ#MGhKKcB-{f|x#cA{^jxEtXtycRZ)`z1&VQ~&nzzDP|*XV=G{qZR0p zp_0WUB&|V2nLgGG#}4GQzr#G7IIx*`XR=aoDHRZeLb$(4i!(oZ{`4$ojzq2iTU5Ty zB6klnP1g3Al`ZM|20Gx28TN{-QEY)Wgez=^H`=73-cbd36@%q!726=h&-dh7ab2Ne zcaO&zX^OQQU=?d9!CK&rL<&O{|8q{QcUN~`2FZ+C-U?3n)kgV>(;7ycXpkqleb3HTXHG!B;VP08x!d5NIUB@i@AwR>bWw-EEN6!~0` zDWxMP)Ui?itens0;MwkX#{)-)m z#pbD3{`MzZez5tzK^7%#-p7`ffVg$IFPM!~G}JLADAmHaqp}Oy2sY0$f%!!7p`}$7j;iUkJ2yAa&F>n_dI$4=1EI^;Vbg^61<22Y} z+dm>i3D{cG*dHVs-J}R<PNp9;#$I{lprj|T&6PPuMr1T^qTg9>_Red@>{3OX1>2dxPhZ)jr-bWvp;9z zugff9VfM!<5OIN&1+XJbt>6H-xk^g^XN$F8jIYVl|0FvO%jo;-s)G)ztZKI>&f4)0 z@EOdxC22SpzU9>zVo95sHjy!pz2Uw;)kux|JuUXS&@F6QJ!5DPN$(Yl_S(B-JJNou z@YBqHLamg`_j-r?S3<+^*u6~nUd}Rj2kw6NTS_WNidR#{a@d+E&~a3BQoA))NI*Jk zy~yU8`$~>JGck&_ME)efMFe_{awqg1ti8!hO3|z3OCv*y*8GECR~&UyS@;kfW>L+! zl1Du5XA-;+{@(W>-TZbMkWvb@wx*G?oGU2R(_(OApmrtdDmZtEmg5?y^>^vI)U$oY zp5-xCl9ks1^e)RroP^*L!?cG1Un1Rt{4{7h!I{+=@*)1A4~G7=%dqNiI^AA#eEFBi zl963<^ev)lDhTa4pcyu6&pBE7Ixw15&A|GY>VqWL$lF~~rt|sv5#4!QJSgl!h-#RO zzRAS^=Qq9Zm-&_0gZ((bLx?Z&HRs0}Tbsi=VY*zZb$q+#T^`Rh`t${Le4)!^mXP}fl3dULBAs^w5o`4{l|yJL_8AH9q|^YI)~ zMvrA{sK!EU=3A(_iuVhBhjY5j1~RyTa1U`I$+77O0sK9GlE>A!?zV{`h6%Gic+ z_O7o-G!viYw#X8PW5d0B%-Wub z|0yF%$4!cvH!m~Lc~iXtJ(n*r&iY-cy9zUWITQIuhyq8PK+;j6bMhh5N<1ZOK*MG| z=R9!)YeM3xebl`Q%n<;K_mk2aE9Tp$w)5siQVI**Vu_LYz%x|&XsJJHaIV4UPjQ?B zeytXk^!w!lc)n~tP!mZdg`~en%-oR@$7I#dvY8-a#wx$;&JdLmLA>Yt{hus?d zNy8sU3$b9&+@c(!A-MKdeD9K-!pu=@r|OmfQ-@kOt*FWBXok4CVWY1@raQRwGI}>2 zlblc8B^PTp_eC!pAI@dFE=MhNh=)GrQdxZ@p`6mOf3|VwRkLya;iAe#vylgu!*QZ_ zswtci5@xxEQ^`KC8e2)4=x`{c>7<5qcEXvLBN2nV{r2$VoF{Rla#fq&aAs}6P;Ybl zGo?>ct$1MXThq**P6=Y-Q~SeA(8@W3$6b4NlKTU2@T*9BXoEq+kKoLiBw%)3M0zsarW zh4U|5ETXw?H&-pim_PFJ*Rx&kWC1I_0=d&-dy}US$CNqs?Jvojm8Idr zhO@i06Z*(5`;=}YgUH#+wk%mbVOX^po!UrJ;N+xxyh{B_>Kv97VHD52DL!B~mt3>i zv`byD|Pp9NSbFz|RSXl5g1`nB%8_`VQe@Ws8LCJ~Z%)Qjjukr3Xlg;F|Mri7I z<$&*8Fv(dZ37zI#*`ILTreS zQ(&=ec6Mo1*kFL|^4_=tIq9aJVoun{@bju`^x~6g4RPZSr7pFOUEqUdl;+u0`G}&? zd`%e-G~1$h=^A&a}k_#Jhk{ACo4mnS4ae6>M(?LIiu=E+;;hY&Dq{ z#sqSMWsL0zzCI39Nr3eUh1nG=E_Gvrd~(4 zh}ol;qNAS;(Ctgnsm*4~bj#IBKPUK!wZW|7oYfD|F}SiSWu+L6rj0Okb8Ud zu6I7O;!1#74tDzXK2cri((|Q|3S&h;3~#}^-I0Ak!0vg7wB52{l={|oEF`)MN%_z6U>CO=%+^jocsa3YIkZ}lFapF-;qHLZ0rN@QXx4~Rtgzq-#ig8er4zzlw;kAH*$V@Ye4Oz+h zBep3t_}E0TL@oWV#^K@UqEKSKfXq&Wrm~${~4oHse;Ac?|IB?RdR4Z zK_X}F;JWxoyMT*~w@3X*aEmCHAKuxNb_;=g6SWk5pgm>7y8FoXlp@Ts0flX@%*5F@jbk7TWacv%&Kk`(5yJvZO#7m z)Q3e&eORp|n>Rl8&$?;L+Tq@-=ZnN3)F5BthX)8I?{Lo#@JqOrBdBm!kc6cBi5GD5 zVeIv=hOppdoBI6k;7ewczLCO0tRRU9iuS_2h$&M-q^y80e%3JRN$mMdyUm(iK{52K zS*qbSMJusA+!|^Vst5)TUsLp!quSlr4@KDV=D!F393HU`ULqk*aq|@m2%B8`yb}M< z=zO~}Ww03rbM!Z{?&tMjbpC5mw)J}rzCo`D%W6rA*B1hyqGZ;1cjvP@gCNB`LJQml z6I?%i0O2QfJpWOdSJ)F$rgT5;BIQxRIV@9v%Mtv>Z#(%8&O9i$ic-%2VTRL(RqoKJ z)>nAq2lZflSEjo3$&}g3gFAG=J0>#>P3w<`&S^WA{lF-LuzMuTR}3#kWE`AHU?Dzi z5gm)~BTiNlrPXh%kypw;M{1KC4^~#qS_FM`%W?U|Wl1ed;YL99#_Lb-0`a!cj zHW-qo%f`~Qt3{QW{}6aAsl8{T&ewCFk%Ln=h}>96Q3;PKJrDnnR&1sgV1ypiAT-vu znksd!Pn~ei+zB4EovT0l0S2#_`&_kg-iOgD%BzxuJ65ewK6h5QuCd4on5sp)*V(c ziM5Jn&<=wiL6tslhW{4G*}QGCW2x*ygKQi-=M81#EX3DkTE9iV&U)!zgjuIkkM)S0 z+tl6&R;Y@1hec^9=rl5fRGydR3cmavM-V_x(wX(RWv@+s`K!Qi|C|s~4+}?;@9(1X zwYu_m%u9l7k*7ZfzP*RB?Z`|^8IG=)`-Pp=>PbM=^eGSa(uRFe)>L=*; z#xM0X2fbQrHJJ_fKNlCBoLeRBZ?^0z=1AX`9Wvvrd7zVeMGc{H#dMqQ@*2%Av^I9@ z^yKf+zzNutM%AbU6{PQ7GZA@fSnbIJs#ruA7-yd~0jO`?gm#}Vz)OYtM-(vomhXN_ z=gG~k;InNU8>RrK9b~4QoX!l?vdrt_nvZuED)0Iq?_amM&HZfE!khWY_P1G?I)M2d zP0f;3ba-1tEX}~$?+jwTXxx%InX##v4vP7^diRs>g27T!cCYKT3E|vNRL8)WcZOfn z0wRAG`*8j&b{`7*2-ty7J@ZJCjci}$54|lF*2h7o;w^HEj%nn3U|DqNx5w<|%D>?p z+l2P&L}<03xBIMOP|$*0N@u{JipaEsgV%AdukcdBrIcO~S-q{=1Qvc>%<}nOb@xB$ zyV{+UC!ak@NIzHC9D@MsbLfU|zZ7iHMDd-Q6{GH!iyR^xkMFgEe{5WYsMaQP?L=B? zHS5|=@-J_r`SokB%njP@cEl*JCPDX;3uL93Pad@N@!AB5X*l-thssawC%5G2T3&9% zT0Zm^SqJAyPZv+h^Mxkfo0Il$Ho|M5>EwFM6j&?ta$ki4f^M_Ot{tRDRPGfUeQfj0 zE+8HN`gBOAwvcH7mr=P)5s{V$o>pY5%v`>9A9e(Al%%-ADe_#$P9hTyA43TcI`eE$XLBdBv~X8Gf| zZv&6#<$cHVw0`ba{evtS4xqM*Zk25j{VGdW9YJenxN;0WlJ*yU0BKDv_q@R`WSx>D z9BLBXT3_%BPh7;~a5-wp$d`Y#w0uA@&yFvCJ~7%uM`ZzR3TQT2LM`j6#o2M#P0`u1 zw-Lk*M3s=zpI?UJmxYkR&aYGYm7{`QX*EShVr4DiIkEnfzGi{(!WUYB4F?)!g}hX-uhkvkdo}&z zPS%)$D86LQ$}GB5K#H#?+kxA8NA%MK7V>qjw^t*oIcjRJ#&)N=k%~WYK}a!VgI8h_ zGPg=@i6l02%a*R6X6_%GUJac@1hDXgM*a|ZX7DtBW!5p?WOKS%j)xj-p zA>KBY67c0FGDNSP>agO+RnX$JPPNtT(iOkQnG~vtKAM%~VngssL^0`^Ho=;R; zP@TzpQs%mT@x9w^R@f2ezkRzK?~h2U8*nOQBzb-4@d^$~8rz2+Y=->O1hM0qZ{^B1XM~yx)h`tNcSe)ARS{MImTcJV-YF=5&V`O-f;r~u+Rvt8T?~k*_1>HS13)wH+ec2pN^EAs8=o++JzPwxO5yby@1Pl z+w@s8{-Pk0cj_z&rRn&(DH0WT0@By%Y0T;U7rI^=(KwBjP5zi3BFp2U_w`E3etP)y z*WAS~ce%o~TFz@4hxMj|cdNezXDissAAK3%k|>4dREMiY$p(UpM1mPxZrA<|r{{&o z`DQPo2g_MmdQ7v%M5534;cex+mjzi(SFXYg}<)|bpP_}4db z;ZiGmP>S`&#S}NDf9WBiXX*7g+}^M++$vqktIbgO+4jyG4EEZWR7|h4T+31T>nmH3 z+j;lR#`s;$*_HYWAyHpBpAK&BgVjblHqYMZA~@-+QobLuv8YITW8L`hGvE0vtYbO& zhJRgQD`KOQ9}}9EdEf(+y??r5*{9_eYgl9es_lS!F<}-+g{>QIDGz0ib@%3|`uThN zng32+_n$jlzwDx&N(F6qVhfDGOTG|0MC=mLyW3G^kIB8=Q>onNCzF>0e}&|T`mHP; zlje^fy%~Lq=Hw%USg-n;V;_AHjX9T}$fNk1t7gS^qmOej=?wQGiJwy6y3*^#*L zR>vT6)o;mMN(JJqB#@TD1Rr)^p*hXaB&teYkyiK*cNL{VX#i9FVdtjI-ptuj=4?`H z+-62qBq<)C`fHnZpdMg5=O=fVODVCK8kwVfbwdDkZLs5iD!XOVTg7A?Rz5(wK0YSZ zhPk~$5X2>7MRwp`W0TW;Hr00%CQ9^XI_um51vQEdBM+cyo*8O^D;BuBl%uzgLpg0h z#KxtcKqgy!$>MKa4f(NtQ}v-Vq56Q-7l?G#J&d~)s^sI$((dcUr=t#D3+7{?d#cZK z4|3PN-?!*7cA>l?ro!|O1rBHYP*zTxACjHtp5$_`E(iLx@cHd!WNZ6P#X@3AQev25 zwMSL0ciRtw2;Vis33XNtJNlS~xI5U6f8q=(xNGlg%G~n}$S7Dx9Hi5grm-5Qyg;aK z5|rlkEKL~icw1+43uUA&op}x;5WqdSlDh0bWgkHcPW70Y2`T%OhNE6n*OBYdxRO-S zRQHI&2P7RY0nfBEp*sG<(V72b@N%spTZ=b4h~-enE0z*AZ@a*%WBL7AO!DY?M3Gv> z%Ix}n?7f3q`oNfoN3KO}LLQ7{G{#+zqi2quwIx(y~k|=Ib)&r-;6F{rBtazt@@w!{p=9j1<0F)+R}Jj z5)+2}m*rVyUV~at+~bJ!v8l3G#i)-k zGXyyUG3s$(`^7S-$!G5-^1-6IK@FXY-hrBSFd`!Rqj5z^KBZ&bgjMdh&8}aB-J$YV z<&2kULpU!DUF>55YYkm@e5e#mfCWsyrnwnVz3G0I@4nQ!8?D7M(7C##f81;}x<#71 zOzy0WV%q)oD8Y>sx{Frw>t9xJ#++4gX6F`7rwcu=S6s|Xnlov7>R!swrTdSWWl`$G zSq%p|L#_nt{q5Ihj&INHueVJke}Ytze+;N~Iq9aQ^`HH=|K1V#rAtIKj-IiW`{IXB zzqv?F5h+b`Q@^O<4Caz%{*Gk#V?Z3faT&PWzTGC=XpoCDavLdLK%b_8Uw%nrpqX(}4iGDG8z#c(aY*ESCU3n=e(50wxHQ@qc zbyX+tGsjp&S_v{^pQp+Lpox&3L7mJI#z-A2HIk0J*&-3GpM}`hAN`zj83~&ypov+J zc9ZJq@&!0|VYl*i9#|enGgst}LL1DQBwt^}9 zUVA9qXCWILa_o3oj#*&()^Tw5z^e29w>IAsuj|-J$}=r~evABh1v61h8(*4tcob8$ z;v{1t+F1OFwhYUuS?S96LYv&RvlD+y)u?gi>#5RKxKUhWSYmfQ*mh>OD4XfY1?xp; zlM(wCpZ7lzBEa#kJiyYZd6HDeh(1z7g7+guEoD4<%U;Fw&|kqyBllNKQcY$~OrmGK zrTj8Us=c9lG2AvY^{6KnD6Hk_tEI{K?_qa(LxZp7+(N$C+DA^5v23C(V}>Eeo5efd zRlm zyzcu)?=>B|Im~JS-Fsyx-gU-<@;z0>w+o3)ghd`<73P()`i`qMkpZFZAJVvyM{L=O zsD}9-D-MppYX>>=`pn%79!t*`E~jl?o_tfj{d^C0`{{#p(Ri@{waAWyYM)E?)4zW! zd~3RVo|iik8})`g=gOvwC;j=`s8jp4IvyJo&C0hBf=g zp(Poq52*59XPDsX)4`Thx4Zj!aF5rKSb0G0FuTePop*i+xcB-*aCyR9$*JEkZcs4K2^GUxCo zTYg3+VokHE#W}1=^tsDoCf5XL2#UZ|z(r@v%`(1wKwh+tRoZo|yiJ$nj&+PY)7LoF zT3PjQdwoHC2%KdLdm2;6omjrH6%MzJHNF$mOeK5g)oDaF(#SS>IW{I{E}C9kJjg9X zh3YY-Q`&j&rhTn>z-`Z{96;(amJsc~V6%EWFj3QSwjC+>UX49pgmGA#s)ZcQ*3G(^&^1YVTJhEb;&yW4;ssuN?ahk@( zJqPbT@$iuWVp`L>CP^`0fX;ddXFJ1wWA97e+1w4?>rK6@fvVmk=f-&WF3zU1T1r!G zKSyUnh4#Qjs&QE_K?Izr2OT~8LFy*H+Br1(?_0Lj-b$_)A+}I=FzMj{I=G4t7oRRx_5(*ddzl^t^jZ&A*RE!z3&iBycpOB4rzJ>_IIRK7l-XH^#qQZ;Jvd6{pPD*xfxOu)R{H2+$u zK4#^Q0F4c-_iRHXtT%+FcZ zTRoXDUFO}TqHbr7IH#X9CBQ5$+mK^|a@;RS5I^TAvpQbfmbQ!x+=oyPP{hqC4JCI~ zgEpN=(uAY>j(>He0lXF(8d27sZP2zB%n6yG2qj)_6}xDD=rbMb`2BpTCM7zkvA4)XWIGJ?l067>2jE|o+~dOq5EgrU1smEq&zQg&134Z>klcysh! zWPNNJD5>u;m`z?5= z9j0?OgHX$Xs566D2qINUe1k=zG+xl-JL1~qjk5n(yE;v~Pm{m|ED3Y)Eaa3l{iLlT z)h8f}Kf-t7b+WWp%K+Du0PLEAUC!6OO30R)EOyN1fR5p5Q^sQ zcJ^P%DfQN&NMo&Hg4Vx|bayrqCo~Pz+Wp9##(8}v#&Vm*1WAqkv61SRXEv>?9i9bt z+H1~_X8xVzZxau|ZlFtF{KKF=AN->g^+p({YXmNFT^_hcF+Z|yx3xtl%Qmb-}YCN*Z0rwlROFVcyfhp(k4&Zid=J_t(YyQ_{JPHz4^$HLB<-mXm{jq zww1jsuVnt>K_JD~k9O4J@fZeQeB_reKBc{9m@Fi>Z2n4zIpQmW%gz6g*A(oZfRKap zzky%f-hZW|HJ^1dYs(3*6N2rn|LF(U&zBnSBcz30-Ot+^T-`jq z!JZILFR%~9+uOy>$;->x!^6urz~`-_y@R8ZgZFODl?z+~&7}a55KSBX`-!oVYD9er z+v8dT{L-uk0G{&a1(I!wm(o0$Hy=YuES*}-wQ3CoQ-)E;(sFhcx*49kw6H_ee+RSM z0Y}W-8|b2nAv=}*37f1$hs^i^#*h0L#r_*c9PEv@m%iLJEms>j#JmFp&>%J65_aa~ z0tWqad15Qk2EnA-?Cj!=KONXTBvYHdqdPMCCT-tH-rL9%KLi=o#YXENEf|HG=-CoL z>-U2PQZpSnrDix>JfB$Af(ae`6S;(=@=F?;$rq10xUc+o&3qT?-!m{fLzxflXlqq_ z`RuHB1r$?VwR9D?ygci`WX`-N_{n&2LHE-VwQMsF8Hw@LWOs^7}|V|!`0K|s_qfFxl$PF{B5fKb=NGefBZ zYm`+%f=~L2C=Ys+u6wnR1#u2@(c>=EWUVCHlpFFScs28his@;)VM_Wzp5K8z_$5O zua6*iCS>4IZk1i69i0Z#1C(lhSlB82;evi{uP)|HbJDiL3>n|kkn?oo-_K!t-=SgZ zfNl{*R#Q@HKa^=V5>IvqEj1pW_xVV9to?lmGzD+#IneM+5=7`OF*F}qY)__6?SRh@c`HB(iftiIDg zKSY|>h_PipRn+@)_kZlrAEx6`(I#`zhjxKaOWWFL(6UW6dp++TNhibATeMN>Qg__- zleC+aHMM!KKVcT^pC5@sHOc133)yYh=Jj7=`NsF=AZfxt@unGH9g3U00u0|2pkihN z#gq2KRTuSSvJ{{HjeM&Kd(0jC_fxH*wfu+D@O(pvcaW1R(Yb}V1>=j={q2anHS)PO z>1V7Q^H;8=nk!wXXA8}l4|8ezJ6i;FbHzDctpkdRTnVXbcNFd&&dydkN5IPNY*fne z-8dh5YnAR~o)75zo4jTBR;n)Fh!DZp=r(H3sZSgIyHj!T31SlWnx0-W<%Y~Z`fCR7 zqi#^<=K$opbI|IHF1n3|`)ey9{2<7eMs_<&^W;gi^vyR-1!=D5I0|mX* zVAYAfh{dc{ti;bSVIL5-KY+)^O8jMS`p-XFGpi!#piFV4b`{^duN>Z%rgQ2gRaXpo zNPM2{PkQ$b7&j&Hz-zG$cxkC7=dWX(FJgq3Lc>NumLuE96zX0rVXI&RUf(chrWk=~ zJVN;=Qsk%3$j-uEde&utr;Sgwn71eau8SmlmA|9&L)m7zofylYncwnx8Bo5)PsfvK zjD4`%wzF^d+*yAf!5k`yDFRKibUh91o6@x|w*{NOU;!O!L4$X&&@zhEbzta{H`d4K zD@-j{C8=ZKkaxV=`1ur|dr@|uyYEF%N$K|5wtY(ACe`6M_;~z=MPgIeN<Xd(s$MnXZ6nVpm0r6AtcY|=@#+Vi4`a{b-~q! z_dh@3tH-`vay80ZTt#bto6`OuBIkpc^B0jIjd#g@<9yfXdm8?`B5rs4eW}W$>a_sc z=3L+@HeITs$-SpQ^E3E3r0GuPc1awu8{^YDT*kd~@H10;-YdQq+OixGhNL&W_?y!f z^}!+?^&Z<631Ozl-m9RyVUbLa>mr|M7&>XFq~>Xw%^{Q8xZ;24dy?|L5_ z*CWehUZ?)IMLvr`8F6nF*lq&QZ|0r^Diic%852xeERG~KNAbeuv9dq&uAN+d3=iUM z6kSsnx!v%3KD?hgS{T(q`w_>GFPVh2tLrwOADyxaLW)dz16u<1%ZFuEUdQfh1gha$ zA+Hpe`K@C<<}QCiyZ(jwnB8x1i*B4#DuV&((NIzNQt5A8?hIwbB+6zy$vivFjS4wA{fe<_@t>q_ufss+?B8H@jy$KS53GS zR^Iz`0$r^a-=}nv8QxOSFFT0ix^aj3RcVQSO}n0|8ZxtO))N+Q_DW)ReV=)qivO5t zA#O;W7R0eQEer-p2}%;nH6KLE{w9S>On2unx)$fl;Ob`gq>FA%-A{xpjGD)AF-t;mFg%WXC|NjUL8Qi~@V%eOhi8=R7O$@M;Af0D+K@tEpcuK1OdW2N zXbm85ZJb&)NjAxdrn5~&J<{EaimqJ}W07p3ao&Om6NEmquWrUKm0?Vft z5$;D%B>wXL5tdP^rrv5TI}?_ezw@T&VgawLDPbZH?^ntHSkU8kqei`3NB^(b^aRH-+bovCiH}~v))HAg$}9dOHCyN#`__?Ms<|a2Zsx%w*Ecc zO7cYxyuoOS?HnBJKA3)bP4VwoUl3Z{wS@LyuRqvADvY+0`Z2Ls#Mp z6K>vQ>A0ZYW%yi?xkh&S6QzWdo9CE#nzYv6gytwp=I9TYxl3*4m}DCMzfZ?GJkTPPM1RXGf$0GxcU(tUF_|pRIiMtY)>dUDnfFZ z$tSj%r!$jp=`Jq@&t}>QoxO)wv#kr(jIS(_jti2y(|8B;Dr~VUK=OwN zP5i*u7gY@yTZ{=X7Z)hB1+;8CDyWZ{tc}zPU{Nzx}wh6tmn3ITbxzyX3Y0b`_^rvzH#nBz^oS!&iMUK(br-z`oJa@ z7C-H&1LWANAGugmTDyYk(DctZ5nej>^7o)y&tU~x^{b;3_>rBdjWtdY&fAEchSa={ zBr03Jb1`L3M9n3udjOJh=f{-7%~{z1GX;6nZNp~n{&W{wrU?ESGF+_WHmmNmxkK^tQKR@_z5)2i+AkfGQyPh68-BbLN#y<1* zT#H!YJDIs>PSHi%-hilkm~j1eP>w05WiIbzojaFRnuvwNW;4HJNDlI$NE~ zUBmLIW%&LxlM;C!jyv;}>J}aQn0JDtdXu@`C*wrx>`8YI+rAKj+}Yo<{%P353+4WN zzn6{rlpqmtgwRSDzACZ3uVB$D3X#6^1BW!pHAp{>8ed08ctkuK?R8C;SKrjF^U3M2 zgu*bmXIY$=lW*S22{XiWH;92D7KyCmvvzjF=@6?$Mviv!x3z2@1~N?iLaMKH0o1R< zHRy#5AfFI8hRVo(emy|L31zsq$O^4OKs0{xFA(*^b~IO;aDz1c{*M;aYjDo;tFvPl&9rJxmP^Gk6G_MNeFCQX^OZOr2^2eVjG=TjrQ zDHfR<_i9x)NEAMUrjYD#6Q8Y4*@XWQonErniVB2H>PS!8MSimjb93A&;YknpjNZZ( z@OafJxx-u{Bz>xM-~Sq$wZ!~u81;V%d%u3M-y~*bhVtHY4aBom9sb@-w<)QJK>}wQ z)E3OPa*iX%GG_1Jdq)PdWuKi}8S$$Xc;0@_MpXZ(aX_cY6s7}A=;xbD`QqNfB4A({ zriij5r?QUOBqu+B-p2E>HD4Fcx*KZtAuPwUBLw=^Ve=2MbW7M+wyE^vB-EXOEiMX$ z_gZc|DV+aou*UR(eyQOq{kBtI!rpS^TnFDi0ckb*rsxRiYzl|P4Hh;nv>HD|?YSJ@ zSjIo0!fk)y2zFQQ+M<}wB7(f1fLsq)G$cjj2z&qr{oI>DCI7Qx473muL}HS1a_LD1 z*~#@uV69oM!P(HVKBMM{MH8+__*_eJ=f5`f&pCN6CuLzY&UlgDa@waSix2nYiO>(* zyA8L-7+e$hqvrnkzsv({g2xP}6rcvXxE);px^bnw!x4?rFjwg)wn*{}nG4aIV~g^B zHGdS=v^Px%$>+xmRHOqH4fb85+knIc0@AA(62c>@zcr-m7S&SSI(BF z3?O?k#F}mSbTxDH-}E+vM94#YAoA1UT3;m3Vm%;@+c%|C&X#MZfw_s{yO!iG3dFF) zg&e;=gAYmJ;XO|aG4nJfU!k9dl}aZ4=lY2D3YS<`-=%d=CBM@Q%x8*pUC}is>Qo~vd z?M9DXla~A)dlqsjr{&A<;9HKwT=Lchb3`N#WepE$s5!KkTa0Y$Xxvy4!zk&s>X27n z!GSidO>o%i?e+D6N&~^n+t0J0fn1lR{c?*fHike9n~;Z{JTKHP);DC_SJF2)qQ=xXIEdBb?ILOY80 zY*0{1MYr=z_Z&KYxWickK0P0=Ef&3&(6*GwJK<$LlIhZJ*L#?Jj8MD}|La(O7W|Z> zC}}OkgS#;MzB$)UMDUh%Xy&zcfJc=Ly>yIHJ)w5p7(V+Ue`uC8*N+?r zONA{kCHmhrSoArg;q6Ohbj<>>0~22eaoxGtT-mkOt_M`OXIRC&>8>d1Wx;~)j3$p@ zG%arZGQwj$#X@vOR7`I61S;4lLsIdHlHtvZFTB#zAaK`uCxH0%3gMI*++F=P|7K07 z-V~)1X%0~&WcRnp@b1YqgYD1h2g$`mIlzqVXtUN*&gRos8CNwYl;yEj$#b3Q?>98& zF43)81w0pHIv7vb5cQb!d-2^--%&R({^9*^@0s!@mEA`gyuH7;Gc8QSoWWMay-;=& z?S!qTKjwT0wR=@*tpOC@t+?(%zXMMzg@h$0W%~UNImCU{S%D8`GZj{jRCs-jXmYPZfQ-8M!mt0~V!>o5x+qBI>psDi* zu5O*`HVMx(#QJIzy}N;Vw9|VUx|bnKzX5x= z-t|+5^Sk{LZGROAwf6`&p7)iDJ2fHqhbNDPN%C|Y}oQYu4V21R>4DXzn!;LkDb5SiOA zHhO=RK8P~NopK~;R-qj!{t^uVRkSmkt>Kk+3K0o>4d1w~RLxfn`!tvBU;D$lZ7T3t zZAfr*3uIG?V*O}$Y$X2roJ1^UDd8;GxZGE~pZ?To%8e;!Ki=9F)VC1D4AVB(_2JmyKJuu8BW*EsE;Nw) zaR&ZS{wkB5gnr{C86n(@g_mh3*HOy;E9M>dt~{rCKP{k;mb5Y3pEi3oimgY(&a6`SP#w1(qT zbl{87VnFIOBh_3_P{Y^malex>1jSjP{Q6IwSR&@jf^=ZR^P9ens#;xu(fvM(Av zizs)*tJJ(P_3C%Leq~Q$hcm=OEcCxYDgzFZ7dfIVxM7du6aBBMcrjD)Qp^5!w3p!% z_?`FwBuyVZTl}5U@d;EFRIGb1+uIFvvGPi7^wS_iP zm085Hq?*_OQ5)$9jMnOscoY9Lt^cn}egTR=3fpheJtl>>;POc*m*Az36M!>GzZJOo zouInwywC;xJC|RAeO1Mv<5{}-mr$#FbmLHMKl|E76n0-8=&f{UwSSHG2F382pd(Z4_ zQa9?_Y+67FExE=Lt#LB26U!6uTqc|2U>1?0$B`#TZT{rTZ{llqu|j+(5%5AXUF1`E zOjdAvN#La`jEs%!drTY=dKr|+>Fv6Z4A2a=qCancS+Z@K*{YVMhT^uH2VDM3ULNJe z$sSENZw;wbJT3aIrdu(>^mUyXm0%2&z3%u)}+^z#%J zx@_<E(?#-npOUK^$Y~bZ%hN3Xd;=Aa0@3mOO{OMAlq;75~_UJ6a zwSwzr-|XIBYXjF5g^_#D%>-%iU^PlR8#tk553sywR+*56x1KRAGdy)Px2e10D0mE# zl0Z~xC>p&JdN+5EVsVqbQ9Q}XD4B!j?bIF$gU$J{ZsH+dcXz=R!i|$C+y6@S+x6xxL_9fER4Hb*1!S}1_l8*P%iLg8)4y#cQ zIZYJWFbUK)ZMa_hk8~^G9?i3LA7a&&#k$Pt&@(=Eq9~b}Gds?ufWME)^mI~fxLsBo z3vr-$?DCL3_2m;udfNiAmugvG_u3G`JB;ttV#89v$D2!o3FhmM*nOmMDf zXb_YSDXv+Qog$wcU^DUIKwkN(r zx?CyE#=lP-x}8fg6O!S;ZPb+a=YAuE)600#deev=>OIB~5UCfifzU^o5j`Q<*dhtY z*E!4KI&8Rdx>{dMo6rn>TL!2=iPzQp`b&`t#k9l)J`ThOp8mim4XI{jzRSNmTAd;| z%o^msK+Xcg1FvjT<>pP7Y6_P1K0OM4B#?$!u&sucnb(A$Mo$zTPHCt0r^Lp+Yw4)> zvwG3t6!|dHl$9;(d*Y^eAMa2va}{UNyJeB=ld7W$Ybsia@l1CLP9V zH@X`xtAVEC@M8itDZ*BuaWJXc^3L$Ubi!oq7x&D}vFR$S;}=(3`55wF&naLhNaMnn z0mXaBXO|Mct`J|~okpJ?*b?M02NF-O)Ytew7c)iqKX`Ng{E*}{1=wQIy465XzO@}2fNz}JD+5NpW1`d#vUWB_CYr(f+K&%#(!>@dxS`C!0#SjRp_6KrwT6n-_uhyt4V z*;zU@fEZG@h9o|Fc<2hhtNuJ4^1Ucln3mJbN#|K}X@cmAyOUFz?h#yhZgiE3wr z$oR?nd6}dA7ZIA)d6e~6MTgpiRul!-@8jgtHU~&50B#)}$nEnVvLV)y#aA8Ni_8j~hDCEL9?e|i052VU z7GdwUV1<-uXfMy{x^q2nAsjZUsagGaW8N$v83%JTe0t%lVoJ>8uYlUy3iY%L7lHsk z%g&74tl`E>*2|!#?BmdgMLdwAT0K5e4UNrOc4wmH7Y&=Lsc)@!D(RoTwIl=glSY^F zJ&-u5hB>8Lll-H}@U4)t!l|eAd#BX2ujHhn*kMKRn;msf3JdP4xjE>D-|k`!U-ks1 zDlWidsV+idYLGXZXj%mwFw|$dqVS@<^|}=QmNbCmCC$=1-;x_6u@~>W%e_#wSLq^S}ubIwKu6?70Rl?sP^x zqvvB;sefN{`V#2H= z*5Pho+?jPawO*FhVFD5)62qu&@MO>}f4Xje(*z*fm{x0YMqD~> z0O4oun%+cnl@ou~OBd2>)br>lSEpXFD%Oh*@D@}oyRh*vPDnlMcj@n|JB}N-)JIKE zH}D;_(J&OUN#Ap?$>>w~0vEZXOW4#afxe&Bs^0!UjYE z%1Mdz-3ItoK`;BU?=)b*kDPy%W5i3-(Tg;0Lt6lS`Y;_8Si8{kr65+!BX{Ogeoh#t z_s}<5Rhon`&)oM5KE{64ZYV$^XxzD_vaTC=TgSrOE0E9HZpz+#Lm`2ia6qolm3(KR zb!%tACoQ+rQK*yC;@$=0{~xp2|39IM@qD>E-|$2DjgymulONdA2khbqKF{2|b@%o2 z^7Q(DOb{Mn;CI*X6u9TA{gEN1b5> zZd@3FeKcaR7PT#cCPyToLz7%y9M@Q7b~Asw*wSR z?t?r}ajnbUYplY3OVE8X#C`*Z=bt>Q3?e>`u+EN*sxJ6%NbSato9F3jS)&=z4%X`3 zeVcH&imxb~9i0t{^ybP|MTEMLy?wYVk1(D821D$YM#hJ8a?_2bEUdw!%-y5@`^P6Z zVt1z_2`y^mc4_`ztDXWsMC`c2>DRkwe*wNLpN<=AZ1${FrF>st-%zz8T8$VI$|4Fc zS2kSTnB|5Wvs`P12bZ{nm6tfev)1v;Nai@cSN)%Lze+`huFQYudj6`C@@2U=%OJ%Z zQSTMr@2Sl=YwGV~c558)fhDr_t&XonTyver>R1n>PNzf}K~wJ|X?Vq^cVwb1St-3B$gvf1?dO z_(Vo4aDChFDa13v@0lmrpjrfm_%-(HoU*BB_N;%+Hk{l#PkNeF^109ECW(XUZe6d2 z@+bO)uB=%0%3M6Jx~C1!8S^Yw-AP6^+c$2E^)qihXtG7)F&-6zEAC#`3rXgAcllS` zmAqW6Gy{JXKl2%TEGJ$U{8z(L;k{Uc<1N+bXgtx7vXa8Gw!r25?iM6+^0m8=XJ60zzBKK5+Q__{mh*YNX-uI; z^#?1GwS!q|`RnotbZ%bCzg{MnZP54hg;BJg8sBSFcfgLmjL7TgYvJ(a#)|I) zdYYfAM}97k3V*9~M)H#DF3p_pE7vyWjkT}JIs=DLs^1CzbJ7AbgyP-ts{~d$-eQ(= z_HWwN24Yzh_^Q7MAQZH@Fyx28Ij=9eE^}QByEj833 zWWmS=<7C?H28Hx+Y=6yQRcTjjNsIr5OI3I4GKM5Fj`m8V;;<8%!_4b7$W6+bHr_!# zZvBq$gEs>e!1L;)=&4J4$IOS=b*0OwaJj(h@p1RhN*`TYzg|A_`2*xwrSmFe8h2>i ztuRR4oxQMzjRxG_$FGab(k1$|b18Dsk!#rfW9{pl3S_Gg$2azy4JnrUn3<*52S1Ey zv^|$RK*)j*(!|XJK3sRB^Vz^@u|^E&esL*#?bE!#E!?0~oL8Yr`#-9jGW}P!*8fuE zzwQQwiU##Hu8_EiOu&yZz@2|bb`h&9-dUO#Mryu)m&<5KDM03@eiOUXMlFIy46L2j zpg~Hu`;a8^SD;WByrOIigmRw+F2bJ)$A?&rlu%de?pTQr8^sDL1YLv;pNtMig7)$P z1|d4!#(l`^*YhMJCo)flDpI8q0u9%CN+V~NQ?6~4J#=-KL%aJ3oM$rsxs|OS*P$k5 zMnil&mjJC$#dHi>$;UDEfU`mho1Y#4<8}c+}_jdM_Qh9eu+%J_u))bcP#&Q9AU3}c{q?l zK>{SkD|D(w8l}h2Lc$i2cp3cDOT8$2^aQP}uUz z)p$3G{ymnG8)ltvOKCo|uTVzj;ig2L|Bmq{HmTW6hvzNP(aFQ*H}z)@{*yi#2Q}7L z1dW9;^nXUT9sA(>rpBv5+l_v0cAj+yW0BLKVMI#)vU21Cb&g=YOuyeh!19`Z$MyTu z?tfyCn@)p89FC>a!|&F-j>yz&}SIOq}dJ1uloS=}ycj`+{sN-{!` zrg>DuIib6W62YC%Jb7pp)1S2>naf;}SW;M0h1ksvFF_{82j`tbwF?f8tZD|CzIo=g;4jHk!`M`ic677X@i7y;ic~Z^$lqw2q6{fBb_&4`lyVAH@e0+G zcVMBFao;tCze{=r4u0Gs-8rE`bC{$~(Y`4dvWK}{h?DPaM!M=CU5Zt>%ckXF^2zzI ziO|O)yTC_C&JmdFgY>f5_wtY0-0hmNe|OcdcSCvqW!kK+6Q2i+mw0`xKU?jW692Jz za~hBRn7*DkHu>asQO*uZ4yfFj6CRE^-3Q+;+`Jfx&F^Bng(dA>E1O8pQ;KZc&E5oP zamB2+HgoT16mSukGk0seS>a!9{3HV?P{<-~rMWe1J}~xY6vVdC|3PX8Xo50PY=?L@ zpOR%SYUK3ZD}c}gtk#pia&LXxGs%QQO|)P&Xj&shqo zl1=U2#&@f9#f9wXH(DwUpHfZ*KT%5oE#cp=>pYZGQ_0QV{bC4&zal+!Y~8?C=T6+@ zpVf$K5C|ruhD6;{@$dg#KN(Or<0W5H`QBN=iLO;fv~IqsF=!}(-G#3$DY2WrI!I!w zXz)M5vhW2=o{J@&cNW(YREb+AqI{r8NzeU!^u@}6|sEErh3d^e90;>iRK{;cy zR5#mM56#k$uoZymfg3Y;?r?vl=rF&9-!w2i+jMl3#yip?jEfD}bt_O#li z+4N>x${#2Z?~cNSF8*%6YcrfNU2|7DbB(wC=MOl!Kq*%{;1i&HpIKqkt=B3v(OAxL z{CiNRaXD^LFHaaf=Vz%8-W{O}(ltk9f4LFKF~{2^3FiS8I_r)tG3>FWw*QP>>!PVg z-%NC-8(C!fX8Bn7vb+^=5s&0K9s0+4X&UxjX0W{Z-q>+bx(SdmEKoER1q@sY>;DXD zBUZbx^f%QO`lr5qTjN41U#Gs2>(@CW#2=hqN9pmwh?L^G#M6^5zUmh{ee*E+lr)>1 z`r9c(q&k+&!kranF&o#0$6=2??WO4$l%UH@JaZcUORb5|IBa)g`h~;bsz2Iw436Qm zS8~C^xiOj)-f(sPu#Yv(CoZ)fg6Q@W4_{YEz3pqAc3;ziKf_-Lu5Y6h68E$j6&mG3 z%DJii8l|16@$M}$+Cwt0uXW2*dd>j?S|L#npI1NqXrX&$x6|{V@UzwrAv;N8VmYJu z(bcI~CEzA{U93r;x#>-AKy5?@$(*LqVMcKrK6C3;lUzxBuwUko<9j`YkmmiM-nwq+ z^+by~i}6wQ%aGvajC-Z0{h~q=#XTNMUze11b)X^)ANI9uv>_?CiCU7?~Un!oTrzVep|!}v$Gkh z%rHk)o&J-rI#GnV-;6=SPaXIb345(n@kr-JFs*t0e~3EEu%`d8Z>y-7VAEg%(kNZ~ zs|X^cq;z+T9z7Kir3LAd&H)3)fB_Q_knS8C(zy*7W3YU9j^}=k`_=Eo_wDb^^|?OR zdBVnqn!du}fK$8ixrZQWLMS_<*_c-?ypUQdfDBoDsu(sGo|v*PYCHkwr73l`&v2k7 zTm2l9_jEvik9q=;*Rii9T(2qKUDhT9`RyNzQxk_n-#F&)&l;h6eaHI$>FK^7M2&`> zKox$xotwd2i8E?nRb{%?a*n`OSfl?S8H@u{zKtMZj3sUF;AWivQ}+|aC|OEOC^f&7 zh0h_m`1XKx5oI+Vu2RBo@{I9?h#Aa=uYX}@O{`!d8{48+&zKLQ{5#XqE+ok`@A z&AlLpP}^ptgV^^lz!{#U^mTn4RK356`V=Ohu-eRBKu{`YPO7b>7AW3*R)Stnu7`%C zwPn!#LHpaz3=G8UABS7ibIDc}4i?JLbDr*&2O7}Z-5H~+*X{Z#S{=j1`Pokh7sOe; zc<^0Bf)Qqvd~}y06B3c}8=2OuE-Nm7cDJKwvhWM;QlVeX(uXLgNcLvyZy`7dpBwuw zlW7iVw6y@ZrO1rvcc~@gEvISVE`I|VH`CkV6JlquTrKJvygR2jlDYNVB!T5%B#KSo zW;;B3i6tuVU9JG+LL|SP@rTu+8ul?r1#znDp84S!_x_eM*Z7__AzKIXiuqj8_#nb? zMA`|qd?pQN3HFOd2n1Cni|rHgTQi(S@aDNSX<5!$Z{MQe`#U!xmdjQNW|Zfj9cyI) z_rb!8)CNVB%)KZ2bGLYZR3j&L*FfXJ(PQ@kG{j!=)W6__$N%4Za_;}i0REpp_4?^| z<8hUufUhUe)ydD(&d%M%!^_jf-^;=;G<+VP`GU|M`-To_eCk zF?DZ?z@l3(86v?mme5`o0zVL=P$$|v;R3m-QhfT4Xh+07crIoWkXO!GYO@M2c%x)n z@u}qw18=+MHO{%M2@&}j9ri7gCnfE>l$2a5x45ew6MvWAXyH41PX&aTDcmu7Zs!;A z4R`xFNVD~z&`oI?&xZQXwaA%e{7%yPcb9$LffYt`NW(V-*-)L0btdoKU{?28*#KpV z86a+=DZzI0QP*vL!Rh`X3Y$|}dponTl*z#tQ#sV|PZi^l-Z09|CyL`Qn z3Sh1Asf)~a8o!p@am!@A%rdak)qX;B1l?x@S*^Y~bzJ!55}-Q{Y49SHUB$*wWyr5xjCc>W=4BkN^`l~A=Q~K z1k)Dzqd2gx3-^)V1gn$*!jK9*-f84Bm&3d%I^ zeKpWSaGus&BiQkox|n(NG+BQL5)pd?>I1wAwY;=upc(u2!-nIo~6a0s^1dtzA8L>2swBCmtbJ3E*Y73vIL;8qB87EHh65uN_=+zpq2)m&<@gJWhQPXb_d9#bh}^c zO7*V&4AcHmVvx}1Mha_Ci-UUhK6tt-A9P#Qzaj&?TAzFo6L5aULZbp=hx=ZTBI=V< z?RM=E_fPwf^T2kAmI?XWoq$UK=_c3T@r$~-F_`JcyX7Arruw9RYUACuqL(kc+Mf#j z$vqOfiT20%wfMfp0t9&HcU#tvat{M+C7x8iF<$zdZ&Jtoolw?B!`|sQ+~~_jtszTi$eD2o)=OgVOZ($xPAg@W!-CRsF73 z31-?H8%bTCowO7kYqfilumw6dvRfYJC-4;c_ZT28GhZY}9>_ zxcf;RtoyjBtco8;%(H*6XTfW5?cy%f@eBHir$BslO@fZNl0|7-kY?*jn1I+0{V+ru zs_i)5McQEK?mK74V*!3?1xuvm!=Pl^iasf zM?k~MlI~`|UT8t7Q$29X-Hk3^SkL}@mURL8(15eVgnPAT3rRNtkcrUB++n_QN_?XU zttnIGgf=lKZ=(lRd--C4fN>bpkZk1^Xc~pglt*5yL>KPV%1wUtKiNE$Z=2P`IQ3!D zEC>cZIb++Hy@Axgj{bim@a=`?AbX4_sQAs=+f-rYVsTnhO0(cU$mN3W8~-@6ODL6&5yGdhTf z*cRKr+Y7>Pi37?G#cah;+X}s0hDkx-pT@;Gz|GL+`x2)4fcVGqTVQ`WWa751Wt) zdbrrc%U8BhCYO|xi>%4vY{4{d#>AiS(#K(An+=Q5nMv^P$E~mymJhCMpIhlq@|HU( zTvx&&6Qr61bXS`8)`mb1eM3mT^)4qn&(qWqnjq3uTD$&dowqbBm(Az>9?v?hvdsK_ zFURd)br4eX&1_}(`_l!Io_X`Zl3hihN}!XgGw8UJ#UR2iRskDZrdq1Oj|DaE8F1)}b1KO~(Ui&&n`udWO zP46ycD#rHv(bllW!wQCHmd>!N<@KhdwdLfR;T_|bU4li|v0sA>v$F@!4nhRACK|Mq z;tFmK6nC}mBX@R3SS1&?-U(H&ZY(>r;Cp+CD+oM=khW%l%aVUdt#f;f=i(MP$lZEJ zuIU;0hr#t9_8ti%gsec+|Cn4YN1r_DttW@0l(_~njP4+(>b`!td*Fci-GRcNK*d`) zn7_oIX)V8Tu0rf{{IzZI?Sh$D-6t;1yP`B=@gSSx40gYQXU<-#oHo@yHRkD(DW*`u z)*JJp!O11y?HI(yz#j5(&VOz`)T?f0$|b+n^5d?t_dWrxq9%hJQi>EXFhjH{1^S9q zPUQRm6A%cEQ~n3jB=pj6y$7NKaHOBPSE6M1W~$!=Sq~Im?jZ%!@SN#3#L4711vrTVE|iO;n@%sR=)xa_vEMzI94(tbCkbucAwSkr!9dCso2IeLGcCK{XtK z&t(9KtcGa+pXE11x-H=vNk4oUHOuN2PXLXGm51YnrTZabYQr9pVLm}3OENAo7=$G9 z?k$>FuKQ*h-a|_-mmIME$GcS@4rE)gkHgE{EA|jL0t*2qZzH5?9g!TE;l&A1={_mr zgf{d$8~Uy=bZ9r=ch2cDuy{UqxwI=-3|QjmQZ8L%IwS^nJqn{TqEvdf(4WjdWJncB z@rjtQq8jJTj=%{zw@@q^e#wna)q+m`L*K-Vt#}L9a>+rB42LEC88$vE5QocH1>2wT z!WkWvTdk1CFO`3EzAMYX<*h_5o`j!lzY6+E$MU!U-@6h}AO|)84rF|_RPE(@O4}P3 zyqOV)Sr2U@Q+uGKQFF{T>&?HJox6y?QLGGi*A&|)B8!?3wY2;rK$V4AgH;=K6EBm* zmKAKt$2zL3H=cy05N`d11B+bL5sK zz1i`cE{}FxGrtCHF^(DP_?ueuf{dm2LuZ)~{lV)Wjo5Y=p~0d;n?6eGPe+?aTaf_6x%wgPZRQ(y`om!djCKVD)uT~I3k_rJ zkn@at*}%;|zvC7uE0*QuA4C6(5}#Hso?)n5u|NjDGIZ*=AzDJAFy1NWDQTXKRvycE zumG##`xc^YOO;XLI+4Qq|0VnS=Wi$rQ*;-P9j=F41S-b0uo;aU()F?=b^>pZy=$s_ zfTSqnb+R>Eo)+2IDr+WJ7k-b1H97G_WC{1r1j@}Ai&$j|Ur?7gb( z&z)K{u^coz@{lxL*?S@5C~O$K%^291pCH9S--gge(QUmt9~S!k8SLU`tZe{T*)$N& zp|m|_^kYcqg!6NmHECDoY?$-;CNbcb9|X$Q=e!9M@7(#3mY}lv@DJE|O`F*AEwSPT zAwwl)Q`pA56m3n^w?e!?(ey4Ql=&P4M8QI|@g>$uYZ#-oE}Ftb8)v`<@?@ikF<**giUpQ#+#o6Cn4%pGx^s%&xBu8ZmW8$O=%D9 z9PLO)3`~QGV zIeBv<0KTnMvA9N&?=4o_#IEX%{iDX{9UBJfhQ;rmArIj5JDAhUka8(?-D=fp=sfvC8?@~(B z()S%83Q}p-<;Cb%@YN_cKcVN>6~R>+b}l+4UbsLAr0T1}bHcR%tn?}0_4sh)RQJ^1NvK0^dRM!MR2x%_yaq+)#yNT?du-N}R)#6O$5n*9J2oYPC zX140{^v%e$5A8&u1iybixUa?1Av5MV;y9@fd=^#iJCJzwAUAuUNadhFUMoX@etv{% z%$W*+r@V1=eC0K3Ca=3n_rxXah`qA#zrVW8QA3toCcVmde?6n`LM-Gk^Rs_BklEj8 zAcaBw)0K8Wd$x*wH3^q&J%tqHqSq?Xus%_uP}lHn(C%*N{t7LS|NZhL_qo}0c0SaJ zSxKMpF%?;0N~&@SVZGbOV<9w#8TV=!QS8MWBDK1|S)3#+g7Ol4&lXy37B;>72|fQJ z$R{QKLrqO0o~uV#ih&o3IojSq7`Fn7u+0hyCswRmskBCKV*X{U1hw z&YS$LECV|*cibkRxuVtNYAJNdcekcZj*DQy@^$)E8&R0M_c(~Hy#8G=Pb*U^lpwT|GTcL8J^ug@cW?wY814i#yQ8-NVJv4(Q30gM{W z0%cjrd6670B;kW=CfdF9d6ru0@SYg?pIuZa3s}Kj>UK`0c;khzMUn+<3;3$EyyP(q2Y|G>1-iBkA*5KiFX`cU?y5;3^StbbK;53{uh}83WrL zfd@H6xGXLi943iPv~Oe8oc26eDL;wNM%r~BIZ;qwP54Ku<)lIqUXwKQ==H#QB3}-l zg~mw1H(BL6qy(B3tTZbm9lYzkxg%@(eK(sUwwC2gC`{?rm%P4zq?GIp8yG64vA2|j zLh3pg|7DxV2APFK2wH`@uRZHTpLN_uP@L{;xA*L2YFH{b;$wE&>@+%o&bYaf^E{HO zZQc>UH6Tlo@O3LD(cDQHM;>x88v@J*L$=X^sF@ef#zL<|hybo;NbZ*)=@_Ehez2FQ zM-9NQrIHFV9cf0`c*D71ncCDqt`sG@o*q)Sr&bbGS>n{+gOFvfv+r&MwvOr{e z=*g56AJvlqr8^XR@&!IKA?c@f-)Xzoo>H#7lw-XbGYL_WN5wbX+I5Q{V(+gQt^NKT zDLGG{fFi_@BrNKP%Np+<@38EOw_9Zg-Ak>pQt&6~jdZtnYJ5QyfL%r0*Z7Js)+UjtA-A1($7cCuJA1uJq5^GT-lv3 zy!unGLu`$j^C$PHrMGRva$vW4(LDsuXY6z@opXbc_Q`eQ=&#MkD|kCyCt-Y+ch^If zXEIDlmy$nDr&q257bveuC+W6dmX@ek3-t~8Xf}@ZX1`k2_o1%ap;$QTQytnhxHn)g zaicI-N7ll^pD?4YyQ`udcAOn$*=jE?T=HKjm^yCZy~))3W~L_1N;3=YT>b`YI9#h|6StxjKQA46!LKf3 za~%}J8qcv3B@7>U@1F&I=#~|2k1Jfm5b8o5#Q@ifLeheaFRCwZKg#3pxtQ^+Kzd*% zCZK)ZY*IaKW<#muB-oWu8>+~6*`@Z)v}m>nGS{f{xaO^?C1K#}^kQ5h`9XSGXi{FE z!+V#Gt1}!d%h56m(Vu*)fIXg(_hNhxhC8&R2!GL8g?x9V;wVX##d0x~q1RYg9{wWz z`+gOvI^$Qj?8=w|MhIpj^&9RQsdsXRhXGeJAF`LWXylIxdtx( zdRf+zE$QA=vf2hEw3igP%_1i8ImieiUk9b+i}9(sa^G%v{C@Rcgl z37CE`kBS?BIs%k>IN4aw-R9*}KN5`aGW<#B1Xj+g{Z*nPnskSi?e|5o;q0ol2_u=k9Tj(YpEfVZNrTuEnx(zNUnd(tXI-!ocP`lc_PZ_J z^YM&1)NkRVFRPZHTo`RL$b!`v)v7%*B;_HJfbe_tY$PA1me{&0f|@_(AKluVkGSH$ zuU2+zTbFhDWB|hcZ=Y?>_=d%uhqu&vdlsqw+U3TmgzgBpIX7c926!clb>#$abN0^d zQ0V!KB?iLhO=RPVgzaII=pS3SuDFpt)I8MS)w7 zbXhu|$op#t-9*^nt#WJva2!%q@HKNg=tDvfRpR$|#*K7gkL44LhP;9^vmZ>g8)a|! z^Iaj%H}A?uU5zq=QD$Yq>Licj&>UE344aJYj^I^v8fuUf-(u4=JU)&w9<^P59L4+n zIDjkpCGQ;ian?}b1ci?>GR+;g9DXM`5)Yf|i6Zq|AK#A>8ianUZy7N2QXS`!jh(X< zys94wXyTI=k$d|da?Az1M8xG<+#?ozB7WcUx3Q_dP#Hyq{U_r!wYq4J!|r~#-KUH zqK!JaY9vR=nxcTP?L&wc&iP`D@iq@Q@!f1^P^o7dPqq8dcG|x=2^sN!W>#4B3$DiP z$4R1Z0ZDodTlp*&jQ(>AfwCL-{y^p6i7mVZ;{XNmu$F%Lp_!X)+TltQL2z46SjK!l zV_9_vH|t|(KimJb3H0`$V)oVAp$roWxRGqNR4 zWHHX+z+qZ#YL!aoErNaYy~LSg*iylEXVKDFi{!@whRpa@w{W678qK=8=iYKFTD3eI z-|pqTHJC4{WP2&zN_{0Y$|}+!m}?xHK+QCsqA?1$mK_mHBD@+t1mO}^ZOUN+5X zJ2QfIUH5oSpt7N`kV;_kUoj6?{2Z|s3{yNfTYfU{6@#FSPr8#*q4@_HnwHn9D+{K= z?oq*$v;AapIiFJ1O3`)AsQoM6noMDhy+S2jTr!)>YViIf0Pgv zNd*E+vw_MPcni>Z#%;b`;PPfhh&%QH`lv^e$bf61JLf(w{FA-=kLlj}cwnk6jR$5n z8-Az3=vKx@hu)R_lyeKPGZPXzl~J22fZb$XvPiSAic&bqIW%lHn8b`%fRH>+apw77 z$y%0*&)dV>RJ8w|(DTD5*ymo#rOmN#Smj|X5^vcAdUbfrw1i=3y}8fi2DfY_f?-u$!w8k%AvpB^RmT$AXp;JvQQy-3>loZ1^#$J7K*opn-WLeZ8t= zWz}^7pWQ6qK=S(0E*~K1CLliXK4*ugihQ^7xXPckR%NFjwyo~`P|}@8Ofu|jtNZOB zZjpAG1)2v0Eyy7lJ*F8Pes&jDWxKh0|2I$$+#WhYo+pM8B%VFXhQR5XNaU6Sa&eaf zJ}8^glAazoc~6I}r#g`glg<Xg1&t44(_Xb4A;q? zw*wQ&6gaZltFW1CKgNA|NMv+OVz=4-+)MFu^Z9hz8W2qhKa8o#*(i3WdhN6i0vV=K z2}jfT0qd6*?IW~E)MW4a>o9rwhkeFv4qZdVW)0ckNbdAJ{aA39i<0v164HY?v#eCc zy?kpq#g26XH8)Q@*J#{j;qJ%hXR06u)*e69^>nX%rQJPoIlNu+y;AwUkrnxS($h2n0?#aRqkE> zTYX_r!;&wLzlIdZ#r@S{>gDA#f1u|anKk~s=}0O=Y^L(*yynkVYDc8^wq@n5>_L>d z1CGYNeI4X?*ciR}Au49=wQR3{VV?t1=Wp0_UJ=MD*YFo*y+XW_&;FF9%ykFu%yyS> zbPR0^XUvVu3`{k3->4khZN7E$&60ki)^&As{3QM`LmpFZ0Sriss?1{M72lai{>>H@ z-coX8hB)~R;aNHCCe|nPFlry+|Ked4RI^B7wM@@l-{@hP+w=SetDQHBCT$A4{P@|y zYT6X-?*KY?P%!s|50b|i{Bp|_aki>C{ai0bD`{~Lw`>(fYxRVK1rPngbKr{$2b@DP zZSCYFf%CmF0m?_Yc{!M4s>?VXq}fsxBdo2VW_^EG>yX07rz3rn|Cv&aV5;ol=jUw? zf_fA2iBkdHzUnzy*&jHHlVqJL_wg`2m9y?JL1#A$$(io(rX8!=De)Rg5M2t1MwA?) z8}^N(q>6E#Piy)Xu%O$^Nhek1yQ?y{Uyb!>?R;efLidga08xrEvN*y|5|K)qepUyn zjNo%s$sSTNfxj&uA&|^raq~h+t<6(@;u`*fB$`GU@=&F|4&g~=Plq! z)?(E{?tQJxylf%yuKHk*y4``;42cb8l|4_)D{lM`jq*I1qF$(io;g<$rWm`$r+8TRs6K;{ue?{OW2u>Yk0@GLPQZ#SOzSBwx(OB2^kz?Fgo1wWTTpHVr9)!N+G?fR7X zrwhqsU2!U9at;H^+s2eo*H1`M#Hlj;$nr$qa(%3^vzv?6-yXTV>l zO!@6D5GVnJh#MgRm1I*(i+of!bhu;TI|aL>_4$+LrMBN{FrAxmpdZX2=bSk02f7al z2)dFmnlf;iNQbVFI**yVrb@_%`pV!F$p+Z@Kj~jyE6vj5$^Quc?L8f|+!+xHB@Vw$ zSgQ$dfzEbMK}T`DAYS7k|LC-;tm&>}^NEW2mfx+}kd~@myD}WLzsWHR&+1G3A)~Mi z!FROtGDCAq&rY6=x<$BcyhMku$!Rm)Q{Zmg*u3&TSX1Hu%_CS(^9Z)&R^7*rjyT;OBDUSNh;cPaj?!ih}wJebzJ=Wg@8r(ZIe@p?sPs~~6YU%juVbbf;Set{SqnmbczGU=FU zOWUSz9qsK}2ykX$9YsEjywVD_?~d(z7TbEl`Y_s^tYC~xF)>`>?tZ_n>9BYp62|(D zr3W?P*ltZqw7@YDOa%aV)qq_QsTju5U~()(b=dQBRy-?(dXV1^7#*f@Z597u(9v|w zk`vE8LdcHUIFGK8=x$hVvnaw0H0tV~G4A*~=;U|B65y~?NJ02KX4&>e12k zkYn2HwX**ftm1rLX*f8$>ndyOh;Rkdxnad~f1o#5?xh(y28wqrQkT|Vj?nnF&fYlG z!QG~9Nv!L%vUTiJ>f_U%O?}JpOQ6-6iEOyvn9IdKP{C^wA?jaC_Dj9IEQQ-VPV0-hr2Ef?ov4RdDEG*t_b;en zb~Pj?G>+e{CM89?g4@N=)$&G-SNeo8@3{qtvrEzztoDt?+?qW7x0IGn{M93BJp|^q za2#S9=g_jFG)Pnu=-n$ty+<`##{abfZiN1Du?A)-Nhefr_G1?JrAsab`e(actP{HC zY+yO(qtQ1_-kDwvpOkyDn7aD)Oe;+8qF%h_W4o@-95E!R>qcZ*3jvFrDoH~jw7@@X zyBmqS{o~ywLFO&!To2(b+tL*csX@D86Srv%V}zqUE_E7gpl=K^u<$-5(cZg6nJe#b zCh5q+l-!TQH*VX8PDo60>bMIgtj&m#mor6Keb0P>eiV%RIw&72w2OZdo~t{Nk;Yey zX$m1B_$XNS&}Yj*(P;K9?AlC=_qdyZ;GZS4I z9va`7Jd=`R)gCh-kYj0#V|As^Po|~qcZ|%GLRxwtR@l~>3~q{Hsmn*D*(=QqzooU~ z#rR?45^rZC?{>G}Mu?$kQCxPus47GV5)P^NL@H#g@L#ZNf57&lsG0qN+Rk6xnH;G6 z?|#{|4J-=K=J__k2u&~{&a2$|u&uv18Rozf+hDoTCg$W@s~BsPd8__~m?zyh|9Pb> zZ>SShKRmA5*MYs+y$vXEAw~4hd7H4Edv7;Cn~0`$gCBBH9Y>aIlY6VbmC%{a{OS1l z@xTv!Rp0j`A3jBS(LA-<&G2;)k_jtJu?)o5&<(r$QLN~0x>VAYa9V+ZoMG=`@v1G*d#)gyP0!g1R8aCN(IG9EWG#Otf4=>8v^0gj2b5#^A(Tj~* z8^ae}EMo{Q@A-am7VLhn2@0bdfT+D47qEw;KX-M}IIaCk49wHr=yjL;S~?jF$0qYU z^j@Zq4xd*(V9JJ_7q-(Ddg#gMlmk@ZHumY{0MPrM8I*5dh?cmOD4*^)Pr}465i_2` zqqID2yn<)5v3s201e4j%CS?V~qiztV$ieh{2HMN0NDXA|exrN@z1|CXOG^8$$l3bz zrR6Hmd-rVmc-t)DOr1o+ias2Y6_?7!l>9XXC+zr7N$V-;$6?=xu# z{$z9LI&s%Y;n}CxTa5a)fWU?E6JZUJPT0x2iRYevKi}uv zk^*hL!xS3D>a%_F6($y+B)#u?JetzqBe9i0iZr zo!PJ^4+ZX4SG6trZ@@^beG_>@h^F8P5a?4Rw5IO;H`J!gbL8K@EU?<9u+f=kdH*ek zi3>&y1wTokVKisP84=PgPCT|nnvyzO1`qo;nEN8^f&ngfM+RK8M!qj5AA?u6h6k>2 z%lZM%Jh+@Cebme>h{In!UOif&>7~8^PrVTP@p@cYkRYkhlA5!sXTOC4S)9reOluJ zH1N$2^UY7%syw#m&)HaaxIFhaKW_eS)68JTkMV`C@cW$8!DF1_>wYSEjab24u`3Ls zuOXHl^HVId>SN5vAu2F4`i*PJxmJkk)w=l4<5m^q;NHadGr$$k*cpB&BUweY{t_z= zQ}QT%+Re!>0hGY$5wkc?=BNl@=7MGgY&6^Q{>Rx_#Yk!QytUz8j_Az@XRNskUTriN z(6K5W(ebsu29KS$XOAdRa(F*k9>GdtayBg6^EX@qqHpjl-? zQyMP4?8`NbQIfnQYSGAFQ?+J)!L3GD>C-s}Pvl z8QA!*3Io0K_pQQX&m2M@+6mWv8UM4(s@`8>BW9_{>nX(gPv5y#t@?D;&-LP4Yw!SrRyiQBA2zaiGJ)+KM*#zZ9!$^K?u_Jg ze{#^&fS_8O+#S5@^_TUeTF#*rZNFT(^PigIC-sS!tm#uy!ir`)*saFbm2ZpG0z?L+ z9?v_s$IsZxKPA0qJ05xRgvQYN2@Q>PwqQ9kbT%G=nc1PyrmB6VL78_VI~@1H>W;z2 zZywGAv~hHLUxioM_^k9DqKOR741BokqbEneghB#s&SDI`Bo*RC7$Gniz*8YY?d9Y(U z;j)>)4qSY3*vQG@d6LEgUGBqoS}nd#?2qC(&X~%peeGf2L@IvcBenyueqw%EWaoY* zy%Y`b36;mw-L!KWJl1y9ZBv3eZDpik>?WB8%jI5HFKhDrNf>E66jS!cWz;KJ;A93C z;|DfzEy4bmG9%WK>zy>*bid<8cIxgv_eam?r#2EfUw;5=T>c5$!fY*=y6Mbd44x~# zLMCwq@m0m{oz%`JpRe9cQ<%_rdvOwW?g{HlPgzwsV7XuegCiccY;OG&711FjM4*?& z97gb6&x60(Q1U>Q<{T@LYSp|6fO2nxHc5Dv2I^_lE=2Jr_}hJf1N`Dxk|W!u->u90 zA@au|Jzq)eEZ2>D+}%2x?kf>lbTUkbZi_WQKMTet|A_7??^!>ScCt=32H6p-p3-Jr zXN-!EgEOrgv`18BRd{nj>qKHr(oRp;EX(@_;SRQ_ON#g%;_ehN`-w$^|1+qmukcY) zUpOw0@By_n9VNRgAH4{lt5vL?h`q~fG1BOrhnJBWgtDK%#JpX>N3m|PO9yT=PfCJ2 zg8{xHBM%lmhNy(DBW6z-QI7c*%M8B@t?mq@MLHtwt^NI;E1yhI z4le^eX_darK^b4$uj-biJ(Nv#6)B}s)=ap+G)rAQug^0ddRVyK>u8>5@)K@Ty)GSJONNY5<^4DQU*ago#f4ksUn5!fv)QmOXU?!-SST5W;>tr9&t_x*m{2$r z#^S+nag!Y+Tqk8_k4+$kU&?mPdVmg{u}pG<>T~OFtQd%+zBHuqrq{NWqBI}L|IGK_ z4e6wtYbr8>)}v|ePtIvw_~i~$%1|=7F`LmIh&DX5hiK-N=9ClAfC zD@z1^CI-@4!tUv$1wk*jH)MHDbG+Vk{y;yjTmRR_8@LPLZR>|8WUH2t()#WBU8{O_ zSYPRwA6YD&pM+*h^Za`xh)%DcD15g-LdpZ{LY6b~RuJ)-Lx#`c(f~%F8}^MEzb<}D zcZGXCg{7frFYTR0?(5OciWh8XJzxptHxe)y{5LYEW4dnLV~UX{?x6HpFIj6aqW7z9 z1B~6_7Pb8sZx)jlt~+%kr9GsD^|KJ`+K!w|_ECMD%t(v4CyKLtQak3rOF;1=sA@lz zt1#BC^XuOEEB#pR7d0vdwE%V91RpeRo|T^OT)F1o^!6?j7vGi`@u$PA-MMV&C66C2 z$XPkJi?uF10IYC0y(0e%l%K`;lKY47;c;f z46C}Re~)e5y}do1om{OwPD!OMZVX11jgzal7tjvqVrygT?&WIh;bH*wyLO4#?r_mX zO}6AvX&6Bo4BeMPmRk7MNn2RqIThr0!O`Yo@$cE9BGqtN=}|%hMvptp3I|esN_}-s~6VXG6N!5NIr7-8TYV7CiTia5lGofAN=5_@2ki$QeG(X zxN5`OirOqc`cwlKI$*ozo(l$8=u^m0CLTf-9B64Z;$QPg_x>#WUW8P}H<1lK%EC#O zXvUQ}^Ot38*+D_|-;ZU^RTxhYu3hS5SN8Sw1wT^nk$TaH+Tft;bPM*b4vsw`V5`$2@u8JM|Lx(J_khK zwy~2!UR}w!;AZuNrRPOftl?mN4>#??fX&$#%dT}fSszF8O_h7VX&Al@>(7Q)tDvzg zm%<%>spc>3=U7?!1(=cE&e_rX(NqdJKG9LT2ZU`!Kx%ev+=eCFM$S zE1g>4`$pf7{w)n}&lUfk@q*7xbabntLAYD;?#77hWw|iUSBZBwl18`8 z%4}zHPMH)HYML*H>Q$%Ax}bGrOxS%K;$8{CrqMKDe`TW#EB=Bzvj!}k#K}7n(g2;q zN^r|U)5uEi`sdpqzX&57bk=#&7J!#BRYJ2*WGf$`y(_ZrqWYd4TO_}8rr;>dR zPnu!2VSISA*eY1jx$&5nIx|*1)695;I4LCVSq)j%cI!bV)BrOlVbld(J-a<0NUu!` zYX70X+>;*TcJvUF4|V$s@;@}Tz3M zjL*b73c$1MvCv;19~-h-E|A0*qu3@{hJ(j#$>C#oAbe8v4Z)9awHRB?;d>k&rH%SG zc)d~<&6W)5Tm-cXd$3>`1Rg0 z11sHyOF3sAL!-~sg9MDrI@ix=YLB41mS4Ho(7CjuB~{{8`7lbH8-MmP^C7FvfYMhj zKXSHlgPVeC%Fe?W5i)p^m&ljy?|Df5}W+a5eF|L-pZwHC3S*&m4Jlmx0 zAni64AOoAMYfI8ll+c80?XAe!xjV68HB`m}vhv?TBhvOvi=@k_VA$m-_mjhp6J?bF z6oTU3eRi-}aBKOJmBg;Navs|Txk?42oFRy5-#;d!JBum7wMvrQR1Ot$C94%s7;VaE zd};nu$CKA~+3y7o%k>Q&Tf4U39he?nQ6G=^YX=1!98J4oYDY}Yi0wt%fFo80;#VQS zJI_1}7xJ^`lTOJ45(Ncy0jgB+Fq{BV;ch1)DL(N41W+94O{0;3mg*mN%{Y~lA3rfc zT#7OVhU5k-%!E9ReJ~!=+QV0fC5pT0SBih!qvsMeS@D# zVE{bxoF|n}Bff>CC7rQ{9VQOGeD{DzKLIn~Gnx#N0APb@#bIj<3B`UVT7wi?8GZ%( zjTa~S7Rw}g>PIKhbaXMG%E^rL*9*V6@|QMgJnO$b{|i0?VEC?t-;VI(N-oHLw)bM^ ziU5Ztb@9@?9|!>RG)Av@h+a)Q#n`?(*CHh>3QYwpM};$Rt~5Y&DW$s#i8|p%9jPeb zb%y0h9l9wGbB61)ak_;YA9@wGF7l#|hd>IPWBa`Tr9Utq1DBn{QP^^vE=p)~W2@;%yzG>hQL6VJ%?1->7x zAgr&J8^AM$zdEZt)yf}0-EiGoNw0p@g)+};gIR6S?&3bm|ATIPUoz8t+m9q1%GMtC zpx(EV1k6)ji0oZ_uA`kLcsw&w!*I~BBWytZxw7}6x0{RmeqRR@QD2s)-c8Fm?jz;5FZ{@_DJMulg@ zi!6tsbz1>{V%05IZ8J9s>*WB7O)Xwg(ZvH&91*9m*EAHFCpxq^loL4PGYksaF zO)GO70%bC<#9d!+LN0#EI_#@^61EWbN2>ei#k%rBdH6!5bBUchs$WwhD$m9IM6yEX zxSyLzNk{ZWft&V4yRyk5xz&oj-;;CCOY7bxt=E6(d_LSzd%~ZvC<;gt5BZzrKIcl` z2>bE+qFaZl-+$WChk9urcN3pG_y*0UdJS*lzK8S#N2zfg%=f7TBL2fnvnWl+I$zhW2SERNWRiT(<3itT|hlD zESZ{gHYK8!7+s}8wwv*1lN~~94p~HC0wx023a;<1tmOwaK1x@)W61jybdypclMD0R zDjPGGvNuAqRZl*cC?i(e!`ac>wL1l~qZ2X!zG;>2{KZQG>moBfIW*XOdUY<4OKt$E zM5bMsx)WC|Vx!Kl|9WR%?8+Rp*_9cep!@94#bW0} zks{kfxw@tQQeT%@>My<)$m*DY%SY>7ww&rblMOk%d5P`r7i{hxqk5s+p(bb_*Af&4 z|M6oeJVcuhqixA631_P`%RWBpkNzUG#GS%W5WVcXnsT_n8nbqf&)E(zy~>x)W&aS5 z&JkiIGoMfzse4nZuT=yb-k(UEJ1r1VudqAue2FYvKhvAyn+!CoPz5gxWYT>lU>jb4 zTjfuoLx()9;SJ`$TJ)X@T8x%tzgYd%XW+J(sMa!Y(Zp9lc9sa)&SqEUc=V;mVb)T% zvTLut9>nu!%c6U+uVDl(bvlHe{Q{F>`Z9laY`>C9t|7yX@psy3rU>})((9U>5yZ-* zJUhZ{OskBzUg`pBRtAjF*6>*^B{5%RT^CbC*~}$`n}vs@_%WOkW**uX3)${S#%<17 z1wZPAH+4RGG==!L*=#r?*OM*u`=TGrFX_qTz!NzVc=QcfI}xy+d2SNn{w@vdy6bvIy}iQV(g;lDKJMsKLH!x!N}N5Cha5kl9C zc(L+d;^{lyuW*$vwZ8qc)$-vn_qp8S0?Ys^S7X5^h@jx)rC4aKH6XrSDJOrED8JpV zX}d$-&hc%fjDpZ zy+*anelp{tDL9BZ;u1T%rydL-Lt>wwlkE5UD;j3iGY0qzcVm__;7cpHtAF5~d z%-mcsXE<~LiNpRMqR#W3?f-xKx@cADP^wm2EwyWJ`RJgks9m-9Ocb$6T8i3LMeW+P zXJW*tUAtxwlB!Jxf`~|RxsKy|9KW0IKj3}i`FNe@<4h|)I%1}*iH@yTOJstdQTj&P z97t@#he}Nz??qpRE~s-ieCb1nSVl|tdR;SIP`^4esRDuT%;=X-vu9NSLF&$wx&~9pwIrxg zp-#JMm)5KVH|^WLirF$KMVI!9o;jf2tmIVP7@u!-_yKbAG|-BU4JpFZ&$fpqg}oa3 zxEop#2rEB39!_sv)_C)W_q7%w^iHxuO_=;x=76wQwyv9-y8LT|871VmJ#J|F=O72? z@}h!B%_Jqkct-eLITadRl42dTz9ZuBWv}qvkCE73j-^X|5+H+1HZW%74j5DZSZlt5p4Q7EZ!@kK@QQ|)cg*ULPwTOMnm zoC;{a2m^K)_FUa5Fw~s9(9s^!dDwwye4F7_oY&rXXpKyLi%hcNL5V(ft-S`F zHp3cpIH>2SH@x)_h?OGdb~=6QM-MHwGACw6yie;P?eFr^sG&hS(LkgBen!uaF#y>`=e#8UXm)2UFLk%0-?p;ZG}=3+g^DWe0OZPiK$7pm zk-kQ2Tid%Oi#9;5F+VnFN<+nrZXJFR(4=q&P)vmFuvfKg^d6X>^`1kETsji8vzffS zM2DVzJMY`pUe3ta1bK3(RZ78wn_b#Zsl<;H=Gmf@lJ%XIkOj3eW~!9)Y(il6=$y0> zNps_vXV6s+UNUwg9HqNrN_Lmytdphw$VWwn4h2=%Z``hFHic<#C6umPJuv2Q*Ry=+ z|D4HnBl6?LZ?HEEMGKsmgVfAdXYZcAViJ3Q7frPQY6}bfp%q-AGu~^*J@2tkzN|Ko$Vm8no3E~^p9P}hP_EaT`{H6)aTrnzYOL*_wc$_~7XYvwdDyy-|r z5Ki-4dsA+6M{pnxlmsS<`!2t2#WYFdPXsQX?yJgPwC3}@iWQM8D7ogAWu>XQj1dv^ z@_>Yzz$Ttt_+Qq+`2SGEjWZO%5z1%#5aj9O@8{~`=wN5(_Wq1OIJmzDfjm9jKpyt? z4h~+fbb7P>YuNLoy2t39w~x6|W?v-%H7jLJJt{WpM<8^2rw=xvq&;3WELbVDb=A$@ zNBO?g6Tkus5OKMwB>a<=m^3DT|I>Mi(jvtfINNAXZ+Cl^N-}grTmc{;>5DhoT^5%3 z8|_sx8){qs;^@W4hTGY&Csj$QojUt3p<(8J%bt&)FIOS+4vvPqe#!*^Tmojm4ZOl7 z;A2Q<;`%REpaomp4FFhy;{yGqi>O$@kwlC$KJQh?{+PIlqHkAFq&iB{Pb04ES+fq} z3gcwKwDM-JLsJF5iqN#IW?od~c&@frP3gr{I`2q8M^&^{8fPvq1-s%wBB@zyH(=^4Wo{v8K1Zl-zvc2$3sNwzPTY*HTU@B>2ts0)TVm%V} zd`zGRHyo7US*u}O>87x$b65F**MG8x^L15b0;V6+B7n#+7ZRa?jW;i7WGNo|Q1{F) zE|$6es!}$&C->u`r9!9*cEjHWrkmN>rlbHrENg(6xaP=B2JPz9uf&BpRUo&5@)ZK51>pCyYCk2tncF0rPZ3yQh`)L_v&k5;nIjC-^D@lE5-<{RSY zri&Rasr(Kgyoi*ZsKzJ(6rHf8O*pDYkDN?Zo`3MlO0`;^-s{!UfE0ryE6OH<;3)Ib z_)A^#u!Lz`JN*?@qPnZUD#as0$buhft~B<$w{W-kFyujYOB8+d{Z$j5z~i9B5?Kat z)$`}wFEQP(xV+Q7cD81<3)TQvI2|ag`)TuG2aE4r_M>;p@5xhb^gv!UZlZYQHykTo z@0&Il7024d6~0wtuFfG_g8y@Rv|RnW9p8Cl+$W(Kh?^>;O1j16nd_gR7Y&$3s+wh! zix5AP{-8tgCrdW5D0!4G90v7m2od0(vfNgbnUl&zCMS&z&>Wv!=D6yyAtTuM^B`Vz zM1g6ntaIE}#amgj(+4srzBgq|@(UMvO0u1X<8mUp`eJ3(<%TC0x$KS+ zi;AaPu^S6|ea}kW(4!vdNiKI`b@&{@x}92#Fx%dr(ZSiHufQ6&fij)Z%f8hIQL*@P z-FMEP%;dOa}*y8ePbf~!Vm-yE4gmhvh{(9m>mE5_-y3a%RZI!$KIptBbNn@Q# z`jOKylA296>rGYa;|O`pwkBS=ACno0I(c||tEXM-EDhYv+T$6bb4~7AqrT+4b;7-P zI#9aba2>pCby$Jx|GC3n8T&G(7OG)(a?XJWt!t(@Ptwe5P33ciD~xI-%q zF$?KTy*nunKk^v2#s5wmeNb##U?gE19~JGJC|)!cfoo=4FS3)U-Rm*VZY&cXoa9BGgPJGZyqkIVQh-)<_LmDiu= zYhsWXT{5o#ls@(%i~0liaN=^n#NykDM-P|$g}4<&fiUiZ{gE!{u8=udsFd#~QOYwe zjDG4JhP`<%=j2Nn)3|AKTPvSI*8NgTx)bI>88#GyH~G%cR`)~mn_e#WgGqjL3t!71 zTVLn2kX2|1nZ$xe1i*0Qq`C%}64sM`16%9eFU1Xf%O)L~ava-K`S(wGd$<5EHDh81 zt^LxFQ6bAz5)578NJ-5iTDoXAdKO4`^6i=LRy?UWi8TKfVZ-FLdV#YN*31xJF8K)t zIK>_n6z7nWjujD2w=^^Hi8WB<9UU`$)nT-Cx!EmB(8`@!45&BK=F(YmpdU)%s@K;e_!OYh&FL zhMQpqk9)v{>Q>Pa_9!|3w-_NWh~&I&)*9zOL zc#@!qi+<<)h4EA#xT#$!Ct5#XG**Q6;lh|lRn3Y@j?7bis~7w~EV2?AE9+QZ^_L8M zTVI(>Xh}og4>9A0t2Y62-_`eb|EZAD**S@g(l-mU$@!)(o++5kdgMj;&$wz&zO?1^ zWZ6@yadPcpYN*WUvAt7mIYtyooYf7Bv3krKB^xTt$@^^L=f*9Dq83H*lxf;iD=YyW z+vGbgKq7^h<#9176Vl*{@PV-wrzY2N?OZ&#HDDin94K8+n5b$s4}w0f)?Z!?G}tgJW_BGCzGTz;(m`9-WqhqXAuLWv+tv2$}IcPFOHJng2jDHX@YN{t8O0p9ZkQpLqa1TLL8MX*ZZXUH!7& zGZ|Mgb2YPY{*_~T88_TjN+@lFelDdtWKmA$4RUS)Kzg$!z>x|Mn*Hk~tpnE+KEv57FMFq*u8#XiHgY zw;{4~$0#g$c0!T!gycLn@sS5Qe9I4n!nrMqp5I^kFhhhrrqp*)#?(dxRF49q?{%sP zrI}5|oKRff>OKjDg<6{MP_m@5TvXOLeCajtx^n2=xulR#H6;>_v`M)N`>LNe9&twZ? zOT+(m6s)~Jr7M`ee(zZ&TWLG<@_Hi`(qW^ol!Wb|z|XW6OYK3e8122s3el;u?nYC^ z?fE(QA)89Jr&VA7LqFnyzeVZPkj5LrsSEs+u8cnrtR!}WHvnodWb=9zT1{^mqHE6x zjsi4?k9gCR+jOpRF;vz)8O_{~kba17=v-T83oagz0K7$0idLD}rUKTW4V07JMwi3a zK&`TOX`TI5H6vHDv?$|D9^4l?p9G6b5f%gZGA3JR-+ehny|^YiYzYjq>9W1$##y8j z_SlBQ0-;sC!UxbXu5#Rku_rTT*U7TT!%UeY!BA`sWLJsfQTkRtKaoH^EIi}*kSnEV zXc`+;Vnw@D!7UjM7l$wD?QPd*d1v^aydtd9a9ugefCktX{H%6hu&M*!>p-2*UtY)a zl85!#`!;(3Ikj@{=(~TW#uJX7#@EHNmzI(3WhPVA?ry~$j3K%S+*lSoYs1@>WuJe* zny>mv<(arZR==>lBvO|UV7mT;%UWA+?-W~!4W`Z{Pe5t$y13ZsL1~niC~w}3-XbD{ z^lLwF53=m7v%GLRdN008l@;}D>~QhUq!qk}Q_rqdK&>Tn)L|-ze5kXPQhJV(?ioEi zI2beL`a;(|oK14&5d@GsFCws+{O1u`EnpEjDbiT()zqM@8lum(vnH^!A@eQOV5RK) zuM6Rh*7KhjLhDqMdH7j*zpXP!##QFM@`G1xAX?umAs5Q`-pj6!IgW*uPrx{$?Ek$| z0R8>h&3|aZyAo5Fs@Dqab{_r&wv5i!fA~1WaldTkF~Q%@l9Sre#A$Ucyc85JA13Ht zEkomIt5P)Uci~_*q>4^4TWATX{fF{+`kc`*TaoYu{^qGC=i3 zoRKKG84pNsC9zaaBzAtrhAF?Y+WQK@G|gL0_EBlrzDG1(jhRlbyVCtj%J*o;{EQ>i zmej2T*~PT4Gjz7{Liqq(P18){C4u|Ym3M&Mx203@26>aCU`&RWd9|U+WNj_ZHSNDW zu~u=p-LL0P!+Kt`T9x+rhcy3i3C}aB4FAPw9f?<$wqKyktTo?ImDHaKbd?`=f{D{w z$a8+2jdj;EQw|oDv}z<5!$LTwaDVt*(@WctJv)g3ooY+<+B`Z6(=RQCy6yxOkM8Ls zoZGp+BVHIjocvR;iq;nOyp7ThU`9c5*?E0D0Mox+a^G{)77D@kKWTk3riSsSpA0Xx z372_9ewB}7ueo`hUn2C}k|e%0Mp%ys=G9!KCUqJc=1Hi&HnKd}9~*!}&DZ{fP!%!+ z)v)WG!N2Kw_4=V(`qUK@Xy!jI@6es-T>t)r-C4G!z}BLWT?2I+CyLsDqC9ZJJXF=O zLWhYcLXa=!1431iz~r1Ma7UZTPsvC>`=_~zFS7;PsAt+p#PZR!Tc-P6g_602){uiu z84@HoxUh_p1n9^s`4$kDG!QKW<;jDa+K^BCCJt6z^@)F)DHQqJ^{ckhU0?ac-7!*t z3@xUx!XU%Xrm1VQ^GrG%Ghm7*OSIJRuCXj}&^|Mk%d|&U1aSD@Nsc!JmD|_%i4yHH z&bbqJD=CHosNKx!|IRa}aZBu}-30C9FNzYlv3a(md`C-taTfiZA2uG@{vJ8v@^I;6 zU`qR9%%cy51LD)Y;{<(Iv@Ut|kv(HaV@HO(nK;GtPh%hm!;0K(NqUs4>6{mTX9^rw zxI*5jT+~#(kd(hxW;4x^EeXf@;2Ki+Tev%7j9%7X&io!{Qi9AoR__ch5WyVAc06Li zpC>=mKV)tabOhrKo0H?~xqNJzy zOLup5J!j{X_rO-IA!x48mj(3KUIv_GEKEf}--&;-aaU>8wQOE_T&Hmux=>pEvj#`ZDssH9$FJ0$5EweyE)^G(J9h< znFdJSiofWbo2wp039mG;8~d&!Q2)V5R4%*8WU?wKMusOUmrj3h3?w?~+p)4ce)u8pC1>g3<(E13`LQtBafn{y#lc^C+(gjZ3!66Mho zv$nyyBpoO6tTXm_h?Vz~v`0I8$rxWq`P`HJa$*i!%^}F|)w1ADcic0RHS^7^BaNRD znDHBZB-zcIgfQfr{~0P7-2RVA+&p6vQlT?nAD*pcc%6;6*1NczkqAErSD?EG2;}PS z{2mOnwYRagJM&h$kJPazCloJvCQKqgBcs1JKL13&<*XSciVHTmf+xEgJ&LKcU32e+ z#KYI&X)UuSIq@+60-sg6PP5eYGI0u*`hq&#hQqm|1qPnEgT|%nXJyqW-=ym)$B5SKA zvVA?bC@8O2Ens5qSTa7w>|S;%sfO&VDXT$-4MY{x;9A_=@;nn&emfI?7!*vzIwKxc z!X6J3d;CNKt;k^l8-8AS3U0Y`_)si*8goPn*$xeJ&!8NP)yw?T__qocZufB<#;@oN z9x1?MZcRrmLeM`ZcTbZyzBz?DS!<-gqf#;48v2a; zXj3|lTLJ@W{%&Rc&@+2`+1r=X*P&iLE_H+S#`oK1pw5SLFHFtGY8G8c4gv&B2op>_ zzRvR;*4N0`a2;|73O(4{Qi9TmMk4fGurlcM&&kw^3&f-*0p-_bCX@5ls*yK9XJfP$ z0YoJE=FQ__EVJjCNWlp@ftLXh5i-LrgN`isLdtW)ly_3=Oe$YFw~Nx3TLQbQu)0dY zhENKQOMN@zjPMlPbswRaMkxdU7k`H7EJ8P&)q0_ zZ?isnA?M{%w?sw2FwuJ?ZpCX30`1nJG&Yk)a8~2E7a_M%Bwr6Sa;gC{+qUi!L~2eS zAnxZQ(f3m}Iv^pp9o(QDy>;#GQ7eJ1;KE(3WQqS?&iCyn)$l&NEefhSZmCQCh6T`6 zv4mDKB^M5~*qB;bE(}%fm=T&h8v^t`BA_I^0|#MO`d-Pb%E}9=Sz0EdJ9h|4f&huzqIRO>=Me z99mIww4uzqRyue_Qi@sM9gfc@a~rQSOW(R5XVK#CRv0$?fQ4X`()1>TylhQzCge?7Xjx=7mY0z|f^ zO!qsOdIWBaFvq$l2j)v;Q&Zp6QU!Me!PlO+5v?|OQpo@HZb?{5UG-nDQAdVtfK`Il zIA58;Nn5p5j`faSe=gqC_{iMW^3wM{&j*#mbUB;3CbF0*R*o>|zm%)Hgv*9-QK9E3-)F6c$oj_ZeKYZ+#{DG?{1 zLw>A15aDX0^8SlIG$$Lp5Nw3i`TCR8 zsH%PPMxbvh`ujeaw(aaPdu=9*xrERPdUwl<<(*=svlHzjo|Iwz@O_Pf^b#OLREt-u zb8;{#t=KQvpE;xz_9^M+myt2MYv7jN{6vOpf<84-1HAq-c~VX}q{I`uN;e%0lFi&v zp&A$u&-t3Ik~_DzleIfEI{)_FDE?2vJ);Z@YKpeX3h8-`W0GdzubpRZqdTjSnvKo2 zO}zzMn`IOLF~yTx6vSc=gQzQTf3IhguWV;m@NNmazH)+KBhpHZ*llXY8{3Ze?pF+S z0&0eBFc0Bx`I}Oz+g|R+@P=Tck^(#dYMfy&=2B~KJm!RS%69{hi34Z2 zM=?kHXLXK^us0DcPyeEJRyAmvih8iAJ&wWSxNhLM)fP94Zk#qcX=5}6e&u~xN z{Zcax@sQO2@_|cl%Kn`K-;&%VmlXJdy( z)hE5oYqx`*%aKOuG4zQOBhJ*_`6Dwj{G93bc6sTl3VMKYl@ z;;-a)y+!@4kCalral2Xmgz=D}F*|zW6p^CD7C^w5T^HTsdJR`;RV_UWF4W$evJ8IN zIN-th{*XF(|FHQq(-GfnHqGuSV&d(}3T$jP&Q#&*%rloTnDamL zZaRIU-;3M(s1iQ!5j^EutmVYI;L$oGKTC#8zEABR$F0+N&HQB%!zJ7ul*F3P3}-q7 znC8|Wk7l}A(h?pDCDwO1K`K@i3G@WjpuO+)O2>v@q0K!Tqqcj>`w@K$>y)J`g?OgcD z(U@WFbhM%Eej02A@Y+Q3^dgE7v+-`^x`SUR=ENt?g`1D_8quqKYu5v#o0wFuA|*Dtyy*@24ygnd#sE2TCv1$k_&rFNy^IZby_15CLZe8q zS1j!PH>WfodGs4=Q*m#6Hh3~@u8y+xOG3@TNXb!n-(dl?h6SHBWJ?PBM?uPCtP@FZ zcNk-#rwZ0g+zbEtU#$P}ZbsNtC6KKv;L}w%?tc@7w#)l zsqPlUz|-37IOJ0~6>U5|ELk5cxghgPBAO&Jj)8hse@7&BWF8+j7@)%zLD^v|N04+i z8!;!ZdO$Gj(#~_f`^psvrEko=7r5Z6m@PTQ`qZ}N%*1{1gzEM`ZsC0N5UJ<#fN!`~ zC51YD#J_1yn6eH!^V8h<(-xkz$;j3;;@mE<{LB%x7wN&iZ6c81(EhP! zoTdI?!z!q!+S-!u%$+{7?4VEn@|3Z3Im5s1KK)G%IU7Q=h2gr=hxdu4L&Y# zmtJ!ijuK^khC1p!>rR2^H;)a3tg$fwdWDT2FRC|&e1XVTRlFFeML^*?w-UFiK<1dO zRZo+iW-#j48ZWYnfzKGB_k89(&k{)7q||!OBY`X+MUsc8*Jc>43?PQcC_~kAZBd~F zl{Wu#Jk{?nmQU^30YWKS(1hvE4uAGG%QMZ}i^x0{r>)j59bjRZuy1QkaRh`qITzj8 zIx;~zbNxqc7A`^9{emktvZ6KBGeh{r12K4F#{t3mJl!YzEBP*2Er#W;mBswW!E*IGW{|GMVW;`D<{4e{343 zrXOxmt_qyr&^~H{oXU7?Va5_g1? z9&Z6xY!2%I+72y1qRCfZiHof#I^R@IDw9i3`Wa0aSr}zI*e~GAxgJmfkk)steYkG- z@o1~%pf!-Lo8JLINuI8Y29^NRql2W6t7Dp4Ss6W&##QTc9!#k*pGST>aX= zQcBO$EfPHz${sBn*faGNZufc?3|kedhGl!ke?_kG3^uJrU+LV@fBoOhx{Dp3?GzvQ zsQo?axvH6!)Zf;5`h`#32tAsfcXXr3hEV3OQqiYRO_`R1X_vTd*=@yzg&q1{Acj6^ zhzkh8b{pQl1@D^b{e4UXXV_>ZF)7`)dEfC>OC??Un_NZh#jxy6)gg~Z48(4(|K%be z|HmM1oiPXtNWzW#XA(+lCtDXc5D0um8@xPi&c4skgIBQs8JIXTLfG@v$6u;8JlDB4 z*d|b&z7Yyuz(4MA5Z&24+zay*gI%}VmOL3_R<9Rwyp^6Wc!e>+t~c9){@d&gur~FT z+M3+qd(XT-ZNVg;cs|Xf8+QptaX@U|AftZqP~pQ;rMB}dLxIRv#Pd6~)WcDtl8SFg zRr(5k2);$z&tEtClGdl1rlJI=`2Ez1NInFuFnXO`;VEJY6WotFR=j0p_WMVDhNKHt zOgF{a8U=4Z>DCawQ9=pkFy3>hAwLYJe0X*FVs~X-{=Zhmd_>FczEoBVF90NN7!TG( z8>9MWjHRQi^@9N%9r#Mip9JY758!*=rf1$kYeBS|Dg!mCo_t@h04fCWro2RXuV4dq zO4`N$K$Mibp#=*y_=5q%7Nt0PFMbrfGgBBDB4`8R+COL?OIJAGbbK5bJ-twu!=9$~ z;3%F;^PKF^%L4N6Hm66K&{Gs9`8NM(9mr-B=wDff@>i4ZQA(QE$!Z;(|8&7i9ybfI zVh`0EWO1x%oNWCtbkAmZlW8}fIqcsBKQl-zXD~+;j(455F!xxgHQKu13Vh7(VbQ_F zOWFOKdl9t+;tq-j6su4JSi-eJ7n{)s+3kqW9#zxJYknw=OWb`vlnT?1&nM)Tlt%Ro z-|bJEIRWhGqm8NON*7ofFycwG`#I#6%m%cP;O%vO@#6HF| zg#~Zu@zM!6I-UCDZ|i#D$=%I|H9dE z@I5zL=&#AdgPTnu+f9XO@FHwLPqpOjl)$3tA06W+yjkfu2cLg~YgtzgDv~FM+2PW~ z@tbB$%zl(CR74W$PSlPyy{SXelh~vcclcTIQpwJVf0js|wMP^0l9IKoZiht4!xR!; zbX%PnjqtP^{MiV`Wrx&ZAFy38bYq+`RVge8Y1>R68hRENpq^`7CR z+Rm5dtG?>lOVFbyyx0?eRe-~6DE3EoZL7Byss*L}ptia9SMG8nbdVO!{GNV2C{?Ki z%=5GmS>8U0{MfhtxGcuQbDn;Y)^(fPq70m@-^=|z`2i8jvY*M`f|2Y!> zO@_IZ_fJaQT>+_#nq7gr?~j&pL29zPfUTI{7cUNl%i6~H-53Fo8%e`ZYU8}AwbfF4 zJFlg68#OSftfGv5{gV(A@%IC>LE&5={Z8(5FU-NImu-`SS?tq^^S#xs+b%ibdmISk zC9FIr_ig%XN&Z)BQyn0ZRt1ZLds|gJ{0TQR_kTBd>UkI@0X#pOck~I?iaBh)p}3&h z{llugtbu)N`^w>s-ljF3Y9VS!p71r+uJnp-)DlshW1W!nCq=cuKSKWxnafB01+$s| zaKs_|Eqkc6__hf}#X*};O|#Me56X$n9@(Z^t;z(}u08t_aZVO)dhKnC=|zJ+iX=Uo zUvt*CU8j?bQs7)_aarkE%ZDRQ9)ti7MNeS=Zt1^?k+LJ`D%&cD}bp%^{Y@y3`<+`M7v z>o=G%2cG9DbHgz;m7Oo^Zd=?#fsRJmeAFpp8=jMwF4kvX((K~`V$=sx#BG299hSTn zMo2S^Y!FWkF})Oa^pNi;AY>nUcK|MTv;Ih3UWcuX4JcvJ zroxaU^er*ABb6r4Ydy{Xk#G;Xy$Z9!%`#gnY-%o?sOk0`aq9c_O@Xqe6M71lW}1rQ zP8&KxW?x?`;|**>8|f6CJB1m)Q2cqVgt$+B?|YQd6j5P9SG~Hvhh+=stehFhdi@>=`UGN}BIm5(xahHq)hp>!cmU&rST9l!_bv=SnH@-)J2K_q9Vtd)N* zv`VKGa$N>-uIfg8nF;=AvmpMsF?x5X(=hhrGkTxta<`@9PcSbk$>cb!$waC01@~%Q z{5g}!v5WR*d*N3#{n4rywQ~~30P>c|tnVegv^iBTebGT}Ohw7J2#rHP+h4Wc*`l2g z=L8Kd&3vO=+YsCtA677FiC505*v7zJd(_$!5>Ze^rEKk0G9&Rm`)Qs1Jw~@;Z4L+# z6dlUiCf3QgKlP?YNa&koAsU;iGF|W$GB{grlH(2FLifdSX@nbI|e%a`SP|- z@a@jA>4k-_h6jKADSrxwc>Cc;+i42t#|Nh_hW&*FrAlNWt~1UMb*gCo5uzsN_2ha5Wrfcp~2GSuhMJtaS-yTYQq(EeLOgpF^F zier1mY-t_3fmtojg{H+hXf108vj5%(R7WcQAX_NejO88MDj#$_zt#(T&ilLj{KO6* zOm%G@n3sS}t0zz83OTg6Vp`khZl`Z-Cv#fvB=Rp-S!VJL^O-O38U?BIAbs;A%kG?v zZxs?4QI%@;HRoMYl7aeIKj^LeIc}(F8U1wm)c=h|r1Ra`MFoIQx!J6YDrd1~v7a`% zx`e(_62UNC_naC0`-v?rYrT`56$4VmEpoEr+xeR7E0K8star9q(;;{n$4$oJ40*kl znk~L$@BplOjrYX)#+ZaMTir^qBWGMWG{Pr{8>d3ZZJ_ggV#*;yXXx zz|oEZ$9IyXBZYJ2)wY!=h-&hd3OchSFla}C&h2%WH1(upoJZQe>R^>Kg&Y?%Rgt++ zR1Gf;3;sLdvf%~@!!-(Q<6plkO7)D_neN?vse55?c&{ao=%U+3V4)cCe#Q z#{0SkEjJ@&>%)xxDekGphUNLo-z7@FmTrlAaLL^LwZKAG5I@?o@Q4@f40$k zc-H(9hqiHSuCRMi(Qu41V!27?%@?wOhE%f!MZYfj>AP$hc9ZXHek`v}_fo&h7PEYR z9sf(?l_$I?yDQeSiWbn`nWD1PqEw6D}`cy&H2dL3G4T)1E`{o*re7L-~UB!)@mX z*IE^1XC9HBij?C8P2t&v_S56qTbcA*BJ0k@+-$=#KdI#~lC>3+Q%w;@T!VZzV@@93 zOR=`JYq_-Wlc&FiAp72zGwbTlmzwiBXb#ymWDu<2xgqvjtWIu4>m!kan50&~ha4=R zba2w`rl!9v4xIxMQ)|_7~Ya#;E8N6)h)z`4`cDQ2}gvwUvCx#}v4M|KWSuZ@}iZ0^iycQ>-G;-)%9EU^YbXA z;$4T>J5nW20W7SopdXf+A%BvritpLzd*)nIB^cw55;9Z(yx#tx#NK7uJEC&c`jVa` zFOyEu-C4)%{srabU+gRJud6Y&r$>hG@)!l`cmDA5ak)LuE-PMRONXFw(&b*jH?-=f zLJv(W4UTs}97>z7)>pM(pQBJ?2HdYTsDej8%i1UUBM%<1qS}3zv5*&68<1#^E&Pbi z!>9X?_?|XC;TkhHMDpf6_NzI*nWUt>1wX^G*7x2st9Ru1#n+t;V(hp0h;b)0cL*n2 z{qVp`EeH?dZrAqsSj-Ow9s zDMw3U{FqO%NxhK3njC^bqsO6Iq7U9;uTWN@-}*-oqIXRmX~X}EOOA#Pgp|FYSetm@ z<~|ovGdPlR#P^sl!MfLU{P=>sN{cB&HbA)}wdomWFQfZU0OFBoo(-J1wOpk<$}O}L zL`)@6{^g_mju#hP+B#!R!{GcS7kg}(DWU>o{~i*<&EmtT{)^mQ!8Ub(DIrAq$hG_j)sfIE6*1K#@X-~c6;)Bpcp&m#h8Nt>~T578qswXq+$=kF|W z+Xqvbm4_H5jk-|kWaIVduCjQe3A-J!iE!RSXjQZJBZFdzYyXo(r2HR&U_bjYi0xK# z!Zvn}jt-t4o*uSmP6#VoAjsqYmoyxJoext;c&;7iT*=8*H7ltoS|V-J=>% zdPY?y%xoFHjJKtWT-}z2XZ!9`g%x_<{8?>T1XjwHNGRvsc<>h&m2%#x+%k^;#qIgk zx}>AE#TxI8(iKf0Gbw?ygJ+mCA%CgqKy(>9xF(>|Rws7u1M^eBwGyBw~B&>(zB zJfaoAR0;7cIdRkMelwMuelSD~TZ2PWr1&a)`K7P9*;iB3mY(F>?W{ym{)k;Y)1ay! zCzfT^c?O{w(f6igz&CGwZTwxw(pYz1>N{_G4*&Bd?>(%oy|w$em6vK!wU_1L-30PT zg}9CXf+$F(fAu%aR;alqRV1|kNX9qm7@7Hs3~t>Ykm$L=5%fS>Xr5#S4Z~Z%uwHJx z_|t`hbdS8P+ZZ+y*1GiWKDI6@8T^4+DgA*EV#bS(XpD{X$m7GYc9B8T}R3&tW8jXJGpfI}d`}2?u zecILAN4ieLOZSpo>sN-}7O@L`^WyJr?O?)=>X7t+M6LG3q+C_rohh}BFpJ`v*`vN= zc&cIE*qd(R!Mg&fCO(A9*hQ6AyCBC6kbwqt+T+F!w(F0BC}rS#tYz?DQ@`5(40E^*Y|6E9LTcT`o~EY3l}YBzxyxBq-R#mF<8jISL(}hU5qTR&+rod=6HbIq8>0xWyMcz z(jn{KobPm4m5sOmwF_KaSLc-pR=cm~>EkP|60iyQ#3d2u8%45u0{IUEWZ6Y5dVQ&A zjh06oQTJE(Mr8lv!Q$An>ufJh@_hSYesqG$1FzA7#fM%meECZpctKe*?&oiS&m7q9 zute!Bar>sv1D4RA-&EW8(!SA}5EF`Wvy;s)@&rBjQ;OOV7NhMOCOHPQv$C?}Rfwp5 zGu!OvJ$vUTj76|OhQZXLz{-Kn9jI&)9Zc$5@+$odlCc~}^~@RDKZ%g>3k_zkQ4b1S zjnIsOMVpH&^qDzqK$j`}x)&d*D0qC-L_Hm@N~6;WUkZ%|DTm%Z8l3ZDV|kW{z z=ZadgzcP7zP5zF?*?`&5ngO8p_2JpH@rn?G4p~@6e3nFLu#9q618Iw=8vpw^-picZ zr9DSl*~DNZkEt~tB7&2#q=*o zd-YN>zJ`X&$vc`wk&meZRYf_Occ7sIcm8s&te`t)X?`xO(J<`*an_~4Z)pn&vHNKz z`*2FFtK zP(|_Wm&c=gK{=0sx_XrZy8HFSV0q-NcV)%XQ3i!q4k@7&Y}(TUZ6D3kJH|V?&V7Cn zJKT~dTn+FXCB@%OiB`QDhl6`_?HNIxANAy?TW(4L3+ELwd1wkJ2m45i@o^Mxe~;>e zP{0L4NVhb>W2uJ|;D$|j1Nr3SP#M{FB`knO90ZtcErW`6+`Pq7dMIvYtEZiumyOXMYaWntULYPi>T$?R0Ex+<9cumb5v+&!@=jw z*qZ(e6Yto`{O(7rHG~6?8p4r}2o#sf$d6vWsnqd8D>%Vt29!D3S#}rrW;1wJ3JlmZr?^t`NET=X8%^%$=$-3R6JyehdsU&FT&|#*Lx;q z8AqqrhgMJP2~{;f)gH2DCUYvrfDf`tW|MT~LbdOkHb{P1-c!Iy49}QyT>JiC^vpY% zr_BmY(}tAlcBO?@l}K~%5rqg808}xoYdtTLd!7@pou_wUwN8w*tjc>a@|PE`R&wAr zOrUdqeYDkAaB*8T@b<}KGDSwWlkS*K)cVtO>1<;MrG5$I;8Mqt-j!(Lk_g%83TP=u zzuTup1<&HlC9BJw(i>RsO^@_IN4GG)hSQh#+lY&>Z?UTen~%QZsacPC{Ck&>RuVUX zOu}g$C5oD292+~7W*JMxg>?s%w`>?_o;$$#UqK>0$Y;LOAN8ZU6>ppBzr;@@o6dl? z@*@oFZcQ5)wC(QgH9LQ3``PV06T?sm3kTo-C}4lGA<(48W|3#KAqU3G)Qi>t7Pl;S z-Ryx@7v(Lu=3N&b9w+DwN-0SF0!T<`BjxUH$LEiq`E=l_C$$bXWoenWVOx;zq2)%4 zPKWJO&-JclzV8K6W5utFV0RyUY1EB*J~&Aur{hm@U=XV^QkZNB#u7Ab2m_U=yq;Sr zy{(cR=Ql&8By81x6;25p6E`#QH){Az3v|}DOWWYPSd^;s3tnj&5c40;U zTwqG!>AJR?!&#Q*>rL5%!FX!&#tf(WXi91ENmAc^V%u6$bRD#;!t<(yV0AY9-4=IP zNXzKv!ABXOjYVRncmH11+Q7mwLd)MAD$W@`(Z~6AIk(h~6rHK@hfKTY;lfHxI=;t3P(lgBt&0;N|t748e$K!A|Cp>f+M#qG%^2-w6Q@pZo)p@Deswc9a1`}Y9hh1N}LpnVXvA=SXPdm{Xk<{JFZ>n;6t&`58 zxBqry=fP-`cJ6{ITt#7N-O)2Q8m;^9`EV651Z{UD96j|hHkrI>3&vzrc&X)?GF1C<4U|;z8c%%7m%nRw#-)FwYL;4?*jAUk*Q+pB(iSi~XhA&3)ToHi z{dupYwXuJ$2hMWNV(Hl9lDc>^HIAlf{Qo2BtfQK6-}jG+N~tJHtCTcI_gj%J1?d>w zT?5886$KFik&b~%x8xWjq#K0MV@x_njWO96`|&&HbI$kw=ilc%_jBFXb-kwWYIbpk zf>KbVP_=vl8TGIac~m_OIT7@+HqO2wEn6i{`#8Au(>)p?o1Y55r{3Y$wYG4W`CoD> zZc~Ow?FP`mz|5|?%0oszv!J$yb>lrVTkqaUDXHUR0h!AIQ$1JVUnS-Ys)dai6}+VF zrTi+6hq&5{{eo3KdnAT{$fO}<#yWmf_1VwH7c?IlzSqfzay`+>Tc=rUj@;Y;oP+6r z#GO%7;uPu8b6=zQ(p8UN8@ScR>O>|zIVfkwyq``;Ynxf_7ZZ(OO~z5Ap(k>E9BQ$b z+jCqcb>h*wExN&dYP;*^kw06!-ENsHhNm=)!S$(JY*)G)z7Xcc<`NQ*)Xx!qDW_|J zpc<@~b&A8Gm>e+_a84poY>LN|UP|JwKgVSV!hIRJqn$pltt^##if16Frp~m9xf!FF zs75)FswBwE7w?@9WS@$Yo2$&*W#UT>HSb`h7kRbaFYwi(D$a;USB?ER&!`mWxxNt{ zj!8Tb*tf8cs5xqeEN|$CTs)fa!gC<+G%hAV)`^fc4GBiqT5swaKfH+2rmQ%Aua1v( zSih!bx{Er)jy5*`wxZ*@Bk9rv*?#IVL4Y-xl=6Nx_4567Bd4Kc*X1tH#B#{KngUkG zq2+I#vMuUF)sLGBvH(87H4GDyhiCeBe6=v*?O5c-vgZX3v4r8MiSx_9@2k7(A4=`E|yF!$! zBWb?Gm+^KVYf-A6r>omUqS-Us0~7&}{{ox~zS zAlM(LRS&C*Jj}&+#+&a5Lww_XV950nyAaiV{K}A6)<`e8kT-7r$*YvkSxu;deN*Ms z+!CYoy_nau6jl`Dzl6%Evy|IV{}tLG{h{{5Q}%e=WMIkEoy!G?KXq|^F3WH{YB>l?d?Cd16Peub6vNct4=o6Jab4M9lC2wLBs{hX znC*DRCm$@52U+OyX+X4@V}T7mZFxW3P=LEE9!DJwngXJ1^$kt&`lblarz13tiyl?+ z1)|d}iWq1lspn|4F1WuB*De7YxwzTk2OciuL5to$(~R)Y@6ny-cK9Ug5?3Umr}Fjw ztla5)0jHrl;x-hh=1ptLTu^uJ6a_37c9ejOJch5PLt<@OZ z{;dEgPOVa{^&T?Qi6eAw-Db`;;I4o6h^0~w4{kAGpWy&LA1HTvzRi)0#l1Q;gLP5p z@j7yC%s7PBMZY~~DrZWjOK=zA$`Tu$`gpAwDfZMiKvsb7%o*kC7LwQsTM2~DCfkYL z^W8rD7tic_>bU6^-^Co+l;3C+?qnZ99UFuI4&XP(j-!Wmq8f|Yx)MQ+u2#()kaVo5 z7)kKn9RDTe|G6Sc{!f#*f1yeEmUj(4cJlZ2a`N=>^a41$d3w5gUa&EKQmNGrZcYx4 zj_&TRZj*@j_>U!Sgj#ky5WuD|5`(D;UM~#tSvr&v)bmHr{!*;E#^bs@-*{Ev6u0t& zy{{{u`n>C+hur(I=_lg>p2H}$!m#%%WViQ@Hms3mN+vG+s$oLD6?d7m6+hJGZgoAT z8SW5!&tsyp(x>!u4SSw_WCRRHI+j4g z8r~tvk3N%dd(Cex80~t6f^~x!?nyF;T+Ec(;Yt&%-x8zRZ^YG$^r!t@+iwS1ioAIY ztyD3FUzhi%?^F#wR}DLBU!1fXKaxA&%e|5=pbwy&?Nat)B z(v%!oNrygQABs=Sc8H`h&;o`RUL@wt5d<4Z%)_2^=3FnfayJ`;-rCWn-w8ibo)`ZW z5FV;Z#U%Ewsx540hk-A)SNx8H-k+r2ReAN5WuLI^eYTv3q|=-SDG;q)w_!kr6((aL5snMt^O71 zH-Ct~uq+-bCg=2KG!VKZEiDhm3l~iSEC^toyTRIaQkvI4p0(q+OihFgPO!dN3;S0p z(YcZ7wmZ$h=J*>at!%IE{KQ-xtzG^P ztO^gmosLW7KARDK8rj3fAhAi?x0~3mYWoK)wXmuDr8aSJ$1aa#fOJP~v8~Vg@85%Z zYZ#17DU0)Gq{N`-&$Q*2lJj-{LsaZN7k`kqFL>zJ*=&lzYE&}1h%Q>!G!1d6S>w-Zs+fXpcOZBL{b%% zo6v?1_G9-sYUd2r4TM0QOJFZ8yK}$EQb)7)8n?6uXP4l@KX(6fo-VQ3UN2s8ImcFt zsFQ(VcJh6jXd0wu6a`zsVCN5u-vgTzYIJE(MNHkFvV=a>DEMDnkf%4Al*0}@Shl`o1V zCnoe2HgW~KybP4HoLYYQM??O50M(5xvU+W>@(;)22_KH%&$}d^FgliA`}mXKe>H(O zYONW&5wb5oyq*UAq7>emspTk&I>+M(wxw%pm&0Xl-3`zodF#--uIMvjj$SkoN`*TdQx5#l#ub#3(=nHAjb`du4 z!g)zwS?w?^GRB2JCXg@o;j1aYuz$tS6svaW#6~ZC)M-~t3*ac2nHOWnsm`#!$0ak; zSgFil3*s!jeYNU1hsjzCrFKdtfMB|G)+)@0rh>9FLZzZ$Tk(hMMNec7d*%YN+M~^H zHV-5)&`x$37Md`;u{iTv^xO`?Vs;e5EEQ)Lzpd%G!@hvTdpN8<>{(b$H%88+hqfBb zs6>Dp{NO9IT)LYKZZ*hEs^_2nsVl9(#730 z3pN@Gt-f*j;*I!8_k5eX7cxv)=P26lsyPdEdOd&iM5le`FmEZM9?wwtI9!!cd{=eD7bpga2QwszcQZ_wh< z@FD_*Ph45D+9=mf$$Sk-xzi~3aVU`rKTA^rrK1Trld}AOAEc&sVzxF5zms=8olsSw z&AW@4xivxUnN=i}JgPN_G4cG|n7A?s9GhuX#>|JLK6q}CkZ_`gvVZx-qr4Z}4{2qO z(HAU*l_LNC2-0K}%-e6;JIbo=SH~=)mYyQ-ne=n{18MeTHLW0zEUhy=$o*& z7yn;o%Q&xQREs5KuxBo8dzoIfvm7gbc$_(s?LuN7V98!a5Vd(Ixq zFHkPPc`yC5s;XJfHp$Ou!Lj_K$ma`=YCY*aKF2#dqF=9UMO)_mVGmZ(`hlWhN-xj* z?c#SIa;wT_b#cSUQ+;tV0JOA%7w={sJ`u~FgfUD1^9GaFP}Oq>Bk~Zp$rpC45e%voW${LU(LW8ITPYJB3BIzMYe)aO!@yp?A*mMT5-0+CdFvnQ;%s-Lugi3^F|FQb5YgYz<{am zqqwdF(Lcj8=+Re*)$}?n&{01bv=MJeA>9 zo3pE~wt@bVNKq1s8khxRby1$zpVYRwcjO>J+d!8Ib<{KCna_RCywlNLCmA)ywVXDM zauUQMUPE~=$F>R;is$^4)g1IZvpB5!FCMGTMYrLI9kqtYYBTdRrFzn9JQe5una#;^ zx9hj2`!$n09WR60$XbshGaqm>O>*!VWDFhN$ma5au`R*~ZW+V(;d3K?s?mOSA;+T2 zExRi}ReaRc9Q)aR8b2<03*S0diF)YU$lDS%?TP~;+R|M=dR3X~%%422t-Gi9&UUS( zHrq-MG|Jk8&~n*fVkvVwveA2C><vj*8#(hlvJ1y`NO;i+(LRQ0mnetW7C zq&B}Vp@8~e+B|Tn)JY($GK%H}<-Q%<&`v6noTE1M`g0P|so6euwD1G4ks5G1nzZse zqN}<_cLz~CKZuQcfuAjp+WMPu{-}Z(s_D!meM7XNjiEe&f+wmS&9Sl~8LFTn?Ujo0 zL_V}Rw8iDs$p#1HV{hySo*LM!vDi5TuEHoLDlX}#GQB$+-5Waw$GlxasnmNYeC`PR znk!@F8^_@wH2$~F`>=|(le$;1Pqq8ht)Fl)Sovd|E|1e&72dkyKpvWSW4yfj&>V=u68qyZ0 z#dSIW`TMTbH(Z|yxWFumNYnO@o#v>CtnRg0uOzld@_VEg2Ni#GP<$}Zkdt-toibac zqsx=e`1qsm$+<5*(#0BnkQacu*T{7?qcFJtqE?QKHKh_7@~|6Js0@~2Mk@J%!erks z*M!#vgI8L50wsi52>gBW6z{!2j&%nd^bH2bchkcg1(y3EfZ@9J`PjZ`g0@2DySy`SlvDmFnN=1H_j-zo< ztj80B)vS}a;^NIs+v+lPQ_0(l#ccgIjzlPVaGZBW$%gvv?8*S9gGD;4!>=7LndOUx zbVme#(2#nI+v`dt;M9qpCy$3}6`FCI-$A-V;ojviqxyyf?(WOJB0Poi@i&zaU%9LC z=i+s*t3HYqA2J6u%bpv>#9A#LTJ80o9yANC-PsLry+XRtE|kbZQ{VdHk8P8F<>t$3 z*5ASD0l4na$Q@&AdDTkusZA~5d*B}XM%IYB@Z3wigsMP*BHylNyr|jxko<^^lDOol zkj+Fitfcr@du;stUjCO4T4im8kqIY~$QF2MbGh8%5_1Q*32nKSYp2T)*?eza8M*O@#8|Jl8-Bx*^H=D9>ak$Y}KiBy9gPQrdA2g_;z8$)f zOh6G9nNPNz(2y4N-V=sw@GRtv{|4<8r^}tFj;^CZOtu(?vG|c>?81qr8LPZ&}rLUyuc!p4Bw-m9Pwq8S;N&X%TypqZhmYG>;-J$)M&CgkPt z(#FI^JFF=hve|1Wbl-}&-u*|VZza8GCOGv+-%(aGszr8NUBTp0+01=LQuLp6SgjhM z(r#?v&^u7@z_Jx{0lvHE= zy8D}Hc(T`-z3sbeCgpzoyEi3= z8B(CM|3CFX-}hI1D~2xvnPPUikl^g_vrQT?f1VL|`v zhJqLAg_i;1Hm7~Gxy4`fpQN`xcZ>zl>>~JY=By{o1m71IG1FrPdMC8m1qaAA>^w&#)v^>eDm(8OqAEsmS>8Zcp!2WzVUp{ch0wT>lDVdiiD0m3;X%)LV1IUu5#Zh(3{_`pTMvhN~aF(s+lWlt8AIW2S z7a4>)^rC^W*C+aoH1Ntcov(Q&Nv#1wew3J>{OlRn$IO21*&=+{Tcd)UVJ>tp3ZGsfd zm#swuQQ@pO$y3iAND{e1DfyDFFW?uQrU&c>7aBA}aRa}ZJ{nnh%RTzRv}$@gV{80G zM(H5tvzB_Ev31mPM*lh_Tle;ErR=r${RCj_P{~wX>C) zp>tFYG~N}K%dSVU%AL?^?$A{PKIk0C)pMle_?aZ1?4Mc+|A^w4))CbDW30A1%Hu*w zdhsJ_`TcRbBMkeE_FPz7n*C}cmwqeXPDrtXGT}iQH&^f?3z~i7C>vNMo~=5NRe=&Y z%Jhgh?cW>AT-bP01W${mSVM;SIb?g^sOR>ct;il&?=prX#PgW{f?M@% z_8m`JlkCZa;F6n1(=(nk?iP=pePh1b?7MJl07RsLQ%99ylXB)vV_Q{lNH}F5Hj;NZ zVy8FvRG)_-E_87#>FzN}#?jsit$w>?dy@Bw{+*%3KBmhXADYxV|EhYYT2}PNmWbca zc>{OO85yH?uhV9EKd;WjSA(Q{n1*R4TV)OOo%$cN*UWNrz{-o2_p?`i@1Wy$F~Y=3 z9T3zu{vbfcya-m{TiME9Vc~7E(o>#XXeO<@p{q(T%r*M=W`y}D;I>&;$lPQ+EjBX= zDp)5@5eGaZ?T>iTg2!@`ZVhfWt7Lu*y|_br^#|{yY0s$CD&5NU*FjLOg;dh>M!4v{ zs8Qnnks76#@=<`>D(zmmNwcun5Gl580~`&G>&gH8l);*Rz-M1}Fg+*bs%WSrIa`zq zHFfg$p<8st&$aVzsdu~GMp54pzEwAx>E#eN?wW(bSU%X(EU3}EZ69mJea3A-!5Yx4 z>=JP!O2^?=1gX(?pP27lW{$a&PEN-h4?S*IZ4OCS>jjoiLa4L3lA0NNAaoCabIjve z@k11f?AOBMg|6GGInIo;j~T>|?6s`-K#TydukwNSi=r^iBd;Z- zEqf!n^R_7NX<9pFRCoyd(NVh24c4rUj5CA(+G}Ai-vmb1AyAU43bIw1-!tiG5&vZv zUGaje_qmx2?&tymDdXTC~dM+bmu&I<~sMo>v(XH!6}|c9tDdpLbf< z>a?l_dYV*!@BRfLfaFPEbTgoB$@WV0d2$#CKyX`=ctedVsrs0oYFpO*gh=lVYGCE# zJny;7!)=6pH{#>25!rZGIX+b*3O~Jwc=c;+4Z}!l8VF#CBql4Lzs?qXOD#X#BCJVz z^D!3tEeDhZySzP(@imK1y;VzF5)7q+7dr#n=Z#qElw`%mP}}+qe6Dx-YyHW(3L^K> zeMQVwBpWQKNlx}WStYbNjx{2=LS}{ei1005H+*CrwGEFbN<2P1ne~qGtL#!!&cqqF z*^NeMDXBy?;zJK|nDDRK&lD1)K>!N`DzUn?CIbwVfk65mWQN_0%YS>7+dZdf`@BCT zhP+aEiPhYR4k9IC< zCib1~<~&&Cz^gp}NN*c!mJ9Ye=elj)T-<#`oz_Z2g7_M0qjla(d3tNDMsa3<6l)wK27S8QnEJWKiaOYpe)waG0;r$rSBtI_k- z{3Yp|r=-ds4{RS{5QKEE;?R;mCkgK3ca=vH1a!_r*ByQ)nv}Q6J|}%T+Vm4=Y*0A9 zel~t$+~&B!sp-_U-5`vLE$zwUZgfk|Sv>Wy&_m}3sLVL&7x2H0ZI#Wr zDQq4ZT1iO6bYg%^eIfK5ciYeQx9^|`7^CdVoVfG>j6^0V7c{Z*ierzv*h8smnKkd@Z{6;NGUb{5+Xl$NWSq?CA)tuaH!cHBWI{|2XU4Wr2rEG@ii zgucj8D`(-@x?uS8&WQ%YWTbX$!kwZKpyD;^%bVrHtQTsx?A5C#YFBSiv!Mi!>f#xP zagm~;t<-0StGqF|x#Y%*wi|(U-hFdcTtypcc8S>OE24SD$l<9*nT2Wn%a>Ja29HA( z4m0K~0=3=jvK6hQ&MH@~B7sk|vc8ES;B=EUEX#k7Zu6G5qwcuj>zAt})wpvCDA>xA zR25mCAE&!b`TpCJmR#x5^WAeh*qy&72;Zb1ktUSf@9#%MouNO*e($S3`@ZJuMpgP- z><8fnu%sW|Ag?G*DInW-97wW5_a<9?X{5h(MMrjZK5&b%4W2*cQ_OvmX3Q!+(v?=& zOEFS|fp?zZwve%oY#mspQX)WgsIDqPsLBHfufwB?Y&O^3NPQ&_cZnqB#e7+ngQdF9xb-vNkcv$~rOlNcqFE^udTTHS$HoQg+jl!BW9 z;{xmwDLbLuDv;XZI+X!87xBCnwPv-*b1&`~E=-@j4!;RrOpq<+ zc5zw(L3$POAEq*0?e%RK8sS3%Qv)jN+y=Ue=vG?|-;~9snc@v7{U1%WA~Mv(fN~_i zTqP_nkn$^63`pAu^rd$W%e26Ep5$b1KIYEe7ax*2P&aj4M#?q1F%&Q2;Ot}ThfH$M zOj=5zwtp$BNG>-b4#U$_WHv_(1ZaML%hDqWg zix4{q#KgvrNZCHMqE5hFq(3IKh>gKQii)1j6}gVaCw^1U1;FF}meqw_Ep^q7;m(z6GuhL&Z;1tuTsZI}9JWzJkerMneCkFf(of4xOX~ z_o8EnPaMwTXunzr4WVMw@%)ooR_l8+;M3|}b+ARbOi1jPtMjtRn=HIeVfOlw&|JW? zZvkglqE8__ri}ZsOUJJQE#!xBUev0Y<~`K44g90Tte0+RpRSC2S`(Xp!RUxz1}JG@ zb}$hlSD7n1mhaj(2FK=(QWR7`#F){(iOt=ao&`aUu<+rL`7Of+OB>^6Cm6j^ta&V4 z=8Il{TmI17z50<5>#@=)JU?tDvUY%F*dN6kQry-4Oi@q75`=4T14UOZJZzLO%~GAVj#Lyl`EPe@pZaJrjan+T{$`<)A&;n%o%*-E}0nb zxA`c|$>&3|rE;ald)}S{NiRjr9+z+0aPuauWN)xx@jioO?eoc{>AAvEm7;`7#VQT0 z>@C;#k`I5CW#+Hj3;7@wZiYq_czC@}3e&bG`RtHeN;Y)uiyxH=H+$}F!GftO#5cvV zR0KglRR;a~JO{Q4ZbV3UYJ^)>8ICq*wKc*;ax-c&hPs39gt8hV`g3g|1h zRxXWs0i1Y-;|y*y7K8_@JY+Q3=9^lY?X-Pua2J7%E7n|jL-zbzKS5tEzR-TBd~TfE zir1{_V}m;5&YxAF`0y3z={8IS)i*dgOgR*5&oEEjlBc7ONyaJA+Hh0Wh}H_tdrNFjBOxPq^U(6DZ(~pD+QI0@_$agTpkQC z6-om=y`8+p&5p^YnrlZ<39&cdi+3JQRA}uxuZG_^wt28RaXjzRozJ9En2O5tFS{0{ z%KYY@aOK;rd<{WOhUKr(fhnp+u~M=U(cYJ6?Bvid?l@e%<_~V~{L=zq+}u{#Vc2#5 znOWIof7q~XpDxlsQumTFAhhUuBzcBH)w6mn3r_T&bBg*^Vc*eSQM&1eX`3n0sQoZ) zf}~|psnq8>{pSo)5qp{Cnz3fpY=kNqcV;@WXJE+Qd!vTgy62i2(4sKWsE+zlZ|%oI zTr1WYWb;IiEUaqiyU%%1{c%g3zqqOl>KQ&>5e~v^IE@v=Uk52Ed8V3s^|@;@X}Lll zdQ1hFhJCeD&N6P6xxpH{Wkz7WLdq~-{54&sm#VPS`V1m{yp0EkwPRa~U>+kAC-#|a zLe{=wM;YH}CHsmjeP>vzfS>qit+zh>S1X1||7Sm4yKHPWK@t7@NN}8|9dK%onn*3(Z={$j5+p#q1BS>CjMv=lGxP z3r*GzB3)COyAJlG6}%^bQv#KVXk?*dN%y0GOmwsib_j)o$_xVp)}pVC_PjLZuJz$~h{k(AnlJy~e*-G_-P>=mEA@+&DWwv%SmpbFR{ zD`-IR!K>;J*P{}-&G3!%s?$6$X^(@o7{D?|Qt%0{YYMydKW*mi^#5ry4=%JBAGSp5 zlMBNK0N`lv;O>4w2dTO!#&EFn^6>I@vatp@dU&`x0sykI=t~UWo?rS%WTN?#SM0tq z*2XIv`fZmqPBb{%D@H@z%pDJE#SCwu-aLh^$8U8?<{J|(n&O`PLKA)klC}{1t+Loj zVW|0jg{6;iyrDFz?q*&J0F#574_M1i57ry28p%o_C5PJeF*j2}tir>>Wnq><;Df&b zFe6F;LM-I;4xC%8f3ziue`aI+Uizs3>J90g_4|kgSwG6TYoMsEeR{2?dtgL?U#f?+Z}o3N z%8kp3tyqo9t1yNVF5Fl;bSm+4gI!d8*}3zhyek>Ip5svL-#3LNFUW2MqEm$F&4|c3 z&t;u?_aMaxcC%@8x@zh_=+oEoU%o-3b+M1sNMCbkbor%bLaS)@8Xw9(h6)b{cYTbV zRu2NTku{YO!F~Brt6S!6w|5dp|3$BXBMRGQmtPqLtQkpXe@&T6a7n+QLg ze44gSv_L;-n6@s{d~9aaCs!MAwz!?52~%slzDr>7m}jgMEw6)(13a17@zC4RP%4 zXj|im@m!~vj)^s_7a@4XA~*P)RMHUXvxlEhm~XvcMapep##R&bS_XunUw#&GhOAD)rasbl9A#G@A+K^27u9WA}&?@oGd?ARNIguZwlJGYdCjx>_uu5l7CrH4-UhDE~DixaIy4 zZjAx2y|K4(HwCV=T)^&H;!9j*DxbwMr7p&rJMipsWR$SEoz^j&@kAC(FSHP%IISjCyb&%7 z(1=R>WAkiCmj9>37v~`=&iC(pO+9x*bhGzH(imc}U^=|~ae!@IGk^rOAk(uQ{=WOD zqT^stKn&&8Tgpvr`_@@ntyM7wRct&Z{74JUFNoZ>!xa7S*e>w%+}eBud_Ye$<|%TV z2t3-iEaowKbJCH*{pDTmJ}ljpui@I87LKZ!FsN#wIB+?geg2%S{JYvf0PDSd*7&Yid!-k5vr;uf*-lkJ+^h6OPC?%y z@*9L5H7{ZTRbD8U-1gsR{Uxxw_zTmH_j;TVDh-)fD6rM5$v-Hrf&extWVN-I&uB7E z$RgQbftTJ=l(-S8a$0uADF`Sh75DCO%nu0QLpI$l^1Oy~-X5G5RZ}E7jQXngBUOAq zZhn#M-Mnk$(38=n?NnX|fF*J*D({Yw&COR@IKt#N|0?c2)Y(Wj5A@Lj8qY4URK<0< zguJW_vd+#**sPV)kH%M3={-MM4)oE>MiBys4#)o!Db8``S;)pTj`$d9rWZxj2akvP zXXQ^861wXXh<0PO?{^i0*`znxGcj$|%c#Iy>6H|}kSjUuDgpSZj)e#r zS#fO(kE3C=?EVhP*a80-cO}DV|G4N1Qr_qo6{$Aq=UqD0I_0XUJp6H?+hBI^iPQ8s zdc0^%sM3tn)ty+kz@70_`|3D4m6f=!GWhS;M%QL#ZS|>C(DLc%v6D{G79xIWETqq9 zWxNPa>thQYf2f<4fz4FlPx%z^q}Al}nct4m{6~FPww%9a6^-w>n6kdEHp0nmNt*i- zEwf-{k@{oBA@TdEqymEGsIt>9>2M}~-zbM>Iy^!_@9o^wea9!7@2LpkA~jUl&3V#^ z$<+Ft?K6nr-x!ybNr=LJzt(w}gZ@Le)|~&|ZW*T@tdOhQH3EVZ;BK}p^*?{^{_3D7 zx2Yv$PCj!${NZdjKcDfgXOWg=B#fPG>5=BaLAVt=I+y6AKJEwQQ1V{j1YNsN&eF2= za@cGklic(akq=6~@PLxqqWb#UB4mX%qm5o6>R-zm6+o{b5(2~WZ*A&&aY$jj_?*-S zCfROX&<+fT7R?e=aG<=i3 z^PXOpm{DqxV?fv}HvEC24m&qDpDO9eG%B}9YoE*X2* zs!doxCEQGc8D52E^zy}#b|v_w^U)GaFAMC4yUdBZ6X9jp{64`wM}`2 z6=W^dHA;(uY5Gj(y4`Y~y>-EyjfL*Ix2sXyxcFOGx@*{gP88igDSMxiwiS?7Gx5o( zCkJ~QIU~#a;*Nh>2z3r{Ekh?b%VT&$z1a(Rv{2Q+s#>50-*W-A$P26B=Kbjrc;KPa zyrhs{aGH%Xtps9a~ODQ~Z4_DS=G%8xKfRWBGV*5BVgwvrjW=k*a%T^H4bkOFztmPGXi?+5nw;kWAUUVLpmnN0uYEqhWAe{N&dc8wiGdS8W zuLrlBFxblP$N%7E@KQg`MUCek$329p`_U;?l@t%aMTquo6NOpieBJhEP7A1`;fAF? z;7SXmd1ZHE4goq#*@G$a*^mjDvOcu77;w<=NWGn%C9Bd6tXc<^ZU%{23xotsO4<>?z_rSX$yg+l;AdRLIt8WM5s;6fwapn;Q2f|1q50SBIuelK@x-d~hm;5ba~HOH+wIo_Ber<=)~{V-$OkcYOIqrI= z?(bo1KH~iNOWbQmFf=1&{{cu?9$G-@Wq&MvDt$%UsyU%+ooZQXd_#1Fq z#S`|va?a{qyE1d8m5Bn|?AVT~t@UOn9YY(rY}eJ^j2?Hz=vZwaTk6Z(asnCD?~|=9 z?}+D$-@ajn=7+|c&m+~6^UgQN+RJI7T}@uMu69}ppz=iSSCN!dt6aB;&o zb0Geuy)cdC{Z$NeA=P(&T8v4;_6E4d}xON(4+)-yrG?XGuA$K+B zwOgt{P8Vt#Hhz-;)?2kkWii|%Qo95X;ZeAFzE9c7h@izkaXG_)RWntPd8mo{S-S0) zxMw=C(_hqgIMuEMn13ipw?g}ECI2TqDZ%+(k@jpLi_!y32nnJytzsL(uXhbk$go@w`$^24^ZzpbvCRh>HR?(Kf5QZt@_gQ10hGnDobAb>AFTExiRM$<;5&D zGwTI1)*2Isl2);*3J8T2n2vWkH4C2djFN@te)@DezRCL7&F#EfEs18%LOkD@y2-B+ z%y;k~$(JdU(ERV+WW>z3Mu3m`#fr7oh1;LubNg5W2_17hh+biKLdiJ%^8k5>4Vx@_ z`$C@+8+<2hBGvTzzTd!NN|tT|Khk^8G6G@aK7yf4vQoS3LE-0>eP!qc;*b7U_^y$} zIpQf0e`WcF*d>~)gIOk;Izj2^QgJ0DYq&F}Fd$^~30ke9R)1C*ptnWtSf#D98eLl; zgt&7_uT06k^}ab9vfPyug`3B9@}oHEy|;l}Uq0!W2P2Nf+kuG{tqNK+b|B;FqVai| zC;uzK*!@4P;o*hWz;!+SKEJ!CvyYpn{e{E;aBy*T2RJy|d3XZ6+%Ie)KO8(=TV82XuUgfAi=()b1^zcJ>6@E$`5wO#~h9X8%E(5K|g@WCSgzh-O^bMx!pGx0mmJ z=Q!>tlYSO|UOTc)L8))yz(sTs+twS!;=Uq_%A{RMwy12u(${J+!h0%9c=v zx8=#n)bOlrJ?V2xmqIz1)#ly55mIhx_OMD%+fSdo?z)M&O1K`|&nBU;wJCP5_OnZz zXLA2NA(-h@+!ZJi#kK#3xT&H9dVJdlJ>|*Ep{@alU3Oy$JX8UPRV` zSIS4Pteh3Zet)?V4{_e~L2W}{kI{eQt;G1eLR@G$Xo2y zlZ$$ji(j_ATw3{kDFP7|UBgcZHa{a+EXYyM4z0@QeIoK@iQp+43=^sm4m_VVL{Anp zD*q5(I~@S}6z_sBtLDHTH(j@0k9LWn>lh{xC+aO+Cb<&~#@86deKRdK4}-8#t2w4P zao!33pFZH)rH9n5JZvO+eCue>l`;X2+%Bs6zU$U}vpp+E)rz>66;mzxL&m)Io|Q~( zQ3zq5!FoP1KEzFK?(Z~Qu@U6hF!@)2_Q@|jhXsakSn$JL9jo>;2LsIOMCQHAckzFs zgsU#%(XnPh?DILDuk;)cr^U!^vqqO zf}I85Z4rlqcU62;&D-`Cr;px$yi@uq`>>Z)eGFdn#WcaueEkwY{I^aVCG@{b^tJzn zoenK(UtXSN!iwS5KCVY0!PfuG%d?S=1eN>&VZn;=<;sTQo#X`E_qzsqiif@j|5U6T zjV%-UCp0+Uyp`RJb}->INWuz*rJvfs5QiNkmW6ArGmdre-*!cu3l09Xsnu6f0Gp7W zB$Hkd4X{iMQk84EfKva@YV4qNb{_M&v_;KUf%wJ`#0ehx_}W*oEL8qV!k`LTcligq z&Ca{)FB5R_gD2)id0!VX`Pe+1kv*Bn&B=8pnbYV+zkryfp*wv4M@6^|cfY4T#QfLx z(Nv>S(d!&leRRhGs*IlGtxrVjaDQ~PoBZX!kbSoxS)4PITio5+%GwW2PD)(@-u79@ z3yb(EE`^z!r$hRuAV5dY8Xr~GU8J-W4q3U2oGjElpAmA(WpjrJW}^y<2ek&k`&bF&qixF((mBD~&EC9F&LF}8(M;cm2!4r0P?ZTifu~)uvGlE<6 zr!`iJO^awNwNF;Ya=pQZtNF(B&Sl-2^V4?cDI@QYQ|2G3g_q`BuNqEAO5WiKtLlDw z))6iFemK70lA1*6i-4bvHbYx$q40BFJ>(VurvfbcIkmcY2COw8ZHfQr`rxXDtM04e z|7cO*&&zZFRqiz&%(@^wRW~5~OwTYru8uzMQ5pxQ$zQpgY3=f{JJ(x&t!4Q+{r!67 zUTy!bt|>#YAO?>{C+XZmW^>#|aa$Hx!IF21MkhY2no2>`WUVK3+8eza+ahij%th+e+}uU{9mM7JvjSdG zDcHauk12~_CEO%5&4*oF@L6J())fHYDkujnulon5TWzCX1&WxXz@wnXLHn}lLuy~S zs5$iEsrS*$VV<-=^Lrw93}uC%e8c&J6_}pflinIeab9hnEQR}Phzlya;W@)}Kk;L?hLe#rsSmO|U^kMNv-w(Xbh7$%Fe?|-a@ zBB{rUEWOxt@`GlT-TpHg<{KuUA%>X-?m*Q;>`w=PRZIiGRwT$yck-Rfnx_!_U?hQA=7319t5VARBjtMIGjgK z{(SZyNkekVvRieZVP&D<25(!D_xx8}nfmlz^^l155x8+AO^lq(JuO$g5(~6zMh5eU z#*r!UTxvyhocHzeQ`y)WrAIEuLzf%zo+6|0&d8(mI=PKjVfsl&{YA6NDT}o{oeJd6 zQo4l}rt#C&Ue~VIj&}0p=lGFudY-OhA1LLFB!=%uZT){lomW&7U7)s66a*Ah5Kx+m zfCz&0PCijVx{A^hlwL#cB`PQ?O+k8zNR!@60wL0S?|~4hp(O!A5?YeOzy7nX!vt7N#=Bh8JZWC>hR(O(R3QW+d|V?UBsbG-lB0ple#ep!F_kPZ zpf)s_Enc1L2*d3PJ~?+kM*TzA0q>swgdhLWH)~q8ZQmE|7SOqk@ z@Yy5$*GfZGm?`V<+5lPj1#uKBJr37~(qi9DSG!5P--PR2WtHq^@30ro^i_6(goGAt z8$v%fN(HV6SY-xI5v3i?Qeio*gsEq1cV>gnFgW|qXL0HqA&VQ+Hi5skMKNI^d4&{a z$EmertSMay=_LhwLCaWOYS+B}{IoHC68b)_l2=Ejafqbh@mz#qU(xD08YC?ecIm!8 z0l@IrJpM~IzFFEcAgyTWXr*ufrlGn1l84Y#XQi}eNGp};H>t3i$6|L=pMiAfhjpBm zR^BfGj)a=un`9rA3QQ#h`b?>wSl=>TK8MDDDC#>IsaUhQ;FfFDftKSR4(wB_ucpdh zpHQ%&$D#DiXq3bn*3bGlx6xGDD*iC%4~wsVf~3*CI$2oQpU28usOK-**h|_SJhCln zo^HRK`nxNXs(W|q1Tc&D;$(kM6RD-kk$aM_wj zPnY`DKUM7c+#IDi^_a5!8^u&`!r|sxtg@wBOc47QZL0gR@ieLxRvMbCdS^bgOzz`P zaUN5rKctirudVCJ8VaI3h7$s8wBQP^R5H`ZrGy!3cN)CmWZ>|j^}@iy#{Qq-K8wB3 z1nF&H`RW@;xQYY}&2?lxS4)?{(&DNaT<$upRl3bCje+a$4jmNB&geVh439;rIiO@r zTZKetq&$U>sd5GAL)6M7j}`FU>y0iPTt{gj=c=`0)q=kcB4I_2+Gv5r-p|Lj4@9=+ zQ1b}DQtJ&J3>j1mK2m%u!y{UQTu=X_A#P;UySM>K{E#+&_?rW9Ktn-Jq;2J&A`9j> zX{-Mf!w%oA%gU+s0zp=f7Op+8k~y_JO$89EA|6K1hpCn0Jp$i2Qm_SV#&9Pv#;%|C zn`e!Qq^6Jhxiqv?leVli7xPUZPhKH91T29TIdg|t=8y2SXpIKgc)&fMA|f^HbS1E# zJMK{$cNFy(HRb|jRh6$a@Ix|M6#m7lo2N9o zC({f&laI#OtOR28Cc~}-QT+l8u-qYo>gq_gYwzfLl>Yq4Vwc}goWnC z`DXV3@?3pm@<5x-tJ;Q~j8ocT6TuxN`=`$EWAE_P$e^nV|I3K{*K-Sw8NZWh2l@xP zYrzDbXWmALxO51+IZ#tiD}3VVt&9MMr~Ak7hoaF>@E@pwG(uAB4Ksl4T}#S z=6ZI;q&jEuBukuDzG`aew)-K|%OFoKEEz*4vfQXa(#p1ewZ=6D2%%i2&}()P`_dMx zs>Dor+sYRERr`8!UZqj&K%1r76(yJv4E zzvrBAX||C~l~i1E<1s#@(nS#0#33YU`In*PZ>M-F+Hdy^+xeETJQ^asa#80p7Z2zq zJgNSwuJz{wD_3h_{=Yv=>(pIxy%*_cBTuSTKxlS>VHajZ{j&adJJe#SW#aX1#DDcFzZp9^Yr>)2%19s+{p3;Rs9UN>i7oxoko!d;&{V>!)tIjvB{8kB!_})V-9F3= zamh0>$S)4RH5MtZ+7Qy%%6mz;NJXRF_Q1uP`(*LC8mZg{?Y8RLO|U6SElur+$dprHsh#_aDdJQiQ+QNXcB!AfzdNglH*=Q?lIMp1w)~ zbDzyJ-5Wi#`}AV=!jC2{I{9b1!Kt=COXdrWg5|eDuqJj3(@Sx_@DL+^i>}dJY+N1$ zBi+s}~$y&l9hzC0I9vzk5-h=j1MsEflDfAz@|v#i%p_ zA);i+#-TV+$JDOh*==l_-fl=_fI}+Oe8%>-)e-=nEFzh%oFx)&l z-+7?lcGt!RWNG0HK0{z+ohd;;?|>j@cQ-rRx4^TDv%}5Z^&sB#@be@hRhW!-pdDLmWuNKp?8meKn#)P^`tHFDd%;UXfXU`LekDEbrYDm zlS8hznC(kSxbT6wAGbFg-xJtdo<}fi?e~b42`|gvXAr2|{EJ{r$V-Dj(knu>!)l4Y ze<%KEo9bsWSQqR!;<6U{T&;#Eul%&LS$ujphM5S@OPMbZN9Y+}p09M#0^EuLgdi#s zrYxtjUp@n5crQV3J1P}tE@jC%&`w~s4;I@3t-4)FAJw=r}OIqTpX$9zgs4nnQ)>x8D;LDv1;&}N+p(qF}^eV;UwAFa*VLRp5Y z8{y$MAMMF}4=uj*BEFHFj`qTXuCq? z6mCOXAD9<|zR9%ki)n_}mYyhy&0ymv0rA}y>Lyl zP4d1J5x-@4L|bXt?%-%_{{v8sI80ZB)uUPh+yC~n-u+JtjE#h+G}LWViEh?;$N zbfULjjP7H-wHXmodKp@2GKAgSBbRve7);TClIU!H7?Ch(^fz1hzk3lgZq!?G8Fbkz zrPwtu+`__P$~*^uhheit*O$oAWxK)b?RsbDy_Ii$xdIS`hW4#$Bb;eX*%{X_Tc~1EQ^$`N+Xr=QmsC6w z>`i@gZ;R7hUin|HU+^=XQ(^;51; z&JhlJ-i$RJk5%e|jcJF&m z$?@Urhw$RsBMT~CH5y+R{a)>7WYF68Ls+NcSFtWv>1))8KFy6Q`%A(P=zD6I@{2+M zKMeH62(6Jbxx??%FORjGj~p!Tt(lMQ`I$|F%hA{fvt=id5Z{V_CXWPJg^vNU)BANT z=bGIj-#WQ@k30`wTy;&pvnjxurFMFFHCyZ?jK2~^D**2karEbVjFsk;njp6o8ZSQc z&~a;KJ8BNXSg)kF8n=cWSX+HpaGa>_#q5E5`pDOFP0WXMgI=bk-L$k^Qto zKmRUbt|;x){?4SvgLa4YB8ZWPPm)qd?yl&$X~oR#C2)F`PH>d>XRh9%v$0uns%f4RTfcbi!e5ZNpx;_Vd-AOR-9h zSur}^n0R_LNcw}0kkpS?m>RP#j3harh9LeyA*zPWPDo9DgE2fHM7=9Qfdq9N@pQn! zK%kJ*@=H21ZIFcZI86GZ)PixHrDaUVT! zXFA)HSl1t@5~VY_t6}`gbGq-gOt#c~ORT0TsznZ#buzv3%QJqow`__SF&APp-QC?5 zu9Vxd25K-34h){W@D=S#j=sLtG#@;^}HdU z;q~o{qUY#PJW{Uhnsx98p54x?2{xxprH@rLo3}#S-;}-@N-KU#SgKooYFgc(TB2d) z;ia&%c#^#TNhPzaTDhUBuJ5r2h`rr}tfTNWAt`Tr_?53}w_tLZz8T~^y$o=lliIOrPDdLU)H|VzZ_R^$DHIU!F_V5K1F!uqa1ci-TP?*E}dJ$jxdT zjEm<_G9EEjj-czdfrXyXrw!%8Z&M{(@!TC_0aeo!iuywT!a`-oD@X)LkcVcTQhM6E z`&P?$>(Bs*`||b58(tt%ygl$DkV9rLY-qAIrZ)W-mz6{T+X3P?V_(*d_*Oa*hn^vZ z`GWM0?lf3|7PEJ;LKC^4aQW!^HHE4Rp-I|#E`z1}S+(zLjR8v`0q@sdy!CH~m$=-t zoblQEb|;#tTap9H_&JVPBwCV2m3( zbMU=;WgJLPgGI=X#wVrFIiExiCGV^Fax+cA?2RMiEvLSw;JU*+Te;5A#H|L6%>YEK zu)_(r)2fH1@!{!EC2*#w;#U1)Z}e4aOmKZUG$NfX0h!G0swy30Up{&b(ISMfetOSAh>E zg6d|j6I{ZQMGnvb#Q}W3Z#yS;A9o)kzT+DMu-Z2cTE^9$Ud^02REfde-zT_coH60c zxFjV)qPe?}CohEbG^{naOabvo!2;Dl{Y`XH_49@qxT*76maz(i5{A%@s ztGql9oP&@gF<-NwWy6AgNy*lu`Ao-oG0*VLPU7bLCjP*ultfZppSimk4D!^sQ;aYF zPCED`vDp2fMMCJVCr$o}RB>?DQq=b_vs*Cj_`oSJuuf;KFh%EZn!A!LV2Adt_ zncobguU+H*#zHYO=9mvax?jsb?SGDWXEo1l{KznmUKy2P_no%UViC(fZa6Ee z>D6q>c7NTLU7BKeR`tM;4oZDapNrZ&)dCeCNqV2Yu{YORWjOrS!+%iz zQx3yd#%fFVGuPZvDC~l##erwwM;p2xhK+>SPZO?-UhX$x9OEA4Ypt)Ega`55l_s?+ znhM|IW77bLff4ISw`b|Oq$Z+<-PqQ-Y=BpSM5auSX6)dFE8iV`D>4}etDpNWO}}`& z5q}>bw3RH=b!(k$*6Mk@smb1Y80PVV&nCI*ILK(CP>cor!6qKXD@6zccK}uczGoXh zuqp8m(#FMvo@`{XY^?>v<04-!c_>8?TPRYn*FmZWV%@9@upp( zW_&mZ8agJueG>{_#(Xukn}t{XZq3?cZUj$=-Z@v1n8-)S&iu^>{OJ(ku0+2EtxPV? z%9KTwZW^j9yw1n0EQ_#3vs1)Bvgll^gPC)478*a_iqKq5)gd)WdCsJQ2XRGCiMFm; z^X@kK<~QsfDoPCtm1fo_eeit>IDzkYN>?>)e+?z2VNUmy6?X0N86>U_WhMVVf;J( zC5iD2RMjs2?_(E)I=xuUqqGQ+riR1Bk)bGHnT@9NdY1zao8A84vlqlw^X>H#-`tL( zkUl+uB%|Lo;>+@AsCZ$SQ-p=zx?Nu|`ai9B(>Ozu61DA{%kLVyg-hPK*6e6=`COZg z8#vd}CVQ2a1XnQ=qSGUY#+issoUxR&G;;!Ah}o_ZI4)U0(kK1@#$E=GX{0iIRWt%UX zA#YkSqj7(&Bq!cyofLHKsgFCY(Wb4mM-)4rpnsP7>Rb3LiK_;^|UzTSn;RtC9Z z$uBy)N|xkYNRdYz)KWUtuG94!3D|EPR_a+qngJ|GFB-1tq=>sw?${Ki8-Lbq%R^5# zO2!O@0ZM?kR-jKRob8;3zSOhk=tG*+?D*Gb6P_*KAMd842OiXX*?jMNEG?hAr}AB8 z@eJi^dQE;MeKM<|QgvT^o2@}eU;&+|*&L)GZCKmlA1I6D-;f7sTD%JCE90}2I5uPq-U zq>F@cK7PvX1d|3yOY7+&p7zpYEIn@!+@aV`Grlt||I2u`-tCRjDX9?kZvI->GS;}0 zxogBWRasG>6IQnWQXu=lziP=2`_f;4r_AYw)z86DxyHbm5e=*M{2#PB$O{Q$VN^td z_nuz!&=P`nL*~~lRo1?u;BMQ5k{o+HJ|FrD@F6^FttdV0XC&*Xlw|EnPNh9v8n)(& zTcA&e{yvgMGyQMe!1I4`!>zNp;dpJ#UxZH2d~5CC1bz#$f9vXU<^^%Je*4bN)&1RD zXD4S@PahAPvj}2eAyb|Wc!L2Sn7?`$y>#H!EQQ&GvMS%PK_8q3N5w1xh0Zm zwt#k${)F2D27+tQ#OZ0nV2^r=q1DF3YD=kL!-&uEcPD_R>X0Y&hpE*cjWtZLcaz$Y zx#PFD?vnIva45Wz$SLH?jU_tw_q;kj?q&aASy`^ZdGlu0ivQT{dA#$ehBR>N$?*89 zf8}hVszCq{mGa9aJ=1OJ^;x6$iNhSX=C@^0pxQ%X#6c1otydiE%%_{k<1ro~A3fey zs2MPt9IyVEi2inY(3BvnQR03<1?^BuBflF`(6;uG;7^}72fXHtT3-GysG`s1`vVnb z*KW;xVY`kWyUzoJC{W~uuk;|}+f_k2EUCE<^J+K`o^O0a`X^6vePa9w zn#BW0*wF2AJvgX+(j!j8V0(WZjXP>l>iK`q7#np*0#1OdfpRnKk3O0`;16F%u(Ewb zXMHmGl0r*tzKnmHQw- zdpgF6^4)c8ey*0VlQZAnkgQP`Vo$^$3y3~LC_VkcG7;jT@SgN|6z9w*ck<8_(0^-9 z^ksMAV%djGY+JS(Y#Th@9;m(Y)2DFz35R?=wgHf9{Kj&UoNpVQcXvUt&&G-}Pz|zF z?hFN^G7La|A;-o3)$Da+y9J6c>T4>407f$hF!-b!f3oW&@I3`)v(t;#*D^3;u!qTx ze&G199XQhyQ0=dtR?Om=(*juKhty!s@LH4slwPdbQEy1KYvkz&Y1bF}MgtnZpN4V% zq6pHBX6-V^u0giZ(G;g0B@^mD*ynQD2$xMq+=Cwl8V#$_Usu`7U?sjj%%nXOK5^-n z7jO8V@&x@AK^`^OpV0&S^6EG5`5M)bnVT)&%VlaD%l{%*Uf0{R<`}t4zg9_oPHtS@ zWgdg}NF?|T0ReNdJmOE6_M}C)J4G-0iFY#iwVh|WDXHv04|!4Jq;aE>(Ta<{Amz}L z2%-*h`l^9Ex43TIY$4L>8^Qk3yiNpdu-dl;;%BkC7u5CDi33xnhQK2=2{D)!5t7`d zEl#ane%W%i&&J8YRU^cN?}<>;??u_O50uEmC1-r$GCdxXLMJyiuY@1}04(ppk+l4H zOL|D@wtZbVym6RwsRd14n;<~rX|Vg6*#i&PhfFd~&x5Xe&FBc=-mYCZ=k}-3&+WMR zsWI`Dxo;+UqXM`JAgbXeNtY0IvK>@ zS^i^9p`zB2JWe|P;gYV5A3IdYdGL~_{9kdmL!aT5(nt`w=98gxtds`RBp$J*>DO}d z^=4+nE6msXrU}$_!aWo3KJK9gA;afAUmlDzxYvJM_X7Zj)(pXv^6!m3z)j$la-1VBW!h zCW6-g%G$t$jAz$d~`VA-+{VsV+SaApz?5VT4kv0F@1=OAoS;5>OxpCz+zWF% zua@vKTkyTp=ZpEUmeP@_e&#dDI3`$m>rsecadriJU3Of90J8x#>;@>RF!D9l1^Ec- zXv`6Qy3kplxja`gUAVY12^e3B7esPul-q1g+WufA#hnWF$LzJ7ghn)}sl*nPQ1j9d z+H3dO84M75rf2m6PUsB(uQ)}tO<}2l*~+^)p98~XeQyaM-lbVn9Zb?D;=VtB@1ron zz4|yc^>#J&0~pnecv>E7X4preFFCLo-ceBwI5XY(uY|N-Uh=h!6A{vLxb-BM%N_PB zv6_3#$>(Go>$|iRCX~H4b3&kRlvm;W5kJc-Q&P3h>-(;FSz?t)2BVfOvktA0!^DY* zj-RIS#XsJ(yhTP#YE@w5Iqq_5-X1Bt5lJo2={op^rQ4N#=JzlkOj_|aq12(b6dCgC z^XtE_a{Z&40`6P2I%8VX0g6B*72X_TX$cCp&KFg1Dh~|YMk(xj!E`TQj~>YqmWRKr z32r?G3N*H3fV-fu9n8)(TzzT}#P}#!@35K7@=v8X4I>hUcuWazYweDz@HAqUVD#D-q}$ zr?uX_d=m885#rrNRgqVZKVHI~K;=I|JVhURdnjMp8+F)#wm$s+aJbvOsM34ZASnFW ztaogix!6vKv5Ne3${OgFrZzuQE1()$=l0U%sb|?^;olED%L-q6E2TV>ZI{+slsDD@Gzl|{`WE5D@b)8@@b`)_?V zy4AIOQuP~S9@}NGZ;_Y9QsmCQJJAAosAOD|+L&Ua$g5#l6Nkfg0XIRmwV|Z1k$sjd zS0E_A@J;3XE4KFE;BjAIMwq3=)VU~F-B9K^&IOt`nw_LRk~(S4v-0;+afgw9$7(e%sJVXwbnh8_ByQl0 zR9WIM|Lp<1y7JM@quTI>UBtHc-N*mxHBX;SJgX6WRzZK`GpKqoJWwfLZNgPH$BJR+ zub!MlMnUC0p_=AXawTnRy{S{c%r}IhgzMlpuJAr{_YU7A8${A@ZSn)PV%uK#Jow8# zxJt_7x_V_|QRM#sg`Zoq170YM{T`Y-XJgJRIHJKh^Ss`8a)gU#p6^-|VsuOT`gElj z3Xc!I@BZ0nzOj*mN~|!qRDvk^$qT@$R#n1bVaua~0eN*e$x@3BigW7Xn>o5rPP3^( z?1}nsnnbwUKUt#zqw?|%DsGn;&K=U4?lkYBV0jVGdx7C+Th{*chiUei9cgsJvHYa) zj1VSR97H+E%t7KpD7h7pQBj&48FDJV$7p?Qu$x!%_*){?O|8(ojgMQb3(@#hTj^`Z z_nY=8`RMk3cGH^2$B%WoDNu_%qF++#cj2Fn<}%$arfTVr8Pw~avhYi-4zJLs#xWT0 zm92N){|v*Mn@%@HaCAXJ{K`}z)>JL1<_J$BqfTC*#`l!A0MWoHtm>u>)Q{h*F>&7T z?eFYb(=Nj{(l$n;(6Al?VH^fkflgX;Z#@So@RCb!Xd08=@mW`Mb!B$Nis@gm86RqU zY+ZD;WA1OncgDc77I0ltCD?1Q8{)$jlJoW?+zaF7%DPiopqOlDGmOy+13$4WBKo#n zWs~iD6x>+Pq$LpIeM=#8mM?z30QJ&k=OBWNUsiD;c0w`i_O0#LB&!ctFSs%nhZZm6 zP!fy!z-;7$>jsdel2PA3W`7Rh6{_oD{&utWl7Wf)u0| zx;kKv3EHv5`JUIQT{TJJ$ELi=UoGsOI@k)TEFaa~3o*!Sx0nqfwB$8q%W7&I9zR3hGjv_?wOclU60 zarBi<$d=3YK&_J$$9mJR3}6v=@FG6-H;As@^JCi+{aCqRuX|?RB@Tq)WG=W?kCpViT2CH2y5N}Z zIaC>$%27D|w)(Y9kpI4Sd$jV5SWXYmC{OjSoMxb~bY8Mq&K1}U zQNp4>9jNP-Yzh!X;FP2Rv>BKc!{pG%+C8Mj)NrXLJwUE=DQvz4`2a!nlfrKvmGVtE zSB~(_I4Z_8y-8HhN-}Ytsj2KLwis+PC#99H)9TPmQmb&DpojP;g>P<`P$0 zLhRY*FK?Fpd^)D8^kObzsj_qtD($M84bMlKQ&-4)ZX2>5PAx{o zYBL`9_)+SXZp|k%Nc2NC8$wr0YxwOtxzBqBk9)$D%cZCHor_?qPFCg45?ZvGyo zkGO&8$HB$mJyN8-i+v+|vRKAK;gy4ONmh`9Kn;15zK!c1YX6YhT#5Xd+eC%!A4#2w zanP#U#FXyf+HxyU-M;EGue4cyp-Vf3cJg{{%{_G8__Z*hS@XJ09DZXDyI`@$aettGCZbt0k1oFf% zT5tgB21f!wr0Iz>t4(IaxXaNvbj5DQIPUJEFNCRcaC|u*#kCy-V{j(Jrdf__B zq2F~~=XBhKzmgE~mEHr0u3o8VBdDe-1YrRK6FPgJE)C5rqZt^d_DoKz>so}is9nkM zqc0`I$tQ=sjlxAmDJ%IVv!!&ax@wx5@#&$!JpWCKi>>>6OO#QG=nAt6>?;S^f{Aa5 zCxTmIGU4jwW}Zq71zi@Fl+X4MOew!f)0z&@EV7XH{ z%oRd?-H~|$727LaSvJ5D|+HZiSt+xaD55-w8O$T;dcKl|OeGuC*%GysCk@3<|`IoREyPQ$Ss~5xQkWvK?Y? zq<0$MnGISGQCeyXwrn){vZsavo_6|Y86{j?BpChNymNjoHWbL;FjE0O@D?(Rc*V!D#Qrp?u`1V# zx%lb-NVYz5Dj$Bbq@P#?H1zs5(O3kVxY*N;X$FnU%{Q z__LG_5aKurAPy}THSg$pTF)fAOhgZdz+}R=1g{8`M>ubNJv=49v>Z<6*0}aY?J_Cp zn6M?#A`y~EVL*8Eb`Z+fZaWWYC`X;j^;7z9+)Wc^Vr2S{diTbn(xb1)bEjQ9T1EGc zFM9B&V1(3TPrHRQi1G>ehurbzBSt6w-&E5RZ$L$Zvi-crUE?)5G=}=6MX00a!EppY zM0`3X9WtEstut^_=ut0u_4@(XjH}rI=B<%jh_t}|=o9kF9_zCh>D)c#ID1d7Y64Q2HrgT zDJ%Ii0`LO*Ge6J#3)8-JjJ|8%x5axbz7>}Qpq~ZIlsA~f;ttnITR<#kz4TM zTOWeB`|gp9%q7zr2j`E#CqBy+eH)3{Cfnpjw;Fzzu&01*|AIHkDC2RJ7{?CH%}2pL zmxD~RcjD!4fmyB9?+qdrqVm4kZ18)}rH<@vTNRJt=2a|X0gki-F!#31!Bnr&$;;+y z{sR54?Bqcw`&nPGH^bqo(IhR0>-lkrG{^p6?nx#jLl zw3lZKLE0BTe`nL#)`aS<_o)V|2Itj7Z0~P3NKd%!^)?+hnPXZu22?7)-Ya@?Y$8OD zWdl#UN}FSHe0`Zz6D{;$P;6m zPHyz0mpJM7xNX~nJcKByWK1`p+cfv7<1uZC?|?$mxSdTu(0_85rgenC2QxyIdW}|0 zQ#tJ?MmllHZjxC_tqIbd5iRNW{E4B6*p@3RzwDnqVJU-^z4rJLNQ*K6wsek;>|AY# z2$pLJVD|wRtZek|j~UG8ob6pOV#IEnBoylE&{9XuRz)KG&$WSVT(=X}Gd@kB57a}x z%uq1Tgua=@WbVXWeq0$z$b-9-j#-&>o-vn8%9vFx!%5j}JbXs=VuPN*8R*2_#*hn+ ztziUeC>5-w$`^55Mn^}9aL@?zJsmeIcDE4C3#yc1UzonuRZhkFaV*ZG_TfF%Kx+pO z;K?(mA(az;J$b{uwCe#R_xopdTJ~#x8FGFCG^HJ?!{)JmrJqE>aF1=^!a>ngXV^aW zIIA`2k2|!8a>A>^x$~oiZkT%VxN(QR((0tr^^;%W?O$r3JLtArXm?9z!i|5Yj>!YA zK*gQ=4iS~VhT7%u3=0T6mls%$ph=TXAnoku?4&f$_nZ&_o5OAyk)BQ_9^F!JFdA}m z+&I++6)!FTlW>n%Qpcl4jb3d~4CD(5Vr}wliZTM9)P0(EZY14$mdK#g{gflQd8T>a z*~vjLa&!*NJ=mqlBD?j$m?T?9v(ITrkfN53UZ!pw`q)+zKN-$QB$b@tB7g{4n68#jaoiX21Qe-vL3o)8LHJDQM7 zax2X9-*bI)dj!smx@M|M6)OMG=G`}P^80kXPE>)iusc~mO6!hVv}C>~L@ zdX@6{dQ=>DRvQzu>-HTv1;e@BU0(nnawe@tIUAUTJB$)X)P<{<69ZNUb&I?E{I8Jn zp#o6DncK0DIvL~O2Zb|Xvhd=sVF1C?7bB>tNyA$6xkXRE_?SZL<}K|aDYr-Bl}_PY z1O2QaK!%pK&nCs>z@n2;zzhL#QnjkAQZZqS5j&BRQ_9W=kn+D470rz$tQ1pfw-vhP zJ_c4ZHk_3E{cAAO4NnjNQi^RKDVEk$th4*oznm5MQKJoifIt2FMg?B{;QcxAGiy=F zxkl<%q8L);KawuwRNnB%Pzf!?$_QJLr!OX`>R-0mu=|48|MyN~(-YGgj;ESxQVzM| z#~;YA?#^fsP9+g7Gg6$$+hqRd;3=1s#oBEbSj@H!^wRC;}>BK*v~PL4!cs4O3XtD3P>Qq5PQ|JqCLXy_I3;SfoW3e}S!?wc=> zQb=ekboA7Y(>&Zbk(A`j=NaWVGxV#$qyp^%Gj^4xM&=31f!`+0hsmJI)w?gQ;|#=U zbMX6SvB?sxA?pdqH-=Huy)JD(lP|4hniYk2@Mm;~GUT_Jr>-tjH5U~V!audkJ1$Nf z;g|j$6N1~i#~TB=bqwaA*y*SA{gyI#oxxkmcMruo3X+A2+!Bv%-ctxiPD#-H<4cgM zTmRHtG>xSB*4MH#*p8(T!{fAIXF~l>EYJP0pX6jsj3v8|w%cD3hyWiSex}3M8-AMC zq-fVlnGhmLg;LMPMl*COCoDMAtX0a_E=!uwR7qSYjJD`$8++OHW7%uTqJFTbDM%&7 zFGIDcdHz#G4rqU$#L1DjdJ;&bRRd&pcP#1hnP@UcU(badqpEhTAHw@2u z@->JWyN$}O4{UEHGc`Ui{e!qIELnE$Bi&E0>PnNdTF^c1wH9U=o2V!G{diK5-ZOj8k zGQ}<&iS)}2Le;SLOn;vwW~I0Q<5OX7@m40fp~D!mP2&cp`{&1%X*lM~)U(mHlfNNUOkHrufMSr~sY@EEUaL5syN69^9oW)kr$~i<>CKVs`Qa zjMQ9f-%xBAZzX6URi~%b^fQ596|r zH~94fE936|Z5?r8vxf7R;VG;H13J{ao z(_Kj_hh@~?G9$JUj`*pPYW61h6xL#!)_j$7>AoMk$m6#?_;XgfVX3ajtYgP_UIxD$9!%K4UzNrx zoXJHgNQLWFu=ZEcjxj(0+RZmyyR*Mfy5wQlH8tL2)>VXK-nAHmoH?Gbok@wKc*B_W{c8jd94y`kB3)@^)K-<*%?F3h>3>^oQ6 z9+Nv$_6`0$`$8Kv3hNvGtH?#htqTzj^)|1Tr#TgB!kp%5Y z{AxR|%I2j&pO`1NNGh*W4f%B137Y1mV{e7O@K#McbXbW`q1F!!-~}P+`Y6|CsXHyY z&9&0jDUJ`9AE667GzT6s>>$|tUftL`!#0P8ymKVfWvRmVhx+Y=)TC=GipbvBHMM+p z%HSC`3fYLh)Q&`5#cwPAeSjQgeo)e@UX0; z!(;)4>`jP}o!5>8o76v9o@QMXUX5kQNB!qrvH!HlwSTvk(QvhSIoxv+fBSZigt&TZs2C#}zju zv`G!uh(;ARhl$Qh>0qUgx-|@9jg<}ui$hM9d6Tpi*s^`kaoEn&L~Q^C{@pBcEUQE8 z?$Yq@NLB$v#OkiYO$9>OE@fa&0%MAte)8(pL=`RwH(PHIz_BOv9y1#It{q2L@K>uqtqLGzPKBk+cO55TwBkZj-!(MVOJQS;UdHlH(U!^%U z{g!6R)TP+Kjstgl-=Pe)YnDp~;cf>{A9S0Mcpui}KR0S~J>QA&k$Yii_0E-Pv@Ac> zQ)7nOI*5*GY(0jBJEG)sxzKjQ4nbK?T3=D4DO4RFE-N|iO+Yy`aj5OYQ8i&p`2M%sy;lr6;rWQ{Bh@d`eA+&{lH*O?@iI|6G2Trcw!3Y1L8V&q+C zrgb&%nfs_d^eyl^Z~@?sP)Fbo9En*N0Dh0!2Z&MPGO10L z^5ns(8cQ5lxs9t}7yiw)#%+;|6a15EewU&0E__&WzIP@|6~%r9FME~HhLs2-;kAwa z1ecS7T_kSW4H^*Oi7MxV4$c52vP(}K!-YI=7I|*AoMP%FK1z||i}V&2g6@3Sc)nf# z;e*7-ub^Xj>wHAFJNixc7kYv9U6C~mM;L8xIlufYCNO>5Gu{;i!cy7R2PD=X@$ZFA z`}K~eg0_`Bl|nnJ(Ah_uF=mZsZsg2J%~?`G5i+w@wF#(0TqBNraa#Oa?i<*69Rfqp zM8teU*fKvXm4D;qT(OjCv$a=THBulK#U;0VtkUCIsAj5TJLq_PGQbgW>RGT7{YEGw z(_&AWn7QjWQZYXxdSL|t;_;S?ej%oAf|TUA~atq>V*8fN`tfcK z%OTv##9=oUuHGtxRDr^~jUU4xeMS!$>-e^;7A{UDDp(q=hfe&j8JPN>U zb>P8I1PL>|2-D0W@T0iZ&Vm=@#|!^suECu*2n~VahfW)RjKvfrG8hJ{MOp7xi(Z(c znNuOi&Xx*+JhA5H#g-EgxwofDEMNQd6o|D-JWS)8m(W(+K0Jh9P{_WR;6iSaPPlG^ z+|&FQP_9O4B^x&4ONwKx{-lt1lZ`$-31wOCwz#PsJy^}1@$CC{*OdWUN;Kxr(5v|* zdqcq!QTZJ%Vyy$j>Q{7BVxRH+Ha#FEE=GlHIMw}n$@A_&RTM1Mi+Q!d7G9s*+VU|b z^Hdm+JF@z6MRMO_`<>R!ir<4D92gi{5KI?LEC8@8M+gGWOkz4FBRq=^tl#@7sxPsgIAQ%50D`>#xTVm-oIsk9L*v#$agwxM+)SQ=vGa+1saI{C1n+VinID zDl}L0zH@&{zI4oT)+xJ_0FyrdM&BlAY-FCvp=Xab#$AY%&e06s>)moIO=jeCLbCsG zOMPPIc?<4ZkYq^k>E!~ama=Jv$$_u5%Xkb7B>dC0XqF}Ud&}8%gZdEu{=sa$naVdm zM6#XxiIDmzR_#mrOHN>7I+R?GY@NV6SoQpz7N%mBGT&D2qGCjs_sK1P8VA&5+RVs~ z@7I%>(Pp0-GgW`kIJ^OihjA6PPMJ|W@5Q;c7FqW(=OEnk;{m~_hzdf>Vdoll{?pxq zcv0+sLAaM^Uy7SOvGGAM>BSVy@uyAx9yUu4`cHc$psbB25;`S_D#N6wWr4M=asqMutLlq ziMhX}1=6am*xEIAmsdq<^-hDK=(12d9vwk;Z@nzj%C@^3>>?ry%7(<@9HZ_i=-LAW z%4OaWj4_Ds-2D?JmdR?YsJ$)w_Cf8e1?y0AykGp0JkwH+cHjs?h`*)g7*%~7fPhUT z?}N8XD<4mN*0yu|=I`LpR>!Z%WQ6Jm92c9JE|NU)3190SbMI%lfRni*t!Aa2u2Xo1 zC7o=)JbOTmJ<;tWYtY6=uVJdztkD+$tfdVgE3U+lH#C-E@1ht%9{~dvr!Om^qbXGn zbM0`IwIt#C+ z-}nDxVi1Z_Qg5Y{l+J-gmq_R6?ru~-M7oih+7C!7`kD2geR-<$8U$H&559bAK>K`a!u!yC z-}pE0*1|zc)W?S_bR(xz#jW7bYq#Jc+~UCB8=Z{Kwrb@=^V^vnl^E43{4)m+xy7}w zt10S!M=+iryi~?#K4!hI^UK0|P*M0_3wRl9U8fU=t-| ze4;m4>|mdx_y+|m>Pk3oNHW_|mN}8H^O0n*kI7o+%h+E704QI}elA(nTL$X&{9fv^ z>yYY($V$p)jhvA&%8fD8U;3Y8C?mh6KO_l|Gh%GG`&8{$xY6@O**EyB)jEaysr*`s zYYVVVQU&+RO@8t1#H{+Y?yGM)FO4j(`6Q87LC3SxTRU5)ltAX?*SXCTI8TSU0MpVc zSzDX2j;DFVzh#lny<+r2n8=1R?lTSnI-YFA=8i0zRmA&tcxoSqf{ zoMGjh)_i`mHivq4$uz(>^t zr78BJ1`|9wM}m1FuT?0Jbb9qlalgvUM=6()VpPnw!%{E6X=H3n9Fds#KC2Z%{U}X} zC+n=L3A2$!Et5>WVQxvU% z^$_GE>q?`ACz=F*S3nC5H!;yea#LCc{cU{tW<89zJ~wx1@nGf59b(D> zetu%ERj1jyqwaV#3}__cYQW68z`)UqqJ2bn-=B7Ou0_Yxy_iETv1Y_QiMqf{^JB zD=H0$Uw&qwEJH{3OH-Qm!Ha}?9Kb$L^jUO$KG9D%pQ6mmi)di3kJX<(0*iPBxciyC z9w-0axnV`e_Gs>t6#JB^byUA)O%^JY~Zm4ihICtIzQUe(-T}hV_f}M zB^q-q$RFCVXT!n|l?8z;AlurFt!=Frv!_5=*Yie02PJ3O#gVh?{gu||%cb;Vy|b3p zjiw=|6P+oSHJ~o1qLtdS&a~C3TGE&X2q zQ1`j*jC{lTSK5oRA6#qA+}NMADlEFQdVH64vY=f)7a)pw7uh^(ao0;_Kw}vkTWKft@g4- zovc-n|A{al>OS)=xdTP)*7RMUIIwnCw7k#nXnMbulw8Fii=y6i`uv(@Yiq1fHTdNH zj#`CfJ3Ym}np)?;f#SnjWyhom^^vo*U2qaezLR4+na_4Ao55$!19t979;#R%I%Gwi z0^_#w=jyG>B7BZm>VMjFSZ&y4>KU2+yYd~m9y6juJ$J}whxRH>XSYSIN;WHT(!SMF z(yJXhv;Le>^S&uV^zxHUS^D1Ix#iDSvmcEZlAd1K@@oXwqr0ASjpfx*_K77^q2ndxS7H#Kv4Q91BOy0+z#&z*cA)LC zy|d>-V&Qdx(_FtEb0PfosC3a_)2N5Qy|RV0J*R-HS0t+aWVbW9UF`nu9|q(6C3Oz% zgK>X#j@tL!Qx?ZuHdUHG0?QSsQ%Z*3&JDFzc0-+CinRDlo6zt^8Z< z%q}BB>qM1N!tStn*SUe;xMr$qJMCXrXIZ>6p!fGJ#>O*a#J$>g`p}^hg_{`&owJUu zTiec>&uclkaNsHKq?dfbV0b9XJqj_De(P1eG`RP&cPb_u6=OW`7|szC;L+I*s)HYE zmIb%l#}_Sy7s0C=v5l7}z$j1Tn0E*))5L+uq}rX4l3R+kxk=BNy85X=TXv2mplBnK zlKd%J7g}K#DJyl`y;~)n34JC3)h4uKr`wK6x<`j$*J&Y4kibrus4s5k2Q`f95#8c9 ziF4qll-d+c=gT;ZPL6#~XLR7JHLvj2+ijm5B}HhXj`DuVeC(*+`V4AB;xmYD;+S=e z7*`jN;^g1=Xw&}9!Ng0#jHH=GXoTTr-^vO9Y#dMG1aDU0Tt!QKGS%3e@V5VYK zaWP=ya_V?s<`gLiy4)^~+0sb(u~jJeO|Yp zD^wO%L-sd)=*ZC-d00Al;6&~|kSNH*Hi?`OE11+3BLfcn9!_E0YT#+a9_n_!2zm@# zVdyDEN^`Gv6kg@I>aj;ynUth?VEd%L6N0&G0s<=@CofOiCQQ$6gnRf8V7UBz8ao}x zP3*7hp{wUfPZ#ht!4)r%Pcqe4ghknJGgH_6w}v#e_3|BJH}=T#>freCNg%^$v7ps zD~liepZ3J$f7FQSh8l5z8CGumRml$aHfBJpn?TRxn`3GSG_$j@1_EuZtbk_5bDumr zXp5jUqO!8QY& z@lJXh_ZGXB)b8v^kVq$1N~}v&7O-zW9^zRa8)0m4CgSp5xw) z9xblP2PCY^wst3L!mSkS<$0#n>mm*dldjyw{6d6!k*?~~8i!}z=- z!X}t{*fD-~e%~KCmX=%CB`zhAZ@ITM>qOu-Cz>4?rANQUxztF6iyBpic$7Tp^?xhA zm2FE2$!=Jl0yCt=xf${^U1?PR~5bA)lkbusEgJGjwBmN)eM$xI+DaFfTgCKeI;XH(z zdT&XtKIXNRQ9<8nz+BZ#pI%`SnKLdfQut?gAFEr{oDf#xK3)E0|L$5)^bT!D25z1( z1^yY3uqa-Az~;DV*E19 zw>Zw-P-DE^Z>o&8Y0cvTn1#rZbEb(ZR}Qwe#AUAhv8U;9`dyVw!&s}|CMfX;zVzh9 z!qAf2a05C-LeTC|Y{6x%;(2T5D}|bR`Mp4rPyIG(Y7T>ND;1JumX7re1I?FvjUHT< z6YjN^eIv=Be))qjU{3vyv6ySYV^t5?DsKS^4@qgB$EfXJ8Z!9NIR=cZ5#2DM%{aaA z7}VYhW{G_mw79dFKCHsY@?>`|(${N{ZL8dwgmLKAy|U$?hu#OoNhw*+(30z_(Cbsl z;U;cW<7G&0?pEhdb4O1uR*?qNP~bHdUhV^Jvv9w7etkF0JAdS9QsmX8{z+xftoNqY z9pVA)dZ)RIeNnGH<<%iYZlV{l^sCRR!>?P{gJeBgNn7zpm@N|6+43uN+t{lB8F^@pG<+>>MIuY+o$obU#k_+#!^ z9xz$9uz@-*ufobHza)JW_PJ9cyOB2qH68BiSPAoqMit^JIAn?%&PvlqP;R&^Ura}M z-Gb?W+f31GXYS|yndyFZ&K*M$KX$oeyz;KT&u3BIM8zJ*sdOsZoh7iBqzc*IT>g-8 zy)O>??I&GD<5RA%E%hZw&|2zJn%gkQ;$(!JR;GbvkFPx;9mo(87iMX%6&iC`5;Qm7 z!uhl5$t~g5jFRE;6~G-QF3Zr+vaP(!H5L4Z(&?eAQ&zQ)3aYkYo%nkX(i8m+jeE(5 zheFdRG;Eo~pM^a;Y&m_P>|b_n<7)EitPyF*++H5Z0=WL2+}MUZR+ntuoSDBc*-Epj z$o^a+$$4-F$oymk_?7zp>c3-a+yC~?w~ub`y7C)OyzRHOCYofzZ)QK@oB3se;ch9k zKVNU6016HforPG)K-WW;4*UsK?SX(&fn^jF?n=3d0^=`xN08Xh`o>L_9lab4ZwpwRT1~?#uQwuh(>&}2#5?U8i+$P#iq;>| zzm!O`SxTtkSj6K0%bXedG0ORRjosYwIZ`-swMu!{+lgI+x3Sc|YlM3VThsB3jMjI8 zDJF*dh6JuubhJ#f%TbT}XMF@6KS!Ch_A|UMyXXTC+Wc1*Mw9lsIh5>0i6y;N%lnd zqP1d{=Q+(e+H1kUtplwsBqP3&3801RvExpY;nMS>TbALD+{Tq3H8h&emeB=6r5NHw zw4LWqm!h~2n@RsIh+3&?9K3&x;yR7&TCkikomQ^n(dz9#hrCRQSU^v&2^U|t&6qIZ zF8q)JGPCSoVn#f|Re*X;meJWj>m)&%*ua>k36=U!&P-7xx}S`sxua@2g7Qbta5LvX z2vTv@g3<(jt^*ZeW|opg#J(h_jgyAto|V`xp4tpg2!N~m`C$Yj0IIDCa-g!W5x)%Y z;=hVyM9j==PI@+G;ZU&|90eVjf)mrW0nBRgE>++nEG{5QQ@j|-yt2=X*--HH7t5Xe zRa+#wtZ%AZ+A1{y2gU>)?lhLSOu6jP39~4&Q0C(O=y2waF>-s50H>9e>~}o}GWfp2 zPYbRYD|(uEd`j5Vb}j~nZQhhk8XGG#Wp)D3QC_zWlUf;V@V{ZzxP|-ng!iR^Ba!Uo}6P$`5rWf;Sh7{1;>JP zYg<|8K0v?GE#w95i#=#>zgPvQS~7Lq-;uL$3FTRby&EU?CbF*UTd3W)#|NZ*PRo8J zCAq;MIF9ZcE@X$yG zjV_~tJNFBUDlv1(DxWYT?hc=$#T-J5vc{Y{0m-8#+uN(!V?A4Z8Z*fs-ZG-D>tx2= zrx#pCdIy$05+i8q%X=Ce{AKz1KK4s$>xVjO^FFDIo7O)=w6zp$+EIy6Z2vhAa4cRh zwN}=dte!lLt~*+G3t=~L28-S*ao=3;)g$?qSR#!096pVfVbv4`^ELvB{MKH~4?Hs!HSAX|o??Dq4WFZ0NXaBys zA-N+GCodbP(`|W2`JpJJduIi2I>LUji2jYA7zgh@Q z@vn zegY^(X9ni$p$nwi!FPbI&Y+B-quV{zsm$rfnmr)DxeOY#Z{E4&CqBBp)Go#Rc!VYT zG!Kb|+JU_62Q6~>d|(ZVM!_Ap{!l~iNO~8ygJWbrq9Hr$^A&ecz3@VC;fuaWu#j^R zJW|a@s8So#YLuQ7vm3-#$@82us$A9TnOr2*{Cq_@C9<(hVQb@So){yJq|$vE6B}66Ff*cY(DH|Jb>5~2_qTlF zp!+PHf*7)xy%YTG;Wr}^o{kug_-G~Vouzh(cc-S3l<8XsPfDBWTZIVYEn!YlVRI+s zCM7rMecl7mmOR-7x?l=$Y0Oi&+6|8DaHKzP-}Vno{u%OoYP(nHJZBTQ5D(pQXcDqg zpw+At>A9ua>U3m#E3#JNtDHIH?9;F(@w~3G=wFDs;&SraJsscstI}$U`#jUy1pTDe zUItzXW(;z|Wqzwj-8&Rfu;#z#r#Vy?IHzEzXDu@s2~eA;onIPro=xO^FjpJra94Qj zySqR8I(k)~oa`UZd1a5<+ywEPE7ZHACQGAKfXat%oH%3k?e))irN{Ved+FUS zZ6B3QC;N)Pu!vV2@2Y_8XBtw?)xC{dC3u-O)xCZfAHlq*_hR_|69`u&`7|Xu{NCU) z@)Vmcq7f-T<$C|^ zA~RATR3Q!ZlG_UW9X&q-lX$h#6CX3Q#ckn!4wqf|rpLBU_iH$FuJ&!-PA49h*Sqk;7j1Au zeH{L5wv+m@3Y2S8>g2TK-(#14zS|#?5g9k5E@)dD!x|*04aaRc@1VOOXm5OiOa$7^ zPiDczFG)G}!T&Xu{`LGHWnjLc3`OCEbd2^M9zaF;p<{!B5NpRQEDET# zT~&on4U{GfqNjnS$Iw`yAvfsh%B_wO7O12Ac(|Ukb zb44;PO1!n`_5g=6<>HJwT{#T?By__0~S^fL#IQ){kl zNI96F`>+upSqNt&o*!@5SytrgYovuKenb7N3ynlhIsE%-;Y?T)l*$+NB%PlW-(VMB zLUinQ9a$dyQF!^DrIt1PVawm)+h#B1q-rE>QgPn)+90zzWC=_z(!Av;zc=AW9&l|i z%${GCE>vrNHx-6y4cHrq_q#gVF;b~Ts^1jgTpM*6NQ%Sp3eFJ6!-ncM6%=k|tsX+! zoV(uqB^2;Fi;u1pJbIe_s?s;0p4l{9AW$OEhy3#I6wI=#IHR~?p|NxsS$W7YkhiyH zC8%}ch4jy))5_WH9+`CyD3^$0hGuTbKI1$s#Es=}@OmeK2iCHJIqOwnm;W;B6<8z+ zVV4^_ov#ufjC&Db^|t5W@y)hIFYS)CNS0SY>vva!8rnl8OgMeJB)HX!3iOyc0y82h zgJoL$2js)WbBbX*Lz_V!b*_UI_5mksIT{@I>))-UpVE4Ucq_2%J}KtJaTc>*9Tl() z&}mg=gUbDQ*dvOUM!cy325|Fc&ozo*8(i@V3c{s@pi16y>+9r9zkeN3{~rE4>i}II zOXZEhkg+30sCc}7eNJefv#yC>oAbTCf0D71#2eYI|1R~!8K9;%p54D9$7paC6kMZK zHO6@@;+ZQLzLp#HE%${|xcIW>KU!Z2#na+7j`5AMkgCyY4IirAty*;Mt1t4?`2Yh@ z%;n?~nb;{@gX%>YP06obwuKLpp;MhD1mvtb`~&F4(juOBwfc9%UaCxO{_5xJl?UTz zfvNZ7ccaa18R7|WFSRfH6|G++jPUzahLC1|*zo~NgH2_pp=bN_b9YE+y040FhV0M9 zp4DbBx#vy9`MJ7R=_TJUGq;t?1pl;wM>%f=nh6Yylk;0)kK1VS{}8O8-ZK~J;VipD zME_J2sUyc;U`Oyoi=!)hpe84#47?~!$281#Oy96Ik}c~U_f3Abii3hf|29clID^*i zY}^zuH)XH)JVYua7yQ~J@dQChAnJx+6c7(7opr%G%OGiVmeAToqH_w_?dV>}@{*jZH9hMJ7YW`v6nAUy~%a^7aN{akiW%@wpxB;Yso zRB+G~z*%5jD}AKmPQY7XIxqLap$nZzi6pJCAbbdB#ybffAh^5N=sE6WqcUR0gtm9=)oSprX(2F5c5b2>Ze<&_^$W8+<2cUMf)$jK{&Pv26`p5eufPNB^Geu<$RqZA{plkVeAG9aATl3f2Huu1CA_c4F`_ULXNMiiXkL}K@4|j}B^mph zy>TfQ&9xY2tlW4C?Zy?OtTgf0bEnO~;pL{nFlAr$<1l6RUp7qkbxWoREh9)%ntf{P z0ubTIIOXr!?Z660mCetKrdH{;kdWIw2*^5^M2%lkgjWV@nOX92x!ju<^(x>e@vnKw zB7q~Kbd%tzPx*E@kVR8-HX67N!W#Nxa+XFmq#^R<+!a#e?L4?S|HK71;Q?y9KL1$5WBzCtt+Iufu$FIZjLoQjT z_o~Xlr*ICzMYJnt2Bf(dL3kuZ&pZNGF5r>%S=buU)X5kfs~%s2PygPKi5hiYoy)y; zakwo!WRw4@W##Q*9D~a^`niV8q4A+8?WQUc-Bx~jc_0SYzov+_S$AuMWyBggD&K-k zL_rgVWZ;z*^d{yhf#cE z>}l1oD8fHMmOHYifzJLiB(W@fpR-#%%_%N!-NM}7kX(=JKh1I=I%tj4lcRfju^lg? zkn{DaC;6yBj)$Tj6MW8st>e7W9x_P5O2RXwUYrz;Pi>*ZX&%lQ4F2>k_fHYC&mrBR za{WElZ@#m0TykpRbSR~d&e_Sle>j!^1o8sC~+-Vaxk9VUA z?OLMSyPKTvXm)%RL%!11^#cc_oi^lFB>2IqFODNwjH+aZc| z={7w3_&r~8ssDKyfGu$&xk-DX0v|za;!oz5`nhE*|K!;g$;M#*D~$)IbK<28E5;$2 zKFs4WXI;bZciZJutK&0GI;&Qva+lHS&qQM(jE!5$_v{3Qpv}7Orq61F+)agM2ha*U z?1tTFw=9NDx&txccGfSMdZ9{-&CpxV$3&I;??#swy7pcI3ErW{7d|=!{dQC0zev|6 zNbrAr_It*i&RU`mm4I8JC{TxyI*{XO@=2eCG&$eJve%Rs<=KIMAoymD- zV;CwVW=`h$T1}wk8(BH!5|W0#C%41j^9zmQELll0nLoU;;|_d#s&#=23jwt|F7!DC z==Oc-c?rQEYP=4+ljiZ-sl#7X9;aBEOw@%bxg6P!RbtCJ;!nNSBe12%pg>#Hp6Bo% z`bCF3iX?Z(xO7<;t~kdk+oUK9EZHpeXBXKz2|2ligp0WwARq&z*tqm+n^12{Mu4dL z%Bx9xOpnRHN*N~pwcU17Qj7m$2mv!faZ9hLmG`B>?lDFMjKl_eQ2Fqa?9bPD>Sxh~ zxgg@b^@k3aP6uPb6R!NSA(n_1u30p-5sgC=ES{K2X6@|P?sf4>n#{nccZl7)=@)IO zv6lIEUru<-LZOw7Hm1mysyVVTbcyuOK$iPr(zb{MF|ejvRF{hnGH$(P_&D=9t8kwEnGBc0u_R$&0r_f&Y5lTYJ%^2b7UE`JZ9 zy#8(ZH~9#`d}@qxZRz=>b-(EyJrY`QmnEUI*gi{ygygf~4;-Mied|AcP8QcMA*ZLT z_r+NI!^XoB?Liu(Pa_ZY&Kv(!=Nt#LA#~+c^)x1W!8;doR=r2tNr`u1XE`muJH5bf zJ^(p`y(^&nFyW@cW#>1+Qn+F;Z|l$pW4BdHV94MSy21p*`eoRntqpA7HI$aQPcUBa zI^3G7cG)NpBC-Mco(g$m%6E2{>6O66HHKwvS-oD!avU~GRB5jwo^QW?5;IdDQtdz$ z>n7P?AZ#Sh8kSS=mrF}Xox<=%CU&aF{IgyREvkYb7I%u2|E*5eJtoq>Mb`qtkv(29oTRRnzmcz9c2# z&Q)*FeG}keP-9s?is3DA2AJ~1o%PmOp|*>MR2XlSZl*0askRV!O>p5S z`CaXT&k(m|&q=LOns`;N8~%l+YcBWgqLTTY#S`iqjn*nap#8)l~f`^`Bvp$37}klPFH?OJ+tw= z^1X`EVHBnJmZyG}`t_zlVzNOsvfH!)c89A@z+kx8y^;CnEfQ=5qCM&a<9Pe;`Bm;X za)^7Ho)}l%ETi1^`~-rU&qxZ2@D?(Ot4*bqf}>e{Av;IN?W2HMzuZ%P z4X1!&SMRn_{1@bGYnOF@7(!dNBCE739Wa5kCe1=kRMP>G$2D;OdD+gc@g@{kq^NK6 zR_Rdsmr>TW`BB=o#Vx}~7W{>9-n0rZcG(sHtqXEcP=2GM*-BR-q(M6Z=L=8*)Uz{I z2-OtF)rWJ6LEl@%RTjPf_c3Z<627JNO44DH(dnL}STZeouVl;It~pWtI>^+sQVO&` z7A+>0uH~Gm{>=0Z?C*s*Mx!BMVpcX5uGd*#fte~X3JqlI$OuNSP^Tj0huz_PWcTjK zcQLI++A7|(MuZeyx(4vHe$lS79Amu}BaOJ2I4f$x0UC?1c>lZmwDGoNlim4l9P7vQ zkPeQig>pfrb~{&sbREwnW0$6j;rO;C{C4FfQX~6b>WlZN{Ko>sBKx+!wI%gpjUkgA zhf2~Ljgw_Q9={ikh?-LjW8tweMLjf*Ru!)(J=jK9r~Tvnr||`-)vH8d`X7yjw$hwG zMtCoD5VXreaIlnXAgJ#A4`;P7p_9#v zsM@1<4xtloCXsWLhfHT5L!r8wcMC8Aj<3XW#g5i~?>B7v z)#2=H*HaM+qcE5N>hXx+T6EALiXsb=RI24hqw)>Z`SOsel!L5@SCE z!V(2krvyc#L8l+o#7HmrvRqf%WfGhVO$}5|@f}nF)~&-L-uvyR_-Pj3<&Wc~Ujf-R zzv-DLz-RUgv_5*#51ISbA@-2GSfj@FZ5;^ONfO^eTRneRwD`jYAOsQ$-MmeGk3@S} zx5Li;4B1B$G3_e?d)R?X%_1$ZseJ1dd{EwK=J^+?&TVSm7k&U~I{7eBSvg@! zs}pRPCz%{KI^m|=?ZvPzsItk?^-0ogU)jeZe)-azF>12E5tXjbC{G67RbO~w^g!15 zHrL`d?6CHghV=!|T+3&9fue}|jC#YZ5-gs%Df>(yp<)3+l9_~Q4-E$wmHL*)%#6_R1u&Q@(&Vk}{A?UhE_Lnq76jdkl_ z`8Mwa84e9=!Z=h85<9wbde#?Y)hH5!ota0} zDbLc^|LR{o$YtVRz6Qk)#8e*^tRG%#dGN8pO@LdIEad8e2kwn5p0Ve{Ezif1M|zr5 zQ`#L@<3Jg0i1bTtg?ODW4uOsFP5in;An2(ty=1PPZ+sDf$Yq}#B6IgPST#%ZY~`pH zE#~7+(ejAi`G} z)g!(4{nD-__vw!${garLs^C~X^P*vnT6~Mh+-OoHgd6x?iTv&c5tB#C& zshs{{->87U8!3kH>O8d`NS}2DJxLr@ek3eH zTyA(1NB;UYAKIl6H11Q-I(}#a2%7>Pat9$nb4%*PGz?wP50L-f>%q=n$aO_~93#@n z8Z`2A=~}oZ8$GCThvc4{jR~1ILV&o9Gmf&7NJ< zHMYNmzVj|CJ-w$r3-hMkOLH=7eB$b`H?W%-f4NSX9e37}>D*ntw;G7UHa6Z0ZVcAS z?)G3G)*b%aSpWF++ZGao0ze_rzx2{S6!u4C(;iTMYr@QOp+ma};+FiI3qsdHH8h#E z^mVHACiP6@%%{&%Y03<_p9O2*x3xxkHn+bDDVAe1%gE9@MsDKO!OXb1kIP-3)!TM* z({VjDlPboji?A;Z|E0FsNuc63?>anvdh&W!OXD?(X#`Fvooe{*o$NzYFWpB#*5nuP zobI%GZtmFOUc0^Zkng#@_8YRqMXw+HKY7PZg94r1TOTm_0_(2v>4cI3CY|w{1JHNG3PXpU-d;*7skgZZk#Q{wRZ^nUR$4<<6Q#-?T8;T77TY zC;dh8veW{Zx5@nU@bAx{OuMLk{fU~}imun~Xomu-v_G@yNT@ufuVMf0 z?__q-~pJRVg$f4NbzJqU$PgC&#h=vSB!vU_VlzaAEN8AL_@OSmlb56tvDV z4{3gn#peU_c*m&CsR`Qo7ggD+3l$QqyD?hOPna|rS#uhs6I;;DP$|BrTKOm;xAH(^ zDKuLWF5TM4M41KbQ}wTso&Ky9J*`wP>CTqosg-;Gz$L!yS~&YOhDihA_H?-NJ>G-F z9Ct6ubW39&!I{omS9F@ZKBrNJJ}7U$jwWG=txo>uW_3zuq|OcEaW77uj_2T4ix+f| ziBN;stdOzuafH8F6Un##>(qmT6JIM$RlXZ0ySwhwaV){J@I?L*b%+;EU)fRV8JSxr z%r~`Civ2x9y_^l$K0?^{j}S7u10G_G-1N=d*STV(kA&np7F5SI<7eGR1%22!PefXaF^}K`s5^TB4y8jbLJTjvF=F&y0OxEnJ zT_UyZ^+CaNB+^^J;#G3wo4kXexI{BS23>Z&&r$-=FD#{V4xZyw*7zK<9Wn5C19l-Q zC8A$o;ZDaf@lj&=oo7tMbNi9$D|pA*v9!B#82*!g|Lhd z_wxB$9pgPiA1HK4xx$(!X1@1*Yf}*)XzZKXTYjv9iqh<2P@kPx;AlPrG@e#+P!9i- z`{*CZd+yfcIv-wuU6WlQ`GV=w2e<8~3Ozcm`*Lt(BoLYzj6$r6iTJeIBia{a?^a*G z-2hy;J;~M~Z7avQ%MfOaK%ti}4b@WlU2$q4dB6YXBsWRDQ zQORrLp5wXs@1riN^z485)i^$hKTmJjyA(FrIK%ds*(ZLyYm;@qR`$yGq9cV_?}yR1 z>@S`Y=61|O<1LZe-+qo}9c5$3fF}EW8BoeSNKUitgg^k9@$f=8dyb6KJiVzH`xR|D zx%b|ksGXr?6o)Btf59*yKt@=E5}Mth9aq1!{DmcOd)BEPr_$MJL&=HUzTJ| zL%n_B*A9Uj^0hfm-J`OkVFeu=GTu%#{?a92FY)+I!%8>xNv__E%((j#MtZ>IpYzr3 z7p)f(0T%#V>9^g%gtI4X+^Yy}2^_pc}A;dN4cYHyu5qz-#tH-fIfn99yo#X#L2`msIH=czqcc60t>{LlgId7g} zHM1(HSjqI1!h3#g73lkmiSj6Y7D!Tjz-$F!x~Xz|P)vn=BYtT2Fna+Z^sP-@Y@osx zncX^-SOWn+% zR!0<=Ju6W|6hEhPD9=d$Pzdw3*9+H836~ogp*B{4bxu`6i5ljo04{Zz+Qhek#i`*E`lb zacApVb~EYOD+zJ<-u@`fTAyQ?PrYHh(T$2a`J-rKh!E6xk?7I|gLK~>%-CpqLx{Zd zBKop&c4i)KF&Lk|AcnR`M-OWp#t$aNU*Q>i|GSu-JB#!p%Cut-a$E}=zwQlcSdzP* zxuFa_QHRsP#zDSzOljmC8ZO`KppF`up8+adS`6*peY;}ZTG>0k>$Bu~8lE&mKaP$~ zV-j&Gup-nTCe~`A;e1v|wOtjxZUi72V;7o`4e@9E=A+rX5~Gm$>n2e_p7Zw9jOiL& z8jF3#)W&xi4eXnO% zbXV1u(DOyF`X@6MkbK<$|Ku$;j!n1?gu5UJm)5n)uwL@{fs(vUtlPOh<%C~F&+f)& zX=brCH_d(72gZU{F}}84NQUNKM6kymvI}><xu55C-~T+O+4cIU4oZh%4C+R2Jl z*~OB^&e}xtMm~)!4JSJ%J3AjcHw}ZTxtW`(wSy(i8wZHBi=%_Rxq~a?^(eRQ{guHa zA*p!_gbB28DA2U~4}R7Ff$%_}ceIQ+F&46nu-FU+&%{`n>ry*=Xku(=JBevHN5{Vz z4Y%175a=$*p8+c}YgN{!U%--tKGNlzfVC2TE)B*fmEQtbt;a7+9dasjY#sU;=`bJZ z+yem`dL{ETh*0%9GYNV6kp#$sDO|dq=BBtjc=`R?hX)A0%g6rS2A_X@X zDhDMVvQ!SsDR7KwnieEBR-(}H& z{DEbHq@xLC%LseQDEfwIl@FLz2zZ9L-l(Z7Y5D54WYJ&78 zgA9;C#v~vANvF^Ss_c8pnS08yZ&(29-o2g;=$my?&i^Y}=){}<-_t9ru9u)!fG#_oDLS2L z->A}dx-ethXt)am*z}bubGr+-G?=>+EDR)R*Vo;#3VlZkxc?gA+Uy|ED-McwXNqn> z8>%cLE<9RbkqNNk1fYs*jr^aV_c#6mEaYjbMZAqaCf8@<8@2cYVo5SEC9(gG1TchT zEYlIqZ%P~Jt23Ci`n{GAD22u*)5B}k|H}KvE%FRg`dbA4 z06jvdlWl9U41m&^S;|fy)^*$e?mn*oyBSZCbfkWufkxgpA$*$iG=|DG3Mb|pM;Bu@ zRYateTwcIiRZ@DG>xY|_(||yMcsE}BZ+7F#f4w;S%cqyW7%F->dzi1Y!nS_F&C)hJ zNo)?li@T7Kqtcjt7Qat=1Co!4F25La0aX0A z;r3_;@F|D>D-G;Zs63_v(hw($8kdNMx`r0SUMInAraH)A(rsY^x-b!8MEvo8cdY*) z2Lym7@CK8yCgB{NDFI3nSbq)tFLIm-JEJMOqiN;KX%&Z=`wqC354nd3-l%XZsy#6n zdg3-hWUj_*Fv4R#qG3J;F`ue3*Qrx4 zU(%A%nz@_$AQKpo>&9N`8O8wUcBf^HEI14VBkR-a##@locD$$ zibuNaeKnC2V1VcLbHGBP_4SydpZzq(5!6s({4S0dm&Ihk0K(o~kO}}di4cyTBqrhF zT{N%5IWS2BLpbg~4Hy*1qzj7W#C#g}S@kCgQ0=A(jFpfjjs2`D0|SBlK^VZ_!;Hbt zPj0WzYs--{Zi0OyswjKZ#D=kIT(|IM@a*fs zuL-g&U?n+>r*Zw_j6e@yF6RB$qK3s8VA;dEvM~5tIe9wRxCCPneB4AK4^Ad03y9K{ z&4Z8X78SusgfJO{u4h#lultDuMr6tWyBQn?3=Xfl9=uXr)%v=JO;HChcpk7oTaK!6 zn+T2$3e!FT7@?^RW5=j&I7=0UX(5mM&B}>@S-}AUO9!Bmn^~~{Jrs)C$>eTC0fUe0 zMijxx)Z~B#+9>1!TWK#GP67scT#o@K0Yr6@*s=iI?~hv`nje9jZ>_rIKr|14&H4V* zjub9T#&(jx4jLFq03Ks|A{g%(j0B7$(E*g?y01%&l!1@0?Sd!A$Ovr9Z+`=O`MZFe zpNpWNkA-(Z61PDlfw3mN0tgzw27#Yd!vzo&fvT#X@Kir>AW{O}%E`s4DsZe`x7p;W z%5ikuwB?zHU$@}_lHtJCpAvv4!N>?72l~1KqrqU4f7ZfINT?1{*ioyy2sR zjp~Ly4OEqrrGv$rgmC~RfG<`zj3ZuMRW1+U3oGkTl>_45IMCLS1dIz9EWE5kS559( zT|k?zEpQORzyXddQiHNmac?HAT7xjY-MB0vpebwtm9U_4IDXfU<}##0x-5wgux zC9I#q6y#*+vWWx$KU3vo%z(XBeL>aKNX5Hbkr{u;fHAkWm2p1#s>Gv`1mu3#bbm3Frc7>%1l)QcVBuCRiZQ zODACSEmbiCe~A7D!1FdJ57;F*X2ixyU;^6&pt&GG;jmbB-Ip+c8BmqxU%lECfk1nAF+ksLlWR)z zGm<@IzFFff&<_C1;`g7LUZ0Etfee@@$@M(hGq4HC${&MdfKwSZuzm~xT{pfZC(C&4 z71y3}?P}M?|EFpT%;maDB6LHw<@orwYKnb5aa)dY-5aWHFZ`dXtsHI(~!XS_gudVU#Ig=yjKc)X^yD6>y zL+HIWPPo7-#dY5luoB?@@9%(K`A0lUCJ4X#$Yq-cCo+wo1ftm|mJTxTHlP|{J%E1# zL2wda8BdaAyk-FwstE3uES zC;7Jr;DFbB8Gi|?ayNc^E$Uhx;Ji=&B>)5641PmUmB6I?4*>=U0A}@<01WhR=~@a1 zGCBGd4%0jjDmfd~r3!*Lu5ITkpPURc?xt}RAJHV{Ft9jv%9;Io`<(F%!asQ_YH zoPiNOX%fyeq3ey4R|yD!pXl5634{O9c?NEZ9>z973-A1MfW$Umk!$fDJ7i2ta{< zZDR82FVz1F%)p0)7yQ5hT)U;XcQ`*{F-Pej6ZdxdAbxW6(*#wH7y|LA(YIl7?}9a* z0*L)@HZ4%ic6!~s!aV1?_7PBq}dX6eW&OpM- zY29@ooSPJsxrTGd=C#MmA%OEc=mQ9J2cMGwlknC1_t+r*S2CY(gC0D@rfhrj>J{tL ztRLT;6!_^=c|joYkY2pdkail53^In7Id}Zq?|t+Ebq!9+)Ytg4-q9ayH^7j-L#!-T`2~eVUrD@{l95wTdaJ7bpF0JAfBzfaS3vIX|M%A52gywn==#>+ zno(E4+Jg8Nnh=dvMx)2xxwyGIq0!T5Gz^W71$mj)m!NQp-{0;(-{wleM#Yy->tU36 zq*{aVF~5<$R!yoFkh*0y(655>f7gG7b!xr2{e_>EaooCQgP-mx?(IE3Z{~>y1zW}4 zw|*FhB%(+5DW41Vq33ZzF8dUyq9Rnn%R)Qv`#&NO@u>NlF+2UWd@meB<6%G^`@8zY zGk=0#a<1*~=gCKS-;P#dte)KB6ymCQZ$^t4n#;TIX_jepaG=3iGQ1fW?VR{F9Kw}_ z<>@-AzbY(hVMWs72^ZmF7*QZ;tX90^ZMyyl#V?S5o&axy$%!rPqY1#Dw>pt0a4sX}ve|Z&}G`!h@_g(apMO^8=c6{AVDl*U3w+ zIt526%+Ku1rGi_my6XKz4Ch^=Y-DsdnyZ@cGHd=?TJ!NTEc{~Zzym|9It~ay%Z79N za*XS}w;>a?Me(lN^Ybiyt0!=*FfQjo`^pVNpK(E(@9?Zpe#I=c0zCJy;QZdfR&0LJ z>g8L(KLz^bErV>I>2sam1#@dJN~spY;^h|HgfQ&%j+kRVj)32TxqH=x=K~|>5HK!_ z=7~S9ar35zrSoC6kkuK2`mc~n)8e~Q$l*-;s(ET#mi9c-(-Vq``}ZxiD_!gsi-rPQ z-f{X2R_tBHqvCR`zTS$$U*dFUn`Z4e++H!p-1zx+P#x`P%NM3aF#0Ysmih1OxIlT0<@1qeqpvakq79#~LBw-Qi3TB#(e0;qyNl zBPvAtDovzZTJ|Rm{DRax&EJ~~)aW*cb1`2b`c`zQ)QG#DT9B}^=PhFlN_2o_c#qvA6;V@VM*pby0-f&=Sl<>Zig6xv9CdA2WA`Npr_cy^-X`GHuk(Mw42PQ;!=14mF=`ZxmxxI-9v&Pr4CDyX@#S80 zUe;VfXA}CoocZkA#(UQV1}!EA+|KHpd=3-17ksy59mK}D;XcPoe11?cs(-6{)vk$o zhivoktgp^`+H*QShk1^2a;AsM_tIrTufKWayv?#`{;0l)+ho33Y=1U+g0C3u{6eEc z-d>N*h23$QB;|<`t1=T96;dqhVjClU zZP#-A{SncdSNE1H1DT&=rbvahQJ(tMg}GB3w6aN^DEJiyw!6`f!HN{w?3=-6j*CB{ zyNgDvxau9s!-z;`8b)?wT0CO%&b9UV_59r8_Um0mMjCfIqs!VpWNuH9p2s3)mCYBK zt7Uq-zTN(Kq>yy(JvSmuxF~GUjH)|r@j>+%c}*|nd+VH```HE#e@P3CQi${o3tsp= zhwjK8zA+Wq<#?d7Xt;uCdh0#smOk$y1kGuJFEy~Bwc~4#GCz=Olv^Jy{}RjT4-qwd zMM$-DH~t{ud=8)P4)-5soAoX$M!An!=WR%N4Wzms>S)a%X{_^bVa>;z$`O$+{B&a_ zIILO>9(BCnumrD*CjRQo%Fv_$`R8 z9?$!xtS7`2cWpk(-9txjp|Z?tYsp%Fe+JF+!ygaRp2)tsy!yeCE^7S;;kC4B+G8}| z?0w$jSli^Yra$lNjm8=oZ{8WFUhpgcH;OuaGi0sr-%ovLYM)5tq+$LEE%nE4Q$ZTt zW?1BXC5pBhE`OdOLr*%32c6_y&I!B0>XMQZ-H1@hOG3cWoBQQirQ(veyZx9$}$p5G>RM;cD0qKR;}+b-HDX<{+daNqC<>9mGe-G zSC(lgh_BbuO3`}0l>Nw+fTA8B6xHXb&|gsS$LD+|3xz^C)Ne)>RO6)QEa^G2*k7D+ z7njdjiXvK$Mx~>ucBgjxwNOih`Gh0!l|086+gY76C*j*%KIqw9u>>#P<64BOhI0S8 zlE-VMoEYX0(e)!Sl8a8tscU`Cap(FTmb7*hsU)H$^tkSj_M?8v)#4+kTzs5$;kY6Z zibwjjl%!E9$BUvq#oK|W4P7I`UzMAtnuQtF-%im=Dr<9Z1=}UR{=Bzdl&+s#eo>5U zKqC`&pZ}WM8E?$PqIRx2&ayWgDQ*xMgy5V?TDdZYfuXM?$UVSB{IzKTs^dmBea*M{ zrh@vxb3UxkdOk%`6dcSIpIF)%B}Xx7uZU(HCRgc-b=hpy$7m0PZWbM>?}z5`Y@c*= z=q9J#(R5Nzh){2ks+acodUpbIy?UKf5Mk3g;EEs6Pv~yEtRl3jJzUzGMJ(!Sgr_`1fS@KylRbD4L|PqlqSdn);v8}vd{}b zFXQFayu@og?tPj%etG=XA-(=N+ z@zd5(K5;y!O;3-RyL0nyoY<+7R8=lc9u+Er%nb>5R?MT@=zR~5>z*cInk(J~%@brn zaLys`4UE5`i%9QD+A5WeM!m?#?q|-&wLkCJsGCmmolGbD5)+sF-itnFF`-do3QSN)Y8>LVEc{Hm8)-SgQ)Rr>zgJ+`Hup+*ZkW_MI>`o$fx z7QN$dUY~3>*DN0V!VIyp38cp{4IfHT>KQCRQZNe4eJ7=E>gTg5nzN(Ea!pbWPu|d> zZ}YV}lfsqzusB7F;63YnB_)dc?yPczK)`xsq3#OTW7ik@7RC_aMeTfMv%}(J+iP#C zg;m=CT3_v?-`M?WGRr=x7#mHA_9hK%b893#Bq# zc_95nQUpy{S+|sYB^FfZ#Z(b{St^uBT*yl;&Za41;j2>7Pa%!Xs#B(OYbVQ%OL%G} zXFccXQ_aM7b>H_ksy((t5xVt`I0px7s0l$RSs_xqqlI~_ zX?0L*oXUWlIcL#*De(*0tMj|`72Q(!Spmhf8pgqnG1_D*K?Vx*E#*re2FC?KqTDgO zxNuKAk@sjPaMd*Sg}9aVPnUj|*H(jdyV{}lFN+*{8aNvd<-ZJA{;63gPw{(XWJw3LA7q>b|6y z1=lZxs?6X|eW~rUh6SJ?4X7(;K?59irERjF0PD{_qg6Gel3=g1twXGMW_o*-sBpLQ z3U)Jg7pYd~?0oZ)0rR_x1etafn$&?W+;fIe(X-0ng4ZVOq)>Hp8x*Jef}QuuPVSIv zk0xW^lgEm$r$|@rJC3oZIeagt6vj+h>x`CuKd!yR8(9od4 z(eIVg;7quHBdSkX{VF$O68Y^)%mW)ry>(y|W!c`gykx~^>WN~TU&bePU;NN$N}7I; zj4$M6^2-&Y>V9kM6m*u*~`ziBOO z-s1q1lxMaJUvBr;Abp8=$U`8L#!aNA=*tOgpGZbFtJ*?76Xygq-jcTA3N1zh;S0ge z2!5I4hTn2`sD9LWo5y_lla6>oy1|=uRi(c6?Ci?T&)u^TL2i5YI?H~X^fEQU6G^F( zxMycCCKUZaQV{kjG8LUVj{gJKKDdD1C+v}EadrzW=)PVZSnmQm(z7SgGido zeSZCP;ZM7FC06O5qBX5C_Rxo=L@b%rHZ}sGbhvSb{=k?wr&uJn=ORv4#_J`zLk~xS0$Ds+uNfF6FT3e zrC6iQ)8|f(5QkX7C`F8Nx8;Gpi=0}+S>Mr;4xh2hz9Ct@i9?23XPt%}6ArNj7Mzr< z&&R!U-TP966En$ZL^pbDEQ&u;1tHK}c!)y7aufOxT%j#JhQhYt9J#AIT)4aP=!JID zZA)Tmy@AAYE#-@Z7rW%!#rApiJF$tE8h`jcyDDLbaO!LOpxfOA4Ltq?2`JK}@(*tr zy-3Th<0Ih9$yiVQxm6|`yc936o3KO{Y%D&Usbb%ph+|1@T^_}~89Jm~axb{sVx^SJ zcx3ly_o(ptA6+9jM9>+{mm$>`{ZqsCFo>t&C_^hQHQKo|)SDhous@uRJWR6rl+~!~ z@}je|+hZmqr=`=zkj2X`N3=t>*m?C>LFg^RQ)m$9cHEUzmi4z@>G`RVN5T9f7`La* z+OWbk@utN#&WcY3kTvh%LGl3{odHu2Kp$M!bgC<+-} zQ&f5LOc<)i*=5^O`*GB_lsn#}6A`fR-kV>*!EHJy{#>}TfhCTPxQeV&DLdgJ+Ld3- zV`neVT{6I#cH1ByDcC630b%HjKOT6kS*{ju7`T%=ay+*?;`494( zd&D|t8`&e${ARTi)A8M@Ig^qo2R99Mq>mv#q{5#0g!x-DbOmKYXW!gb&)D?SbWi~E zu(V*XC4VHydzEc%Y#;g3U5B@^9lr6>SS++Yvic$A{7L07o9UPQd_rT+0~!YzMKx$K z){EbJrG03mUFqIimJanHEwIFi`-x{{j(hW41t#Re7fa-P3)958`=*ZHsZ2r;AL0%2 zXkSg8WnF1a1)cXK-=_>}gQd(nE%naRI2B(&diTS=hN;pMxiMm!5ztR#S>MDEeU9(p zE2X<9QZLoiu_@rL>rGQ#QZT{ksJNhuocR8k3wV)vVbRah=8LghE`q{jRR>x7X$y7~ zDTe1XSBc05p-(xqq~*niIclNhlb%;ugb0i1=GnK|HAf`AvNPjM&g7K$ZB#|{*FR#d z-peua9(1?O;5Bz*QX}ZMa|g}~E9bgqGy;s8&HAe;znq@+IC-HIGSAYq50_lZ*TNe@4H2^vK4f5k$lLu$RI3(y4}Av^DuIAo8WB~f`0yfo+e zbBwQE8)DX(Jg86_kM=ygf@GLE*PI*8sfeIXrhnCX$IBIKy}%b586nxVO=%#-j@s4x z$ddYdj9tZ?}edJFk6ee}g7<$PyAKXndg6v+nb!RRHE!B~wT^$UZ+H>lD!h`^%r7#duK!&s(m{E4#7n%lzsb_jT3FYB926gIMu!e99YXX`YVo zVe`H?U-d!jD0@htUAAfGJsd0bh?tIzBR+CVR)rXkeeE&TT(KZf%Uhl6i5`<@ZRmd_ zz>htarr(jOQadRvzGCjuzJMe0APncq!tB`YELyu5RQQ;$OYN|tN?`U1UYzCtMpPem zIz+KK?vjaiRWL?Sv-W0Co%!rHT(}U$d)J{;T&E|>;ofgV3U?X}Jol9bU*^6wN+VzW zJ!XJ%eyY#cf4^YoIaIemFIccK6EXXagC8~6VNlc5t1Wev5iRVu?TU}AqQt7?CBWF3 zziT+{zwMV7Y&}h7bgm4)6f2>gZU%ESpS5UG0GCxOrs$chJJexZn@Z-s3nRjiei7$r z>AfrSKE5eJB*%V|Pjxm6_;5h)!-iaL^$cIcMU5;N`?yOu9zihjN~>oTmFi}pU% zNb*eZF>N0;n2esWFtN=SJ@u1;qQFYC%K0xQm%qyWNG9Ei&E_X2v!<6o-dv_ zNI%{s+CCvS^j+4@$5li8IZOAQH*ny^Ko;c8@QxfhwT?8i`3Q90_cdXdB7VYTaW%($ zpM&yD_`Qc&>*HYPFSH9qI}Ovz;rb^N*r_czUB;v1kH8Fj;& zQM2qzYfJDao_f5xf}4IG8QS~^v9#}UZV&#MB)R}UWG!4R=lY64UzO2TbDYk7IM=))P*9z& zC|njr$3;6qH>#gx@Geh@DMj4=q|@OX;^gsF-%*p7V|o0z!?MSywy3Eol1~4C0!Qmm zqEj4gkP*ect<#|Ug{``_MQ@k1#C-cjYQWI9Iq+3m#Rx^)aJNZ+{$}D%n)(2APhm1i zvo6-nLV&>bITYqGyy?|Luvhca!i}dtA*x76>As;_TM6r)Xd&Js$9o{N9l^z4r9CFb z+q?O!>cq-?6BQTwHJ)gAcvSmt`qlZtds70_o~0F^3m2q?=;)4cn)iNL?uxKN(~qRL zt3{P(0~%Od;h&W2KB?D>&YC-wQH8776oQ+rd^It|AO>D?=W^DOvh(Iw-9@XEB}X~q zyeO!9)*Va*DlW{R2MyvA3l-TOi7*3BR8^l={m~2GikZV>^VH_5O@xx3wDJ}55CwNc zqFxC9`ME$_%sYA3ns|pfU+Ll^k6ObelqKkV=!=J-4W8v8sV|d_GBOWb`Y|QzL#+>Z zss3}qokf&eS4@oRwD2AE4V8`<6N^Z5Si?0E5~+%L*5F`-cHI>RJF>*Bt+oz@N9Va04)2*&7nGb#FeB`mGcUDW zQ9PI2@s;H}pFd^Eo))H+6K?c3=?Xa}(QNFX@b285|1h5!7Voc~A>dz7nL3dmy_tjG zNM*u2ckcGiwy?EKhs;1ZcSiE!+{j~>J>ZulWp{}+Thk{9zYpTYlK`EPi@mqxzPWd< zjFYu@>%RXqos|AvU&GHZH_uX>IY8jV!n&Q-he~#H<0>8}9R4`a{ZS^u|7xbuQ5AWGy~EWcE&#RKRH1zvgY zxvhYmk=ve``43=1-j^$HInIj^Ka>psG z?u?jEFFA3ANMx)CM2`6QWFpPkyWxWyS>pbG=gzNc~1F(x{Tpq&Ev<2VRy-fIJep>a+EGol zeSD25lChaJGM`r4iY3c+M;%RWitI>v1lRQRugIoJ>hEzOAd04wH=>wE-z=4+sSNMBTN8u`G$rhHXQNt`~!+d;Iz6AaZUJUeWOS#tVwS~sO%6N236SwL0T z9iBa$CBEPPvXirbgcnakYk-9IPSI`Q%TTn*Krg;~%h^_m^3@66_9e;Z!fF%``7P{@ z$Y*G)2g(%+y(qt?y1h~7W`tvXLV(duatSWQuD1iJy^x}{UW&YTgQaiejwLiRcOA9e zvi7<9fG73wh1&`_p}D zvv*)3lb~q?mJ-R}Y`f}vHd=wn*xS}M0hRB=Bn(+))Z3v6V6bI*UK42k(c)so4osKbTu1Srwf&SC+`;!sF{c$O} z$9WSI2jji15o6L(hizNjv%ti4n;O)IKu5Lt1I^@~%AGQH=(PTUoPvx=u{{W**ul>B zf}yXkJ=>X}qgoLnePEz|V{WMvb{Y>G>{qg*92g;yuuvL%;>RO!=^gRBnWNPE^2;Va z9D2qF;WX?X71^PBV0BmJ%5wAG?JB3l)9r$tSs>;jsWHt|sT$xC@8e+fwYQRPxuO;~ z-|BsWcOJ{Tr(CjnJmxe(n~Ut&Prulq#I79e{PlyH@+`l1Wb9IjugOVkA>tvE`RMN= z-uh#k#9s&Kp7Nf3*WS%#gR6dBF;qDSy6soAat!C-7EgaI#tDB7`g;N4FY!MY5WtHj zppa!gd2%Zcci@K=x6tSkH2Tch)eQ`Ba&>YCJ3-t$XcEMIa(oNh(H`6jyt+Kc$QBD347 zx#pvBlv1(&Z8(Dkpm%b!9Ousk3;O5B4UJ90ew$M0rir75b+?pxitx&>Nop(LgvoIx4xEqbkR^&PtVxsBC2r>620 zgeh;-A@&XSW4cqQgy!vX5Rj_G^S+i{zq6suo%as)J@IOOD-jqo2W`Ia1`$t{R6kTt z%as+X7QWfGnI+w>RI#8q(o?G?yz4ws(Z`pRtd{PY!p$3IC?%z2YE}Bt+SAvIFRQ=) zh{b3kh&i6^VnoY%u#sOO;!PgXZD&AK0lfN>Ok84<{6e$n%tWv@I}h50JvP}zDw|>z zmKVd$K}m&wQQ!0^f>V1RfrElo*fKY!V(gz?j=wP-td?NDl!i`6eXWM@E%c4ZH#^i< z%}~Y=Y#z0IJ)K=%oAK7|p%%59%gF1o3^3s9407i7xH7X`qZ%`J5ltc8X*tLSm-Xyg zW=&VR7&Yh3^)ip>cf)sm?>MqndE1WzjusCcd(sFy&O{DKQ?@FN1>m=_uWb#=Vzj^T z*x9i7{+2~7UFGVN+GNxC>pO;bIg`R3V!4!YSMNWtJlD9Pq! z>r~<-rAv#W@+-1S-T2}Kc7dN|9*&M!E4;4~V{&e<^uYWK|BkeGAJW-Ak3jk&VHw5; zzoWOVq34Wu4WC&ntcruLVV^k(o=UUldgL~Bx@CBfIn zkJ>Mh2hw118Qnys=%@X@J}KNtD(cU6#jUM(ak*O!W&90^J2X4sU)q3zIf4 z9Eg_YwzvtFbE_#m-|gp^`P1G*z=t!gH$nEi5yvIs!tm;Ws$HAOoguzPlDSfWNWOy7&!ZXRCi4}BJc4yr2_G^IKXSve8(E)Q zu0>UkYmy)x+m`D?w&+1?Z)hqTIvq8CDHZwDH$2_mQdH~r?Lk`*qc8sa*}s^t%H4z# zM^>CPMfP~^9TKl++UY?_Y0#@Xk)0nS7=vT(X_z)N>P)M8JNMusIbYzW-);heP8w3s zi|AaF=`ItMZscy;NY5j>wO2NCVx(2sy_{R$#GUvM2j`y6=_U@VtB)V&R;W)+b7j&K zGtG6j5o~>ZKzJ5etwhgvYgN&DoLHQqr?_ftR509lic_qJr`jevJgNV$C+o?tEd{&TTW!Pe03lsAgY(q2K1ZZ39y#?Z6yn z>+UmHU3ADzE&3xP`uLpiy&@!v-ZGQN#;}Q^ps9>2#O-ikO!(Jq3bq5QqfSCKk@fbW z+mdNXg)<-S*MQfOMLMlu*Ulmp!^DW`@U4V%H=gfWnkO=!iG_rUl)=~yIRX3?vsX0n zhdI&{iSPV_>gPUGEi6V)$Dx4OG}exgcV z5_-F34Lr=-zU#f&_Sj~F3&r5ch+6A2E^H)c%82~vg>4Ky(ivvQn){soVkYGoal>>8 z<(#oP>zVg@SRsCctL2KGJZqOoa!>Pj)0D`DVi!@`9_WMBpbWRd_L&bZ4k)1SsZVPi`WDCo>^Fk%o=vI2aP)f$y1E*%z2{qBpE6{` zsw^)b#oCEmeL2|TK-RZYhZctV9@(y!j3p~yMGhVM9Pc!qNfY_HUhdTm8aSLMP$_e5 zoYWypYizvIPA|30(Qa2~jnjtI6;d&V1Dy3Fa4rVO&SsUAuM2YWE*d88e+3@lqh39!3X59FV*ozg-4An65caoi~HiP-$`r${p(@zeEc$KM6QcLR8up`7x zkFuDGMvC>4v&hK5Rz4T<;^v_Lm{(y{ICCdSU+)vgm?usB@X(U%Z(2q z;ri6W3K_jq1Uz1G*OTh}D4SmU-PV-7^!68qcw-*xU2@V@p8TcY>afoWQCGUI_vdm@ z&ut$WR!TPDRtb+cnl_BH7DZViEitj5th`xXbX*`QWYrSS}Es*8>whT73`-}1skIfBOzPOW1R6Z-HUKS z`W~jyS_=)htFS8-Di1^!JwRIvtMEMnP+r@)IMKTP*So?&V)uEhn1k;FTA(X zPZlqxQMP_k91e6w?)SR4AyY&G4pq3s1qPl4b%I8awtC`Dl?wTx@XSMkGyw^3Rdq0 zXzC(%=R2o`y<_-f`AB>Hf;m3-|3s%`Pg!Nh85rnHcewe0;EYy#^0oe^4t;1cz2qmc zMe3o9$+Dw^=Lv2tr(Q|!#}`PZjdSDUsMXKa=TV)b`s3iKBz1`zbS89B`Od=ERl)#w zPj0_)42}Bl6-m4O^_Rm=;D-i}-uVs$QqUy69Br<7^MZbIY(wZpiPEm^(tvTh32fqClq3u$_zw+GRmqJ?mB`27}Zk-&Q?=u5PxZJu4TPG#TDews7S z_BdA*Ep?yjMJ^0~v%8LKDmN1QWnc3XV%)}hjP?1Uajjr6Xi$!2E40VM!vRYI93?*K zb~YY<)7GMpQrb+=tg!P$ydAic?7#gzjJ$f^+TF1x{=yGaKkor|&!ZCNy1=`<7;Cd# z^ZV23nwK5Wi>7+sq$q0XaUCQ3Ai}=cS~<$Q0X93{^KMzVh(3~C#zEVlF_Mlj9OX|c z@>fh0>}TCL?pk%BO%4}xGZN1SxC&Yixdik+k!}$LkEog5o~BX|ny2zTEB2enTmXgF z$~=uuUw-fi&9|y2W>+%~$_r)x-3clJh%*-{|f$){zEKry|_fTe#2n9Zjv`%#g_ZL;ug?5WW zO|X_IBX5V7)g)+xJlP-ZR+9;!1K+6Yo_Y>HJZdigfjD1>=V%=VU?eDQ&cIUA$7 z1(aD?buDKSwq1pj+n#6Cq>lRVHPAH3uIMX!a_S^LUTHnH#ckU4Pxu@bpuZC_>+8*^ zAR=gwh8rnkm$;(nyoy-`6xdz!TxvVr+P6LrmL9q09vTaVlsL*st$N}@CKMN*Ov_Mr zMoKBMHPysbJ;*sM-d@kRj46v9Gn^u-H}W`eIO`jRFAE3bNT^^ykdQ_dt|-x%+6G`WOCm6*2!2!_nujO2xD?f zr2(Ww=*6(@a&Qt`T3LwPJ_JfElze2VmmFWh*71T0x|==zmgEF+*lD}^ITZvo+{_!e zrJkoSwS<%R;9IkpV@{jeR!L)H0n_fovj+o>^TmD6#@=2&GpX5E3GOF0SC@6R((cA+ za|a&h*D*i!2kN>M6>Ky$a+7?6cgP$2wxoZ*Gs?ImAUE^-bjCZDSVHFa#r-8-?$lDt zh>wH!vJz|R0F1C~&}5)9BJ_if4UW@kPX~8nL92tiuXv*p`iOI< z$rfS7lll{PdS$Hy93x4$LEc9w$ z2BZ6*mmq+Dt#JKZ|4IY+wH-*qYUvi8hr5ldi<^_Jvz?2h3)s=g%+k@-*~;7r>KX!IH6%lzl#6N`5zcuhu^_45_ zVDX>Y`_K`j$2Q7*yFbjM{&wvHlaEv<>T+9Sv&YpRv2jeMUu4zY@2!p1dTsh#Lx0#9 ziq7?BUoM<<%x(Xq7)h@Si3o3Kgg@zbHskc0v{t4T-amJ8(r#j32=~bI(%nm+H}WBV zuIJVAD}6I$;pqE9!^f7%kn+cgE(OTn)FL(VF!KFhh{3N6^3+X|$D@~(wJsS%-n?y+ zyPQ;!6B7cy-!K3C?m04;KA;~^^L|&nWTYn~w)*gFW@3eIVDaSe=-e1TE=F>*N}kv7 zj7hlQ!q>1h$&-0@)htQL#NyjtUX6rt(B8`I&%7c+$MNyN6x<=JxsV?|`XulZmA^ zdG~gg_~5U*bEFKsj|-1m40y~9C34nlifm$b8}l+*JG;;NQCHi&q3>$_BCzkd*0&_% zQG>xPk*Ce5E$CzG_jK7s*)kTgld4?NA9ewZZ8W%RpPjAuTr5l+3NrQ72OCM5T2qaO zI4+b8Z08&nwN(V$?}}{NA1)IoBB=V-b)@aGG+?0$!~ED2b-$uG%eJRl3Cd;}M8@Gi zuRKmy$16ky6P(tN@p=265#G-O;)E=gr0^TcGSj901jfST#_L_nSj~>z)>n3V(l_6n zbS`ZhWc4#PiBc!1=AX<6DpBtUDaNdgr3fHuus-An$;!}gJKxuu39Y`>7b>|tQCP*5 zQTaHpeu-Dlvrp6h5YA6!6l0lf0{*%3AXWb2h)ccZ!-K%^GS?rvzi=_1uUdef)^`%8 zJ8bzVv@a+7x#nUfIrEM4w)H&8t!n1x!@ASMBNjW{dh+-})UUoYASDv`=5>tn%@p(M zRX$Nvj;vRyUN0hNvd7`9pS90;Dcfz`DTuo}6Qgz*zIW=W zV%^LRXU$RmX=J96?hfO3C1<(IOLBK%!{dJub)G>@eNnr&^A{1U6lp37D!unaMM0@5 zQly36dkGy>6qFXFOO;+jKuUlB5h(!zM0yDjkzPW8G(t#neec{m@0r;%=iB+P=bW?l zv!3;Pw9L3Y!ru*GOL5>VfVE0a+RdVT#~UB&OOo+iMHcVzFdw71k2PGlct~|HK?sl8 z9v8#udEm}wnDPc(O2?lEhoQ%|@e$J9UGcM)z*t&j-h)n{d4ppd7qm(s_Fm;VL&MP< zIGf6~aBWVE(KXpe{M#O=V^QL8VE1?QSy|z9pR|6?CYAPUPKh=Ur7r5?SxO2Qm&+5izt13`F{SV zcZPS5rlXf%(lFBGgAWJml!9h7Vka-OV?iN2?+*MquO)r$&W+tL5rwR+TIhmT_O1a+ zsYzK=GvJw=T(Oy|k)+|}JR!$khLe^z@zSQ!vu3$%~LUKEh{B%zwu2)jI13@Iqtj$G8t-j+fZ zx$k$O67C=6n|{K;-Ssszt6=SvLymJeO4;l$QG?aovCmA%j}tsl6XGst-c*Pl&xb99 zihE`FzMqG)+!}sB?&eq%e@2)kGi=D;>j^nQ0NT*^GyoYi2SaU)ZJ5e~pcYmeO%_5; zlizLIA%u7tdx<{Mrdw;BdAF@&Wfr7QyUOX*7y(VeM<|Zr$r0i3+-R$M(%LX{p(3(8 zy{>07X|;01UTUXtthq}ocy?{19zJ)qTs_jqf9ECvB-v(GDrY*a_CQ-?V(}g}{vhg| zJyKekf=Na79P(gUo|H&4bso&q=)>%tuNsx4xAdFy+}{awR71@)`3leW_T!a26(g26 z{yaf}xw35ftG37DxuT01J+fEewP_BHW>>L|rMFP_!u8XyEcv<=NKyfaNePXf&Zixe zDU^C&BNB+YeOrDJ^gzAi!9S{wRqKi07vBo~CO8DI+-jq0qXyAaqCxFxs3xjTDp9#o zPOc3~OWT`Baa{m@`{Ppn$Z4Y1WfwPcngvoG6JlA!RM9aEW^3T`caP|FVWn$A|k`-Pp2f5Te@u2Re{eh`H@mA z`y|84ACl2C+Ud{c>Yr{p_AURt@Vw-aKGgi()#CTVFCy;acU+F znQ@wGV3axq;0t|LZ<(Vo&1`XF%|cyr0%t~qp6mWF6I6Tg?-~ zsM-G>gz#w^($VulgD~A?qq)7D2IX(o^-+yNFcfrU!p=s5pLozEt4n*%@Z9}1Ao?{3 z9iyLS_kDU!DC0o=9lfUo1%fnfDtlM>bY-dL@p;yg`fDJ^WSM!4zT z?qUL?KK81UeazsjHgHrJvi}@gClu#_uHF1oycPHY?Q@?pfB5X}0Xb_kW%T5k)-y@5 zP9^NRGb}9f=unNoNE+&>4y6x4@Sggd-!ZU63002 zXLybbngMMhWqrgec1jv=n|gyU{EVjWkzYs;M=^Waj{W;RkddryghV#8U0`^lQ%Ge7 zG%uMC*T@dq))eWR2(0}$Mss(+BQ!!Pj^=!3>m~8)L8T5zSna{Hs035|J+8d5=bK1k zpE2*B>hz-=lj5KWiLmiZm5A94YTAk8l8V1jjt|6ZBvgLvzU7`Li(*nZ?MHy5FG9!F zi_}w&wu>n!wc4owK+>pNb^)b=!)WSf#A@?aYXKwQ`F-K9Xg{ska9eAQ_P&7e#X;SPZ8IV|)9LVN%O*;~VU<%MS$HWp<;}IqhvyZ!?*IFz`tkyg1CR(a3Y3u} zUD_5se;6giDLfD=V-B}l= z-4PEPoWf)avjhK~QJF75JzP;?az4$>1H3~{M+Z4NfH0p9!gaKxBQvz!4Hq5A-DVHs zP`zs4N2Q#4m6x+QLRH)ncjRM!2Zcd`S|sR_BH<5)D`F9Og^#M1J=Yp8$T^VAlj_hU z^R|Cry))2=UQdlI!#1bxF&siTxtFt;lY;5qjW?#jAj+L!M5S2Xu%Ft_L?j%bi}Q|P zF5xVOK2~0pC(uy17RA+q%`A|2hm(quHc9Sl*+Q06jq&GSYF=iom=8wi=CohY#n{(9 zr7wBd$NU-{u7QE0DMO(ND%9Gj;xnZhy}$U+bA2EO6KEgtVc;Ng*`X7QEsrX2PkNFF zFv|QO5XB^#e+n0`7zsvtde#GTwf$>`o3?UJz~){}$2 z(NW8K$}2*GQfk+^?V%HPsfNCb^|S5<;G~2o6Q9`NAu=sOX1-$}lJhE`iBF7acW^8x z>F+-F5gb_KWztuIo?g^p^YXTl{TaX%GIS62p|opmX@JL!|68ePw7*UGeC8x*mDoYB z!kM=LJc)ZW4aUK&x(Wyv5&p*OUq_>IKjcS5ptSnFweP=Chqm0FoYgILn{#yw5=G^{ zsEao5nZ+D9i>KonUNfhvnTX3kI=ua!SB&J%l{Ohw(oXD zUT}oWB4>$Bs?0yb1_g`55LQSui}+1vmn#n*4o}(0&NuSE5uu^_CcgW#+E|&OT98m? zW`nDMR7KNQrejIrZL)4ZvAn>^N$GcL|E&qmqp~?&wVk(f`2N47CzzLGpg$&863}s8 zCj?64T?v{867QY?%Z@BMq$GM)RD2C#-KOEgadkX6R& zcN7exi;AgTY05U&c6eVvCQ1`D%^!=4&S|x#tYrQWE2BGr5|0UwCoa26G{0J1G^u=Y zFsCA?nt!Go#!@?RTr>D`_uIr~zf3lh2`g85amn!z+Oe9GH&N|0X*ns2#S_QhyKF+S3tAxaoXIBDtQ&X8iibdQw$T zn#bG`AZtdEFdpj&{LU?YI?wg&|Fb~2{9pCq|H=>N{@p(3JUe?gFfb@ID8$FnGZ5?- z92gYn?d|LRZ+ZIry1M##`1=I}di(oxKnMLoZ@k17)PM0(Y4g!FP}V7qlWisUZw)eo zD{b$@LM#G-@Q<({KyoMH%B91vL@Hn zWFjM|69oM-`{Rsh%EGFpfB`-Ww4Q$)1oz6l+a-rp2wgw+k)C)HU-(d}C{WI(n=RsR zWH$d=59U9x=BM&;HYP5Zv#drtY!IUBALW+%a?G&Mj0_NA5Rv(CNu$^IFRe7{)*E;` za`1)VQi=!=^aW5DRyOz}q}Ry9$PT5G`O9E-MQ^|Z;pOheqs-3s-cMU_Bv;+%Bb+N? zzgxUrDM#Y{Q&^s3YvndW!N1;F`cc6b2-Gh~#X@q;`Qj5~^<(MC2^qV|HCe>Pm-`un zF$U4`YSFS(85zi~nUdfTOdC0oYVQXmi<#2`;1=0Q^lqIiS6^gN{uoU3<@ z1yT$2!YbKtoVHfJ~ETFy0js;%s^Gn9Uo5Lseb|r(g|Q z9WfSyaupMu-@uOMf#9uPVoVPWwgLw}9gqCB8XFY0zWL+(Bt`MC zhQ61@MR~ob*%nsV%vX%?$Pqp|12lkI(nkL zW{@<*Z4qo?Mc1YLF{ITX%vek|&={j@IL`!u3UKch%U<@KlTge#8oRn&o1xzPSlzUt z@l|!;-ZHLz%OpK|g2Q4%qes{A?*=?zGz0JDMsBRK$ou$k&C^}3gRKE0HN%=c@_Zs0 z7TK!N(_RDKc-X}Ewx!k|7{Bx=Ujol^nqJ#v?qu(nEr>}BY*ZRmF>E37t{{{91K+2r zm(K+E_Ptm4Dhg81;ZG`076T*Pt08C?dgI^tOu}hTK>gH}>HU8A$SZDGwUKblv1{N7 z4k)R}j@M1f{?^Z$81A zIr=t`V#)7{YfHM<%vLNe7!gHm#QLw-GiU!Y$1eaU`sURX2lI9D3f&G*uKsY&KHV;ui35)zR_MD+4FzELW$$|2Vi09x;^q^=!p;<<$Dw~; zGh335!xN#AniHoK_imJ7TB_F?N|8pibgHjL{%UDjK<6C5kG!A5w0|swogP&uq7bt{mPAoH|GANb`j9UCtj^z_Ji%=(9#&B5K zJ?YT(AqlTY8dxGFw{PgJi(MP2h@bTa{3m4eb(hDq>Y5FBedI~*F=ztp+j*_;)3rbFHpZB_wC)#yDM2<(cmeJ6ae4JIwq}lyKF5*SBHjrxV8Py6&B`DDg1nY zcF&8SE>898bQ90Z4&J6q+N`8-Tg z3%f4~?(<*?+Di<1ZWpuMKm}Zb+MdKE&87WU9Kw2T`w&07TfZ>VbkRukPUaMI-lULrMSylMQkyrAv+GW6?}$M@8rW} zN2VaOcWJl@D--_8Bh? zIA4Z9kh+WWUiB+0ZSTw?8ZIQPC-;FJ_S92wFgeU$fu-y%jx8V;RpQ z>KpHMGJoF{PfU^2bXVK78;#B64w_;0_6&B>qa4&5(>*nWbKI4Pwm&P!kDKec)GRJW zyHw#^hqj*M>&-YJ%9e9%DZUmC?=GF6lmKZi%CPBqmxZso3CCQ2aB`*QZC_$ck;t9J z=y9XCD58_BweYbc?QkZZ1*ma$Xw7?Nex;gIecTOT@rp)|*qmZctVWQmkgHY#hVQj= z%tfbacNYF!-mRL7VBXG&bTVOhd2}xumiV_RJ`w>*jM;3!TGiWUu1$WbESUT6(y5`w zdk{Ox2Ym1(Wm$;)0nyZtY9 zfAlOc^=_TJDkZa1vd4K}QR|`3PIUJ%<$*Xg9MEATZA6a_3ZBIio#A}r3O*^RY=kg- zwJK=PkI4eh**6L&J3dl@o_B9dNuy-s9AdR1tG03JwFkd?lq@r+;^u2Avt-w88gE~3 zJ}SxWS(`n(Ja|lmMGK0Y+NpV=dGFE{*UN3lZ5$CdLEJs^V}#ADl_IG@pa4S1b{520 zg$+RhjteyL4>-hR;6;Ir+r^z;IA^I@1Bp!+9?SE@v>NN@h&w@AIQig#Pi&biEm79u zjmXt|Eb6gPK;ADG)=9icc^~Q~==6!5x))quWcc1s9sI^1XG6B}`$P@>UO8Djq$PVy zsZ?zL85hMTaDA_hh+f|A8Zhb>@rlyKtgYIrpS$vo{F&Y|AaySASs2|K<2heFpW0)8 zbE{mxhre&jM~fDRw@j?eYLs3uomGmxmvCaI#(coQ>Z{m8+i0`>=pQ9S(hRy-tcT3# z2HK?9htd?1+MZ!^z8X^dxH!tYJbkA5$Dyg2p2bF9oX~|2{hE`ha;j>pJQ91IPv?Wv zfA7FXL8q6FxspHyD`nSTw39GRN6zKc(yAPjL3#&o4K(fCN}VesCT8Q>7zblx6aKU} zBoI-4C^EN(@j;Kr9dwLL1eU~FK>apV%cXybyKzSAblm74U%pIiFATBvMmCH_1YBgi zTbNtEl2N%bUYU;(e;z#wPr|r3{I2CL-SZo`@#aWITVmHh?0we#LFKs9^L2sQrj*W? z0I|MW(q8hY)(unZ#es2;t7hnfy`kCkhJnz=Qjs57Ky@Oe79Ll24g=4b6snX{78)tN zGB@u$v@_N|Nu9fOK6=06+u>k1ben*_qsd{Ud)nt)ZO{|ALj+s&cdwC?$L+T7?BFNQ zlBDO4#y$?GgMz$Ah8wZ6lCQ!W?E?!mgbX%DuGju!Z>#1d=ZAu{*E>0yyY99wW{azL z%HWmrEYoOXmYHWV>s5yF1j%^<0;P^i$BMA6%f2h?^s}74N@O0(IG*OcR`2R z|NS&h8G~#~g;)=?F@egIWCZXJ#gbu;PWEaz^82;jPMk`DpNj+}=1%r3AGz@msBzx* zsU7d_VJhi8+kdddFP+`s3bZ5W*oW$30kY45zVi^UiaH@6MQL2^8}kVjwKadPGJH#+ z()^_SHMb1g+uqT*)y`-_KZ&VDoH=6lS#u6&3!CK(i=l7y?3&dkuh#K$m_@sPO&1|T zc~FrWYR12;xizLvrsuM~;}*hoxBha~qUJ29>U4BM!8oH#o6OHPe47b(Rvs7(!Hf%d zuaN4#PtW+j*S++pYg0#RhzH>VCk{ZuY>_V0y8}r_$xOc;*0r`~mX+^oZh0i%;EFk` zRch7ab8xAd?rpzqLzI&B*j3X2(mx{5`7#Kf4`Fm~tZ#$&~ zUjpFCY!fTE@ZO+lVygC%4g;+9^SsY;`}E`r{#9<1TUKeqa(qAl@{vkdaCj8ZEIYFg z`^?1&GtMUC_c4#@8nY;Wp~>LXnfkZM7EMkT*EVeV!)CwkCai0OM|OR(Q=6G?U2`ed z4A-<0;u?y)6Do{p1I%<<_}tBpRS?8v$La88)Y+eHT`=0}ZM+)$>~16Zzrnrhp{Rs~ z$K2GVyGjVy#3ENVlnA?Zmw0q^Mb% z3D?@1C!o)5EELV5OYzK3Y?rb>aJc@!myl7@1DyqqXPYc}`sG_DG*K3i>rUaRTT8sP zK~wBlalw1D$L453B**{Hxd{7TG2$PU_k^t?aO19b;6KlUe~@>ehnG)ipm#uMh%4CN z+XW2v`v6Vcn&Qx zuv;0e4yjfDDO4VM?v_zTp=RKf4nUnn#=^T3hj6&tTJgF1hwzPFneuOpHip4^& zZ^ea4x75MPvEAWDeAyLn5s1VN>?`LvZl28cP5)e}Mp@HK7lAMCC>kI1W?C_nn(nfpE|olM7>pk~)gaRQtU zPV0Xx#%vos_?23{a*xq>_8S;sooRFX(eGZ}yS9F)7R2~H+rl6>yj*}_O_f9Lc4A^# z1(ZY(^)r~`b^V|`_2$#kcXd%x!h9oA6_~;0)X8PKW$7-dEZlf;fzycd{VHI6lRn{! zxAD+2OsURtg(M|OCKHfZ90)I@!BBN>goCw9{hsTZvY7D8OGjgFnJW&P=z0~ttJWVr zR-RG1y$MyyIkq{9T%BM_op5X#Z1G9c{Un7$mE!2?JJX?U8HLf{=m3Zi2-XmNEW%mI zQmr9m7w|8JE^p3AYa?NEa*i@BE1Fr zi<_ojE*p97wxK+PPwyVR+FPo{GtqPW458sI75#ell6H^A+e&4SVzc0h^=?8q0-Xdr znvyp36Ur6Qtk|16le*NHKhgl1N!Ad!5*KrAoB^KG0oQu0KF`bES^3?CY@yglTD(r} zTar|d=ojR=7dxvi!+_-)6dKHCXAAl%OGQ>!{4_x z)mQI>RD<>3j+<1}UC#HHoRp5*1`1E`2H1KrnS(Xx>!~`1lWL!@Dme!K{T%ZIN>aEW zdTyGcwf{0`zz!GDa&2)Ak*2ixq#dIsTPhowuxydo>n?@`=eTd0%j9j0 zfcE={`44!sw~s7{^MRNUE^fhEQBHaC?cT51>K;Jyev&)LosK=xq!FqQf0ZSu^saKK z!n3}lVyDJB^G}EB4#;{D(7pgXeC1BqryLEB>~AtW772Rtgf`d6j((=Cgt;Wyem-fU zPn~-Wv1U-4uSG3Pm>tSElXE?C`Gt$Mu@GPj*woMx`>qIO52>uv41F?Ozmp}>Nk(Nh z(K)XqfzNtEbDwGOdn!7~eEnMVb?pRXVI&&gIUf3EDTL@SD}SaL+h^}KJzC6?!JuTF z1oez9T^8l%Mx{Ig>v!4YjO`djC8sM7NiURVrFp66T2-r-xWg}qRKC0a7SIG`P1Ry= zhx6$!o_o2Zu=DU{1Sqog@+R<&L?HZxe!c6-3O}dsjt`NOIMxxA4*tp`%<}fxV(KK~ zM^6q{7`*bZe1O5{IWnt%{SZp5F|@)>x2hau$8Api*{j-A&kJq6K44K|bsD3s=a)I( ztjDwp7O%XR*2KOk**CJrX#XY5AEhBIV}My=T1Xy&)`!jrRO-IC)N=;d&8RRVSRHL1 z6i}(;{iif?k|1mMDo-TA=z7ef@YDi2!jw~O_Wn@K z?)&lu1M~h53KE6{n~=QN!givel?-dIhEYC-5g&fc$c`*yWIm?ME|h}_*!z<)U0>CI zr3O?-$Yc~hDoDi1hBs4%^j4Yb3Ao?A2Pe~!;U*cL3$?qSdy#_mLF~&5KXLD}20u&P zTKGcjDmq}hx8l!8-#fEYquWmY`SjWs(K~fWKB`kB>`tR(h2?(E~TGK0!0>o?wvKho26Ne&CkLN%CJbD~nc4xz&(B*;r4!Y5KbJz

Rm;+rLYzNcc+E1pe0C9bit2 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 pT8}>{|7@%mm&ZF literal 0 HcmV?d00001 diff --git a/Resources/Audio/Weapons/glug.ogg b/Resources/Audio/Weapons/glug.ogg new file mode 100644 index 0000000000000000000000000000000000000000..424bedd35169c7adffd61011c23b4b375429d92f GIT binary patch literal 27517 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 P>2DsC+9J2}@%aA-FPz9A literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/storage/components/secret-stash-component.ftl b/Resources/Locale/en-US/storage/components/secret-stash-component.ftl index d41933b3744..d0dfed2b5dd 100644 --- a/Resources/Locale/en-US/storage/components/secret-stash-component.ftl +++ b/Resources/Locale/en-US/storage/components/secret-stash-component.ftl @@ -5,6 +5,7 @@ comp-secret-stash-action-hide-success = You hide { THE($item) } in { $this } comp-secret-stash-action-hide-container-not-empty = There's already something in here?! comp-secret-stash-action-hide-item-too-big = { THE($item) } is too big to fit in {$stash}! comp-secret-stash-action-get-item-found-something = There was something inside {$stash}! +comp-secret-stash-on-examine-found-hidden-item = There is something hidden inside. secret-stash-part-plant = the plant secret-stash-part-toilet = the toilet cistern diff --git a/Resources/Locale/en-US/toilet/toilet-component.ftl b/Resources/Locale/en-US/toilet/toilet-component.ftl index 7807759e42c..1129f61b4d7 100644 --- a/Resources/Locale/en-US/toilet/toilet-component.ftl +++ b/Resources/Locale/en-US/toilet/toilet-component.ftl @@ -7,3 +7,5 @@ toilet-component-suicide-message-others = {CAPITALIZE(THE($victim))} bashes them toilet-component-suicide-message = You bash yourself with {THE($owner)}! toilet-seat-close = Close Seat toilet-seat-open = Open Seat + +plunger-unblock = You unblock the {THE($target)}! diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index db08481dc53..cad2d9a9245 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -622,6 +622,15 @@ damage: types: Blunt: 3 + - type: Plunger + +- type: weightedRandomEntity + id: PlungerLoot + weights: + RandomSpawner100: 56 + SpacemenFigureSpawner: 28 + SpawnMobCockroach: 5 + MaintenanceToolSpawner: 5 - type: entity parent: BaseItem diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index a6f14b383cb..f02b1262489 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -2,48 +2,100 @@ name: toilet id: ToiletEmpty suffix: Empty - parent: SeatBase + parent: [ DisposalUnitBase, SeatBase ] description: The HT-451, a torque rotation-based, waste disposal unit for small matter. This one seems remarkably clean. components: - - type: MeleeSound - soundGroups: - Brute: - path: - "/Audio/Weapons/slash.ogg" - - type: Anchorable - type: Sprite sprite: Structures/Furniture/toilet.rsi - state: closed_toilet_seat_up + layers: + - state: condisposal + map: [ "enum.DisposalUnitVisualLayers.Unanchored" ] + - state: disposal + map: [ "enum.DisposalUnitVisualLayers.Base" ] + - state: disposal-flush + map: [ "enum.DisposalUnitVisualLayers.OverlayFlush" ] + - state: dispover-charge + map: [ "enum.DisposalUnitVisualLayers.OverlayCharging" ] + - state: dispover-ready + map: [ "enum.DisposalUnitVisualLayers.OverlayReady" ] + - state: dispover-full + map: [ "enum.DisposalUnitVisualLayers.OverlayFull" ] + - state: dispover-handle + map: [ "enum.DisposalUnitVisualLayers.OverlayEngaged" ] + - map: [ "DoorVisualState.DoorOpen" ] + - map: [ "SeatVisualState.SeatUp" ] + - type: Rotatable + - type: Transform + noRot: false + - type: Strap + whitelist: + components: + - HumanoidAppearance + - type: DisposalUnit + autoEngageEnabled: false + noUI: true + blacklist: + components: + - HumanoidAppearance + - Plunger + - SolutionTransfer + whitelist: + components: + - Item + soundFlush: /Audio/Effects/Fluids/flush.ogg + soundInsert: /Audio/Effects/Fluids/splash.ogg - type: Toilet - - type: SecretStash - secretPartName: secret-stash-part-toilet - type: ContainerContainer containers: stash: !type:ContainerSlot {} - - type: SolutionContainerManager - solutions: - drainBuffer: - maxVol: 500 - toilet: - maxVol: 250 - - type: Transform - anchored: true + disposals: !type:Container - type: Physics bodyType: Static - type: Construction graph: Toilet node: toilet + - type: PlungerUse - type: Appearance + - type: SecretStash + secretPartName: secret-stash-part-toilet + examineStash: toilet-component-on-examine-found-hidden-item + openableStash: true - type: Drain autoDrain: false - - type: DumpableSolution - solution: drainBuffer - - type: SolutionContainerVisuals - maxFillLevels: 1 - fillBaseName: fill- - solutionName: drainBuffer - type: StaticPrice price: 25 + - type: UserInterface + interfaces: + - key: enum.DisposalUnitUiKey.Key + type: DisposalUnitBoundUserInterface + - type: RatKingRummageable + - type: SolutionContainerManager + solutions: + drainBuffer: + maxVol: 100 + tank: + maxVol: 500 + - type: SolutionRegeneration + solution: tank + generated: + reagents: + - ReagentId: Water + Quantity: 1 + - type: DrainableSolution + solution: tank + - type: ReagentTank + - type: DumpableSolution + solution: drainBuffer + - type: GenericVisualizer + visuals: + enum.ToiletVisuals.SeatVisualState: + SeatVisualState.SeatUp: + SeatUp: { state: disposal-up } + SeatDown: { state: disposal-down } + enum.StashVisuals.DoorVisualState: + DoorVisualState.DoorOpen: + DoorOpen: { state: disposal-open } + DoorClosed: { state: disposal-closed } - type: entity id: ToiletDirtyWater diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml index 1193182d09f..25047bb5752 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml @@ -10,7 +10,6 @@ components: - type: Sprite sprite: Structures/Piping/disposal.rsi - snapCardinals: true layers: - state: condisposal map: [ "enum.DisposalUnitVisualLayers.Unanchored" ] @@ -19,7 +18,7 @@ - state: disposal-charging map: [ "enum.DisposalUnitVisualLayers.BaseCharging" ] - state: disposal-flush - map: [ "enum.DisposalUnitVisualLayers.BaseFlush" ] + map: [ "enum.DisposalUnitVisualLayers.OverlayFlush" ] - state: dispover-charge map: [ "enum.DisposalUnitVisualLayers.OverlayCharging" ] - state: dispover-ready @@ -30,17 +29,6 @@ map: [ "enum.DisposalUnitVisualLayers.OverlayEngaged" ] - type: Physics bodyType: Static - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.4,0.25,0.4" - density: 75 - mask: - - MachineMask - layer: - - MachineLayer - type: Destructible thresholds: - trigger: @@ -82,6 +70,9 @@ parent: DisposalUnitBase name: disposal unit components: + - type: Sprite + sprite: Structures/Piping/disposal.rsi + snapCardinals: true - type: Construction graph: DisposalMachine node: disposal_unit @@ -100,6 +91,7 @@ components: - type: Sprite sprite: Structures/Piping/disposal.rsi + snapCardinals: true layers: - state: conmailing map: [ "enum.DisposalUnitVisualLayers.Unanchored" ] @@ -108,7 +100,7 @@ - state: mailing-charging map: [ "enum.DisposalUnitVisualLayers.BaseCharging" ] - state: mailing-flush - map: [ "enum.DisposalUnitVisualLayers.BaseFlush" ] + map: [ "enum.DisposalUnitVisualLayers.OverlayFlush" ] - state: dispover-charge map: [ "enum.DisposalUnitVisualLayers.OverlayCharging" ] - state: dispover-ready diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml index 7daf9ca22df..d9a4cd02fb4 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml @@ -24,7 +24,6 @@ conditions: - !type:EntityAnchored anchored: false - - !type:ToiletLidClosed {} steps: - tool: Welding doAfter: 2 diff --git a/Resources/Prototypes/Recipes/Construction/furniture.yml b/Resources/Prototypes/Recipes/Construction/furniture.yml index 25978771872..a5cf53107db 100644 --- a/Resources/Prototypes/Recipes/Construction/furniture.yml +++ b/Resources/Prototypes/Recipes/Construction/furniture.yml @@ -606,7 +606,7 @@ description: A human excrement flushing apparatus. icon: sprite: Structures/Furniture/toilet.rsi - state: closed_toilet_seat_up + state: disposal objectType: Structure placementMode: SnapgridCenter canBuildInImpassable: false diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_down.png b/Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_down.png deleted file mode 100644 index 738c92a6d361f4ddb1e3fe611aafc897329d39b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1445 zcmV;W1zP%vP)f? z2|){?XcKPQ1gV>F`N299XSnlxZ{B_X?V2W0+t-n@73x#!)vbMO5P(J?<7j=BC( z9e@x9T)fu1*L?DKboR`-a9CoOP*TF9ZHp-cuy8RzEaqJJz<&t0H?-l^3>0suNZoMCCx0n!KHT1^5lIXRgi zDgmGaBp{=sqqPTs=>tG>Iz@c$?CeY!(S7G;AwB>s^l>_rdFm~I#~>CTAL)(hZ#%$% z@u|i5Qr@uul$Q*20TIeR6#%e4f*(HrVQuqRU0qG$008vk=l5J$003<3+T;jffeHXF zE}fvw0&8n)QAc6~Ji;DI$>7y`fn z=H&z#g1e$l*Z;NQ_>l(y@yEjKqu8)i))t4D*ZKw@t6~uq0Gc>}mWOb8#sLxl(wmN$ z?|8XAcA5$q09n=6o zh|`ywA~3<`co+x3^1nW9s|C(Qrmd=k_k07ylGV6vb{<*^oX z5~kDR_j2+O!2l2h+ZNDxUG1K?jUxa|z;FxzKyv250H{QXO76AA1aiIu*#ea)P|3Ye zetqSIcZ&9|yFpI4tS#V`qJRN#z}jg;wqn%)pjAF}TpfT=#AOW{l)DF8~rLTj4=Y284MO4Te-Yt8vqoKDPqERI{!9l{cW$ip5;@06DR?DjRBT zjMrAFVOZIP$CrXIYa?n|RU5l1s|Yac1pc z-|Mhhd?13R#W{JA#Zce&W3kpWk_1J5wiQmS`GBknj2ROg=5GTS?Gb>ic*RvUawbGf z5X@!-Mv3Lr<9wFYUY8BrP+mU8By+P$W|s0o~#w_&~u-!~R4jTYTcblALZ2@gD^2CMzzac!RN1%)c z@VWFg{RZgrAFhIFJu9sBxV1X2&VX9AtX8kTnW^1a0OLSk00000NkvXXu0mjfobIH7 diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_up.png b/Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_up.png deleted file mode 100644 index 204c5ff89df0d7ad1e4bd2abf6df7f9cd4c68576..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1553 zcmV+s2JZQZP)Px)%Sl8*RCr$PTTMt+Q4qdD>sA%CX(Mfml4%lFp#%?yBI_=Cd% z#a0(rTjc2-=q-?@!a31bSd)<$0327hgDkG7L>VpfCj&qsbW$QiR89bp12FNTvaTqk z-ytcpBts?ubROV~$55PMGFKiT0g$-{L#{6*X9wWM^;;#iEr}2Sdh+y9mP80y=;G>P z3;Q7lH%83;wNVCo2TYl$i2<11nvbXE;ugmS5GLm)OYr5om$c{i_?#G?Fe#6@%>Y1% z!)Fc@U_z)S2+MzezhxG{0sz{O3DWv zc(yn`Id2jGu)^@faM@pjKxZ^JIhWn~5&&4)7y7J@uQC9nB04-hXVGq_ z`>&ZYz)DddI(v-`e9LaPOR-T)pbmUH0MvnMlg8{Yph?!ML)H!hQcy)KM*GwaN6H+# zXQ7E-<6&ZH`K<9(|e-tA~aLYh(At(sUC5 zU|C$=CZRzI0Be?Avf@hsbiEzbmL>uqx6HC;2t9d%T>|j%!DDBIheb%qfF?LW$pBqQ zl}xyO=VsvrfJ$R9G$cq(0YJ)3U8G;5UO@mzAq@bJ!a1Pj2r7q{*JJ1_b98*tXz0Tw zoCJWRiY!Hg0s4**5Z)^=q|(~S!L!5j%fpgT1-o1VEH#I&zrgb&eqtHmkqYB``5=#| zixE)XH6Q0EAW#f)`0f<(_ztJsCIF~3(e#zXbKefrl!wFSZ8cZOW{AV5D+EebqfH5b z%mYF!@Et|#L35o6x_k!nmDpvikgT-+P97jLLD#vP#d&d1d@^;m^h<5S)Rq9m4r;a2 zWBf_%5ALIoDX>kr-=V54UiSP!gHj)HI{_i?Can=JuCos$jI4MF-yM83a zG@i0(*(89zQzLIdM1!C&Bm51&4RZaK$#st?bYvE+2)VAO(3AeH0O(~CyD$+1fK?(S zAKKf>t!9-|+1UDyz1P2Cl(*LCt8issR)1V$nwNm9GwIfD0HmXK$ysR$SPvulJA%Cm zfifS!|N6?Vk2$`(3+AeDjFL@#-vOYhmdhpgf6sv5>dmib5PItT00000NkvXXu0mjf D0+_@x diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/condisposal.png b/Resources/Textures/Structures/Furniture/toilet.rsi/condisposal.png new file mode 100644 index 0000000000000000000000000000000000000000..c60cabe80b18aa8a578e2225633f58de1b405a5b GIT binary patch literal 31043 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-charging.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-charging.png new file mode 100644 index 0000000000000000000000000000000000000000..cb9ad3a9c75d02d9915666999f56fa89dbcd3d24 GIT binary patch literal 31052 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-down.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-down.png new file mode 100644 index 0000000000000000000000000000000000000000..97712ddc65380c3b71547a3b31bc8e6263080778 GIT binary patch literal 29554 zcmeHQ3v?aDd0rD9#&N(9P910hLg0WKukXz4&g{-b7SQXS20R8qpq_r%u z;_9~4&Fa`!qurT*X21F8oBuH@eP!7R$9-tlA+s#Y`q0uPoh$Htar}Sg-uV1p;mlv- z+rEQKP8qhW4}LiQKf~Jm&5u}C@%M$^m8F$EpUUfE|3VcO1G8|Xe-O1TtL4~{L8Z?y zrKZ5FE(|Pce))%YH8&N)Ma?Jk9?~<|Vb&CuoHb-lI_rd9ebyOThRw&e&T1LSLqNYN zsiu+szJcNV$fD-5+&s$h=9xk~AbMa^w-hfOPcmNj)0hfEV+C~QrH*W}5ClzX1* zEofqd@*Ht;4zZb&cS+u5P1UdF)>$aE424mCMQ3-lJN&e$c}=M_n9t?buV24#y|b`5 zv^qznlsUq3jM=DR4{sPKsS$f%_=t*-nw(BEtcMDNr9yF_DVD2(;@Z-p=H|Gg(XZ-r z^$(7AG%#Gn4k+eE)L@P-B)N$ig?hBj;M$?S^3Xz^GkvDt43vh^j!xKaa80pP99~nL zq|4FUlXi?b>FF8md&2Sd_fOdMaH(q@AgDq*Vaws(4TC1P!VDMJ4r$Z14v%ugL{C3l zT4Ba!X9}WFeL_K{LNtk-_|Bvtn32LH&Eh+iW@VkKOmz!D%XbVJRVohk7K?qY6?I)! zn~SE7jdG9da(>guBtRoR2ttLYcNzLl_nrrEg>S*`KUP5uS%_jezn@< z1_xHRC<}3@4@r6I5RJ?P~ST zS8F%=3av!8bu;F)cXf7i+1=6BbzI4({8`LU2><( zb)?_kLEBh+o8W?zwl*nPx1TD2yQHhl?{*n)lbx(xbhh(u$_Q~f9Z4V~Re903fW;Kue{>DrVUV=F)6w$d^nN(HW_>F7)%T5Dw(!$u%{C^j{r9T0qIVF;Cu z>l3N#95p2-@OY$bVK}ddR+@XZR1VkD)yycjUeQ!VkQ~@y=mZtfen3MTE6oXrBQ)c* zUeQbhP7E_4(yL${Xi$gmc|=6Uk=(5t5auZnQH)yqx;knaNjq{m)`~*|DZF||t&EDa z_U*uxWvmTK+_iNW2uX1I z)QU2usIUpvWDRR9PAPpxKw^#~>s>M~9-I3VSc8fxqJw}ETLywS)Bz?YouVh^5LdB~ zM9SFGWu^*9*Y3{|Iw@K8idIvmPJYfGj<%Eyf#Ne+T0u2<#|pspj=4iyEdhMwou%^||mOnV-v zjs)dtjQ1NBf%$ak3x{#ntCvHB?;5Q%wIg&=(U3qfj;+QqLP+pSP_Kq~p@TgDFdI)- zcbULoaca-^6l0NcXuZ=YK^4YTJm%}FXum;xEkkV#5eyt+%JkGYy_P0)q5x6>Lv3#~ zSoeV!0w(6jh}TO`%H5BT#4{o!BrdlPdJOL55lAXs~q% zZG)v##oE=%_F=9wMm2%D*NHW@Y^kV;uo}av5!|Rl!2oq#be zq*t${8p1jXL%5@jH-ZgZ3|~MwfOU1$szF#Asg)aos-;`8`Jf>UG8T+7L8!4BF^y$& zvT>wpLfFtSd;cc@F%F|};Q#}#DxXpwvlYkQo(mD>}VdseiX*9T2GDgz)WNW zm#(rMPgRXz7^yHPeI+>zWZ$TINi|}WIX;xn6`0EksgelLM=((xM>8*|TQp~|qp|H` zfK|S3*r>4 zR}n%OsieBPfWTcPW>GFu_~44T7-|#=+YO)=fi}_(cmz^`0Rawp&q86iC|?O-J6uE% z>cT4`K8#fgD;}`~-}Tc?$nhf;p%0Z3%Ju?+k-A*ll4GebM@M07{e^zIs*M*aL#hH_ zcs8tBW4n<6*D+WgMsc|RA;RqRy+3|LDEtvbJfs3kxS<}g2)fam0zbTgzTOfm|sK$OCGeqa;j z2Da}bNUh<3`Xt2Kbf%735RgEJ5f(29!w$I{A#MP|kRl)&tHbzH2*TDz79u_p!qc=7 z5_N6i!21p&$H4~^ei(RKMTneFaW*4PL+N6Lu>cCe6@(#B;@g4=9TEh?5NMscFcnV$ zd`W?3c-DC5^e~%|Ls+B<14m4PS*44eW7x?6`MX8H<8(q#Pk!mZa7iIRzO=J7Mye3& zJ@!H4A;<(H)U}-e4<|iPl+S3WJmdI5>^ASxd@KrH6Pys#pCw+yL`H7l__2eB4OcM08rxJh#5 zxEPhzt0B|28X20YB*rRIcWyRbeA<$m&O{Uk)|sLAG8LF`g?zO^-YFU?_BV~DyP8VH zCN((NSI{b+)J0atL@mYt}y6LH$1Fr?90s<6Ndp7d$h=5ZDmr*L0 zNMsCjgwSkVDi_Clyw?g7_v*IMzF9l*Un>gOFV@A8aEOQVKz zjUsBmRt@O!JMh@1o#2<<%sy)zla6i;e*`uu7cS0E#Qcf)5W&$Zc!Kb1xuAiE12Mj) zQB8LeUex6JGv)@tCZ*Y;EK_jRAg(UHX)`H&bs>EO%#bpK(@x;N%P>CDKoOJI8VP4H z4M!qA_2`2}^KNF7u4^EZ?mqTe=lpEZt2cb{jTHj_>*kC!dVF*5x>z@$jb;~;^?$!kMBWH()8 z!#!lJ5ZOk)68T)N>hta^Oe*3xaU7cGZpNgUO*)Fe~bw3 zt$c4~d`_fqZTTFChqpcn$@467-QoD8i@;&Wa+~otCTw`hWDOHHstqg4 zCh=l^d15M8@!CIr<3MioR}LiREJ1sXE{;7>qa`QgWt}9zNH1n^rDK$+k-?P!BfXfx zm5x!OMg~^`jPzm#S2{+C8W~&(Fw%<|TZYC2C}FCBR58W^korl&F!xl>j5Xn8B5fQKCi$ zR|1UmVg^?_Mu{33TnRAJiy2($7$s_Ca3#PFj0+15Mq5jKfpRuL?v-ywhbL$m{{CLIP8-@;iXv0-MdZp!}Z(D~A?%VYAU2UJ-@Y-WHFF5$Y zBj$hkYY(?vdV%`MnnUOHiMtAa`oPWMm3JQR?0fHVSMGS@XTSZA*401T^ntAhUH8 z=rJ!?^IB(~+i}F^jmz$T%bItB`NH;KbHu;3_=|tBy!hxz_uO~Qb>|=d{6C&HXHU{I z{PGQj&3`xh+|$lJ(0=QvyI))7QeamZ?+tK=YBJ+ZLhAnM zxz+yuskcA%VouI2otiu2R~N7S;X~iq%Ub;Q?eF|x$LwVb&Oc}SwN1TmE;x2m;T!LK z{x{+W&1au`-~2P5aPkDt>`F2CToj*H&327i9y^25G#;nutF=~{dE!({gSFE3v8 zRLdiC=bpXjxP@QZ98Mud8_yO&9FG;rN6!HCkJ11`leq#^kUz-+sSP! zf4cqI-!I&;bbENnwGZF6D7qlpRJiV2i!VED>p?3Qyl~WmANbD?9-5zj<)=FOzjVWS z-Opau|I9H%|E0dO=hcUQl#PsRIoSGYcWZ0wInO@-{JfLz+p*)a*S45KtM;pk|<(}8nKgT zh<}WV#@@h!8b!s9N)#bM#0p{oL3n#8mWdht`C03&_tv;{m~+qF`<(mjy}z^fIm4JR zWKchg79CnB6bg&}UwRCKpFhICc=P7)I#=L3!jBKaz8s}dC|b7B{WDRlSk+FU@clZ_ zZ)C(spMj2oI>dn!)x6{o9TEn2D-=#H(P5krBt_WrQb1s6AG?S9((P;m#Xfc;C?C`( z%w3ug_~qnq$#?P~KVfo^z>0P*&Mlmx9YH~e6v5d>hXjXe9HaZ#)#!DE_qxkUJKLID zB7*wZx#|YA9qBW~)?FPg*-{QPBA~R&R>eACluAXadfDPAMk!H3NuUT$IFhI%iQCrw zv2$(#@0`L#*>RYMSKV;X>SH${A|lLDsf>z>a)=@v)Zqb2jAdCRiYsv(fjbaQOlSlb zjf85t)GDdh;~{B;@W8N$Ky|3CPA|u+BP06Q+35y)`KRu=Lc(4S6soDi4p3A^b74x% z0adl+Pb^fc$MuJBLYK3b(E%ulTu&g zmAzkSlRUTx$r(p+0>Lo&>xa>fDCtOHqhFJ+y-82pM=b`*G4FI4NIUeC^JcZsh2$4pxW5 zj3j5mN%^+RZ;3z0fMb`2FI4WO@i%_?AjIyp?I8L~EdU#Q+ zm%FPci8Jn$7lW!iQQQrqoRqqlgd1OtUT5;U>zEhg=EhQlCrPl3n>*%;ySdU7O`)!? zERB0H?^HmMsHZFAMdFkz>w&w`9&VHuhNCFqL9i(3c&7qZ#V{a15jcZ;(3Go+Wj!d& z3-k13F;6d**E(u9W_NqSK@<>u+>O0yV7xM4878^O{ZUheNyP)oL=S_l*!Us(;i~i^wDeu|i-x zORE|>Yfj>1L0}M`WNWaN;2lLGg2>YXMv8z(Lp=l&a1LitOy_)DEg+5Md^jnAd$AaT zvJ_gYnvnn>PG(drFX9|8@C^inz-We*Q4&!Ja-A7DLPQZ+h2tWQ3KWlu4OOioRH)3d zh(rr=of!r2aDikc2A4$|#~VCr4#RN{qY)HhvL0&)PL^eG6iAF9*oKA-A(1>O(h%qv z_(e^ib2Q5%k|fcnND~~5zMG=o&LJcqN-?mk!W8M+S@S%GA}mj%1V#YdQiD}zo)nP3YI08|`fnM+*w4s1dz!(gTqlly;>i}UI$&3^yiW0{Y2v8_f3kbvTB*L)} zNZ>mnkY59xj}mDaX9)`Y8mo`SM)GS)Vq}&A?ng;g4Lt$sf=nVJhXG%UDwV3i^hB!! zTu`ZiMNz)SjJl-^mw{9Tk)a8kB2^8gCz>H8ffF!9hCymQgy(PyRxv`NX%?u9Z>Wc; zL;{ur~Sm3(kiE<|>IrsfIcqPeHE0!HNY`RD(4Q9?qbUJ>eLG ziz*S=uYrLMt2RY3v;Z|VM0+h?<1}yx4%|Y}EFlq6LsjFvBr!Ck2!t#)F4h7;gO5PD z&!Ai#hcs583o=kB&l0+vuZC2?uNx`n6A1y>S_E%{B&rT;Qs58xskoD5iKbYRLkJaDmtRBv z3+W`ou@vOqjKnpRRAn5)2?m^xq`{W8q>9P_y$mTTK?t~tZ?I|#hZG%_U6^1UUpLyU zAr@&G)#Xgw%c`Jp%70mc%0i-G*&_M63aqh;J6YmoMuai|D%-kM28a-YB$(wXk>X_7 z9eF3I1_Z)(VC{BD&07r0dk!UGs`!Fg?P~f; zSrOxShE^ev)k+8{)4+8&WF8Vn!OnsRd+ykO|M)VBK|KPSDJ%ypL5d_Kbda|oDuTp- zn_$fM$outY3iYuBibdGm5aIDTOh7ml4Spa}5~ouOiPt*~1sl5#BJ6}=C?P<_PQ#Q? zcy^8?5LQ-k2uE;~NCFMv+<&1AZl_6_M`#N6UT~abfLRz0!7(roWN!p6!?x)=_A2XV zM!?-Tuqgs-n1s+I2|IJLih+EP6y%+Qhft8q;53wBr1b8brd}C~W8h-2n8B~NoC*B`3o=15>lz|C=eGri%W!T>U_F-ur6(H0k<~=rx>t`myK8^@w zD*{`$BtjEV%JVWozza?@BCirS8GUbS$`mHDB!nx@gF{dpD1!|h2BC3U5K-8)gAL*L zSH|f~0EHD}Lp$}l6AvFB$Nr(32o5&# z`+GR+kP3t#!U;-Lfgd5dSVG`AVW(Fm>#PaP!834y;cCu8-V}eeTVJQTesoa%_U)ke zEB0&W`I@qS3^X`A5Kb<+;MeY{w>s>_GCT|3BSE5oz*!@ZD+<-Lj0%_tZzx`XlCE}$ zx2XBYP+tb>PCR1XJesIE@$i)w&>e6|5X^5}eKoC@l1{ZZ1pd>U?+3v@#Q%Mas0mW&zZ{U&YN@&5-{m!G zIUDmbo1Q6;8( ztrLB{TVHgPVbRyS^+jJv4i62CaE6WCnu+RNeoadEafRBWbzk^ojpR&I6amK?`j_=3 zh6F}S!6Q8a;b>i>qkfEr2dfd%?|G}_sMjQMpp#dUul3YP)*9=zPr=lzT(wu>GdA$? z9_7o=_xxi~d%5H^=9^cK)RhO=>cN6X@tu#K6<@Ixby%q z+%&>v7$3b}BV2lb7;YNjGK`O2uMsXiKnypHa2du&uh$5d9w3IBMz{>)qt|PMOAipk zO(R@}@zLuw!leg@;ieHT!}#d+8sX9d#BkFHmtlPLdW~@D0b;mmgv&5Kdc8)t^Z+s3 zG{R*VAH7~9TzY^QZW`e-jE`Qg5iUJI3^$E%8OBGi*9ey$AcmVpxD4Z?*K34J4-msm zBV2~@(d#wBr3Z-NrV%c~_~`W-;nD-daMK8vVSMy@jd1A!Vz_C9%P>BAy+*k705RM& z!etmAyS96~2>mhC)$Z z4L^$%ibzzUxHDd%a9pNPv{f(iKkB7WST5=B;p!KC?cVCR;Eshc$J4v7i5>Ox7sJ+$ z@unr~)9CS?;eyjQXAK}-peCs}7so(O}J?1Qp`9zu+xA0-ne8)%gUFOeS zkk!^UV(*j%$vS-@hETm{hY4jHpS=MKhwj#qM~BYn5W&-wwCYjP+3;; zmw8juQHSo+z5fi2>XqYVn^saTMYOiH_Fd5?ENbxay^-!S+}m~NG+B;@x>Ge~&aRG%LPQY+X|Ap4GW^Qrz>a0WlV9%8nIiiXKeA9vU@pw#Ts0 z^pd0)#lfAa7uWal9X@v6n9Z)U)xl|Z%%g7}tXh)eFs7X!Qu<)Z!@`Rdy6eCAhRs+v zbm9xk`JMXj%Ux9+I&P3jhRPv1dvwAbi%ac?JBGU~%D!s_^B8;q{aN{d92ghp@(kpK zGjXbQHr9JOA6ixIaAWGj7pvs_1hrPDFzmA>Pe=DH`ZMe4iCMGNW72+UT6ufjf;o-@ z+vM)n4$8cMhVJTp`N@E!vbgN64oAZete>2{wE93>#qO%As#W&=wSPUz{C!%^{Yf4j zI!elY*ByQv$tN$fIn*YXshs;@`o8&*Kc%EJOWf(WWW-m0jDw%sw{QRX-lyeOCG(Ce zGe5T3vTW$Mp&uk(osMsAC5>s{OKy46zUN0_qQk|D7yr0i)FZQUf6wGkZTn>0=>2K( zrJ>3+Db~5G|KOe5KRj)3`O(2e|D?FjMzTG*joF zJGtCybgM&!r9VCMQ_3&4#k6!A=sFh9V)z*XQ#Acr)HtR?S$M3kj;Yt-4Y**9$F`D}< zy5M-1Zil>F`@+cf0f)U@t@3OZ^qZI)oRXpP88k;5wA!L$Gm~FVoS0sG)%D63=36db z6-&&#`n7BwHv#*6K4ZBn*d%`XBcH{K6^fuu)|W@y-}}iED<=wqR?J?rV#W8v^NaJ2 z%qeiPv23C-IrTh1s`$GnRkhmg(ZeOtm3wUBeA=9INV1>w_;$+)xjVdf-ncQ|OwsgK zli%|ufAdXqby_(wckbM<@(GIkj!CKI6E_ZadEqf8H1x*y2OV6m`Ti`Gb_GvPWlc`Y zEuCLHZZmM;!11|hFXp&EK6}=7)u8UD7RCIeO(;9|chI1NpXTi1DxX_0s|U5Ru;{sz zE7o0zF5|~_jE5UEqc^c*97fzLNE&z1@9E>^+hX=r*+P_$>2?EkNb>g0)0zziM`rQv zcGkX+S6j92YMan{p~u>$pZ~C?EO^oYY4oz++S`?QFUSfoeNa@?f8W^D?|==;wB3p~ zkJvk2z2!G&i|-TdyO($;rUu#+FArFgu=)<$^>9kA#lks#EI(P+^5&HiA-ZsN)T8s} z1GG=09esDFjnmo;Ql}j_Fjn-wJM=$gDV2Oi@}E_~?8-9g`b?4}6^ai#JI~#@I%Lb` z(Vvl_OAc(@@zwVG!EL6P<#g{nEI(;wbo)3>M9TQe)90+e&%87AkFu+6{>P6-bdUuU1V<-1(dD#koJ@!($TEfpS7_s*8i z`grX5ScU$DUc%oJ0r+-2I+4&_HbE}54(!sp<$Qd3VI>D~IR z+xRIJn^!5;ts9}amJAmP|6Y{Z?ZDV;3Dvhtw}qb=T+nu7%D|m=X;`^-r}cm{4}zzB zP~yEZwP^9er%%UD2qV`{cHeP3J~r?0kN4KE_^{KJ^v$Pk-@df}R0i-=<;Jm()RmG= z@2Kr2ip}#DocQjB+wSdca@%Qrl4ojTD>O%+goNary}T1yH}&_AU}o7%;_u(zhdNHS z)>Q4wFJ8H^W!vUQ@9XB4Hnn?fU@^6N`ru?XG0A`ON9(gz{jl-6tv!LUJ>b_n+YEO9 zyO+*iR{*yA*rZJBj_I+c!-Br+oL=HRiY?k%a#xixRa5iK0e@RuxOnmNs<^wQ&8~Xh z7B)rZ?<^_`9J{~&Y_(IGJmch4R_+zJ~rT?klT4$r?I6xG*qY3KaQn^y9p4%|C5Tiwg~>b8&4|D*jqpiAt~axu%R z=Y~Hze&_MQm*BeJe__&pN&M%-T)VEEU3JDXZszlpn4>xQ)n%7bTjck7_UEA)8OqPn zf;-&WGs8^i?jJW+J~|$jt_OdzO`Dipx3Eg^pR9Fkwm5BBcKk5q!lGY$XiJOKKW~>W zxo0RXVox>|$JxRPw zWNT*oJ8DNY88mQEenwDgkF>m1)thfy;CGWf{rx*%o3t}!qoU%#?18a;y8BlB(mlO- zKh#`NoD=+e0sp9_bzIIFx-5iN#CK`86NSAKw*J#yJvtVbX;-Gz?Y0#z}VcK;6;5bG5(hE155l0T0nmtXt(RpBA zSi1}IwtV~HyxenB<#=@W{A4G)f|V{IbeGl%w(VNkwQF^9X`yLp{_LLvD$nmu$-HrL z&h+B)oLwJ%IP0SX533Jn9Tg6jv00|SoIiZHNz?UhG81xJ{MLUjW$D-{KG!m6)9|EU zr+xcvvtDGV$MTs<^XYTdr7MwGVNJ(`&?Vwx%0S4Dz;9*&QCQ3j>^w$UEJHHa%km>{Hzfr zkGz%n$JLei(*?N|4#$p#-n>@UP8YD>%~G!2rco+x-u15@eXWJqX20LQXWErz%?s_j z*?PMvMo!-1x-sMa*GcCtUYrTxuduRTG@yKo`C_^WEDaPXpSR zc>Tee`O}+uRQ5|u%`x8{8D!u5jHM!e!%@=`@1)cmpFdWoJNyM$#y@fJ{?@9bvqkK< zetqf60qGaVeECuJxxWe{o32{9vQ6H~biZk*{U@*e@xX$z1vW0JU4FdS`^?=Joo20Z z@jGktWo5qwsew&TT{3$B$++i*yTP+2)#T&Tf=Z@&KeO4>T(`ok=@o_J9WoL)@maga$8(0dx0psOeOYfPQWz1{gKlRC_9f{{=CN5f({b$DCqSt{z z3ChUW@TN1G6?or0xU}V=!G+(Byj`~VhnC4BW+Zl-eaO}=@iV{AH)ikuaMo>$?DOZR zr=>Yx9Np{pXU`9P-{toKW2?%l{W|t-nQLFNuJn`H56!kjxP&(O{PXfAcTTizqx{Gv zeg|D#F?4IY^L=7}@!L6NPcb9{%yididwVNvCAU1GLhEr#F3v;C!rE~~n|4<_Kz)!g z&9uTKqtC5nCwyrB!0Hl{zUIxPOf9pqcXig*;U0ED54((AGw7nHrE|yo_S$jn`(0~( zZ`tibN!^l{D2F>}$7P!Qr#OZ)%|EepOK$6?Cf+-CC?KOAZoW&Mde@}f%G+74?rIZ1 z6KSXLI4xfD#HUpkN_-ZDgBQCL^cZ=kNLFu2v>tk=$FQxM^oxVrDXOB2T5ldx-gKeU z++~Ammam7+_FaE)#dq+1PfckzALp=*=OG{67U9V%+O=wNtVgKR)nS_m%#-P(MAwr|&JZ^|zWG zKlytelM|odKe}@J2Uo7{N$NfIRLTBmO>3sk#R1>8=@VqY`|mip#VSY|*m uJiT+d>7qG3!@X>WHd!=aM74|JS@5`|A5Y%zt^1EL`g;!Y*z7ib`u_n=yD4S> literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-open.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-open.png new file mode 100644 index 0000000000000000000000000000000000000000..f48f67bf9a6467f84373d16fdea11b6b28e5d682 GIT binary patch literal 28502 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 hBqqeRluU(tV51%s>3@{|3?0LSz5{ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-up.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-up.png new file mode 100644 index 0000000000000000000000000000000000000000..3b37aba4729a861a0a8b57de28bad057864f07b8 GIT binary patch literal 29203 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal.png new file mode 100644 index 0000000000000000000000000000000000000000..cb2db2f9d8e9006ef560ad3c6962142e9de840dd GIT binary patch literal 31095 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-charge.png b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-charge.png new file mode 100644 index 0000000000000000000000000000000000000000..23ecafa9d8245cbbc5ce3a248e644b7c0c775f4f GIT binary patch literal 28316 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 VznP1#7v1jdy)pQ+oiG2^e*?F$;(`DG literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-full.png b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-full.png new file mode 100644 index 0000000000000000000000000000000000000000..ceb04dfb268413751fab3bdd4a36b96ba49ba594 GIT binary patch literal 17416 zcmeI4KWyAo7{DD`p*B?|BqTtctgDcyz~}$=*}A(n&9x*F7ozm4rY!W?e!f__v#++3 zyF?&WFtNgd5Cbzpj4TXH%rGz@1~vu|QWqAc^8D}FcezVmNC6?esL6eO{{Hy=zVE&F z*~5LXx$*ktujOV8_Dm5XVKr!y@x-ekBg(V`w_!jdOrDGVm|rwC5BnO z;`O%U?d}_<9SrNh2`pHj3`a0$J*QQ>r*?(zXcg{w zYlk7cbGXs74|i?Dsa&Q5RKRSM=9dNf1?Dka=0)B-vC1Q<2#Gr`BFZQZsa1bE|VZ4~PEO zs_n$_$mIF)cw8UL^&sr?qG1@kAn}sKAq_4%@MAFH{OD@tr0~;$ksW%Y*b96%@e8bA zFK*RpNg(`_&ucisf&3_EhbZzBFyh6!z|S*sY-}^y3kT_~IW`Xma0vZ4LU!U@yU|V% z2hmQjASAxLFfhtVw~J%Xz2D(*Zs;h!wT}?wNatEcy@L_tw_p_Pg*LpkkEU{U-t41z z3zpd-aD~+8I*L7afuH2cLI`l;Eig;2WM*kiWvOl=Xy$4NK^%m=AQ-f>a@{O)!LF{R z<;lKs&-0yN99?VjvnFNXl-^(m#IP+1lFSJz`qvXR6VXVEQJOVJYtR8PILSd(t>VJ5O*aUKAZ~j@(1-lU?>G5!u%d~J z(p(J&L5S`Mw&f;2Z+Na%C$RUDRk7mRFhVJaDycyUO4t2O(4QVHE)2YOw(B^7?Kx%{ zR2H>{;R*_;+pdnlLC68w5jaK>Z3Vp)=^8fb2otaCC{WX@0on-ihy080U? z;AX`(ZC*|~cU?qBuIEo20;nmnB5)1C(Xxp*6v^cpy6rSHSyC0fk(MP2RB?7B(3_!$ z4jM41=p=J-&|Cz=RV=yD;4H;;IZaYT4nWJ{Y`G!pwk6pb(2Edr+#CtD<|QR{FrDGF zr0&3n)1zfcrANXDgb_?OyjHE)@$eavRY_he>BeaSJ2_0+cCw8?R9RD_>d4s1$ySBxxiL|u4OtN<^1XVOh{#(e6W z$h|xHbIii_ne34yyVZ)bd9i}CcFym(ecrp2#n^rH+n%+O&w0e2gw6whIdvROJqsc^J^p`J39zUYKimf01G3LT;EXo61Y9G%?vI zdPRob&hhy5TqS4t;p6EG<&2)bZ|soegmow`jDkd3f<&OWNGM?)iVLG45hyMaN?3>D!YD`tii?C2)}gpC z3KD_hBB6wJC@zeGM4-4xC}ACn3!@+rj>WZHe&Gf5(YIWT(brjQ>@HnHUxmTiYg=80 zIeeL69=*>ne?CENgbhIujg@a=DJBi)_#&Ps3c)9>FeEiugbbHX2w z-)a3=`{JV?`ggv&_#AuV5wrBn`3rwvcy#FnhI#Vj9CP=pbMKnJY<-4eGV8ZCI$z#= G@ZMi6)#3gC literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-handle.png b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..4f40192d5bc52eee3a62b5066f434e89ba6d3dde GIT binary patch literal 17416 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 diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/open_toilet_seat_up.png b/Resources/Textures/Structures/Furniture/toilet.rsi/open_toilet_seat_up.png deleted file mode 100644 index 77995657cf61bffee7e5180374375219108ba40f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1535 zcmVMGzPWMTiAqt3W|WXd{w_xCsn|gf=2+h?~GbP-v9|B@zWf zLeN4e+=QDpLAq)k80W$H+<%)lbLaWq|GxSI@$J8PGxwf*=FXja=eH%1?Z)lvN4{6y zzIxXdIb>72M+pn%H(tK+3OsPO^(%m7gNt*tFD0DXOZNtHk_0U#|806IH6ognP* z?ei;IgUeWVVi7w7|;4FHrM z9UZO7uYJ+f)Kn4+LKFba^Z`&F4%X#>@g0q`yu2I*LKFbaV!)+qLq{`{Lz$MBl!lBj zl$8l)0Q#@qO3|O0UrSb(9ywjX@Yv%7fh9=1I(4xt zRT>Ing5O{E>OjDAKEne%-#yqhjURHXO$Pu@@cg;1lmS^mkctSx`2q=0%sLEdh56^^=F)DQTN*-t7qc6fC`tjK6wd-c zSvFV*4W1hRVuXkRAk-JnW9H5UfL5MD0faIDSXfx7+y6r#U@M%Mm@w&P>ZBO}09;P+ zp9cWQV+r_n0iFh9Xy~)3T>l}<%jeel%?u#5;c!4P?UCVq5vIa9(N|c5kq`hJSNDS~ zu3d>TTIP=ifI{fR2rW;R6txQ=24LVtWnH_FeukjT5)7FD&~boNR$2v0bDJzG4v+xI zSc6+`)|Om20Au$aq|~+qLI7y#`O`uIAw;3;+v|1gha5Z@G4|I+8SEW2MWQAKV0MP1 zmYiTrEN*dZ0AY4zHU(dO9GUKudrI4@0tN13$WMw0Emght;(|>S?KaX+@39tPtF?!0IV=FJ(9k!L7+2Q znO!N|`Vs(GIcw{)I=hFpPRR3SlvcU(rmFw|)8g_r2@OgBn6pf&?W?*X z-;Y*B03dfrw>DUR{>d|EhKET=$$%<2LCFA}NR>>Oe0V>}1b|9oFmxnHRRKWCOkJd3 zqh4MBn03(ruoTV#EhVTTyu2PmUzwxhlSackNSJDW5ClL{MW&*`0DVUY2sa80skBxK zo*kZF7E62?>~sk*)$F_e0?&{A6UzWiDva-CLmW{SBcQx%KF&`-pcv%ftrE=~mhVu& zF#$lOiDp(xyy)9uT5vdQ-d1yM+1%pr>DmG%tI;O~K*j++7G#y8^`J%71YJIZ@k;2j zmQPk%zZM5zB1VF)x|_v$aZr3Rbhf+~+lQeo0SFapwb#f0sbgIvs6mkQ@=YQsSGMw0 zMuXs53R#M?uS}oUod)HZfL7&TK5q)Eqc!T7%wsPOIzG9i?Chs&2Y(%hb@4%7K#&&a z;AIv=LrVm*%9zCEq$NPE!ih5<;H$teCZKTGa+>ucDTeWsMaw1u%&Hn?CPXv{`ZB^v z_-&Buw@j{kM81-lHzVY_ot)id*#JmK?UFOs60jB{`8$F}ZGkc#z~B1HZWeR0?klnx&M0x`~ldPqtxPMGKv5I002ovPDHLkV1jyetCs)( From ce71cde429c7fad648d3b8c164f12b1e9649153c Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 03:22:24 +0000 Subject: [PATCH 054/133] 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 dbfd5c37db9..f36805bd1d8 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Nairodian - changes: - - message: All vent critter events now have a small delay and alert message before - they spawn. - type: Add - id: 5765 - time: '2024-01-22T01:33:38.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24383 - author: Flareguy changes: - message: The Research Director's hardsuit now uses its older sprites. @@ -3794,3 +3786,10 @@ id: 6264 time: '2024-03-31T03:00:45.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26487 +- author: brainfood1183 + changes: + - message: Toilets can now be connected to the disposal system. + type: Add + id: 6265 + time: '2024-03-31T03:21:18.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/22133 From de62ec204b04809175945a2ccfd4dbce6dafd8eb Mon Sep 17 00:00:00 2001 From: "J. Brown" Date: Sun, 31 Mar 2024 05:09:15 +0100 Subject: [PATCH 055/133] Uplink store interface searchable with a searchbar. (#24287) * Can now search the uplink store interface with a searchbar. * Search text updates no longer send server messages. Persists listings locally. * Formatting fixes and tidying. * Added helper method to get localised name and description (or otherwise, entity name and description) of store listing items. * Update Content.Client/Store/Ui/StoreMenu.xaml * Review change; moved localisation helper functions to their own class. * Prevent thread-unsafe behaviour as-per review. * Remove dummy boxcontainer --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: metalgearsloth --- .../Store/Ui/StoreBoundUserInterface.cs | 40 ++++++++++++++---- Content.Client/Store/Ui/StoreMenu.xaml | 3 +- Content.Client/Store/Ui/StoreMenu.xaml.cs | 25 +++++------ .../Store/Systems/StoreSystem.Ui.cs | 4 +- .../Store/ListingLocalisationHelpers.cs | 42 +++++++++++++++++++ 5 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 Content.Shared/Store/ListingLocalisationHelpers.cs diff --git a/Content.Client/Store/Ui/StoreBoundUserInterface.cs b/Content.Client/Store/Ui/StoreBoundUserInterface.cs index b549918d7c4..f87b92bc615 100644 --- a/Content.Client/Store/Ui/StoreBoundUserInterface.cs +++ b/Content.Client/Store/Ui/StoreBoundUserInterface.cs @@ -1,22 +1,27 @@ using Content.Shared.Store; using JetBrains.Annotations; -using Robust.Client.GameObjects; using System.Linq; -using System.Threading; -using Serilog; -using Timer = Robust.Shared.Timing.Timer; +using Robust.Shared.Prototypes; namespace Content.Client.Store.Ui; [UsedImplicitly] public sealed class StoreBoundUserInterface : BoundUserInterface { + private IPrototypeManager _prototypeManager = default!; + [ViewVariables] private StoreMenu? _menu; [ViewVariables] private string _windowName = Loc.GetString("store-ui-default-title"); + [ViewVariables] + private string _search = ""; + + [ViewVariables] + private HashSet _listings = new(); + public StoreBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { } @@ -49,6 +54,12 @@ protected override void Open() SendMessage(new StoreRequestUpdateInterfaceMessage()); }; + _menu.SearchTextUpdated += (_, search) => + { + _search = search.Trim().ToLowerInvariant(); + UpdateListingsWithSearchFilter(); + }; + _menu.OnRefundAttempt += (_) => { SendMessage(new StoreRequestRefundMessage()); @@ -64,10 +75,10 @@ protected override void UpdateState(BoundUserInterfaceState state) switch (state) { case StoreUpdateState msg: - _menu.UpdateBalance(msg.Balance); - _menu.PopulateStoreCategoryButtons(msg.Listings); + _listings = msg.Listings; - _menu.UpdateListing(msg.Listings.ToList()); + _menu.UpdateBalance(msg.Balance); + UpdateListingsWithSearchFilter(); _menu.SetFooterVisibility(msg.ShowFooter); _menu.UpdateRefund(msg.AllowRefund); break; @@ -89,4 +100,19 @@ protected override void Dispose(bool disposing) _menu?.Close(); _menu?.Dispose(); } + + private void UpdateListingsWithSearchFilter() + { + if (_menu == null) + return; + + var filteredListings = new HashSet(_listings); + if (!string.IsNullOrEmpty(_search)) + { + filteredListings.RemoveWhere(listingData => !ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listingData, _prototypeManager).Trim().ToLowerInvariant().Contains(_search) && + !ListingLocalisationHelpers.GetLocalisedDescriptionOrEntityDescription(listingData, _prototypeManager).Trim().ToLowerInvariant().Contains(_search)); + } + _menu.PopulateStoreCategoryButtons(filteredListings); + _menu.UpdateListing(filteredListings.ToList()); + } } diff --git a/Content.Client/Store/Ui/StoreMenu.xaml b/Content.Client/Store/Ui/StoreMenu.xaml index 4b38352a44a..fc4cbe444fc 100644 --- a/Content.Client/Store/Ui/StoreMenu.xaml +++ b/Content.Client/Store/Ui/StoreMenu.xaml @@ -28,7 +28,8 @@ HorizontalAlignment="Right" Text="Refund" /> - + + diff --git a/Content.Client/Store/Ui/StoreMenu.xaml.cs b/Content.Client/Store/Ui/StoreMenu.xaml.cs index 5dc1ab246bd..67e5d360a3a 100644 --- a/Content.Client/Store/Ui/StoreMenu.xaml.cs +++ b/Content.Client/Store/Ui/StoreMenu.xaml.cs @@ -1,5 +1,4 @@ using System.Linq; -using System.Threading; using Content.Client.Actions; using Content.Client.GameTicking.Managers; using Content.Client.Message; @@ -27,6 +26,7 @@ public sealed partial class StoreMenu : DefaultWindow private StoreWithdrawWindow? _withdrawWindow; + public event EventHandler? SearchTextUpdated; public event Action? OnListingButtonPressed; public event Action? OnCategoryButtonPressed; public event Action? OnWithdrawAttempt; @@ -46,6 +46,7 @@ public StoreMenu(string name) WithdrawButton.OnButtonDown += OnWithdrawButtonDown; RefreshButton.OnButtonDown += OnRefreshButtonDown; RefundButton.OnButtonDown += OnRefundButtonDown; + SearchBar.OnTextChanged += _ => SearchTextUpdated?.Invoke(this, SearchBar.Text); if (Window != null) Window.Title = name; @@ -59,7 +60,7 @@ public void UpdateBalance(Dictionary balance) (type.Key, type.Value), type => _prototypeManager.Index(type.Key)); var balanceStr = string.Empty; - foreach (var ((type, amount),proto) in currency) + foreach (var ((_, amount), proto) in currency) { balanceStr += Loc.GetString("store-ui-balance-display", ("amount", amount), ("currency", Loc.GetString(proto.DisplayName, ("amount", 1)))); @@ -81,7 +82,6 @@ public void UpdateListing(List listings) { var sorted = listings.OrderBy(l => l.Priority).ThenBy(l => l.Cost.Values.Sum()); - // should probably chunk these out instead. to-do if this clogs the internet tubes. // maybe read clients prototypes instead? ClearListings(); @@ -129,8 +129,8 @@ private void AddListingGui(ListingData listing) if (!listing.Categories.Contains(CurrentCategory)) return; - var listingName = Loc.GetString(listing.Name); - var listingDesc = Loc.GetString(listing.Description); + var listingName = ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, _prototypeManager); + var listingDesc = ListingLocalisationHelpers.GetLocalisedDescriptionOrEntityDescription(listing, _prototypeManager); var listingPrice = listing.Cost; var canBuy = CanBuyListing(Balance, listingPrice); @@ -144,12 +144,6 @@ private void AddListingGui(ListingData listing) { if (texture == null) texture = spriteSys.GetPrototypeIcon(listing.ProductEntity).Default; - - var proto = _prototypeManager.Index(listing.ProductEntity); - if (listingName == string.Empty) - listingName = proto.Name; - if (listingDesc == string.Empty) - listingDesc = proto.Description; } else if (listing.ProductAction != null) { @@ -243,13 +237,16 @@ public void PopulateStoreCategoryButtons(HashSet listings) allCategories = allCategories.OrderBy(c => c.Priority).ToList(); + // This will reset the Current Category selection if nothing matches the search. + if (allCategories.All(category => category.ID != CurrentCategory)) + CurrentCategory = string.Empty; + if (CurrentCategory == string.Empty && allCategories.Count > 0) CurrentCategory = allCategories.First().ID; - if (allCategories.Count <= 1) - return; - CategoryListContainer.Children.Clear(); + if (allCategories.Count < 1) + return; foreach (var proto in allCategories) { diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index 49db980451e..e6c4eb0ccea 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -15,6 +15,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Player; +using Robust.Shared.Prototypes; namespace Content.Server.Store.Systems; @@ -29,6 +30,7 @@ public sealed partial class StoreSystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private void InitializeUi() { @@ -259,7 +261,7 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi //log dat shit. _admin.Add(LogType.StorePurchase, LogImpact.Low, - $"{ToPrettyString(buyer):player} purchased listing \"{Loc.GetString(listing.Name)}\" from {ToPrettyString(uid)}"); + $"{ToPrettyString(buyer):player} purchased listing \"{ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, _prototypeManager)}\" from {ToPrettyString(uid)}"); listing.PurchaseAmount++; //track how many times something has been purchased _audio.PlayEntity(component.BuySuccessSound, msg.Session, uid); //cha-ching! diff --git a/Content.Shared/Store/ListingLocalisationHelpers.cs b/Content.Shared/Store/ListingLocalisationHelpers.cs new file mode 100644 index 00000000000..3ac75cd8010 --- /dev/null +++ b/Content.Shared/Store/ListingLocalisationHelpers.cs @@ -0,0 +1,42 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Store; + +public static class ListingLocalisationHelpers +{ + ///

+ /// ListingData's Name field can be either a localisation string or the actual entity's name. + /// This function gets a localised name from the localisation string if it exists, and if not, it gets the entity's name. + /// If neither a localised string exists, or an associated entity name, it will return the value of the "Name" field. + /// + public static string GetLocalisedNameOrEntityName(ListingData listingData, IPrototypeManager prototypeManager) + { + bool wasLocalised = Loc.TryGetString(listingData.Name, out string? listingName); + + if (!wasLocalised && listingData.ProductEntity != null) + { + var proto = prototypeManager.Index(listingData.ProductEntity); + listingName = proto.Name; + } + + return listingName ?? listingData.Name; + } + + /// + /// ListingData's Description field can be either a localisation string or the actual entity's description. + /// This function gets a localised description from the localisation string if it exists, and if not, it gets the entity's description. + /// If neither a localised string exists, or an associated entity description, it will return the value of the "Description" field. + /// + public static string GetLocalisedDescriptionOrEntityDescription(ListingData listingData, IPrototypeManager prototypeManager) + { + bool wasLocalised = Loc.TryGetString(listingData.Description, out string? listingDesc); + + if (!wasLocalised && listingData.ProductEntity != null) + { + var proto = prototypeManager.Index(listingData.ProductEntity); + listingDesc = proto.Description; + } + + return listingDesc ?? listingData.Description; + } +} From 4d2aa1a70a4226cc65399907484c68fdda9e5e5d Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 04:10:21 +0000 Subject: [PATCH 056/133] 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 f36805bd1d8..46d239e83ff 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Flareguy - changes: - - message: The Research Director's hardsuit now uses its older sprites. - type: Tweak - id: 5766 - time: '2024-01-22T01:52:32.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24377 - author: SonicHDC changes: - message: Added reinforced diagonal! @@ -3793,3 +3786,11 @@ id: 6265 time: '2024-03-31T03:21:18.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/22133 +- author: DrMelon + changes: + - message: Syndicate Uplinks now have a searchbar to help those dirty, rotten antagonists + find appropriate equipment more easily! + type: Tweak + id: 6266 + time: '2024-03-31T04:09:15.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/24287 From 02273ca0e7a4091bac88e585ecca5253dd0e7fd7 Mon Sep 17 00:00:00 2001 From: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Date: Sat, 30 Mar 2024 23:29:47 -0500 Subject: [PATCH 057/133] Improved RCDs (#22799) * Initial radial menu prototyping for the RCD * Radial UI buttons can send messages to the server * Beginning to update RCDSystem * RCD building system in progress * Further updates * Added extra effects, RCDSystem now reads RCD prototype data * Replacing tiles is instant, multiple constructions are allowed, deconstruction is broken * Added extra functionality to RadialContainers plus documentation * Fixed localization of RCD UI strings * Menu opens near cursor, added basic RCD * Avoiding merge conflict * Implemented atomized construction / deconstruction rules * Increased RCD ammo base charges * Moved input context definition to content * Removed obsoleted code * Updates to system * Switch machine and computer frames for electrical cabling * Added construction ghosts * Fixed issue with keybind detection code * Fixed RCD construction ghost mispredications * Code clean up * Updated deconstruction effects * RCDs effects don't rotate * Code clean up * Balancing for ammo counts * Code clean up * Added missing localized strings * More clean up * Made directional window handling more robust * Added documentation to radial menus and made them no longer dependent on Content * Made radial containers more robust * Further robustness to the radial menu * The RCD submenu buttons are only shown when the destination layer has at least one children * Expanded upon deconstructing plus construction balance * Fixed line endings * Updated list of RCD deconstructable entities. Now needs a component to deconstruct instead of a tag * Bug fixes * Revert unnecessary change * Updated RCD strings * Fixed bug * More fixes * Deconstructed tiles/subflooring convert to lattice instead * Fixed failed tests (Linux doesn't like invalid spritespecifer paths) * Fixing merge conflict * Updated airlock assembly * Fixing merge conflict * Fixing merge conflict * More fixing... * Removed erroneous project file change * Fixed string handling issue * Trying to fix merge conflict * Still fixing merge conflicts * Balancing * Hidden RCD construction ghosts when in 'build' mode * Fixing merge conflict * Implemented requested changes (Part 1) * Added more requested changes * Fix for failed test. Removed sussy null suppression * Made requested changes - custom construction ghost system was replaced * Fixing merge conflict * Fixed merge conflict * Fixed bug in RCD construction ghost validation * Fixing merge conflict * Merge conflict fixed * Made required update * Removed lingering RCD deconstruct tag * Fixing merge conflict * Merge conflict fixed * Made requested changes * Bug fixes and balancing * Made string names more consistent * Can no longer stack catwalks --- Content.Client/Input/ContentContexts.cs | 3 + Content.Client/RCD/AlignRCDConstruction.cs | 122 ++++ .../RCD/RCDConstructionGhostSystem.cs | 78 ++ Content.Client/RCD/RCDMenu.xaml | 47 ++ Content.Client/RCD/RCDMenu.xaml.cs | 137 ++++ .../RCD/RCDMenuBoundUserInterface.cs | 49 ++ Content.Client/Stylesheets/StyleNano.cs | 47 +- .../UserInterface/Controls/RadialContainer.cs | 105 +++ .../UserInterface/Controls/RadialMenu.cs | 255 +++++++ .../Charges/Systems/SharedChargesSystem.cs | 22 + .../Components/ComputerBoardComponent.cs | 4 +- .../RCD/Components/RCDAmmoComponent.cs | 4 +- Content.Shared/RCD/Components/RCDComponent.cs | 71 +- .../Components/RCDDeconstructibleComponent.cs | 34 + Content.Shared/RCD/RCDEvents.cs | 34 + Content.Shared/RCD/RCDPrototype.cs | 144 ++++ Content.Shared/RCD/Systems/RCDSystem.cs | 682 +++++++++++++----- .../en-US/rcd/components/rcd-component.ftl | 68 +- Resources/Locale/en-US/ui/general.ftl | 3 + .../Catalog/Fills/Crates/engineering.yml | 6 +- .../Entities/Effects/chemistry_effects.yml | 7 +- Resources/Prototypes/Entities/Effects/rcd.yml | 107 ++- .../Entities/Markers/construction_ghost.yml | 2 +- .../Entities/Objects/Tools/tools.yml | 71 +- .../Structures/Doors/Airlocks/airlocks.yml | 44 +- .../Doors/Airlocks/base_assembly.yml | 4 + .../Doors/Airlocks/base_structureairlocks.yml | 54 +- .../Structures/Doors/Airlocks/external.yml | 2 +- .../Structures/Doors/Airlocks/shuttle.yml | 2 +- .../Structures/Doors/Firelocks/firelock.yml | 4 + .../Structures/Doors/Firelocks/frame.yml | 4 + .../Doors/MaterialDoors/material_doors.yml | 4 + .../Doors/SecretDoor/secret_door.yml | 8 + .../Structures/Doors/Windoors/assembly.yml | 4 + .../Doors/Windoors/base_structurewindoors.yml | 6 +- .../Structures/Lighting/base_lighting.yml | 6 +- .../Structures/Power/cable_terminal.yml | 4 + .../Entities/Structures/Power/cables.yml | 4 + .../Entities/Structures/Walls/fence_metal.yml | 7 +- .../Entities/Structures/Walls/fence_wood.yml | 5 +- .../Entities/Structures/Walls/grille.yml | 14 +- .../Entities/Structures/Walls/railing.yml | 18 +- .../Entities/Structures/Walls/walls.yml | 58 +- .../Entities/Structures/Windows/mining.yml | 2 +- .../Entities/Structures/Windows/plasma.yml | 4 +- .../Structures/Windows/reinforced.yml | 8 + .../Entities/Structures/Windows/rplasma.yml | 4 +- .../Entities/Structures/Windows/ruranium.yml | 2 +- .../Entities/Structures/Windows/shuttle.yml | 2 +- .../Entities/Structures/Windows/uranium.yml | 2 +- .../Entities/Structures/Windows/window.yml | 25 +- .../Entities/Structures/catwalk.yml | 4 + Resources/Prototypes/RCD/rcd.yml | 294 ++++++++ Resources/Prototypes/tags.yml | 3 - .../Textures/Effects/rcd.rsi/construct.png | Bin 3612 -> 0 bytes .../Textures/Effects/rcd.rsi/construct0.png | Bin 0 -> 1095 bytes .../Textures/Effects/rcd.rsi/construct1.png | Bin 0 -> 3663 bytes .../Textures/Effects/rcd.rsi/construct2.png | Bin 0 -> 3663 bytes .../Textures/Effects/rcd.rsi/construct3.png | Bin 0 -> 3663 bytes .../Textures/Effects/rcd.rsi/construct4.png | Bin 0 -> 6200 bytes .../Textures/Effects/rcd.rsi/deconstruct2.png | Bin 0 -> 4964 bytes .../Textures/Effects/rcd.rsi/deconstruct4.png | Bin 0 -> 8158 bytes .../Textures/Effects/rcd.rsi/deconstruct6.png | Bin 0 -> 8158 bytes .../Textures/Effects/rcd.rsi/deconstruct8.png | Bin 0 -> 7941 bytes .../Effects/rcd.rsi/deconstructPreview.png | Bin 0 -> 47364 bytes Resources/Textures/Effects/rcd.rsi/meta.json | 411 ++++++++++- .../Textures/Interface/Radial/RCD/airlock.png | Bin 0 -> 710 bytes .../Interface/Radial/RCD/airlocks.png | Bin 0 -> 776 bytes .../Interface/Radial/RCD/bulb_light.png | Bin 0 -> 283 bytes .../Interface/Radial/RCD/cable_terminal.png | Bin 0 -> 646 bytes .../Textures/Interface/Radial/RCD/catwalk.png | Bin 0 -> 404 bytes .../Interface/Radial/RCD/computer_frame.png | Bin 0 -> 467 bytes .../Radial/RCD/computers_and_frames.png | Bin 0 -> 775 bytes .../Interface/Radial/RCD/deconstruct.png | Bin 0 -> 1200 bytes .../Interface/Radial/RCD/directional.png | Bin 0 -> 312 bytes .../Radial/RCD/directional_reinforced.png | Bin 0 -> 296 bytes .../Interface/Radial/RCD/firelock.png | Bin 0 -> 637 bytes .../Interface/Radial/RCD/glass_airlock.png | Bin 0 -> 698 bytes .../Textures/Interface/Radial/RCD/grille.png | Bin 0 -> 223 bytes .../Textures/Interface/Radial/RCD/hv_coil.png | Bin 0 -> 856 bytes .../Interface/Radial/RCD/lighting.png | Bin 0 -> 901 bytes .../Textures/Interface/Radial/RCD/lv_coil.png | Bin 0 -> 850 bytes .../Interface/Radial/RCD/machine_frame.png | Bin 0 -> 655 bytes .../Interface/Radial/RCD/metal_tile.png | Bin 0 -> 297 bytes .../Interface/Radial/RCD/multicoil.png | Bin 0 -> 976 bytes .../Textures/Interface/Radial/RCD/mv_coil.png | Bin 0 -> 833 bytes .../Textures/Interface/Radial/RCD/plating.png | Bin 0 -> 436 bytes .../Interface/Radial/RCD/reinforced_wall.png | Bin 0 -> 2312 bytes .../Interface/Radial/RCD/solid_wall.png | Bin 0 -> 448 bytes .../Interface/Radial/RCD/tube_light.png | Bin 0 -> 266 bytes .../Radial/RCD/walls_and_flooring.png | Bin 0 -> 431 bytes .../Textures/Interface/Radial/RCD/window.png | Bin 0 -> 739 bytes .../Radial/RCD/window_reinforced.png | Bin 0 -> 1175 bytes .../Radial/RCD/windows_and_grilles.png | Bin 0 -> 1205 bytes .../Textures/Interface/Radial/back_hover.png | Bin 0 -> 495 bytes .../Textures/Interface/Radial/back_normal.png | Bin 0 -> 525 bytes .../Interface/Radial/button_hover.png | Bin 0 -> 211 bytes .../Interface/Radial/button_normal.png | Bin 0 -> 186 bytes .../Textures/Interface/Radial/close_hover.png | Bin 0 -> 389 bytes .../Interface/Radial/close_normal.png | Bin 0 -> 391 bytes 100 files changed, 2750 insertions(+), 365 deletions(-) create mode 100644 Content.Client/RCD/AlignRCDConstruction.cs create mode 100644 Content.Client/RCD/RCDConstructionGhostSystem.cs create mode 100644 Content.Client/RCD/RCDMenu.xaml create mode 100644 Content.Client/RCD/RCDMenu.xaml.cs create mode 100644 Content.Client/RCD/RCDMenuBoundUserInterface.cs create mode 100644 Content.Client/UserInterface/Controls/RadialContainer.cs create mode 100644 Content.Client/UserInterface/Controls/RadialMenu.cs create mode 100644 Content.Shared/RCD/Components/RCDDeconstructibleComponent.cs create mode 100644 Content.Shared/RCD/RCDEvents.cs create mode 100644 Content.Shared/RCD/RCDPrototype.cs create mode 100644 Resources/Locale/en-US/ui/general.ftl create mode 100644 Resources/Prototypes/RCD/rcd.yml delete mode 100644 Resources/Textures/Effects/rcd.rsi/construct.png create mode 100644 Resources/Textures/Effects/rcd.rsi/construct0.png create mode 100644 Resources/Textures/Effects/rcd.rsi/construct1.png create mode 100644 Resources/Textures/Effects/rcd.rsi/construct2.png create mode 100644 Resources/Textures/Effects/rcd.rsi/construct3.png create mode 100644 Resources/Textures/Effects/rcd.rsi/construct4.png create mode 100644 Resources/Textures/Effects/rcd.rsi/deconstruct2.png create mode 100644 Resources/Textures/Effects/rcd.rsi/deconstruct4.png create mode 100644 Resources/Textures/Effects/rcd.rsi/deconstruct6.png create mode 100644 Resources/Textures/Effects/rcd.rsi/deconstruct8.png create mode 100644 Resources/Textures/Effects/rcd.rsi/deconstructPreview.png create mode 100644 Resources/Textures/Interface/Radial/RCD/airlock.png create mode 100644 Resources/Textures/Interface/Radial/RCD/airlocks.png create mode 100644 Resources/Textures/Interface/Radial/RCD/bulb_light.png create mode 100644 Resources/Textures/Interface/Radial/RCD/cable_terminal.png create mode 100644 Resources/Textures/Interface/Radial/RCD/catwalk.png create mode 100644 Resources/Textures/Interface/Radial/RCD/computer_frame.png create mode 100644 Resources/Textures/Interface/Radial/RCD/computers_and_frames.png create mode 100644 Resources/Textures/Interface/Radial/RCD/deconstruct.png create mode 100644 Resources/Textures/Interface/Radial/RCD/directional.png create mode 100644 Resources/Textures/Interface/Radial/RCD/directional_reinforced.png create mode 100644 Resources/Textures/Interface/Radial/RCD/firelock.png create mode 100644 Resources/Textures/Interface/Radial/RCD/glass_airlock.png create mode 100644 Resources/Textures/Interface/Radial/RCD/grille.png create mode 100644 Resources/Textures/Interface/Radial/RCD/hv_coil.png create mode 100644 Resources/Textures/Interface/Radial/RCD/lighting.png create mode 100644 Resources/Textures/Interface/Radial/RCD/lv_coil.png create mode 100644 Resources/Textures/Interface/Radial/RCD/machine_frame.png create mode 100644 Resources/Textures/Interface/Radial/RCD/metal_tile.png create mode 100644 Resources/Textures/Interface/Radial/RCD/multicoil.png create mode 100644 Resources/Textures/Interface/Radial/RCD/mv_coil.png create mode 100644 Resources/Textures/Interface/Radial/RCD/plating.png create mode 100644 Resources/Textures/Interface/Radial/RCD/reinforced_wall.png create mode 100644 Resources/Textures/Interface/Radial/RCD/solid_wall.png create mode 100644 Resources/Textures/Interface/Radial/RCD/tube_light.png create mode 100644 Resources/Textures/Interface/Radial/RCD/walls_and_flooring.png create mode 100644 Resources/Textures/Interface/Radial/RCD/window.png create mode 100644 Resources/Textures/Interface/Radial/RCD/window_reinforced.png create mode 100644 Resources/Textures/Interface/Radial/RCD/windows_and_grilles.png create mode 100644 Resources/Textures/Interface/Radial/back_hover.png create mode 100644 Resources/Textures/Interface/Radial/back_normal.png create mode 100644 Resources/Textures/Interface/Radial/button_hover.png create mode 100644 Resources/Textures/Interface/Radial/button_normal.png create mode 100644 Resources/Textures/Interface/Radial/close_hover.png create mode 100644 Resources/Textures/Interface/Radial/close_normal.png diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs index 589de6d6a78..2e888b3df98 100644 --- a/Content.Client/Input/ContentContexts.cs +++ b/Content.Client/Input/ContentContexts.cs @@ -45,6 +45,9 @@ public static void SetupContexts(IInputContextContainer contexts) // Not in engine because the engine doesn't understand what a flipped object is common.AddFunction(ContentKeyFunctions.EditorFlipObject); + // Not in engine so that the RCD can rotate objects + common.AddFunction(EngineKeyFunctions.EditorRotateObject); + var human = contexts.GetContext("human"); human.AddFunction(EngineKeyFunctions.MoveUp); human.AddFunction(EngineKeyFunctions.MoveDown); diff --git a/Content.Client/RCD/AlignRCDConstruction.cs b/Content.Client/RCD/AlignRCDConstruction.cs new file mode 100644 index 00000000000..da7b22c91a8 --- /dev/null +++ b/Content.Client/RCD/AlignRCDConstruction.cs @@ -0,0 +1,122 @@ +using System.Numerics; +using Content.Client.Gameplay; +using Content.Shared.Hands.Components; +using Content.Shared.Interaction; +using Content.Shared.RCD.Components; +using Content.Shared.RCD.Systems; +using Robust.Client.Placement; +using Robust.Client.Player; +using Robust.Client.State; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; + +namespace Content.Client.RCD; + +public sealed class AlignRCDConstruction : PlacementMode +{ + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly RCDSystem _rcdSystem = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IStateManager _stateManager = default!; + + private const float SearchBoxSize = 2f; + private const float PlaceColorBaseAlpha = 0.5f; + + private EntityCoordinates _unalignedMouseCoords = default; + + /// + /// This placement mode is not on the engine because it is content specific (i.e., for the RCD) + /// + public AlignRCDConstruction(PlacementManager pMan) : base(pMan) + { + var dependencies = IoCManager.Instance!; + _entityManager = dependencies.Resolve(); + _mapManager = dependencies.Resolve(); + _playerManager = dependencies.Resolve(); + _stateManager = dependencies.Resolve(); + + _mapSystem = _entityManager.System(); + _rcdSystem = _entityManager.System(); + _transformSystem = _entityManager.System(); + + ValidPlaceColor = ValidPlaceColor.WithAlpha(PlaceColorBaseAlpha); + } + + public override void AlignPlacementMode(ScreenCoordinates mouseScreen) + { + _unalignedMouseCoords = ScreenToCursorGrid(mouseScreen); + MouseCoords = _unalignedMouseCoords.AlignWithClosestGridTile(SearchBoxSize, _entityManager, _mapManager); + + var gridId = MouseCoords.GetGridUid(_entityManager); + + if (!_entityManager.TryGetComponent(gridId, out var mapGrid)) + return; + + CurrentTile = _mapSystem.GetTileRef(gridId.Value, mapGrid, MouseCoords); + + float tileSize = mapGrid.TileSize; + GridDistancing = tileSize; + + if (pManager.CurrentPermission!.IsTile) + { + MouseCoords = new EntityCoordinates(MouseCoords.EntityId, new Vector2(CurrentTile.X + tileSize / 2, + CurrentTile.Y + tileSize / 2)); + } + else + { + MouseCoords = new EntityCoordinates(MouseCoords.EntityId, new Vector2(CurrentTile.X + tileSize / 2 + pManager.PlacementOffset.X, + CurrentTile.Y + tileSize / 2 + pManager.PlacementOffset.Y)); + } + } + + public override bool IsValidPosition(EntityCoordinates position) + { + var player = _playerManager.LocalSession?.AttachedEntity; + + // If the destination is out of interaction range, set the placer alpha to zero + if (!_entityManager.TryGetComponent(player, out var xform)) + return false; + + if (!xform.Coordinates.InRange(_entityManager, _transformSystem, position, SharedInteractionSystem.InteractionRange)) + { + InvalidPlaceColor = InvalidPlaceColor.WithAlpha(0); + return false; + } + + // Otherwise restore the alpha value + else + { + InvalidPlaceColor = InvalidPlaceColor.WithAlpha(PlaceColorBaseAlpha); + } + + // Determine if player is carrying an RCD in their active hand + if (!_entityManager.TryGetComponent(player, out var hands)) + return false; + + var heldEntity = hands.ActiveHand?.HeldEntity; + + if (!_entityManager.TryGetComponent(heldEntity, out var rcd)) + return false; + + // Retrieve the map grid data for the position + if (!_rcdSystem.TryGetMapGridData(position, out var mapGridData)) + return false; + + // Determine if the user is hovering over a target + var currentState = _stateManager.CurrentState; + + if (currentState is not GameplayStateBase screen) + return false; + + var target = screen.GetClickedEntity(_unalignedMouseCoords.ToMap(_entityManager, _transformSystem)); + + // Determine if the RCD operation is valid or not + if (!_rcdSystem.IsRCDOperationStillValid(heldEntity.Value, rcd, mapGridData.Value, target, player.Value, false)) + return false; + + return true; + } +} diff --git a/Content.Client/RCD/RCDConstructionGhostSystem.cs b/Content.Client/RCD/RCDConstructionGhostSystem.cs new file mode 100644 index 00000000000..792916b8922 --- /dev/null +++ b/Content.Client/RCD/RCDConstructionGhostSystem.cs @@ -0,0 +1,78 @@ +using Content.Shared.Hands.Components; +using Content.Shared.Interaction; +using Content.Shared.RCD; +using Content.Shared.RCD.Components; +using Content.Shared.RCD.Systems; +using Robust.Client.Placement; +using Robust.Client.Player; +using Robust.Shared.Enums; + +namespace Content.Client.RCD; + +public sealed class RCDConstructionGhostSystem : EntitySystem +{ + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly RCDSystem _rcdSystem = default!; + [Dependency] private readonly IPlacementManager _placementManager = default!; + + private string _placementMode = typeof(AlignRCDConstruction).Name; + private Direction _placementDirection = default; + + public override void Update(float frameTime) + { + base.Update(frameTime); + + // Get current placer data + var placerEntity = _placementManager.CurrentPermission?.MobUid; + var placerProto = _placementManager.CurrentPermission?.EntityType; + var placerIsRCD = HasComp(placerEntity); + + // Exit if erasing or the current placer is not an RCD (build mode is active) + if (_placementManager.Eraser || (placerEntity != null && !placerIsRCD)) + return; + + // Determine if player is carrying an RCD in their active hand + var player = _playerManager.LocalSession?.AttachedEntity; + + if (!TryComp(player, out var hands)) + return; + + var heldEntity = hands.ActiveHand?.HeldEntity; + + if (!TryComp(heldEntity, out var rcd)) + { + // If the player was holding an RCD, but is no longer, cancel placement + if (placerIsRCD) + _placementManager.Clear(); + + return; + } + + // Update the direction the RCD prototype based on the placer direction + if (_placementDirection != _placementManager.Direction) + { + _placementDirection = _placementManager.Direction; + RaiseNetworkEvent(new RCDConstructionGhostRotationEvent(GetNetEntity(heldEntity.Value), _placementDirection)); + } + + // If the placer has not changed, exit + _rcdSystem.UpdateCachedPrototype(heldEntity.Value, rcd); + + if (heldEntity == placerEntity && rcd.CachedPrototype.Prototype == placerProto) + return; + + // Create a new placer + var newObjInfo = new PlacementInformation + { + MobUid = heldEntity.Value, + PlacementOption = _placementMode, + EntityType = rcd.CachedPrototype.Prototype, + Range = (int) Math.Ceiling(SharedInteractionSystem.InteractionRange), + IsTile = (rcd.CachedPrototype.Mode == RcdMode.ConstructTile), + UseEditorContext = false, + }; + + _placementManager.Clear(); + _placementManager.BeginPlacing(newObjInfo); + } +} diff --git a/Content.Client/RCD/RCDMenu.xaml b/Content.Client/RCD/RCDMenu.xaml new file mode 100644 index 00000000000..b3d5367a5fd --- /dev/null +++ b/Content.Client/RCD/RCDMenu.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content.Client/RCD/RCDMenu.xaml.cs b/Content.Client/RCD/RCDMenu.xaml.cs new file mode 100644 index 00000000000..8679e789dc7 --- /dev/null +++ b/Content.Client/RCD/RCDMenu.xaml.cs @@ -0,0 +1,137 @@ +using Content.Client.UserInterface.Controls; +using Content.Shared.RCD; +using Content.Shared.RCD.Components; +using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; +using System.Numerics; + +namespace Content.Client.RCD; + +[GenerateTypedNameReferences] +public sealed partial class RCDMenu : RadialMenu +{ + [Dependency] private readonly EntityManager _entManager = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + + private readonly SpriteSystem _spriteSystem; + + public event Action>? SendRCDSystemMessageAction; + + public RCDMenu(EntityUid owner, RCDMenuBoundUserInterface bui) + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + + _spriteSystem = _entManager.System(); + + // Find the main radial container + var main = FindControl("Main"); + + if (main == null) + return; + + // Populate secondary radial containers + if (!_entManager.TryGetComponent(owner, out var rcd)) + return; + + foreach (var protoId in rcd.AvailablePrototypes) + { + if (!_protoManager.TryIndex(protoId, out var proto)) + continue; + + if (proto.Mode == RcdMode.Invalid) + continue; + + var parent = FindControl(proto.Category); + + if (parent == null) + continue; + + var name = Loc.GetString(proto.SetName); + name = char.ToUpper(name[0]) + name.Remove(0, 1); + + var button = new RCDMenuButton() + { + StyleClasses = { "RadialMenuButton" }, + SetSize = new Vector2(64f, 64f), + ToolTip = name, + ProtoId = protoId, + }; + + if (proto.Sprite != null) + { + var tex = new TextureRect() + { + VerticalAlignment = VAlignment.Center, + HorizontalAlignment = HAlignment.Center, + Texture = _spriteSystem.Frame0(proto.Sprite), + TextureScale = new Vector2(2f, 2f), + }; + + button.AddChild(tex); + } + + parent.AddChild(button); + + // Ensure that the button that transitions the menu to the associated category layer + // is visible in the main radial container (as these all start with Visible = false) + foreach (var child in main.Children) + { + var castChild = child as RadialMenuTextureButton; + + if (castChild is not RadialMenuTextureButton) + continue; + + if (castChild.TargetLayer == proto.Category) + { + castChild.Visible = true; + break; + } + } + } + + // Set up menu actions + foreach (var child in Children) + AddRCDMenuButtonOnClickActions(child); + + OnChildAdded += AddRCDMenuButtonOnClickActions; + + SendRCDSystemMessageAction += bui.SendRCDSystemMessage; + } + + private void AddRCDMenuButtonOnClickActions(Control control) + { + var radialContainer = control as RadialContainer; + + if (radialContainer == null) + return; + + foreach (var child in radialContainer.Children) + { + var castChild = child as RCDMenuButton; + + if (castChild == null) + continue; + + castChild.OnButtonUp += _ => + { + SendRCDSystemMessageAction?.Invoke(castChild.ProtoId); + Close(); + }; + } + } +} + +public sealed class RCDMenuButton : RadialMenuTextureButton +{ + public ProtoId ProtoId { get; set; } + + public RCDMenuButton() + { + + } +} diff --git a/Content.Client/RCD/RCDMenuBoundUserInterface.cs b/Content.Client/RCD/RCDMenuBoundUserInterface.cs new file mode 100644 index 00000000000..a37dbcecf8c --- /dev/null +++ b/Content.Client/RCD/RCDMenuBoundUserInterface.cs @@ -0,0 +1,49 @@ +using Content.Shared.RCD; +using Content.Shared.RCD.Components; +using JetBrains.Annotations; +using Robust.Client.Graphics; +using Robust.Client.Input; +using Robust.Shared.Prototypes; + +namespace Content.Client.RCD; + +[UsedImplicitly] +public sealed class RCDMenuBoundUserInterface : BoundUserInterface +{ + [Dependency] private readonly IClyde _displayManager = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + + private RCDMenu? _menu; + + public RCDMenuBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + IoCManager.InjectDependencies(this); + } + + protected override void Open() + { + base.Open(); + + _menu = new(Owner, this); + _menu.OnClose += Close; + + // Open the menu, centered on the mouse + var vpSize = _displayManager.ScreenSize; + _menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize); + } + + public void SendRCDSystemMessage(ProtoId protoId) + { + // A predicted message cannot be used here as the RCD UI is closed immediately + // after this message is sent, which will stop the server from receiving it + SendMessage(new RCDSystemMessage(protoId)); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) return; + + _menu?.Dispose(); + } +} diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs index 426af1616ec..2c7a1873a36 100644 --- a/Content.Client/Stylesheets/StyleNano.cs +++ b/Content.Client/Stylesheets/StyleNano.cs @@ -290,7 +290,7 @@ public StyleNano(IResourceCache resCache) : base(resCache) var buttonTex = resCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); var topButtonBase = new StyleBoxTexture { - Texture = buttonTex, + Texture = buttonTex, }; topButtonBase.SetPatchMargin(StyleBox.Margin.All, 10); topButtonBase.SetPadding(StyleBox.Margin.All, 0); @@ -298,19 +298,19 @@ public StyleNano(IResourceCache resCache) : base(resCache) var topButtonOpenRight = new StyleBoxTexture(topButtonBase) { - Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(0, 0), new Vector2(14, 24))), + Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(0, 0), new Vector2(14, 24))), }; topButtonOpenRight.SetPatchMargin(StyleBox.Margin.Right, 0); var topButtonOpenLeft = new StyleBoxTexture(topButtonBase) { - Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(10, 0), new Vector2(14, 24))), + Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(10, 0), new Vector2(14, 24))), }; topButtonOpenLeft.SetPatchMargin(StyleBox.Margin.Left, 0); var topButtonSquare = new StyleBoxTexture(topButtonBase) { - Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(10, 0), new Vector2(3, 24))), + Texture = new AtlasTexture(buttonTex, UIBox2.FromDimensions(new Vector2(10, 0), new Vector2(3, 24))), }; topButtonSquare.SetPatchMargin(StyleBox.Margin.Horizontal, 0); @@ -368,9 +368,9 @@ public StyleNano(IResourceCache resCache) : base(resCache) }; tabContainerPanel.SetPatchMargin(StyleBox.Margin.All, 2); - var tabContainerBoxActive = new StyleBoxFlat {BackgroundColor = new Color(64, 64, 64)}; + var tabContainerBoxActive = new StyleBoxFlat { BackgroundColor = new Color(64, 64, 64) }; tabContainerBoxActive.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5); - var tabContainerBoxInactive = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 32)}; + var tabContainerBoxInactive = new StyleBoxFlat { BackgroundColor = new Color(32, 32, 32) }; tabContainerBoxInactive.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5); var progressBarBackground = new StyleBoxFlat @@ -409,21 +409,21 @@ public StyleNano(IResourceCache resCache) : base(resCache) // Placeholder var placeholderTexture = resCache.GetTexture("/Textures/Interface/Nano/placeholder.png"); - var placeholder = new StyleBoxTexture {Texture = placeholderTexture}; + var placeholder = new StyleBoxTexture { Texture = placeholderTexture }; placeholder.SetPatchMargin(StyleBox.Margin.All, 19); placeholder.SetExpandMargin(StyleBox.Margin.All, -5); placeholder.Mode = StyleBoxTexture.StretchMode.Tile; - var itemListBackgroundSelected = new StyleBoxFlat {BackgroundColor = new Color(75, 75, 86)}; + var itemListBackgroundSelected = new StyleBoxFlat { BackgroundColor = new Color(75, 75, 86) }; itemListBackgroundSelected.SetContentMarginOverride(StyleBox.Margin.Vertical, 2); itemListBackgroundSelected.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4); - var itemListItemBackgroundDisabled = new StyleBoxFlat {BackgroundColor = new Color(10, 10, 12)}; + var itemListItemBackgroundDisabled = new StyleBoxFlat { BackgroundColor = new Color(10, 10, 12) }; itemListItemBackgroundDisabled.SetContentMarginOverride(StyleBox.Margin.Vertical, 2); itemListItemBackgroundDisabled.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4); - var itemListItemBackground = new StyleBoxFlat {BackgroundColor = new Color(55, 55, 68)}; + var itemListItemBackground = new StyleBoxFlat { BackgroundColor = new Color(55, 55, 68) }; itemListItemBackground.SetContentMarginOverride(StyleBox.Margin.Vertical, 2); itemListItemBackground.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4); - var itemListItemBackgroundTransparent = new StyleBoxFlat {BackgroundColor = Color.Transparent}; + var itemListItemBackgroundTransparent = new StyleBoxFlat { BackgroundColor = Color.Transparent }; itemListItemBackgroundTransparent.SetContentMarginOverride(StyleBox.Margin.Vertical, 2); itemListItemBackgroundTransparent.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4); @@ -489,9 +489,9 @@ public StyleNano(IResourceCache resCache) : base(resCache) sliderForeBox.SetPatchMargin(StyleBox.Margin.All, 12); sliderGrabBox.SetPatchMargin(StyleBox.Margin.All, 12); - var sliderFillGreen = new StyleBoxTexture(sliderFillBox) {Modulate = Color.LimeGreen}; - var sliderFillRed = new StyleBoxTexture(sliderFillBox) {Modulate = Color.Red}; - var sliderFillBlue = new StyleBoxTexture(sliderFillBox) {Modulate = Color.Blue}; + var sliderFillGreen = new StyleBoxTexture(sliderFillBox) { Modulate = Color.LimeGreen }; + var sliderFillRed = new StyleBoxTexture(sliderFillBox) { Modulate = Color.Red }; + var sliderFillBlue = new StyleBoxTexture(sliderFillBox) { Modulate = Color.Blue }; var sliderFillWhite = new StyleBoxTexture(sliderFillBox) { Modulate = Color.White }; var boxFont13 = resCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13); @@ -1468,6 +1468,25 @@ public StyleNano(IResourceCache resCache) : base(resCache) Element
[DataField("charges"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public int Charges = 5; + public int Charges = 30; } - -// TODO: state??? check if it desyncs diff --git a/Content.Shared/RCD/Components/RCDComponent.cs b/Content.Shared/RCD/Components/RCDComponent.cs index 8e1032884aa..39bb6fd3e9f 100644 --- a/Content.Shared/RCD/Components/RCDComponent.cs +++ b/Content.Shared/RCD/Components/RCDComponent.cs @@ -1,20 +1,11 @@ -using Content.Shared.Maps; using Content.Shared.RCD.Systems; using Robust.Shared.Audio; using Robust.Shared.GameStates; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Physics; +using Robust.Shared.Prototypes; namespace Content.Shared.RCD.Components; -public enum RcdMode : byte -{ - Floors, - Walls, - Airlock, - Deconstruct -} - /// /// Main component for the RCD /// Optionally uses LimitedChargesComponent. @@ -25,27 +16,57 @@ public enum RcdMode : byte public sealed partial class RCDComponent : Component { /// - /// Time taken to do an action like placing a wall + /// List of RCD prototypes that the device comes loaded with /// - [DataField("delay"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public float Delay = 2f; + [DataField, AutoNetworkedField] + public HashSet> AvailablePrototypes { get; set; } = new(); - [DataField("swapModeSound")] - public SoundSpecifier SwapModeSound = new SoundPathSpecifier("/Audio/Items/genhit.ogg"); + /// + /// Sound that plays when a RCD operation successfully completes + /// + [DataField] + public SoundSpecifier SuccessSound { get; set; } = new SoundPathSpecifier("/Audio/Items/deconstruct.ogg"); - [DataField("successSound")] - public SoundSpecifier SuccessSound = new SoundPathSpecifier("/Audio/Items/deconstruct.ogg"); + /// + /// The ProtoId of the currently selected RCD prototype + /// + [DataField, AutoNetworkedField] + public ProtoId ProtoId { get; set; } = "Invalid"; /// - /// What mode are we on? Can be floors, walls, airlock, deconstruct. + /// A cached copy of currently selected RCD prototype /// - [DataField("mode"), AutoNetworkedField] - public RcdMode Mode = RcdMode.Floors; + /// + /// If the ProtoId is changed, make sure to update the CachedPrototype as well + /// + [ViewVariables(VVAccess.ReadOnly)] + public RCDPrototype CachedPrototype { get; set; } = default!; + + /// + /// The direction constructed entities will face upon spawning + /// + [DataField, AutoNetworkedField] + public Direction ConstructionDirection + { + get + { + return _constructionDirection; + } + set + { + _constructionDirection = value; + ConstructionTransform = new Transform(new(), _constructionDirection.ToAngle()); + } + } + + private Direction _constructionDirection = Direction.South; /// - /// ID of the floor to create when using the floor mode. + /// Returns a rotated transform based on the specified ConstructionDirection /// - [DataField("floor", customTypeSerializer: typeof(PrototypeIdSerializer))] - [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public string Floor = "FloorSteel"; + /// + /// Contains no position data + /// + [ViewVariables(VVAccess.ReadOnly)] + public Transform ConstructionTransform { get; private set; } = default!; } diff --git a/Content.Shared/RCD/Components/RCDDeconstructibleComponent.cs b/Content.Shared/RCD/Components/RCDDeconstructibleComponent.cs new file mode 100644 index 00000000000..0ddc6897f05 --- /dev/null +++ b/Content.Shared/RCD/Components/RCDDeconstructibleComponent.cs @@ -0,0 +1,34 @@ +using Content.Shared.RCD.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.RCD.Components; + +[RegisterComponent, NetworkedComponent] +[Access(typeof(RCDSystem))] +public sealed partial class RCDDeconstructableComponent : Component +{ + /// + /// Number of charges consumed when the deconstruction is completed + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int Cost = 1; + + /// + /// The length of the deconstruction + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float Delay = 1f; + + /// + /// The visual effect that plays during deconstruction + /// + [DataField("fx"), ViewVariables(VVAccess.ReadWrite)] + public EntProtoId? Effect = null; + + /// + /// Toggles whether this entity is deconstructable or not + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool Deconstructable = true; +} diff --git a/Content.Shared/RCD/RCDEvents.cs b/Content.Shared/RCD/RCDEvents.cs new file mode 100644 index 00000000000..a15a010277b --- /dev/null +++ b/Content.Shared/RCD/RCDEvents.cs @@ -0,0 +1,34 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.RCD; + +[Serializable, NetSerializable] +public sealed class RCDSystemMessage : BoundUserInterfaceMessage +{ + public ProtoId ProtoId; + + public RCDSystemMessage(ProtoId protoId) + { + ProtoId = protoId; + } +} + +[Serializable, NetSerializable] +public sealed class RCDConstructionGhostRotationEvent : EntityEventArgs +{ + public readonly NetEntity NetEntity; + public readonly Direction Direction; + + public RCDConstructionGhostRotationEvent(NetEntity netEntity, Direction direction) + { + NetEntity = netEntity; + Direction = direction; + } +} + +[Serializable, NetSerializable] +public enum RcdUiKey : byte +{ + Key +} diff --git a/Content.Shared/RCD/RCDPrototype.cs b/Content.Shared/RCD/RCDPrototype.cs new file mode 100644 index 00000000000..1e80abfb723 --- /dev/null +++ b/Content.Shared/RCD/RCDPrototype.cs @@ -0,0 +1,144 @@ +using Content.Shared.Physics; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Shared.RCD; + +/// +/// Contains the parameters for a RCD construction / operation +/// +[Prototype("rcd")] +public sealed class RCDPrototype : IPrototype +{ + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// The RCD mode associated with the operation + /// + [DataField(required: true), ViewVariables(VVAccess.ReadOnly)] + public RcdMode Mode { get; private set; } = RcdMode.Invalid; + + /// + /// The name associated with the prototype + /// + [DataField("name"), ViewVariables(VVAccess.ReadOnly)] + public string SetName { get; private set; } = "Unknown"; + + /// + /// The name of the radial container that this prototype will be listed under on the RCD menu + /// + [DataField, ViewVariables(VVAccess.ReadOnly)] + public string Category { get; private set; } = "Undefined"; + + /// + /// Texture path for this prototypes menu icon + /// + [DataField, ViewVariables(VVAccess.ReadOnly)] + public SpriteSpecifier? Sprite { get; private set; } = null; + + /// + /// The entity prototype that will be constructed (mode dependent) + /// + [DataField, ViewVariables(VVAccess.ReadOnly)] + public string? Prototype { get; private set; } = string.Empty; + + /// + /// Number of charges consumed when the operation is completed + /// + [DataField, ViewVariables(VVAccess.ReadOnly)] + public int Cost { get; private set; } = 1; + + /// + /// The length of the operation + /// + [DataField, ViewVariables(VVAccess.ReadOnly)] + public float Delay { get; private set; } = 1f; + + /// + /// The visual effect that plays during this operation + /// + [DataField("fx"), ViewVariables(VVAccess.ReadOnly)] + public EntProtoId? Effect { get; private set; } = null; + + /// + /// A list of rules that govern where the entity prototype can be contructed + /// + [DataField("rules"), ViewVariables(VVAccess.ReadOnly)] + public HashSet ConstructionRules { get; private set; } = new(); + + /// + /// The collision mask used for determining whether the entity prototype will fit into a target tile + /// + [DataField, ViewVariables(VVAccess.ReadOnly)] + public CollisionGroup CollisionMask { get; private set; } = CollisionGroup.None; + + /// + /// Specifies a set of custom collision bounds for determining whether the entity prototype will fit into a target tile + /// + /// + /// Should be set assuming that the entity faces south. + /// Make sure that Rotation is set to RcdRotation.User if the entity is to be rotated by the user + /// + [DataField, ViewVariables(VVAccess.ReadOnly)] + public Box2? CollisionBounds + { + get + { + return _collisionBounds; + } + + private set + { + _collisionBounds = value; + + if (_collisionBounds != null) + { + var poly = new PolygonShape(); + poly.SetAsBox(_collisionBounds.Value); + + CollisionPolygon = poly; + } + } + } + + private Box2? _collisionBounds = null; + + /// + /// The polygon shape associated with the prototype CollisionBounds (if set) + /// + [ViewVariables(VVAccess.ReadOnly)] + public PolygonShape? CollisionPolygon { get; private set; } = null; + + /// + /// Governs how the local rotation of the constructed entity will be set + /// + [DataField, ViewVariables(VVAccess.ReadOnly)] + public RcdRotation Rotation { get; private set; } = RcdRotation.User; +} + +public enum RcdMode : byte +{ + Invalid, + Deconstruct, + ConstructTile, + ConstructObject, +} + +// These are to be replaced with more flexible 'RulesRule' at a later time +public enum RcdConstructionRule : byte +{ + MustBuildOnEmptyTile, // Can only be built on empty space (e.g. lattice) + CanBuildOnEmptyTile, // Can be built on empty space or replace an existing tile (e.g. hull plating) + MustBuildOnSubfloor, // Can only be built on exposed subfloor (e.g. catwalks on lattice or hull plating) + IsWindow, // The entity is a window and can be built on grilles + IsCatwalk, // The entity is a catwalk +} + +public enum RcdRotation : byte +{ + Fixed, // The entity has a local rotation of zero + Camera, // The rotation of the entity matches the local player camera + User, // The entity can be rotated by the local player prior to placement +} diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index 6282a117bb3..cd1e90dc1ff 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -1,28 +1,35 @@ using Content.Shared.Administration.Logs; using Content.Shared.Charges.Components; using Content.Shared.Charges.Systems; +using Content.Shared.Construction; using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Examine; +using Content.Shared.Hands.Components; using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; using Content.Shared.Maps; using Content.Shared.Physics; using Content.Shared.Popups; using Content.Shared.RCD.Components; using Content.Shared.Tag; using Content.Shared.Tiles; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Network; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Timing; +using System.Diagnostics.CodeAnalysis; +using System.Linq; namespace Content.Shared.RCD.Systems; -public sealed class RCDSystem : EntitySystem +[Virtual] +public class RCDSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly INetManager _net = default!; @@ -34,312 +41,599 @@ public sealed class RCDSystem : EntitySystem [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly TurfSystem _turf = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly TagSystem _tags = default!; - private readonly int _rcdModeCount = Enum.GetValues(typeof(RcdMode)).Length; + private readonly int _instantConstructionDelay = 0; + private readonly EntProtoId _instantConstructionFx = "EffectRCDConstruct0"; + private readonly ProtoId _deconstructTileProto = "DeconstructTile"; + private readonly ProtoId _deconstructLatticeProto = "DeconstructLattice"; + + private HashSet _intersectingEntities = new(); public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnExamine); - SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnDoAfter); SubscribeLocalEvent>(OnDoAfterAttempt); + SubscribeLocalEvent(OnRCDSystemMessage); + SubscribeNetworkEvent(OnRCDconstructionGhostRotationEvent); } - private void OnExamine(EntityUid uid, RCDComponent comp, ExaminedEvent args) + #region Event handling + + private void OnMapInit(EntityUid uid, RCDComponent component, MapInitEvent args) { - if (!args.IsInDetailsRange) + // On init, set the RCD to its first available recipe + if (component.AvailablePrototypes.Any()) + { + component.ProtoId = component.AvailablePrototypes.First(); + UpdateCachedPrototype(uid, component); + Dirty(uid, component); + return; + } - var msg = Loc.GetString("rcd-component-examine-detail", ("mode", comp.Mode)); - args.PushMarkup(msg); + // The RCD has no valid recipes somehow? Get rid of it + QueueDel(uid); } - private void OnUseInHand(EntityUid uid, RCDComponent comp, UseInHandEvent args) + private void OnRCDSystemMessage(EntityUid uid, RCDComponent component, RCDSystemMessage args) { - if (args.Handled) + // Exit if the RCD doesn't actually know the supplied prototype + if (!component.AvailablePrototypes.Contains(args.ProtoId)) return; - NextMode(uid, comp, args.User); - args.Handled = true; + if (!_protoManager.HasIndex(args.ProtoId)) + return; + + // Set the current RCD prototype to the one supplied + component.ProtoId = args.ProtoId; + UpdateCachedPrototype(uid, component); + Dirty(uid, component); + + if (args.Session.AttachedEntity != null) + { + // Popup message + var msg = (component.CachedPrototype.Prototype != null) ? + Loc.GetString("rcd-component-change-build-mode", ("name", Loc.GetString(component.CachedPrototype.SetName))) : + Loc.GetString("rcd-component-change-mode", ("mode", Loc.GetString(component.CachedPrototype.SetName))); + + _popup.PopupClient(msg, uid, args.Session.AttachedEntity.Value); + } } - private void OnAfterInteract(EntityUid uid, RCDComponent comp, AfterInteractEvent args) + private void OnExamine(EntityUid uid, RCDComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + // Update cached prototype if required + UpdateCachedPrototype(uid, component); + + var msg = (component.CachedPrototype.Prototype != null) ? + Loc.GetString("rcd-component-examine-build-details", ("name", Loc.GetString(component.CachedPrototype.SetName))) : + Loc.GetString("rcd-component-examine-mode-details", ("mode", Loc.GetString(component.CachedPrototype.SetName))); + + args.PushMarkup(msg); + } + + private void OnAfterInteract(EntityUid uid, RCDComponent component, AfterInteractEvent args) { if (args.Handled || !args.CanReach) return; var user = args.User; + var location = args.ClickLocation; - TryComp(uid, out var charges); - if (_charges.IsEmpty(uid, charges)) + // Initial validity checks + if (!location.IsValid(EntityManager)) + return; + + if (!TryGetMapGridData(location, out var mapGridData)) { - _popup.PopupClient(Loc.GetString("rcd-component-no-ammo-message"), uid, user); + _popup.PopupClient(Loc.GetString("rcd-component-no-valid-grid"), uid, user); return; } - var location = args.ClickLocation; - // Initial validity check - if (!location.IsValid(EntityManager)) + if (!IsRCDOperationStillValid(uid, component, mapGridData.Value, args.Target, args.User)) return; - var gridId = location.GetGridUid(EntityManager); - if (!HasComp(gridId)) + if (!_net.IsServer) + return; + + // Get the starting cost, delay, and effect from the prototype + var cost = component.CachedPrototype.Cost; + var delay = component.CachedPrototype.Delay; + var effectPrototype = component.CachedPrototype.Effect; + + #region: Operation modifiers + + // Deconstruction modifiers + switch (component.CachedPrototype.Mode) { - location = location.AlignWithClosestGridTile(); - gridId = location.GetGridUid(EntityManager); - // Check if fixing it failed / get final grid ID - if (!HasComp(gridId)) - return; + case RcdMode.Deconstruct: + + // Deconstructing an object + if (args.Target != null) + { + if (TryComp(args.Target, out var destructible)) + { + cost = destructible.Cost; + delay = destructible.Delay; + effectPrototype = destructible.Effect; + } + } + + // Deconstructing a tile + else + { + var deconstructedTile = _mapSystem.GetTileRef(mapGridData.Value.GridUid, mapGridData.Value.Component, mapGridData.Value.Location); + var protoName = deconstructedTile.IsSpace() ? _deconstructTileProto : _deconstructLatticeProto; + + if (_protoManager.TryIndex(protoName, out var deconProto)) + { + cost = deconProto.Cost; + delay = deconProto.Delay; + effectPrototype = deconProto.Effect; + } + } + + break; + + case RcdMode.ConstructTile: + + // If replacing a tile, make the construction instant + var contructedTile = _mapSystem.GetTileRef(mapGridData.Value.GridUid, mapGridData.Value.Component, mapGridData.Value.Location); + + if (!contructedTile.Tile.IsEmpty) + { + delay = _instantConstructionDelay; + effectPrototype = _instantConstructionFx; + } + + break; } - var doAfterArgs = new DoAfterArgs(EntityManager, user, comp.Delay, new RCDDoAfterEvent(GetNetCoordinates(location), comp.Mode), uid, target: args.Target, used: uid) + #endregion + + // Try to start the do after + var effect = Spawn(effectPrototype, mapGridData.Value.Location); + var ev = new RCDDoAfterEvent(GetNetCoordinates(mapGridData.Value.Location), component.ProtoId, cost, EntityManager.GetNetEntity(effect)); + + var doAfterArgs = new DoAfterArgs(EntityManager, user, delay, ev, uid, target: args.Target, used: uid) { BreakOnDamage = true, - NeedHand = true, BreakOnHandChange = true, BreakOnMove = true, - AttemptFrequency = AttemptFrequency.EveryTick + AttemptFrequency = AttemptFrequency.EveryTick, + CancelDuplicate = false, + BlockDuplicate = false }; args.Handled = true; - if (_doAfter.TryStartDoAfter(doAfterArgs) && _gameTiming.IsFirstTimePredicted) - Spawn("EffectRCDConstruction", location); + if (!_doAfter.TryStartDoAfter(doAfterArgs)) + QueueDel(effect); } - private void OnDoAfterAttempt(EntityUid uid, RCDComponent comp, DoAfterAttemptEvent args) + private void OnDoAfterAttempt(EntityUid uid, RCDComponent component, DoAfterAttemptEvent args) { - // sus client crash why if (args.Event?.DoAfter?.Args == null) return; - var location = GetCoordinates(args.Event.Location); + // Exit if the RCD prototype has changed + if (component.ProtoId != args.Event.StartingProtoId) + return; - var gridId = location.GetGridUid(EntityManager); - if (!HasComp(gridId)) - { - location = location.AlignWithClosestGridTile(); - gridId = location.GetGridUid(EntityManager); - // Check if fixing it failed / get final grid ID - if (!HasComp(gridId)) - return; - } + // Ensure the RCD operation is still valid + var location = GetCoordinates(args.Event.Location); - var mapGrid = Comp(gridId.Value); - var tile = mapGrid.GetTileRef(location); + if (!TryGetMapGridData(location, out var mapGridData)) + return; - if (!IsRCDStillValid(uid, comp, args.Event.User, args.Event.Target, mapGrid, tile, args.Event.StartingMode)) + if (!IsRCDOperationStillValid(uid, component, mapGridData.Value, args.Event.Target, args.Event.User)) args.Cancel(); } - private void OnDoAfter(EntityUid uid, RCDComponent comp, RCDDoAfterEvent args) + private void OnDoAfter(EntityUid uid, RCDComponent component, RCDDoAfterEvent args) { + if (args.Cancelled && _net.IsServer) + QueueDel(EntityManager.GetEntity(args.Effect)); + if (args.Handled || args.Cancelled || !_timing.IsFirstTimePredicted) return; - var user = args.User; + args.Handled = true; + var location = GetCoordinates(args.Location); - var gridId = location.GetGridUid(EntityManager); - if (!HasComp(gridId)) + if (!TryGetMapGridData(location, out var mapGridData)) + return; + + // Ensure the RCD operation is still valid + if (!IsRCDOperationStillValid(uid, component, mapGridData.Value, args.Target, args.User)) + return; + + // Finalize the operation + FinalizeRCDOperation(uid, component, mapGridData.Value, args.Target, args.User); + + // Play audio and consume charges + _audio.PlayPredicted(component.SuccessSound, uid, args.User); + _charges.UseCharges(uid, args.Cost); + } + + private void OnRCDconstructionGhostRotationEvent(RCDConstructionGhostRotationEvent ev, EntitySessionEventArgs session) + { + var uid = GetEntity(ev.NetEntity); + + // Determine if player that send the message is carrying the specified RCD in their active hand + if (session.SenderSession.AttachedEntity == null) + return; + + if (!TryComp(session.SenderSession.AttachedEntity, out var hands) || + uid != hands.ActiveHand?.HeldEntity) + return; + + if (!TryComp(uid, out var rcd)) + return; + + // Update the construction direction + rcd.ConstructionDirection = ev.Direction; + Dirty(uid, rcd); + } + + #endregion + + #region Entity construction/deconstruction rule checks + + public bool IsRCDOperationStillValid(EntityUid uid, RCDComponent component, MapGridData mapGridData, EntityUid? target, EntityUid user, bool popMsgs = true) + { + // Update cached prototype if required + UpdateCachedPrototype(uid, component); + + // Check that the RCD has enough ammo to get the job done + TryComp(uid, out var charges); + + // Both of these were messages were suppose to be predicted, but HasInsufficientCharges wasn't being checked on the client for some reason? + if (_charges.IsEmpty(uid, charges)) { - location = location.AlignWithClosestGridTile(); - gridId = location.GetGridUid(EntityManager); - // Check if fixing it failed / get final grid ID - if (!HasComp(gridId)) - return; + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-no-ammo-message"), uid, user); + + return false; } - var mapGrid = Comp(gridId.Value); - var tile = mapGrid.GetTileRef(location); - var snapPos = mapGrid.TileIndicesFor(location); + if (_charges.HasInsufficientCharges(uid, component.CachedPrototype.Cost, charges)) + { + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-insufficient-ammo-message"), uid, user); - // I love that this uses entirely separate code to construction and tile placement!!! + return false; + } - switch (comp.Mode) - { - //Floor mode just needs the tile to be a space tile (subFloor) - case RcdMode.Floors: - if (!_floors.CanPlaceTile(gridId.Value, mapGrid, out var reason)) - { - _popup.PopupClient(reason, user, user); - return; - } + // Exit if the target / target location is obstructed + var unobstructed = (target == null) + ? _interaction.InRangeUnobstructed(user, _mapSystem.GridTileToWorld(mapGridData.GridUid, mapGridData.Component, mapGridData.Position), popup: popMsgs) + : _interaction.InRangeUnobstructed(user, target.Value, popup: popMsgs); - mapGrid.SetTile(snapPos, new Tile(_tileDefMan[comp.Floor].TileId)); - _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to set grid: {tile.GridUid} {snapPos} to {comp.Floor}"); - break; - //We don't want to place a space tile on something that's already a space tile. Let's do the inverse of the last check. - case RcdMode.Deconstruct: - if (!IsTileBlocked(tile)) // Delete the turf - { - mapGrid.SetTile(snapPos, Tile.Empty); - _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to set grid: {tile.GridUid} tile: {snapPos} to space"); - } - else // Delete the targeted thing - { - var target = args.Target!.Value; - _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to delete {ToPrettyString(target):target}"); - QueueDel(target); - } - break; - //Walls are a special behaviour, and require us to build a new object with a transform rather than setting a grid tile, - // thus we early return to avoid the tile set code. - case RcdMode.Walls: - // only spawn on the server - if (_net.IsServer) - { - var ent = Spawn("WallSolid", mapGrid.GridTileToLocal(snapPos)); - Transform(ent).LocalRotation = Angle.Zero; // Walls always need to point south. - _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to spawn {ToPrettyString(ent)} at {snapPos} on grid {tile.GridUid}"); - } - break; - case RcdMode.Airlock: - // only spawn on the server - if (_net.IsServer) - { - var airlock = Spawn("Airlock", mapGrid.GridTileToLocal(snapPos)); - Transform(airlock).LocalRotation = Transform(uid).LocalRotation; //Now apply icon smoothing. - _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to spawn {ToPrettyString(airlock)} at {snapPos} on grid {tile.GridUid}"); - } - break; - default: - args.Handled = true; - return; //I don't know why this would happen, but sure I guess. Get out of here invalid state! + if (!unobstructed) + return false; + + // Return whether the operation location is valid + switch (component.CachedPrototype.Mode) + { + case RcdMode.ConstructTile: return IsConstructionLocationValid(uid, component, mapGridData, user, popMsgs); + case RcdMode.ConstructObject: return IsConstructionLocationValid(uid, component, mapGridData, user, popMsgs); + case RcdMode.Deconstruct: return IsDeconstructionStillValid(uid, component, mapGridData, target, user, popMsgs); } - _audio.PlayPredicted(comp.SuccessSound, uid, user); - _charges.UseCharge(uid); - args.Handled = true; + return false; } - private bool IsRCDStillValid(EntityUid uid, RCDComponent comp, EntityUid user, EntityUid? target, MapGridComponent mapGrid, TileRef tile, RcdMode startingMode) + private bool IsConstructionLocationValid(EntityUid uid, RCDComponent component, MapGridData mapGridData, EntityUid user, bool popMsgs = true) { - //Less expensive checks first. Failing those ones, we need to check that the tile isn't obstructed. - if (comp.Mode != startingMode) + // Check rule: Must build on empty tile + if (component.CachedPrototype.ConstructionRules.Contains(RcdConstructionRule.MustBuildOnEmptyTile) && !mapGridData.Tile.Tile.IsEmpty) + { + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-must-build-on-empty-tile-message"), uid, user); + return false; + } - var unobstructed = target == null - ? _interaction.InRangeUnobstructed(user, mapGrid.GridTileToWorld(tile.GridIndices), popup: true) - : _interaction.InRangeUnobstructed(user, target.Value, popup: true); + // Check rule: Must build on non-empty tile + if (!component.CachedPrototype.ConstructionRules.Contains(RcdConstructionRule.CanBuildOnEmptyTile) && mapGridData.Tile.Tile.IsEmpty) + { + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-cannot-build-on-empty-tile-message"), uid, user); - if (!unobstructed) return false; + } + + // Check rule: Must place on subfloor + if (component.CachedPrototype.ConstructionRules.Contains(RcdConstructionRule.MustBuildOnSubfloor) && !mapGridData.Tile.Tile.GetContentTileDefinition().IsSubFloor) + { + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-must-build-on-subfloor-message"), uid, user); + + return false; + } + + // Tile specific rules + if (component.CachedPrototype.Mode == RcdMode.ConstructTile) + { + // Check rule: Tile placement is valid + if (!_floors.CanPlaceTile(mapGridData.GridUid, mapGridData.Component, out var reason)) + { + if (popMsgs) + _popup.PopupClient(reason, uid, user); + + return false; + } + + // Check rule: Tiles can't be identical + if (mapGridData.Tile.Tile.GetContentTileDefinition().ID == component.CachedPrototype.Prototype) + { + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-cannot-build-identical-tile"), uid, user); + + return false; + } + + // Ensure that all construction rules shared between tiles and object are checked before exiting here + return true; + } + + // Entity specific rules + + // Check rule: The tile is unoccupied + var isWindow = component.CachedPrototype.ConstructionRules.Contains(RcdConstructionRule.IsWindow); + var isCatwalk = component.CachedPrototype.ConstructionRules.Contains(RcdConstructionRule.IsCatwalk); - switch (comp.Mode) + _intersectingEntities.Clear(); + _lookup.GetLocalEntitiesIntersecting(mapGridData.GridUid, mapGridData.Position, _intersectingEntities, -0.05f, LookupFlags.Uncontained); + + foreach (var ent in _intersectingEntities) { - //Floor mode just needs the tile to be a space tile (subFloor) - case RcdMode.Floors: - if (!tile.Tile.IsEmpty) + if (isWindow && HasComp(ent)) + continue; + + if (isCatwalk && _tags.HasTag(ent, "Catwalk")) + { + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-cannot-build-on-occupied-tile-message"), uid, user); + + return false; + } + + if (component.CachedPrototype.CollisionMask != CollisionGroup.None && TryComp(ent, out var fixtures)) + { + foreach (var fixture in fixtures.Fixtures.Values) { - _popup.PopupClient(Loc.GetString("rcd-component-cannot-build-floor-tile-not-empty-message"), uid, user); + // Continue if no collision is possible + if (fixture.CollisionLayer <= 0 || (fixture.CollisionLayer & (int) component.CachedPrototype.CollisionMask) == 0) + continue; + + // Continue if our custom collision bounds are not intersected + if (component.CachedPrototype.CollisionPolygon != null && + !DoesCustomBoundsIntersectWithFixture(component.CachedPrototype.CollisionPolygon, component.ConstructionTransform, ent, fixture)) + continue; + + // Collision was detected + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-cannot-build-on-occupied-tile-message"), uid, user); + return false; } + } + } - return true; - //We don't want to place a space tile on something that's already a space tile. Let's do the inverse of the last check. - case RcdMode.Deconstruct: - if (tile.Tile.IsEmpty) - return false; + return true; + } - //They tried to decon a turf but... - if (target == null) - { - // the turf is blocked - if (IsTileBlocked(tile)) - { - _popup.PopupClient(Loc.GetString("rcd-component-tile-obstructed-message"), uid, user); - return false; - } - // the turf can't be destroyed (planet probably) - var tileDef = (ContentTileDefinition) _tileDefMan[tile.Tile.TypeId]; - if (tileDef.Indestructible) - { - _popup.PopupClient(Loc.GetString("rcd-component-tile-indestructible-message"), uid, user); - return false; - } - } - //They tried to decon a non-turf but it's not in the whitelist - else if (!_tag.HasTag(target.Value, "RCDDeconstructWhitelist")) - { + private bool IsDeconstructionStillValid(EntityUid uid, RCDComponent component, MapGridData mapGridData, EntityUid? target, EntityUid user, bool popMsgs = true) + { + // Attempt to deconstruct a floor tile + if (target == null) + { + // The tile is empty + if (mapGridData.Tile.Tile.IsEmpty) + { + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-nothing-to-deconstruct-message"), uid, user); + + return false; + } + + // The tile has a structure sitting on it + if (_turf.IsTileBlocked(mapGridData.Tile, CollisionGroup.MobMask)) + { + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-tile-obstructed-message"), uid, user); + + return false; + } + + // The tile cannot be destroyed + var tileDef = (ContentTileDefinition) _tileDefMan[mapGridData.Tile.Tile.TypeId]; + + if (tileDef.Indestructible) + { + if (popMsgs) + _popup.PopupClient(Loc.GetString("rcd-component-tile-indestructible-message"), uid, user); + + return false; + } + } + + // Attempt to deconstruct an object + else + { + // The object is not in the whitelist + if (!TryComp(target, out var deconstructible) || !deconstructible.Deconstructable) + { + if (popMsgs) _popup.PopupClient(Loc.GetString("rcd-component-deconstruct-target-not-on-whitelist-message"), uid, user); - return false; - } - return true; - //Walls are a special behaviour, and require us to build a new object with a transform rather than setting a grid tile, thus we early return to avoid the tile set code. - case RcdMode.Walls: - if (tile.Tile.IsEmpty) - { - _popup.PopupClient(Loc.GetString("rcd-component-cannot-build-wall-tile-not-empty-message"), uid, user); - return false; - } + return false; + } + } + + return true; + } + + #endregion + + #region Entity construction/deconstruction + + private void FinalizeRCDOperation(EntityUid uid, RCDComponent component, MapGridData mapGridData, EntityUid? target, EntityUid user) + { + if (!_net.IsServer) + return; + + if (component.CachedPrototype.Prototype == null) + return; - if (IsTileBlocked(tile)) + switch (component.CachedPrototype.Mode) + { + case RcdMode.ConstructTile: + _mapSystem.SetTile(mapGridData.GridUid, mapGridData.Component, mapGridData.Position, new Tile(_tileDefMan[component.CachedPrototype.Prototype].TileId)); + _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(user):user} used RCD to set grid: {mapGridData.GridUid} {mapGridData.Position} to {component.CachedPrototype.Prototype}"); + break; + + case RcdMode.ConstructObject: + var ent = Spawn(component.CachedPrototype.Prototype, _mapSystem.GridTileToLocal(mapGridData.GridUid, mapGridData.Component, mapGridData.Position)); + + switch (component.CachedPrototype.Rotation) { - _popup.PopupClient(Loc.GetString("rcd-component-tile-obstructed-message"), uid, user); - return false; + case RcdRotation.Fixed: + Transform(ent).LocalRotation = Angle.Zero; + break; + case RcdRotation.Camera: + Transform(ent).LocalRotation = Transform(uid).LocalRotation; + break; + case RcdRotation.User: + Transform(ent).LocalRotation = component.ConstructionDirection.ToAngle(); + break; } - return true; - case RcdMode.Airlock: - if (tile.Tile.IsEmpty) + + _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(user):user} used RCD to spawn {ToPrettyString(ent)} at {mapGridData.Position} on grid {mapGridData.GridUid}"); + break; + + case RcdMode.Deconstruct: + + if (target == null) { - _popup.PopupClient(Loc.GetString("rcd-component-cannot-build-airlock-tile-not-empty-message"), uid, user); - return false; + // Deconstruct tile (either converts the tile to lattice, or removes lattice) + var tile = (mapGridData.Tile.Tile.GetContentTileDefinition().ID != "Lattice") ? new Tile(_tileDefMan["Lattice"].TileId) : Tile.Empty; + _mapSystem.SetTile(mapGridData.GridUid, mapGridData.Component, mapGridData.Position, tile); + _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(user):user} used RCD to set grid: {mapGridData.GridUid} tile: {mapGridData.Position} open to space"); } - if (IsTileBlocked(tile)) + else { - _popup.PopupClient(Loc.GetString("rcd-component-tile-obstructed-message"), uid, user); - return false; + // Deconstruct object + _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(user):user} used RCD to delete {ToPrettyString(target):target}"); + QueueDel(target); } - return true; - default: - return false; //I don't know why this would happen, but sure I guess. Get out of here invalid state! + + break; } } - private void NextMode(EntityUid uid, RCDComponent comp, EntityUid user) + #endregion + + #region Utility functions + + public bool TryGetMapGridData(EntityCoordinates location, [NotNullWhen(true)] out MapGridData? mapGridData) { - _audio.PlayPredicted(comp.SwapModeSound, uid, user); + mapGridData = null; + var gridUid = location.GetGridUid(EntityManager); + + if (!TryComp(gridUid, out var mapGrid)) + { + location = location.AlignWithClosestGridTile(1.75f, EntityManager); + gridUid = location.GetGridUid(EntityManager); + + // Check if we got a grid ID the second time round + if (!TryComp(gridUid, out mapGrid)) + return false; + } + + gridUid = mapGrid.Owner; - var mode = (int) comp.Mode; - mode = ++mode % _rcdModeCount; - comp.Mode = (RcdMode) mode; - Dirty(uid, comp); + var tile = _mapSystem.GetTileRef(gridUid.Value, mapGrid, location); + var position = _mapSystem.TileIndicesFor(gridUid.Value, mapGrid, location); + mapGridData = new MapGridData(gridUid.Value, mapGrid, location, tile, position); - var msg = Loc.GetString("rcd-component-change-mode", ("mode", comp.Mode.ToString())); - _popup.PopupClient(msg, uid, user); + return true; } - private bool IsTileBlocked(TileRef tile) + private bool DoesCustomBoundsIntersectWithFixture(PolygonShape boundingPolygon, Transform boundingTransform, EntityUid fixtureOwner, Fixture fixture) { - return _turf.IsTileBlocked(tile, CollisionGroup.MobMask); + var entXformComp = Transform(fixtureOwner); + var entXform = new Transform(new(), entXformComp.LocalRotation); + + return boundingPolygon.ComputeAABB(boundingTransform, 0).Intersects(fixture.Shape.ComputeAABB(entXform, 0)); + } + + public void UpdateCachedPrototype(EntityUid uid, RCDComponent component) + { + if (component.ProtoId.Id != component.CachedPrototype?.Prototype) + component.CachedPrototype = _protoManager.Index(component.ProtoId); + } + + #endregion +} + +public struct MapGridData +{ + public EntityUid GridUid; + public MapGridComponent Component; + public EntityCoordinates Location; + public TileRef Tile; + public Vector2i Position; + + public MapGridData(EntityUid gridUid, MapGridComponent component, EntityCoordinates location, TileRef tile, Vector2i position) + { + GridUid = gridUid; + Component = component; + Location = location; + Tile = tile; + Position = position; } } [Serializable, NetSerializable] public sealed partial class RCDDoAfterEvent : DoAfterEvent { - [DataField("location", required: true)] - public NetCoordinates Location = default!; + [DataField(required: true)] + public NetCoordinates Location { get; private set; } = default!; - [DataField("startingMode", required: true)] - public RcdMode StartingMode = default!; + [DataField] + public ProtoId StartingProtoId { get; private set; } = default!; - private RCDDoAfterEvent() - { - } + [DataField] + public int Cost { get; private set; } = 1; + + [DataField("fx")] + public NetEntity? Effect { get; private set; } = null; + + private RCDDoAfterEvent() { } - public RCDDoAfterEvent(NetCoordinates location, RcdMode startingMode) + public RCDDoAfterEvent(NetCoordinates location, ProtoId startingProtoId, int cost, NetEntity? effect = null) { Location = location; - StartingMode = startingMode; + StartingProtoId = startingProtoId; + Cost = cost; + Effect = effect; } public override DoAfterEvent Clone() => this; diff --git a/Resources/Locale/en-US/rcd/components/rcd-component.ftl b/Resources/Locale/en-US/rcd/components/rcd-component.ftl index b7920c9edea..bb65e76f3f7 100644 --- a/Resources/Locale/en-US/rcd/components/rcd-component.ftl +++ b/Resources/Locale/en-US/rcd/components/rcd-component.ftl @@ -1,18 +1,66 @@ ### UI -# Shown when an RCD is examined in details range -rcd-component-examine-detail = It's currently on {$mode} mode. +rcd-component-examine-mode-details = It's currently set to '{$mode}' mode. +rcd-component-examine-build-details = It's currently set to build {MAKEPLURAL($name)}. + ### Interaction Messages -# Shown when changing RCD Mode -rcd-component-change-mode = The RCD is now set to {$mode} mode. +# Mode change +rcd-component-change-mode = The RCD is now set to '{$mode}' mode. +rcd-component-change-build-mode = The RCD is now set to build {MAKEPLURAL($name)}. + +# Ammo count +rcd-component-no-ammo-message = The RCD has run out of charges! +rcd-component-insufficient-ammo-message = The RCD doesn't have enough charges left! -rcd-component-no-ammo-message = The RCD is out of ammo! -rcd-component-tile-obstructed-message = That tile is obstructed! -rcd-component-tile-indestructible-message = That tile can't be destroyed! +# Deconstruction +rcd-component-tile-indestructible-message = That tile can't be destructed! rcd-component-deconstruct-target-not-on-whitelist-message = You can't deconstruct that! -rcd-component-cannot-build-floor-tile-not-empty-message = You can only build a floor on space! -rcd-component-cannot-build-wall-tile-not-empty-message = You cannot build a wall on space! -rcd-component-cannot-build-airlock-tile-not-empty-message = Cannot build an airlock on space! +rcd-component-nothing-to-deconstruct-message = There's nothing to deconstruct! +rcd-component-tile-obstructed-message = You can't deconstruct tiles when there's something on top of them! + +# Construction +rcd-component-no-valid-grid = You're too far into open space to build here! +rcd-component-must-build-on-empty-tile-message = A foundation already exists here! +rcd-component-cannot-build-on-empty-tile-message = You can't build that without a foundation! +rcd-component-must-build-on-subfloor-message = You can only build that on exposed subfloor! +rcd-component-cannot-build-on-subfloor-message = You can't build that on exposed subfloor! +rcd-component-cannot-build-on-occupied-tile-message = You can't build here, the space is already occupied! +rcd-component-cannot-build-identical-tile = That tile already exists there! + + +### Category names + +rcd-component-walls-and-flooring = Walls and flooring +rcd-component-windows-and-grilles = Windows and grilles +rcd-component-airlocks = Airlocks +rcd-component-electrical = Electrical +rcd-component-lighting = Lighting + + +### Prototype names (note: constructable items will be puralized) + +rcd-component-deconstruct = deconstruct +rcd-component-wall-solid = solid wall +rcd-component-floor-steel = steel tile +rcd-component-plating = hull plate +rcd-component-catwalk = catwalk +rcd-component-wall-reinforced = reinforced wall +rcd-component-grille = grille +rcd-component-window = window +rcd-component-window-directional = directional window +rcd-component-window-reinforced-directional = directional reinforced window +rcd-component-reinforced-window = reinforced window +rcd-component-airlock = standard airlock +rcd-component-airlock-glass = glass airlock +rcd-component-firelock = firelock +rcd-component-computer-frame = computer frame +rcd-component-machine-frame = machine frame +rcd-component-tube-light = light +rcd-component-window-bulb-light = small light +rcd-component-window-lv-cable = LV cable +rcd-component-window-mv-cable = MV cable +rcd-component-window-hv-cable = HV cable +rcd-component-window-cable-terminal = cable terminal diff --git a/Resources/Locale/en-US/ui/general.ftl b/Resources/Locale/en-US/ui/general.ftl new file mode 100644 index 00000000000..1471261dcb7 --- /dev/null +++ b/Resources/Locale/en-US/ui/general.ftl @@ -0,0 +1,3 @@ +### Loc for the various UI-related verbs +ui-verb-toggle-open = Toggle UI +verb-instrument-openui = Play Music diff --git a/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml b/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml index 668f3776dd1..03c870fa580 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml @@ -166,8 +166,8 @@ - type: entity id: CrateRCDAmmo parent: CrateEngineering - name: RCD ammo crate - description: 3 RCD ammo, each restoring 5 charges. + name: compressed matter crate + description: Contains three compressed matter cartridges. components: - type: StorageFill contents: @@ -178,7 +178,7 @@ id: CrateRCD parent: CrateEngineeringSecure name: RCD crate - description: A crate containing a single Rapid Construction Device. + description: A crate containing a single rapid construction device. components: - type: StorageFill contents: diff --git a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml index a8e28a1ef73..739464e9611 100644 --- a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml +++ b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml @@ -128,9 +128,10 @@ snap: - Wall components: - - type: Tag - tags: - - RCDDeconstructWhitelist + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 - type: Clickable - type: InteractionOutline - type: Sprite diff --git a/Resources/Prototypes/Entities/Effects/rcd.yml b/Resources/Prototypes/Entities/Effects/rcd.yml index adc6aa593c1..902429818e5 100644 --- a/Resources/Prototypes/Entities/Effects/rcd.yml +++ b/Resources/Prototypes/Entities/Effects/rcd.yml @@ -1,16 +1,115 @@ - type: entity - id: EffectRCDConstruction + id: EffectRCDBase + abstract: true noSpawn: true components: - type: Transform anchored: True - type: Sprite + snapCardinals: true + noRot: true drawdepth: Effects sprite: /Textures/Effects/rcd.rsi - state: construct - - type: TimedDespawn - lifetime: 3.2 + state: construct0 - type: Tag tags: - HideContextMenu - type: AnimationPlayer + +- type: entity + parent: EffectRCDBase + id: EffectRCDDeconstructPreview + noSpawn: true + components: + - type: Sprite + state: deconstructPreview + +- type: entity + parent: EffectRCDBase + id: EffectRCDConstruct0 + noSpawn: true + components: + - type: Sprite + state: construct0 + - type: TimedDespawn + lifetime: 1.2 + +- type: entity + parent: EffectRCDBase + id: EffectRCDConstruct1 + noSpawn: true + components: + - type: Sprite + state: construct1 + - type: TimedDespawn + lifetime: 2.2 + +- type: entity + parent: EffectRCDBase + id: EffectRCDConstruct2 + noSpawn: true + components: + - type: Sprite + state: construct2 + - type: TimedDespawn + lifetime: 3.2 + +- type: entity + parent: EffectRCDBase + id: EffectRCDConstruct3 + noSpawn: true + components: + - type: Sprite + state: construct3 + - type: TimedDespawn + lifetime: 4.2 + +- type: entity + parent: EffectRCDBase + id: EffectRCDConstruct4 + noSpawn: true + components: + - type: Sprite + state: construct4 + - type: TimedDespawn + lifetime: 5.2 + +- type: entity + parent: EffectRCDBase + id: EffectRCDDeconstruct2 + noSpawn: true + components: + - type: Sprite + state: deconstruct2 + - type: TimedDespawn + lifetime: 3.2 + +- type: entity + parent: EffectRCDBase + id: EffectRCDDeconstruct4 + noSpawn: true + components: + - type: Sprite + state: deconstruct4 + - type: TimedDespawn + lifetime: 5.2 + +- type: entity + parent: EffectRCDBase + id: EffectRCDDeconstruct6 + noSpawn: true + components: + - type: Sprite + state: deconstruct6 + - type: TimedDespawn + lifetime: 7.2 + +- type: entity + parent: EffectRCDBase + id: EffectRCDDeconstruct8 + noSpawn: true + components: + - type: Sprite + state: deconstruct8 + - type: TimedDespawn + lifetime: 9.2 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Markers/construction_ghost.yml b/Resources/Prototypes/Entities/Markers/construction_ghost.yml index d198ebdd51f..be9cc915d91 100644 --- a/Resources/Prototypes/Entities/Markers/construction_ghost.yml +++ b/Resources/Prototypes/Entities/Markers/construction_ghost.yml @@ -7,4 +7,4 @@ color: '#3F38' - type: ConstructionGhost - type: Clickable - - type: InteractionOutline + - type: InteractionOutline \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index b46eded7d84..2b11c211e8e 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -337,15 +337,35 @@ path: "/Audio/Items/drill_hit.ogg" - type: entity - name: RCD - parent: BaseItem id: RCD - description: An advanced construction device which can place/remove walls, floors, and airlocks quickly. + parent: BaseItem + name: RCD + description: The rapid construction device can be used to quickly place and remove various station structures and fixtures. Requires compressed matter to function. components: - type: RCD + availablePrototypes: + - WallSolid + - FloorSteel + - Plating + - Catwalk + - Grille + - Window + - WindowDirectional + - WindowReinforcedDirectional + - ReinforcedWindow + - Airlock + - AirlockGlass + - Firelock + - TubeLight + - BulbLight + - LVCable + - MVCable + - HVCable + - CableTerminal + - Deconstruct - type: LimitedCharges - maxCharges: 5 - charges: 5 + maxCharges: 30 + charges: 30 - type: UseDelay - type: Sprite sprite: Objects/Tools/rcd.rsi @@ -363,6 +383,12 @@ Plastic: 100 - type: StaticPrice price: 100 + - type: UserInterface + interfaces: + - key: enum.RcdUiKey.Key + type: RCDMenuBoundUserInterface + - type: ActivatableUI + key: enum.RcdUiKey.Key - type: entity id: RCDEmpty @@ -370,37 +396,50 @@ suffix: Empty components: - type: LimitedCharges - maxCharges: 5 charges: 0 + - type: RCD + availablePrototypes: + - WallSolid + - FloorSteel + - Plating + - Catwalk + - Grille + - Window + - WindowDirectional + - WindowReinforcedDirectional + - ReinforcedWindow + - Airlock + - AirlockGlass + - Firelock - type: entity id: RCDRecharging parent: RCD - name: experimental rcd - description: A bluespace-enhanced RCD that regenerates charges passively. + name: experimental RCD + description: A bluespace-enhanced rapid construction device that passively generates its own compressed matter. suffix: AutoRecharge components: - type: LimitedCharges - maxCharges: 3 - charges: 3 + maxCharges: 20 + charges: 20 - type: AutoRecharge - rechargeDuration: 30 + rechargeDuration: 10 - type: entity id: RCDExperimental parent: RCD suffix: Admeme - name: experimental rcd - description: A bluespace-enhanced RCD that regenerates charges passively. + name: experimental RCD + description: A bluespace-enhanced rapid construction device that passively generates its own compressed matter. components: - type: AutoRecharge - rechargeDuration: 5 + rechargeDuration: 1 - type: entity - name: RCD Ammo + name: compressed matter parent: BaseItem id: RCDAmmo - description: Ammo cartridge for an RCD. + description: A cartridge of raw matter compacted by bluespace technology. Used in rapid construction devices. components: - type: RCDAmmo - type: Sprite diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml index ce2b3261294..ff02e315cb2 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml @@ -138,48 +138,6 @@ sprite: Structures/Doors/Airlocks/Standard/hatch_maint.rsi # Glass - -- type: entity - id: AirlockGlass - parent: Airlock - name: glass airlock - components: - - type: MeleeSound - soundGroups: - Brute: - collection: GlassSmack - - type: Door - occludes: false - - type: Occluder - enabled: false - - type: Sprite - sprite: Structures/Doors/Airlocks/Glass/glass.rsi - - type: AnimationPlayer - - 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 - - type: LayerChangeOnWeld - unWeldedLayer: GlassAirlockLayer - weldedLayer: GlassLayer - - type: Construction - graph: Airlock - node: glassAirlock - - type: PaintableAirlock - group: Glass - - type: RadiationBlocker - resistance: 2 - - type: Tag - tags: - - GlassAirlock - # This tag is used to nagivate the Airlock construction graph. It's needed because the construction graph is shared between Airlock, AirlockGlass, and HighSecDoor - type: entity parent: AirlockGlass id: AirlockEngineeringGlass @@ -295,4 +253,4 @@ - type: Sprite sprite: Structures/Doors/Airlocks/Glass/centcomm.rsi - type: WiresPanelSecurity - securityLevel: medSecurity + securityLevel: medSecurity \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_assembly.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_assembly.yml index fcdb0d2deaf..283c9f22ae5 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_assembly.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_assembly.yml @@ -30,6 +30,10 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index 53a32e0f6fa..abc86b2be8e 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -111,6 +111,10 @@ - type: Damageable damageContainer: StructuralInorganic damageModifierSet: StrongMetallic + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -149,5 +153,53 @@ - type: BlockWeather placement: mode: SnapgridCenter + +- type: entity + id: AirlockRCDResistant + parent: Airlock + abstract: true + components: + - type: RCDDeconstructable + deconstructable: false - +- type: entity + id: AirlockGlass + parent: Airlock + name: glass airlock + components: + - type: MeleeSound + soundGroups: + Brute: + collection: GlassSmack + - type: Door + occludes: false + - type: Occluder + enabled: false + - type: Sprite + sprite: Structures/Doors/Airlocks/Glass/glass.rsi + - type: AnimationPlayer + - 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 + - type: LayerChangeOnWeld + unWeldedLayer: GlassAirlockLayer + weldedLayer: GlassLayer + - type: Construction + graph: Airlock + node: glassAirlock + - type: PaintableAirlock + group: Glass + - type: RadiationBlocker + resistance: 2 + - type: Tag + tags: + - GlassAirlock + # This tag is used to nagivate the Airlock construction graph. It's needed because the construction graph is shared between Airlock, AirlockGlass, and HighSecDoor \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml index 75b23f70719..293aaac273d 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml @@ -1,5 +1,5 @@ - type: entity - parent: Airlock + parent: AirlockRCDResistant id: AirlockExternal suffix: External description: It opens, it closes, it might crush you, and there might be only space behind it. diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml index 9771f633888..5d6b1088f12 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml @@ -1,5 +1,5 @@ - type: entity - parent: Airlock + parent: AirlockRCDResistant id: AirlockShuttle suffix: Docking name: external airlock diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml index e677ef185be..0dd65ab4d07 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml @@ -19,6 +19,10 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic + - type: RCDDeconstructable + cost: 4 + delay: 6 + fx: EffectRCDDeconstruct6 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/frame.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/frame.yml index 8cf75e89e1a..3f4306e4aa1 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/frame.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/frame.yml @@ -25,6 +25,10 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic + - type: RCDDeconstructable + cost: 4 + delay: 6 + fx: EffectRCDDeconstruct6 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml b/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml index 8dfe2f62a51..b8fb203b517 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml @@ -43,6 +43,10 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic + - type: RCDDeconstructable + cost: 6 + delay: 6 + fx: EffectRCDDeconstruct6 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/SecretDoor/secret_door.yml b/Resources/Prototypes/Entities/Structures/Doors/SecretDoor/secret_door.yml index d6c087af0a5..06e9d2219a5 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/SecretDoor/secret_door.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/SecretDoor/secret_door.yml @@ -41,6 +41,10 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -97,6 +101,10 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml index 8d9cedac03a..5d47d9c5c4a 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml @@ -26,6 +26,10 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml index d03765d4fc9..d58273edcc9 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml @@ -66,7 +66,11 @@ damageContainer: Inorganic damageModifierSet: Glass - type: ExaminableDamage - messages: WindowMessages + messages: WindowMessages + - type: RCDDeconstructable + cost: 8 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml index b4b198eb774..ef89088d1ab 100644 --- a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml +++ b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml @@ -40,6 +40,10 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic + - type: RCDDeconstructable + cost: 4 + delay: 2 + fx: EffectRCDDeconstruct2 - type: Destructible thresholds: - trigger: @@ -70,7 +74,7 @@ mode: SnapgridCenter snap: - Wallmount - + - type: entity name: light description: "A light fixture. Draws power and produces light when equipped with a light tube." diff --git a/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml b/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml index da724014dc4..2e8f047c214 100644 --- a/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml +++ b/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml @@ -17,6 +17,10 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Power/cables.yml b/Resources/Prototypes/Entities/Structures/Power/cables.yml index de66dfc66d6..a81c89de0fb 100644 --- a/Resources/Prototypes/Entities/Structures/Power/cables.yml +++ b/Resources/Prototypes/Entities/Structures/Power/cables.yml @@ -43,6 +43,10 @@ lowVoltageNode: power - type: CableVis node: power + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 - type: entity parent: CableBase diff --git a/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml b/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml index 88d2f272c08..1dca59225cb 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml @@ -10,9 +10,6 @@ Brute: path: "/Audio/Weapons/grille_hit.ogg" - - type: Tag - tags: - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/fence.rsi drawdepth: WallTops @@ -78,6 +75,10 @@ True: { visible: True } False: { visible: False } - type: AnimationPlayer + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 - type: entity parent: BaseFenceMetal diff --git a/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml b/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml index f2b03aaeb8f..55b7e40803b 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml @@ -12,7 +12,6 @@ "/Audio/Weapons/boxingpunch1.ogg" - type: Tag tags: - - RCDDeconstructWhitelist - Wooden - type: Sprite sprite: Structures/Walls/wooden_fence.rsi @@ -24,6 +23,10 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: Wood + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Walls/grille.yml b/Resources/Prototypes/Entities/Structures/Walls/grille.yml index b532db221da..11ada142fa5 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/grille.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/grille.yml @@ -9,9 +9,10 @@ Brute: path: "/Audio/Weapons/grille_hit.ogg" - - type: Tag - tags: - - RCDDeconstructWhitelist + - type: RCDDeconstructable + cost: 6 + delay: 4 + fx: EffectRCDDeconstruct4 - type: CanBuildWindowOnTop - type: Sprite drawdepth: Walls @@ -120,9 +121,10 @@ - type: Icon sprite: Structures/Walls/grille.rsi state: grille_broken - - type: Tag - tags: - - RCDDeconstructWhitelist + - type: RCDDeconstructable + cost: 6 + delay: 4 + fx: EffectRCDDeconstruct4 - type: Construction graph: Grille node: grilleBroken diff --git a/Resources/Prototypes/Entities/Structures/Walls/railing.yml b/Resources/Prototypes/Entities/Structures/Walls/railing.yml index 95d16742d58..a23c559abab 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/railing.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/railing.yml @@ -57,6 +57,10 @@ - type: Construction graph: Railing node: railing + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 - type: entity parent: BaseStructure @@ -126,6 +130,10 @@ - type: Construction graph: Railing node: railingCorner + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 - type: entity parent: BaseStructure @@ -186,7 +194,11 @@ - type: Construction graph: Railing node: railingCornerSmall - + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 + - type: entity parent: BaseStructure id: RailingRound @@ -261,3 +273,7 @@ - type: Construction graph: Railing node: railingRound + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml index f06c0fc4244..8eca58b1242 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml @@ -59,11 +59,14 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/brick.rsi - type: Icon sprite: Structures/Walls/brick.rsi + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -90,7 +93,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/clock.rsi - type: Icon @@ -123,7 +125,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/clown.rsi - type: Icon @@ -131,6 +132,10 @@ - type: Construction graph: Girder node: bananiumWall + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -156,7 +161,6 @@ components: - type: Tag tags: - - RCDDeconstructWhitelist - Wall - Structure - type: Sprite @@ -190,7 +194,6 @@ components: - type: Tag tags: - - RCDDeconstructWhitelist - Wall - type: Sprite sprite: Structures/Walls/cult.rsi @@ -223,7 +226,6 @@ tags: - Wall - Debug - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/debug.rsi - type: Icon @@ -253,11 +255,14 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/diamond.rsi - type: Icon sprite: Structures/Walls/diamond.rsi + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -283,7 +288,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/gold.rsi - type: Icon @@ -291,6 +295,10 @@ - type: Construction graph: Girder node: goldWall + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -325,7 +333,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/ice.rsi - type: Icon @@ -355,7 +362,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/plasma.rsi - type: Icon @@ -363,6 +369,10 @@ - type: Construction graph: Girder node: plasmaWall + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -399,7 +409,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/plastic.rsi - type: Icon @@ -407,6 +416,10 @@ - type: Construction graph: Girder node: plasticWall + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -459,7 +472,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Destructible thresholds: - trigger: @@ -604,7 +616,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/riveted.rsi - type: Icon @@ -639,11 +650,14 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/sandstone.rsi - type: Icon sprite: Structures/Walls/sandstone.rsi + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -669,7 +683,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/silver.rsi - type: Icon @@ -677,6 +690,10 @@ - type: Construction graph: Girder node: silverWall + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -842,7 +859,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/solid.rsi - type: WallReplacementMarker @@ -851,6 +867,10 @@ node: wall - type: Icon sprite: Structures/Walls/solid.rsi + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: @@ -1012,7 +1032,6 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/web.rsi - type: Icon @@ -1224,11 +1243,14 @@ - type: Tag tags: - Wall - - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/cobblebrick.rsi - type: Icon sprite: Structures/Walls/cobblebrick.rsi + - type: RCDDeconstructable + cost: 6 + delay: 8 + fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Windows/mining.yml b/Resources/Prototypes/Entities/Structures/Windows/mining.yml index 910c3daae2a..82d11b732b6 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/mining.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/mining.yml @@ -1,7 +1,7 @@ - type: entity id: MiningWindow name: mining window - parent: Window + parent: WindowRCDResistant components: - type: Sprite drawdepth: WallTops diff --git a/Resources/Prototypes/Entities/Structures/Windows/plasma.yml b/Resources/Prototypes/Entities/Structures/Windows/plasma.yml index 0dd2a1b06cc..36a12f2d844 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/plasma.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/plasma.yml @@ -1,7 +1,7 @@ - type: entity id: PlasmaWindow name: plasma window - parent: Window + parent: WindowRCDResistant components: - type: Sprite drawdepth: WallTops @@ -55,7 +55,7 @@ - type: entity id: PlasmaWindowDirectional - parent: WindowDirectional + parent: WindowDirectionalRCDResistant name: directional plasma window description: Don't smudge up the glass down there. placement: diff --git a/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml b/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml index 1c79644ce4d..d8b6c7d11d8 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml @@ -14,6 +14,10 @@ - type: Damageable damageContainer: StructuralInorganic damageModifierSet: RGlass + - type: RCDDeconstructable + cost: 6 + delay: 6 + fx: EffectRCDDeconstruct6 - type: Destructible thresholds: - trigger: @@ -99,6 +103,10 @@ sprite: Structures/Windows/cracks_directional.rsi - type: Damageable damageModifierSet: RGlass + - type: RCDDeconstructable + cost: 4 + delay: 4 + fx: EffectRCDDeconstruct4 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml b/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml index d81204be071..93859b1db2c 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml @@ -1,7 +1,7 @@ - type: entity id: ReinforcedPlasmaWindow name: reinforced plasma window - parent: Window + parent: WindowRCDResistant components: - type: Sprite drawdepth: WallTops @@ -58,7 +58,7 @@ - type: entity id: PlasmaReinforcedWindowDirectional - parent: WindowDirectional + parent: WindowDirectionalRCDResistant name: directional reinforced plasma window description: Don't smudge up the glass down there. placement: diff --git a/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml b/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml index 6ed2cc59267..e26fec65b77 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml @@ -1,7 +1,7 @@ - type: entity id: ReinforcedUraniumWindow name: reinforced uranium window - parent: Window + parent: WindowRCDResistant components: - type: Sprite drawdepth: WallTops diff --git a/Resources/Prototypes/Entities/Structures/Windows/shuttle.yml b/Resources/Prototypes/Entities/Structures/Windows/shuttle.yml index d953cc588ac..f1b840c1435 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/shuttle.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/shuttle.yml @@ -1,7 +1,7 @@ - type: entity id: ShuttleWindow name: shuttle window - parent: Window + parent: WindowRCDResistant components: - type: Sprite drawdepth: WallTops diff --git a/Resources/Prototypes/Entities/Structures/Windows/uranium.yml b/Resources/Prototypes/Entities/Structures/Windows/uranium.yml index b956d369fa3..e5228bc593e 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/uranium.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/uranium.yml @@ -1,7 +1,7 @@ - type: entity id: UraniumWindow name: uranium window - parent: Window + parent: WindowRCDResistant components: - type: Sprite drawdepth: WallTops diff --git a/Resources/Prototypes/Entities/Structures/Windows/window.yml b/Resources/Prototypes/Entities/Structures/Windows/window.yml index 375d0c16aed..606c54e35b8 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/window.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/window.yml @@ -16,7 +16,6 @@ arc: 360 # interact despite grilles - type: Tag tags: - - RCDDeconstructWhitelist - ForceFixRotations - Window - type: Sprite @@ -42,6 +41,10 @@ - type: ExaminableDamage messages: WindowMessages - type: Repairable + - type: RCDDeconstructable + cost: 6 + delay: 4 + fx: EffectRCDDeconstruct4 - type: Destructible thresholds: - trigger: @@ -89,6 +92,14 @@ - type: StaticPrice price: 100 - type: BlockWeather + +- type: entity + id: WindowRCDResistant + parent: Window + abstract: true + components: + - type: RCDDeconstructable + deconstructable: false - type: entity id: WindowDirectional @@ -139,6 +150,10 @@ damageModifierSet: Glass - type: ExaminableDamage messages: WindowMessages + - type: RCDDeconstructable + cost: 4 + delay: 2 + fx: EffectRCDDeconstruct2 - type: Destructible thresholds: - trigger: @@ -181,6 +196,14 @@ - type: StaticPrice price: 10 +- type: entity + id: WindowDirectionalRCDResistant + parent: WindowDirectional + abstract: true + components: + - type: RCDDeconstructable + deconstructable: false + - type: entity id: WindowFrostedDirectional parent: WindowDirectional diff --git a/Resources/Prototypes/Entities/Structures/catwalk.yml b/Resources/Prototypes/Entities/Structures/catwalk.yml index c727c249522..6dee91365ef 100644 --- a/Resources/Prototypes/Entities/Structures/catwalk.yml +++ b/Resources/Prototypes/Entities/Structures/catwalk.yml @@ -51,3 +51,7 @@ max: 1 - !type:DoActsBehavior acts: [ "Destruction" ] + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 \ No newline at end of file diff --git a/Resources/Prototypes/RCD/rcd.yml b/Resources/Prototypes/RCD/rcd.yml new file mode 100644 index 00000000000..cb2c9ed2341 --- /dev/null +++ b/Resources/Prototypes/RCD/rcd.yml @@ -0,0 +1,294 @@ +# Operations +- type: rcd + id: Invalid # Hidden prototype - do not add to RCDs + mode: Invalid + +- type: rcd + id: Deconstruct + name: rcd-component-deconstruct + category: Main + sprite: /Textures/Interface/Radial/RCD/deconstruct.png + mode: Deconstruct + prototype: EffectRCDDeconstructPreview + rotation: Camera + +- type: rcd + id: DeconstructLattice # Hidden prototype - do not add to RCDs + mode: Deconstruct + cost: 2 + delay: 1 + rotation: Camera + fx: EffectRCDDeconstruct2 + +- type: rcd + id: DeconstructTile # Hidden prototype - do not add to RCDs + mode: Deconstruct + cost: 4 + delay: 4 + rotation: Camera + fx: EffectRCDDeconstruct4 + +# Flooring +- type: rcd + id: Plating + name: rcd-component-plating + category: WallsAndFlooring + sprite: /Textures/Interface/Radial/RCD/plating.png + mode: ConstructTile + prototype: Plating + cost: 1 + delay: 1 + collisionMask: InteractImpassable + rules: + - CanBuildOnEmptyTile + fx: EffectRCDConstruct1 + +- type: rcd + id: FloorSteel + name: rcd-component-floor-steel + category: WallsAndFlooring + sprite: /Textures/Interface/Radial/RCD/metal_tile.png + mode: ConstructTile + prototype: FloorSteel + cost: 1 + delay: 1 + collisionMask: InteractImpassable + rules: + - CanBuildOnEmptyTile + fx: EffectRCDConstruct1 + +- type: rcd + id: Catwalk + name: rcd-component-catwalk + category: WallsAndFlooring + sprite: /Textures/Interface/Radial/RCD/catwalk.png + mode: ConstructObject + prototype: Catwalk + cost: 1 + delay: 1 + collisionMask: InteractImpassable + rules: + - MustBuildOnSubfloor + - IsCatwalk + rotation: Fixed + fx: EffectRCDConstruct1 + +# Walls +- type: rcd + id: WallSolid + name: rcd-component-wall-solid + category: WallsAndFlooring + sprite: /Textures/Interface/Radial/RCD/solid_wall.png + mode: ConstructObject + prototype: WallSolid + cost: 4 + delay: 2 + collisionMask: FullTileMask + rotation: Fixed + fx: EffectRCDConstruct2 + +- type: rcd + id: Grille + name: rcd-component-grille + category: WindowsAndGrilles + sprite: /Textures/Interface/Radial/RCD/grille.png + mode: ConstructObject + prototype: Grille + cost: 4 + delay: 2 + collisionMask: FullTileMask + rotation: Fixed + fx: EffectRCDConstruct2 + +# Windows +- type: rcd + id: Window + name: rcd-component-window + category: WindowsAndGrilles + sprite: /Textures/Interface/Radial/RCD/window.png + mode: ConstructObject + prototype: Window + cost: 3 + delay: 2 + collisionMask: FullTileMask + rules: + - IsWindow + rotation: Fixed + fx: EffectRCDConstruct2 + +- type: rcd + id: WindowDirectional + name: rcd-component-window-directional + category: WindowsAndGrilles + sprite: /Textures/Interface/Radial/RCD/directional.png + mode: ConstructObject + prototype: WindowDirectional + cost: 2 + delay: 1 + collisionMask: FullTileMask + collisionBounds: "-0.23,-0.49,0.23,-0.36" + rules: + - IsWindow + rotation: User + fx: EffectRCDConstruct1 + +- type: rcd + id: ReinforcedWindow + name: rcd-component-reinforced-window + category: WindowsAndGrilles + sprite: /Textures/Interface/Radial/RCD/window_reinforced.png + mode: ConstructObject + prototype: ReinforcedWindow + cost: 4 + delay: 3 + collisionMask: FullTileMask + rules: + - IsWindow + rotation: User + fx: EffectRCDConstruct3 + +- type: rcd + id: WindowReinforcedDirectional + name: rcd-component-window-reinforced-directional + category: WindowsAndGrilles + sprite: /Textures/Interface/Radial/RCD/directional_reinforced.png + mode: ConstructObject + prototype: WindowReinforcedDirectional + cost: 3 + delay: 2 + collisionMask: FullTileMask + collisionBounds: "-0.23,-0.49,0.23,-0.36" + rules: + - IsWindow + rotation: User + fx: EffectRCDConstruct2 + +# Airlocks +- type: rcd + id: Airlock + name: rcd-component-airlock + category: Airlocks + sprite: /Textures/Interface/Radial/RCD/airlock.png + mode: ConstructObject + prototype: Airlock + cost: 4 + delay: 4 + collisionMask: FullTileMask + rotation: Camera + fx: EffectRCDConstruct4 + +- type: rcd + id: AirlockGlass + name: rcd-component-airlock-glass + category: Airlocks + sprite: /Textures/Interface/Radial/RCD/glass_airlock.png + mode: ConstructObject + prototype: AirlockGlass + cost: 4 + delay: 4 + collisionMask: FullTileMask + rotation: Camera + fx: EffectRCDConstruct4 + +- type: rcd + id: Firelock + name: rcd-component-firelock + category: Airlocks + sprite: /Textures/Interface/Radial/RCD/firelock.png + mode: ConstructObject + prototype: Firelock + cost: 4 + delay: 3 + collisionMask: FullTileMask + rotation: Camera + fx: EffectRCDConstruct3 + +# Lighting +- type: rcd + id: TubeLight + name: rcd-component-tube-light + category: Lighting + sprite: /Textures/Interface/Radial/RCD/tube_light.png + mode: ConstructObject + prototype: Poweredlight + cost: 2 + delay: 1 + collisionMask: TabletopMachineMask + collisionBounds: "-0.23,-0.49,0.23,-0.36" + rotation: User + fx: EffectRCDConstruct1 + +- type: rcd + id: BulbLight + name: rcd-component-window-bulb-light + category: Lighting + sprite: /Textures/Interface/Radial/RCD/bulb_light.png + mode: ConstructObject + prototype: PoweredSmallLight + cost: 2 + delay: 1 + collisionMask: TabletopMachineMask + collisionBounds: "-0.23,-0.49,0.23,-0.36" + rotation: User + fx: EffectRCDConstruct1 + +# Electrical +- type: rcd + id: LVCable + name: rcd-component-window-lv-cable + category: Electrical + sprite: /Textures/Interface/Radial/RCD/lv_coil.png + mode: ConstructObject + prototype: CableApcExtension + cost: 1 + delay: 0 + collisionMask: InteractImpassable + rules: + - MustBuildOnSubfloor + rotation: Fixed + fx: EffectRCDConstruct0 + +- type: rcd + id: MVCable + name: rcd-component-window-mv-cable + category: Electrical + sprite: /Textures/Interface/Radial/RCD/mv_coil.png + mode: ConstructObject + prototype: CableMV + cost: 1 + delay: 0 + collisionMask: InteractImpassable + rules: + - MustBuildOnSubfloor + rotation: Fixed + fx: EffectRCDConstruct0 + +- type: rcd + id: HVCable + name: rcd-component-window-hv-cable + category: Electrical + sprite: /Textures/Interface/Radial/RCD/hv_coil.png + mode: ConstructObject + prototype: CableHV + cost: 1 + delay: 0 + collisionMask: InteractImpassable + rules: + - MustBuildOnSubfloor + rotation: Fixed + fx: EffectRCDConstruct0 + +- type: rcd + id: CableTerminal + name: rcd-component-window-cable-terminal + category: Electrical + sprite: /Textures/Interface/Radial/RCD/cable_terminal.png + mode: ConstructObject + prototype: CableTerminal + cost: 1 + delay: 0 + collisionMask: InteractImpassable + rules: + - MustBuildOnSubfloor + rotation: User + fx: EffectRCDConstruct0 \ No newline at end of file diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index fe8c5a3cc17..8f0038915de 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -1034,9 +1034,6 @@ - type: Tag id: RawMaterial -- type: Tag - id: RCDDeconstructWhitelist - # Give this to something that doesn't need any special recycler behavior and just needs deleting. - type: Tag id: Recyclable diff --git a/Resources/Textures/Effects/rcd.rsi/construct.png b/Resources/Textures/Effects/rcd.rsi/construct.png deleted file mode 100644 index f4be36c9bf4363ad314986a38dfa3dd7a506ea6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3612 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 a+X}&kW$HhP#uT9I6EM4EfvPrgjs73@Qy{Pa diff --git a/Resources/Textures/Effects/rcd.rsi/construct0.png b/Resources/Textures/Effects/rcd.rsi/construct0.png new file mode 100644 index 0000000000000000000000000000000000000000..d83fa0506c6776eefcde77cb0a4e5203f70efe98 GIT binary patch literal 1095 zcmV-N1i1T&P)003YJ1^@s6nkRXg000CINkl$#fB^)q-zTO16ndjAkeLp zjP0PEoi2mTrh4kT-tWy|NKw%=YIWMvO7F8xvW(@0AVuG0<0{@0E=>PI_>0r%jWu zH;nY!CbwI$-!{fLPTHWaudii5$4Q%%XE)FGsaAvh#d-7a9 zr86QTf_|W1jY$9Gp8oY!O4Wd`t~@!q`6bzxuSG;8v$ZLg`#W~q@7{>>bZ`UE5A>^@ z@68W3fAnmjk*67-RDFhRGI6CR->4|EV_HwHtG<2SC4JvW1d$17-^) zcSK*&fGg`8=TiG4a=E|rubVzNv)(>h8_Wx#KdNti7#`BsVtwOWRzyUO)zx)g?WC`@ExxM3R=i;!M+1jjzXouFf%{YBkmm+H_~ok2C|u zVIGhSIGuiIW8~`%mkc;X1{4vs4CJ#6`eB(zKdKBEn;}?h9xNCF!t1-a-HI!{wtG9T z<+p9=hwhmsUC#shkZZQj9Q-zNCs4X7Cb(4PHK|@Cm4`D zA@S0WWJ!LU_Jpi-TSc`D)@>C9{i0e1^!2sqwu-C=jGMk~Vz(ZUJsIo?(exe}kPN7s z0htGkmwvWT3aaUBq2w;Q%{<_8&I3MfeeFka8VBQyWFYj_^i?w;$FrfYslPhU9M0X> zQSL7tzIit1vc`S)B>h>g=b_ImQvZL~73MQ{iWW1j9ZFxj3r0WL`m5SI<28wczUGsN zIItoB7!VR`K<$UoN!O82OM|4TvX@Ng~@jF>f6ek%WOV>1&!~W;r2`h94FHE z%4Xi>I{tS|y11y91r6UkP$KE))8T-G8<1asDi{C*0tN)YfB*ng!2kdN0000000000 z000000000>2FtMkfF6(@5Hw&<2w*_KfB+Z}Fd#(`L<+~VaXgzUe*t3ehmV#4$AbU> N002ovPDHLkV1lq$_jCXN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Effects/rcd.rsi/construct1.png b/Resources/Textures/Effects/rcd.rsi/construct1.png new file mode 100644 index 0000000000000000000000000000000000000000..0b597fe1080de7a05f31f43f2c38b85a9bbc90c8 GIT binary patch literal 3663 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 BrtAO! literal 0 HcmV?d00001 diff --git a/Resources/Textures/Effects/rcd.rsi/deconstruct2.png b/Resources/Textures/Effects/rcd.rsi/deconstruct2.png new file mode 100644 index 0000000000000000000000000000000000000000..d2144af3c78b41f5a6844dbdd01a23c9694b3d50 GIT binary patch literal 4964 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�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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Effects/rcd.rsi/deconstruct6.png b/Resources/Textures/Effects/rcd.rsi/deconstruct6.png new file mode 100644 index 0000000000000000000000000000000000000000..70771150669b4abb1da6f41476099d5299f097d4 GIT binary patch literal 8158 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�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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Effects/rcd.rsi/deconstruct8.png b/Resources/Textures/Effects/rcd.rsi/deconstruct8.png new file mode 100644 index 0000000000000000000000000000000000000000..79d724e6ba2e76bc3ec4bc2bead42ee8fbc006a3 GIT binary patch literal 7941 zcmYLOdpwix`yXPWIaV}jK04@xk`b~|N`<5xDu*SN9I{jnG27%*>4PLih=q{joaZ=n zpqxey3$ZZ_OWVw0W47Pp^ZkBbzki>+rs=_w$PvtRZqxIS>d0Id|6T z5(p##3_)~R8Q}WcvgRTPw5#)+m8E0o@O(PXUxWFm4c30Xv8I}||IFb`N0SFvOjGWd zqT?PU^?81L^C4eJ*1arW{iWm!cysw>%Klg19L`W}9$zeqG=yI&xZ*1FcI3JyG~BIu zZ{Spb-|AdYApPHCTe+eO85YQIogvV!(mUEtuu=9@TC?qE7_6WaUrh_}}-<_BW zJ#j_)&gYjt2oiL(mCLwReZ&K*0oBP0B*N?B7}iz(o|T41hUcxag`u~g?lj!fqtlLF zEZn2hj5nG@Ho<^%$R;)=4)qWbi_uQ)D}Pe^<7dxK<*x59h-lO_157{IM-PB!j)+{l zTn4_ZE-$I+oBa6cQAJ8W;qILglfXVfHoqqvxVLA#*@Zzl(t2BM`_LAzqVj&Bi*2=g zYqBQcMr@pISNT>i|A~KAgLk09PI&p#e;$fe_gd3GHfZP(P(L70&cJ;YrL`V18LMgP z4(V$ux*H_?A-u3X%Dg75dDpKU;AQ$agPMpxll;SZi^XzgZmmaabFqi`mBGs=(lFu7LLeA#VIird6n$2vyp$dVb4UCEDMcIo100{v>=uVA+vg%n zw2FkKoy<9PPKyh}511sM{XRZwHBj#NpLgXbekv!UA{DV~9M$zGs{sR_*;v2L_?;Hv zz7~|Z1+&R;=VaRR)}?a2rbOO|B>A_{K-cwpPT5~)tUBifzyCIN3+Kkc$GYDgx2>!W zb>iMyk0Q2`hG9;TnG&bkl9v?8P*gPzEF2n%A=Ip6O?hz<9~zfZS#^t^lO3yIR2;er zx4)<*D>w3Pe@=22F;k(TkDJ#iI3FZ%BC4U1J(NszUBA6)eR|(&&xs~CckI<}i#HVN z5PFWl=(sPF6ePICbQwstNIG_BL0Z-n{JwqVN1CoIxlFGynpRM^HgX{&U^A^iuTc$? zw>B~rK6OgT8M|gK>!{n7__?0(T7M9`!<9nW`z(aHA62xzP|&)-_g42hN%qGM7}zMV zZ*RQueLzq<$@$~f{tj)9bV49u>!C#c!2S8mx0tf!A&r24RM70}6JK;X< ziFWI|5|%mCb3F)B!YghV^1oxagFr)++>&PyVv9R*6LMZp1?S<2JP9X3HsCs8?;kvA z5*zppm%O*U#VqteT&uRQck@bXa_dJ!>;p3Nie%a<3{ilxe3HL@qZ* z^$2sVm7U-p-J;$u>tMG2$j?36_n`|~RMHYR=AR4gFh_-wfh~4lt|&UUd$o<|XOWJ) z_j?;pGzslTiw1%JulTk8T!>`xbPT*fAS!u->ac_A@#R4-i|XEB!t7JWF>*tjZ;%v@YQFPoTi2E_~7e z9|G;Q#=1)lQgCtlzbQ{;meRzpl5`s!p&DV&qdnyC`u&{kYsOQ1+<+slE=lf21dV~MGU6}J=u11aLA<|X$k~|lz5Vaa`b-i!E(GJ_Mu?EoE(C$ zJbem?3CapWke1Rn35yNq-YWQC!m-*Tb@k`+MEEQuk9WwXGZ^PFc+-%RGAe3V+}k7g zRe!_)fl~a;T`K*|Tw6(vH^Upr>-%ZXc@{lWI!!Ly6?Kpn!@a^C9Xy5KRMY+`zHX7F z@3BzSjWJsQV(k1|_SeuNzCng70wY0}w_v-GS`$rq^X=l67mBqdfipKx1oH#@B-{Dz zAt0A*WkRC!ciDWlgcNQoh)i4p@tB`ZEe16!LR}4Xz@(aI9b1hV_ zmzwA$QIkc54U*`L(XKn)u|42G9AxyQ6)Cif9evFmtBl+anUkh1rO=2!$NttGNuTXH1AurW*DmPFXYWqE{`-RLg-+K}Sq{l$og@P6%gBv+zna7pC z&hvNe59>SkK!z3`{}D)H7pxeP(pNDgF|#QwfxCEu>ZVO<>+r==geLc0obAP z;Zq0ll|!0th5OC};px04wIJQdKRH68>fyc%f*Zw-fmMf#mwF&fdt>K=9iOR zIQ4EbA!UgI(>Fs(l4nEXoFa8sldJ3~M0dmhKlo7+QwKz5PzvoaBLo)$f8+q_SH{k< z#`J=z`P)qIV9&DU(UAGLee&`s(E6rMy~D#R9+#PEt6S9hoz*L;b;jwlp+V#veY~$` zC>WC*G^-iDIqXY_@X^w9^crtYwP|^ZqDJzY?F_Eq9sBG{TmK29%s~L0b%u6aF$sKI z>2FEha4j``&YrlAwg9Kd!{?F`o8E~0@5cK>-t3} zZ{x>RBZ&wudTKb&ikVlQ8y`j)y&S&vEeh0`bY60{td zkl$U?i87TTjY7tVUldFZTU1z>W{&e5I}hk^sR(_ zYcIS-k7z(JoZXa_7Q(M;nWgWr(P|kloyoH0f}#BJ{i*TXWw(j0wiW6{ z9_0e$lrr)bo!!)F%epGNivCE+Y^S#2E)c;zf4WZu%uTGdxa^4UCM`(cyN0%W!p<>H zr}0(_5MNhgk|k4`$Ppvo`+~@XCD2VCdd&=9D%g5_M?7FPzN6tf82!dsVMdb+LIQ}5 z`(6rIBe+t=sU@-ZqBLg&j3?mMY(NBf@@Res_&Hv-E8?Lq)8&VS^)LuR;x=5vTm4u!VGA5`D`?#*Lh^689CC->TjW)~ z9EW##VY3nkfayx&P@S?>CXwro-$wu^FPVgjYLawgSvq|e3Zu8Nl8DZ#Whwegr(beL2lu>Q z)eh|>O=Z6)o|5;S7%H5dDZhR5;Bo8;lLj+{mxpSPhuyj4CDTh`cFj40^v)~EZWsIF z_rRBL2F-pi3Lt;ftHZ;R`+qW6ioX;34?9jV9euSgJ+M2OumAXOire6xoymg#et??8 z6x^)H%^pcjP%T`WiR49c!jqXuYWSaBDxA9muOGS+y>0~2W~7*GpAxWEk3|w0v}Yy8 zW$9^RSp7LeWu%7_xj*J&*}lJU*wn#H6LN9tKXUNRPsZ*YctGxy@40Ip{WR!KhP{!W zY26)WrTg<@mBzaB{`~{Yhy3~=@J;?M5_DqoeLUAC!)ksN&{6pD~x+&G>v00kne@Dv+ z@_SIDt5~LD7x5jNB$McKeSkM#C9_<=FWpvZtqa}LoUkHIe*lt%d?<(<4a=?_xSj;< zm!Y8{`}&#_|6z{!L4JqWnWo4rr_tU=O3|r)3+C9rLb1;v8v<#x^sln5`Rwt1l@w+jO+!| zql~p0hMK`m?hO+E4Hf?hNdJ~cyW5{3%>eY19KRC;+)Tjupeum59&VtDT4KHl7Ysb? zhlze~ltfrtoX)O)hvtY*%MGl=wh#C$e%JNC>Mxc?pmoELflmhfM9+o2k8lsjIG`lX zQTGC%tbgs8#b=Q)xRA@j6msp|JeNe5V+U-l7QGAoc<7&~(ne3As*8FbYwrxkODyCI zL{$%OPZn<*Q*SlIdH0WIz+i461}(P6!`PDQW71bb>qYmAoHTKbAGVW9LI(ph0UbB2 zK1L&4T{&SJs`N!lA|Eu_IY-xMLtX7NCfz!9S94h)&nt^K#40Tp9DM_gzIn+jaKj+<4h^*0Gr~1wX_BtlJPp04IoJkzmU->Iw`q;~w#oG}U?~GxnzKhP;7ptCL zzif^V0_zk_=gq}v6@5mJ!>RNx=6jqqTsw|MKZ8az&FOR287BB)T*#&=`+!AjeRItF zsj)j!I(kHY1&)Wh)@_fyZFqvG*qjzX=NHnS;q}ARnqASAw;>+H=iUp4E2%*OJ9ugL z$`#%)gBepCw*8l56e${A(84{8l^v5V9haxKYjkz`5i|FV)vY-hR%M&=vjnzmLD|QB zX@Kw(;nUQ6alDlrnXDO1YA4}ZPK!gL{wBDDv}n>m-y)}4>)AzWa~-nI#;$m7z=akt zLHQHpj{Qsg+|QQDwt-mn3Jgi?{x+eHjRrGY6tng5w6TZ%Hz5AQ0SPj# z!pxSDcC}_Sx-K`U$NA2#!r&fOvjYLi=Sa&Y(IhiI#?>v-KdXTNx4X_E9z{eScSyZ`^Xg;W&maVp-DYPB}IBH7y>b$q0TOa;?lyG9O&ZR&CncCX^zJ=-_YG zcGc;TQ=p593c_YTNo_D5Xn%zEM@W<^ybyS4B3@w}?3$CPR;TPN`ZsMtYGL0BdxPSB z6c`btdfKQ3!B?hXNi0PyOK@~FUF|<^j|Kmw6g_jMf4vvzs|*S>HkYqkjlRVh>%{8QnO@l8OUy(FFhX7;e|kdJxfDuSY5z zM`#8kzhF=e2J;Kzl|Lh5qgs}1qo@~|NoG0s)lGsG&#j78sG^2#fFt4+$#5EsXqyIr zn7`W94wy8geZ00$WBtGVhgJZz>qV$(ZG}q!>NRX^x{Y7I_I~_O-|2(q3tGLyU5+Vw=O0MEnVq?21Qk!nK~6D&9_iieu+PaN4BqM7+4 zH|?W$TXBgX4A;8n{WgN)P$>W9LYaa*_M3q0bI%L|hW@(W5QYOX&|j+9?5E6=rn?X| zTn=Uu)cj0l#W|{@)s@8_m#SP`Hn(j?_i#E$+@-UNimwJj5B7akC*fW&*hd%4qNDeX z$@3%lQL4I3oEnS1bcHO+@b%w@Z4XONqtdGfYi~hEL)Q;%k9o7}(UEH5RA6DtO3!dt z`1$w~HF*Z^zjpuhVCipHU66*74*=!Thgb)MO@?nt^ znZ(;@05ZWjmY+W>4?7B+%l=w&(*++ffNPjQ zQXk6@dC9~k64W)4aHG?)PtK6o7!>gQjIjsq!iJO`NnY?uiKFpALC%39f!QcMw5Q{Y z%j)8H9an^0u&TtY>ooTNJSfx;_|~S-u$G(BIt>r~)on_$&9*Q4qfv|!16^vFuAM7a zqS6AWeu3(OawX0KH*vV**rq>P+I6~*;atSn4n2q{#y;Y8^vqW+KDxiMXf!Bbp!#Bk zh5*L?P?uY5tL9Z1T;ZU~g9;C?#8ZC?xsbcsVYVIHcOBbMw9uE6D+pZYVq97XiVoe8 zj%cZ@nK+~RQgtZ0W~*1xT4vkT75nov#m5`nnk-BLP+el^Uk{*)Mx9+c0+n$<>DQfn zrpm2;9lnDY=Q?wIDu(-%MO5h$J{9C+S6&Nto%zrx;e9fk1V2@iX_fWe#lzbXX#tazyPa(+$-cs=5zID-e}0Q}TU#)Qvw-2<_POR@sUyGNn%UwNmjW?^<2WdkEOo)4_guHx|D7KR1QJ);0vG~f~ zs%)}k_rvKI^=HFbaBb~<^tL2{l~v#$enl2Tm!J5H8-7#xx`z{d zhead@As3I=BY5y?!IxvVp>Fm!=9f~@1u0z*K#M+}}@v%T-ht4kpu=H;pmHC|` zK~LN%s`S&}t)%RL`_uY#UQIs!$FmG<0+u_khS8+_AS$pFvwCQah|^8AE1SpF+I8)J z52~M>2?mB;I%> zuEP5>EyC1pLk8&RD@!WZrQ(fo0@-kyOMC^bk%_1l;Y1aCF%gyU`vgD+m`xLaZakR`A>PyP$*CSEP2Hwn^1cVIqSFu}wAK;L&+ zXSiH*F%Ce9Ss}B=8e`}>q;f(MLhITy;<;C?-i>Lij8oO=?07|nDJBbdPv5B{8~}-E zV8~wJ#ESn+iROVkGe1!Nq|vG}Ir?d=c+>j&!n1mr85{QaA2sDqOi;A1K=yN8S#eNM z#M&yV$!6o9AfQJtz#fIX*uIA7DHW$^ATP4SuzmTg)nSyyXVViv%N)v|iXhyXVFO#e zuI8-|j2H>XRS=9&`o$5Xt!U<&XtdOw(O37yc;0DXCAft+n&E$NRIyY5K^}d6B8IoJC9Wlumm7A zy6=!p9BDwvRdu(uc3VZJY4u&=OQk9Q_!>37W=>=H*JwTW!K zD%DJl&Yt#W=#pF;C;owftmwT116>DgVjZe>+Ak`$0@TT>s&}oG`1%FTlaEMl(hTe- z#v}Jseon;Gu}@eY z^jqu>^4kfyf)oKE&S7GCEeA8ZS$4*$^Zk(Q-@7#r_RzzssG(W7()og{8#@$~r=kyd zXBFNOU0pv;`W@svC4YA7?~0yppLS>MY`2&69g=+W5YpY$kf?icGjnU?9m2&%h~1*% zV5f4yRNba4!5D`mP$)n}-l>3rh(mk{3oXgmkIAQ7bwJ8fGtCD!*w76=^p}K|lI$c1 z;1;#+0pykzFaUVWW=((w0|pxuXUGN%iUi$-eY4a8;OxsdeJy}+1#Jj!?5d<2_mL=h zecQ$yz=y(si*gwrY36LH)EifK7cLyqAk8L?1^BJ7A`W-4Z#oN|!#B-PHODsfz7T^^ Yg`;!s@;^-gdK+}^^aZPulh+^rAKYv^4gdfE literal 0 HcmV?d00001 diff --git a/Resources/Textures/Effects/rcd.rsi/deconstructPreview.png b/Resources/Textures/Effects/rcd.rsi/deconstructPreview.png new file mode 100644 index 0000000000000000000000000000000000000000..2d114002fb288ad27decd3b2c005be23cb013b7a GIT binary patch literal 47364 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Effects/rcd.rsi/meta.json b/Resources/Textures/Effects/rcd.rsi/meta.json index 5004a9c4fcf..c9e8320c60b 100644 --- a/Resources/Textures/Effects/rcd.rsi/meta.json +++ b/Resources/Textures/Effects/rcd.rsi/meta.json @@ -8,7 +8,202 @@ "copyright": "Taken from tgStation at commit https://github.com/tgstation/tgstation/commit/d75cbd0a2900fdec4c12cd5ba986b52ccff03713/icons/effects/effects_rcd.dmi", "states": [ { - "name": "construct", + "name": "construct0", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "construct1", + "delays": [ + [ + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "construct2", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "construct3", + "delays": [ + [ + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "construct4", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "deconstruct2", "delays": [ [ 0.1, @@ -45,6 +240,220 @@ 0.1 ] ] + }, + { + "name": "deconstruct4", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "deconstruct6", + "delays": [ + [ + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "deconstruct8", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "deconstructPreview", + "delays": [ + [ + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05 + ] + ] } ] } diff --git a/Resources/Textures/Interface/Radial/RCD/airlock.png b/Resources/Textures/Interface/Radial/RCD/airlock.png new file mode 100644 index 0000000000000000000000000000000000000000..7b1e0e9aa089b0179f3f63a6348e58d5db13725b GIT binary patch literal 710 zcmV;%0y+JOP)_w=D+yqbaBpyWE*{gqm{sHbX|AOI8!VZH7 z8+d8ic5p#*C@ge^K{`7GOCXFf4>sAdCRr-Fdf0Qz^Yi;Y-}imqHzS*yn+#J%8M0gh z7&?HPamuBorPD^ELAhLJcXyYk3k%$ungZba;UOPaSGhkk!K%brERFcT& z^H`P@>NQQH+wCT{GtM7ietsU$^Rn-OQ%M2{lPQXV5CW;EPZCLz5JI3RN+kY|HesPq zh$M>>!!R&SGu9S>E))v?$0?9y`Dy_qaXv7BxI}?2t-VPr&J3V+QtwN2QMa%+2rf%I?q18WN+;(=Hc^! zC}=a)M?t$sOA-Zb_ST93yk9t>z6f_7Ux@je6uf#{<-?oki^{Srs?{p>#l=jMFp&ap zya@-T2nCc5kbm5b^#}0An+QA0466VQ;P$PT>qL~0~j`dU$%iKY0NyE!vFvP07*qoM6N<$g3SC$xBvhE literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/RCD/airlocks.png b/Resources/Textures/Interface/Radial/RCD/airlocks.png new file mode 100644 index 0000000000000000000000000000000000000000..1195d79b58bb2196da79fef53d163110eed1d880 GIT binary patch literal 776 zcmV+j1NZ!iP) zO-mb56o#KzWAvKZN;I*7Qd1apilbc!UAZWA5V|bdO*e(E+Et;O?(2V$7P@mG)s@gf z$f5)ZwTc9cA;_pvgw%Xkr3q6(>~vv_rkPBVw!w`DW^vzp?mh24UvuD6F6Dy7W(JOp zjTxGzAnAG??(uM)9EB0j~70t z(`jkUOn*5VMK30~W56_c=`9+@2qg{m@M7XsmMxN7575zuwLEaq~# zitxF)xtfey64*KZ3Q$yve`3?q(}gm$a{G&SQdt6xW}xr>5m!B4vPnB|lt=A(?C4x0 zIMJP&ShRfk$jAu6V6Z+pFg3BrKr+dv-QAMCTQ`~k=;`UH%>(hzcAU<3QmItI-MX&h zcDu=)t7{%r(JA1S> zpS=3V;bFen+sLHTwGpVs?Pf9WYHK_5PE!-DEe1QO9N+i%YY%~beH#+qq7dLs5dN5~ zxzrxro+KE&Q(F?=ymGI!$^3=!|JDglJDklhDi0Zk!BG`@mN~Guw?{IWEc{sB2!PW| zD_<^vjbs)oc&xEp`xQcw^o+U9x#?%}iQFk5$zb|8e{)kuc+ zRvVHeRX>#W_VyZzqM&ISPx@E?Y2uw)?YA6CFXd7$Tz&)TKqMS2eiK#z0000|)|!nmY^~*`d-MAtmv~Y2ZP<5hiLNcb+SWq9GB9x_0&{%_ zkijQ8lzSU|0w`AisEE?XQnW<0Edy&JjLkx939cW}1AuaXPjUcR?{k=d-H8>@0H~l` z0iTSgK@apk#|R)wpZ#_eF?~GrS%vx|YfWG0D(HhMupK*wvu+<6t_dTL8N(<6TiN^% hoM8$?fFKCF^8$1Ha?PK@$TEX>4Tx04R}tkv&MmP!xqvQ%glE3U*L&$WR5rf~bh2RIvyaN?V~-2a}in2u&K2 z6c<1NmzipIj037> z8L4*mUrKu)ve@B2KYqcS*9Bn@jCJJ zrloVJzVjIhC%u0qzJVhK)RE_e5 ztjh}LEzWAW%9{7&FAU|i?Y{ z03*jdDo`Oge(*o|JzKLdIpHRSVnE=8f^XD6qK(j^EZ5Y1m@fn}dwLI8wp=Fl{!l%%IG#@IdAXpOD4&HGY{ zYMvR1%{ljZo^w{u8WI>;!~~eYGy&~1n~L{dw?OyoZ@6&j!YT5L6e(5kUHn2i1F8tbN*b>~(_};{Hk3lFR-*-v znH^#&x~_wB4r`3Mt|6Usczyc>037z`n}pt|o9{>eZ<+=bRvz01WDM+$`t~(rEqkNx zbd2fiYair`tPKFb>G%aAnqnS|mE!LoGP-%0It>v)YYl5HwAObyCAhUV$)JqcCQRZ9 zc<=(bIL{f{GFgO?#GQAlDuM6jaAuZ>rZwc;1#+F};(d<^{F1_BjLa5cNM$MQAoqdHWzFz*j%u=;Qzb8 z+o@0En81^9aZ}1YTyC?Oi)Xt4fH>^W0)Xg^64!2a!j|IME(lMBD)A* y{4nvU`%7GW6LLK~x+a?D1)^T(Lj{8gNSVl5IF!@_duo84O6IE zsR+ISR7zdBtRS07BbD0a5;n~d`hy`Xqm zb$|IiuRw^GxM9>oiLz8W4Gv%!b@V!|Pyh^rF$Dnd)9rGJALerGHLx&I6q&?r+vY36 zWpP4CAo2Np?hD+P*LXaRL?RIgU^1CtI-Q0C;5ZIuvsoYj$8q@jK)xWM*J;7FZHS_X z@p#N!1Nnl4@2?O40G4GzuW69W3fiAduD>8(kf7HzSeE4=&WPke2}2Z)*Sm002ov JPDHLkV1lxd%lZHS literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/RCD/computers_and_frames.png b/Resources/Textures/Interface/Radial/RCD/computers_and_frames.png new file mode 100644 index 0000000000000000000000000000000000000000..0f17ab5ef1d42abf861dcfa6161dfc21ecb6c3bb GIT binary patch literal 775 zcmV+i1Ni)jP)ok{PLVm2R zo!GcgRD>v2(sjQ#o-+af27}?)HO7JhT&|JaHKKow>-C2()f)`hY%NSpy*PADFc{2! z2aHA|CX-23_a_n|911Zx@kCW$d*wQ%rJ}YF#9}d`(P$dJ6^~OP%Y)p1R#gGExXbSIAX z{?Hr%Xliawn`o9X1*J9fW& literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/RCD/deconstruct.png b/Resources/Textures/Interface/Radial/RCD/deconstruct.png new file mode 100644 index 0000000000000000000000000000000000000000..ca9548e0cf932a6f099c84b8585b262696df86a9 GIT binary patch literal 1200 zcmV;h1W)^kP)_7Q_+9?!o%}C@8|vI z`{wige7@h$BR-#x`#B8m^LSw3z8kP719$G+sX973SY=tVCpDF-%1ZL`^8pBScWZA` zleqi(_&ge=&NNSLHH?g^%F1c^1qCaWQdTe!Nbxo`?SDHOow+9^rE}M=T?1nokYrV` zZrwVy%CaQep449+5SZri$chy!biHYU$RdvRb!(7sJi!bFcKZX)sQJ#M+832NV6LWuD7Npc;s@p zv>N`Na1HUz_Z&6aho&NO2JA86sVgrKUiu?P60>#KKC3)YOC1GiHJ z>GAtpiGZQ?XBb8aq7RHF7yXl|?sgek09p^FKn#(Y1`GmEjV4D^dLIEe?{@dz4FejG za4d$1d{bPQrhzX6UR6qYfK#Id1F1R!P~!2VPsl)UM&uSOFe9Qh=tOz0>D}`Ns6HY>>3#O|ue~!Xm$q4Hf&kr`oA-C3A<>Te5(}e&Zi7~jnBo^*+ z)zv-WbUNSET5oJ=X>s@pt_F_*IR|SW?^&|D?M|0Kat^r_fQZpOOutDhTbDxFoK9zL zYisL~o}Qk;va+)DV{2~i1IZ>IGXkxLAW&G0v2-MU;$$)YD}6{j7vkI*Kqv5S*OW)g z2c8^g@caE|gTY|I!i5WG0~aS;1x^=~&GFxYm(B6xbb-@_%yqFX&h4DLSS@lc)~WMm zU;I^TeN03ie!soFQYm#Ip>-T|_uO$62#A0!{>)FWn=Jm`LTj;u9053U=FBJM<>d=b z)Yk54YipaF(1w)CSSH&nbHH^g_nQzs8gGeG--tgJ0twp>f_ zmn~cN*KPL$xt&>(1WQKUnmDU+JF`E$XAQ|Pci(D}VxS6P{arKQztV@drbc^fYIHJ< zh8H(m7WJmdB)k1OT7Ny95e_p=)4K4C4hOxfl5l3-?Q?h&a`1^sqrpYSHf(Sg5k@4U5 zJhB&%cqA4H`~o}{3=hnEIwPY4k^MkkFdY3j6WkMCg3{7b_L?T)Sd4HiHX<^KOT#um z3mjEYiq=aJIiO(^O66;<_i3&7CA6zouf8$v2%J8Bnpv}EvA3gxP$&fb{lh;`0{ZFi z?*nABWdU>m4iWiXDU~N8-N0mr!|~&n@B%D4LOdc4xm*ZbLQqAvkA?^|Y O0000T~J=tB9o2v45|A@`Cn9t<@)B1Zf6^*VbPFV~Z6$S=c!KF)%PN{Q32R ziICcxS1(3jvxga%<%-&-KlPJqAH2PFl|wJK=&78g$9oYSvrlhtUFERMZGZUo)>RJk ztma2@mLBhs#jvSmdjE=>nc;85+t)AGpxecQD+J_xlODg`vs`ZN+NB>E7#R5B65dO{ z78X{bienQ)5|{H$dUXHlX%%et;B>&F>gCe3Kpp2S-a8CzU^giJzUw0{(NEp0000xrWIA literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/RCD/firelock.png b/Resources/Textures/Interface/Radial/RCD/firelock.png new file mode 100644 index 0000000000000000000000000000000000000000..5b581a2aa373e2a7a7953aae0ccab5c8f2c75a3e GIT binary patch literal 637 zcmV-@0)qXCP)R4NtX3V{0nb^t7-Is7>4S@u3Po3`KN1;N5Eq&d`86_O;O zR;zgfa7kV50(X*8tFQ^YxSz3z);AA^^t#%`WW4|Y+9xLffbGLy;5p97j9w2s$008W zLte=X!jP^JWyG}Q3Bd1DBM%o-3H&)XVu!|JBX1+{yt$^a7~;2d93FQ~Tb|PJ+1>TP zf^d{AM*v?oZiG9KtUY!p9J)Tfxad0iF7!smz)#+Y;JucL#njbeG~PJ<|1Aia48UM7 z*lVfRJCXrF>qy7@P6q&R{7pk9H)rl0d~O4PDRbiIS1Bl^$khLOivdwI#<15d6J&QojvNo5)b|Z?%@0jhP%lI zLWB*xw5&V0ACg01qbms8%n()rWsG=`#AQultBwxYyXE`)zTfYg_g>!bCAGG;MlbCW zJ+=!0Jr{5#3Y?#x_iD8oxm=FT%}pN8&T@5l7=Um4`@CCPVtQhNPwVR#hC#Vpj^s>D zO<~(MilQKdU}0e))sX=Ia9Nhob)B6?gPlerSl4wu#82Yf93SUPt%j_3^V6#%Glmb{;S@DttNRkwAP1E#*5M;C2 z?rQ)blgUsl7E?*ihGAe?7KUN8t(_F@I8N`s0$G;-qdwplR3?)-I{}U3N-XAwb8&Go zl>a23u8J^w=ZOB%u{K=4JI3Me(J#RI$@Q&&bPUrpJx$X9s8*{ycy`L?tZP8iG^*7q zj^p49={f@2t9_)?gG?8~_}21BTO-UTgxK=yq-O#G8f_VFH`X3Ri3xc!xNPdf_y$tWo|Cimb*@YJ5Xo(ei$!* z_=^1fMkF7=9jH?(y$rf%p-|w>`xlxWnSXfPCm9(5Aq1+bV%s(c2M374Q7x-aO}gNDm><)Bt_6#DAUF=$lkD#%%2+J!j@6eR_ae z>!Y8%@%GI}bqPZcoU!wfuJ*v$`m_gDcfQxv9$4M^URQYlIe;8M4j>1R1IU4Y6nP)EX>4Tx04R}tkv&MmP!xqvQ%glE3U*L&$WR5rf~bh2RIvyaN?V~-2a}in2u&K2 z6c<1NmzipIj037> z8L4*mUrKu)ve@B2KYqcS*9Bn@jCJJ zrloVJzVjIhC%u0qzJVhK)RE_e5 ztjh}LEzWAW%9{7&FAU|i?Y{ z03*jdDo`Oge(*o|JzKLdIpHRSVnE=N_=^lXd^2>?KY z714cfIhP!Xx4VmC^F8Y>ig6*^6+zKO*^q|?WO*fh&$^R$%bn$wL{VwvDnio8h5ez1 zqHG?U0VGeTGf3X`E6PTx2nvfso6Md8e!9x{w--2`Z(5Z~r~`}(puvi4cNdAoWBv=B zo_~GUO0UPzwZ)-Lrhdj^iX2Oh{Tu3I`zucQXbC`a#s&DEMKBx21*DPN+W{DpMlLWe zwD!TDGf2YO65xcl i@8@K0U|?WifcORW6SO_Bl^qn~_51CdINneh{^%T^eR`f?pwaaCY)gQDYtvFl zDQy6Kuf1NkzVg=A)(uIB8r55Xhkz$B>fS)n??p(c+KX70&I55EB}7+cJsda;5NPjc z0^S0C*LD5#$%*m7%}LZszy&;jsXmG@9cy+K!-{D+1~dY`E#ODqy>Q_vMN#5jk0FLlRyZxPNwdyiV z6J6J_TCE5n?pck0czBp_I9$`f5-^7_zDBC~wT>Ba1$u5hbNQC(ER*-th1xT zIdJYxGMNm;ViD6cqmvWk;kLFmB9RC^Jv}>m3I70oK`}-U<_8$o5{3;F^Fx&C$H1>} zB)1CBWwW7br4q~M^UnJEdVD?~CypOud3iZHmKY7MUfSQ^|BuGd3p@l4LR&E?vI70lrz5S_XvZ%1Sws1N;cHyzKmnA8%|6ziX|`xkF0n&n_%HbNkk;Ude zBoe2q)oQGvp~0zX8cv6UAFf^d*YKUhufF)w%TIKMP8Z_wxcqr;PTokRi;Ig`ESBg* zQrorIxBV9Ax_&gD&$}EB2bD?%tJNAE*Ajcz^R0C9RC8}rbBkr b&|c{;rGF|O5qow*00000NkvXXu0mjf4SBQ| literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/RCD/lv_coil.png b/Resources/Textures/Interface/Radial/RCD/lv_coil.png new file mode 100644 index 0000000000000000000000000000000000000000..f83ed75325b3aeb23335db6ac95d9413a414a80e GIT binary patch literal 850 zcmV-Y1FigtP)EX>4Tx04R}tkv&MmP!xqvQ%glE3U*L&$WR5rf~bh2RIvyaN?V~-2a}in2u&K2 z6c<1NmzipIj037> z8L4*mUrKu)ve@B2KYqcS*9Bn@jCJJ zrloVJzVjIhC%u0qzJVhK)RE_e5 ztjh}LEzWAW%9{7&FAU|i?Y{ z03*jdDo`Oge(*o|JzKLdIpHRSVnE=+ut>5SzMB&TEbHtBSF zPw!3#;Nalk@ZZ6vHlJUfjEVK(s`~2&TL@FmOnL9GAKH4oR51|;86U-@Km`EcCmyrC zZ@rY9h|8kVVq3oHL`i|F?+A%*upPN>E#SCAwdI?Rd}#M^hl)hSd87%6^9auS3kg{6 z0Feh7pCktW2vg4JTv=3_BP1+LIs1D1UM8SET-C#)5AEq@5P>CB9{U?`@)nnDG=g38qEO8iStOy zx%(S7*mjNMC!V%{-e0KBYFK;f^nT7=0_I*oo!*{qqd_DET2I42O+q4|4)5;g6dZB~Qr81bgi9LIK^z(k9VA7%bQ1rxXd#2p(BND_=}-_MNVk?k zh8_WHF&!EQgZ(q4RyuU*&?H;^CC4om8i$x`Q?1%uXb|$Id*Ao_-S>O%_ujiZM<$b@ zmDWLvtu3JC0y<9%j76iBW)i)go_74RfU#(_GCKMkS(ckZUMLh8i$*KI6Vds%&56Vt zTjV1nPZ~Rd<2Et=$k7P-iQGxiAtr? z*)vcq7FhedWQ*7z2;+9kjUPdm)5-UzyLeZI5kdg48(GA=GEDCF95><@DEmT4l7tWf zNs_F++uNV)*8o6|K0q!qSLX`^J^BEUw(#TRO)Elx)vN;9$MgpCIM>ranvVXVYOsF@ zgy3*FS?%i@-po1B8_d%i%(I$RDEmT`eIX8)lL#RYLf~9a^JVg&4*&4}a&t1^a=9?g zJpg3+9j568z*JSXvWr~zcraC!OE`{~bz z_&7Dit@&r%*FvzwQOr&TQu<7te>d{L!n(V=L7zFobcqzT^)saO>iULipja$eZFq5F zAAcZBm(yusnqly5|6U#D4}|dt!f1wpW*AnicCUuJ;L5CrovC?vc-6wv2L~u}0=Q;j z`TW=Fg{s!ZpS>d}77M6q9{@XSp{jirc1P`l9Cg@9Q<9HP*CbuH!tnqA002ovPDHLkV1g9tF*5)F literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/RCD/metal_tile.png b/Resources/Textures/Interface/Radial/RCD/metal_tile.png new file mode 100644 index 0000000000000000000000000000000000000000..14f1ce2bd9d63cc5a257255ebae0adeb4958a546 GIT binary patch literal 297 zcmV+^0oMMBP)hMT|COj%1gaOlFq=cz7HP zGIw~*GNo%Q%M$mh!X=8p7}F97A#ls{558cj;n8)g7hd8RbzOrohG*YHN@?W)bz!3| zxibM<;064y07A&0JD`+;av)%K5pm8(k~O@fgb>1rsA(E=0cn~VUCKE}k|f5p#sW-M zv2CrB47i$rQwRv0z3UTEgpe`7x*${A5nMYnCErcJAsLw3{!LZ_R}R=ZvFR<4O6@ZR vOIvG-VHiLtMNt$8!%$n_wypmHc$^b&xLasC2fjuj00000NkvXXu0mjfypVN- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/RCD/multicoil.png b/Resources/Textures/Interface/Radial/RCD/multicoil.png new file mode 100644 index 0000000000000000000000000000000000000000..9e32919a7baaff69fd7af50625709fcc08970d35 GIT binary patch literal 976 zcmV;>126oEP)EX>4Tx04R}tkv&MmP!xqvQ%glE3U*L&$WR5rf~bh2RIvyaN?V~-2a}in2u&K2 z6c<1NmzipIj037> z8L4*mUrKu)ve@B2KYqcS*9Bn@jCJJ zrloVJzVjIhC%u0qzJVhK)RE_e5 ztjh}LEzWAW%9{7&FAU|i?Y{ z03*jdDo`Oge(*o|JzKLdIpHRSVnE=BjE6vuy8 z7gN{N2?aq6GIYt*W^p@t$dC`9QKfY6(oN7TT|3`b=+=OO7mpo01Y9r;>X0R1FbNhk zREuL*iDM$JgK}=%$i;HU-fz=;clZAHfA<6igTe5>BeH_e-#=Tp;qb!|0K2>E>UKN~ z6C9`X`@GND`(J&G=DzReRlND~i2~SrSH6?;{a69i>qW8bdAS6f_r_{t+X6t!VigV( zyqyZ&lPkhuvYG?B_{O$Hzd7LTo6?<|I8I5lT9?YU3&{2ed%b{LPaTo%RG70>V(;*0 zJ^|;wv3mS;jmeLzrJQozGXA%idaWpAd!%j(imCK->TN#RMp3uD9(1?B8vO6YBYwGJWCn(sEX>4Tx04R}tkv&MmP!xqvQ%glE3U*L&$WR5rf~bh2RIvyaN?V~-2a}in2u&K2 z6c<1NmzipIj037> z8L4*mUrKu)ve@B2KYqcS*9Bn@jCJJ zrloVJzVjIhC%u0qzJVhK)RE_e5 ztjh}LEzWAW%9{7&FAU|i?Y{ z03*jdDo`Oge(*o|JzKLdIpHRSVnE=@pMqo?bA%z>?!JzJS0bv|s^r|(MKUhx6XgX=Nn$hfP0TL1t z622X5Y4djb-At_OrysAa?LkV;w(_pmzhHgZtC)zD%4RV{=K%nOIJ4OMPJ79TxGWz* ztSF9yC`9MSj*w_;` zK5&GDNy*vn9;oZ5zg&KxX zk(1?Azoj~kzv9-JC;>!{=mz_w$Alkn{F8w6-4_M~HC0syeE(pVpo_ zeVp@_fVCG$$(b>&Z<^O^5FxtgY5373Bm(O2@&2BdCnO{!BoO`q2UEGHuY0KG00000 LNkvXXu0mjff4_KD literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/RCD/plating.png b/Resources/Textures/Interface/Radial/RCD/plating.png new file mode 100644 index 0000000000000000000000000000000000000000..d3a63acc610c5a0b0bdf72e2850f18200f539cf2 GIT binary patch literal 436 zcmV;l0ZaagP)?`jEiRV}hGB>ruBr-M*CpkM2z6b1@4Nvp zGlpS6p663`&uo3j^W1yxoP!VoecuOPQ>SWlyWMa+9%twOvnHaMOxw07iXvW~RDFHU zmtu*oj5W?X5cFcL{cHg5!pBPxoC0QsF$TsM@4YbwS(afOM+hN;axVoSgg{kQN#*jws6#P18W@B%R=JI4lpqIX5w(k6`{1k=7bS6qEyi>-8G^ zFW%CGFw9@XDYcrDn62+E!Lw`Iwtd-g(=@QwMmMJ902f7(+<1KZ0RZfF zyX2>)Lt{fk==*+RF)8I_OiBr6MwVrGJRURWN+~e&lsORvM^T5GrmpKneX!r}7xBQF eO}$~ms`vrJ)ueS~515(&0000 zaB^>EX>4U6ba`-PAZ2)IW&i+q+SONCb}gq4{bv*z0uqR3I0A~bGJ`k#Y*CF*e{H|~ zj9hX-G#!$%-qE|5IThf56K3J#Y>e7`WO&rjwY@0_E|x$V|zOz>zr zB5FCCPBz%s;PS9OOL#I~0iD-V@f5GV1M=a!Z+JRYVxTe|MH-|uXa@cWziL8t;jS#B;cP<`83G@-B93Jd3eeS&^Z zA#!;f4B#Tj7AQjj2>3)nD3Q<1nFxL?07#)ZhsX^GkO&Vb$uWe)E^}d%jc+ooy)2yA zrcs*!gdm$Tu&F@-R*Zu9$*~}Y6uA{eBuSRyq>84HVv>|nPFCbJha9uylyfe*W-Xz_ zA|;hvN~uLF*MJ%*HPu{8tyPtsLXd)&V!vXhaSJUrX{qH_T5Z}9K0S8nspnpL?K*gb zfif}D$fJxpbR)N+QfzUPEp2%#TitXb)@GP-%1krQGV9a}wFA|s=Le|Kff_HQY+t@m z!>r5sWI6(C_(L`G$?MS%0ATsEb!{u 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/RCD/windows_and_grilles.png b/Resources/Textures/Interface/Radial/RCD/windows_and_grilles.png new file mode 100644 index 0000000000000000000000000000000000000000..e51ab094233f2f46c2da73f27f0ab409d4f0abea GIT binary patch literal 1205 zcmV;m1WNmfP)Y}j6o#KWo*6ri?HSt>w~ZS56_KV%NN7t3wIBpYRV4(K%1`JX7Oc8~C1S~jU%{qQ zK@~_<7bWe`R1g9wO&bV-B#k{2&)A8-J;Nd&O+sARUC>R>?#!KY=e_5==e=jd&6_uQ zo+j~}?FE464nW@P;l+y=gV*2v0Nb`(1{#LZ(fXswajssyD%t|jKYEsf?_8oQn?*~d zP<@{U0U;q#uaxmTmrPF&G221YG!#X_CqP0_Yc}w_JbKC?QYfOUDxr8B!!X*((R4+M*j$KOj?&W*kdK)sPB2uGQnony+WB5b>5wps#+ z1~tM#f!~UUtlaFhsU_m97K@|~9Gp1hVmxwRuv2^B;eH0pRrM zQ#<@Zp|Hc((!rO^2&99L?W@npP0C}!7&HDDH{g;g98)_g{_ZmW?~b}W+tX5rvMO=grs(nI&gsU zv*H#yOf z4j4w7uDC`#p;0cZJ+-;vfZJTb}p;YW%KZ%f^TJznUh$<=x z+aXMYVzB^1>UxVoU>q8FCLPc=PChs?Y6U!l4ul8@fb84@6JTKMBvD1>)>ofh zk18tJg{2+-^yJi*fA;(Zq*{e!MB(1u3A0hHbgl!nK#*UnqK3QZQocFkhjXk{Wdgqm_^`oj zfVgPllu8s!CH!sa^Z^3!%WH^`#KgqUIBRr157e6h|1`q9b>ff}QyOO9{vKS{WpH@R zx^;WpJl5CCNN+dChexgPJC?be%`34PY;4UY8g=W#BF5E6obMgV0(Xo`ea_u>8z zT>0p7do$sgp~L)f|27L7HO;y%)ou6vUh{z8V%^zTKQnZ={S3VLF?j*t|FnMrHpwI= T(z-m800000NkvXXu0mjf=vzYl literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/back_hover.png b/Resources/Textures/Interface/Radial/back_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..7378a60fef4f78362e422ba5659a1497a87fb5a3 GIT binary patch literal 495 zcmVfN`NmvpMPcmu$Y=bipvZDB&j-L-&_Pd zZwFPafr$7>1`yPneMi@n2)ErIu=#q6dn3tVuIGdb~DsV_AVeI5K%xx&@~0C`NR&-Kt%FtKCyL8L8CXcVrmuFh8(#NB1r6I zR)&=jLKNGEtbnd5KyT;(K%+ObbWOqLFWMlEy^8`IApnFmU~}}m7+W|2&i86QF##lN z07N8r#*VYyA8t}Wx002ovPDHLkV1lJz)-?bC literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/back_normal.png b/Resources/Textures/Interface/Radial/back_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..2c4a20048c6b4f75a7e8c7415b01e5766c7f4fef GIT binary patch literal 525 zcmV+o0`mQdP)As=6vzK9pJDl*w=4w{UV0tcz1ub+{=6gcH;+AYC-j!KwHO-24WeoLmI8 z$brEi4qftWNX0}9bZ8y~l6U{h{oc#FcjRt5o#HR;9)D~ZKn9QjXh}rna%E>2d92q< z02sx{Ur0M1zvpfSpv3{eF!FeMdR$!6d*1$}Etf01IEYvPTKsrC^Z-DqL)?@e0BG7n zljhz8uwE}=7Y zc-|hVwqnoQH-j^wees1qk*Cv&$K&^0{~q8Qbm`z{o`@+0 P00000NkvXXu0mjfftc^@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/button_hover.png b/Resources/Textures/Interface/Radial/button_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..49885a26c37de185dd882df53e6cdf4fea744873 GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJS)MMAArXg@6C_?X2+mYiQ#0+A zo3^j&s}A>JMuXFzZ>3$l^2swaQ=>1zaav2{z8$+i3F_+f>*h{BojseioTZIf0tn2` zzN$EK{=C2N{rp*1Kc3KE>%4pS3j3^P6KYxF3FM83J;26GS!GbRxVf&sxe_sFP!9iQg6hj{N znm<1#uFGs-?bgoBdieW$zNDLTf(Z*IQP6$>DEl=R679^D-K+w?{pp#{#|}Wxs$F`eiLT0w_6z#d-q#&B z(Lk5l`?_PaU7$v>nuI2`>jB19W3>;awxhUD?WXQ%-@QtNWw}MlfVgS^h?4Zl#E(zQ j$aWO}`8sxRaB%nrkAA6lY1Zx400000NkvXXu0mjfgC?>Q literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Radial/close_normal.png b/Resources/Textures/Interface/Radial/close_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..99417f28295d3d2693f15493944818ffc2f036e8 GIT binary patch literal 391 zcmV;20eJq2P)I(57Hhkii}DTN(@<^A#TT6&V8^@>>}akcneK;u2#>m&g^umein5)qz0R zC%vcB={+DIAmG0vav^2e`jfMUpxt#A`ZF|pLg988|ITnlOor!b0Da#X`NizXZz;Y@ lsJ7#!i)yazI3OS(;2W3Ut002ovPDHLkV1g--t;7HT literal 0 HcmV?d00001 From 93bda6f5936d52bd42007552192454565ff84fa9 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 04:30:53 +0000 Subject: [PATCH 058/133] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 46d239e83ff..50019fb804c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: SonicHDC - changes: - - message: Added reinforced diagonal! - type: Add - id: 5767 - time: '2024-01-22T14:53:19.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24393 - author: Alekshhh changes: - message: Hud eyepatches have been added to BarDrobes, MediDrobes and SecTechs. @@ -3794,3 +3787,14 @@ id: 6266 time: '2024-03-31T04:09:15.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/24287 +- author: chromiumboy + changes: + - message: Additional construction options have been added to the Rapid Construction + Device (RCD), along with a radial style UI for fast navigation + type: Add + - message: The number of charges and the length of time required to build a structure + with the RCD now vary depending on the complexity of the constructed fixture + type: Tweak + id: 6267 + time: '2024-03-31T04:29:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/22799 From 32bd6630ef7d8ba4993b626f73a7401f4ea27aaa Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 31 Mar 2024 15:36:02 +1100 Subject: [PATCH 059/133] Update submodule to 217.2.0 (#26592) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index b28b5ed09b3..99c5b0ad083 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit b28b5ed09b361c4f2da5dd9c3e392b79e6b23c51 +Subproject commit 99c5b0ad08351af347db3a122373f2c4482e94dc From 6b7427e3ee8a4453d30b585e0fc4fa734f5f46d5 Mon Sep 17 00:00:00 2001 From: UBlueberry <161545003+UBlueberry@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:39:40 -0400 Subject: [PATCH 060/133] Southern accent (#26543) * created the AccentComponent and the AccentSystem * word replacement schtuhff * made it a trait fr ongg!!1 * Update Content.Server/Speech/EntitySystems/SouthernAccentSystem.cs --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../Components/SouthernAccentComponent.cs | 8 ++++++ .../EntitySystems/SouthernAccentSystem.cs | 28 +++++++++++++++++++ Resources/Locale/en-US/accent/southern.ftl | 17 +++++++++++ Resources/Locale/en-US/traits/traits.ftl | 3 ++ .../Prototypes/Accents/word_replacements.yml | 9 ++++++ Resources/Prototypes/Traits/neutral.yml | 7 +++++ 6 files changed, 72 insertions(+) create mode 100644 Content.Server/Speech/Components/SouthernAccentComponent.cs create mode 100644 Content.Server/Speech/EntitySystems/SouthernAccentSystem.cs create mode 100644 Resources/Locale/en-US/accent/southern.ftl diff --git a/Content.Server/Speech/Components/SouthernAccentComponent.cs b/Content.Server/Speech/Components/SouthernAccentComponent.cs new file mode 100644 index 00000000000..0c44290086f --- /dev/null +++ b/Content.Server/Speech/Components/SouthernAccentComponent.cs @@ -0,0 +1,8 @@ +using Content.Server.Speech.EntitySystems; + +namespace Content.Server.Speech.Components; + +[RegisterComponent] +[Access(typeof(SouthernAccentSystem))] +public sealed partial class SouthernAccentComponent : Component +{ } diff --git a/Content.Server/Speech/EntitySystems/SouthernAccentSystem.cs b/Content.Server/Speech/EntitySystems/SouthernAccentSystem.cs new file mode 100644 index 00000000000..4d401367cca --- /dev/null +++ b/Content.Server/Speech/EntitySystems/SouthernAccentSystem.cs @@ -0,0 +1,28 @@ +using System.Text.RegularExpressions; +using Content.Server.Speech.Components; + +namespace Content.Server.Speech.EntitySystems; + +public sealed class SouthernAccentSystem : EntitySystem +{ + [Dependency] private readonly ReplacementAccentSystem _replacement = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnAccent); + } + + private void OnAccent(EntityUid uid, SouthernAccentComponent component, AccentGetEvent args) + { + var message = args.Message; + + message = _replacement.ApplyReplacements(message, "southern"); + + //They shoulda started runnin' an' hidin' from me! + message = Regex.Replace(message, @"ing\b", "in'"); + message = Regex.Replace(message, @"\band\b", "an'"); + message = Regex.Replace(message, "d've", "da"); + args.Message = message; + } +}; diff --git a/Resources/Locale/en-US/accent/southern.ftl b/Resources/Locale/en-US/accent/southern.ftl new file mode 100644 index 00000000000..7e1657a3ed7 --- /dev/null +++ b/Resources/Locale/en-US/accent/southern.ftl @@ -0,0 +1,17 @@ +accent-southern-words-1 = you all +accent-southern-words-replace-1 = y'all + +accent-southern-words-2 = you guys +accent-southern-words-replace-2 = y'all + +accent-southern-words-3 = isn't +accent-southern-words-replace-3 = ain't + +accent-southern-words-4 = is not +accent-southern-words-replace-4 = ain't + +accent-southern-words-5 = aren't +accent-southern-words-replace-5 = ain't + +accent-southern-words-6 = are not +accent-southern-words-replace-6 = ain't diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index b82cb0033a7..98f0817f744 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -33,5 +33,8 @@ trait-frontal-lisp-desc = You thpeak with a lithp trait-socialanxiety-name = Social Anxiety trait-socialanxiety-desc = You are anxious when you speak and stutter. +trait-southern-name = Southern Drawl +trait-southern-desc = Are you wonderin' what this does? + trait-snoring-name = Snoring trait-snoring-desc = You will snore while sleeping. diff --git a/Resources/Prototypes/Accents/word_replacements.yml b/Resources/Prototypes/Accents/word_replacements.yml index abcfeded22b..3fadc69010a 100644 --- a/Resources/Prototypes/Accents/word_replacements.yml +++ b/Resources/Prototypes/Accents/word_replacements.yml @@ -367,6 +367,15 @@ accent-cowboy-words-98: accent-cowboy-replacement-98 accent-cowboy-words-99: accent-cowboy-replacement-99 +- type: accent + id: southern + wordReplacements: + accent-southern-words-1: accent-southern-words-replace-1 + accent-southern-words-2: accent-southern-words-replace-2 + accent-southern-words-3: accent-southern-words-replace-3 + accent-southern-words-4: accent-southern-words-replace-4 + accent-southern-words-5: accent-southern-words-replace-5 + accent-southern-words-6: accent-southern-words-replace-6 # For the chat sanitization system - type: accent diff --git a/Resources/Prototypes/Traits/neutral.yml b/Resources/Prototypes/Traits/neutral.yml index 9e7f7655076..ba9bd8d8868 100644 --- a/Resources/Prototypes/Traits/neutral.yml +++ b/Resources/Prototypes/Traits/neutral.yml @@ -16,3 +16,10 @@ - type: MothAccent - type: ReplacementAccent accent: dwarf + +- type: trait + id: Southern + name: trait-southern-name + description: trait-southern-desc + components: + - type: SouthernAccent From 1b94e0156311c918d17c7de4b79fedd328e04efc Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Sun, 31 Mar 2024 00:40:22 -0400 Subject: [PATCH 061/133] Prevent storing liquids in equipped buckets (#24412) * Block access to solutions in equipped spillables. * Stop Drink verb appearing if the solution can't be accessed. --- .../Fluids/EntitySystems/PuddleSystem.Spillable.cs | 12 ++++++++++++ .../Nutrition/EntitySystems/DrinkSystem.cs | 4 ++++ .../Components/BlockSolutionAccessComponent.cs | 11 +++++++++++ .../EntitySystems/SharedSolutionContainerSystem.cs | 9 +++++++++ 4 files changed, 36 insertions(+) create mode 100644 Content.Shared/Chemistry/Components/BlockSolutionAccessComponent.cs diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs index ce5b5b36378..a365b8d0a45 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs @@ -29,6 +29,7 @@ protected override void InitializeSpillable() // Openable handles the event if it's closed SubscribeLocalEvent(SplashOnMeleeHit, after: [typeof(OpenableSystem)]); SubscribeLocalEvent(OnGotEquipped); + SubscribeLocalEvent(OnGotUnequipped); SubscribeLocalEvent(OnOverflow); SubscribeLocalEvent(OnDoAfter); SubscribeLocalEvent(OnAttemptPacifiedThrow); @@ -114,6 +115,9 @@ private void OnGotEquipped(Entity entity, ref GotEquippedEve if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution)) return; + // block access to the solution while worn + AddComp(entity); + if (solution.Volume == 0) return; @@ -122,6 +126,14 @@ private void OnGotEquipped(Entity entity, ref GotEquippedEve TrySplashSpillAt(entity.Owner, Transform(args.Equipee).Coordinates, drainedSolution, out _); } + private void OnGotUnequipped(Entity entity, ref GotUnequippedEvent args) + { + if (!entity.Comp.SpillWorn) + return; + + RemCompDeferred(entity); + } + private void SpillOnLand(Entity entity, ref LandEvent args) { if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution)) diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index 5fb090087a7..036c855dbba 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -410,6 +410,10 @@ private void AddDrinkVerb(Entity entity, ref GetVerbsEvent(ev.User, out var stomachs, body)) return; + // Make sure the solution exists + if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out var solution)) + return; + // no drinking from living drinks, have to kill them first. if (_mobState.IsAlive(entity)) return; diff --git a/Content.Shared/Chemistry/Components/BlockSolutionAccessComponent.cs b/Content.Shared/Chemistry/Components/BlockSolutionAccessComponent.cs new file mode 100644 index 00000000000..182f92d7d33 --- /dev/null +++ b/Content.Shared/Chemistry/Components/BlockSolutionAccessComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Chemistry.Components; + +/// +/// Blocks all attempts to access solutions contained by this entity. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class BlockSolutionAccessComponent : Component +{ +} diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index 6e762aa5984..d71fffcdee6 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -133,6 +133,12 @@ public bool TryGetSolution(Entity container, /// public bool TryGetSolution(Entity container, string? name, [NotNullWhen(true)] out Entity? entity) { + if (TryComp(container, out BlockSolutionAccessComponent? blocker)) + { + entity = null; + return false; + } + EntityUid uid; if (name is null) uid = container; @@ -178,6 +184,9 @@ public bool TryGetSolution(SolutionContainerManagerComponent container, string n if (!Resolve(container, ref container.Comp, logMissing: false)) yield break; + if (HasComp(container)) + yield break; + foreach (var name in container.Comp.Containers) { if (ContainerSystem.GetContainer(container, $"solution@{name}") is ContainerSlot slot && slot.ContainedEntity is { } solutionId) From 1ad509173dc99b9ed577ed5aa696641ebcb354cc Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 04:41:28 +0000 Subject: [PATCH 062/133] Automatic changelog update --- Resources/Changelog/Changelog.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 50019fb804c..02a44e56383 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,19 +1,4 @@ Entries: -- author: Alekshhh - changes: - - message: Hud eyepatches have been added to BarDrobes, MediDrobes and SecTechs. - They're just cooler looking huds. - type: Add - id: 5768 - time: '2024-01-22T14:59:32.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24132 -- author: CrigCrag - changes: - - message: Removed the revolutionaries gamemode pending a rework. - type: Remove - id: 5769 - time: '2024-01-23T02:11:09.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24420 - author: themias changes: - message: Added different footstep sounds for blood, soda and juice @@ -3798,3 +3783,18 @@ id: 6267 time: '2024-03-31T04:29:47.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/22799 +- author: UBlueberry + changes: + - message: Nanotransen is now recruitin' personnel that speak with a Southern drawl. + type: Add + id: 6268 + time: '2024-03-31T04:39:40.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26543 +- author: Tayrtahn + changes: + - message: You can no longer drink out of or put liquids into buckets worn on your + head. + type: Fix + id: 6269 + time: '2024-03-31T04:40:22.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/24412 From 4e618e9387ffe9696e1241f6073aaa03e126832d Mon Sep 17 00:00:00 2001 From: drteaspoon420 <87363733+drteaspoon420@users.noreply.github.com> Date: Sun, 31 Mar 2024 07:59:36 +0300 Subject: [PATCH 063/133] Fix 'Hypopen shouldn't display solution examine text' (#26453) * stealthy hypo * ExaminableSolution hand check when in covert implement. ExaminableSolution now has 'hidden' datafield to enable chemical inspection only in hand. * cleaning code * more cleaning * Hidden datafield renamed to HeldOnly * review --------- Co-authored-by: metalgearsloth --- .../ExaminableSolutionComponent.cs | 6 +++++ .../SharedSolutionContainerSystem.cs | 26 +++++++++++++++++++ .../Objects/Specific/Medical/hypospray.yml | 1 + 3 files changed, 33 insertions(+) diff --git a/Content.Shared/Chemistry/Components/SolutionManager/ExaminableSolutionComponent.cs b/Content.Shared/Chemistry/Components/SolutionManager/ExaminableSolutionComponent.cs index 76e7967db26..1abe81180c8 100644 --- a/Content.Shared/Chemistry/Components/SolutionManager/ExaminableSolutionComponent.cs +++ b/Content.Shared/Chemistry/Components/SolutionManager/ExaminableSolutionComponent.cs @@ -5,4 +5,10 @@ public sealed partial class ExaminableSolutionComponent : Component { [DataField, ViewVariables(VVAccess.ReadWrite)] public string Solution = "default"; + + /// + /// If false then the hidden solution is always visible. + /// + [DataField] + public bool HeldOnly; } diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index d71fffcdee6..5bb97e83ebf 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -13,6 +13,8 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Text; +using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; using Dependency = Robust.Shared.IoC.DependencyAttribute; namespace Content.Shared.Chemistry.EntitySystems; @@ -53,6 +55,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem [Dependency] protected readonly ChemicalReactionSystem ChemicalReactionSystem = default!; [Dependency] protected readonly ExamineSystemShared ExamineSystem = default!; [Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!; + [Dependency] protected readonly SharedHandsSystem Hands = default!; [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!; public override void Initialize() @@ -729,6 +732,9 @@ private void OnExamineSolution(Entity entity, ref E return; } + if (!CanSeeHiddenSolution(entity,args.Examiner)) + return; + var primaryReagent = solution.GetPrimaryReagentId(); if (string.IsNullOrEmpty(primaryReagent?.Prototype)) @@ -825,6 +831,9 @@ private void OnSolutionExaminableVerb(Entity entity return; } + if (!CanSeeHiddenSolution(entity,args.User)) + return; + var target = args.Target; var user = args.User; var verb = new ExamineVerb() @@ -874,5 +883,22 @@ private FormattedMessage GetSolutionExamine(Solution solution) return msg; } + /// + /// Check if examinable solution requires you to hold the item in hand. + /// + private bool CanSeeHiddenSolution(Entity entity, EntityUid examiner) + { + // If not held-only then it's always visible. + if (!entity.Comp.HeldOnly) + return true; + + if (TryComp(examiner, out HandsComponent? handsComp)) + { + return Hands.IsHolding(examiner, entity, out _, handsComp); + } + + return true; + } + #endregion Event Handlers } diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml index abcabd74810..dbc78a84098 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml @@ -417,6 +417,7 @@ solution: hypospray - type: ExaminableSolution solution: hypospray + heldOnly: true # Allow examination only when held in hand. - type: Hypospray onlyAffectsMobs: false - type: UseDelay From 213c075e13a2b8aa5f1995dbf95ca82ef4b2ba92 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 05:00:42 +0000 Subject: [PATCH 064/133] 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 02a44e56383..19c1c744cf1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: themias - changes: - - message: Added different footstep sounds for blood, soda and juice - type: Add - id: 5770 - time: '2024-01-23T04:18:33.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24406 - author: TheShuEd changes: - message: 'Flesh and Rock anom reworked: It should be easier to maintain them now @@ -3798,3 +3791,11 @@ id: 6269 time: '2024-03-31T04:40:22.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/24412 +- author: DrTeaspoon + changes: + - message: Hypopen no longer shows chemical contents when examined, when not held + by the examinee. + type: Fix + id: 6270 + time: '2024-03-31T04:59:36.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26453 From c91ed9685349efd0cb7dc0e541f5349dbdb17f81 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 31 Mar 2024 16:12:52 +1100 Subject: [PATCH 065/133] Revert Paint (#26593) * Revert "Fix build (#26258)" This reverts commit 6de5fbfafbde700d711a566f6a43f05f7a99e455. * Revert "Spray Paint (Review Ready) (#23003)" This reverts commit e4d5e7f1aebfc37b1bc3453fdb39578f3897b6a1. # Conflicts: # Resources/Prototypes/Entities/Structures/Holographic/projections.yml --- Content.Client/Paint/PaintVisualizerSystem.cs | 116 ------- Content.Server/Paint/PaintSystem.cs | 178 ----------- .../EntitySystems/SpawnItemsOnUseSystem.cs | 6 +- Content.Shared/Paint/PaintComponent.cs | 60 ---- Content.Shared/Paint/PaintDoAfterEvent.cs | 9 - Content.Shared/Paint/PaintRemoverComponent.cs | 24 -- .../Paint/PaintRemoverDoAfterEvent.cs | 9 - Content.Shared/Paint/PaintRemoverSystem.cs | 94 ------ Content.Shared/Paint/PaintedComponent.cs | 41 --- Content.Shared/Paint/SharedPaintSystem.cs | 11 - .../SprayPainter/SharedSprayPainterSystem.cs | 3 - Resources/Locale/en-US/paint/paint.ftl | 8 - .../Prototypes/Catalog/Cargo/cargo_fun.yml | 10 - .../Prototypes/Catalog/Fills/Crates/fun.yml | 48 +-- .../Prototypes/Catalog/Fills/Lockers/misc.yml | 4 - .../Markers/Spawners/Random/crates.yml | 1 - .../Markers/Spawners/Random/maintenance.yml | 5 - .../Prototypes/Entities/Mobs/NPCs/carp.yml | 1 - .../Entities/Mobs/NPCs/revenant.yml | 3 - .../Entities/Mobs/Player/guardian.yml | 2 - .../Entities/Objects/Fun/spray_paint.yml | 292 ------------------ .../Objects/Materials/Sheets/glass.yml | 1 - .../Objects/Materials/Sheets/metal.yml | 1 - .../Objects/Materials/Sheets/other.yml | 3 - .../Entities/Objects/Materials/materials.yml | 1 - .../Entities/Objects/Misc/tiles.yml | 3 - .../Objects/Specific/Janitorial/soap.yml | 1 - .../Objects/Weapons/Melee/e_sword.yml | 4 - .../Structures/Decoration/bonfire.yml | 3 - .../Structures/Holographic/projections.yml | 3 - .../Entities/Structures/hydro_tray.yml | 3 - Resources/Prototypes/tags.yml | 3 - .../Textures/Interface/VerbIcons/paint.svg | 39 --- .../Interface/VerbIcons/paint.svg.192dpi.png | Bin 15653 -> 0 bytes .../VerbIcons/paint.svg.192dpi.png.yml | 2 - .../Fun/spraycans.rsi/clown-inhand-left.png | Bin 20773 -> 0 bytes .../Fun/spraycans.rsi/clown-inhand-right.png | Bin 20783 -> 0 bytes .../Objects/Fun/spraycans.rsi/meta.json | 19 +- .../Fun/spraycans.rsi/spray-inhand-left.png | Bin 21199 -> 0 bytes .../Fun/spraycans.rsi/spray-inhand-right.png | Bin 21213 -> 0 bytes .../Objects/Fun/spraycans.rsi/spray_cap.png | Bin 0 -> 246 bytes 41 files changed, 11 insertions(+), 1000 deletions(-) delete mode 100644 Content.Client/Paint/PaintVisualizerSystem.cs delete mode 100644 Content.Server/Paint/PaintSystem.cs delete mode 100644 Content.Shared/Paint/PaintComponent.cs delete mode 100644 Content.Shared/Paint/PaintDoAfterEvent.cs delete mode 100644 Content.Shared/Paint/PaintRemoverComponent.cs delete mode 100644 Content.Shared/Paint/PaintRemoverDoAfterEvent.cs delete mode 100644 Content.Shared/Paint/PaintRemoverSystem.cs delete mode 100644 Content.Shared/Paint/PaintedComponent.cs delete mode 100644 Content.Shared/Paint/SharedPaintSystem.cs delete mode 100644 Resources/Locale/en-US/paint/paint.ftl delete mode 100644 Resources/Prototypes/Entities/Objects/Fun/spray_paint.yml delete mode 100644 Resources/Textures/Interface/VerbIcons/paint.svg delete mode 100644 Resources/Textures/Interface/VerbIcons/paint.svg.192dpi.png delete mode 100644 Resources/Textures/Interface/VerbIcons/paint.svg.192dpi.png.yml delete mode 100644 Resources/Textures/Objects/Fun/spraycans.rsi/clown-inhand-left.png delete mode 100644 Resources/Textures/Objects/Fun/spraycans.rsi/clown-inhand-right.png delete mode 100644 Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-left.png delete mode 100644 Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/spraycans.rsi/spray_cap.png diff --git a/Content.Client/Paint/PaintVisualizerSystem.cs b/Content.Client/Paint/PaintVisualizerSystem.cs deleted file mode 100644 index 8d037811fab..00000000000 --- a/Content.Client/Paint/PaintVisualizerSystem.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System.Linq; -using Robust.Client.GameObjects; -using static Robust.Client.GameObjects.SpriteComponent; -using Content.Shared.Clothing; -using Content.Shared.Hands; -using Content.Shared.Paint; -using Robust.Client.Graphics; -using Robust.Shared.Prototypes; - -namespace Content.Client.Paint; - -public sealed class PaintedVisualizerSystem : VisualizerSystem -{ - /// - /// Visualizer for Paint which applies a shader and colors the entity. - /// - - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly IPrototypeManager _protoMan = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnHeldVisualsUpdated); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnEquipmentVisualsUpdated); - } - - protected override void OnAppearanceChange(EntityUid uid, PaintedComponent component, ref AppearanceChangeEvent args) - { - var shader = _protoMan.Index(component.ShaderName).Instance(); - - if (args.Sprite == null) - return; - - // What is this even doing? It's not even checking what the value is. - if (!_appearance.TryGetData(uid, PaintVisuals.Painted, out bool isPainted)) - return; - - var sprite = args.Sprite; - - foreach (var spriteLayer in sprite.AllLayers) - { - if (spriteLayer is not Layer layer) - continue; - - if (layer.Shader == null) // If shader isn't null we dont want to replace the original shader. - { - layer.Shader = shader; - layer.Color = component.Color; - } - } - } - - private void OnHeldVisualsUpdated(EntityUid uid, PaintedComponent component, HeldVisualsUpdatedEvent args) - { - if (args.RevealedLayers.Count == 0) - return; - - if (!TryComp(args.User, out SpriteComponent? sprite)) - return; - - foreach (var revealed in args.RevealedLayers) - { - if (!sprite.LayerMapTryGet(revealed, out var layer)) - continue; - - sprite.LayerSetShader(layer, component.ShaderName); - sprite.LayerSetColor(layer, component.Color); - } - } - - private void OnEquipmentVisualsUpdated(EntityUid uid, PaintedComponent component, EquipmentVisualsUpdatedEvent args) - { - if (args.RevealedLayers.Count == 0) - return; - - if (!TryComp(args.Equipee, out SpriteComponent? sprite)) - return; - - foreach (var revealed in args.RevealedLayers) - { - if (!sprite.LayerMapTryGet(revealed, out var layer)) - continue; - - sprite.LayerSetShader(layer, component.ShaderName); - sprite.LayerSetColor(layer, component.Color); - } - } - - private void OnShutdown(EntityUid uid, PaintedComponent component, ref ComponentShutdown args) - { - if (!TryComp(uid, out SpriteComponent? sprite)) - return; - - component.BeforeColor = sprite.Color; - var shader = _protoMan.Index(component.ShaderName).Instance(); - - if (!Terminating(uid)) - { - foreach (var spriteLayer in sprite.AllLayers) - { - if (spriteLayer is not Layer layer) - continue; - - if (layer.Shader == shader) // If shader isn't same as one in component we need to ignore it. - { - layer.Shader = null; - if (layer.Color == component.Color) // If color isn't the same as one in component we don't want to change it. - layer.Color = component.BeforeColor; - } - } - } - } -} diff --git a/Content.Server/Paint/PaintSystem.cs b/Content.Server/Paint/PaintSystem.cs deleted file mode 100644 index 892f961d634..00000000000 --- a/Content.Server/Paint/PaintSystem.cs +++ /dev/null @@ -1,178 +0,0 @@ -using Content.Shared.Popups; -using Content.Shared.Paint; -using Content.Shared.Sprite; -using Content.Shared.DoAfter; -using Content.Shared.Interaction; -using Content.Server.Chemistry.Containers.EntitySystems; -using Robust.Shared.Audio.Systems; -using Content.Shared.Humanoid; -using Robust.Shared.Utility; -using Content.Shared.Verbs; -using Content.Shared.SubFloor; -using Content.Server.Nutrition.Components; -using Content.Shared.Inventory; -using Content.Server.Nutrition.EntitySystems; - -namespace Content.Server.Paint; - -/// -/// Colors target and consumes reagent on each color success. -/// -public sealed class PaintSystem : SharedPaintSystem -{ - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; - [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly OpenableSystem _openable = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnInteract); - SubscribeLocalEvent(OnPaint); - SubscribeLocalEvent>(OnPaintVerb); - } - - private void OnInteract(EntityUid uid, PaintComponent component, AfterInteractEvent args) - { - if (!args.CanReach) - return; - - if (args.Target is not { Valid: true } target) - return; - - PrepPaint(uid, component, target, args.User); - } - - private void OnPaintVerb(EntityUid uid, PaintComponent component, GetVerbsEvent args) - { - if (!args.CanInteract || !args.CanAccess) - return; - - var paintText = Loc.GetString("paint-verb"); - - var verb = new UtilityVerb() - { - Act = () => - { - PrepPaint(uid, component, args.Target, args.User); - }, - - Text = paintText, - Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/paint.svg.192dpi.png")) - }; - args.Verbs.Add(verb); - } - private void PrepPaint(EntityUid uid, PaintComponent component, EntityUid target, EntityUid user) - { - - var doAfterEventArgs = new DoAfterArgs(EntityManager, user, component.Delay, new PaintDoAfterEvent(), uid, target: target, used: uid) - { - BreakOnMove = true, - NeedHand = true, - BreakOnHandChange = true - }; - - _doAfterSystem.TryStartDoAfter(doAfterEventArgs); - } - - private void OnPaint(Entity entity, ref PaintDoAfterEvent args) - { - if (args.Target == null || args.Used == null) - return; - - if (args.Handled || args.Cancelled) - return; - - if (args.Target is not { Valid: true } target) - return; - - if (!_openable.IsOpen(entity)) - { - _popup.PopupEntity(Loc.GetString("paint-closed", ("used", args.Used)), args.User, args.User, PopupType.Medium); - return; - } - - if (HasComp(target) || HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("paint-failure-painted", ("target", args.Target)), args.User, args.User, PopupType.Medium); - return; - } - - if (!entity.Comp.Blacklist?.IsValid(target, EntityManager) != true || HasComp(target) || HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("paint-failure", ("target", args.Target)), args.User, args.User, PopupType.Medium); - return; - } - - if (TryPaint(entity, target)) - { - EnsureComp(target, out PaintedComponent? paint); - EnsureComp(target); - - paint.Color = entity.Comp.Color; // set the target color to the color specified in the spray paint yml. - _audio.PlayPvs(entity.Comp.Spray, entity); - paint.Enabled = true; - - if (HasComp(target)) // Paint any clothing the target is wearing. - { - if (_inventory.TryGetSlots(target, out var slotDefinitions)) - { - foreach (var slot in slotDefinitions) - { - if (!_inventory.TryGetSlotEntity(target, slot.Name, out var slotEnt)) - continue; - - if (HasComp(slotEnt.Value) || !entity.Comp.Blacklist?.IsValid(slotEnt.Value, - EntityManager) != true - || HasComp(slotEnt.Value) || - HasComp( - slotEnt.Value)) - { - continue; - } - - EnsureComp(slotEnt.Value, out PaintedComponent? slotpaint); - EnsureComp(slotEnt.Value); - slotpaint.Color = entity.Comp.Color; - _appearanceSystem.SetData(slotEnt.Value, PaintVisuals.Painted, true); - Dirty(slotEnt.Value, slotpaint); - } - } - } - - _popup.PopupEntity(Loc.GetString("paint-success", ("target", args.Target)), args.User, args.User, PopupType.Medium); - _appearanceSystem.SetData(target, PaintVisuals.Painted, true); - Dirty(target, paint); - args.Handled = true; - return; - } - - if (!TryPaint(entity, target)) - { - _popup.PopupEntity(Loc.GetString("paint-empty", ("used", args.Used)), args.User, args.User, PopupType.Medium); - return; - } - } - - private bool TryPaint(Entity reagent, EntityUid target) - { - if (HasComp(target) || HasComp(target)) - return false; - - if (_solutionContainer.TryGetSolution(reagent.Owner, reagent.Comp.Solution, out _, out var solution)) - { - var quantity = solution.RemoveReagent(reagent.Comp.Reagent, reagent.Comp.ConsumptionUnit); - if (quantity > 0)// checks quantity of solution is more than 0. - return true; - - if (quantity < 1) - return false; - } - return false; - } -} diff --git a/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs b/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs index 0b4b13d6e4b..c49bfdec931 100644 --- a/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs +++ b/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs @@ -80,6 +80,11 @@ private void OnUseInHand(EntityUid uid, SpawnItemsOnUseComponent component, UseI _adminLogger.Add(LogType.EntitySpawn, LogImpact.Low, $"{ToPrettyString(args.User)} used {ToPrettyString(uid)} which spawned {ToPrettyString(entityToPlaceInHands.Value)}"); } + if (component.Sound != null) + { + _audio.PlayPvs(component.Sound, uid); + } + component.Uses--; // Delete entity only if component was successfully used @@ -92,7 +97,6 @@ private void OnUseInHand(EntityUid uid, SpawnItemsOnUseComponent component, UseI if (entityToPlaceInHands != null) { _hands.PickupOrDrop(args.User, entityToPlaceInHands.Value); - _audio.PlayPvs(component.Sound, entityToPlaceInHands.Value); } } } diff --git a/Content.Shared/Paint/PaintComponent.cs b/Content.Shared/Paint/PaintComponent.cs deleted file mode 100644 index ad09f4ca730..00000000000 --- a/Content.Shared/Paint/PaintComponent.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Content.Shared.Chemistry.Reagent; -using Content.Shared.FixedPoint; -using Robust.Shared.Audio; -using Content.Shared.Whitelist; -using Robust.Shared.Prototypes; -using Robust.Shared.GameStates; - -namespace Content.Shared.Paint; - -/// -/// Entity when used on another entity will paint target entity. -/// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -[Access(typeof(SharedPaintSystem))] -public sealed partial class PaintComponent : Component -{ - /// - /// Noise made when paint applied. - /// - [DataField] - public SoundSpecifier Spray = new SoundPathSpecifier("/Audio/Effects/spray2.ogg"); - - /// - /// Solution on the entity that contains the paint. - /// - [DataField] - public string Solution = "drink"; - - /// - /// How long the doafter will take. - /// - [DataField] - public int Delay = 2; - - /// - /// Reagent that will be used as paint. - /// - [DataField, AutoNetworkedField] - public ProtoId Reagent = "SpaceGlue"; - - /// - /// Color that the painting entity will instruct the painted entity to be. - /// - [DataField, AutoNetworkedField] - public Color Color = Color.FromHex("#c62121"); - - [DataField, ViewVariables(VVAccess.ReadWrite)] - public EntityWhitelist? Blacklist; - /// - /// Reagent consumption per use. - /// - [DataField] - public FixedPoint2 ConsumptionUnit = FixedPoint2.New(5); - - /// - /// Duration per unit - /// - [DataField] - public TimeSpan DurationPerUnit = TimeSpan.FromSeconds(6); -} diff --git a/Content.Shared/Paint/PaintDoAfterEvent.cs b/Content.Shared/Paint/PaintDoAfterEvent.cs deleted file mode 100644 index 0851f1541b4..00000000000 --- a/Content.Shared/Paint/PaintDoAfterEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Shared.DoAfter; -using Robust.Shared.Serialization; - -namespace Content.Shared.Paint; - -[Serializable, NetSerializable] -public sealed partial class PaintDoAfterEvent : SimpleDoAfterEvent -{ -} diff --git a/Content.Shared/Paint/PaintRemoverComponent.cs b/Content.Shared/Paint/PaintRemoverComponent.cs deleted file mode 100644 index 54d0ed7a71b..00000000000 --- a/Content.Shared/Paint/PaintRemoverComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Audio; - -namespace Content.Shared.Paint; - -/// -/// Removes paint from an entity that was painted with spray paint. -/// -[RegisterComponent, NetworkedComponent] -[Access(typeof(PaintRemoverSystem))] -public sealed partial class PaintRemoverComponent : Component -{ - /// - /// Sound when target is cleaned. - /// - [DataField] - public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Effects/Fluids/watersplash.ogg"); - - /// - /// DoAfter wait time. - /// - [DataField] - public float CleanDelay = 2f; -} diff --git a/Content.Shared/Paint/PaintRemoverDoAfterEvent.cs b/Content.Shared/Paint/PaintRemoverDoAfterEvent.cs deleted file mode 100644 index 940b1aa513c..00000000000 --- a/Content.Shared/Paint/PaintRemoverDoAfterEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Shared.DoAfter; -using Robust.Shared.Serialization; - -namespace Content.Shared.Paint; - -[Serializable, NetSerializable] -public sealed partial class PaintRemoverDoAfterEvent : SimpleDoAfterEvent -{ -} diff --git a/Content.Shared/Paint/PaintRemoverSystem.cs b/Content.Shared/Paint/PaintRemoverSystem.cs deleted file mode 100644 index efc1ded0677..00000000000 --- a/Content.Shared/Paint/PaintRemoverSystem.cs +++ /dev/null @@ -1,94 +0,0 @@ -using Content.Shared.Popups; -using Content.Shared.Interaction; -using Content.Shared.DoAfter; -using Content.Shared.Verbs; -using Content.Shared.Sprite; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Timing; - -namespace Content.Shared.Paint; - -/// -/// Removes paint from an entity. -/// -public sealed class PaintRemoverSystem : SharedPaintSystem -{ - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnInteract); - SubscribeLocalEvent(OnDoAfter); - SubscribeLocalEvent>(OnPaintRemoveVerb); - } - - // When entity is painted, remove paint from that entity. - private void OnInteract(EntityUid uid, PaintRemoverComponent component, AfterInteractEvent args) - { - if (args.Handled) - return; - - if (!args.CanReach || args.Target is not { Valid: true } target || !HasComp(target)) - return; - - _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new PaintRemoverDoAfterEvent(), uid, args.Target, uid) - { - BreakOnMove = true, - BreakOnDamage = true, - MovementThreshold = 1.0f, - }); - args.Handled = true; - } - - private void OnDoAfter(EntityUid uid, PaintRemoverComponent component, DoAfterEvent args) - { - if (args.Cancelled || args.Handled || args.Args.Target == null) - return; - - if (args.Target is not { Valid: true } target) - return; - - if (!TryComp(target, out PaintedComponent? paint)) - return; - - paint.Enabled = false; - _audio.PlayPredicted(component.Sound, target, args.User); - _popup.PopupClient(Loc.GetString("paint-removed", ("target", target)), args.User, args.User, PopupType.Medium); - _appearanceSystem.SetData(target, PaintVisuals.Painted, false); - RemComp(target); - Dirty(target, paint); - - args.Handled = true; - } - - private void OnPaintRemoveVerb(EntityUid uid, PaintRemoverComponent component, GetVerbsEvent args) - { - if (!args.CanInteract || !args.CanAccess) - return; - - var paintremovalText = Loc.GetString("paint-remove-verb"); - - var verb = new UtilityVerb() - { - Act = () => - { - - _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new PaintRemoverDoAfterEvent(), uid, args.Target, uid) - { - BreakOnMove = true, - BreakOnDamage = true, - MovementThreshold = 1.0f, - }); - }, - - Text = paintremovalText - }; - - args.Verbs.Add(verb); - } -} diff --git a/Content.Shared/Paint/PaintedComponent.cs b/Content.Shared/Paint/PaintedComponent.cs deleted file mode 100644 index a6ee7377e11..00000000000 --- a/Content.Shared/Paint/PaintedComponent.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared.Paint; - -/// -/// Component applied to target entity when painted. -/// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -public sealed partial class PaintedComponent : Component -{ - /// - /// Color of the paint. - /// - [DataField, AutoNetworkedField] - public Color Color = Color.FromHex("#2cdbd5"); - - /// - /// Used to remove the color when component removed. - /// - [DataField, AutoNetworkedField] - public Color BeforeColor; - - /// - /// If paint is enabled. - /// - [DataField, AutoNetworkedField] - public bool Enabled; - - /// - /// Name of the shader. - /// - [DataField, AutoNetworkedField] - public string ShaderName = "Greyscale"; -} - -[Serializable, NetSerializable] -public enum PaintVisuals : byte -{ - Painted, -} diff --git a/Content.Shared/Paint/SharedPaintSystem.cs b/Content.Shared/Paint/SharedPaintSystem.cs deleted file mode 100644 index 10185817b86..00000000000 --- a/Content.Shared/Paint/SharedPaintSystem.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Content.Shared.Paint; - -/// -/// Colors target and consumes reagent on each color success. -/// -public abstract class SharedPaintSystem : EntitySystem -{ - public virtual void UpdateAppearance(EntityUid uid, PaintedComponent? component = null) - { - } -} diff --git a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs index feb1cebb8e1..b238c6fc722 100644 --- a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs +++ b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs @@ -4,7 +4,6 @@ using Content.Shared.Doors.Components; using Content.Shared.Interaction; using Content.Shared.Popups; -using Content.Shared.Paint; using Content.Shared.SprayPainter.Components; using Content.Shared.SprayPainter.Prototypes; using Robust.Shared.Audio.Systems; @@ -130,8 +129,6 @@ private void OnAirlockInteract(Entity ent, ref Intera return; } - RemComp(ent); - var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.AirlockSprayTime, new SprayPainterDoorDoAfterEvent(sprite, style.Department), args.Used, target: ent, used: args.Used) { BreakOnMove = true, diff --git a/Resources/Locale/en-US/paint/paint.ftl b/Resources/Locale/en-US/paint/paint.ftl deleted file mode 100644 index 200b1f6e3f3..00000000000 --- a/Resources/Locale/en-US/paint/paint.ftl +++ /dev/null @@ -1,8 +0,0 @@ -paint-success = {THE($target)} has been covered in paint! -paint-failure = Can't cover {THE($target)} in paint! -paint-failure-painted = {THE($target)} is already covered in paint! -paint-empty = {THE($used)} is empty! -paint-removed = You clean off the paint! -paint-closed = You must open {THE($used)} first! -paint-verb = Paint -paint-remove-verb = Remove Paint diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml b/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml index c29458a1ee5..61085f13b94 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml @@ -68,16 +68,6 @@ category: cargoproduct-category-name-fun group: market -- type: cargoProduct - id: FunSprayPaints - icon: - sprite: Objects/Fun/spraycans.rsi - state: death2_cap - product: CrateFunSprayPaints - cost: 2000 - category: Fun - group: market - - type: cargoProduct id: FunParty icon: diff --git a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml index 17018cb9e60..72cf6c447cc 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml @@ -290,21 +290,14 @@ contents: - id: SnapPopBox - id: CrazyGlue + amount: 2 - id: PlasticBanana - - id: FunnyPaint - orGroup: Paint - prob: 0.5 - - id: FunnyPaintYellow - orGroup: Paint - prob: 0.5 - id: WhoopieCushion - id: ToyHammer - id: MrChips - prob: 0.5 - orGroup: Dummy + orGroup: GiftPool - id: MrDips - prob: 0.5 - orGroup: Dummy + orGroup: Giftpool - id: RevolverCapGun - id: BalloonNT - id: ClothingShoesClownLarge @@ -337,41 +330,6 @@ amount: 15 prob: 0.05 -- type: entity - id: CrateFunSprayPaints - name: spray paint crate - description: a crate filled with spray paint. - parent: CratePlastic - suffix: Spray Paint - components: - - type: StorageFill - contents: - - id: SprayPaintBlue - amount: 2 - prob: 0.33 - - id: SprayPaintRed - amount: 2 - prob: 0.33 - - id: SprayPaintOrange - amount: 2 - prob: 0.33 - - id: SprayPaintBlack - amount: 2 - prob: 0.33 - - id: SprayPaintGreen - amount: 2 - prob: 0.33 - - id: SprayPaintPurple - amount: 2 - prob: 0.33 - - id: SprayPaintWhite - amount: 2 - prob: 0.33 - - id: DeathPaint - amount: 2 - - id: DeathPaintTwo - amount: 2 - - type: entity name: dartboard box set description: A box with everything you need for a fun game of darts. diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index 850fb912734..25078dbe57f 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -145,10 +145,6 @@ prob: 0.25 - id: StrangePill prob: 0.20 - - id: DeathPaint - prob: 0.05 - - id: DeathPaintTwo - prob: 0.05 - id: DrinkMopwataBottleRandom prob: 0.20 - id: ModularReceiver diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/crates.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/crates.yml index 883182aae8d..ae7e5bcf762 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/crates.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/crates.yml @@ -44,7 +44,6 @@ - CrateMaterialPlastic - CrateMaterialWood - CrateMaterialPlasteel - - CrateFunSprayPaints - CrateFunArtSupplies - CrateEngineeringCableLV - CrateEngineeringCableMV diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml index 450b501d1aa..6419c1aaa10 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml @@ -173,12 +173,7 @@ - MaterialCloth10 - MaterialWoodPlank10 - ResearchDisk - - DeathPaint - Plunger - - SprayPaintBlue - - SprayPaintRed - - SprayPaintGreen - - SprayPaintOrange - TechnologyDisk - PowerCellMedium - PowerCellSmall diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml index 3e6c603626b..73082674736 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml @@ -70,7 +70,6 @@ tags: - Carp - DoorBumpOpener - - NoPaint - type: ReplacementAccent accent: genericAggressive - type: Speech diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml index 4a35f71ac05..68ebf52dc06 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml @@ -93,6 +93,3 @@ - RevenantTheme - type: Speech speechVerb: Ghost - - type: Tag - tags: - - NoPaint diff --git a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml index 9f0d54ee64a..c7cd40988d4 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml @@ -104,7 +104,6 @@ - type: Tag tags: - CannotSuicide - - NoPaint # From the uplink injector - type: entity @@ -213,7 +212,6 @@ tags: - CannotSuicide - FootstepSound - - NoPaint - type: Inventory templateId: holoclown - type: Hands diff --git a/Resources/Prototypes/Entities/Objects/Fun/spray_paint.yml b/Resources/Prototypes/Entities/Objects/Fun/spray_paint.yml deleted file mode 100644 index 1b417f6cde0..00000000000 --- a/Resources/Prototypes/Entities/Objects/Fun/spray_paint.yml +++ /dev/null @@ -1,292 +0,0 @@ -# Base Paints -- type: entity - parent: BaseItem - id: PaintBase - name: spray paint - description: A tin of spray paint. - noSpawn: true - components: - - type: Appearance - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - state: clown_cap - layers: - - state: clown_cap - map: ["enum.OpenableVisuals.Layer"] - - type: Paint - consumptionUnit: 10 - blacklist: - tags: - - NoPaint - - type: Item - sprite: Objects/Fun/spraycans.rsi - heldPrefix: spray - - type: SolutionContainerManager - solutions: - drink: - maxVol: 50 - reagents: - - ReagentId: SpaceGlue - Quantity: 50 - - type: TrashOnSolutionEmpty - solution: drink - - type: Sealable - - type: Openable - sound: - path: /Audio/Effects/pop_high.ogg - closeable: true - closeSound: - path: /Audio/Effects/pop_high.ogg - -# Paints - -# funnypaint -- type: entity - parent: PaintBase - id: FunnyPaint - name: funny paint - description: A tin of funny paint, manufactured by Honk! Co. - components: - - type: Paint - color: "#fa74df" - - type: Item - sprite: Objects/Fun/spraycans.rsi - heldPrefix: clown - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "clown"} - False: {state: "clown_cap"} - -- type: entity - parent: PaintBase - id: FunnyPaintYellow - name: funny paint - description: A tin of funny paint, manufactured by Honk! Co. - components: - - type: Paint - color: "#d5e028" - - type: Item - sprite: Objects/Fun/spraycans.rsi - heldPrefix: clown - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - state: clown2_cap - layers: - - state: clown2_cap - map: ["enum.OpenableVisuals.Layer"] - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "clown2"} - False: {state: "clown2_cap"} - -#death paint -- type: entity - parent: PaintBase - id: DeathPaint - components: - - type: Paint - color: "#ff20c8" - - type: Item - sprite: Objects/Fun/spraycans.rsi - heldPrefix: spray - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - state: death_cap - layers: - - state: death_cap - map: ["enum.OpenableVisuals.Layer"] - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "death"} - False: {state: "death_cap"} - -- type: entity - parent: PaintBase - id: DeathPaintTwo - components: - - type: Paint - color: "#ff2020" - - type: Item - sprite: Objects/Fun/spraycans.rsi - heldPrefix: spray - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - state: death2_cap - layers: - - state: death2_cap - map: ["enum.OpenableVisuals.Layer"] - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "death2"} - False: {state: "death2_cap"} - -#Sprays - -#Blue -- type: entity - parent: PaintBase - id: SprayPaintBlue - suffix: Blue - components: - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - layers: - - state: spray - map: ["Base"] - - state: spray_cap_colors - map: ["enum.OpenableVisuals.Layer"] - color: "#5890f7" - - type: Paint - color: "#5890f7" - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "spray_colors" , color: "#5890f7"} - False: {state: "spray_cap_colors" , color: "#5890f7"} - -#Red -- type: entity - parent: PaintBase - id: SprayPaintRed - suffix: Red - components: - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - layers: - - state: spray - map: ["Base"] - - state: spray_cap_colors - map: ["enum.OpenableVisuals.Layer"] - color: "#ff3b3b" - - type: Paint - color: "#ff3b3b" - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "spray_colors" , color: "#ff3b3b"} - False: {state: "spray_cap_colors" , color: "#ff3b3b"} - -#Green -- type: entity - parent: PaintBase - id: SprayPaintGreen - suffix: Green - components: - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - layers: - - state: spray - map: ["Base"] - - state: spray_cap_colors - map: ["enum.OpenableVisuals.Layer"] - color: "#73f170" - - type: Paint - color: "#73f170" - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "spray_colors" , color: "#73f170"} - False: {state: "spray_cap_colors" , color: "#73f170"} - -#Black -- type: entity - parent: PaintBase - id: SprayPaintBlack - suffix: Black - components: - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - layers: - - state: spray - map: ["Base"] - - state: spray_cap_colors - map: ["enum.OpenableVisuals.Layer"] - color: "#3a3a3a" - - type: Paint - color: "#3a3a3a" - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "spray_colors" , color: "#3a3a3a"} - False: {state: "spray_cap_colors" , color: "#3a3a3a"} - -#Orange -- type: entity - parent: PaintBase - id: SprayPaintOrange - suffix: Orange - components: - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - layers: - - state: spray - map: ["Base"] - - state: spray_cap_colors - map: ["enum.OpenableVisuals.Layer"] - color: "#f6a44b" - - type: Paint - color: "#f6a44b" - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "spray_colors" , color: "#f6a44b"} - False: {state: "spray_cap_colors" , color: "#f6a44b"} - -#Purple -- type: entity - parent: PaintBase - id: SprayPaintPurple - suffix: Purple - components: - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - layers: - - state: spray - map: ["Base"] - - state: spray_cap_colors - map: ["enum.OpenableVisuals.Layer"] - color: "#c063f5" - - type: Paint - color: "#c063f5" - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "spray_colors" , color: "#c063f5"} - False: {state: "spray_cap_colors" , color: "#c063f5"} - -#White -- type: entity - parent: PaintBase - id: SprayPaintWhite - suffix: White - components: - - type: Sprite - sprite: Objects/Fun/spraycans.rsi - layers: - - state: spray - map: ["Base"] - - state: spray_cap_colors - map: ["enum.OpenableVisuals.Layer"] - color: "#f2f2f2" - - type: Paint - color: "#f2f2f2" - - type: GenericVisualizer - visuals: - enum.OpenableVisuals.Opened: - enum.OpenableVisuals.Layer: - True: {state: "spray_colors" , color: "#f2f2f2"} - False: {state: "spray_cap_colors" , color: "#f2f2f2"} diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml index 2e0eec7a658..59d8ed19220 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml @@ -15,7 +15,6 @@ - type: Tag tags: - Sheet - - NoPaint - type: Material - type: Damageable damageContainer: Inorganic diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml index 3a887848bf5..82b9f62837a 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml @@ -15,7 +15,6 @@ tags: - Sheet - Metal - - NoPaint - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml index 9dc87a9117d..dfb51336289 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml @@ -12,7 +12,6 @@ - type: Tag tags: - Sheet - - NoPaint - type: Damageable damageContainer: Inorganic - type: Destructible @@ -111,7 +110,6 @@ - type: Tag tags: - Sheet - - NoPaint - type: entity parent: SheetPlasma @@ -134,7 +132,6 @@ tags: - Plastic - Sheet - - NoPaint - type: Material - type: PhysicalComposition materialComposition: diff --git a/Resources/Prototypes/Entities/Objects/Materials/materials.yml b/Resources/Prototypes/Entities/Objects/Materials/materials.yml index 2bfd409c300..d11df5d94e8 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/materials.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/materials.yml @@ -12,7 +12,6 @@ - type: Tag tags: - RawMaterial - - NoPaint - type: Damageable damageContainer: Inorganic - type: Destructible diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index 6f351ee9db2..3b2e4cd8f1b 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -15,9 +15,6 @@ Blunt: 5 - type: Stack count: 1 - - type: Tag - tags: - - NoPaint - type: Damageable damageContainer: Inorganic - type: Destructible diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml index 56786057d57..5678de6bafc 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml @@ -62,7 +62,6 @@ solution: soap - type: DeleteOnSolutionEmpty solution: soap - - type: PaintRemover - type: FlavorProfile flavors: - clean diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index c5ea3595409..13c8b9cb25c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -78,9 +78,6 @@ malus: 0 - type: Reflect enabled: false - - type: Tag - tags: - - NoPaint - type: IgnitionSource temperature: 700 @@ -159,7 +156,6 @@ - type: Tag tags: - Write - - NoPaint - type: DisarmMalus malus: 0 diff --git a/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml b/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml index f82fe8b51bb..7777153bbac 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml @@ -31,9 +31,6 @@ sound: path: /Audio/Ambience/Objects/fireplace.ogg - type: AlwaysHot - - type: Tag - tags: - - NoPaint - type: entity id: LegionnaireBonfire diff --git a/Resources/Prototypes/Entities/Structures/Holographic/projections.yml b/Resources/Prototypes/Entities/Structures/Holographic/projections.yml index a62af015f72..aaa0ed716d7 100644 --- a/Resources/Prototypes/Entities/Structures/Holographic/projections.yml +++ b/Resources/Prototypes/Entities/Structures/Holographic/projections.yml @@ -25,9 +25,6 @@ behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] - - type: Tag - tags: - - NoPaint - type: entity id: HoloFan diff --git a/Resources/Prototypes/Entities/Structures/hydro_tray.yml b/Resources/Prototypes/Entities/Structures/hydro_tray.yml index 43b8bd197a5..1ab1fd5b2fd 100644 --- a/Resources/Prototypes/Entities/Structures/hydro_tray.yml +++ b/Resources/Prototypes/Entities/Structures/hydro_tray.yml @@ -92,9 +92,6 @@ - type: GuideHelp guides: - Botany - - type: Tag - tags: - - NoPaint - type: entity parent: hydroponicsTray diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 8f0038915de..303a9b7c87b 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -911,9 +911,6 @@ - type: Tag id: NoBlockAnchoring -- type: Tag - id: NoPaint - - type: Tag id: NozzleBackTank diff --git a/Resources/Textures/Interface/VerbIcons/paint.svg b/Resources/Textures/Interface/VerbIcons/paint.svg deleted file mode 100644 index 78a56e3570a..00000000000 --- a/Resources/Textures/Interface/VerbIcons/paint.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - diff --git a/Resources/Textures/Interface/VerbIcons/paint.svg.192dpi.png b/Resources/Textures/Interface/VerbIcons/paint.svg.192dpi.png deleted file mode 100644 index b7bd88245f2ab069dbf5a39850a866af0c5dc174..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15653 zcmeI3Ym6J!6@c$%At6fwD^Z{%uuL393Tejkve)auHrd!ob^#~b&7$l^6=XbfZ4a?$ zoSE^icLQO0mqGyXgEvtlDvC%zAkn5$c`8a@KR`fGMTH1e8(?XniJ&}$1l*bN^RD+2 zK_vcM%O3mObI&>Ve&?R!`7^f-ZQQW9Ye^RXVDaEUY8ZX5aX+WcN7pCx9q*v8g{6Tp z8-NRLbw3?<9X$>KxOkO5GU|+`FPBxb7*sSf1A~=f3C#u|c1fkAsN12#WMED=;(`DD z@x=h6Yw^IAaGFn-k}$6iOj&UA)W#8YYP%ZM0+%GZVig%VC_+bJD#e0f%awS*^DCp@ z?l2c%yeZE1c%a`6$c(0kn51bzCLHW#RlYaEM4~|<9EpS?s~M3O!W=Jg63>c~9OC7W z$W&i}L>KxMv$U){oLXNEhopEQ?>Hry<0d91f)i5Ev~rvfjYc_Mg|JMlok4OD+s_f;&_0~vOe9iqrploBTdd9Iz2rq*pr zWvk#VO;b5ofJJCHHnJ1i+LiLAW7>Ihj*xZroPkkJhV@+DvFq{MuG#YDv|R%7kWQ1W zi^aBugabg z<-_PbBJ|3l^fO+NdA`YmKMiw`HZ?svS#uC#dC_&i^Kv*OhoW13^QK9-IjTHOag^_J z&^)clg{I0`(<&-XLN6*g$d!y-jB5hvH6m++G<$_}5;(-dQ z!K_j)IDtem$umRcOhH$flvz}CgGoV6R~4^5tLI5|^Q4bMRC+}d9SOJEOP-D9nQ&9k zGQAYXIJcw=dg~he(blDTdz-b5SiQ{Tq**X6L>nfg7}svNiAcTl%>G|h4MqM=|}ffstSqQ-{9s>Eth6fBd;s;r=9#7L-5vzZbuw8|-Se054j$BE zT+3)#&A8*Rs9&9t~OehxY&!OXSB8|`Y+G>Wo zZ(n&QB@4DP{=;%`VkPxGa?qwn|9Ba2S^Lq|JnFKj3vsoBTN^mBr1`0At*Ba+&aS|i z`?oBk>P3gi>)Ht+#?2aQME2*8x>Z;xdsQYxMIOC4!M#>(oAtQu*mu}A>v7wL=@o|V zB*bbsY`DqFy?gNg3pS%$WtivVor!L$??281k9Tx33&mg2&IkVT4%%m>p_To@3tNO6(*U|Na`140%lE;1iXOL1X9 z$Rfo>=7VV|E({1+q`1g@FfGM}0U?VN7nu*HrMNI4WRc<`^TD(f7Y2kZQe0#{n3m$g zfRIIsi_8bpQd}4ivPf}}`CwX#3j;zHDK0V}OiOWLK*%D+MdpKPDJ~2MS){ngd@wD= zg#jUp6c?Efrlq(rAY_r^BJ;tt6c+}BEK*!#KA4u`!hn!PBCf8+1F+CQPrXi{$6Mb! zmwOC78q25y!)XAfRsb-4BLH81gT7|~DDwb(xD5dLegMuj@44#GdH_1ggQ@`d;nH-J03Zr|Ct&;EP&wwn%Znb)2De;!;OJaeCP>1qFb`z&qb zg)8@-^X`%LZ){q!ck-1BpXgZqbmESl&P`|DK7V@ADIfoI-Z{G~r|l^uJHXQD`dFJrr zYaZS8?4R>VcFlroKUneXvSrhEzG~gOYt5<*A7mc6YOA>XZxXv~Xw}p^pRE1e-k*Ri zrS~5^-uH*Y_pQ)xy78qiuK-iITc3L1-+S`XlSgL0{?D<$Ja+2N$%Dtaf!HnY9TInZ z)bZ|<2bV7FeDvz;xy2v6_Sfs5(R#+um`?6F@bIGFb{so;PtTRY>VL&{pML1-rJp4h p2EWO5KELuJFpt6C3+_AuIgtw=ow{Om$WDZhO1a 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 Lg-7S^{K9_#hzGN8 diff --git a/Resources/Textures/Objects/Fun/spraycans.rsi/clown-inhand-right.png b/Resources/Textures/Objects/Fun/spraycans.rsi/clown-inhand-right.png deleted file mode 100644 index 27b68e2cfe5d2b364b379707b887bdfcb115eeb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20783 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 diff --git a/Resources/Textures/Objects/Fun/spraycans.rsi/meta.json b/Resources/Textures/Objects/Fun/spraycans.rsi/meta.json index f34820cec45..0f883ee2801 100644 --- a/Resources/Textures/Objects/Fun/spraycans.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/spraycans.rsi/meta.json @@ -58,6 +58,9 @@ { "name": "spray" }, + { + "name": "spray_cap" + }, { "name": "spray_cap_colors" }, @@ -67,22 +70,6 @@ { "name": "equipped-BELT", "directions": 4 - }, - { - "name": "clown-inhand-right", - "directions": 4 - }, - { - "name": "clown-inhand-left", - "directions": 4 - }, - { - "name": "spray-inhand-right", - "directions": 4 - }, - { - "name": "spray-inhand-left", - "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-left.png b/Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-left.png deleted file mode 100644 index ad3ad959de4138922ea09c84316ad9369fedf403..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21199 zcmeI4eT*bU6~G%61ILFZ5K;bdTr`SUJN3~YRm1GS-tL`u35)0667CKYuDYsvuVr_7 zn3=oXJBb1O!)S=2Mx!SZC4MAEi5mFBaK=P}e+c}c5s4o$l3-MVL5+!GqWF4xW~ygy z_O6-RC?q|}?$&m{_p18$>f^mPllj=b8{d1$jw^RKj&sT0J&Vim_xkkbg4e_EZ^pO( z7XDn=+H)H5wvKAci@W>X!GCizN4nisty(^2iX)s2&8CABNV z6sw!ERVA~yI!Q^Zp-$^)d#yJ#t*U0ttea-H1A1hlUh7EGO*%)CExH`u-m+ux$7;) zVJE$_B?Pk>Z=sgn$<%stDsy!mpw$-IMs}0-N|LNKvgx{S;ET$_LT`Fj-gR@_)X7HY z>Uwot#ZDY+%`D2UX;2Jvkzj=B3h`>ptC8RuOllbCjAW8yHkJ|{N5=;?7$Ou=u|oVB zp*6w}W$HPhadlXOT6X2x4CKXfCbX(WNqb#(8}YhaHPu#gwO+LsbCSbJSzAcfk~WOU zG+4bl>GDLa#Y7!VSH*l&nGU$1lXDF?sJHHS%GJ@UgBxpcgY4!zcM6Z~a+%;e7sx!_ zIq!SEhv(;oPj~ZrHC^E#XnXJby?;I1wsfLmU9+RcK9`=N24NF&M2Im+a_S=?IK@n0 z#)LbqX{Ds0U`zQN<_0kug&2M*-(Vt`%dt>Xnq~nDa3lmWzKSx_TzMW+t}vWN+NU&} z(zGk7#*s8gxIm>hY929)A`(hXv1>4%7Kq0~!hMR730!x3M;&OUSx6+9R$iaA0X{0= zfuuB&jL<282(664J_Xh$*xpeKqgfP!)zFBrZ`u%o!a&2bP$nX8S~n`J^x zp;j(3A=1B#?)T*)<$2$}XMFp=>+;Y!2; z+22&*6u?MuDLmM4xsiIx84^)K8HX9-`T)GQsY)CHp9mX|F-3!LYSSKtg9^4M7+`;) zzx+|J#t<82M`N!ETaV<@_Yw2_2)UsP2O{PSaiWcc(~`UV?1umZPqP{hThDi;;=&-G z1tDU-3rDPy9@21@D3?eTndq!s2gbq>he}5T$;b$#JvV}*Is_;AAsFMX;lwL!Eb@pJ zF7^=(xkR3XQwdH_j(qAXjTKjc5XFs24l$r2$OknTUOGnum!ODY8YxTxC&WvWf|Ts) zXz-Jdtj-iVrE%!eQf+Uu)0Gfe?Okl7q+-Ah@r&{`48`|!fUtB!Bp6jFfOs+T!yr)F z??soR>Nb4pn;!HIrnd;8VE5#a2ABALZw7*Yg@!nX7&r6{_&WlM**J5?PV4A>?HCeM zxt5a|cs)?y4CQGQ1rbaf=6Wb}!Syh$m`j*zL?8!UOfYj=S9{_#1U)_)EA~Rr{pR-3 zt2PAfrI0Pz?wB+|nwuG9NW+{ni#PjGXD`vJNtGs_4V9)H2A259RXePqW<3*#Pj_cA z1gEKQ-N(ie(>zn!l9}pBZEd8JMBNS9HuI|x+84>FGOMF%@c> zU_KX~z!8SpNVzD7fk9RvTZ_*j2svk`u|RPG6(&R#WL!LiKwd~6PC*rTjC)}erM0rD zoR(HS-L&e-+!~|hPP&ad=~*_ml2&EFnZJ$Po-{Oxg7t9es1?l$24qTXP}F6Xq;x0$ndL-)f+D@?=pJO;(b@yp{H z`{GTrc3?4vcSAdAama_Dsi!l3W1`~0trT-N`ij}I$$bc-=a}p&e8d7i-BBHWyd&o` zyHjp^%RbSzo3;kaPArQ8trAxONLHl8WmyzxmADE(vLYoe%c4N5#8m*26)AC976n=* zt^$y(NQuj`D9|c#6@X+#N?evjfmVsD03<6?;<79Xv`SnBAX$+Tmt|3)RpKfD$%>S? zEQ)qoTJ!ro;&O~wa+`w70IV>eQY<#zOZ+3er5Al&;RnfmpqCu zUi|6)d*4Fux$?-Tp*^Ug1S?#}!7{0M*M3s<-6 zm)Aji^mTaopZYrfLI}hLeU!MjguK)l5 diff --git a/Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-right.png b/Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-right.png deleted file mode 100644 index 353e47c56fa71b948a6443d4a3a850de2edcb5c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21213 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 literal 0 HcmV?d00001 From 90a880a9bea64601db77d5bbb2b184d5371dc082 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Sat, 30 Mar 2024 22:20:48 -0700 Subject: [PATCH 066/133] Fix: Prevent single-use hyposprays from getting the toggle draw verb (#26595) Prevent single-use hyposprays from getting the toggle draw verb Co-authored-by: Plykiya --- Content.Shared/Chemistry/Components/HyposprayComponent.cs | 7 +++++++ .../Chemistry/EntitySystems/SharedHypospraySystem.cs | 2 +- .../Entities/Objects/Specific/Medical/hypospray.yml | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Chemistry/Components/HyposprayComponent.cs b/Content.Shared/Chemistry/Components/HyposprayComponent.cs index 05d202aaaa3..05ef84bbaf7 100644 --- a/Content.Shared/Chemistry/Components/HyposprayComponent.cs +++ b/Content.Shared/Chemistry/Components/HyposprayComponent.cs @@ -30,4 +30,11 @@ public sealed partial class HyposprayComponent : Component [AutoNetworkedField] [DataField(required: true)] public bool OnlyAffectsMobs = false; + + /// + /// Whether or not the hypospray is able to draw from containers or if it's a single use + /// device that can only inject. + /// + [DataField] + public bool InjectOnly = false; } diff --git a/Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs index f91e5621f0a..b647d33c98c 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedHypospraySystem.cs @@ -27,7 +27,7 @@ public override void Initialize() // private void AddToggleModeVerb(Entity entity, ref GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract || args.Hands == null) + if (!args.CanAccess || !args.CanInteract || args.Hands == null || entity.Comp.InjectOnly) return; var (_, component) = entity; diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml index dbc78a84098..d6f3ee75fa6 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml @@ -115,6 +115,7 @@ solutionName: pen transferAmount: 15 onlyAffectsMobs: false + injectOnly: true - type: Appearance - type: SolutionContainerVisuals maxFillLevels: 1 @@ -205,6 +206,7 @@ solutionName: pen transferAmount: 20 onlyAffectsMobs: false + injectOnly: true - type: SolutionContainerManager solutions: pen: @@ -236,6 +238,7 @@ solutionName: pen transferAmount: 20 onlyAffectsMobs: false + injectOnly: true - type: SolutionContainerManager solutions: pen: @@ -267,6 +270,8 @@ solutionName: pen transferAmount: 20 onlyAffectsMobs: false + injectOnly: true + - type: SolutionContainerManager solutions: pen: @@ -299,6 +304,7 @@ solutionName: pen transferAmount: 30 onlyAffectsMobs: false + injectOnly: true - type: SolutionContainerManager solutions: pen: @@ -337,6 +343,7 @@ solutionName: pen transferAmount: 30 onlyAffectsMobs: false + injectOnly: true - type: StaticPrice price: 500 - type: Tag @@ -397,6 +404,7 @@ solutionName: pen transferAmount: 30 onlyAffectsMobs: false + injectOnly: true - type: StaticPrice price: 500 - type: Tag From ae8a68b7cd1d4f89b4d9f429b49d5336dccac6bb Mon Sep 17 00:00:00 2001 From: Bixkitts <72874643+Bixkitts@users.noreply.github.com> Date: Sun, 31 Mar 2024 07:21:01 +0200 Subject: [PATCH 067/133] MeleeHitSoundSystem (#25005) * Began work to unscrew melee noises * finished * cleanup * cleanup * Update Content.Server/Weapons/Melee/MeleeWeaponSystem.cs Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com> * _Style * Fix merge --------- Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com> Co-authored-by: metalgearsloth --- .../Weapons/Melee/MeleeWeaponSystem.cs | 26 ++-- .../Weapons/Ranged/Systems/GunSystem.cs | 2 +- .../Weapons/Melee/MeleeSoundSystem.cs | 108 ++++++++++++++++ .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 119 ++++-------------- 4 files changed, 147 insertions(+), 108 deletions(-) create mode 100644 Content.Shared/Weapons/Melee/MeleeSoundSystem.cs diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index a01a3240139..ef4b1614770 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -23,6 +23,7 @@ using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Random; @@ -33,16 +34,17 @@ namespace Content.Server.Weapons.Melee; public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem { - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly BloodstreamSystem _bloodstream = default!; - [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly DamageExamineSystem _damageExamine = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly LagCompensationSystem _lag = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; - [Dependency] private readonly SolutionContainerSystem _solutions = default!; - [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly BloodstreamSystem _bloodstream = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly DamageExamineSystem _damageExamine = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly LagCompensationSystem _lag = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; + [Dependency] private readonly SolutionContainerSystem _solutions = default!; + [Dependency] private readonly TagSystem _tag = default!; public override void Initialize() { @@ -158,7 +160,8 @@ protected override bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid return false; } - Audio.PlayPvs(combatMode.DisarmSuccessSound, user, AudioParams.Default.WithVariation(0.025f).WithVolume(5f)); + _audio.PlayPvs(combatMode.DisarmSuccessSound, user, AudioParams.Default.WithVariation(0.025f).WithVolume(5f)); + AdminLogger.Add(LogType.DisarmedAction, $"{ToPrettyString(user):user} used disarm on {ToPrettyString(target):target}"); var targetEnt = Identity.Entity(target, EntityManager); var userEnt = Identity.Entity(user, EntityManager); @@ -175,7 +178,6 @@ protected override bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid PopupSystem.PopupEntity(msgOther, user, filterOther, true); PopupSystem.PopupEntity(msgUser, target, user); - if (eventArgs.IsStunned) { diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index e64657743de..f5f4e3f1995 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -38,7 +38,7 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly StunSystem _stun = default!; - public const float DamagePitchVariation = SharedMeleeWeaponSystem.DamagePitchVariation; + private const float DamagePitchVariation = 0.05f; public const float GunClumsyChance = 0.5f; public override void Initialize() diff --git a/Content.Shared/Weapons/Melee/MeleeSoundSystem.cs b/Content.Shared/Weapons/Melee/MeleeSoundSystem.cs new file mode 100644 index 00000000000..5bf74802026 --- /dev/null +++ b/Content.Shared/Weapons/Melee/MeleeSoundSystem.cs @@ -0,0 +1,108 @@ +using Content.Shared.Weapons.Melee.Components; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; + +namespace Content.Shared.Weapons.Melee; + +/// +/// This handles +/// +public sealed class MeleeSoundSystem : EntitySystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + + public const float DamagePitchVariation = 0.05f; + + /// + /// Plays the SwingSound from a weapon component + /// for immediate feedback, misses and such + /// (Swinging a weapon goes "whoosh" whether it hits or not) + /// + public void PlaySwingSound(EntityUid userUid, EntityUid weaponUid, MeleeWeaponComponent weaponComponent) + { + _audio.PlayPredicted(weaponComponent.SwingSound, weaponUid, userUid); + } + + /// + /// Takes a "damageType" string as an argument and uses it to + /// search one of the various Dictionaries in the MeleeSoundComponent + /// for a sound to play, and falls back if that fails + /// + /// Serves as a lookup key for a hit sound + /// A sound can be supplied by the itself to override everything else + public void PlayHitSound(EntityUid targetUid, EntityUid? userUid, string? damageType, SoundSpecifier? hitSoundOverride, MeleeWeaponComponent weaponComponent) + { + var hitSound = weaponComponent.HitSound; + var noDamageSound = weaponComponent.NoDamageSound; + + var playedSound = false; + + if (Deleted(targetUid)) + return; + + // hitting can obv destroy an entity so we play at coords and not following them + var coords = Transform(targetUid).Coordinates; + // Play sound based off of highest damage type. + if (TryComp(targetUid, out var damageSoundComp)) + { + if (damageType == null && damageSoundComp.NoDamageSound != null) + { + _audio.PlayPredicted(damageSoundComp.NoDamageSound, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + else if (damageType != null && damageSoundComp.SoundTypes?.TryGetValue(damageType, out var damageSoundType) == true) + { + _audio.PlayPredicted(damageSoundType, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + else if (damageType != null && damageSoundComp.SoundGroups?.TryGetValue(damageType, out var damageSoundGroup) == true) + { + _audio.PlayPredicted(damageSoundGroup, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + } + + // Use weapon sounds if the thing being hit doesn't specify its own sounds. + if (!playedSound) + { + if (hitSoundOverride != null) + { + _audio.PlayPredicted(hitSoundOverride, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + else if (hitSound != null) + { + _audio.PlayPredicted(hitSound, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + else + { + _audio.PlayPredicted(noDamageSound, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + } + + // Fallback to generic sounds. + if (!playedSound) + { + switch (damageType) + { + // Unfortunately heat returns caustic group so can't just use the damagegroup in that instance. + case "Burn": + case "Heat": + case "Radiation": + case "Cold": + _audio.PlayPredicted(new SoundPathSpecifier("/Audio/Items/welder.ogg"), targetUid, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + break; + // No damage, fallback to tappies + case null: + _audio.PlayPredicted(new SoundCollectionSpecifier("WeakHit"), targetUid, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + break; + case "Brute": + _audio.PlayPredicted(new SoundCollectionSpecifier("MetalThud"), targetUid, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + break; + } + } + } + +} diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 6a5127f2c95..e59b4a13fed 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -21,8 +21,6 @@ using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; @@ -36,22 +34,21 @@ namespace Content.Shared.Weapons.Melee; public abstract class SharedMeleeWeaponSystem : EntitySystem { - [Dependency] protected readonly IGameTiming Timing = default!; - [Dependency] protected readonly IMapManager MapManager = default!; - [Dependency] private readonly IPrototypeManager _protoManager = default!; - [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; - [Dependency] protected readonly ActionBlockerSystem Blocker = default!; - [Dependency] protected readonly DamageableSystem Damageable = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] protected readonly SharedAudioSystem Audio = default!; - [Dependency] protected readonly SharedCombatModeSystem CombatMode = default!; - [Dependency] protected readonly SharedInteractionSystem Interaction = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] protected readonly SharedPopupSystem PopupSystem = default!; - [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; - [Dependency] private readonly StaminaSystem _stamina = default!; - - public const float DamagePitchVariation = 0.05f; + [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; + [Dependency] protected readonly ActionBlockerSystem Blocker = default!; + [Dependency] protected readonly SharedCombatModeSystem CombatMode = default!; + [Dependency] protected readonly DamageableSystem Damageable = default!; + [Dependency] protected readonly SharedInteractionSystem Interaction = default!; + [Dependency] protected readonly IMapManager MapManager = default!; + [Dependency] protected readonly SharedPopupSystem PopupSystem = default!; + [Dependency] protected readonly IGameTiming Timing = default!; + [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly MeleeSoundSystem _meleeSound = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly StaminaSystem _stamina = default!; + private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque); /// @@ -83,7 +80,8 @@ public override void Initialize() SubscribeAllEvent(OnStopAttack); #if DEBUG - SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent (OnMapInit); } private void OnMapInit(EntityUid uid, MeleeWeaponComponent component, MapInitEvent args) @@ -465,7 +463,7 @@ protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, Entity } var missEvent = new MeleeHitEvent(new List(), user, meleeUid, damage, null); RaiseLocalEvent(meleeUid, missEvent); - Audio.PlayPredicted(component.SwingSound, meleeUid, user); + _meleeSound.PlaySwingSound(user, meleeUid, component); return; } @@ -520,7 +518,7 @@ protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, Entity } - PlayHitSound(target.Value, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component.HitSound, component.NoDamageSound); + _meleeSound.PlayHitSound(target.Value, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component); if (damageResult?.GetTotal() > FixedPoint2.Zero) { @@ -563,7 +561,9 @@ private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeU var missEvent = new MeleeHitEvent(new List(), user, meleeUid, damage, direction); RaiseLocalEvent(meleeUid, missEvent); - Audio.PlayPredicted(component.SwingSound, meleeUid, user); + // immediate audio feedback + _meleeSound.PlaySwingSound(user, meleeUid, component); + return true; } @@ -658,7 +658,7 @@ private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeU if (entities.Count != 0) { var target = entities.First(); - PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component.HitSound, component.NoDamageSound); + _meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component); } if (appliedDamage.GetTotal() > FixedPoint2.Zero) @@ -702,77 +702,6 @@ protected virtual bool ArcRaySuccessful(EntityUid targetUid, Vector2 position, A return true; } - public void PlayHitSound(EntityUid target, EntityUid? user, string? type, SoundSpecifier? hitSoundOverride, SoundSpecifier? hitSound, SoundSpecifier? noDamageSound) - { - var playedSound = false; - - if (Deleted(target)) - return; - - // hitting can obv destroy an entity so we play at coords and not following them - var coords = Transform(target).Coordinates; - // Play sound based off of highest damage type. - if (TryComp(target, out var damageSoundComp)) - { - if (type == null && damageSoundComp.NoDamageSound != null) - { - Audio.PlayPredicted(damageSoundComp.NoDamageSound, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - else if (type != null && damageSoundComp.SoundTypes?.TryGetValue(type, out var damageSoundType) == true) - { - Audio.PlayPredicted(damageSoundType, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - else if (type != null && damageSoundComp.SoundGroups?.TryGetValue(type, out var damageSoundGroup) == true) - { - Audio.PlayPredicted(damageSoundGroup, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - } - - // Use weapon sounds if the thing being hit doesn't specify its own sounds. - if (!playedSound) - { - if (hitSoundOverride != null) - { - Audio.PlayPredicted(hitSoundOverride, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - else if (hitSound != null) - { - Audio.PlayPredicted(hitSound, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - else if (noDamageSound != null) - { - Audio.PlayPredicted(noDamageSound, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - } - - // Fallback to generic sounds. - if (!playedSound) - { - switch (type) - { - // Unfortunately heat returns caustic group so can't just use the damagegroup in that instance. - case "Burn": - case "Heat": - case "Radiation": - case "Cold": - Audio.PlayPredicted(new SoundPathSpecifier("/Audio/Items/welder.ogg"), target, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - break; - // No damage, fallback to tappies - case null: - Audio.PlayPredicted(new SoundCollectionSpecifier("WeakHit"), target, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - break; - case "Brute": - Audio.PlayPredicted(new SoundCollectionSpecifier("MetalThud"), target, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - break; - } - } - } public static string? GetHighestDamageSound(DamageSpecifier modifiedDamage, IPrototypeManager protoManager) { @@ -809,7 +738,7 @@ protected virtual bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid } // Play a sound to give instant feedback; same with playing the animations - Audio.PlayPredicted(component.SwingSound, meleeUid, user); + _meleeSound.PlaySwingSound(user, meleeUid, component); return true; } From f5e5b6b0952e73acfcc500cd2d65d30d6bcd5b96 Mon Sep 17 00:00:00 2001 From: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:30:48 -0500 Subject: [PATCH 068/133] Remove physics comp from VendingMachineWallmount (#25632) * Remove physics comp from VendingMachineWallmount * Fixtures removal --------- Co-authored-by: Jeff Co-authored-by: metalgearsloth --- .../Entities/Structures/Machines/vending_machines.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index c97dc4b9dc9..e738510277b 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -1391,9 +1391,6 @@ snap: - Wallmount components: - - type: Physics - canCollide: false - - type: Fixtures - type: Sprite drawdepth: WallMountedItems snapCardinals: false From d5052697aa21ffddb9cec49f9b445ce194e41eab Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Sun, 31 Mar 2024 07:33:23 +0200 Subject: [PATCH 069/133] Remake hairflowers (#25475) * Add more lily usage (orange hairflower and flowercrown) * comit 2 * ee * more fixes * w * im stupid * bring poppy in authodrobe * weh --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../VendingMachines/Inventories/theater.yml | 2 +- .../Entities/Clothing/Head/misc.yml | 14 ---------- .../Objects/Consumable/Food/produce.yml | 4 +++ .../Entities/Objects/Decoration/present.yml | 2 +- .../Crafting/Graphs/improvised/hairflower.yml | 16 ----------- .../Recipes/Crafting/improvised.yml | 13 --------- .../Head/Misc/hairflower.rsi/icon.png | Bin 259 -> 0 bytes .../Head/Misc/hairflower.rsi/inhand-left.png | Bin 291 -> 0 bytes .../Head/Misc/hairflower.rsi/inhand-right.png | Bin 266 -> 0 bytes .../Head/Misc/hairflower.rsi/meta.json | 26 ------------------ .../Hydroponics/lily.rsi/equipped-HELMET.png | Bin 0 -> 341 bytes .../Specific/Hydroponics/lily.rsi/meta.json | 6 +++- .../poppy.rsi}/equipped-HELMET.png | Bin .../Specific/Hydroponics/poppy.rsi/meta.json | 6 +++- Resources/migration.yml | 7 +++-- 15 files changed, 21 insertions(+), 75 deletions(-) delete mode 100644 Resources/Prototypes/Recipes/Crafting/Graphs/improvised/hairflower.yml delete mode 100644 Resources/Textures/Clothing/Head/Misc/hairflower.rsi/icon.png delete mode 100644 Resources/Textures/Clothing/Head/Misc/hairflower.rsi/inhand-left.png delete mode 100644 Resources/Textures/Clothing/Head/Misc/hairflower.rsi/inhand-right.png delete mode 100644 Resources/Textures/Clothing/Head/Misc/hairflower.rsi/meta.json create mode 100644 Resources/Textures/Objects/Specific/Hydroponics/lily.rsi/equipped-HELMET.png rename Resources/Textures/{Clothing/Head/Misc/hairflower.rsi => Objects/Specific/Hydroponics/poppy.rsi}/equipped-HELMET.png (100%) diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml index 960a8f8797e..5e3c7d94010 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml @@ -43,7 +43,7 @@ ClothingMaskScaredMime: 1 ClothingUniformJumpsuitKimono: 1 ClothingHeadHatCasa: 1 - ClothingHeadHatHairflower: 1 + FoodPoppy: 1 ClothingHeadHatGladiator: 1 ClothingUniformJumpsuitGladiator: 1 ClothingHeadHatCowboyBrown: 1 diff --git a/Resources/Prototypes/Entities/Clothing/Head/misc.yml b/Resources/Prototypes/Entities/Clothing/Head/misc.yml index 3fd55faf266..1149224fc3e 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/misc.yml @@ -47,20 +47,6 @@ graph: flowercrown node: flowercrown -- type: entity - parent: ClothingHeadBase - id: ClothingHeadHatHairflower - name: hairflower - description: A red flower for beautiful ladies. - components: - - type: Sprite - sprite: Clothing/Head/Misc/hairflower.rsi - - type: Clothing - sprite: Clothing/Head/Misc/hairflower.rsi - - type: Construction - graph: hairflower - node: hairflower - - type: entity parent: ClothingHeadLightBase id: ClothingHeadHatPumpkin diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index 21eb0fb9423..3f0277e1bc3 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -1220,6 +1220,10 @@ id: FoodPoppy description: A flower with extracts often used in the production of medicine components: + - type: Clothing + slots: + - HEAD + quickEquip: false - type: FlavorProfile flavors: - medicine diff --git a/Resources/Prototypes/Entities/Objects/Decoration/present.yml b/Resources/Prototypes/Entities/Objects/Decoration/present.yml index 8fdc4793513..a417fdf0769 100644 --- a/Resources/Prototypes/Entities/Objects/Decoration/present.yml +++ b/Resources/Prototypes/Entities/Objects/Decoration/present.yml @@ -294,7 +294,7 @@ orGroup: GiftPool - id: ClothingHeadHatFlowerCrown orGroup: GiftPool - - id: ClothingHeadHatHairflower + - id: FoodPoppy orGroup: GiftPool - id: ClothingMaskClown orGroup: GiftPool diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/hairflower.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/hairflower.yml deleted file mode 100644 index 76bc1242901..00000000000 --- a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/hairflower.yml +++ /dev/null @@ -1,16 +0,0 @@ -- type: constructionGraph - id: hairflower - start: start - graph: - - node: start - edges: - - to: hairflower - steps: - - tag: Flower # TODO change to "RedFlower" or "Poppy" tag, so you cant make red flower from bluesyellow etc., when it will be - name: flower - icon: - sprite: Objects/Specific/Hydroponics/poppy.rsi - state: produce - doAfter: 3.5 - - node: hairflower - entity: ClothingHeadHatHairflower diff --git a/Resources/Prototypes/Recipes/Crafting/improvised.yml b/Resources/Prototypes/Recipes/Crafting/improvised.yml index 74148375922..2c55e4fc262 100644 --- a/Resources/Prototypes/Recipes/Crafting/improvised.yml +++ b/Resources/Prototypes/Recipes/Crafting/improvised.yml @@ -98,19 +98,6 @@ sprite: Clothing/Eyes/Misc/blindfold.rsi state: icon -- type: construction - name: hairflower - id: hairflower - graph: hairflower - startNode: start - targetNode: hairflower - category: construction-category-clothing - description: "A red flower for beautiful ladies." - icon: - sprite: Clothing/Head/Misc/hairflower.rsi - state: icon - objectType: Item - - type: construction name: flower crown id: flowercrown diff --git a/Resources/Textures/Clothing/Head/Misc/hairflower.rsi/icon.png b/Resources/Textures/Clothing/Head/Misc/hairflower.rsi/icon.png deleted file mode 100644 index a87d0676fb4ce93c6344dcaa77a8b773c0d439ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJQ=Tr4Ar*7pUfammWFXM+@c)&G zoh=8|3)$itHhkvV!)85u_w3GxtO8->wQS}>!X7=HE16$D$S~Y+CpY)fo$}oEE4SpH zp0qK_WH*z@0tQwMh8sI{R$aQlAkxTCaC81q(GQ>bcp_7}9B!;TUU*ZoO~+_6BSW5k zhwZ{QJUidV?2&%(OQw5mwey>k*YD^w+}$WRd(XS$N5qV{7kD`*3qSi;!4rIFC3ntK z(}McL4--9P7$1F7-JHuYVa_bi?H4#doR9dU8qxr^xrX6zkab-|&C3%&Uom*P`njxg HN@xNAZU}62 diff --git a/Resources/Textures/Clothing/Head/Misc/hairflower.rsi/inhand-left.png b/Resources/Textures/Clothing/Head/Misc/hairflower.rsi/inhand-left.png deleted file mode 100644 index f838e54e74169b230a7dfd569d5c292509d39f82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=uRL8GLn`LHy=BPP4ZLn`LHy=BYSb0+;`ELFT~e<|;C_;WqY!iLuy zIs4a_R3Beioi@FM)8o>&D=kH*geJeqZ{m5tSCGt5-XP7qgZYlc0a>s$3|oXGTvd#gK50YKk1p2$CQP2BR01_nlU zPZ!6KinzD8HgX;c5IFX+d+t>39SvoA3i-^{(&+^Z-!~MqTMIt)Rpe6;E8DoaOQp4A zW95g|iaqtOo)mq5EV$$J%tFNpTOB4w)%I-sdgAkz70-k>zuma$>${0E#-{8~|1!I7 z*!AP|*&M5~(CXk_ED1Ywx~ntG_JwizCn+>AFmWJ=65R{0;_JWuR%}`K`GxMjC9>~y zs}FW>W*3kUVq#8f5rd+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Hydroponics/lily.rsi/meta.json b/Resources/Textures/Objects/Specific/Hydroponics/lily.rsi/meta.json index ddbda4f0afc..782dc2bfda0 100644 --- a/Resources/Textures/Objects/Specific/Hydroponics/lily.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Hydroponics/lily.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from https://github.com/tgstation/tgstation/commit/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a seed modified by potato1234_X (github) for ss14", + "copyright": "Taken from https://github.com/tgstation/tgstation/commit/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a seed modified by potato1234_X (github) for ss14, equipped-HELMET taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e and changed hue", "size": { "x": 32, "y": 32 @@ -27,6 +27,10 @@ }, { "name": "stage-3" + }, + { + "name": "equipped-HELMET", + "directions": 4 } ] } diff --git a/Resources/Textures/Clothing/Head/Misc/hairflower.rsi/equipped-HELMET.png b/Resources/Textures/Objects/Specific/Hydroponics/poppy.rsi/equipped-HELMET.png similarity index 100% rename from Resources/Textures/Clothing/Head/Misc/hairflower.rsi/equipped-HELMET.png rename to Resources/Textures/Objects/Specific/Hydroponics/poppy.rsi/equipped-HELMET.png diff --git a/Resources/Textures/Objects/Specific/Hydroponics/poppy.rsi/meta.json b/Resources/Textures/Objects/Specific/Hydroponics/poppy.rsi/meta.json index 8b6952d030b..b49b49cc850 100644 --- a/Resources/Textures/Objects/Specific/Hydroponics/poppy.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Hydroponics/poppy.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from https://github.com/tgstation/tgstation/commit/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a", + "copyright": "Taken from https://github.com/tgstation/tgstation/commit/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a, equipped-HELMET taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e", "size": { "x": 32, "y": 32 @@ -27,6 +27,10 @@ }, { "name": "stage-3" + }, + { + "name": "equipped-HELMET", + "directions": 4 } ] } diff --git a/Resources/migration.yml b/Resources/migration.yml index 147e322fb5e..8c0fe2064f4 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -237,7 +237,7 @@ AirlockExternalGlassEasyPryLocked: AirlockExternalGlassLocked AirlockGlassShuttleEasyPryLocked: AirlockExternalGlassShuttleLocked AirlockShuttleEasyPryLocked: AirlockExternalShuttleLocked -#2024-03-10 +# 2024-03-10 ClothingBackpackFilledDetective: ClothingBackpackSecurityFilledDetective ClothingBackpackDuffelFilledDetective: ClothingBackpackDuffelSecurityFilledDetective ClothingBackpackSatchelFilledDetective: ClothingBackpackSatchelSecurityFilledDetective @@ -247,10 +247,13 @@ ImprovisedExplosive: FireBomb ImprovisedExplosiveEmpty: FireBombEmpty ImprovisedExplosiveFuel: FireBombFuel +# 2024-03-16 +ClothingHeadHatHairflower: FoodPoppy + # 2024-03-21 RPED: null # 2024-03-30 # These are technically not equivalent, but it probably makes more sense to replace any existing SCAF stuff with SOME kind of armor, instead of just deleting it outright. ClothingHeadHelmetScaf: ClothingHeadHelmetBasic -ClothingOuterArmorScaf: ClothingOuterArmorBasic \ No newline at end of file +ClothingOuterArmorScaf: ClothingOuterArmorBasic From 7c7ff5abf64dff98b922c4adefad9d5f7d755351 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 05:34:29 +0000 Subject: [PATCH 070/133] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 19c1c744cf1..23cf2b5d2b3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: TheShuEd - changes: - - message: 'Flesh and Rock anom reworked: It should be easier to maintain them now - (I guess)' - type: Tweak - id: 5771 - time: '2024-01-23T12:32:05.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24381 - author: metalgearsloth changes: - message: Fix thrusters. @@ -3799,3 +3791,13 @@ id: 6270 time: '2024-03-31T04:59:36.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26453 +- author: lzk228 + changes: + - message: Hairflower was removed. + type: Remove + - message: Now you can wear poppy (and other flowers) on your head instead of crafting + hairflower with it. + type: Tweak + id: 6271 + time: '2024-03-31T05:33:23.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25475 From d71062a64c569d59bc67c950a34454eef7ccaa14 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Sat, 30 Mar 2024 22:37:33 -0700 Subject: [PATCH 071/133] Injector UI shows TransferAmount change, Spilling liquid changes Injector mode (#26596) * Injector UI shows TransferAmount change, spill changes mode * Update Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs * Update Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs --------- Co-authored-by: Plykiya Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- Content.Client/Chemistry/UI/InjectorStatusControl.cs | 3 +++ Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/Content.Client/Chemistry/UI/InjectorStatusControl.cs b/Content.Client/Chemistry/UI/InjectorStatusControl.cs index 9cb699330c2..ba1f97cd1e4 100644 --- a/Content.Client/Chemistry/UI/InjectorStatusControl.cs +++ b/Content.Client/Chemistry/UI/InjectorStatusControl.cs @@ -17,6 +17,7 @@ public sealed class InjectorStatusControl : Control private FixedPoint2 PrevVolume; private FixedPoint2 PrevMaxVolume; + private FixedPoint2 PrevTransferAmount; private InjectorToggleMode PrevToggleState; public InjectorStatusControl(Entity parent, SharedSolutionContainerSystem solutionContainers) @@ -37,11 +38,13 @@ protected override void FrameUpdate(FrameEventArgs args) // only updates the UI if any of the details are different than they previously were if (PrevVolume == solution.Volume && PrevMaxVolume == solution.MaxVolume + && PrevTransferAmount == _parent.Comp.TransferAmount && PrevToggleState == _parent.Comp.ToggleState) return; PrevVolume = solution.Volume; PrevMaxVolume = solution.MaxVolume; + PrevTransferAmount = _parent.Comp.TransferAmount; PrevToggleState = _parent.Comp.ToggleState; // Update current volume and injector state diff --git a/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs b/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs index 1e9e742a38f..92ea9621401 100644 --- a/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs +++ b/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs @@ -1,3 +1,4 @@ +using Content.Shared.Chemistry.Components; using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Examine; @@ -62,6 +63,12 @@ private void AddSpillVerb(Entity entity, ref GetVerbsEvent(entity, out var injectorComp)) + { + injectorComp.ToggleState = InjectorToggleMode.Draw; + Dirty(entity, injectorComp); + } }; } else From d512bc141a0a629e5fe49435529b0b5d68bbf9a9 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 31 Mar 2024 17:03:52 +1100 Subject: [PATCH 072/133] Update submodule to 217.2.1 (#26599) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 99c5b0ad083..6764ed56b06 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 99c5b0ad08351af347db3a122373f2c4482e94dc +Subproject commit 6764ed56b06309b56bd35c8ebffdf64882d4c4c1 From 1b69762816c5ae5184baa024a4bcd23b75236a6c Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 31 Mar 2024 02:34:17 -0400 Subject: [PATCH 073/133] disallow unanchoring or opening panels on locked emitters/APEs (#26600) * disallow unanchoring or opening panels on locked emitters/APEs * no locking open panels * oops * needback feedback * Update Content.Shared/Lock/LockSystem.cs * Update Content.Shared/Lock/LockSystem.cs * Update Content.Shared/Lock/LockSystem.cs * sanity --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: metalgearsloth --- Content.Client/Popups/PopupSystem.cs | 7 +- Content.Server/Popups/PopupSystem.cs | 2 +- Content.Shared/Lock/LockSystem.cs | 69 ++++++++++++++++--- .../Lock/LockedAnchorableComponent.cs | 13 ++++ .../Lock/LockedWiresPanelComponent.cs | 13 ++++ Content.Shared/Popups/SharedPopupSystem.cs | 2 +- Content.Shared/Wires/SharedWiresSystem.cs | 24 ++++++- Content.Shared/Wires/WiresPanelComponent.cs | 6 ++ .../Locale/en-US/lock/lock-component.ftl | 3 +- .../Mobs/Cyborgs/base_borg_chassis.yml | 1 + .../Structures/Machines/anomaly_equipment.yml | 2 + .../Power/Generation/Singularity/emitter.yml | 2 + 12 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 Content.Shared/Lock/LockedAnchorableComponent.cs create mode 100644 Content.Shared/Lock/LockedWiresPanelComponent.cs diff --git a/Content.Client/Popups/PopupSystem.cs b/Content.Client/Popups/PopupSystem.cs index 479fb02906c..fcc8bfc420a 100644 --- a/Content.Client/Popups/PopupSystem.cs +++ b/Content.Client/Popups/PopupSystem.cs @@ -163,10 +163,13 @@ public override void PopupEntity(string? message, EntityUid uid, Filter filter, PopupEntity(message, uid, type); } - public override void PopupClient(string? message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small) + public override void PopupClient(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small) { + if (recipient == null) + return; + if (_timing.IsFirstTimePredicted) - PopupEntity(message, uid, recipient, type); + PopupEntity(message, uid, recipient.Value, type); } public override void PopupEntity(string? message, EntityUid uid, PopupType type = PopupType.Small) diff --git a/Content.Server/Popups/PopupSystem.cs b/Content.Server/Popups/PopupSystem.cs index c5eb3819b54..237ca33a4db 100644 --- a/Content.Server/Popups/PopupSystem.cs +++ b/Content.Server/Popups/PopupSystem.cs @@ -88,7 +88,7 @@ public override void PopupEntity(string? message, EntityUid uid, EntityUid recip RaiseNetworkEvent(new PopupEntityEvent(message, type, GetNetEntity(uid)), actor.PlayerSession); } - public override void PopupClient(string? message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small) + public override void PopupClient(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small) { // do nothing duh its for client only } diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index 74cf5496d9a..5644a6b02f6 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Access.Components; using Content.Shared.Access.Systems; +using Content.Shared.Construction.Components; using Content.Shared.DoAfter; using Content.Shared.Emag.Systems; using Content.Shared.Examine; @@ -9,6 +10,7 @@ using Content.Shared.Popups; using Content.Shared.Storage.Components; using Content.Shared.Verbs; +using Content.Shared.Wires; using JetBrains.Annotations; using Robust.Shared.Audio.Systems; using Robust.Shared.Utility; @@ -40,8 +42,11 @@ public override void Initialize() SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnDoAfterLock); SubscribeLocalEvent(OnDoAfterUnlock); - } + SubscribeLocalEvent(OnLockToggleAttempt); + SubscribeLocalEvent(OnAttemptChangePanel); + SubscribeLocalEvent(OnUnanchorAttempt); + } private void OnStartup(EntityUid uid, LockComponent lockComp, ComponentStartup args) { _appearanceSystem.SetData(uid, LockVisuals.Locked, lockComp.Locked); @@ -226,18 +231,18 @@ private bool HasUserAccess(EntityUid uid, EntityUid user, AccessReaderComponent? private void AddToggleLockVerb(EntityUid uid, LockComponent component, GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract || !CanToggleLock(uid, args.User)) + if (!args.CanAccess || !args.CanInteract) return; AlternativeVerb verb = new() { - Act = component.Locked ? - () => TryUnlock(uid, args.User, component) : - () => TryLock(uid, args.User, component), + Act = component.Locked + ? () => TryUnlock(uid, args.User, component) + : () => TryLock(uid, args.User, component), Text = Loc.GetString(component.Locked ? "toggle-lock-verb-unlock" : "toggle-lock-verb-lock"), - Icon = component.Locked ? - new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/unlock.svg.192dpi.png")) : - new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/lock.svg.192dpi.png")), + Icon = !component.Locked + ? new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/lock.svg.192dpi.png")) + : new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/unlock.svg.192dpi.png")), }; args.Verbs.Add(verb); } @@ -275,5 +280,53 @@ private void OnDoAfterUnlock(EntityUid uid, LockComponent component, UnlockDoAft TryUnlock(uid, args.User, skipDoAfter: true); } + + private void OnLockToggleAttempt(Entity ent, ref LockToggleAttemptEvent args) + { + if (args.Cancelled) + return; + + if (!TryComp(ent, out var panel) || !panel.Open) + return; + + if (!args.Silent) + { + _sharedPopupSystem.PopupClient(Loc.GetString("construction-step-condition-wire-panel-close"), + ent, + args.User); + } + args.Cancelled = true; + } + + + private void OnAttemptChangePanel(Entity ent, ref AttemptChangePanelEvent args) + { + if (args.Cancelled) + return; + + if (!TryComp(ent, out var lockComp) || !lockComp.Locked) + return; + + _sharedPopupSystem.PopupClient(Loc.GetString("lock-comp-generic-fail", + ("target", Identity.Entity(ent, EntityManager))), + ent, + args.User); + args.Cancelled = true; + } + + private void OnUnanchorAttempt(Entity ent, ref UnanchorAttemptEvent args) + { + if (args.Cancelled) + return; + + if (!TryComp(ent, out var lockComp) || !lockComp.Locked) + return; + + _sharedPopupSystem.PopupClient(Loc.GetString("lock-comp-generic-fail", + ("target", Identity.Entity(ent, EntityManager))), + ent, + args.User); + args.Cancel(); + } } diff --git a/Content.Shared/Lock/LockedAnchorableComponent.cs b/Content.Shared/Lock/LockedAnchorableComponent.cs new file mode 100644 index 00000000000..781b7f65322 --- /dev/null +++ b/Content.Shared/Lock/LockedAnchorableComponent.cs @@ -0,0 +1,13 @@ +using Content.Shared.Construction.Components; +using Robust.Shared.GameStates; + +namespace Content.Shared.Lock; + +/// +/// This is used for a that cannot be unanchored while locked. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(LockSystem))] +public sealed partial class LockedAnchorableComponent : Component +{ + +} diff --git a/Content.Shared/Lock/LockedWiresPanelComponent.cs b/Content.Shared/Lock/LockedWiresPanelComponent.cs new file mode 100644 index 00000000000..1dbe6a4932d --- /dev/null +++ b/Content.Shared/Lock/LockedWiresPanelComponent.cs @@ -0,0 +1,13 @@ +using Content.Shared.Wires; +using Robust.Shared.GameStates; + +namespace Content.Shared.Lock; + +/// +/// This is used for a that cannot be opened while locked. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(LockSystem))] +public sealed partial class LockedWiresPanelComponent : Component +{ + +} diff --git a/Content.Shared/Popups/SharedPopupSystem.cs b/Content.Shared/Popups/SharedPopupSystem.cs index aeb85de2f59..b199884afb4 100644 --- a/Content.Shared/Popups/SharedPopupSystem.cs +++ b/Content.Shared/Popups/SharedPopupSystem.cs @@ -86,7 +86,7 @@ public abstract class SharedPopupSystem : EntitySystem /// Variant of that only runs on the client, outside of prediction. /// Useful for shared code that is always ran by both sides to avoid duplicate popups. /// - public abstract void PopupClient(string? message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small); + public abstract void PopupClient(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small); /// /// Variant of for use with prediction. The local client will show diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index f069687ffb7..b4b0768e0f7 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -28,15 +28,24 @@ private void OnPanelDoAfter(EntityUid uid, WiresPanelComponent panel, WirePanelD if (args.Cancelled) return; - TogglePanel(uid, panel, !panel.Open); + if (!TogglePanel(uid, panel, !panel.Open, args.User)) + return; + AdminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} screwed {ToPrettyString(uid):target}'s maintenance panel {(panel.Open ? "open" : "closed")}"); var sound = panel.Open ? panel.ScrewdriverOpenSound : panel.ScrewdriverCloseSound; Audio.PlayPredicted(sound, uid, args.User); + args.Handled = true; } private void OnInteractUsing(Entity ent, ref InteractUsingEvent args) { + if (!Tool.HasQuality(args.Used, ent.Comp.OpeningTool)) + return; + + if (!CanTogglePanel(ent, args.User)) + return; + if (!Tool.UseTool( args.Used, args.User, @@ -89,14 +98,25 @@ protected void UpdateAppearance(EntityUid uid, WiresPanelComponent panel) Appearance.SetData(uid, WiresVisuals.MaintenancePanelState, panel.Open && panel.Visible, appearance); } - public void TogglePanel(EntityUid uid, WiresPanelComponent component, bool open) + public bool TogglePanel(EntityUid uid, WiresPanelComponent component, bool open, EntityUid? user = null) { + if (!CanTogglePanel((uid, component), user)) + return false; + component.Open = open; UpdateAppearance(uid, component); Dirty(uid, component); var ev = new PanelChangedEvent(component.Open); RaiseLocalEvent(uid, ref ev); + return true; + } + + public bool CanTogglePanel(Entity ent, EntityUid? user) + { + var attempt = new AttemptChangePanelEvent(ent.Comp.Open, user); + RaiseLocalEvent(ent, ref attempt); + return !attempt.Cancelled; } public bool IsPanelOpen(Entity entity) diff --git a/Content.Shared/Wires/WiresPanelComponent.cs b/Content.Shared/Wires/WiresPanelComponent.cs index 9c7444778e7..a18e590e21b 100644 --- a/Content.Shared/Wires/WiresPanelComponent.cs +++ b/Content.Shared/Wires/WiresPanelComponent.cs @@ -57,6 +57,12 @@ public sealed partial class WiresPanelComponent : Component public LocId? ExamineTextOpen = "wires-panel-component-on-examine-open"; } +/// +/// Event raised on a before its open state is about to be changed. +/// +[ByRefEvent] +public record struct AttemptChangePanelEvent(bool Open, EntityUid? User, bool Cancelled = false); + /// /// Event raised when a panel is opened or closed. /// diff --git a/Resources/Locale/en-US/lock/lock-component.ftl b/Resources/Locale/en-US/lock/lock-component.ftl index f9f975c96e9..380605697ba 100644 --- a/Resources/Locale/en-US/lock/lock-component.ftl +++ b/Resources/Locale/en-US/lock/lock-component.ftl @@ -3,8 +3,9 @@ lock-comp-on-examined-is-unlocked = The {$entityName} seems to be unlocked. lock-comp-do-lock-success = You lock the {$entityName}. lock-comp-do-unlock-success = You unlock the {$entityName}. lock-comp-has-user-access-fail = Access denied +lock-comp-generic-fail = {CAPITALIZE(SUBJECT($target))} {CONJUGATE-BE($target)} locked. ## ToggleLockVerb toggle-lock-verb-unlock = Unlock -toggle-lock-verb-lock = Lock \ No newline at end of file +toggle-lock-verb-lock = Lock diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index dc6e718290b..5c7a4e139fe 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -140,6 +140,7 @@ - type: Lock locked: true - type: ActivatableUIRequiresLock + - type: LockedWiresPanel - type: Flashable - type: Damageable damageContainer: Silicon diff --git a/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml b/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml index cc9f8035fe7..36d77a236d0 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml @@ -160,6 +160,7 @@ board: APECircuitboard - type: Lock locked: false + - type: LockedWiresPanel - type: AccessReader access: [[ "Research" ]] - type: Emitter @@ -204,6 +205,7 @@ True: { visible: true } False: { visible: false } - type: LockVisuals + - type: LockedAnchorable - type: DeviceNetwork deviceNetId: Wireless receiveFrequencyId: BasicDevice diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml index 52698f62cc0..b999b2bdede 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml @@ -84,6 +84,8 @@ - type: Lock locked: false - type: LockVisuals + - type: LockedAnchorable + - type: LockedWiresPanel - type: AccessReader access: [[ "Engineering" ]] - type: Machine From d1ad6d912643e223ed3717916826c68d3a3ac5e5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 06:35:23 +0000 Subject: [PATCH 074/133] 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 23cf2b5d2b3..e2b7f81231d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix thrusters. - type: Fix - id: 5772 - time: '2024-01-23T12:49:19.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24446 - author: '0x6273' changes: - message: Hotplates now work again @@ -3801,3 +3794,11 @@ id: 6271 time: '2024-03-31T05:33:23.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25475 +- author: EmoGarbage404 + changes: + - message: Borgs, Emitters, and APEs can no longer have their panels opened while + locked. APEs and Emitters additionally cannot be unanchored either. + type: Add + id: 6272 + time: '2024-03-31T06:34:17.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26600 From 83374935262265679e554780bcd42017238f059a Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Sun, 31 Mar 2024 08:49:46 +0000 Subject: [PATCH 075/133] Fix grave digging sound indefinitely playing if dug by aghost. (#26420) Admins bypass doafters. As such, the code that runs on doafter completion is ran before the sound is actually created. This then leads to the sound never being stopped, and as such it would infinitely play. This commit gets around the issue by manually stopping the sound should the doafter fail to start. If we could be sure that the doafter would never fail, then we could just move the call to StartDigging above starting the doafter but this is currently not possible. Co-authored-by: metalgearsloth --- Content.Shared/Burial/BurialSystem.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Burial/BurialSystem.cs b/Content.Shared/Burial/BurialSystem.cs index e19ac2e9c67..ccf5f1a298f 100644 --- a/Content.Shared/Burial/BurialSystem.cs +++ b/Content.Shared/Burial/BurialSystem.cs @@ -51,8 +51,15 @@ private void OnInteractUsing(EntityUid uid, GraveComponent component, InteractUs BreakOnHandChange = true }; + if (component.Stream == null) + component.Stream = _audioSystem.PlayPredicted(component.DigSound, uid, args.User)?.Entity; + if (!_doAfterSystem.TryStartDoAfter(doAfterEventArgs)) + { + _audioSystem.Stop(component.Stream); return; + } + StartDigging(uid, args.User, args.Used, component); } @@ -111,8 +118,6 @@ private void StartDigging(EntityUid uid, EntityUid user, EntityUid? used, GraveC { _popupSystem.PopupClient(Loc.GetString("grave-start-digging-user", ("grave", uid), ("tool", used)), user, user); _popupSystem.PopupEntity(Loc.GetString("grave-start-digging-others", ("user", user), ("grave", uid), ("tool", used)), user, Filter.PvsExcept(user), true); - if (component.Stream == null) - component.Stream = _audioSystem.PlayPredicted(component.DigSound, uid, user)?.Entity; component.ActiveShovelDigging = true; Dirty(uid, component); } @@ -163,8 +168,15 @@ private void OnRelayMovement(EntityUid uid, GraveComponent component, ref Contai BreakOnDamage = false }; - if (!_doAfterSystem.TryStartDoAfter(doAfterEventArgs, out component.HandDiggingDoAfter)) + + if (component.Stream == null) + component.Stream = _audioSystem.PlayPredicted(component.DigSound, uid, args.Entity)?.Entity; + + if (!_doAfterSystem.TryStartDoAfter(doAfterEventArgs)) + { + _audioSystem.Stop(component.Stream); return; + } StartDigging(uid, args.Entity, null, component); } From ad438a7ac2216c6295cae97025b4204a9c2ae41f Mon Sep 17 00:00:00 2001 From: "Wrexbe (Josh)" <81056464+wrexbe@users.noreply.github.com> Date: Sun, 31 Mar 2024 01:51:02 -0700 Subject: [PATCH 076/133] Make the buttons on the map ui not squished (#26604) Make the map ui work Co-authored-by: wrexbe --- Content.Client/Pinpointer/UI/NavMapControl.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Content.Client/Pinpointer/UI/NavMapControl.cs b/Content.Client/Pinpointer/UI/NavMapControl.cs index a8ec7b37a0b..677092e1918 100644 --- a/Content.Client/Pinpointer/UI/NavMapControl.cs +++ b/Content.Client/Pinpointer/UI/NavMapControl.cs @@ -114,9 +114,16 @@ public NavMapControl() : base(MinDisplayedRange, MaxDisplayedRange, DefaultDispl VerticalExpand = false, Children = { - _zoom, - _beacons, - _recenter, + new BoxContainer() + { + Orientation = BoxContainer.LayoutOrientation.Horizontal, + Children = + { + _zoom, + _beacons, + _recenter + } + } } }; From 55c77af33cad8e173c6281286fc66f3a67579eb5 Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Sun, 31 Mar 2024 10:52:52 +0200 Subject: [PATCH 077/133] Combine flower crown and wreath (#26605) * Combine flower crown and wreath * huh * huuh :trollface: --- .../Entities/Clothing/Head/misc.yml | 17 +++++++----- .../Entities/Clothing/Neck/misc.yml | 14 ---------- .../Entities/Objects/Decoration/present.yml | 2 +- .../Graphs/improvised/flowercrown.yml | 26 ------------------ .../Graphs/improvised/flowerwreath.yml | 2 +- .../Recipes/Crafting/improvised.yml | 19 ++----------- .../Head/Misc/flower-crown.rsi/icon.png | Bin 5190 -> 0 bytes .../equipped-HELMET.png | Bin .../Misc/flower-wreath.rsi/equipped-NECK.png | Bin .../Misc/flower-wreath.rsi/icon.png | Bin .../meta.json | 6 +++- .../Neck/Misc/flower-wreath.rsi/meta.json | 19 ------------- Resources/migration.yml | 4 +++ 13 files changed, 24 insertions(+), 85 deletions(-) delete mode 100644 Resources/Prototypes/Recipes/Crafting/Graphs/improvised/flowercrown.yml delete mode 100644 Resources/Textures/Clothing/Head/Misc/flower-crown.rsi/icon.png rename Resources/Textures/Clothing/Head/Misc/{flower-crown.rsi => flower-wreath.rsi}/equipped-HELMET.png (100%) rename Resources/Textures/Clothing/{Neck => Head}/Misc/flower-wreath.rsi/equipped-NECK.png (100%) rename Resources/Textures/Clothing/{Neck => Head}/Misc/flower-wreath.rsi/icon.png (100%) rename Resources/Textures/Clothing/Head/Misc/{flower-crown.rsi => flower-wreath.rsi}/meta.json (59%) delete mode 100644 Resources/Textures/Clothing/Neck/Misc/flower-wreath.rsi/meta.json diff --git a/Resources/Prototypes/Entities/Clothing/Head/misc.yml b/Resources/Prototypes/Entities/Clothing/Head/misc.yml index 1149224fc3e..c7ba6e0b32a 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/misc.yml @@ -35,17 +35,20 @@ - type: entity parent: ClothingHeadBase - id: ClothingHeadHatFlowerCrown - name: flower crown - description: A coronet of fresh and fragrant flowers. + id: ClothingHeadHatFlowerWreath + name: flower wreath + description: A wreath of colourful flowers. Can be worn both on head and neck. components: - type: Sprite - sprite: Clothing/Head/Misc/flower-crown.rsi + sprite: Clothing/Head/Misc/flower-wreath.rsi - type: Clothing - sprite: Clothing/Head/Misc/flower-crown.rsi + sprite: Clothing/Head/Misc/flower-wreath.rsi + slots: + - HEAD + - neck - type: Construction - graph: flowercrown - node: flowercrown + graph: flowerwreath + node: flowerwreath - type: entity parent: ClothingHeadLightBase diff --git a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml index 6b4be3c9f84..51325c0bbbd 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml @@ -79,17 +79,3 @@ event: !type:StethoscopeActionEvent checkCanInteract: false priority: -1 - -- type: entity - parent: ClothingNeckBase - id: ClothingNeckFlowerWreath - name: flower wreath - description: A wreath of colourful flowers. - components: - - type: Sprite - sprite: Clothing/Neck/Misc/flower-wreath.rsi - - type: Clothing - sprite: Clothing/Neck/Misc/flower-wreath.rsi - - type: Construction - graph: flowerwreath - node: flowerwreath diff --git a/Resources/Prototypes/Entities/Objects/Decoration/present.yml b/Resources/Prototypes/Entities/Objects/Decoration/present.yml index a417fdf0769..3fb5675f83f 100644 --- a/Resources/Prototypes/Entities/Objects/Decoration/present.yml +++ b/Resources/Prototypes/Entities/Objects/Decoration/present.yml @@ -292,7 +292,7 @@ orGroup: GiftPool - id: ClothingHeadHatBunny orGroup: GiftPool - - id: ClothingHeadHatFlowerCrown + - id: ClothingHeadHatFlowerWreath orGroup: GiftPool - id: FoodPoppy orGroup: GiftPool diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/flowercrown.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/flowercrown.yml deleted file mode 100644 index cba454b6638..00000000000 --- a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/flowercrown.yml +++ /dev/null @@ -1,26 +0,0 @@ -- type: constructionGraph - id: flowercrown - start: start - graph: - - node: start - edges: - - to: flowercrown - steps: - - tag: Flower - name: flower - icon: - sprite: Objects/Specific/Hydroponics/poppy.rsi - state: produce - - tag: Flower - name: flower - icon: - sprite: Objects/Specific/Hydroponics/poppy.rsi - state: produce - - tag: Ambrosia - name: ambrosia - icon: - sprite: Objects/Specific/Hydroponics/ambrosia_vulgaris.rsi - state: produce - doAfter: 10 - - node: flowercrown - entity: ClothingHeadHatFlowerCrown diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/flowerwreath.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/flowerwreath.yml index a0a87a14d8e..b8e580230da 100644 --- a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/flowerwreath.yml +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/flowerwreath.yml @@ -23,4 +23,4 @@ state: produce doAfter: 10 - node: flowerwreath - entity: ClothingNeckFlowerWreath + entity: ClothingHeadHatFlowerWreath diff --git a/Resources/Prototypes/Recipes/Crafting/improvised.yml b/Resources/Prototypes/Recipes/Crafting/improvised.yml index 2c55e4fc262..38d254c1416 100644 --- a/Resources/Prototypes/Recipes/Crafting/improvised.yml +++ b/Resources/Prototypes/Recipes/Crafting/improvised.yml @@ -98,19 +98,6 @@ sprite: Clothing/Eyes/Misc/blindfold.rsi state: icon -- type: construction - name: flower crown - id: flowercrown - graph: flowercrown - startNode: start - targetNode: flowercrown - category: construction-category-clothing - description: "A coronet of fresh and fragrant flowers." - icon: - sprite: Clothing/Head/Misc/flower-crown.rsi - state: icon - objectType: Item - - type: construction name: flower wreath id: flowerwreath @@ -118,9 +105,9 @@ startNode: start targetNode: flowerwreath category: construction-category-clothing - description: "A wreath of colourful flowers." + description: A wreath of colourful flowers. Can be worn both on head and neck. icon: - sprite: Clothing/Neck/Misc/flower-wreath.rsi + sprite: Clothing/Head/Misc/flower-wreath.rsi state: icon objectType: Item @@ -226,4 +213,4 @@ description: An improvised explosive made from pipes and wire. icon: sprite: Objects/Weapons/Bombs/pipebomb.rsi - state: icon \ No newline at end of file + state: icon diff --git a/Resources/Textures/Clothing/Head/Misc/flower-crown.rsi/icon.png b/Resources/Textures/Clothing/Head/Misc/flower-crown.rsi/icon.png deleted file mode 100644 index e1e80b9293d449cd5f31588a33d99fcafc8e8f20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5190 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 diff --git a/Resources/Textures/Clothing/Head/Misc/flower-crown.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Misc/flower-wreath.rsi/equipped-HELMET.png similarity index 100% rename from Resources/Textures/Clothing/Head/Misc/flower-crown.rsi/equipped-HELMET.png rename to Resources/Textures/Clothing/Head/Misc/flower-wreath.rsi/equipped-HELMET.png diff --git a/Resources/Textures/Clothing/Neck/Misc/flower-wreath.rsi/equipped-NECK.png b/Resources/Textures/Clothing/Head/Misc/flower-wreath.rsi/equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/flower-wreath.rsi/equipped-NECK.png rename to Resources/Textures/Clothing/Head/Misc/flower-wreath.rsi/equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/flower-wreath.rsi/icon.png b/Resources/Textures/Clothing/Head/Misc/flower-wreath.rsi/icon.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/flower-wreath.rsi/icon.png rename to Resources/Textures/Clothing/Head/Misc/flower-wreath.rsi/icon.png diff --git a/Resources/Textures/Clothing/Head/Misc/flower-crown.rsi/meta.json b/Resources/Textures/Clothing/Head/Misc/flower-wreath.rsi/meta.json similarity index 59% rename from Resources/Textures/Clothing/Head/Misc/flower-crown.rsi/meta.json rename to Resources/Textures/Clothing/Head/Misc/flower-wreath.rsi/meta.json index 2899868b907..c3dc06f19c5 100644 --- a/Resources/Textures/Clothing/Head/Misc/flower-crown.rsi/meta.json +++ b/Resources/Textures/Clothing/Head/Misc/flower-wreath.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-4.0", - "copyright": "Kevin Zheng 2022", + "copyright": "Kevin Zheng 2022 | equipped-NECK and icon Drawn by Ubaser.", "size": { "x": 32, "y": 32 @@ -10,6 +10,10 @@ { "name": "icon" }, + { + "name": "equipped-NECK", + "directions": 4 + }, { "name": "equipped-HELMET", "directions": 4 diff --git a/Resources/Textures/Clothing/Neck/Misc/flower-wreath.rsi/meta.json b/Resources/Textures/Clothing/Neck/Misc/flower-wreath.rsi/meta.json deleted file mode 100644 index 71f798f2c4a..00000000000 --- a/Resources/Textures/Clothing/Neck/Misc/flower-wreath.rsi/meta.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Drawn by Ubaser.", - "size": { - "x": 32, - "y": 32 - }, - - "states": [ - { - "name": "equipped-NECK", - "directions": 4 - }, - { - "name": "icon" - } - ] -} diff --git a/Resources/migration.yml b/Resources/migration.yml index 8c0fe2064f4..f5306c8b7c1 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -257,3 +257,7 @@ RPED: null # These are technically not equivalent, but it probably makes more sense to replace any existing SCAF stuff with SOME kind of armor, instead of just deleting it outright. ClothingHeadHelmetScaf: ClothingHeadHelmetBasic ClothingOuterArmorScaf: ClothingOuterArmorBasic + +# 2024-03-31 +ClothingNeckFlowerWreath: ClothingHeadHatFlowerWreath +ClothingHeadHatFlowerCrown: ClothingHeadHatFlowerWreath From 241b153f6a6466306edf1aa5011829c73f48ceb5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 08:53:58 +0000 Subject: [PATCH 078/133] 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 e2b7f81231d..fc8e6bb559d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: '0x6273' - changes: - - message: Hotplates now work again - type: Fix - id: 5773 - time: '2024-01-23T13:06:14.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24450 - author: Boaz1111 changes: - message: Lizards can now properly eat fruit cakes and banana cream pie slices. @@ -3802,3 +3795,11 @@ id: 6272 time: '2024-03-31T06:34:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26600 +- author: lzk228 + changes: + - message: Flower crown and wreath were combined. Now you can wear wreath both on + head and on neck. + type: Tweak + id: 6273 + time: '2024-03-31T08:52:52.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26605 From 2a1903dae0948e315761fbde46552b5ceb3b57e1 Mon Sep 17 00:00:00 2001 From: Ubaser <134914314+UbaserB@users.noreply.github.com> Date: Sun, 31 Mar 2024 22:48:36 +1100 Subject: [PATCH 079/133] Add AP damage to throwing knives (#26380) * add * ap * no more stam dmg --- Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml index 03654061ced..afe4644517c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml @@ -275,6 +275,7 @@ - type: EmbeddableProjectile sound: /Audio/Weapons/star_hit.ogg - type: DamageOtherOnHit + ignoreResistances: true damage: types: Slash: 10 From 7f2e6ccbb887addf5c3381f8c84fff6c451d1405 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 11:49:42 +0000 Subject: [PATCH 080/133] 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 fc8e6bb559d..41e707cb49a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Boaz1111 - changes: - - message: Lizards can now properly eat fruit cakes and banana cream pie slices. - type: Fix - id: 5774 - time: '2024-01-23T18:08:32.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24457 - author: icekot8 changes: - message: The bounty of briefcases has been removed @@ -3803,3 +3796,10 @@ id: 6273 time: '2024-03-31T08:52:52.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26605 +- author: Ubaser + changes: + - message: Throwing knives now additionally do armour piercing damage. + type: Tweak + id: 6274 + time: '2024-03-31T11:48:36.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26380 From 5eff7f169e95e94aab54b742c6928c4925cf7887 Mon Sep 17 00:00:00 2001 From: avery <51971268+graevy@users.noreply.github.com> Date: Sun, 31 Mar 2024 13:44:02 -0700 Subject: [PATCH 081/133] cancelable brig timers (#26557) brig timers now cancelable. also some screensystem yakshave --- Content.Client/TextScreen/TextScreenSystem.cs | 14 ++++------- .../Components/SignalTimerComponent.cs | 6 +++++ .../Systems/SignalTimerSystem.cs | 23 +++++++++++++------ .../Screens/Systems/ScreenSystem.cs | 1 + 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Content.Client/TextScreen/TextScreenSystem.cs b/Content.Client/TextScreen/TextScreenSystem.cs index b4d67f5f218..53a620bd46d 100644 --- a/Content.Client/TextScreen/TextScreenSystem.cs +++ b/Content.Client/TextScreen/TextScreenSystem.cs @@ -112,17 +112,11 @@ protected override void OnAppearanceChange(EntityUid uid, TextScreenVisualsCompo if (args.AppearanceData.TryGetValue(TextScreenVisuals.Color, out var color) && color is Color) component.Color = (Color) color; - // DefaultText: broadcast updates from comms consoles - // ScreenText: the text accompanying shuttle timers e.g. "ETA" + // DefaultText: fallback text e.g. broadcast updates from comms consoles if (args.AppearanceData.TryGetValue(TextScreenVisuals.DefaultText, out var newDefault) && newDefault is string) - { - string?[] defaultText = SegmentText((string) newDefault, component); - component.Text = defaultText; - component.TextToDraw = defaultText; - ResetText(uid, component); - BuildTextLayers(uid, component, args.Sprite); - DrawLayers(uid, component.LayerStatesToDraw); - } + component.Text = SegmentText((string) newDefault, component); + + // ScreenText: currently rendered text e.g. the "ETA" accompanying shuttle timers if (args.AppearanceData.TryGetValue(TextScreenVisuals.ScreenText, out var text) && text is string) { component.TextToDraw = SegmentText((string) text, component); diff --git a/Content.Server/DeviceLinking/Components/SignalTimerComponent.cs b/Content.Server/DeviceLinking/Components/SignalTimerComponent.cs index 2e9b369b99a..b3535cde1fd 100644 --- a/Content.Server/DeviceLinking/Components/SignalTimerComponent.cs +++ b/Content.Server/DeviceLinking/Components/SignalTimerComponent.cs @@ -23,6 +23,12 @@ public sealed partial class SignalTimerComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public string Label = string.Empty; + /// + /// Default max width of a label (how many letters can this render?) + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MaxLength = 5; + /// /// The port that gets signaled when the timer triggers. /// diff --git a/Content.Server/DeviceLinking/Systems/SignalTimerSystem.cs b/Content.Server/DeviceLinking/Systems/SignalTimerSystem.cs index f9c2d3430e9..0e214ee865a 100644 --- a/Content.Server/DeviceLinking/Systems/SignalTimerSystem.cs +++ b/Content.Server/DeviceLinking/Systems/SignalTimerSystem.cs @@ -39,6 +39,7 @@ public override void Initialize() private void OnInit(EntityUid uid, SignalTimerComponent component, ComponentInit args) { + _appearanceSystem.SetData(uid, TextScreenVisuals.DefaultText, component.Label); _appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, component.Label); _signalSystem.EnsureSinkPorts(uid, component.Trigger); } @@ -66,11 +67,6 @@ public void Trigger(EntityUid uid, SignalTimerComponent signalTimer) { RemComp(uid); - if (TryComp(uid, out var appearance)) - { - _appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, signalTimer.Label, appearance); - } - _audio.PlayPvs(signalTimer.DoneSound, uid); _signalSystem.InvokePort(uid, signalTimer.TriggerPort); @@ -139,10 +135,15 @@ private void OnTextChangedMessage(EntityUid uid, SignalTimerComponent component, if (!IsMessageValid(uid, args)) return; - component.Label = args.Text[..Math.Min(5, args.Text.Length)]; + component.Label = args.Text[..Math.Min(component.MaxLength, args.Text.Length)]; if (!HasComp(uid)) + { + // could maybe move the defaulttext update out of this block, + // if you delved deep into appearance update batching + _appearanceSystem.SetData(uid, TextScreenVisuals.DefaultText, component.Label); _appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, component.Label); + } } /// @@ -166,7 +167,15 @@ private void OnTimerStartMessage(EntityUid uid, SignalTimerComponent component, { if (!IsMessageValid(uid, args)) return; - OnStartTimer(uid, component); + + // feedback received: pressing the timer button while a timer is running should cancel the timer. + if (HasComp(uid)) + { + _appearanceSystem.SetData(uid, TextScreenVisuals.TargetTime, _gameTiming.CurTime); + Trigger(uid, component); + } + else + OnStartTimer(uid, component); } private void OnSignalReceived(EntityUid uid, SignalTimerComponent component, ref SignalReceivedEvent args) diff --git a/Content.Server/Screens/Systems/ScreenSystem.cs b/Content.Server/Screens/Systems/ScreenSystem.cs index 19790f64d5b..782fe38c888 100644 --- a/Content.Server/Screens/Systems/ScreenSystem.cs +++ b/Content.Server/Screens/Systems/ScreenSystem.cs @@ -56,6 +56,7 @@ private void ScreenText(EntityUid uid, ScreenComponent component, DeviceNetworkP ) { _appearanceSystem.SetData(uid, TextScreenVisuals.DefaultText, text); + _appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, text); } } From 7e950ea1d59bf8c19416c060a55b08839ba11412 Mon Sep 17 00:00:00 2001 From: eoineoineoin Date: Sun, 31 Mar 2024 21:44:24 +0100 Subject: [PATCH 082/133] Fix orientation of roller skate sprites (#26627) Co-authored-by: Eoin Mcloughlin --- .../Specific/skates.rsi/equipped-FEET.png | Bin 21114 -> 773 bytes .../Shoes/Specific/skates.rsi/inhand-left.png | Bin 20591 -> 630 bytes .../Specific/skates.rsi/inhand-right.png | Bin 20584 -> 651 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Textures/Clothing/Shoes/Specific/skates.rsi/equipped-FEET.png b/Resources/Textures/Clothing/Shoes/Specific/skates.rsi/equipped-FEET.png index 09c92cfa5f54d70dadb3b6374b83a34a35d9930f..e62bf7af7d8c2fade57ad514393644eaa28f1f2d 100644 GIT binary patch delta 750 zcmVPx%zez+vRCt{2+RsZAVHgMS@8k->POY22 zW(89WY%^4V~l?>#u#IaF~%5UjIsZO zkV~+pX{7&NSy@3S6cWb9#>nIGWMTq=Ku&WWcm5M*1N(|X1OUZl5{k>DUH6RF^tgPp zE5E4%^c97ud$0n2cRNO?VV(fj_$#VycfaF{Eln^nx;{4nS?LD$E|;eYZV?L zSqPvRxU~q}SX+Okd~651^Jgu<U<73y*t8I+(G*Ia=0FGc?x>`H#z(B znfV6DVx<=VRj4^-47-Nhv#u#IaF~Ga!FJPACEpXvt{l4q#Ypc*S4dHOu zmhGUbDg^?81OM^4+1Sx-fpkyagVa(l=4N9U81H6*Z?qJ{i6JY~W2F80Z zHycB0sTZyd4ad+v;_xP#t31#QJ0)YuRtmjV*+f?Nu0-vY*5jSWFEuEolHR=)wq g*^|$9ULYsG0DQ4IG-qd~4*&oF07*qoM6N<$f_3I@aR2}S literal 21114 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 literal 20591 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 Us`Drc4?w`)dX`nDrO(R$0PZK-2LJ#7 diff --git a/Resources/Textures/Clothing/Shoes/Specific/skates.rsi/inhand-right.png b/Resources/Textures/Clothing/Shoes/Specific/skates.rsi/inhand-right.png index 7886baaeda6342587754b9ad910a1bc27fd2c536..0b772c7c8560d855512fe2b03124fa84937f4611 100644 GIT binary patch delta 627 zcmV-(0*w9WpaF{okR*Qrb5ch_0Itp)=>Px%MM*?KRCt{2+P_O1VI0Tt_tk6)vDKPf zM=Qky67Uaj&>`T|UB^NW!9c1*$tF1X2eeE22MAppidZwdNWPxI}k;A@Px^pj+$#i^A=rRGNQb|or#aq|W?>eVn zo1_yFMrWH{e{7hpD-rcO50H`){q!ST<&~5?1NGBpx8#&-l z_I|ofK)q(O+B^rqzFJ~&c@BMZD}bDXp>J-nxIEWd{;Pk@^S%clbeVuuDy8ksZ*X%s z1Hi%MJ{98#ZJ`ig7+Ls%Q~4_68L#(({`- zOt&*yj;prEGxy5c^9(3lm|DGNV@&25nFd0B28>LDF`1`cvngDdp**aG5JCtcgb+dq zA%qY@JQ!Lb4$e3*IK0{HCU!_3nJ`0e1s%+mx6q=SD*{C4nR=J^be4qp6QeggdI4)5y=Ckp@o N002ovPDHLkV1h=iGGYJ# literal 20584 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 From 3adf6c1ecc51b538fe1b1546206a089fee8dd668 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 20:45:08 +0000 Subject: [PATCH 083/133] 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 41e707cb49a..8a8f3911eec 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: icekot8 - changes: - - message: The bounty of briefcases has been removed - type: Remove - id: 5775 - time: '2024-01-23T18:26:28.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24392 - author: Errant changes: - message: '"Not enough oxygen" hud alerts now indicate the proper gas for nitrogen @@ -3803,3 +3796,10 @@ id: 6274 time: '2024-03-31T11:48:36.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26380 +- author: graevy + changes: + - message: clicking the brig timer button will now cancel its countdown + type: Tweak + id: 6275 + time: '2024-03-31T20:44:02.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26557 From d2bee7ec91fef6cb2a1ee9b2aada98ae1ba136f3 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Mon, 1 Apr 2024 09:45:40 +1300 Subject: [PATCH 084/133] Fix GastTileOverlay sending redundant data (#26623) Fix GastTileOverlay not updating properly --- .../Atmos/EntitySystems/GasTileOverlaySystem.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs index c42cfd08da3..89b9c520787 100644 --- a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -432,14 +432,16 @@ public void Execute(int index) if (!overlay.Chunks.TryGetValue(gIndex, out var value)) continue; - if (previousChunks != null && - previousChunks.Contains(gIndex) && - value.LastUpdate > LastSessionUpdate) + // If the chunk was updated since we last sent it, send it again + if (value.LastUpdate > LastSessionUpdate) { + dataToSend.Add(value); continue; } - dataToSend.Add(value); + // Always send it if we didn't previously send it + if (previousChunks == null || !previousChunks.Contains(gIndex)) + dataToSend.Add(value); } previouslySent[netGrid] = gridChunks; From 6d1511124f5ba8f3a2ecc5297600e0dd008cfb82 Mon Sep 17 00:00:00 2001 From: "Wrexbe (Josh)" <81056464+wrexbe@users.noreply.github.com> Date: Sun, 31 Mar 2024 13:49:51 -0700 Subject: [PATCH 085/133] Auto DeAdmin sooner (#26551) Co-authored-by: wrexbe --- .../GameTicking/GameTicker.RoundFlow.cs | 35 +++++++++++-------- .../GameTicking/GameTicker.Spawning.cs | 4 --- .../GameTicking/Rules/NukeopsRuleSystem.cs | 4 --- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index 004508ab916..202daf256d2 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -4,6 +4,7 @@ using Content.Server.GameTicking.Events; using Content.Server.Ghost; using Content.Server.Maps; +using Content.Shared.CCVar; using Content.Shared.Database; using Content.Shared.GameTicking; using Content.Shared.Mind; @@ -194,26 +195,18 @@ public void StartRound(bool force = false) SendServerMessage(Loc.GetString("game-ticker-start-round")); - // Just in case it hasn't been loaded previously we'll try loading it. - LoadMaps(); - - // map has been selected so update the lobby info text - // applies to players who didn't ready up - UpdateInfoText(); - - StartGamePresetRules(); - - RoundLengthMetric.Set(0); - - var startingEvent = new RoundStartingEvent(RoundId); - RaiseLocalEvent(startingEvent); var readyPlayers = new List(); var readyPlayerProfiles = new Dictionary(); - + var autoDeAdmin = _cfg.GetCVar(CCVars.AdminDeadminOnJoin); foreach (var (userId, status) in _playerGameStatuses) { if (LobbyEnabled && status != PlayerGameStatus.ReadyToPlay) continue; if (!_playerManager.TryGetSessionById(userId, out var session)) continue; + + if (autoDeAdmin && _adminManager.IsAdmin(session)) + { + _adminManager.DeAdmin(session); + } #if DEBUG DebugTools.Assert(_userDb.IsLoadComplete(session), $"Player was readied up but didn't have user DB data loaded yet??"); #endif @@ -235,6 +228,20 @@ public void StartRound(bool force = false) readyPlayerProfiles.Add(userId, profile); } + // Just in case it hasn't been loaded previously we'll try loading it. + LoadMaps(); + + // map has been selected so update the lobby info text + // applies to players who didn't ready up + UpdateInfoText(); + + StartGamePresetRules(); + + RoundLengthMetric.Set(0); + + var startingEvent = new RoundStartingEvent(RoundId); + RaiseLocalEvent(startingEvent); + var origReadyPlayers = readyPlayers.ToArray(); if (!StartPreset(origReadyPlayers, force)) diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 52bab07ce8f..5f4610744a0 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -154,10 +154,6 @@ private void SpawnPlayer(ICommonSession player, HumanoidCharacterProfile charact return; } - // Automatically de-admin players who are joining. - if (_cfg.GetCVar(CCVars.AdminDeadminOnJoin) && _adminManager.IsAdmin(player)) - _adminManager.DeAdmin(player); - // We raise this event to allow other systems to handle spawning this player themselves. (e.g. late-join wizard, etc) var bev = new PlayerBeforeSpawnEvent(player, character, jobId, lateJoin, station); RaiseLocalEvent(bev); diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index ea5ab9c2abe..886af60987c 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -763,10 +763,6 @@ private void SpawnOperatives(List sessions, bool spawnGhostRoles, Nu _mind.SetUserId(newMind, nukieSession.Session.UserId); _roles.MindAddRole(newMind, new NukeopsRoleComponent { PrototypeId = nukieSession.Type.AntagRoleProto }); - // Automatically de-admin players who are being made nukeops - if (_cfg.GetCVar(CCVars.AdminDeadminOnJoin) && _adminManager.IsAdmin(nukieSession.Session)) - _adminManager.DeAdmin(nukieSession.Session); - _mind.TransferTo(newMind, mob); } //Otherwise, spawn as a ghost role From 0602e643006e9068f30322d52a797e481c3c15f1 Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Sun, 31 Mar 2024 22:52:49 +0200 Subject: [PATCH 086/133] Add briefcase to curadrobe and lawdrobe, and some briefcases cleanup (#26527) * Add briefcase to curadrobe and some briefcases cleanup * also add to lawdrobe --- .../Catalog/Fills/Items/briefcases.yml | 6 +---- .../VendingMachines/Inventories/curadrobe.yml | 3 ++- .../VendingMachines/Inventories/lawdrobe.yml | 1 + .../Entities/Objects/Misc/briefcases.yml | 23 ++++--------------- Resources/migration.yml | 1 + 5 files changed, 9 insertions(+), 25 deletions(-) diff --git a/Resources/Prototypes/Catalog/Fills/Items/briefcases.yml b/Resources/Prototypes/Catalog/Fills/Items/briefcases.yml index 6be5ae0d302..58a49aa6e95 100644 --- a/Resources/Prototypes/Catalog/Fills/Items/briefcases.yml +++ b/Resources/Prototypes/Catalog/Fills/Items/briefcases.yml @@ -1,6 +1,5 @@ - type: entity id: BriefcaseBrownFilled - name: brown briefcase parent: BriefcaseBrown suffix: Filled, Paper components: @@ -11,9 +10,8 @@ - type: entity id: BriefcaseSyndieSniperBundleFilled - name: brown briefcase parent: BriefcaseSyndie - suffix: SniperBundle + suffix: Syndicate, Sniper Bundle components: - type: Item size: Ginormous @@ -32,7 +30,6 @@ - type: entity id: BriefcaseSyndieLobbyingBundleFilled - name: brown briefcase parent: BriefcaseSyndie suffix: Syndicate, Spesos components: @@ -52,7 +49,6 @@ - type: entity id: BriefcaseThiefBribingBundleFilled - name: brown briefcase parent: BriefcaseSyndie suffix: Thief, Spesos components: diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/curadrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/curadrobe.yml index 07e957a5e0e..aac1cbb3f49 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/curadrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/curadrobe.yml @@ -2,6 +2,7 @@ id: CuraDrobeInventory startingInventory: BooksBag: 2 + BriefcaseBrown: 2 HandLabeler: 2 ClothingEyesGlasses: 2 ClothingEyesGlassesJamjar: 2 @@ -12,4 +13,4 @@ ClothingUniformJumpskirtLibrarian: 3 ClothingShoesBootsLaceup: 2 ClothingHeadsetService: 2 - + diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/lawdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/lawdrobe.yml index b1478a7cfc4..28f63e44e20 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/lawdrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/lawdrobe.yml @@ -14,6 +14,7 @@ ClothingShoesBootsLaceup: 2 ClothingHeadsetService: 2 ClothingNeckLawyerbadge: 2 + BriefcaseBrown: 2 LuxuryPen: 2 contrabandInventory: ClothingOuterRobesJudge: 1 diff --git a/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml b/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml index aa8823d70d8..762204701cb 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml @@ -1,8 +1,8 @@ - type: entity parent: BaseStorageItem - abstract: true id: BriefcaseBase description: Useful for carrying items in your hands. + abstract: true components: - type: Item size: Ginormous @@ -14,10 +14,9 @@ - Briefcase - type: entity - name: brown briefcase parent: BriefcaseBase id: BriefcaseBrown - description: A handy briefcase. + name: brown briefcase components: - type: Sprite sprite: Objects/Storage/Briefcases/briefcase_brown.rsi @@ -26,23 +25,9 @@ sprite: Objects/Storage/Briefcases/briefcase_brown.rsi - type: entity - parent: BriefcaseBase - abstract: true - id: BriefcaseSyndieBase + parent: BriefcaseBrown + id: BriefcaseSyndie suffix: Syndicate, Empty - description: Useful for carrying items in your hands. components: - type: Item size: Huge - -- type: entity - name: brown briefcase - parent: BriefcaseSyndieBase - id: BriefcaseSyndie - description: A handy briefcase. - components: - - type: Sprite - sprite: Objects/Storage/Briefcases/briefcase_brown.rsi - state: icon - - type: Item - sprite: Objects/Storage/Briefcases/briefcase_brown.rsi diff --git a/Resources/migration.yml b/Resources/migration.yml index f5306c8b7c1..eadb76305c7 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -261,3 +261,4 @@ ClothingOuterArmorScaf: ClothingOuterArmorBasic # 2024-03-31 ClothingNeckFlowerWreath: ClothingHeadHatFlowerWreath ClothingHeadHatFlowerCrown: ClothingHeadHatFlowerWreath +BriefcaseSyndieBase: null From c62e90afb364b443c830540e362d21583667eceb Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 Mar 2024 20:53:55 +0000 Subject: [PATCH 087/133] 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 8a8f3911eec..29108f69f87 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Errant - changes: - - message: '"Not enough oxygen" hud alerts now indicate the proper gas for nitrogen - breathers.' - type: Fix - id: 5776 - time: '2024-01-23T20:17:41.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24373 - author: themias changes: - message: Cyborgs can no longer spawn with the Wheelchair Bound or Muted trait @@ -3803,3 +3795,10 @@ id: 6275 time: '2024-03-31T20:44:02.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26557 +- author: lzk228 + changes: + - message: Briefcases was added to CuraDrobe and LawDrobe. + type: Tweak + id: 6276 + time: '2024-03-31T20:52:49.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26527 From 9b08c2c908e344e3356c7cb4bd75efca0adae2e4 Mon Sep 17 00:00:00 2001 From: eoineoineoin Date: Mon, 1 Apr 2024 01:07:13 +0100 Subject: [PATCH 088/133] Fix some text overflow bugs in HUD (#26615) * Don't clip text in item status * Fix overflow in examine tooltip --------- Co-authored-by: Eoin Mcloughlin --- Content.Client/Examine/ExamineSystem.cs | 27 +++++++++---------- .../Inventory/Controls/ItemStatusPanel.xaml | 4 +-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs index 1be472b06d6..45db4efa53c 100644 --- a/Content.Client/Examine/ExamineSystem.cs +++ b/Content.Client/Examine/ExamineSystem.cs @@ -212,14 +212,16 @@ public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCurso var vBox = new BoxContainer { Name = "ExaminePopupVbox", - Orientation = LayoutOrientation.Vertical + Orientation = LayoutOrientation.Vertical, + MaxWidth = _examineTooltipOpen.MaxWidth }; panel.AddChild(vBox); var hBox = new BoxContainer { Orientation = LayoutOrientation.Horizontal, - SeparationOverride = 5 + SeparationOverride = 5, + Margin = new Thickness(6, 0, 6, 0) }; vBox.AddChild(hBox); @@ -229,8 +231,7 @@ public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCurso var spriteView = new SpriteView { OverrideDirection = Direction.South, - SetSize = new Vector2(32, 32), - Margin = new Thickness(2, 0, 2, 0), + SetSize = new Vector2(32, 32) }; spriteView.SetEntity(target); hBox.AddChild(spriteView); @@ -238,19 +239,17 @@ public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCurso if (knowTarget) { - hBox.AddChild(new Label - { - Text = Identity.Name(target, EntityManager, player), - HorizontalExpand = true, - }); + var itemName = FormattedMessage.RemoveMarkup(Identity.Name(target, EntityManager, player)); + var labelMessage = FormattedMessage.FromMarkup($"[bold]{itemName}[/bold]"); + var label = new RichTextLabel(); + label.SetMessage(labelMessage); + hBox.AddChild(label); } else { - hBox.AddChild(new Label - { - Text = "???", - HorizontalExpand = true, - }); + var label = new RichTextLabel(); + label.SetMessage(FormattedMessage.FromMarkup("[bold]???[/bold]")); + hBox.AddChild(label); } panel.Measure(Vector2Helpers.Infinity); diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml index 2c622b82b9e..d469e6ced03 100644 --- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml +++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml @@ -1,4 +1,4 @@ - - From 652de4d3213fcd222b180a994113c3b60899ed1a Mon Sep 17 00:00:00 2001 From: RiceMar1244 <138547931+RiceMar1244@users.noreply.github.com> Date: Sun, 31 Mar 2024 20:07:30 -0400 Subject: [PATCH 089/133] Adds two milk cartons to the BoozeOMat (#26635) --- .../Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml index 793121e2f2c..91dc9f51cbb 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml @@ -14,6 +14,7 @@ DrinkBlueCuracaoBottleFull: 2 DrinkCognacBottleFull: 4 DrinkColaBottleFull: 4 + DrinkMilkCarton: 2 DrinkCreamCarton: 5 DrinkGinBottleFull: 3 DrinkGildlagerBottleFull: 2 #if champagne gets less because its premium, then gildlager should match this and have two From e53bfed5899db7b7d840498a04df9b28644f8b4d Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 00:08:36 +0000 Subject: [PATCH 090/133] 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 29108f69f87..07fae067fe2 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: themias - changes: - - message: Cyborgs can no longer spawn with the Wheelchair Bound or Muted trait - type: Fix - id: 5777 - time: '2024-01-23T20:18:01.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24459 - author: Nimfar11 changes: - message: Adds solo comfy benches in different colors. @@ -3802,3 +3795,10 @@ id: 6276 time: '2024-03-31T20:52:49.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26527 +- author: RiceMar + changes: + - message: Added milk cartons to the BoozeOMat vendor. Got milk? + type: Add + id: 6277 + time: '2024-04-01T00:07:30.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26635 From ce6b6fd4284d9dd576f0dc2b82152f4bf04bade2 Mon Sep 17 00:00:00 2001 From: UBlueberry <161545003+UBlueberry@users.noreply.github.com> Date: Sun, 31 Mar 2024 21:58:27 -0400 Subject: [PATCH 091/133] made the hover text less vague (sorry) (#26630) --- Resources/Locale/en-US/traits/traits.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 98f0817f744..fa17dc23ec3 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -34,7 +34,7 @@ trait-socialanxiety-name = Social Anxiety trait-socialanxiety-desc = You are anxious when you speak and stutter. trait-southern-name = Southern Drawl -trait-southern-desc = Are you wonderin' what this does? +trait-southern-desc = You have a different way of speakin'. trait-snoring-name = Snoring trait-snoring-desc = You will snore while sleeping. From adaaf0fefc989a827337796257c06b500f2ff496 Mon Sep 17 00:00:00 2001 From: Zealith-Gamer <61980908+Zealith-Gamer@users.noreply.github.com> Date: Sun, 31 Mar 2024 19:12:46 -0700 Subject: [PATCH 092/133] blacklisted throwing knifes from pneumatic cannon (#26628) --- Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml index afe4644517c..9cd1bb29408 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml @@ -263,6 +263,7 @@ tags: - CombatKnife - Knife + - CannonRestrict - type: Sprite sprite: Objects/Weapons/Melee/throwing_knife.rsi state: icon From 29c81bcc052ab53c5b10d9a5dbe1c761745e3d1e Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Mon, 1 Apr 2024 02:13:51 +0000 Subject: [PATCH 093/133] Fix radio jammer not blocking suit sensors. (#26632) As it turns out, they are not in fact on their own netid. They are actually just on wireless. The way I had tested my previous pr led to this mistake being made. I originally had the radio jammer block wireless as well, but decided to take out under the flase assumption that it suit sensors were actually on their own netid and did not require the ability to block all wireless packets at the last moment. --- Content.Server/Radio/EntitySystems/JammerSystem.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Content.Server/Radio/EntitySystems/JammerSystem.cs b/Content.Server/Radio/EntitySystems/JammerSystem.cs index 1258cc10fa0..5a2a854017b 100644 --- a/Content.Server/Radio/EntitySystems/JammerSystem.cs +++ b/Content.Server/Radio/EntitySystems/JammerSystem.cs @@ -54,14 +54,10 @@ private void OnActivate(EntityUid uid, RadioJammerComponent comp, ActivateInWorl if (activated) { EnsureComp(uid); - var stationId = _stationSystem.GetOwningStation(uid); - if (stationId != null && _singletonServerSystem.TryGetActiveServerAddress(stationId.Value, out var netId)) - { - EnsureComp(uid, out var jammingComp); - jammingComp.Range = comp.Range; - jammingComp.JammableNetworks.Add(netId); - Dirty(uid, jammingComp); - } + EnsureComp(uid, out var jammingComp); + jammingComp.Range = comp.Range; + jammingComp.JammableNetworks.Add(DeviceNetworkComponent.DeviceNetIdDefaults.Wireless.ToString()); + Dirty(uid, jammingComp); } else { From 311ad83f058a9dbc7e47c53217623c01cecffec6 Mon Sep 17 00:00:00 2001 From: hamurlik <75280571+hamurlik@users.noreply.github.com> Date: Mon, 1 Apr 2024 05:14:28 +0300 Subject: [PATCH 094/133] Fix dirt decals in reach not being cleanable (#26636) made all dirt decals cleanable Co-authored-by: hamurlik --- Resources/Maps/reach.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/Maps/reach.yml b/Resources/Maps/reach.yml index f84136e9ec0..9cc0887d173 100644 --- a/Resources/Maps/reach.yml +++ b/Resources/Maps/reach.yml @@ -529,12 +529,14 @@ entities: 7: 4,-14 259: 0,11 - node: + cleanable: True color: '#FFFFFFFF' id: DirtHeavy decals: 222: -18,-4 275: 1,13 - node: + cleanable: True color: '#FFFFFFFF' id: DirtLight decals: @@ -574,6 +576,7 @@ entities: 283: 0,8 284: 1,7 - node: + cleanable: True color: '#FFFFFFFF' id: DirtMedium decals: From 1d2bf51488f4080b3543db9152bfc881308b5e28 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 02:14:59 +0000 Subject: [PATCH 095/133] 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 07fae067fe2..82536b2ad28 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Nimfar11 - changes: - - message: Adds solo comfy benches in different colors. - type: Add - id: 5778 - time: '2024-01-23T22:15:48.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24203 - author: Tin-Man-Tim changes: - message: Attempting to microwave metal now can cause the microwave to spark and @@ -3802,3 +3795,10 @@ id: 6277 time: '2024-04-01T00:07:30.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26635 +- author: nikthechampiongr + changes: + - message: Radio jammers now actually block suit sensors. + type: Fix + id: 6278 + time: '2024-04-01T02:13:52.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26632 From c4f383c9ba3c99f02ebb9afe0e4aea9b663ca65b Mon Sep 17 00:00:00 2001 From: no <165581243+pissdemon@users.noreply.github.com> Date: Mon, 1 Apr 2024 04:25:36 +0200 Subject: [PATCH 096/133] Replace drill_hit.ogg and drill_use.ogg with better sounds (#26622) * Replace drill_hit.ogg and drill_use.ogg with better sounds * Fix attribution source for drill_hit.ogg * Update Resources/Audio/Items/attributions.yml Co-authored-by: Kara * Update Resources/Audio/Items/attributions.yml Co-authored-by: Kara --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Kara --- Resources/Audio/Items/attributions.yml | 10 ++++++++++ Resources/Audio/Items/drill_hit.ogg | Bin 40866 -> 40108 bytes Resources/Audio/Items/drill_use.ogg | Bin 45403 -> 54931 bytes 3 files changed, 10 insertions(+) diff --git a/Resources/Audio/Items/attributions.yml b/Resources/Audio/Items/attributions.yml index e63b678ef81..c6fea50bd25 100644 --- a/Resources/Audio/Items/attributions.yml +++ b/Resources/Audio/Items/attributions.yml @@ -107,3 +107,13 @@ license: "CC0-1.0" copyright: "Bhijn and Myr (github username deathride58)" source: "https://github.com/space-wizards/space-station-14/pull/23476" + +- files: ["drill_hit.ogg"] + license: "CC-BY-4.0" + copyright: "Created by vanilla with sounds from MinecraftGamerLR and stomachache on freesound.org" + source: "https://github.com/space-wizards/space-station-14/pull/26622" + +- files: ["drill_use.ogg"] + license: "CC0-1.0" + copyright: "Original sound by stomachache on freesound.org, processed by vanilla" + source: "https://freesound.org/s/262213/" diff --git a/Resources/Audio/Items/drill_hit.ogg b/Resources/Audio/Items/drill_hit.ogg index 0f8fa631aa1a0943d7cad0e33ccaa1d703d3b8ba..dcdfe5df3a1ca5c5ba0f1eb233bd5dbd5beb6efe 100644 GIT binary patch delta 38991 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 diff --git a/Resources/Audio/Items/drill_use.ogg b/Resources/Audio/Items/drill_use.ogg index 82f37cd35bb2c3e233211e0b99ebb0e09b3ea5ba..6cd027d3b27e27e8232eff2312b44d6374015a08 100644 GIT binary patch delta 53931 zcmb@tWmr_*7dSddBPD_$DIy?J(hY)03@Jzr-QArB1p%eIOBj&Op;Jnv89D`|yPLU# zzQ6yy_j&Hedp?}mYp>jEueJ7>efF7Meh^!N3MiSItAZYY{=3?2|21T>Kn^I>C~o#n zMwZU^T_}>p|1hr$Q2#dSQ2uiM>qOq~IZ=?mQ_GB_k0cTQ?`s_OpCoQ1fu^N{1)GwS z`7;|!BX#>|KkQeWuV3@M=6?24#ni;r*wW7Y**iNoODB6fTT?q1rh5e_=>OQ6t58t< z(SeOFEZRWAn4AO%VT;BuftNhzdK_lNoeSR3B=HERgJdJHz)M*EAO|!gT1T5X`gx}z zzMz^S(|7Up30W*wOo_MZXrT8Md3%^W97TfiCJhAo6y+Skfkk3WD2P?yLa0(m z77!)CNbigfen}4{1cQ+X&h-~2gRmYxh?UtYl{u&h^|t6$lcMAixMka$si1#i&?Ab0?4pmtjD5^I4pVie*9E7~~~A z42hwSpMY3%lmepAij7b|mD9(iBKpJB@crR*H z{3SGZ6e0_SDTC$cp%W5JC9nx2`2rX@SQaS?B3l3(gOrrO$b_(%J|oQnpw$VGuN=@P z#xJ3NwL_8GVKw*Kt3Rk%-m_ShbR)GFActtpQ8Qr`&e2V2JRkriHnw8w9)pCj)=(N3 z^SJ*V`xB&DFeHMt8|jq4X0akU|wNF68OJL-xVB`p`LLs5eg-8AAY|%FawdEZ zC3D7?=tj1{z4t99sz9RqsUZY2F_C~k%D+J%LW6+3&SlW&pkg$T!~+mnV4P9E!1^=f zGz3NgDq#Zalz}QLUkO#da;&EXD1*WAD)Jm__kC6cDqxQ8zkLO!VfTH6NYSvc`#wSm zCMMVflGg>P4ho$TMDikaB3%wa%0tpYp%X^if9dFi8q#cV!9O@oZD|ub@a`q_fojxz?%#1Uf*&1Z6#-P?zRsB7ey8caSKcHe^bR zW6&BCO5B$n2`m6F46;-$Rzt4U|?CM`^dNtm-~3T4}kwb zYosyvAeqo#tTji_KhPNW-g0Y>3CLfpbwBJs&>Dj4kHa{SNp|n|`w6)R|K9Rq4{*tVwg2G$`^g9sSfjj0 zr#UMkBmN^Q=#78)^VG*-Xe9R|!a`1MnBm`uj{@h!{q<+OIyp2gfapGkk!6VyJx98QI-VP9Fh$(?tRj(}JL_YsM#eN#x9iIlfY z_cX|H=^*)#hymF8{rHislKT96F%vq@`y%&ZU~6gK$WbQ=^Ml^dPw{tvN41tL9R7}a z4g`{7LJk4{znO{umzn=2>J>7}|AxRlXc8s>AoK1YM&jrH0~0uWpUi&|$`VXaBn#%> zS*8tC2LBI11bzvP`=2c6eJ=h-LUbQVDuVyw0@*@>|3v)_F%>ZOKf1pNAdcwX%zrG% zkh>?${)<80{zmG(tb2jTaHahp0;%b*_P+=f2`u{mgSgj(H0vJ%sp)^Mdnq8$@ZTwY z6#v91|EUJ`M>?FR0K>nWT0*XT|0A|~@jn4{$m;T+sQ;Pq|1$_6<^O-`0DbSn#cbQa zrZCr(6=bHwWBZ$TPmoF0kz^bp&WKE1h;`B@Wr>a4+)=Pi1rJ#K-d!rl%$VTZ_#PJn z2A6a_F%C-(P*Hh>ES$(HIDsz#W`#Y`4y{;&SYu}AA`2%gfQ+ru=71=$b;&B3ak;?y zjQC3?*pyKi&m_bfzn~f^0d}TiJs=Q*tn3^U4+)%82I31|P~(DrhC6`X=)8`J66`A<4WUqKBm9UgDmb7JH+$Q*u@8cmz1!V&veJaSAq zG~OUs-64R~|1)sEmi0(M?Nf3V0qC$DTAT!642J*8#h z5D=GrkF3JT>lx?*S^CudLoCeJ_yvVU-$+PG$$;e*l~q3cpNCq1e}5z;00O;1{`mX< zdv1M$46ys_{<-y@a;9Jj{M63H)!g0P%GCq;`1%}y2t*)K5Qt<1A}+V{2BqyeVf9|w zI%)F0uIt{>q|g58NbJoi%`Y}T+s(Br&*=;C3RRlfX|v-?K6^d&jFQ5&-gEWCYx^Pv zcEO_=0-!IY%+#7MAj-<`F+k2eo31HOd0^P9n88eygolL%)6muP;sK#LPv9W|n0v+ZTlPMIvq!#Y9Ez?SXln3E=W_b*b+A zs{NME>1TG0`Zh_57b)J={eSSD5NGzPIhHQl+5vWW4r$-Lze&DY+h+Z-Q?ER#1WyUi zL1=}^rJG?+S)=aojG!5GI)S9`PTA*M?}i5YUV2uBp3QI1ibS4O5R^5GotUyT(9md$ zfT^`pZ`TsX-7|eX?Gn6S6s z_yTNjM;MlDyvs9p7e#$_v5LEMW?ABnF?V`{_hEpa(G|G49f%c+T=PoW^Wyb`%ij>g zuTw??W?Z@hCa{C%W&4abOZ*)C9teYEXuss+NwJ!Z$T=y7i6m*upAR!d)~d)6FCS@; z2d~f5MJfIAx@>&cSNY!cQpyuB+mb*%83A5COrs?{_3n|98VYv4@wBAYenq?w$hqBi zEL;liyGAsgR2J?}?$lxqHbyVXA2w*#f3Ldb)O%O}xp8yNl&10Jt&K7ljjBx5zxrAB z5>J%?8e|(-e&|E_s*VOU;@$o8eGO6fXrUyRa&KfB82!4#mD-K=WbFmqjb#d)l>oe0k`?cCs-`ze+^2=pddGgw<5)sh4iRpHJ zXd(sa-1Fluv*#N8E>dCkAxSh|yOq#qR_eu->8C4o?-;i8o0;HUwY}OgGLbs!FVa)b z=U@YAX$y7+hf~!9Bd_B#NVu)8(Er>vjf9X(ysbN5c&JE{Ir;gb+@oa&IJ&(W>S?UZ zIkxOZ@0NsVV@Z*4QVU34TbA8ZD z`{>MHW;zq(wPWK$mQ!F46WiRUUOMl4{MX$@eLwDr66EguPIy3mIy2>f!5J)t5uQ01 zJsX^QvouLMdAcCacRHY~>vVGCa%v|oBDT0UTS^WO=giPP2U_BFPqzTJ(ma>m>&8aQ zyQQ<~Z9TE#3@VqsD=p#{g|1aaomDyZ&+Bh98@FYY)cu&MN^28?6OVK+!(D(H)y*N1 znZ#SirWxxh^9=#s@Dv`Gzn-H#xDGJ`CLKk4HZs>& zsXp^5&6dsf^h~tI^)FVnoV%S^HGFaZI6xYQN8rATg(`Y9gJi)D%uy^ zmK_*P?1Zj##WO~U$C_-Vo?^5Ypn-#=uw{rP68$2S`uC4O2 zmom2VtlXWZyuG>1#m)Z(7Mz_AYtKQGr<+TyAQ?3jbx@H+9qq|Y)tkQiIIEogwWH_Y z{gTu88LsaIg$V#t5?j@lde9j&-S*^J?lflan%0IVLomTJkwcbx49wC+!DtMTcHOD# z1{SWy>>jQY;hQca02=gto`9~j*^1G&K4{S3>ro_n*K-5ckMQ@;_?3**vI#G=XWFv1 zvg}{{MqlTh)%-AVlnHUKttBVqJa=+CMBF)D zx2|i^ODT;3k;z%EzAip<-1gNtKfD)eBl-&5>*TkqYV+6nqwV9XL-v8n#M!D}_|~&mcg#nr!>e)w7GQ_%ncCm8%*PL>FPCheyb2wV#QJ`db{jV@qW1o= z)?FoqyZa?D^O~+DI?AvvN5Egg|N2|uhl+BZWwd-@{Dj3i{L->mJAUfA z%5Yf+mO9Fh4@D{;y>AwS%fKc!*K`{d(t%0Z7F{`_r?QE2F!S^d*%7%(pDEEDw3CTRlVi`lOs z{OCpTd&a8$lho9Asy&qiCX1DmBkjpeq(VV=IViJBrgIPkt}?qdmCM@hd;G??m@A2S zR=W#->^|vEp?wDRaK?3joPui@gH!5lRIHx3WVQT=M>EEZ6XFi^>;jR3(u zX{rH2c#d9Xj;xxh((ZFztF6UE$FnXmv&C`w2(O5q-|byBJ*{1ajiqj@u+VvPzES}BA9svv!QepzIc6ngUDufrBs;4b zpV!uQ7XC{BdO5YZNSX?Xt@-|ZyW22og7kvv%D=1wo2 zNpmR~dv#T#5a)N`F;`jlSyf1iYi?WZ>i~1#M04HrZR$38k?)CzY_e#2nv=c=Jm!JQgrgVP<6y$JO(0bphjk{!-EGQl30lfw&QfmP z+vI=FUG`Q5Tlx(GyPv>2V&{gfuQ%3llvh8vN?XvUkFnoeCH#6H{TO{}(p()pJvv3^ z9k_b7g3t(CWw71jYAzL;xC4$(9dnX9PY5AR)#YSvs7%LyHlJ-FpB5O3N{kU%rPMgy z*9(jACv0JILw^*S&g{G)qL%{mh${q75zoBUHRXAdSp>;s=w!nM>wsl@hqZfG%W>gyK0m|H$LS2CA2ohD}KW4X6u-#6_FD!@zpX21r3BQNTz;$^7z zjKK(cQ$3x|&bwP%+$lQTXFb%>`VbA|&zH>D$Nq>Pr<7Yz(RB`^PEY^bZpO%X^DO!c zKU$VON-Io;CH7qXC8EOo`1)qovb?EwE#bCVs6Uy~U*X4&&WQhRYyie1wb|!W>pVX( z-c*m8pbp~D*qt?Np#s@Bkm!aW1pH(t-&T3H?*oYXY%Z|4r{s1l!|dZ}@Z69VrcuY~ z@>ILTSi^oyMEr+Sj1eC5z)uwFsrsRKPekfwV_T%S3WQt1RdD zVX52Z<7-Qtob8d1kMMhGn`QH*_V8;n*}McNj_20_d`6gIy|C=2l!RQxZANErx>pj< z;yT2(n$HsJp{+EOXe%m(@bP1WU=0jg-}|r`k*uAGzfCrw{!2D}KLV1!?)?7zN$^RP z8!tR`*o#M=7SOgybZYQXUpv&Nr$j}~mY@?aqQ4f=Mbxy*yD(82D}=vSt%&Ot?$$cA zl2%uf4PH9@ru_|NJ{!6@VSwV9QCnlArW&(!BC|91a_3Pi%>q+r7O9YZS=3FO`}Rwp z;>gtxqI+*HuLUEIt|Pg2R^p)#jba4t$oIi_LEwj{LO}Ric7vivjopv7xYWR=O&v%6 zBm9;em^8TB1X{_j5e@d+)BIbWpD(3aDmba-r1~VcX9UCW4ljf}Q4mX^6sJMi)u*ao zeHSrHQ|(gCHA)<|wGS6Q#_83JRQ0C$Of9J07+DpTQZa>>Mb$!1c5K@VYeLiCKcG%D zH!`+(&)15umpQ$QWbuYf&863Ps=YcZY%5ot@1ev|Hv~v? zY-LX9?);KhQORi)=Jn#a&R4f1KUXb7TT`*6Y7pG zjUfTxA}W0O)PgmN3*4iN_k&yuw&uBV2Jcy)#aO?>{xG9Mk9$yOIvMQa zN`94Wm27GEP35?pn9r>(RhWB@-{Lnd>q#r+sVvo1x+9oj1_v#{10^5n200E6M5U<|PLSjgi}IuDkYPNoHF2 z93^{6=rS#zi<@-JKqfW%18Y{(&`8FrpxOCZ(&#Rk#J+WLn+^(B6KZXy{75>wbuP#J zN&?@ys>iaJY_%Yan3w1v9;}ZVikEoxb>6>uPJxKbo$&*7i$1cikMiisej5YZ(`1a4 zltVhSqs-p#Lwqj(oQKP^+^%Jzw7Kr3C%zgcdG8zdaTU`dnG4k^j7?4=uaXk;DBd{c zgQ$}24rsm;Lmw0L-`)-9->sX!dpGxQBY!_s8Y4jf&eVYgb!Uln`%E+6u z*r1)J0!i@!DMrmH~KYa#nZKC=F|vr5;(m2=!oEU?kjB2s2xh^3nsAG6+1xIN1_w;H>)p9NE^ zz&d!OAOla~P&u~j2ev{Ti5JVY)DKNMsf@n82H)1Pv&(NeBxtg>7};~xmbKp8-SgwA ziKjJFLhJg)H%8}vt22WmyRGLmjRAQyc)Yji4U^$fEGZ$>7_$S)Hl29z@M>P!oPacr zV~-lZ24i!ATcp|3b+Yd4jUlNZps}$d`0AT#W|PwK`ylyu%(NIGYMb4@ z=7EKB%o=Iw6mRkNO-d4rS;%Y3&sY>15j7D*v$=ike&0(qufHXb7k?=vrR%26NGwS$ zb;^8sP2>7~#OD*5)I4r7@y2`*886xR_ODxfU{A>3_;{CZetBRTsN!h0i*g6Q59>Kg z^!~!v7_n^SPxrVsfUoUI1BJOpW{0X*fpyUe#t>)kUegl>!?d zK$=gis~E07Ke2ZCgkjf16Ni6-_(PG?-%u*2YU%5nbzg_qs9p2V7g10Bilsxi)r(|d$%f$|U0{AYEiMe3`QS8B_| zX@==QA;rrNK%zBs7dmLh1Rz|3lzQMGpeZ?sY0Jj2U(U+=#gAQ~V>h?3d0NX9c~_1& z%aGV6H;bSEvm#r^1lN~KX-#&&WEG^1pl=iClYIp!1v$~$-CSwcOZDD{mR3mQ*u;Kfax z9Lm{hui9$VJiBC9{nN8eJajt34g0zDj(c$@H#Q@zRwzzXophi!Ts!QAMJ%OY;l~%% z=V_WMRqLl#)@x^Ltq#OA0s_N8&;F1?eh}aLUS?cl{-z{;%@cUNn7772&zIU8g40g5 zhi~3|EY)so*Z0N~gTP-dQsWE^2tU?Cm9kc!x5(#DY@0r@^;;sK@rOUeisRJsqU2(~ zE8la)SfOYgx5%UVl3It$YqyeDxU=O~Nr!cuM?YI5r5ntu?aJl{2vEaFHs#E^ z5BF-EM_2m37GojQ)cigqw5@clu;{w(%k3;j&Mwv})e7GYzVlhx6|?k?UO3_EmtH@W zT9))!x6um{wck6_ofr}{bds(8xW=?F@Ihk*l|FP5wI-(!CPtdI|04Of;RbLk4Z5n!@1ELm8?K1b!8qt(O}RV zF$&1PH%891C)D@+Ojmcyy6Ag97+AAYV(083q+A@iIg{}s`>1>6&~$CU^cyqYktj9p z@~{Dz zycxs2YegXUlp?azQ6?{M!E)Aq-K*j``EEG{w()+e%I(^CLwqeGZ|z_qG>ST6G{(+* zt$Tn9@5)$`n<=hlhn{XT+B^61#!hjhI+a_>nJ0+?y1#%%eHJ`YV;udiyvqFAqwbKc z=0^zw)=;Am+h{Vd^p;tAA#Ij{+*L*d4JxEP$|P{zKe#jl8B1j+%n@kbmF@3vfifuO zlWp=(xHikC9^a{Z{2XMtZ5T_OU+uY+s8UGGIaQu1rr0uim0^7KDD!l1ySO2<b?HdYfyoccKD<{NVS?j;IylGST1!ba#Zr*#*zNY& zP^J^NL+k6kn;k!9Q-{}l3QRIMUm}&5m4#;TC``QC0)pD=h*#pCdZ#ik*cV27)!6{@ zc6)|#+1FpcsTDq`^|X6EacPwAP0cGmJbiueT&fG-78fh5$6fzDr?R=Pk)nm#_5O{^o$pFh9SC_*#pcp{u8FBBg|m zX8zfYFx6Yvr~5DHJ7osRT8(WVYMFm+{bKS0%bzxekJ!Hee_}$R_ZmCKgX!bbZYdA3 ztRhd)41khxHdj{;I-}+Kcxsm0b-C@x15-%fCX1l2$IG^q&*;buzC4P{?|DkVvyjRr zK?lEhJ>7YyXKKnnnx7Xr6DhD%A3?y8`6c^{tz1rRNblxJ48H4&OV!uH4VrVZW1bDk zkMqX1zWar|?>rbCeMU^F$VHw-K2bweC06~p1{H`ejIDv6QsN{qQYXMY_h`rKjp@r7 z0!LEy3E&*&KfHv!z6fx?g3Sy(V*GRbtM|1K@IQICh$(EDAL!v z+dh1r!M!x2B`Es2Pl@vc-iJHBw07z-sx5C2wz*h)KBqENOyXQim3y|kY0VLCDVR8J zkq;=PA4OJ87BA$THT2c1@eTt6GjsRX*Okun;InGOku3MqW3rb$a#rV?&xBr>tU4En)cK6!CIkSC ztI3liw54WqINWuZH=aErnAEy_Lg{zgQZ^+IsOk>U{Vp!l$Rk$PANPGn2;9==H1x|J z8=jgqDfS&R4wfnUUWjTxV#PI#zPNI<%F3*zMbsW{GICY>$hd_j zdeIRSWvcT@@d2o9ihpjKxQ#bg6&DQvl^3DXBUk&3ggF_uPo~oE*ikz#lv5f!-ES17 z-0_qWBuKnWacT4uSUbLNc1NBIKC!N|+}=AV5oQb_ucG$-SuWT=yzx~JPc*mDGp*i| zm?n>7&pJaey4%JibVf@|&uKDZeR%UaX*Qn^TKpOTA6WUePR2A-KKSd{@%9MFJ#M;O zb6QxxT<`&&&MyDvh}9o*O;d0ob)`xd&e*q`6m!aJjR|D>X<=cY7Y)*u> z>67%JmExJ1MfH<_?oyWXp8V-?KJsv3L=BHT-s)=HpsOMkRoZlIjJLHB^vr!{EtrwI zTc+`JrCGE{hvieRv*BwvT-^fTPh<97)t*&?tOe2#vC28(9~p?TIS)v*ec^4qXPSmPlv{8cncvu<9Oyx;lI4eF>*hX_w; zH#YS~*B^XqA?+m=x{E5<&Dzf5#NZ~cG*PqXhLXapnrrlT)bXmv3Y>kwyDi~F!+s`U zJb~$x zWZCL8>kWy!2}3;wGJ5;3N%qLMG3$&+H?Pw4^!VPML8}tu$@Ey?3mxxy@r4aZq&a?# zw2$QTTWtewXikcNl=}RpC5gkQdV|%Fa3TjQRtHx}A`6CP93M$*uien;_Xn0~Va``z*9ElCG{ zMWAWO$&n*GhqL6MW26bXyz%~J4bPfLt-tX_=_QNh2Is=WSJOx!s*P#(&4YY*9Nxn7 zoPCNI34TZtwW(dFXDZH@AmUfIGuZ zDqO*WKgKR0Vn5NVXd2c_Zsz-KmNhSfZ01FvqGF@QDEB&D(0p}bt#_zc=141qlBcJM zwok0IYfDnzHDnJE9E}!tcvObi;_vTc(Uv;@z0cosq71iYm(tFb>R{BFAIsm3>IsBq zi)X+qiBA|-`BZr+c6!MZ5mnyrFoyLV>D#X2lN0qDU6z#59Md(k1tCBB{t##Mv z3?jyTn&M!0wTSZo+Q~@eZMC;d8sA(qChQ0vkKpV9HhkBQlu~!NI=ngVzN9x?W{PhL z*%TOhGYN=(UA|-FbhCgw)o=JR-@V%?9a8yb{SPjU&^yW&!i^IYWpz?EjgGxe8`I4S zzLjUPuM<;1WhE7nylWmrln2u-n|Ln17oX1nA|(+9PNLb@zv{vExYDWuT(#FvC1dq9 zBR;-Z2bdovgKjPaK^Z}F7TqY*Xn6dhNqLt;KgN>6Jr#GW$KxgrXWWLWHEC!fVvKyA z`kX7wEGp{Is29aIx=YP?%}DjrH}x%u3r6d{}aS@G?Rvu`US(pX$~tJB_y0p+_luXj;-))?7xE###f8%SYPyAD|e zwyC2gXu;#?vp>P0lU1!^C6>j=A}yK8=&I6ybL& z0egO8)gKMMCF7^ZxXb<~nste$ zvBwPp(nMa+#cuAvoI=KOi%=ua^7hqHYw+^S_+{s$5kQ#{a$1^yZdXU=9eLH*;U=Z} zx#aB5hgxdZRK?w{FU~Pk|B^wGt`(y;0icQH5OLHFV#zAQcYEPo1kSwWYOmTHBsMwY zEhvkRkUp6^S0(hOC}Iwc>d&vA9krcXIIFhIuFJIK~5R96{9cr%b3r~ULfnw^{D7$aKi@CX42}X z((U37WgltJcyiPC=dWy)M1IB>o^D)|sF~?_ouB8Q016t~`4u;1JCojg)yuTJWKQ&o z#`T(K_NHD{kUeab*C}3|8sH-FwcT(9p$jwTM2Ah!q1%B1ofO%MiMzMo^Pdt(&6Uw# zUP&$}KqEGMTi{g>Y?uS1mVX;H_2TNe`;yX#Lu+{(`Z$L_HzVj=8&hWWbZeGsKJAyZ zB&LqB0?=aEqF!id6%B%`qj28H_s2^y2I1hfbYik%gVrKy(V$Aa0n{NqCL57K9hE0! zZ@chvO2qQ-LPz1Kdu8yI!C2J7uK*>wzNFUDFb1gZCYt_t@y_&<+tC@OG=nxE3EfNLn+yy|69zaOFe)9-4q~{J>Y! zy_^c-T=@380ce2m4t{MvyK04kgd1_e^zBLBDid+v>lzm`-^rHDeVTom~5QPOQ+EPP%Q9`O4qfyvdjC;k?10i?JaYJV-59 zU4HKVr5p9o679ep*Almk-!z+o=aGx?bqAz`7hlZ&4KuKi&Y)xPXd$ z(z}pH*+D_i?Zb4QxTChk(`r>!n6;4Gg)zO8fgd%0^QE3%9mNxIyV=+V1n= z3Z}osEo&602Xc8fcZ@Z42?WwH^Be)++u7dS76B#9;3eUZ<3IVmMfH&!O=)84T`qbF z)wp7p#zu)TQwm_1O1j>f#sDCv-8wGsyzrbS!1FWHQy4bpd&clZ1)-1q(tPhmD23m zzU^EpVw>S;`syVkQbplybvm(@C1$I{mk;+C^A*;{r3$ktgt;B*pVa`rC9z8i-*A)( zW5z$GZj|o9In(&ALG~hVw&(<9C7fGBC2-}?@B#L-)>r9Rvi?}Ai|@FxkIHr##m``k zXFZ*U*au5Ijp|!s$hRm5-ypSWHU=l*!pM&yav(FA8GVDKXGwy1YofnMVzdS+rB(w+ z8-0sQ7Sp>P6tBlpR1^yV2jF^!0H5mi*){ZmIO{y1{oRYYh}(gk7+uLZGh2DoqluH0 z8u7-t_s_9{ZWWkeK+)cOvkSMr0=;`683OcEvLX`r2(#0oshIap6Zn zJF8BzyrvZtF2;uuwbLL@gzSzX{-*+?Z}Te;RSg(@ogRmxx0^`=>}Pu46%!2}8-6u+ zgvA^|8?*yp;c_~1H%q>73Sgll${kBa@Svlnxhi3P+tt$k4HdA_&~%4W1Uvp3gG)SiF{ zzYN$6FER@#3;52HTCoqaPr2T~eY@20bI?B{4WGk^I_Kwah3}5~#RTrC(`Fqn+WhbZ zHHvrgIhg{bWIo(UtGA2KqSt<5vosBn7_M<2%XK)ALo|iY{bC>?-6KcW*jhb7bLi*N zcylPU9LV>3-QM@79XMMf^=Rm>J3HAK>>Pm+G&nR5h}WHu_pBuzku>frEzB>vqboc$ zjCd9Uz5VD(?$40Jzy@dD9VPPdPO3h>z4df%X2k(iQ)+ybsByb=O9H)h>t4<0;U#g{ zkH_3b_c?bvQocAa0|N6?zf$>uTbzc=->#|Y3%fOnWi@Ql&lXibJmWjBDO&%+bE#k{ ziy^2A1Qm!qiCc4H|4eEA9`h(z|8sZy+54?U?jg=lgbV)Lf>h`tix>k7=*8%1m+$^z zh|_S+g-y+IT5C~1XvlGDwHl{upxB?S{$1mf4TbJd9e41mB@=O-?;ROYD;`mIRy0Qf zT9hXa$Uh2Q2OISZ9rEo*;76<^baID=k-Q5Pphj#zM5^VLC^Y{_Pq2Dh;iOMU=s2W% zjQNy~hl!qYE$H3W)Nd(53Z+&7Yx6&4!)HC}ym@XD9nmHVGPS*CoWJKN8kzos`6(#^T6{xE(%|v(Bx&7xvVr79XBqJJ`MPH9=}u@R2d0dt*TjlIDV*Nf*jtBO-m4Baa;ANby; zp2^KHt=&ZquABdWq_Ev6fmM0ZnR_g(bR^(`&g4Xo{}|TMjIzClYC{q%|-4Wy1XiJdUra1o4e(TaMIDkQ*kiu|fQgL1JXv zl+2(z^!eOu2?O`2*9M0lEnApkl_{`Ooo6yu9X9qVvW4J|=0&!D^sVib?y5MBiV zQbkVrxsQ1#FOSR~GZI%0av!R_tB3xWBUF1;OP80`~JQYg=+ zI=gp_(OYOJk*hc>JGo>Mg3Uzu&!zFGxkzS_dT(rkq6(}3`_qBbnkiMgK zQ1~DAW$;ur2032>7emU!Rrw`2w&4cmQFaLyPQY+NKiDs-J zSB)s)w(3*u`isoelJ9znwF7g~P~62!V;rMoLI!^@yr>kY3M$LdwA-_4=@Z2(!K6oB zJtP_&IrU9_?i=GiR|ja%c83KU)uwNj(RH7VN5^u-C9c>DinhgLU(=LtDesiFi+_Kf ze0_@$^E#RiM<1-I?Sy=Fjw9XfimeohO4@v#6_3rZwULxqv*ynIG_@MtLc#j zcbWy=@yvJnpm?AdTu6|r$-uuBz_s>iz@xe)(^E5X^L-^}hN1Z0JSsMD(VqcwMeKXj ztP@p^w0`6e7XU_WtdD!j8NT`3CrijB)Baeuf%|ey*QM**-srAz_td5nyeGlROQW1& zQ>^4oLsQ)%JUwa=?PFue>8jt}pCPeQSMu6T1EBCNgoy5Gd+e@5%4y@o)3Y?59-CCtzI6f#P7)XiNSKX&^;aHLNCmi`Y}!-SQ+9Heo5u zXqygqD`@fcU6sfoC-3JT7x~tXc9`_&3DY}Aop4KI$@Ct%>pw2}BHUpT#2L(4PNXHG zpR~uSINA7~6B#HMf7Sb9$4K_4p~3n2cbg+rb+J8-e!Z~$=}w!A?0g=1Bb;k$ZO_1E z#`B6>{}Z6a_nJJU@L)o0V|dos&u2=QpT7~7z?{-%4=EXRs7j;cXD~xW(-kqg>{+n3 z1YY8EB#Ml9Xz6=zwLd(0G4$k@+DNt4HRN;S=`8H(Rwu!i`90^R+2N8C5bKJjed-+h zs)KpnPNAD|rtEX&%K04=7XNZZSd!4<$((UBa1HERnI&;sjiKd1rd~mcROM{!bti0h zj6&?qgWeuh&b7_kow6yUp>*LAc&LV#%ucK8o$`J2XlasvIXtsq>hJBDP`Y=0YVNk8 zb3Su_vWzD~Oqk{H?)dVYxn z&FDajjGy_D|BuA@wBot*S~nElC)Yn4Z*p*m26Ic2@JWf!-fkGO`7cQiLO>wflPVPd zD_V4kmk$4ZXaB|uxo?8J?!Q+;es2mA>Uwg4+(L=^d)nm_0^#?!4It0IWMItK$>U*s zLh0vw&Z{&Rl9U>Q_3dL5d)DD-q{&f|~ z#gmc?TT*v#eMiHtxc5t#V!%%igls@A;;V9O?*pQtamA}lY6#h8l zptAGs;^>dX)!-LJ(Y)XW{u+gOricc8uM`9LDcV|RM|Ga(9OUQ^@G~N!IxNq)j{8vF zCi;|Bf57;h#8_5gGS@6Uo#`s)ggeuGVuFLAcP+YNgEZ7qhro6o%+5Lmk>6KT{Zoki zt|GDU>}jZ{-U!=sm0PClT}5+I2KV!Ai!J4<;>;jFDue)+ZRy!%iI{qrx*wM(|7VIL zpH+EwPb-#aeG#1#06wg#2Y*fo`KT$;$7$s89lJ|rW~8NzY&Tu7KJk*XMx{xX+K^YC zwjrzvv458~yr&>~$2M+z-0A(Up7ty#~M(r&-zo#Ndi z0%`9p3;y`LrMb^%I<5;QQHZIx26AQ$uU8J+j(>?w!LrXKf#dq6-vejOWtr~i+iZ<@ zZA)k!w@QUIP3i%Q+*6*Tv+P|L4@E)+14NU>(u;=lqsdObem zE>I(+l3q~0$y-4+{MHS)z52c_-=JNwSgMq2U^^qs8T5tGwJOrg7-B0k$7z)o#5(a2 zQcSy3b^F#`Iyv9cZ^IywhS3E|$2Q4DA&R+sJWN^=pDv);P-90CyEbv%LQW-YxlMg% z1t{mobr)7n*2u!d+LJrT8u@@#!7`{|MFjv}CyRy4@13#cbPpw9h9%J9mOk9-E8HHgB5H64GfUInP zC9lun5y4ooe}=vAsL><(lyk6o{PbXdqmfN}7a#nC(Il&0o6Ngm`P$O{B(4|X@y4_{ zK=-)yh2pA!^8k&TqE?1Q|HA{leIHN1vq88wuV@LTiALr^`Ap)~|3%YVzcu~7|Nlc2Ob}2?LPbF7 z91Y@AN=mxBbHEr44+SKoI|tG^8b*hdbdMZ}#ONAb`|^4p-_PTh{R7u=?!0c-J;eST zp$(qd3v&%;Bs7LS5c;>LTcRw2rG93xxYLj)Y7*Up+$l+pQxNAE97R%g{aXHVbLi`B zBN8+lh+biFdY{6B!vlA~6G;>~GOJqI1x+hX{ zuPKB7y?@(xWM@8PjpLs2L`FnwtkuoX>6WD4D~I=wnq6e|25m27B#%LyW_MWyU+#wG zv%lmuGE)@(RS&)ar*N(sszdz?2cOWZd3d>m&_j(lWK5M-@_N+eaa z<(tdFnr4CHDAz^(>LAk04U^W29t761eseymnF-_Qt6;K1no(*ppnt45Z!vd$eln{x zSCufS$ttd>)9V&JV8G0rmcOh_TZMq#XNc_Z_4lNxo9 z1x7(EL`g)MHi=*}1h49Z_R`e%xzCLUEj<-zGi3t_2Z#RSn6;TGa8Gf$3t4J<^CBB! zwM!Jn6n9-+?PVp33+X1y8JuaeLHeEsYPR(phzT*mswl~d?kuN@3q-4;0m$6#Ggk?P-A zXaCm35$BB4^?$RJeWgzJVH`9k7cq}F)y+get2-!j(JO&BBTHB7N14LWDnl)>uus>D z_|iaA_?EnH*)7NMjK}GNR`ypmYj*EN|2Wv?9Ob0`t^q{~^EcvMl1qQ9{#KS=fE&cS z@8h;CJ~bnEfg71(za2HcG+-%g8NBg@9Pe98`8{>#r}WBpbkiK{3_083rOIh;lJ6WJ zz?aiouoN{4L_}OZ1c6H3?hMEGgiYr`HH&s1LxxH_e0%mt0-Lq&s44PB#ruA)=d$8D-UWdq5- zU+~vLm6{VSd(cYU(i+6^CAOcfS#}xdt^|1sh0uhoZyveS-2Fuh72A~xz%Er$lyYj~ zDOhuKmOJ6T!VKGwGtTgj1-+jqmmf4W$Kqs?&1j zYa@GNqQZ(MenSep(E?uE-AqS6*5(45KWQ`2{1ndPp8f%wOnF3`~NdnKF%KMUC zGAmz2=7uGa>dbuafa_5%x$k%9FqFK(`^{UyNQ-it<|y{|^dfsSW!PQxiWW#>zqu7G z)AGeH|Fvhp+v4pdW`glW+<0%oQf_-q>h=(Gk(3((HPV2DsP0X-DyicyPri?OLV?wg zOhuG4{PlV>+_%@A_LjZ2zXB4#$AY+_9<40w6D z84Ck80vqanPY2RoaLXRBg((`(Ib=WoahF;n`5C1CG=d%^_#N-P9`M*?>vzJFLJ-K1 z0DWF@Z~ehD{Jn^kP5N&2kz4D^g|50k>~5*T+!pZC*8nL!1QBCPi6ENJ!ma}vP66-_r!TUobVx2J{#=bDwo(&}c0%@_oR zC%Rq?7xhf)@z{vFHvniH|LtcH+M(t}aN-P+N0?#ns#e&tY6gLX!w5s>XND+iRRrtY zOs+60-#ic@)X*Ot*e~tS^hNb&owKG{vuQkH&b274=Y>AfYIp5zVA}$>1GksgoO~Pe zqHj)u_Ij|uiW! z$&@}g^5>|J)WPWFSn12DTy1FIe($nLGtUcuZZ?96(6fN1RkfbH{SL=iQn#W^<756(0E)w)+uTw9V zTDCfZ0uS3bf$JkW5gYHah=)ogG(I=9Tj3Mvy*Ss~6-^6U6^=KFe&Tgw%`z7Q`#QAK zgahGDhv{Y@{#;+KwzlWF<1D-hXFh@L*B6frklm=~{vw(8iYyMTSWwpb{m~u5D^)Y{(mZ-suJ88`Wzx=NXJ|JJMW69aTyZ0BW7P^v)x+^+e;WY0q5uo#Zev zR)a-*Ki$pZsU=2<)iX*WF6Yvz{spIeIb2EC|Cl;Ld=Dp5JvCmCvl*ElY9YSPP3|1X zq;7c)j*4qGMk8df!F(0h$*Ho?}^bGRXS(9;Nh(SHnm%5{q^cDY5|qzS}oBCnvl zJYF`HqJdGd?{b-2PFTkHplRSNv4Db2XWvh!z;m=;Y|~+Pm6*WIoI*=%ZG#xoW^zd> zxYMZ`i|dt(`8>rH*fTrc{FF#r7^dxkKT+2jCRPh{e zLTg3>feijC5}eZ&?{}$MjA`!ivzazK|J=j%o7{M^u9rNBQA3*|qW#UK)&s^dnHgsh zx$ke%EW9dN@pe|}RmJ_6s>67G_i$3F=;`?|wl=GC0#N&c&dK_h>#q5>2uv5Sg;3)hlYtn(%tvX_qi!KblabO zP^rgc@E1}_B)!*URGLbyUOg|ezQGlxotqZ#_Zu+^WszhAl-BHLwu!vYc0Z%na>{S< zD&n5t^hpX(grIw``z@h^1LBDpx-z#t84q16D{iKyaL}eh3>_Kz7lWz{3D^UP|OpF zqzJIn?zi<}e7%|J(&rB=9q@x|%t|~`Mf~<94KR-j-E)jeNIeGMZ)a+sax18`t7nC5 zz~6h*-@mR4Ah39{RpCqPe&RF@@Q7$JhJ`-}1N}0+8-kQ}cs>t60+-exP!*m4H{R{k zLf7|)xrFbciRkUTL%28O9G@y)7rOgc*lhRit@Ey(!NRARqI5GQ21HY-IkQg$VMG~= z#6H#9jow7m4z^X0S9{c#eSb#FEL1ktKB00nz0cXkn$>U|5vfnr!0e^~_1p++z^_Zq zMYht84GfvMQq+Oahvc*1;6%xKsrNM3kcuEk=3c-qBhC6}ePz|uye|Cyz~<|&(LFP9rEnQNHS_{=m^RF*G5jQ2JXvnY>B06I zbHiA7ETjze1~aHE1w#T3;^eluCZDHYU@Bm0K{x%NG|H*WT?rB5pprr6ENX(>l?)Gm zr_KK;thoj~$ps6Y>lDcwd86VL$)wAKxw-8!=aX9(Lh@P!Ai&Y*>6W1W>}X=X3@C9UQHuZP_kk;l^U zy!xKrpkYs6*i_}1!G`$nf{Qf*<7!`NwTBO$x>3-Xq{6F3jgEsn5-j@Yzc#N&(77lC zTL-Lt1+};8xi>#t0JKDHTxq)BS+;!4uA}vN?$5bADsBacYZHjvLn^~-7r6tvvspoQ zMR>nHJgQMu|Ig?*|6c-Q0?_II)5`?iO~$+Lf2@b#?)Uw~?#3PK3H?9HG96%}l{$Y;tmuLE0BRd|G@pN2>TbLob7GI9Y*S+1K2HPhbe>Fkp{az6i_$;^eD{RCMV|FO;W@w_ zRL+|ZO&yx5R4+=)VtrF)t<8&yLQm>Y{mqVbN;I*K9cG<-1-IX; zk_0tO95jxpM_m`C{>WMW8YI=q6~Y*U5284Ilbb+x8QQ@}wl*>7u&xDo!uask+_RvqF{O@L$GTfn-I)-m$0L@WPgM@B&d(kjMM!a$;!Pt( zjBKGuaXQ?NgblI3;;+Djk@GLgCePiL$A)s0c!Gv`4|JQA=`KxC!;5 zkf#WDlN`6E_q{-qer9=pOAK4$s?!=P18rz8o0S*eD(*S!L*QQ)TH!8(QGvp5Xm{!I z&qnzONKVM+@ptMHMJ;MfTdWe*6`39$sB0j2EB52#J5kwf&Fkp1Cg0x%p0jgDu8eCh z?ELr4W2K^FrhZU^;>qul@$f&Ajxtn;(B5|aCL#Dg8S9(bw9okD#OtDddd+*vP`g|= zu-gwg=7=IaAT5d*&byzlB(Mn4RY5Yr@iqsX3$4h6*$_#%*zl8gC)GV*d9sWk{()JXLz26`MB5^zxe@ zfHw^Uovn{cJsj^W-Bq-dW!Xx>+m>kAfY8&wSlW3<_x4BU*jxTM{YIZsmxrJB>DScr z+bZvsel{pm`R%3?HTymX-Fo|)Z_SH89Af2t;PDNsVpBCo(O{uT+axa+bittz^=l{T zLG(WLl9=P8g8b{&Nv*l2Q0E4gv+oJ8E0Wv zo__BIHaj$Y)8`HCq|kc$Of7CFmt!8M{6j>OZUY>=m?N?y^~KYlmtPvSzOE&fz39|N zjP0VDz0Pd3PyVZY;)?#Up^vbxWB;-W@uRMFzsqSXMKFZVTz6e9@hHHIOC!s(@UPiA zfj4*`?OvYeS9c-|6BoSZELOC199|6LeLQt~VV1M&|FRHUd}=)AT#@z7p@p4_<(uE$ z#$Ma74y}jF{jus0B@5JG{4=^=_qZNH%+-eTKK*Y%(%pIXFWP-b%_Q(Zm!P|}hda7s zN%-LI0H)?$j8HrLG?P`^%_n|hQ-W~>_K=%~^ikaVnIxDq8}QqipT1^p_KIeD?r|6m z=AwH8s89IY#?^ku@5tELb6?URskkR~wvL>BXZ0c~1I-+tTbL@mawnV9Rb<^D%3~an z&$zCcbDoX3Vk4Idvs9g+6v@snSsaEz(+d2)35?E1Ez?95T{8r zu|Yr)?4JoT-37Dw45D}YC1O%YsD8JExv^D5nW@^A75og;K2hvBQARnxo9{%&BRwg* z??O`KK>puWhRlG9g#FKD*F8u3vSIJkwC_+1op_&sV(rjrLR(PE4+@^bt^n{i7aWZw9RE9>JrB*q3j^u*DzWX?R3i@xa z_^ofw^$!{3Z;Q`Xr@Jl3)XFaod)|2b;c7#X?QT;e2fE%i-HN0oSrg_u0@_o2M1MAp zSZO7aMRlWIU%r3S_-ioj4`cgTYFmQcfipAME!6^qJ&6MN!IN@E^DT{FTba z?EamL8X{MIeJaaj=gtct%zRqk$g3N`iDUmV++N%~;G$0-_2<#IxBEtUR=Xg8m+^2> z-*XqW;$hR&l74UW4!R`k1LF+3tenCUMdxo!w=ZNtuk5~&&8A3R(~ziC*RIz=P{m6f zJl+y%Z_BaWFRpyX9P3X(s2~3w;nENdejdz%la>^TmRl~WSn0~hX1^7@yXZYG(E8pz zr)MDfN1%f{89xEqno05ty==SFtM#Um{dDLgILst6S$tz>BF|?QSc)*P7jr=HQ>dNz zZ7WpQMcQOE7J*^tAWn>998rI#twDMIg_&Z?be-#*fU}KS9r(jq4B4ffVCt1nJvGFg z(fU}oDj)qAZ9+OjguA77rB)>28E*Bi)1lT&)h&W~i(A%I`xV6?%6;bU#|3An&Z!_W zpu1!5@> zKlsg$^zEzv%={>+dm0tZo++OP1RxHx%xeWHWx?){=^2gQT z`sXoW7!BOkPQp?@=|$DZweT-(j;QaQB;HpepAuVR)6_nb%e(KY)%JKVK9U^1Dil=* zQaq7uz!u=6M-(MU@dJb0vR%GV6BW^l_X%=TI90bNi_j5Nbp9$+eQ8CdLV+ab!e|S2 zV^%%QxxeR4ubMG^y;;<|7cs@OWTBV%L``Qs!J|yXkOQ5&I?IQ9TK-PSC9JVB%S}BMEF{+;UOT`|D;YJ|SZFta) zk(f^dQ${;|@6r=F0C~{e4Z=s1A(P@E->LC-9FLJ8zn%qcj(evB-_`;)GNu{to_+JX zO960LlYd^FbbK-Yp3qv)TDZ?;+bj(~v%P)PGbPh~Y_rW^LQd_m(mzvbv7X1iJl6cx zMUT*kzPzvmuLYvakG_}5^jLxQ=^+)yWbDCPf-omm5@>YA7*dl!8mCWYONzQ8E73;I zDIwLoCkrw9EbRZtce(Nl$L!cFVXY}sfc@%*!#JXd(MM2+nJT8NdRSNyGBU4NXMHD6 zSop6e^1krpPuFSfGH&J*RWtJnll}5tb7~AX@vghxt^}XV=3f`H{njBad)W3~Y<;eT zTZ?7cLcy8o=WwuY(+h^W<>lQ|)&rm3t!&$1(@f}H+%v+S($qJG1n{I!eX;l#ag}XGPNJk(emf{B_qKG?ADe-~g zPSW?^2mf3}R^fJ?%OvepTj(S!u@%O4XMs8o_nT&9YM1cVrqN%7H&MWUTlpoakr)R@ z$30!Xywn%Xv4?-R>7j0NwuB3ufLPGP3e3=w%}x~&?z@t;r*_4i+|gv1TeFj?IP78@ za1=B&P?4R6Q@0V0>K|xSB8%}0y&=;}PqjJWfbK_ckPcbs<-1VyCGm;*KJr`1irB=3 zAudzR1Ozl!3LdD;Qclc2m+U|$H5*xKhg;*~Pg?BS3m9&x-cpULzc3O5&e!M|x%(DI zZi+ppS}?YY+O)LzY}MaV8?O4W_g*&?l8Toc-lzAe#U+f*vZcYA8>hAbyeGOMniSVqDA``>&yVJPp-`gRg z=4w^poW`;!cJk$M3&&Cdrnvf!=AZvHli0$EX_VBkn`5Ls;^frgKNEayubpP!AzxkX zd$9^SWE~$JsX!N)6VKy_sl{g*$e#t%6|6T+r*&Rb>cy`8p#ftWkwoKU%5dvf5jMpa zs6VE3A`_M-eJ4FfUeu|i;W?QzC^vn}G`q7lKk=gGR-b+GAK*j480pP#Qd70^OT#E| zEK+-Pter%eJUwf+g}o0!MYC> zWB{9uDQTm_!njc#-Tx>AaQxdsi>&ABglC_;&+``hqL%uAVVW-d`gV<=%ZYjseFG&K!uJ^t++<^HrtS;`z?uIE&wZ-PPT@g?#$c-g*pL z8&0SEq!YpXZDxm1+!{OOtBy*cL`RSGD^}&VF$sE>=V(sb`N}KIE*tW@lTmd9P@)oQ z8~VCMFSQQXZLzqyCM)oMC9?PdpXnZ`!iE;Fr$xT33$?D+f96n#AAP?ydz#=r9JN^? zCSr2z0BOH=u=7NPwYXnUylS4(Xf!)a4Y+f5T9BZBW0w39vL=9ZWiMK+LGb{_l|lGlY2?@Xb7S8E_@#Yf_k}ia z;se88<~HTiA`9lx7=(y}`}Unx7A&A_fQl;zob#2Z)ypBdY;9jApyhk?9a*%WYX+X zIVv>QU~Z-5)Zfc_hT6^UAP77Cuky|0dneD%x~p#^kll=lCc+a=3JA`NC+uo@2#WSM zPx6vY=$P-#8#=rGv*_&avT9qMEC6Yt#rIl7&<6lfF z#FbcUsQemp)YNuKpIC>}Yk29ue0=ka|J7B(we|*Y(wkgOtTl?{KO>8{v4e8Td0m>9 z8`oKbET<{}=DKF%!-<+8fT^Y>mgoDL=D9oJ<4a?Oqv8DT(UZ1TexACQs+yy7%)Rfd zUaB!)#W64R?INQ6*9~l7XDWW)KQ8#Rk(_d(ge|ICx>^9xrlcr?bhAN-+EpKalV1TXdFAlEk=qd%{`zP`@g%y|Kc zC=U|Z*xMUnI3WqCPl`J>^ zO2zsS*j^IX=X&;Ic#~(%Q|>lZD=rPy`PXfCH%f1Iul2NV)5W$R<*LKl;$tUnvg7F; ze*7X=#p_*Xic3r2W=wXmJC;$;p!--RA&{RB$-MP3E%=u0PMl%q=L9+y@(rXSWPJV3 zZK~3EP8)2!FP24i_v$whV~mMxUmfa-Ng&Om~NsuMMT|cGDR4}i_BK*`@KX; zSV7kTinX7qu=42GI4{I5;mbwoP%h=>neJ@QXvk_x%F;)?V=;I0zXajdgRM81Mt5L< z194SLKgx>(i*vLcuq@P!u zD{(iFtaYEz97(R9^>-w`E#VpIop+v?;^}f{DC=EWz?U$Z=#L%86NS^;Tq#%Bx@!xA zvf&NXN`M$yT){;+{8kd9$L-E9E!@-V`%P!Tsbmz(*OxQhLD20w{0Q!JBX0|=0VMEVU9P~?p}VrZ?PPttT)R{KuBp` zyX!Cpo7#ag7wqXzd2JTi1Bh*}{p2dIZ|rSJq>BK=|wdF5+j z_u3ib(HpEg@u&81#UH5qplgc(;XkX-xo{Ey&crJgSs)|t-W@eSFLNOw}KwYh9ownS0LbAI?ef!%G`t&R}G{x0|LyZ4LGxq;tKuwQ*B(`TH`V+`FCEcS?u*-o1BH|654nEXfA?YqyP$UcTDbMRb3-s9s=1u?|gok(7ZE zuZ@KGRVV>#H;rfc4SQ}s2jL{}mhE$kj12ChlC|5Dq#F0o4-#Rh9M3ZJaL38_PkMeR z=C!aD^z`~ljxbz@ci&coTnE!G+%(&#%Qg zj)IMIgMr^t(#YY2)lo*v%DAB?P7%|Cq@Pkd{0A(r`zIkVS0bj=I&>mXVWVsz%-#Uh zcQ_``Lp*I7Q|r|rHr2wRBD!V}lgh-uj#yu?5Y?XvG1(P&lVz9?n$E2~j8j}o=Sv`sGA z2H1BLuaiZuM<<8MM9#mUR)snkxL*N-bdZNai}fapryqk zrG1AtO{l`8ujQEbU!m;|gN^Hd303EETDd4ZuVBjTJ{?(>K^OA&R+FR$ItsTm^5`fy zm1^p&j)dN>SZ2cxgYCaWs~Y<1y$<{n3XmkU?5MowqQo*90UY?MqLTQi-(A{bu&h?0 zYJ}YRQb86PHS(dxZNaE*g->vu;K=M%m#v)DqJ~XTANEu9(zUDjTw(r2G3q5|o4nNI z966DM_vs2bc6j||-&D!7-yIvHQquP@$+{bR@;%lmZTs^|ljNyM-|x%Oh7g1g;N#;u z?rGwNFD2FFXjki6M+(z_DDl3}0#Tb9%+oZ#>2_HqwxRb_#UxW+_Ld zmCxcI14thg2jv^8Kdaqq{>7GJ4Mai=XiSlvDR(t5@Qsr@czC+d<#T`a2{qT!q|gyk zEj-si-0UJBZik|HdE)0JRA&yoYLQ5?68A8gAmFEOqlW32bp967y+hJezJsjDV6&f9&|B}Omb+$kL79~|+3fIg8 zKRkinO-WZPn<@#%$iYnqIUMZpc(;zQkx_PLPewPiN?aQ@C!s$l(hT7|p#fk67kN~K zM%8mW+eaU((;ds$!y!`2hC*1+>3ra{WXl?04x2DWEhbuUzqHKR zdTh#Tz;A>3dVZnQwD`W};jy__)y_z!e16qnSBj>)os0g27`fmjH4zzY0q}eTTrQIB zv>x?4s+L=GVNb;U{N)noDNV{aLUr6~5x&84+0e>0WqW;`$Ch@|JpQsa*>BFcjT?VyfVI)$QMy|ux-onWmk&n&e^Q|n@{oH3s8*)*%L0F9fBdt_l zz{AB#j(adX2I1P$)ouH~Pu_|oR@d2(w7&s8fBrC_#!Cb+>P^Ow6F2pId;JqM>KP{4 z8K1yJxswGd-?QZx7V5` zvC6j8k3{s$ipJlHxs4gYUxCkL(Ih`t>P76f@^cx|M9octh~W#ykn4v{XU*;g2=%o7Z=4&Fv!5tUjFuqiv++o&L>PAybic*4=VLK@urJ>f)zU)asB>twBhr? zsvuq&Y?qTlsFd!h&H77jC~_QZp>@2gL2*EC}^2`uAEDlAJ{$!IaI za{8c_bb3=l^UqX};T7M>dv^a>#A&W#soB`CkU{>NstAK2*IAHl^yMbQ#YN5*-FuKa zGd}3ASKoblT2`opRxXZ2)ahlbZ`V7P`pGAc<5xfN>7=$P49ErE7Msn78lU1n0)*(3 zNbD3c?rsd>oyH`B5}{>ExVZ;GN zYfKzt2pu~7v`027G73FK;t)Qmh>^Ss6TM(j!6!)bnSL=JEHxp`oM$6$7cFRLNKLis zz~JK!75oFGQNun*$TMqgrw4HZ&vM|2(grllRtH695&WY6ia(K>yBySu`K{1Dw{S3}y6>%6`6Q009B zt$Uq)d}Wj6Vd>K}HL$szr%9h!H#fcVsdSVDvL5&%8e7{GUkYig2u_s%!JjY+V^eKc zBfTs2Au4KfOxDI+=`uZTk%;89dJ>nXE^eB90H#YBELUAz1TBtI{@}XlB~Mw^3|ZclfjTUs7Y@q$(9CXbTt(gI@SD5B*YcEyr7k_wk-7-BCe zmCe)omJjg!(@Vb2q1=NA)i}ZKwqdIQ*oT+<6z?r7m}_M&bA^C6XGQc9t7~`FVoqRY ziL;`*DN251+VN3#QL{{~!Jvwb%pDo}^QsZDu(tJ;Gz(Sz+-NF)YumH!-(J$cDXhW{ z@x*?oND+Blrj=n%9_4n=T|{+By_u}PQ2yMiRJL=?xuVabE|>4f|6Je88;<^ocYg1jT@=pQsnE=-z3={wR_UBt1EJ2{2tiVL#n6UD}#`j z{N=yKw2{LqtH*Q5pSPFv+YU$GjG$HGuMc0h-=ou9=u)_EPsmt#utbT@{IU}PU2H_h z*dEgu4Rab1b+3DzR}RdhPZ0TIkgikZf>_&PcLyr=a`-@Hf}1}r6#;tlr!B8f zwbFRK8YvK2?v~;0m;XpQxs1W9Ef|^PXMF(;;+avi?>Tik*>g%>_QW!d2fMPwa!jIu~|E%`S6|LqE*w=sMs31*0n_v8mE_;W0=0m(bsw=9F z3f`-TI-P=>&RgQyxID#}1r$j(x;8;t2Uz8+Er6-R=3c&gNJ^2kTCK&_qN6mm-P2bL zPd7tTWj*p=U^WR&F)MDjJA;&ky>Pj7PKB~K#Wa92Ho3mWB*2TUbh zZn=b|Jv=?NZYWUePWU4LZxLIcZvfWvjC!!#nNv4dGs>ofOdH96uq~D%{!`;65BS}b z1+asm-{$GPmS42{xz{r3dUv?_x}GX)-5#4kJ?B4^*F96jAu$KlDb>7ke!f2$67I)? zg9j-KTGT(5J28b4fR_u%(u_fc1aQU$HtRSuYr7WW4>SBd?X6TlJTgC-tB=cmA3LzE z=B74t{E;d=%{N^({l;sQ*6CVQoS=fZ6?g?gRwr`{4X;L(m+U>8qWK4UHc9c<8PfNcL%ET-PY7zB$*8WapS{&?^I4=Zl^AFH{`zxf}oxzmJ$ za+aUo+^KmIaky|CE(C|m$Ki5txFQ^`8;MtXBfi3&`!*upA5Pr}GxxH0 zU;RU)RAU!?gGiHxjzD*5BqXG(Hm2p(b*QPH7HG}|hlvNR2a1#hU7S!NZ0gp0d)FKK#4q2iXQaaZ?rrq~`!EtBWXOX;*dUZ}B~hKQEfWXxS2HS&fRyxOoWne!TMcu#v?;fuqk zw+`D{ms(o_! z2I5)alkHye06L}W+@s8I(KW8M$i|~E*gy#t!dg14*@Zn1$+E99T=X=dAL86@b&LgE zOR9EuE7`yPe0jTAz@Rz>rJm__3~Y1!_Vk8apFYLm3?iwX?`WO(S)J;T9n!qJ|6Z4G ztFbZr6c#TOFq8TNZ)t-m)~a|-zho7sviDW9WDp+<*sQDt5)bb`%hp(w7vDE7zs-L) zySXSg|K}b0obI~IY+(YDUbimflAZmjFX%POms@Jw^71maVI$5_*Wf#C@O{1x>U+N5 zDj&*qckYT`KbfV0oGgAOb^>vw5on`%rqLvU*~}^4S8?kEqo@k47zMcR|pgKQ(B&y5O1*^3AIn8-`mAjlTU_Z+zO5@#ww zJz%@52~7{56le$0-B}9l@s5aVMpyP6eH7Ygbtqw7ZhK!S<*r=nM1EU7lRs-nQGH z?wrn1ph>lndmJ}AC~bFXthwP~NXc+>Xskbd(WjcQ{`FwC=X8MnXi?OlacF(WW_52g zA2qKDG0OB2cH&;Iux^_AQZe9bt3)T3F88{oy0&Ix`}-}I;|5^V7fH|7`Y1dmhD;o= z7NzWw(nE6luGt6*2sN$_d0~Ee7fp=XH|2xnn+^BfGaQw-J5c<)Sb~*5L=yZOmnW#~ zPHGC?p=IQs(&6EM-q9DsTCbpb5ie{#XY)}S;V50c`*^)D>D0=?r*;3G=P;;sj>Bh6 zcsZI28gIIY8;^bXmpHMyv=L6#Sm}I>~4?4y~X!9rdckY!; zh9|h!FTs88Ez5Iz5+fW*JJz+cE@J|2uN<_eqY@{lug*7gqif}B%ldBX)LzzVWmg`H zr<#aLI9zTS(Au6{F+TLc>=LcLeU=^K&Tgi%$y_9141|^MhhKc3{--SE<%L|b> zthnw6dTMcghj?YC+9~<$EYl&M56j4_4kVTPe;%Ye|Lna5fS>Eb zXAHfn&e2w1TdJ{=>p&1wIY+~ETRFgTgdv0QgMbskhp1XaN* z0R9v@(uMd+2)h4vChx;eaUAe2wj=8fV}^d0iLiF0*uRGUIde9H-89f)oNPQX7GD>R zyyd4sK zsyBg7-5AN;+#2LF%J=rpuv?^uMsOFUPSiY2S~y_p`j3`v0-Ba|=*7idD{%uBG@#eP z2^vY99ad=LSk6tjGtvC$XK1~x^%Eb*M4_zzSb}LL45PBhz#23V)ApA2Bb$sg4+x#i z#6a+-Zg;w#3ewhhT1nlb_s^ zvZvEJCE}|+l7%O(^fhGQ8SoJIrgL6v^LpQ(bXA=0ybm6k_0!b-oo`L7c&G%KdPNak zlwA5@2~C;E>(j=!u4^sIr^YK%%4;7D^p(p(!sZKD&lKR|vz4&6G3Ev=ZwkH9$Ckr! z?KQRSq5XQmJCc*r{Bz0jp`s4#OSX4oLJ&F2juU^vxJZCyJx}gg8PlzEBE{CY*tGW2 z!3@AuL}h}lx`Irb2_U$MT3Cr@f%zd7z5HmfQSfu`>d=hAKdr{1TD3Fk0whV$>_kn+ zpIbdMXYd!(gn|l9y42J6;QlXD{PLXze(|5uL*D>jBUp|7mYDqBK4;2;zj#N>Xn3qu zsnOcFP+Y_i)B3J@H@K#K|g2a(?JJjR<4&mGp`>>X9+0B&*vWo;a>siO47n2gTsP(D?7 zFYumYYA;Knzi?Lh#6)Y(-#N~{8_nVEYkJW^tybRpQk_U!Tx0cF_6%uQM3pr7q}q7<+NOE2rs@whs%VUP6Y z2F7Q1&rXN83!HBPBU>F}#pX?i$#GfAixNCXTTe-4+8blFO3CQXB3T(3|Qzq_PkV=eEoPuOQnt!p58RqYBKdm5K$uVZ4a6j)b7yn2lT@m_#Rp z0qgme^FZ}id!*#cv{FjtG^h9GQ;+>`P-}0dy1OwQ)T*YBcUS%_1v@pkd;_ z`(Cd6G|@zbdQ8~2@b ze*5i%dSxD(_TGn`#SUH-#58TqKvrXl4;1l@3u*t#T8f37z^|x#e$)5a+qs+uh@tH^ zLsED;_o_G#pFpFh@#c07xut39hmiPR#+?cYhZLsNpWr2k6O!Rkh>BytivP8~vZ!q{vm;ti^3kli6F)C7bAoY?z7_kLL_ zWTSNz5G%^;sbl?O4OulF|9Qhw6_-w>udjt(zWeU)mgYHiCkI9|P~vJw+3!|Cb*Ek^ ztwo~S7e;80)%YO{h_T4OJBKYW2_F1<=^nsP>{f% z7kD37uMw3I+$Z3on!|4eY8P^sUyi=IANFHkni{jqO|H^ylFP)To#rT!+F31n!89f6 z=PYJ)KMplK6gLm>CRvX6uK3KzZ(AQ1HYsPaZ!V_q-E^mDfCmuK4y^pMV$C7{9d>Fq zg=dQDLk1>r1{~wKRv&RA#re5+HRCi#!r9e%FVE7a-Oeyp4Jw(yuuXVABCI>iAgsiX zd#6s{E!=*o7hy2hz1*8Bk~Alq=4IEi{%hc7!goTE!O}}9%4ntc{>wxSawB7}+DQvB z%5zzNZI}g53#tCPC3S{U`Fs?_@91V7j^@{EV4+bNBk4>gR@o>+Xwn!Qv2FxD)3YH;Tge{kQ@2n_W2mr!9#dw-J@wx zJs{#(8!MI?Y03I!(7n06r-{cBTIcJ>Os;jp!g{jRkm*I0B-sHKZm*!=Uh`ozD^2Q3 zVz^-zdrt&hmsY-?_z}hO#^MBF)D+}i@dCXESG8(6$PMl7=n0Ub{SorA{Y%_~jZl)< zdDtxM!MjY?s`CuhXBN(UMZ?BsHOui3;pFiz*oL}&E}b1$Z$*?rO!ia!%RFLx$v;4H=I+x6~As6dV>WgIWOpeI^vHNjgs`h?Nr>7V|;_epr86E zL&#z#;i9jLAJ01u)~BEuLcbq(r9+vb|Z4fKyF_UE)@ubbqIxnJZU)NzT zgY#iYZWCd-cny`ir3G&CnFP@UES1Zu2Dadp8ZT-${Xr@htzO`e3#K+>NsvyP^EQlm zn^pzL@fO5n>loe7!9T;xVmd~hqdnnjoep7)vr2t#Yi&k5)!W;_0vJXEa{4(a-v`BB z@5;#G=Ifmaz9|(90+O<=_-Do^#jG%9G1W0xcUaDPNP4`+;;GK?cYB2743}T+EmI3+ z_|qXyXlBhWhqri*>P?*y}KjXbR8oc}RDQ1fj>wC_0Q4_cJa5P|~ z$y#bx>k;1f)lE;0z*^BT#`fAg5io$9sj<7quzn$#&f5U+Eqsmr$QS)xcreMR@e>&0 z4BD!i%<5xLlWwfw&i|oIBiw5Lyp+4Ld%5jIg{#j^lJk^;J9_hFwV8mPa!WHLY2ZEf z+*`Nhu4H6jiO!odVgHd-ujG!y5%9MfR`^eBTWb^-Fp1SkVwk_WRk0TNj1Mbu&DT;U zxWS*gavHK0?-3WFzwZ^@k_&aro+6r>4PF&8I3IuW-U87bi4hvJ_e&>yKs^dOA8e?1B|dx(?!5m9vS zF$Mnnr|pC|=C7F}$PPxpDUCbpj<$BSxpSlRXmDfp3^Hgf+Jq1A18%0$nTlihkco9y z{)He1+|=jIV&HZORXqhyn?7jNW<`LvWOXVtvTX@1a&pZjmlqr4{#INQ8ExT!k+C~C zv%U*WN!z2-go&Sf_X`-d&c44$mE=I!>~Dc>1&WzLs)<(xZ+VF##AJ_{`S7`}PwAi< z%?cdsf_BQiqKf1IxBBMuv^Cl4YFNV^OXH)=j;ACCWIHkql6+QkFl&Hn*{@<>jCJ!a z*)(uCS^atCy?U1WuZn3c4QcT@XP-FH%kDLu^FE@6;ln{)9ojMVD!$eJVj()^s@^9E zoxz5JvW(m~aVb=QyE>Ach&BHbm_YSXSEK%laCekZW%mOC4-V7Wu7Xl*H0P`*BIjOh zl=DHJVZZ!zml`NI`m7`Y?ZVu~&fn;r64f$miU02R@7PuZ_D?ZLBypu6t>ahvZMivy zfTf@Ex&&y^Q+DmAX01e#PJypJ`Dj>M@sb$J5kO_O-<;n_`)tI;4rUom$8HKXob?G@ zHg%n+|I7sF29<7>!EeZ6V!~0`*@~~<|BDlAJ_@u!?}Z&4iM|E=g1E9z+`Yr4o;$yS z-*gdb-Gxz^j7AC7n9(@--)?>mdw*qGbqxV|$t|mV`i}&TZ zIl#Mjo?|UGBx47&v+@$4yngb%Vw{j#1jZ`m$w%6GHK4Qjod$Qecr-k54Rc#k!f?9+; zj6wJO37Onv^thd5AQ0=0fbaLe37|6wqDs@|O6AX)B`AgDo}*`Ny5JyE5M0H^zN_C0 z4Q2Nyk&IP{-n+xVpl><1J7YXW=Gg05thVO>k3~PeHWa_<-oWbI434!_Rg+uOw0veI zhKB%hZL3zeFhaS#Rdvi3>dGi>^eMVYC_QlPl~}_>U7G+f5d=B&9Q`|6*EmX&yrArp z}b z`#z+Uj@Z*%+_Uz^7r@(np!UFz>X*&)a-8-U_CO#4?>Jn2p!6LQ_4w+jfq#z8Hva0y zRG;33cXl~z`s!oh6Ce}Y{4T3Y!wO2W?BiF}5Yj9pWK{H+BIZ`!R>${>xQ(wR-9((_H0Y?-Ga{cbUX2E!U;5{zOy(!o!KNd(VB z-SH^=14bLDuX1i5=XE~~ukW$1QlGaJ#_kigwp{}*B! zZzCeTRw8wHFt!exx&P4otx{+)CFHn;{p35vNOQ4}MFtUy5=Xz0>m^4A{qd4EaHCu| zZyFOC|FSUd-hT@EQj7;jfndA(=UfrO#6-5eUzB%>Z&3@q_PR1MUrhYh-wtIg!S7+^ENq3sX zM#oEFARcN}ST8uzmOK?VnKp0;#nVW+x5wh_Ue}3{S z(;hBSIv^aA&MH-tnuh!KTcza7yu=PkYfeT=H>}Rz9S$Tb(A1gHi_lXokW|4bMP<1T z<8|Ryfp>v~==FpwmciT(B`~oy0OVIEBBppV@F>M5H)W3;@(!y$#by4aZaV*P0EU_> zxilFV*5M6Y9|#KtaooGf1-rS=xz=@{n|&i7auJ?Hv2As<`JP-?cA?j04~e+pUr#- zznHl~rlk)PHfBgvPLHv8axHowFFMv(Myb!eBdv=?tsv`-8c~NyX32--EMQl9@>M*8 zT+Svo(H2EXO-|EMNyCo)%;)HXu6*sisC z3t2kKAIeF!butofE+&<>fmf$KZ@UbIr%Mjqjy}`K;bVr&wn>7g1@aHjqIz-6r1vL0 zhCy|>A+Ph_Z}@t^)W#}SPBAvELLcvVUlrWVI;1#H?IT+eGi ztLYxP?|q8l!t!N@z@Lz2XC4lAGvjS(h`ng{-;Ufmekm%<=RY*r4a{}m;0XGvb3eev zkz`&-enP0=XFD=ZQ%gdbrGXwubJ!Y0|Ne*`WqEc^*%rjo6bB;LAJx6c?f~&N+SGL( z*L8`IG{#hg66I3b@K_f3HNzElSs{KDOhT4nJw?_VTE9#6s(5>LC)Sb%uZr?=MG$BUzbA>(G{fyw>b!`SS`SEV0?TG0X`uf~=tbSwfJwP&8R# z#j6QfBsTE1#Q={pll9J=Xr3O@#=k#wdfjq+3*7ua<>E*m|0~?0?6z$m(L}Yb~Q-*IO)PXFYAIgY!|wAJf0c(pHD&^M)J~hs|r%;=JMTz*}QK)g+N!F>DNa$_PDjF9p-F3Z_vC_V7cv>G)ehZSqwV{*m6 zWOCs-#{sII-lNA|F#mPW4$qSt8Ih2gRdIrz3d1)b|6*P0P3)vR3>hFeX{C^StY^IA z{0MbOkELlVJ<*tSP({vdZT7WiONs{bMbA?L)r)8ZLj67;xR0?BXyud@Bnh3FHz6CJ ztZSxFMqK?`pqt~ff773MidUVAr@qL0*oF#8yHd@M!-!C<8UI0j^=(0ycW`a&dhq1Q zmHHFNPJVV+`uFGIk)&fLPHDIrb8Jx-6a)(68?oifr6w$%l{U)~2q*|Dgjh#s7&HQ$ zKP4&-%;3e=f9-V9KBE6bSDB&4CiS@-p9&dZci3@vz6SB}{i^*e%!);p00ged+@Y}% zk#0Tv-gWwr6JiV>5U)AKf$-v&bGRBx1T<jURn7^Luyf)~v?bes;=Dn_7(H3%yi(o~3kNgXZ_R1NVF=JCtQW zC9iZ+2oXFgJxb<VG|$zO)B7e%kUn@%B5g+|^wm0oSnZeckOOoA_m1+(XcYaJ5eB za0nmpeEf6F@_F;)*+RyFBaRzyox2?t^O{gUUwZfRJvx#PPQy`n-*CI9={;Gkh|)#e zez-48BjIZ3tc6M8upz6tp})X;5&@l@mO#2n;ZiFqzMj~?$D7=pWifN0^1%w9N7s}` z2K}Afey90;NK;cA-4QVx!|h!(XS+RCce|my6Nk z*|Lbxkw7QkgLgPPRAi?`x&2T3{TeUA$+ zOkbdZbIC5!xfQFCJ;m?C@JLdDi{F#0u&)meBCa1)8P9CrbLROP+3K*#$nogWZ`jf5 z$LZ)0)g2x138s~bRxXAWo`xpKKYitdXP`r>`b3k}fxif12BbgepH?$fzMNc~QoXE# zStI4wMNSWFH#RD}9N^VRD53`S>?Q%75(zLT^%@6G_(Y@(uKcKkV?Ax19dXVyM~-y< z`A|lC)gFNjl69oKoom!$w6;^A>m^NWKYM)L#lx%9*#+#*wZ>Wtw(34}NsH5I`l#fY zrJ<|DmangYR4WWuFVBuf{}r!;W*OfRdICSmPTuzk`T$7UCCoO{*I+yRN>`fNyKSpA z>@nuF9DZCWFg4!)j`iUX%}W2Kzi(a1&WiD3qo2W!A+)U~8||W@&Y|mW2#cdynW;hs z$@$tTHiC{4WewDehsC+6mw8@iO5yugK$E&s#IOK7xe>TJ?^Ae4e5&T!ycK4jLTRr3 zaD0`NM&i4_Fb-p^Y#J>aqDG|(2cq>REgs*6B129?HbH?q7It2*un@peE3S6H_QGPBD6$lKN@H{Dgq5aT@g*{GPq zB((Mt4owm}528IbnU7+&vzuQo5xxiwm(cQ9UN|%f5_);V*ZMq6?jv`Wz*ea6zv9f1 z3NNM=^hKJ5zvNuDmV{Y#bJ}@l7wz0L=S`IF9{R>26Tb0PtZBd;Q)A9H<(+;oN(Crp z@pB0D!-@HaC*!Xt0?17%iX0XO5?}uO?F|Dpv+hS|)W=eV&N&fO%gDuj^Y^oJ6LmIK zs7Ux_(n7PB>?o#T6~d`l^;cSUSqO@fSPe0DD(#cn_6y)NN@E#IVOUM>_l;%HFtSK{ zFzG{>IviSXpB+8xWpdVdL~-;Hvj8j{jA)LX=gqI0EVi9(Ihw-;HkC_BEPVP@`(}1? zP3(^lht3IF-|9e@-BEcb?HkkXV!2wp?YB9*uNa=!5MmeK?vJok-U1%#x&37cCB~Zr zp~RR8wSubN_(JPL3jZv=3Ag8G?l+qIlo1D`#1hX5Z8mCaan(uhJ|h}B0pM|kR?W8X znTHFXlS=aQKcgX9tPS$r9S%~Ove3l$4DT50A@FTTrzftx`kGtgN!coNVvPICs_IwH zO;#g5Xj=5)$sF}x;!HsS%bg_~Yb(cFxyYza8KUIVC#@pvf20o8?CS}EwIWc7G+wC{ z%Z2RUVgLkNH_b3wrgg zhNKH|@W^E7qY=fRD+_Dy>%W#n<`lU-_ZH9Kf1cv9<9uzMu8Y5Fvaz1M=v|mdn8y+~ zrFyapJLjCw9QkF{={DsGtSB?-PYza@Alx#xd)aoygQomH=(jn5fT^;RpduE2p-FA- zHbb8&4Yq(yM;J1!?a0q?v$e2FgLR+!bqR-)wpI^-w>vV`jq0naO1p?{;!{1|sPvFL zcemTAxYgkk>E@9aF^+DX3B8A_D`l@^E$rA|fHr@P9EHnA103S15xU_Kti9jARnH`K z(>DJG1zy+oZCFVG%{k=~HEj%t)i!MkB*K7qx^WkBbsN-(5TDCFwRe9N34^0|@};r{ zb&&TI=qeGL^fAqU<*4l^a4P+6MXU6nYd!V5wgQ-IUe*0TwAR0TO*0duUOnuOd9{eT z-DCrAUUWDQ3i;9iQFt!&FH!%{)`It&zoMMq&&auIE-}E*Sz>3)tt|PuphF9>5*Doh zcC~i8Dq=Y066hInlYVBBp+DJqWy9T?qIjiI8U^|3PK0zzaeN1P@tgN_A78Eixbq)Z zP?@vfl57By%i~|utyE1o`~uX|>ZQSI%{7w&!iKaZ!DU4BnQuKWd~~E}=`Sw{Z&Y&) z^`6N1WdamJLQ$BJ+k1p+7b~H@%LCauJ0K@AchJ|zZ$5(JHRO_(Ufrk3R_{s^S}G7~ zRtG|d_Di{FsSwTZJ=~uG&d|F&9oS5_z{iCKj~cUaBmLhh?VUv$u|teijiRkQdAVQY%>><3;qp_XHTxEFO>!;^Fr2yfhf{bm*nQ^?7q zTXfDU9Ul27GL!;SXP|G;t6<6Ah03$jo!G1Y{4E-j`j_$T$DOc-X?Oebvm?*-&w%BV zTq3j$lAm_$@=fi;dEx@)0)C$u0xxhlZ?B!ghF0S z49(SL!yP3<{p3)+9Zuu*2Yn@Y#tzp)Z$pAkFfHzriccYx{chjO zozHeq#37ok2Naq#hj6q)7c3!ofKGv}sG|BOSA{7|h^eR7lQ%xigvbzcA~uC7zPz4m zxff>LW6Xegz~r%RHk^epnhuPmoK+61IH%V^j+VdcTwf3ycc`r%HgGnv!?1}C3f1&r zPZ@ODkKQdM0bf2L_efHVhSS1Je6cUB+z&c)&vv5E=Ok|-^$= z7;~?#wd=}US4nJr*Y`dxO_+B(&+l8U4G*0U9Sf>+0j>}IP9+%XZ#L;xcUxRj5L_iu z6Y9fqwkmD0J|lL4{36${cORi5uDGS3PO(?v*iYa37uM$wVd@$5Y#s|HhZpm__kuq+ zpBdwv@xJoSyWD^D+U4Dkw|`c=optN@pakXx9=%C@cutI+0V`;sjn7@U8=9ZrL(aq{ z!>BaIoB^@(Gn@F1XqvP5(l4jp6oW>&{%&s=qd~!s9!)(<@b4CaUuGzMauXSM7rz*C z;|gg0-2a;SzGPYfQ)|d=blIh~jDzmJWxCV9Gth|u0ggHCU6N-XyoEM`$QDQc+q4oL z^h(mjD2!z`t@5v3?eX}!|*ht(iHsr~nErt}G4 zGa(xCY?>Di?!}8I^W`PG#b5b7HGA|hkT4ii^x9NkHV12@A;QyVMn_Rfuwt0XAcdX7 zVc@p66rceh=`S`tnsz#fhf0cvL{2|*rvr4H<0q=O>_a_tyKEVx!8A1h@^?wVs; z?(V;z605U%WbtI}es1_WDL+7KCugP&CiO?$`4U7&lpFir9-wZL&NpDU=|idMVp<;4 zbPvBRo?mfPj9S=wJ5*-2H%RG!#qBgD=dzl2V8Tta9xU~!p0b5GXu6{iavbU&(Il_h zG|>X4aaV_JBVKPMPdmy8&LCBdwF4Zub5BdGC+(deZ(X499h99`wY_ zKfi*qMsID`FC-&1vc;h(F)Vdb@lU^H?qg%R+=}b_&Uk{hZs~vALL?s(NN;UqjHJbW zu8Zpq9#WWfl{|Gq&G^*3j3;9JaQebX}8j+)i;@e@6gMS^tfw$EXopDpjo*;1b zrYLXYnknh>?A;jWO+8vk>QO4B#mm2hlc8eFfBX4Th5h-w-r=*LyrNIV+cx>xk8Gx2 z>y~O5r!wzkowD@LaaI-kmOjpB>UntLn&KGi9}#oy<7U3B(OfzAE28xsDx_aj~#A+QX88;S#2?mD)KQ`E$vecPce??OX@ zS7g;phaLMQtda9I$NuP{LH}Wi0YXae7E`L%?iHL&dR-T`>2zSu270J$_yFwtwmkb> zisAcLBz)DAVeroqjbdC0*m$->R=Loy--d%{m9?{1H%1)h6NN2mWd5f5?X z%3wMwePMxFWXY_;4(2rY$0?6Er~7kFR%sy=X0Ru*^aN8E>+GuC*}#5c=$x!Co9#U zIENL@Qc=3rdh1ltDeob}CY~xhY1r!gI*jyr4JJtFZa}VN*2S(ZKs!FVMN;fS)-yF4 zCA^fDW;|=Hc1|o`zDPUQ`F*fQLF(||t#!q|Ck(^j*l*@i_qBd6^bD}iSagX*O?dpd z#BP@SxRUqF$JZjYeXi42jd=Dre)Mska`Oty}m`#*X%Wt(@30m{}kA}VsCVN+z}b| zg1aR)M&5~&)ou2lsV`8N7N2+Z*nX6_ju){~X7eEJn4wrVOCoN{%(x45|LNF;idH6U zei*%|d*$WfaLL6!gFz`{eRl}A%8U>uL(h0Qfn8B5haKOIpI>HQ&SulmCi%4KU0Uq6 zkF4?%e$p`#lVpvD`uAs&^P}5)|DcCKl`ALeMZs1cPuG&fuK-%69g~8Q*dt4_a-p0o zE|fynN2(`;TzzCyFCk%`(mJj?-8i4K&1QvZM81b9vZjpuDL>cB-HkOuzRf?sY7?{R|q{-<_Z6bseNj+2Xje$$1p&q$z* z(6Cn$HE^Wpw&xzdb%UL~CRPIc*$xZnW} ziv&;pcJ4>GDI{aEPb!}qpf#2#&i`}8s5e-aoo}p6Xb#1bmg!trlay4SB zgx&<;%ZO;AN$E5FH?j<|@OJB*idH+6tW@&;`u1q<-f^UQLy5uIaH+Q&9Q#>niy|?W zqlMowcdvov0b-5vJV!_H@DA;L%OV7lbh0uZ7Iy?cdQBGXq5H!P<8MKKk{V@F?G(8{ z*(GGvZRs|v+&YhV-Z0VbXsh#cWtFb}?qU_-H`=KqJtbB^IAi-;Zbhj2T%SsnaqA1L zR{otewl{I5L+VC3h{vtP`|ZD&5Ob)Z7SMT5wbpyllV?{Qu6NRTEn7D#{Fk@?*D4zg zw9=sMRy^ud6_o4qXCeL{|YUw7_cHGM=F2WLYGTQ;9 z4s^WRBe<&DWt9qPFi782Q+V6SxL^VVEScRd6!myg=>o0aU%Z6lW&~F8^W35S2a6%l zyOxG$a<-n7d}=s<^e$&Zps4rJZZwdY^<|BEq)$zk|NcF-_T9(Bfeg9gh?&vP{h8=2 zXz6e^+on1Yo%0gBIDam|OWU9QH7OyFP883^yS*slbAXpqFh{{Wtk=b}-T6Vi&tR3; zt0y4Ru!k0sFI{C@gK@SPad0$hHm!p1;VVXEmH!eZDrscI2|9zjzJI{~o#VQbjUtQ_bH=5{8ZY`R; zb)Mu^6nI_DzUb_nEjeqtJ$>DRZ6R>$=)HQ9O>-$9DE8 z!`F}11HQL3*l~~`tHuZdSqIFy_I!LRn_54R=J-~}4($C*+baym?9Kst+2x7?m>$i{ zS{jco{P%=>B&92iaSab=Z){_zqZ>z3T1QzkizU^gFV=pgcBnl32TLjA+pWM4b8$hc zs2CX#QzfYivA&asZIeTKOUY%bhEMo0(rv3Js~9F42Dr~e%=)^@>9}O4dyUleyN82} zqk$ESoo-{T4#YJhE+`=H6J}s6@5FPh;wHw2^y*G*jv4?R!5YvN=8RUp5Zv~ zo!O<4q-JWYuq{nOK=AjS_T{o`qTIoR?-ibYZO-sa^nLLJ|DK%3x(bznmg&0lFpb6R z!heRkQp|C=pCX9o>+sRhQM!jMPQ{iri(3~T_=Ij{jojJX6byk)W0#y9yTilBZK5-O z=&pG4deu~p_uQQBBG^ju3!M3IH#_mjdrzQyvt~_VZrArTRacggY~*!>_{Z;GUOOpD z9;}_O@Eq~bQ*JEj7JqlBfVxfdwtro)MUD??&HM>Zs8}XHy0qgOvMbRecW5+&2b>%+?+RrW4@c+Uy4wy^UPTAvILTS@!NJgsA6&osTW(I zY+|zxsgbIwJ-Dp1IfzBNvD0Rs3|15NJwN^cmVDZcI>M#vBOAF1-w^KM zI{X4BeDR5^UI<9DbNh7=sxY4P&aIrJj=__L`N)Rez!Utht*ry&9ob4+zvv(9Fz>W!FVrH{kcpdPAgomq*4%@!? z6o+yjNq&(pa5ZO6ta+wb^hHBFlVisKK)L*$izO!MSCdl`-Co-~jJKtcs-FG(t0d+g z;<1(4Lr2Ri4F=T!(h8Mi-01f<&t~*RypNl)1~$>eFe~$`d63ij`wM5b+)xwoyCE(n z?l)2q2lvg9H9AJxchZ8x|8cqq|LZ;YujW8_xBm6lqTrpICJhVxXUePkzjm6tSe{HQ zHtDtzzU7Mh?bpL)?r3<45vb|Xy;@_)A&>71#5c1i)WHOs)Ru^DFyJLVQgB*B<{yB0 z)a^}OjA+!^?JAY6n_8NC=fF@R1G**_t&Wj;{^@u(a_4<1b|h0rpy0O3Cl4u`0}*w z-GsEo7qQ}5c89g-kKl4dTnT+7+!E~W=QvI+00xlAI1w+QFPM%o>EGFPQk1o-T8qL| zC*?9rgwy$BdwP5GTlGf~ku3eki9Gm5%qI5?x&s1-CTc!y4tUn6)`z{U|G`lelwxjx+iyLFKI9HJA_&@5%!wO04L4+g0~H4JooD z4Zy3!lSn|>GGqUv}gSxztF_?F6`qOno z|FqtSs^y!#rLMY{=SHUSHiAO_=h;%0`u_-Jz5wH{t3iA*7xP5iekD$hOj z@cw;`QT)MsqZ-~z&=%dI&!D8=$8tKewH=jT@j_)bQ%*QvvVOgF{$XMr)h{x6RT#!d zBHlgymTGd^TCny7A`sl7h*9&?-aXWbU$Y=n`4y6bk2CPd`l0)&&pcYHT=%RHT@Mhr zaf!e~7w`tj19tCOi2uFY5m^3jQ@=^K)m_6^$Pr`O_N+sVU_ zbh5*&Zlp@NP`GNKjbsiry7^1>oixkmcv^q^w=vy+V8-;1hgH9!$_v=Pi3#nXC&nP; ze0bo2=^t|g;vRWuPC5tFX8m0jle z5nR;xd~T1@I_K)&6#wSIptOT^oI}Nh$kw?-6_iQoO*_96N_%lHHt3?i5@^vr6wEuh z6<*(7?RTp=ZQGRK$P+7~rgkW%SWzS z;aRIDJ1F<}k0LA9CHN>lbSghcPEA@x@bysO%i!ma(`SjMW|~|; zL%|s7Dnj0-nD>g0(Y#0dZBJmlGw@vr?_>J|Ue-?EW}(wv?I4)m5-=F#Me|e2FOtUF z@m2}zl$7z&tOGH*M@yttT<m4f`?hL{P%q4Ug*Et^PFfr z7wfepW2q}XX%?7j#F_07Ogw-}W$pjsTExiVaW0<$Jf|;laNx*bN1WSr_#bY9acLrz z42wY9yD1wiGfhI2hYHgZp=em#=+T_h*m=Qo^ja#9T`v+0@G0c55JAA-&?7y|rDJA| z$5ho>6W`I};JHM867^~iON5@X(3-OQHvyB~GabUa#C4HkbxP-Nxe=Lr{fxz%a;#ml zM!N>334x-oErxOpYh0yP$$ARyB!o1*hAAB==3n(JB%FxHfO<+K9G*hrT51K=h^@0- znKzp1^c*~Z=lyhK`=SD6UWk7dzCf~A96UfJx&w6GP_^&n#uabsBPMlg<+C~4-08__ z3QPZ;*B9%Rw9kijCJBu$?y;zoShxnA+ZqJg+Wu@|&h+J;X5pWXP>fG!!g?P@f2o_a$-<3kTSfy)!h=#(_aFnJe8PYAQaarkEWjqsp5A7z3~}^ zm~VC*KQBhC$z+HV1W{76QG(0bX)ka4W?XU@x{0`qu{;nP)dwB(b9s~S*`S7{-|uCi zg?|ID4+IZJ;KV(=xv(&VXIY(Fp*@%3w zd0_%jqs2~K0}Y9jX#*Lwo|7mGBVt>v^fp}o-eBER%^98J^{)G(9uPHrMf0H|sDFVl zlFu1>zx?h-_@rU)OG;i`M5^!5bOcv7sKq>ZJJ>PoKqusj-XA)NEImmbGze<#c+TN6 z>hxRPO-wmWb_Tz2Hht_U1^-V>`^**Hmmk4Eo?-_0@=`WU2q&34pzCelPlR3Ran?eR zxK}g{xrT`#Lwpz*_B2637)*>W=-0YC{Ix$hbIw$6eN#v4v^X`&$Mg9YUJ@|8bhJ>l z^~(Mk;(o|Pr)&oT|2Ni)fNpT8OYDJGP}Wu~#Du)h??N1=Wioqh&^4LqbfzsSJ$ z8#^v3DT()K_qpc5HHJ~~LTrGxV034Fn-s77O|v1PT366)kO#w*rSzoa%D}Yvulr=g ziZB%|DUiVCF%Plj{_%Pr)4eu@f<(*|MYp-QdBPoVs3mFMYT}%J+u_B49H@#8@H~(z z-Q1g*$BXos)7`qc%=^T#9zJ9zuW#NOn*up@e&Z3I0If0>=X`BW3mI_pT|0tbnSe2sI7Lm_e}qQ+``rd&r^8a2`Y?CCJ&S@yym0!{ZP)c z7}ang6fMQ(@v%?;Bld;T*RUa8$+V0|_8M^1V@rqSCwfSF8!FN1J6aLt&rUj(_jvKy z6?A$9DR{T%9Ba0A>JNc^?+_vv-}FcHCGRiN=zmEs{s=Koe1QLclK15yPUSak`VDH@ zac+&?h@XFNmwN+!c%tPZ%9F;LQdo<8AjTtIqp~uN5W{Wjr%D<1oI-)lCB>6_6fnn+bZ#qel8H6N)DTJ?rk=sd142BdcOdA#O+NHsZM_ zzl~APjGypAs9-t9dwjit4Rnzd!6Rc>6emBXYtN$kZ;pt6UAv^D;k-2$zj%1VW-OMa;^l<^ib0)a>zbZ zN0}eZe5~11l(bYMx!&451zq0QakM%^H0OJ}iHqI_rh=LuW~ZN&9uNCg&d#GlM0-&4 zu?^d@k~ASkek`x2jGrP*H5^Js9{L@KZBB36RaqatMFCef=C#iu$vQhDE^DpC0g0Of zGl!Huv3GM2RzAtHL^(Ne6~Fw=q6aA0185eSo2ajW^6I2KNpxexkQOp|QJ&Dd5947f zczpm;UdtC3d*^V=pw0AnA1T3o+!4$YG&*fUNA+U6>o!ld(+}9 znE;(q=^&e@>J=uUJM+aR!xAV(SElJ)=lDb~!7NI85heA-?omvnXj2o{xHgQos1QZl zn`5e5ygQ7p^Wf^`PN#P=V#xgVoE>GKI$3yhFgtz>s$x8~TFf*SfHb%VcU$Ic=Q_3_ zt4+mvZ_TzR;`Wh_Lb+Tyt=g*lI@!6q0Jz<`xzyBY#%7x+bbkYKxq_$cpY<`VvI;XW z@&j2X=T&g;Fle8*JxP$^4UsRtA$({2*A}8oI4?6}sk7a|l$ zQ1rbgZYqYtcf!QQ_60S6=963G!&}p~Qp@|J&T0_^($OkfT;fhp>PKF)W>3^M0WMet z1q+OM=F$C0nD+MXALsq-y`_!2^B3o5_{EMSoIC2j&q|^86B_7knQR(k5E=(jiW$AG z@Lx;H+_M`iiECJ;+MKOGR4fKBEeUyD)X50Sz82qEjc#=85K#9ckW67V|IUHy@GvL* zoshq2e!g4Y`r5l#60^J$w|IIQU`uOVs*axmI;e-AXNeiW5`&tM41=+FU;ao`=QVHo zO0`_)m&dR2Xpze-&0BQ8YtZb2vVOX@uny_ZN&61&?yDzKSi{8;t-g3N=fLp-1#VcK z1y>p|aIOeB{#ddb+~tdsp8jUIYxOSb91VA{Xj=kHQfOwV)H;x)0C{)$Lv_V%nP`#% z!zR&GhbQgBkVbOQ=-%puPJve0^CSXStlKdk6k|&FVt}J;v}bory0(3a6ISugd!)kW ze!tvi96_x`AwVdU2)z%q?vnve>M2=jzk$)O8V0AEDg4xbf;kf&bKw01XX8) zeDOWqJem)w5uefPULL?gqJ(JuVkG4Jh3HJZ%Gm6boeVGLbG!d5`SNdnTtyAOK^%9D z?_Q>aQ_WHHFo^HsElrQB)WTzD78fE#E5ldTf-hz^J)aKz&XLNkBCi^EC045^y$s`b z-poF6EMe>WH5N%MQdFK9x7VxA8uT%DQ>m3G&biCqR};N&WCH`NYsrG~Dy}2^NR8RI zYN;Chy#(ONbY15Sw`cQ%1D*a{W|HagRePAbxYPM-`vy5UIqS=l$7)k~RboHyl7Egz z0}dcn1K`w$5#CV-A1_OCS{)rO3U8kKv#v>ajFA)s@pm4Cu3eP`)mG$bbw@d@!* z+#l|@srM|o`u-EZOXXebk>Yu7(_H?lc1MWb;OcmFpm@LZq{#Rd6nm4tj9qznz##j4>e9b|B!Yojdu*(Ag*R0_DT$}Di>bcRQ!A!yye85%A~fs zOicVkM}1Sh`F>1%F9PSI3U8SF4bWtKT_)m^t7GppjXXRWlfu8~lDIsg*PCf22xsVP zecK+^vb^GwWMqhg&yx2M$2a~y4&(HLFF?wR9CGb9twt(bFpFodidO2~bx?Yy)cKK4HBStm`g zF-w`tjgM>ZKL)eg-I@dc^}kY`GsMQ0cXfwDDL;{FaKdzXAIWEygED>D%4%zkOvlPg~iHXvG1{12`rYUvzL1AJxB@J!DsWbDH`#ny+&Y#$5kZl)owSjHh~aZ~ zgx?^`ITq@g@V_#UrxMSmWqwTmx7~)UZKceH%y^(-(eS*@=e^?bha+C;SUF@-nhWK1!=3kg3VX&+^m3Mn$+EJzt>|A z1^H)GS344bPC3~%XS{^0=Y{V6biN0W{_2C|dc{m!obaBP_^F^lg%yxiyRB=}YcYmd z#6QRKT{kGx!}sB?-<{u(6-cz}`s6l?C_f2}SN&*JaTs>?)%UZXUkup}L43?YkZeZW zqt_>S=CtxIjXk%^K~wV)1-mumEO+fayZbKD=X4e;;4hkIzf0Nn&8MgEjFofR>K=NigX4-sf1Y#k4d4o6}?z-y|5{ z$lAy^vh%I`uU$)FPj+nRl^>L2!Tp%g(}I8jl%2L=?89K%s^1a^Xoh4s7oVNHuaX{K z1=B+St!8wVVAVkoYif)kN32_;UERM{mvFqhj^Y9GnCTp~?CN-8;U9Bevl7~RV+z=V z>*o$bq?}88K*)y=maO=@r`5Ld|q+XV!1W-%7i04xYr9UzmtD zB|&V=3E+6F%4W2H;hvEDFnqRY8tScVBnBKiFE-v&6{~khcNxVTn?E@?&d#VVnVu4* z`(%(&GM`3pT8?o|e|8deod0+E`>5Sm9Rne1kCngJ;)UMJ9j6WOX0vxx8L(Vt^xDA( zpJxxv%i8hNRUOpkujSvzFu)~9Q^{j2N?mNNO;KZ8PRqb4oca)OecoR8G*(H7T^RV$ za>0N2>-mC@Y07~S7!`3IPO@#?@7rt`UFRkh=^~SaEj+r)@VC#>oO+#<$D4nS3K;CD z{~ogaS-NCno5AclO_JdWd|LzRQ5|SC+v#!73v8NA7hm{*yH5!X9WlXkV#RQPH!s%e z5|-2x*OPH^Xz$}_g21GYsM}C@hrqEl<_scG|D}Evm*OYqs{ZkGs;Z!o&_s%CR+!dy z4}&3@dfXCHI*n!ZU8KkR_{ovEee{vxg~7G4=m6EgqDe}smQOs-%%`h1K0HXd;&j>V ztYj+Fz>=Jv6y~LV1KnDR*!0gutZ^_Og|xzBs9x{WyL!UG={K=`b@;dZ90;viiMD;l zLrd}EkOV+^yBr=S@&Z)alx&XRCFL@FzKgA5ZxT z$I$bD#T9ZMWLdkaFx0X&CJQgW8<)=L2IaD^Z9Ceoa?88J`}b1ZZ;j&=`%dqP;k6M) zJ@XIL9ozH=#iAMWonAL?J@MdMfMk6e!%bxtr87G;nxX&Y!{CB@r~iAws57%U&?BZo zktN4R<;Z;l{c6-1NO+D<8B4U3-V{mIUMESi=~J}sVKzt@G5bSfpuzo>$Inrrm-Hiy zb9=^V22&5t;TGJFrP~K9l*}g0_DWmF&Zkda+$MK{v-Z!Ee<)Z$jh>mMv9VGkhMKo|$3)R3*#c)e=z zoy39@fn&}{7l)fZc>uT_j|n=#5*}rrobgNG-dEFRs|XnZ4$1wr`BZ3emJf~>L!|Yw z1ekW+5cgZP&m%CQwis$V<{Yw+uqd1vPOiK3?n>N)xhO3gmXG3Yb`x)E+y}7o`LUi= z&uELsPF9m$39UG1)uT7IHAPcXgK+K~O~n7R;o|&~zY9&{UYo&~IaB zQjL4z@yYxLL)F#oo$JHuX|aO`?~l2sdN({Qfua#gm4RzV>S;j*Foo>J_2Sg|{{IW6 z3|aHHPLD!)I6P%utXm~KO@~5rrPI)x5`Ih_YMlfb@|jYL4JS^vEB5UYI{$oWJ&Gbv zFO7RHCWm`#jXfRb+dd7pMh^FG%~s$NyJZ<*vVCgYJn0D`nHP z0(QhaZ=-)2AhD~yj=EEY9D-ujzqS19ii)A~dB+*MCh3*zLcC)+JnIygb?>f2&BdR_ zt@O}lT96wWh8b6`t-F8VW3Ap_={(POeeEO75yGQvgpZ=qL|Cm}DS-=+I_^tl`{-V3 z=$fN-?LWJlDEE4ob`zZ3I_m}gCXaF3Jq;{cp45MzMvD!TpW6JU-Ss$S7gfitTbJd! z$2R(-Gaetfd(2r-c^TR0|Hf=i-dY+}3t#2X0M4+-&1aymBqf9s-eFd`BL#EbADi^9>APlV z&lj&gCFHyXQG?q~dR#gNtvo!~EKN-ODsX=vY2r~Z?^+)JWU!R(58F7h)M7one-jSJ zi9fXRx78fFFTd`%T+La#k9KYzEkd(f(u7k7rgwzQT_Y!Mh1Z)+YpZpty$>G@>+(;Z zL*_JU&eboa+3Jpjr-AN!wFnk@II3P2js{7Efr=;2jbqgV(zN$uC^2={$*Yxww&k&ttwnnGJvQxg z3^t5Dx?5i3#As)GwQTzBvgxK!8^k>9Xs_RzVirg1gRKL^Xt1p|2IKoH-h0zoM^Qnl z1mK4?3IIFNtoFWD%HJ}os*Z1K)180a5^qZ{Tpj17Kh?ocUkvX?lODF}&G^ z$E6rd9|jNtg{}htKysh%;Ti_o@4BWqcJ3K@rjOlvw0r)t z<5n(bYul4I#~ynSvz8wsUDSVnM)$%MlL`O;06t{%DCH_Gwh|DSId!kJAKloD_^PEz zmuZ+xXy2sCvk6;?LoEGGOwCYxGjyrLe z?=s`r&{a!iGEZmCckl5$EsZ(FVzjC$YWZk#(c~adHA#Qy{+({8+c1Cf#--cGROYy| z?+0$b+j0_kTmFzwQ}zdWHua;L+dw6Q*J@1*_9c4!RN2V-FuYI+n}-q~>xTd8Rq(qTrA z%}U2l<-Xtfyqg*h#X5hQ1;AT<+aF8&o%9FnuEx|ukfBajV{jSKm*lf0+j(F>5asHs?F5&D0TodkW^}<1a$M}E7 zVz*ZMGrTFq&#qG`58`9PE~sDJ$?RU%jNvdSdni%pGuHM$8qfD$c&T`d-=;S0kWh#5Gi%B9!uP}tn1vZxX z{Ebe?^iQ7mSb6d>g(oS-`&9sd#{}#<$WRF&kwpLi{$rEM9wnZX?LdaIFKkiiwZwn{T&?7Z$4fw*28~<*&H_M>+?=9({4K)x3DvA zIq}flBz)ZU1J{`GU5%qrB{<$X^yxfkZC88a=KT<=X_!--w(%paIr={4lihnKv_)ONG21n6j3wWz!+rjZN%T0s+*JE%N5x=ojH{ipR6hS&Fw-Wa}IoQbG8Mw!qY87rR=GXy?00h)60RR91006*k000dD008gAd!|)E$El4te*|ew7Odlx2JzQ z%%}oPmRzq%}{TXim^L6mPuMSn^~d159y0b6?9r8L7jQoTn3Gc(MG=`Y!L&e^2~k zdDwg!>~S{Nm-Y?qkzv@w=CDQa{cwM|xR{R)${3TFv1Wc`%0SJ+e&_D}FZ#`!WMx0| z&ghnum{Xe#3gj3uoOO#+j$!p~Iei(?1-5Kx>}u$Y&r3Mp^Sjy4V6vijr{^rK1*7vg zUC>T$ExffjJ`=rv)k4u!3pXAHV}LU2{kK|GmD5p_(jIHa8Qn$v&r-|jve zJ^e%e!>~KKRRg7R7p2)pd-_Sb zdF$ff2nN1EEm;EG11=hWgYS%D>YyvxzoBd6#A->qNJ@4rAEm!lAfHMx?_~`S%DM8Z zHG5dMmt~l|l8yC`R|v7U`gebJ5Iyq2)F_4$k?pSO(C(9td;20Aop=A}X09ll6)_-N zUiN~EJ9#{?Jx{sHbG0P(R0hPvnP=l&N8h1G7^UA za{s=)w-ReQnRA{DuJ5VIE1!ReAI7{fd$?;X4tMPe*W_fHoM8;#AI#$K4`W`u@ckiv z2wHu90{Q#H51;}7z4(7@nho{Ju!v5g8_^2sp&ntG7?w$ z2JGA4nql8*5Ax*etGv!tciNvTuB+Tm@`Lb&-yWaegfQa1vPMo-mmz=pKmS6v+e4H+YGCpR-s0^KYoA6#h{cvIb8d-bk~0Q zCcd}+h$g;>^cd_O=M9UxGY^LbR9e~}(m{Q{9&D^W6PBYYPT#MGh56|fI)eU9d*!r8L4!kZcMtCF?ruSY?j+B<`|bYQ znQMyfx=&YC_qn^K>FE=iuYw`Wu5d^rPlv>F#CXYVk(g+{x5}LfpgL#@)urk%EVf1N!C^;N+!X&{j~E zaxznLc2l=-aCUMvu@~av;1FQrtfhYtk+%-nR@r^bT=PnI=mLNh_H(^Kt7B`w8 zI+c5jIQ^T3Sivr|%XL)-JSq9$C|dq#3iMz3`m#F$NKiv?fC>NvarSd|6S|Jd4rEG= zaw4b4_Y2bJ#ZAgGvL+I8Gt%Z`n{B1#Ov(=A#uLhgr{`jug_S}Z0f-Q&K+PJD!2^lm z0)Q!+DhlJLc-2wH-5AwD&K*)!uwX%&+NdBBS;!zKLV7S3V;-s6C}SUK$gucMTJX3y z0{~ETfS?}<1|IWw02V#4?o2Zl$0LVJD~I!L4A)`~M`133XM~4WMO|YEM`K1^V+mhD zqmEVqM?v9AT?2yiP6JP4Dp5hB)?tjtVnJPF1z%&P&SD8aP%F`6jaxwnTAhWW9&?ES zMzanEN&mB@fcwuDpT=AruZp{diiU-bx`vLYh0d*p)@*sHYql?**}X?7>bGti|QPT z%1ZUJ_lvTN2CJ-!%1Q>S%8SeFkDzGT&!Q@pq9T^!s*;BymZQ3#MNOrHRTZga#Yc5# zs9h{aP0mGSWyMv;f6=DWqh6PzCJz66#-aS;!=}QfCdZ>Dm*F}GQbV8V^bp4Ud(0gDov+PaKTAe(2qPs&Wu+FMSwKyM`friP?3Q|69)z}TJaWp>ko&$u2?9cd@&jj3EQ^vECP)AT zAW-h%s$ntpP`G2M9#Z70b0Sc>DS~@Q&E z1G8!lL+P9|DK1l(i>)SGSPQk4my0h4=B&fjbL5;Qg|^7W){|xI#Z_})?M+B<7n`qw*CWdbA?>V0=nH&QTu z(iCbdRy7jZM9;o4KhYKnq^L&J?k>97^WIGBmi%=hS39fg_O=h4H9hJ4gavc_ALY5d7q%U-pmtbVn7#2sOtsNC)WbBz0M54{Zmt$h>(X?Y^ zgm!}!fB>2QrZ0j*SPuvVT?ZMEg3jh%3TOgCAj%^Q9w*GjHN&FHBZS_XJX|yA@DjRV zNH7tZjfyichA4xX7(ZcwWoh%ENfdjL}TdI6qUkt8+2E3XuoX z7k+?_X{iU44tYJpy0)b?^AItl(oeHoav0 z1+B15hXwaZd7wFxGz5dGH!T=L0tpWq2NGq-upm@mNk9QC38buGsK|?-{0pdp4=A8I z#yt1GK)fmn05CZN08)jDx!cEYnG%u^O&GxEZ~6oaB4)))Gxk!%$Erg8K|59GiUu9N z9?ic%yzVgtlpglHo>)+B_3BE3OMA5K$|&<@O-p#^pj58iO|b(*0lSe#09b}c1S()j z-to~BA~W%r|La8%1#}=IIX~l?hI8(vNg^@!B)N?;=Cc#JjSB9?n??%mrMX2y5oom+ ziU{tJ{uOC%(SOCi(mxURfJ_DfUTK;}a`qCEAVCGxs5JJ!s+s+N)!wB4s(&wmqW}Me zo?R)FUjJ_ah_$020xyUpn(qY|iBVbpb_NTA0RV(uNzl*`$U;6d?&pR;??jd)Sdf7h z$_PeAC{>^nI9?Z=?53*9M4OlRH&smQf?1))5oz;gRc)begpl%TrvLhy7nfnoo7IhA zg-$Rqv}zj3`&kuwNzc4#31{x4Ds(mynw3DOi>@8CD|W1mwUAlU$Xe)7Lo>pxX$0@A zE-J9@=nMqORJeRj$gTc0u@3!KgaIf)0G!0XafRM{N>%8vXZ~ddr4$&-puc>e41o^# zf4Y#E|3`-v7t`>q4BDJ=?5L0zra6U{wpC<{vEJ?7gPS%_OB@@9by0GUFv7fKXJ00$N&_XjZ$E@3~^QwSU*kXg|&&Z!Rvw~ft( zf`-x;;x=gm3t)xxqkRk2M#IJ_`jBlZ1Ze9U_ag?OMMXhsB2f3aI^YeKJt#sb;3z|T zK)5kM!8nfIo9IdnoybD(@-Y5e*nt4(nkW{Xf{H3dFdE}4W(?N1SFzY}IPuVx5&*1& zf-r#~8u-xAP|N6?t}R#^Df5ur?rk{QfAC*{goO034EL}6-@;D}BJ@x9Z{_#b&*m-F zFgF|fc=rH36CDFR13fnn2RAP_HzyMd6E7bh4=*n_7at$r-xU}eKQBKY-}ct-?*7SU zH&GA5qp2L@oxh|^>5$H~BZ1fdj5DPuJMAhcK4 zBch_Wp6TC)uvtH(C=SIv{ggzbxH)HQT8o!bNY1Gp6lr}!I(lX&omghV-cv}UV>F_q z-P;y`o1UbZd!>WSnb{{qMY>#@oG(Elzm8Vh0;wY+12tJmbx*UxiE4!7@br%}4Ozcc zO)s`Hz}L$63SIIz>kcdGvd-E414|IY6;)>YmiblHsG{%LbJ*yTU@Q(l7OsA_;4&~s zvD#|U%7VYkM?8AiR4yj|f-D@t`q@${0AD!*1#8$FxyvbJ(dvB5y0@b*IFX~!>t zOuTryzF;QCY|5wJ^f<9q({-P#i!~Be(mnNG74SGJ)_` z2QoXX+5!Zih>ZE0B&8(+#fygAO~rX@rAL;{wOd0Si@vBx{l_rO7RTA?nVFw8qmY4t z$>EZ3_ko1R=Efxj`Cbw@zj-Gxvn`eMRjj6MoJU3zzsWX<03)me#+?T^U(T2&r)_gt zbNeW`ncq+U^h)RL-r>wgCKeqvdm*Nxp|v7bSXamkQZxh3WN&@*B?0QJKlL-S##_I> z&lq(sx#vgT+DZ>3ZhDXb?s(TKBrJs)+6?`TAVN?ry8b=2)8GWd$if;&xJsYJt&mXP z6RoDj0P+>`1X5M{>iFi1KABkgM^+7YjcoNOHOV}IU7EciOz~uGI+|vaW5}ydNVzmd z))zN!hcV80{^>P|oIJHA8cvw{e)`**O|8~HZ9FBfF_w{A)0c9b*Dqh*U^k#(VCpPI zsqI*Xvwd=CNxk~PkJI2c*Euo{T1GFFri#4&?0FgBO7D#9ChFs9A4C%e!&JoGle zIQlkW`V|BMUxW9~4v>H}z(a=4^jEXmDRl*A9>l)A)yPjxkqkAtg#|(vwCe2 zSLUi(_zyiAHH!5DhE>m*3L~;rY%MR7DG6HLhbGLb1WIaG>#-83uy7a-f`cHDHu!jPcQ^2G-$s3%P1CEa;-}cb1D9eEdt60AT zVdBSUT?zzvuD69f9+2{blM!&nm?z58eV5!3ZLM3jMsfciGxW&wxvx2Lh*z{;&RcQD zzq?hH?a@KX7%pBkw4A&x7Fx`g{;i2h$%B_(N4(sQCzm3$wcxNNk;2JP8Y(a6=1wek zaOH=G71*C2hq5$;-i8(vr1N%Ww8g0Sf#1!Aj~#|~x^$j(jYCfELbaA~aH+hASq-KOqcD&WS&VMF<1F2lzjPWW*&1MqMu#=Nz0sn}c|>BX(Rhv*ph3eJN+N^ZSBAUA zMHYDNxQ$>{={}eRc}^dEIY<%3=2BUUqg1UfPg{V8qbe8b@)pSfSAHJ2QGxY^wIit1 zS@Fde(WIBJ%!g=i=M*|gTcJeGSu5z8i@cU2f)u43g9qY4*xZ5{yR5A=HYYE_MSV4T zCS&f*gdf8D40=dYgw>sV)$OAZ(Ab8U53&~5P21PW%fcyxHmibg6_StbC+g+qy|pDg z7`F`ji6G=0k{YK`K_Vn^Le^i904bq}Z(PR5kwe<$)kno482GzJ(^ro_jxU`D$G_80 zUC5S~JsniEgSHiYYRUOPshfc_GiWRlUY7C9$|1*i+jJQ`B<>9TQe+-+CG+F${0GA| z_BRHPr)14^qX#`%@Qc3PRqn;kO?0rNY;qsS79c;2QGZO&@_~px4F_An7RO(v(gHOQbB@+U`^5*niZj1G%ObawLaD~pBH*Oi&QXVoaWFyHZT zy6rNb*vZ}7KvAgg6|4Y6O_#!G6rM#wjsh#?bqKBqy3qtr6Fmipy$d$yYpY_;ZT)mo zgbif&Lt*7+a*~hcAK&PPQBxtA+pIk^5RZ@-xAKT~nI)}r^CohK9R+aL-YOGtl9?J4 zuM_elt0uU1m6-jb$v41MY~jyKz^rrLCO3{0msjTC`u2>)F2Z8~AVd`e_vKmttZ*nU zrC_+eFj2m#a>3OyEN)o0{)w73lK+PjYz2h89F~X8OL@CSvp7~C^)|wd0)s#%F9@R>A3_-=hb^z(RCUI#ByoCtGN6k0cDIeWz$ot4Lr(_C3|3SwJRRXTiDTLHs% zKa7$X5ZmF7kn11D2!&rR-I{FN8AM%#p95wYK{Q}5)MIMGi^1Txk4K}Rb=??-9zV#K zt`@;aHm}vZeC54^ub!1;Kn2cMmzpNiRnBhdpWeD2@Nsm&)jK1}u!;KwSlZ4TCfIQo zU)T4$jt8Okhtlo=X!iE9n^i(v{XFZ9bJ6>)M0aQaDVNt>)G?_WM?&}MknzRiCSJ2J zH`^9{ZL<3X6C`q!{B11dhh1_eDpW}Pof&W}UcSXgA=js>Uy&gLlWR|)%)?q`B^y&X z(?C5M8Sl7h^T}~+z3O{*iLT>ti~lX93m+M77Yq%bm+GQp5UW&d`9=hB=o^;$oF{jE zHR5i$Be5y3xu?i;r|+1!TY)v1Gw$m>q}6ip$4T z)V0pC+-h#c=~rf?MW>w@@e+95;}9TDjHI!9Iz0{<;R%4?biH7k4M4AlHA#*AR1m}l zcfm5}A44^9qFeC#=+G$5i6WnE6}}Yxfx(-T_8>)?7V4G92C*0psnxA?c*4Rr4`{pw zia@_6*4k$A?p?8P!AilVqMMB zDvtJ3L?F|?3vMfSF?NnuOt2I{yLF+FsAk(3=={+5LX$YkCMbJP^Y90}D9X5-pJj<@ zSgFA1@pKkFnh^-DjGtn8|N3I!zE-Z{U1DbG-MbiNY)Ou_3f zr87@-1V0`4GLK6=aQT|LW zvIPm{_$bHG$gd(X5fJ7UDB@H>A}`+2xa*NiLNA(Y0mXMvWbQlwOqpsG|8 zv!i#2d&m5n1TaJ%p^JfVx|IyxDJ+qxo>1QPChzwwn!5-Pb_FJ&B7}eHqGBP1M;Z0} z83{R+R_OL17TxukUvsr*`sOCs@s?6zq#pW@+`{>pP^$h(U|F61ZDC=7(R2MwK}cd% zp{f-U78#kfq>`O>yOveN&&Wn{&Pp*pD=i&n(@w#vTFJ_*SdTmi^>F1J@gJ`?GJ>uO z_9J%BH*rY2X}D67`_zh8?Hl*#;zV#rqioUc(p1R&h`TnDqWtH*t}CQj>K39txBnF#KLJNY_8!X{2B9?E7Q_NDsuf zCSIRA-0#2zt9BQTtfb^j`}0Tet826@vXcGUyT~@Qq0K$mkYR(Tk9ID2ru2f%WkNj% zQ|foQ@AQOV1$blYw1#z0!EIrf7z5YXtQ{Y4Qn*gUJMGs6)D&BPD9U5O$y~UuR5M{W z7P~Wn0+Gn*8n!@_h z)f~B%la{q92)SpWO(YKjXC#b(RLb$%i?65O&5*a?lK0)#Q2a$lYp1vJdH$}XG)6z0 z?YJFx^>~5($#3?VZ=Z^i1`2IgGq)JX>UH0ma+LY-Xvw11wm`3}>2t@#srmG?v-Zb$evDNL!gX*M646A5iW1pX zf`{866GZbsm&7|QgP{F|{v?9=d)MB~%o8=E_&$)SoaWKaim#&`0o#}{LjsE1d~^T4 zI}5MYo~9_z`g6SAX6T^X!^KwGJ0nj)BG{#?x@Gqyy03Wa6f;KBqs+wE@F?a1N+U_y zMk?pgXF|aRTbI-hwdwsU`m?R!L9`j{j=xI!dA#=hMz!z+7i%8~Yh?+$3sl1juz zi7}zmlZ-=31}pNCBf@TfrMc;Y)RDtkN3P~@9)oBagRHTjU_S0c7A9-0e3ygV!bSjD z;A1s^qtDZx)>uig_`nD~=X4&+?SrJ}i@$cSiAMIJgpy-R!&TkO!({QFzH&dGHqnE( z@Q^<1*?~`JsIG~`c%KxvsfaQN)CoQCSUGC*)-@Fd+RRAo$~B`JOuYs*Xa3O9Q)}X| zogF3C77xjNDM2I=S&!-Z@G*tm^_LKKJ^t^imWpa}{!`uHR_6C;F$)=vvaXU<=E@j2 z*bBA+X(#@|!V&9saY=Bl@hgx&F=Wg)F?bM*4m##o3?U%!4=ESYPl7a86Us@aGkD_I z_4MxfA&kC33|W{Y5>pRJoFu?{k2KOXe6Cz2g6`FId;vm1_re!;(tg~L`j9Cph2<=o zZZ7l2bF0&?v1g2w7sFK6Q)Fw7rTUS2_bi1p6wo(*PWqYH!g2|80cHYq*60fA%5M;U z_G~qrZWHR)M1uG(&OAN68>z$LZwph)I;aJkd?i!)(W&Q0>MG7=Jq#ZG_2{MeqZ!Ei z2;OzEPT6jG$XIU?lO(n_CjemKq!guh88!1t2;j5BLHBQJ#y!g!Q7(L^TuD38o@F5H zar^&md3^joJ07smJzIbt3F82J>-6~i>ga6iV0U|UZ}(v9Xy+~Q2vU%k2%WSZ%VuB; zQ!k4v=G0bQMlS9=&$-hO*`L(kW}`M;{&np;o9CGrA~xh^t`)CF zZ^#X7U^?d(R%Qf)jbN!}F%K$FvR5wsZoMLu;nao2Q@nl#Ltv?yhE66YBMjMFwTwp0 zI>(0Y-{`#S9q;lg{!(aL*nluqQICacHuC(ZdH-i!271Pu@3S=C3c=%kLWxCt=gDFx zY`3}G>L#J^z2>uwTFz9n2I1&VWZU02RVH|j@t5Nnq^LB4nEqQGpzRvUC<-+^ehM6*tICLu~_(g3>E!<#ykDSw`0^}x&L?oTJMhJub8T4Kw5dyppP*|x;d4pUD+9(naPTXuNu%wGCz%(> z4ZCEBdbo1QmlIp&>Ip9UnD~&JqaY*94C%&D1f#LQ6^PIKSX)OMzn--h&Ig5V-HWcb zm1O`tqVB1_PZ`V3$*=^04*6*Xn#Y;m(HM_ByEEOXtx2NII}u^^MD!vQ$4C;_mwEK0 zW3E_!L=m)(M!$f>UqE;Wt_`|k&`-Ok9|IU8M9iH?t}Vk3mLFgYn`+1q&p%=t*17s= zJkkb#wSkn_^qlX*WtnuX=VefvFjiO`K@y*)t69(5p0BMV_f8JJO{r}K%vafMA$U3% zkzLIc-+H#&BA`86=c^bFqkW9WoT)W}k=bn;5qV3`Ukq?5$7dUh*hBF^;P9wL(jP~}J<%fS8K5LtkwvmEpz;ExA6hBDd(?&Y2%uKI4Vfac5C(WE(q`hYNVN7F)Tqr|v zzsc0|$>z`>kFqdp=L2Ib&*q|!l>>7{dK&E~tL~{%w#XrAcoL#*7@}v7V;*dU6KB_M zm8UVz9J?G&y;zj7G~wE#6EZ#lodvv3ta=a`7+UJl1_n!}vaKnbq;(4YCT@*R%=w8S z-#1Epl)rs0z~&*fdWn?VSR$d@&(aAm-vji}2tCTVXh2!`D*kK3>fX}Aj&_NBv4ZEo z>qRtFP_Vx^_`3#gUNgIXVvSq{OfEN1sLgu11APZX9GkVqQqJM>5eTAHqj=4jiPwB7{? z++T>O7#AJ5^wB=8_m64{2YgUO=ib8RGHRuwIpUb^C)@4ujOe}gtB!XuGCeDu-YZ08`EOV2OQpYv9Ucg5b&dM7#VvVfwo<{{friF zAE-*m?9^^mhb|ex*UTK-HOBzTc@Ja6uJzJrI)AQm59 z;nFu=(i0wx65(GDI^s8$xt!@nH`dp$@NaZoFAehzme$QUKxbduf5sU=wHE4`Bf%bj9W8=F^t%v4g`gN%#=lPD<+3CAMus5Dynt zakbA))lhthD23g3>k10Tze0WxTd7CF&Vpf{b>0}O*b28-V&XelDQMu2IKLHLtPh_o zP>>ys5Kd{-K$Ds;YKAdftP#NsB?xJ$dRgMvhvu?KUa>FaOB3F{n1FD5q0v|K_4B#r zvx3xJ!~ox?`Fsf1)M=I3ra@j?``uC4Ml(tCN6kjtC;MhWPApOt5QIkkMhI^ep#Xg{ zH?`k*i)S)gQh#SVw-OOdX6@D#o-C1S%G%ox@WQh z^;Psj0P>r4$Bm4r;IK&4cUMd*xcIF3>Ih$CB+aB z! zu??|Q|M_mmbr61Wv+N6~If7S`NK7O=HqFX; zYG&!4Zj7WbL57?>@KO+<1~v{8!B>AnjbC;tMrJ=!x3T;P<`%0dD^jQJ{H%=#yoGSS zpHM1)^a^ZdGkm=B_>Hl&ilLy8ZE`W(u-)y!%GRCsXI*7$syC0`4MI9RbOsZ29)8f7eo)!myHW(5#1wn zv-Gbuuawshm3YYa$iM0fdf|FgMPw}sx6%4_?0Vv`N$yseBNjW480C`xV0I?V&^_8~ zmfM`hJiONPCqFJ#&h{(rbBdnJN12gKxB6a8r4ZB;Pg}@N2f9Dk=p*)OBU<{WisSvB zs>OnV2v-O&jHSB-7ncHJ)@sUEB($-2bAth1o`TmL-k9NaUvm@%DHsNjJo}5>Y-ECw zihZPPEu`pleIB1=<6@geJ{*Y3odK_h``B&+qlsiMJugX^N1_`VRgMn1BX)d$C?a56 zpu}!xx%eczX3h?w2rwsm&ue{e;un@_><)UY;DyLWN#z8tL}R6l*_S#|mts+ppK~I5 zOD!$X)Y+uS<&7XXAJ=4FQ|OoB%G$~!^4-*UHsun{(mrkk7zo(9td77$1}!L9Aku2u zC)Xf9MBO)Kau0u+TLNAqlL08(yICr~2K^sFfDC$M0%$cUgg&~KhfoVRX8~ci@M(Pc z44EoaGIv$UCSsE+;*HnV{w`W4i&*q&I}`Gfo`a<4%0i+R;VNymm;T3Z*PXRYQ56SeCXJFXzXdyLuLY}~%f~vy{$LzYfr(+4F{!2NaV}mN zHx?wEBFdZ`q_r!c{osHBGhV(z_~rf5+^G`DLiJLeNdP-!xL-8PX=vskj<7jX7a773 zwSPk|nlKBR8@T<_2eBeC=#l%W=MhHe%RTp`=meu>;$I(5ZT%VZH%%cyAsk@(F*P=PS%;VanET_|MO9EeyTR77( zks@8rg0m#bv_B-JN{NT@V@v2q+eZ+t4>%|ijB_yC&!4k|sj_Zus^zk%6iZ!=$!(%$ z5T6-Gcwk%u#6!QAwmd4rcD$nsn}C6K(b%%jP>z@y{SF0wH|6%RtU>d77|$}~bIgqX z)#s7YxoGWSx%Bkr>EV^6OvxFCmq*gjb(o$6GtpV5CKmR?L2b1scp(&FZ*s`hN?83vE&#w{XVXO1&t;=`Tjm(znD8dPhF-636_B8gDZW`H@Vt zxEf2`UYvM8diV5Y^$XGh4!g`l3pAx6`nS&+rUi^t31Sg+m-2Me z*Y1I>1bT!+teFvZA9>_EOI9|lh;9j~WY>X*;j6mugt${Sa!V!N@8=-?RFYKfpGrM8 z5+Ev)!b|%>Rh(~_iSkN0+5}`OH22N)xYL(z$P|cEm@5tW;?5mRe5wp7vT7~eSGCFT zEy%?EU-K&PU>3W3I%cV=1)--XyWkTWH1`{5tLg415F3P1&5kB?!ACFu#^zin1_Fs| zsX?E=%%ZX_<-T2J>Xe2!j5mqTm^%dV>8DKRXBdHk`s5r_b1U7lygw5Fn^ z4H}77m8w|^zcL}pXWZBzH>i7U$VwZcWCMUR<-1xgJpB29;wg=aisbKFhKAHCQ^r?+ zOz56asYi?{hwB>QEkJ+t27c&d+u$h#ZiF8P(&fK=cF;STELVrrAnjU<{Nhs;h``ey z-_4tR2CL-!k>;@Fr2aIYap`kYk=GiwQ11Jc;rMWMwqIB)-rL~5YzBt)PK`0Et!@er zoFGH7*;bkrV>W%>Gh)Iwz?91wlrhwS@T@c91DAZ)iS(VdMN|teCOx78)(joEM2wkI z(Xo24X+;924mKcyvfduobKm(#_2F$wpH`k#-Uv8_EeNw^n-*<-EL7PE$bR|;Hp%{} zq}8?5K$5f=3x^o=^&KDIilB4N^jw*=?l?JhaYii_DWLEQ260dbHue5@6*ChGy)=6P zaZ$oo)WJ6G>sZW(0c(YHa<%sdv5|v%C=cH!d)$V6wSF~2z6*~^vq_eYPVvmd4_(8i zQG9NqrJ`DK7LdyOwQS{~8C}AdJN$`6&+zO9%}l<7^%wb@%P;wb!aGa(IX9<)y0ttY ziANskCgVyHEW)3`sHi3IF{D7I6U?}QZG6eY0Lb{1+-|e%&-Q%8z`A%?;a(qVzM1Z< zVMK15y%st#r0sF`a?o;18q)xEZBFgiadt{%grM~=!esTZ4c$!dt84cX{5LG!vh$Q2 zk=0`(3_y&pxVd)UfKgbQBNK_$XZ`nAcDvK*uTN;Ik3XsgB>luL4-C^*nseTNfX!}l zDrq`uuq?AbQIQ_jMe*Al&J}{B^=ICOk1MCZt^h=hvp4L4yuEvNq@|{p@?P zJ!Luuql{`2%oOiFz7G-+mJz&DvqU{Jzz^^a8FD}7dC&#=r)*w6M0<4QI0e`uI?7n} zr*6Wa?qZ#%rpTQ3Npc)NzgzNq#~DcAs)#m3`!yzw>2H~$p+61g*aZErs(!;WN2N&6 zA_18XFOW7SB%r{T!h_4&pTSSaislQsAu~<)PJ`6qO+&lWINDgF#SB+)lJol-o;PL? zhVeLGTXw5&_$MZG-)X5oSY!xrZKY3u`CmEg%ny;XpRC#iyAGWVQpH{<}454T#ZhG~x?kqks-_p8UgTA`4`V&nsn*$fU%CX()-eB$0^-;lFK) zj=ul1P4V}{27lK{Anwu5(b3l7<^KNu?(V_e;WfV?|IKS5bTT3uxK4}o;Ru7hPcX01 zD_DZu^+DUt*tKW(VMbUT)0c&ai&xA}Uv7WfM?c;1w}%p3*vYuzB+yvtb&?<_59_+(`R2xzvj5_0 zwXpPpVX3(>mC9Zv^XFI8m3;GczDmze5#LAZe6c_Jp~<%et{!ys;;7`7%ImEKEX{)9 zo^^XTKkX-~t2a?_Mx!OKL2$0WgcP-s=?JdA3xVzJ;(|ksdmS`Fz^?wKvhcI*%RF=`2e|005Glkwaz2bn*HkUHKL*nW1pFZY0mlOWbLqF;hLL_Ckf`qX&&6dpLw3Fd) zc9vQfbzSJK+hrIT(J#F%a|~vRFtiZam6lqM)@pin-2_v)@mjTG9OjsF7BbJieGk&p zdZ#9cugKrMyHY(K@D9RB7DpWGAMNg|ORJYSzmWyOd)fxx+ScME!+nS)5B1&yRTLv2 zBLd2CTpK?Uab}b_Uiqw;S2X;q^1foR?m1gS|Z-9y|_z z#daif=j?o&?Bpwu+2nDhVOL_Dl*36j@NwcGA1HaqbZ6nX zUZ%q>3cuCxa)@blW=Sy6F4~&0yk+G~5IjG){ZTOT5Ex_%JPv-<3LYoT?>k0>`Q=9z zA6Z7Fmx*V-PF3k_F?%b6+P8AyYYx_oOL!2^6b-+}!hk#>CA9eL)~>d_`|YHFA}LN6 zSzkLIyL+eH_r%sDD|xXo&RFF5L0GHd?yi0C34jx5?W&uf2W)$8okYg zsfuV`fk(Z99{j2hEYOQbX7(j=qL#2>`Bx$FzqYD(#l9HL_J)L-!F zQ?_U|(KZdrrLba0R%dlKgF-oV=>&22krwlkv5USYzu24`SVQ#-YtFmPRm(i=0@#2*XqE$DZ?v=_8x4kL4}i@*;?{BXP$rEe_zj) zA6`PLWf%G{4mvRp*H_$k^8F&(-{?$^y@`)Ri$hTfg8BN;OS)82jg9cB>7su$x}lVm z_ie4X8FTd;RTkjx+EpCmHY+$a8l2r)T(9i*O$FtNsJMtVsm5$!hXnU<-b*^E8g^<9OS!Pw^5s0c5Db2irL44 z+;(_qAcb4zFn37(y3uzbQSuDqsV{ot*Z+1Y-cvR}%nTXqq4{IX(4>MKJufZ7qomEw zo{n=`yr@%2ee%Gten|E%Fe2;6>(@fUjm#~6Mijhi>@t&|;=cx39R)_By>a2AM~#S| z-%adxw@@6VBVkvIFV508_p#)c$X@u;V8OzR6e67mfsn~7 zEx&SW-2)$|3Wn%8UMG8Y-lBY!C7Yc2{7OwKPShHgm06VQZHC(88-7Vs#m^^}zVu9r z@`BZo`y_du*SQlXTdTU*YBvtPWTn_6aBO;M=ikVQMUWl#ikAX1wH!W29XiURwlKL5A>Ejc#pB)u%7=(xx;HGROILs(USn5xlGh+8be4 z!_axZgAhd4{a%cN=@5jVI~K}unm1~4@shF9z-NVEBQ{ z`&Rp)ow)M+dj>WnZpL(B1I3$WQ8p$PdXE)d@bcx*FH(k&Grai|8bZ}`t%eM4>Z`-T zUH8V8trus>pJwly!1eK?L3G)qzB+%&JNV`8F{@W+egP=%_$ zOMGP5c~_AWL$ERW3r4Yj3S!0VruS2^jM^|+C$}byHzHsYK`;U<}HYRds1w`oGulog@Cj!@cmvgZ&ls#O)sO z&O~1!j(T&~0e$XkL9XTx?8&ajQIjtBEbaNuVtZ6{v)V=cN88WS&XBvCUw7h7CkV&- z>7npy=x%KbCx)@&3SEU$aAS>ZubY~9>~A{ZM~iw%3nJ=JBr*?rPfg;tzM4+n3f7ZG zq}e>o^jU^S(UswV`L<|+ro-Tq!{NU5@K$tSIiczkMjtiET6#e2RJqve#_6*3q8AR@ ziguV^ma4sEj|-<(7odRf?@qqUn$AI39;WO5}Zze}T?ce6%T3YE0c zH$}>>#1yr78bF7*=!L?&lAnu~!;|BVAoYOC#PR`LnOszt@^MZKPq(H+BC$r%^je~N z%DthSrq?*V+X6hECG^A@uv7UKuvrmwM7^t#tO96~wOM*Q2#`6rvk8O1bneAgPWLu; z{hL_3K0!5464^57x44^03+?%0(N#A;em-C4aKm9g8+`z$2TSNc)QRawv+x$7G@7PH zS^;_Ox3U2YL{E3Uy`&Mt!>>MV^&rz5rf4vIkLb<}LEL2&!DQK5LGvrySBBp#@6C#>ZPSwhIv@^8>2Ls@G zec*G@^v&{vOD3S#SibP+i?6GLcV#LWcY}n&@bu;dA6OEJRPtRpS2__aFdvY;H;#`m zHa|{`Y#oednW$>^Ul-y^(0vGOk+2%hov^5;_GA3=FAlXw+%m zV=JZ6DC*;4o3e}67nkoDi=mYsz-vGFZGc}j(QdqiQ!bGkDzl5PJAOOB zh^;!LqLO_7&J!GcvNp1~?fN3JyLuU5yx`7zJsAilL4^ZZ?;y5SkU}0s8mW|lAm36O zh`dio-bYWO2(KUf^WotTgZGtrp*l{BsDS-NIj|rhVE>1|3{R zvCRZl2b?np(@*gGz;GN zN#8TdAlVcw|G`Io?ORLsO`i&0^C#U}DiJ;`{*YMSmbOLCxrAHAS`z?Atf^wTXTN~2 zy->v_!*(zr+Iy)+rDm$MtB{2*0QqyTB}(3sqPfYv5nXv9p<>}tK41EWxqI-g0@5a{ zt}9zoA&J68#+s1wN=j6(jeZr3X@bg~og0&@OxAa{Choh&{Yl1^CXSDe8)km2Zbn%m z^u0)unsZH@BmD3-65f4W-3H#zOXY9Ykn7DEiPCldsYPAA86EC2R8SlnL3LTGXe49r zTg^wKX(53hFwvD>zP9yWCMH}9#v$(x4KFBsNZ_S;Ts2M7eYD_#Sq3s`!H&nwc+`VO zSU@=q%%#bF@ykR3r7^>CA6QrKo6mmtxxeCi5C13=@tq;0TG;nOdX~V*@ag&4>FI() zK}<*N@Vg&k#-8S%x}lP5BhP}#R<0%7&wIEu+9RVp`FbzQzVj#HYGmZq)R3mQ>P)WP zN)6{l&t=vjIvzihLaGmWDrA_KMRSwz}*m?Rhd(e=VX$eD6JH4m+Jb)J&bA1s=mD6kjl-i|Uqe zOYatPn&n0yR<3gk$xz#7ScH^~`e9mO62Y7|ReNS8}7fO@*HF-sYtm zA6Yua~V}QD7N~JgN^Dnu3tuM zwA!oRT^=Q+1V`k>&V=IBYKkk{x54JhIeniA?!dI!+CY&ku1bW&Xtad$dgJ{fjue+L z_{Mu1u!4&-RpT<0PqCyCI@3!)9jKSOE`n|f{O|Mx%HQXM|9esx9{Lxbww#vql!u3% ziHraGl#QK@l~qLO_x|$6_QvVX?&kL9Y$)kDD3Fl=3*Y+p^UwCNG-v(d_^BA<5@)I} z1&eZ!+{*ABGTb{Ke?gaev$(UivxEVNV56nkXCoyR!TKbE`{`bVg2$dajpwL#J*l9L zjeX+eC*)?AIF`_*A|o+>Mf_+Coew|Idlb&&xPPBfY@aA`WueZ1S7tg9I6AEy&#+c@ zyM)JF{BL`|skZ4>St6T|`drifsWTtoWO9zi8Y+FS84 z>PK`)JACAh)O0Y zsj|{xqmEbfFr=U*5wke%!**B3fhT_b;@Bjp@hfmPnx?B~==zbX6Xo3%b%7fHd-n3S z=~T$C5?FslROJ{4f$jOPBtHQ-mcuh~V~lsSp~nW;JtC}M0w;^7rA^1q`E|8DU1R4_ z0UC*NSm}J};45LPX^L+j2wzrZqNv`7Le3U18%`GlJZJ?kt?o<_k)Hk#t4l}l=mnQ3 z*06Jn!&79b*tjKSJCMpPJ0TpjA^mcA=lpO%8l{-4&#Yy$lyp=Ud<=4zK&_C_0>A36 zfmta=98ZpM&4gq9_UZGe-9p7_vkI3~e4b{s0L!!yLHWnK$%x?!w}^O#8K6m<48o=i ze2n+~N%w*WV0qHB*ok|3>Zv-|L}La8knIxU1Ip`~CWwWsxyYP>rH)a4@$w6nA02~R*(lt$Ax}bUjVLZPWCQt=b#VjApQ<^BYpNsa9lErZ>nbsHMsR_~w_fnEStJ!Jy_|K|}$dEvT}$ z2poxg3`*T2=>ize-&@=s4)bE&y&cJTN#A~Z+Qezq8)s*f_WYX7`o3Y2g%}azX5u~h zj}o8ZUx;DH8AxJ|tO6IXUviwb;9Qt7FDb9BYpQ4GW^cknAemfx8a={Zs zy5L<>n=jr5xdE>9#NeopS$Q1NeiPf#boMsuZC9hXT(9Gu50z7ti1T_1WnVln1(n=g zsVH?Gw>59pV+P>>bz(XH4(ll@aOs%3RFDJ{@3=eRn|?Jz_39<-CJ!Ya^sVYSa=iCm!^87j&cs!{+V}y_Xz`CDijJkA z97u%83$-IGJ}7*RtOFKF*^M{TH_5g~qP%_ddNDrPHR^`-py;HXL9IQ$;&@0z@~9H_rfeQdHuzx7x#U`miJNkd25iy~fN>b4 zbL3a$FP7K?pX-WAxC0X`t`n0`BxPwtYuoJQC+}Q|*NVllA=eJrP44(K>RfWXusW4Q z{Cj0ZKngyf8cp9hJ5(U=pKWz>gM6k&_qI@B37hhjR*rYG<$=dZVCxt7rr>5fG=O-3 z6d%vjERdrQw$1BkYzytT8kooeF%r55@m*OABW4K0}v_ic3JvsHfLkAs!$t*nCW(*f()V~(e=;qmEt-C+ep1{|}a z1oqHMD*b_mxpZ>MWQSlLRKCz#x1v)%)qD3jmmmcK62u5nr2#c#-v5iJbB@g{+`9N% zyPevd+O{$6)V4dd?J3^cw%tx`+qP}n#@CzgCikzCoSdBGa6kF&z1DiGYc`{O4VNJ& zw`JbRU08A9$rY;H`>^vJyp}N#DF>p}Ew@^*47)D6-viCLL8-f&;G zfk*NN0Tj~)EJ9j}Xsu$@MA4$1(u1l|Sj)B*2sa(YT7nbtWxStO?Vpn0&a+Or+T&0U zPCsyWdt99_V>i@IIbfF_imcqC|6a|U-IxWbli+JVhcWmea{nYyV)_N0uZR03_vS#InE6wnw0-5k8#YYEgc+NWLrJ?Qzl#BnOZoJuzSPWXzj4 zbo~+-9#q2m^Dn{JyhAK^G>bxxa3bXdgYMD2c}?@o{6ic-6=a`&lTS$8l!F(JT~^>G z1W0as6hoJZpvg5#_7JL;DE-$_Kd!#Tlnp<97Rizg(vV_lp(YB(O>`vXYDXs3L{VkS z)pw7)<5fZ7#365m#6`x-JoK#7GB>NPq2dGeY^OFK6Z0j|^2}iSI$p~iCt?9zg|xSz zNlw5(4wgP!Jxe5M!%d|eni8xtSb<2AJv1>=1 z)7OTO2LyWu-pt0TAr{uy)O;~heVs3ge%fEQ-L8h&#o!>iJ^byVLVekPYzv7sbGKTj48j8-Y*EkPa^cv;UD zMgNbn>P~AB_I~v!RJ?6L2+KyCIR}TUkWM)0b7OP-2a9H%B1Ffq!yB>HWVOG~FHL{# zm52D5YJc6rx3zt`=>1J_A}se>m*lNE!L5eA%$g$=)a>BGjzA=U_XvC;=rY*qIo4CO zs~EBG0Z&9En+rYOVxFGwlK!eN5L78_a6KSmebI9K{&j9{aQpIQh_x}J;osi-<)%RB z2ROuO$iZ7lLKDHZEF$+%v5i6R_>o?}vNnt^s zdX4cFQGv`P0CDE2!0)Drc%%jv@4(5le;}xqJ0zN|b+ra*<&9e?wJ8KvKt9e@JAJYf z^RS`c5l<>6D?P2Tck~b;U575bJo?B8@Uztn(g6p^+9==+p^Dy<>?W=ae&)kiU!d3( z!$@FsG7zA=L?Br$^`1UahY+){Y|ke_NWP&TD1Q8cP*TZ^x3 zkOYFFkEm>ty2)f5=7n9FAbCySIaUI zo@<&0#ABIl7zz&>ZNo!Se8EYAC>I|84qIFz#C8F(JqixqD%OEXT9`b^;SnvX)%S;g zKmQ6MCZ`C5_Y2LOJ1iHk}YSo!(F;l@i7S75L=1mtAA>F@=9M7gSyy!m(rR=Jq_EPIKGUZ?f9DY zb~hRm7@8ob7f}<+0U|D9EQ@jF$gTH|txPu*znQ8wwBW&&2qcvI!?1W_+KOb;=$l!& zjjl4Iy-yv)ZLkeJ{f*$7hEnY?&|je_4@AKM3-L0NG2pH;SKQ^dz|hu!0r?0VHWz)Y z4?e9Wm0WIpi5k6cQ~R-BTCwX}jNhb}f5^H5$Z#LuG>NEyQ}H%2G_h z#ME_7Ny-0xgUcT}?v5Ut4mE!7^J%}v)|zVBz1%3HSm-yCOd-iM&;Z7R7^Jdm`6iOV z5@T;?IL^1}?){V?HPP05+{|8-KiI(#CGfnPIcTIbKse;hTT=HI+0%B}SeXl-C^4VPZs)zej}LpKLpC z%eoj?Ss*#NBhxP$Y~<0Li3Z|-eH+SW#<@yGOLy_2BW<2>y z*%n+UKdlXj0z>WG^WZ$Bg>C+TQfhsy)=@V6<47-2F|+S1CB(Ydt}vH9qN~V^8b%z% zZa6N+^nQVdjm8>)I?e^QA_ymrfE%`gh+tmMAprr8%|&HM^3uL=Vu=5;hP;W2SG&H< z-WJZ*vff8@JTA-(7J?g7$z-$J=U!ylom4^r(3HlCWD>xso?6Wk)K zpy7iyIobST2N!XgS)4F1Yj|Fhi~kBJr~KUh-sDN$P`YSia0>dhi*Ke|2L5o{etNr` z9{?I+)<=D6??O&m=IvT%i*wQa)};ceU$eBm@fH{`@ClKht45HTxn|K=eTX|cf~Evb zWrQ*CMEl1lvT0yz-IOZa*6FC~H~4`9Ab1(aX}}`OqxASaf7Nvi4uQe&119HUHIByi z4eH$fUiK)}xTtx>>O_Mou2acb2Z+7h+zaGdwA6$n29vhvlb|BvmQb-@d-rO<+~i%U zv#OLi$#0HIkvGf4b2{R|Opc~c5Ci^jJIT;bOAS&~ztzQ(Dc zUR2SmHl_CO+F>Rnh;%^KFP0v6yvutAM1d88r~|3$3C227%Qd^V!Mwc1NoJk%%??;* z>CPu9G0T7}mVjsAGn`483ip)cC?LEDG2yY9@XsL^>pigvqvE0n!w2Ef2n4|Y0-qmO zK{{P7-*>fX4e$yHgo-fKLpe4s`?SYfl7a_RU~$??`XLe9942}yqL6t$=O(#Me^zd3 z1eRaOJH!Mlyh?u^hB(99TRE4>Z~#H@lCT$oJet7STT5%C`lA2@5!?lSu`T78T#-b? z1ZM)jQfJpt?3T79f7fFKPMVy@!+2w~pWsZ>W}?5+5TJZ7G>|6!#lQ0j4WkbZp^%OYw9YtQF1%34HtnV1N!q1UO5A z_>Jt<$j3_wRv5*Bps4?yfKdD&YJmJk4TI3`%Q(k-$ESNcr-$cp-=fSIr$!vZPf6(41LZ`LBzzV&s4gEr)evR|b@sAlfs6cIFo<*nnfN!C7{b(g!} z-+|ghNCG0@^Z{K435LCHe74jpcU86rW}iOs3A*at$9V4n+XXL3LY*He8GpndxNioa z$z7W5_Zu-r>;rrM*d3}!XN0G1`snYJO{b+E7bQV1_qX4IhZBL3dzI}EHXd?txw0Nz zjbuVpPlKWw5VTvobs?CJ=9XBht)+}L(km6DQ38=y%6mNhNouF!YwTn!eZx@P8t$pN<{jn2I=2lazMMq1y`x}L#1(s{WwPkw9!geAxPoM%QQRHUm&Xs2KOLVh*) zZ>6H<+iG*CN6Yzwd6&0mk(AlJtwM~=W~}6G{8OLRMWIeL5*ORCK~I-gh*>{BYjlV$ zeSseR4Rs2w{_|eu5&=b#3LilPfATi28ahWY09-H2(x*bd`4!6}wIE*0gc;q-yv;XV z(sW`sq1P8nWHFU^u=+ianKEjK*7|uIksYsfuHyFVYfvf$%n;Xz6ZPNfPUg>|wI_jW z30$COU5vAVt6*4{v3u=9C&yq%ms9wHr1kA@vtynfr9A5 zAfTv0mE;u273T(XgayK}bG$-fS>loX%V#Y&a7QO&Rno@bQWC3|p?T`lJ+SsNjb8fn zijp2rn4Np#G;b;0$^9~DOxkOS>3*uf)uizsXQi2CutKGdVEBq+)@=7_UJA7J#{zCJ z&z|55H9W<+jy6(Y-^p_nvo4`ICrfDN!@}TnMpQQI={TR*E5;>1H@LJ4sVACy30li5Q%*#tvFo6HIODO}#y6gJS@19{ByYyH@Bt$^`=d+6U3`-4WDnSk}SB>Wr#dltvPYY8~r9Mydeq-mSQlf2p&^kQ*J@FFa=&KZ14& z5t08Ei|p4Xc(p)2J~~wvSy*V~DGdHFe<==#XHPl;YSKYVu3W)IjkvB)4Dep*yZiyu zrE1{Sv$0x+KEa#^@5+@9c#v$EL~u)`gfo}@$3o* z?+;O3=))+bFTUFV2@(G?Nc@jz$MG!Lt1PfqIh94s@{po}g#@8ymNFoY zvu*R_JDe!0kpPUVmnjDSOW?ebtuOI-%oybp4)iwG*9%WQaVvv;( zDC&6+*NY$hU`l6`7{I&5AG}1=#KAxTUeD()&(7=8hdeyxC@K|=CDC-f3aMcqicM3a zr`)yz&Pntgx|y+3OvuCEElD{gHo<={$G)%*?0>|bokELC4UvS2*!rnRUno*~`PG3%7ngCWh{nS`Y4EjpD;9W} zJb26(G6<|`QFx0&@>+OURb*|_<~L03jmxIs2Xb~IjA8{(R(@YqeVJm>AP;kMZX}V` z5kz$-roJ+k^{-9YRRWq7r+?5PDsj&YSlRl~mRYdaoq+jvVT&M(=ha!lC`*JxB4iPz zOQnEB(&+Yn^%L0X-wXhLE(Fq3b*K`)n4!Oir^dB2=Ma+Kn$ zNqZShTObAiO?P+u_u{n}*5DRS**w?zDUw!bskJj(YU7AM<1?OdA zxlD}b0WZ5;y3M+$IVO{HG7fJm0`kxrO2+$qpJ3yP_UM*)L zfC4E~+?BCcv}#n?D98ip5rxfD)TZw^Es^oOTGKMuz|gU!YM&!-*HA?yFG_neXA8s7#KZ{OYty8V* z;Xj_qoY*i$yHj1mii{xN7fYr<+~@lY)F&SBcp_)5)=z>?agtEs2P;$0s$GGg{89I+ zsY(Nsvmm>t7)#wGDD;y%qrRNrE{rl+j()M+kAR#PO1yLcR-wnuqJ7sE1TwWEG!ylL$6Cp- zCvVWqKe9Xs0Yi-yk42s(>`BujQ8Zc_TlIDhnn#^~d&R<8IcmfFC+QGSl zs8E&B!NGNilM$ixOwSigpwtc))GRilY^>B$oWtu&tqO^%-;|0e(odl5^j<5t;unbl zjUa`@go2(Ob>ohkZmCud>sX=Kdn)+c*s0y9NXvB|tFwP9&EseiKdLVAW?8?Q!OrD1kmDIFK>aKcV8MM(e)*G$nss=s z?kUI<$al1+YTj2g5y}YkTK(osEu?XL{XDhK=s#zAe8W0wi6(zoDVp(q~A1stL0nQp;EewSzt!Mp!j- zAck7zT2NND!j^yuCK2C-MW_muy{@--`>VzM6$dW5vizS0h3!Qjw65$r3fjfa%1L{! z1f6f6Z2q$<-|Ig?5WnJKXc{g{xw;s8t_+P07lfT35py{a3}77*EWQ0-Lza$tv&{`} z;rC-p%>r&hsIbSuArI_bV;jH4Ac`AW}AvseQKNl*XzX5EiHj7VcdA|~pN ze*cNY8`zQ)))%GzT2g?uTp-i_fJv$MBBOv?{;cqKDFCgOWXZ;7@JwaSXe`%lWA`xN zc;4)o%dAoh+K-n|4?ztuk~!&4mYTn+ z&_ul(g!}}`X{8(0dC|T4fH=ZE+|h;hH?7lr$!KbCs9H0~~oP}KF z#m&uLH!Kq$(@e*~0oTC!x z+<@tA?hxrCHH6isnF@)9SiaynHtsR5m?07Wej^Z6x{aQ8=yQj&G8u48iRM$WGqtYP z<~>oJZ|3_o+PU@`-5?{ds_X4gNpjfYU_{I5YNTixV<2HAl3JAYNzS}DQoQf2NjMQ& zCd7d$8dD#4i98-6Nb}CLKayW(Xyc7NZQ&y6aGaP_KF`)UX#dQdvKZ*rw z3k4i4Y2=OY%v^a*R1a6ofKfVj6@3)a64e+>=1)O!*GjCEzuqch^ zggq)MIU2z3p(n=u3Sp}5X0I0%WO8c+mNcZC=~GAT$l{ljv)2jSkzm4KbrFR<#i{pX z%^Y})q;?7BK;G`h5s`%xyPM?%p%j_KX`UU87yBQ_b_uPOL`cM4*e@P3Va14fz}NJA z;h7rdAOjUd#L;PDHSGvjM`L16UMemo5HyD|62kh;8w~#%cAsV;5FG&WDWLRYn2DV< zFdf79;z*~gP=2_$_JQV%=V2n>5TNYxAqWwZJ3P-~|Lu@72mu;(U^j!RCW+$;%y|xI zQYl7(ItvL9^wJ5HmUdy&tQjE$vM3oPu`mIsa52R>ts|MJ{PYu~Re1q4JZM^Dw;K^O zP0wud>4C$3=tw9x}RD^dp z@7AsWZb2!`9)pHr1VR%?vSQMl(M<3^Ba?|vRnX4TS}z6jKW%a@d5*l;K+sG+x2E8Zy_Yjgz3$1X}4&nb#?c7AObX z_g>Y_Vj|E`(jhZOlL}`nj0UI%4?fdt%%nJT{9>3A{R}4*wy>@=J2srKZ#B#ndAPwC zd<0)pCG5?p77j33PrcNH+#cIQ=y&i|@SzC+1u!Yi&x_FoB2W_&L3S>wr6K}RN6)DK znE8l-JChC32~GKz3iiK@jV_;!9W~60fIlcTiSXG(a*V>SYV|eJo|Ql1wcM9*CUU zpHgxC=kAE#uj!XHB&x`dm@0cn0J!eQUlLHf=k=;0$e3DeGENaTOgK+ln|3rYNO>mg zv8U1ZIn`SZzLr3`@_3k$JIhL70^+?+m4U-gmq8}9^qU4SLFq)hVk*J&BXt(21_R4M zBXkcc^FHH^300&r>+t;|CPbV!GPWqmZ)>ZLgvOsMNk;&hD0^2xfmJ8HNDjOpmIGO+ z+MK1t?r>w_sTvtG&e=mJW7s(DU#oRc`X+fmtr-F@4(=ZL@hBwA%M&xOYy56fmxRZN z2@I=@8wj=hQ^Ot?@W+@83-INy^}7;$;NhZ6gNQei=gYx+ar~K7HzZyQ!?%d~+w5oJ zAbCi4Nn>u2qLQesLJ5|kXH59XKoj+G2Ii39Fl3xJnZSe|GF*-NHqXcAZs!T}iVr29 za^mkBGg^6j(^g)r`*05stbz(zPu=WdhlN#!?~3V1m&p+cM$dlfv%_cTUq+rh8vW&| zmDsaHf{}d5sG~N2;hEO$pL(bkRuU<%v&E+(KOd47s)eG^%dolyEdaENQs6l`!pO^N z^gi=Qe%m~)AEy!-i}Cp=I+?#G^hk~@cxwnRUfMDc0V0xcW`I;6b7N9Tks_c#S`NTl z{yvq=jjl235E}S!q=!hg=I=?I%8tr%NzjKT3+Fse?p`7fe?9p@?~~vG;AQAPP?o?t zjav}}Ki{G~R)ZQXL>}BXK02p`gih7BltiKrZ`N z*!pb#iGq8t$msTbb1Slai*R=o{FVcs@!eS{7t_F0x2*PQ@JSw7$mA3kM7jB5Jqv&* z=EdP&ixKt%Ftu85?y&JWj>tdtc6BK$Nq=#|c2?yY`kNTIqCVS;NfNZO@>R6c?vL_Bd9J})kKO@s&qwMuYE-)!dCybGDnL^1qsM10cLq&?;*qDjqrICO6~ zU+(nu>=4R35m`$B523ewD!J7vwII`}JpKc3$JYI-`^^7g)vA%4-1V>lHT!iaJ@hznvo!|#HzzOZiXJ&t1JDC6( z2-xB27shZ-*rU$B_^NgWW4*%lC+GwgcZQ42d-!fDjKCBW!XN$APPrfY=t$^?GaFO= zaVixWttNg|)I=%Oq*X7hHnJfCz39O-Q?gNCohOG(Cl2 zs3Lpw=LfaA6iNvUn><2uRmi~FkkE{wiXfXLb$RBAOh#YFGABZ$Ix-ZBOu*iU52<;iGvnB8Y0!Ry~b^ z(Dn>7qhQg1Ih_DhnG}uDP$3Di7{12{_ceQ*90Nxl{~5W48m*cx&GiA@;%lj@o}7iC zm7TlTI5rxDo%LRFf#g%NWg@13Utf`l*N9}meXYeSzZ)hsH}jdeS%2hIu9JL*$Pn3b zo8A~J9j3VAn*T7iyxXDF`Lk6G`d{8uJbVB&n6@qP(b;a{}QOn6}5ksxn z24(#F*fwzds;w|4b7`?})}$}*x8a*1!`VOxjaBU9%s$syK9B;Io)jLl@2A{=t79VW z(=Z=zsKdwCC8>6eB@^G3E+iL-LTK2>xg1^S({_`aGzek0H-plp3_HAT$TFYZb4{T5 zLz7R@>%?Y*ruK+R3-L2LoM97dB!7YR>_a4TzMD8W&Yz@-t4FC}(!*{Eaqs2&22GQ4FpOiHhY4AyM(*6! z5>Fr8U+6&S2%<#|svp_(e2&{jUW6Mi4}a2*+(UsnQSh*XsIlJGe4*kq+&;(6q@SUt zEaY>(d3eZ7Z@9s@zxTqrb}XF)ChxPw5%6Nj|&CYOE5sUNK|h z+Z{AfI2x@BH&Ch-8;sK?0uL1teX;)+estuM%%BL=nc$_r)RuxrRjz`c4LUyfy~0Z=qG$)F|UZeVceyV>w_OL>KF)I<$r}Rr1+QO;0jJGgqcYL}xxljN5 zs`?3BH7373CW0=WOUE@ecYJ!Jc%atgsBI5YliQ2`;p;D9QN z&1S6&EaH85z7^c#tP%X2D93Kn8S_xt4BCQ#{ssLhlBR5 z?qzasZw#&tdjE9$fdX8X)>C{fry(Z{vcJ4C+d9eYmxmEyu`D6w@BZKh7&&R;_<&Bo zv8plrAwR2{;d+9r`kHw&pZ%wb`B-I?MXssh@Hi*)D_9iq1tFAKTP+&AE_GS9a-S^VDT}I8m!WiXm1dfg1*GChGn7}%q z@#n;D=(N;%F%Ig;EP^)a{&HCiz?1}@oEfgPkma0H~sbY4z6Mu)JLocZ;!lY|PRkRj z0QzFNPEgQer`6fr<0Zp+u2D{Tof(Ehcz{-=?4n1<2zKSz@;DOQBpv*HjTU6Sg8vlQN6#twkn8>D+Vip z1z5t!-&uW~`YLzF=0n7!j`joDB+jb3IiWRmEO@o`HoQ$ayabHLEAHZ&EG%kH{8>Ub z;|FK|>78}HPbLfSeT|d4WEb<7iIq5}Q82*D0!Z6VyiJ})SA$W)q7Vy5Vq9ZMEU$)H zqxhxH&z`d*O`68aZz@?(F15jiWr+P>jBBbbGMm|gi)G>?&xdSh4Z?HnuVP!!*&82ExLqHy)+D7qfrOVlQ^yS9t>CT_r?o=rUM)L z*rRimOI)mNTOe7@_3+qMe$1+IqhFD@;^L%!?D%Wm&PFo;XgR)LF)9j#hfO*ZCkA?S zH7{49XE!HT8ZKQXC-K0q==~Mi7OkUyCa)is<$a(S*Y{dc{Hyp5i7|8j%2^Ud9H(yi zpr0nbG0SU+QaCkw2qDO8)GQ&b|9Lt3y#uF;I1N*{BJ$n2sBCUUc092dCExaRep^RM$f)c(E<}er9piz43Kj@H)3ugB{~pup;7p~fLpG8+h`xWPbc zg{#o3R#u7>4FVA2x0Qkp9DoQJ^4~lEvn49eeRZVLO=ESgN&Y#SXvsdtXD8?9LSOoP z`Y%a*mn5*CL~f;gH+6V0e(9>)6GK8}8Ev4Slgm3`BZ+vA$&I+P3r{NudD69) zIIJqH5?l&T>k(62id5E_6xF3n5d(gXM`11s$oG13P4d8elvIH575T$kLMyKg1u4@1 zgAJdV|ACGFq?UNEg!pjN(X+9#Pws3n(9wU(OjwzAH)$DY=-HW=w~uJ(7@1haWT6Q|B+|*NQwvo$Fkb% zieOnkQ(h-TBcM$Q!et=m@y9}gda!Jw<@{#1FMootMs0(>8zs|CDk6t}!Y|~YwHvEr z>xJv6g)ufWfb^3|Q3>}=Qk48N=Qju4n7f(wr;4}x_9)Mj)~aVuT~Idu8l%d(ShV*{ z^lJDm8KPUH$_dz~k`lSlsF8vMircV0hXR;Fz^=Kz2Kr@sXOfsmC~%#4XZpAiPa8rp zzeXwyJ76>JQRPtHvBQ77lJeFish`6GWh@Efumb$u#!oOjTKZe@?oL)}Ue_+(Mv5aC zRV7F$8drXOp7rn)Lg4(vJAC#I%qQ&oDwS)Z*Nz*P#Z3USZp#ybw1R5|iju+jQ27k* zK;U@H=vLlrx}DqapU(mR8NQx*5i5*I&XzAl-i<$U@!hK9D(Rp4 z*42OQqdKcV{3T*xuFH6s(Z%6dX9d#I9=oCQn0Hs>1*UziT_H#1yY;xjNzR;@VW3MsrTEeUy4=AVDbok z21~xD7B{@=b=zWcz=iMv#vaZ0swr+)_vxd#E67v!j^Ux?R-h8kKsWLM| zFgA+Ow6CM5OLa_v?dOch%nrH*m*Cm59HpqK@>eV%>`i#no?BK3>rg_g9iz0P_FT4R zJdQr~?D)_oW!(W{9Kk${5O$aYwx1~+r||3@{2PZ8ok97xm&sMx1k&_1M->hq#-Ha! z(l9td#;jMw!OW5QYrI##8UjEY%9EXMR|PO9q)b8h7R$4CL2oFVlhqSqvamVf84cs&CXVpu%h9Q?0RF%}=r&Za1NAYt%Tq+pYVbu1VHEF4ATGISZ_CsgZ-guH#GH z3-V=8@@$p@U496f5h5ZK;2N`*>04P%N;d==tsZYKiZ5s}U^lrkl|HY5++&aBZ`gW zM1KTBVqE@14ClTv^fwWAjfoBfFX7FG>LtE9aU4Se-s6w45?VP>6KgfAJ=6<^_2>Gm zhp+m}K}(1?c%DMcHO=W%o%t-A4Nf(&&p~gn) z;e4pCpFXG~cjWLsxFay&3$~!}1X2n(rxMM?{U)y2v_g2$@GBr}k_A!n=2H5?9Nu)k zjD{}2g3*hP_wa!2&@&|*O zXHBRCO@B23=M8uj+;#M-oY}sM0y1{@H3~5whv7rL*2}+CQxCh%v}9H3-?^VsQ3Wpq zkrZNu+i*3GNMJZkc;tdpb&NkGO(lvfJl#xzLATEBa|V+x_kZrd=HRb|*>I;Vd?gEf z(P>$H;^iD~-`|Nbc;50&=km|XsLGITd*fcxvnkbvPI?CXc0Vh2@;h%*P2xjiU z({;733&;5Bhuva!!2DVbU7e`dfQapu%CzFpB~1MIACkf-^KH*n%<~Z(S}|bwwJ@K6gzP;MgU39LE5@1se-PiGJb!UDOeYi#Kec zO@;~#Z~>1c+2&W_PwN`D9t2x$0C)qQr95=`C%3<3oP9p8x5+>x#eZ>~2BRb;)!>Y8 zl0)p~c3>q~5#OsyPL3zLoCVhDh{J!qd|jOV{L&jmgQ~~KLtHTW;BS`*Ga?Cl7AlHK z4~%VgStY%dCs`oG(yUbqmO305;pzp|vSyu;x5}}aYNzt=mTZmKg&(-E{yPZt>UZ4k zwXOJZIYpVmi`=Uy>qIBj?g2W<*dUU?rwhx-ws{x^=3PEk2}4tnunX^s3z&zIays{+ za8n9@%a;5BwHpU;oI%NERD2OsX^imcf&`dr&oLcDO*AW^sEQC{=M9g>fuf1(3h&jq%`E}E%h<=0B&KnjpupUDbLrSH7GS+M`?qIAy; zyhXLthf$#UQ9h0z9;`I-v+BHGk95}p>Oox3uvFSk2iZ3;kqI|NNPHq(BHZv=UGei~ zEbGw)Wz5U$9iwhcw_}U*Tgl2n!uebb?_!8qonI~h2&|yRAmYmmWf;H+gsq=wzliQw zb(SKqqifH8{_7T7<5#2Cnnb`ihiQkX!fXfz#}I;Q>jFqZm3>hto%j-W%y}FpczIhEALq%XynynRBfXr7{ z!}s5_*}_#`x;Q?3qfV@<3%>L^SC*sA`&Bb*l;8#K(tw@Bxk6FYE8X8$N6AXj*8J@g z`R}`T&c5*zX*|^>1;R&x_)%nH!rpv(BSvGIBcvV`9Nt8HR3t#)b5XSw1A~5n%Na^} zYNo9aP5^TD*t?zFKZyYl(bqB(QE;dTDKk4|&;hlzv&c^cT6_UgNF9PI7& z4T!32fS{OhNa6AwK5#Q^f|bm30A?!yz8B&ZvL9Iq43MN&AIaRf#)e*4zHj@4p0DL{ zA$#^WaFh(i2OUW04{EN5n`LCI8UWUHYDj5dD?0&o^l#Nt2Vukh{N1o&HnX-^Nnxri*2BoJ zP0#S4W7iDEr+0d?T|zzC{BN8hVRCMMh!bb6KQS>VlQ=MAbpP%#286Sp7ONo2#f8;! zCizgpr!>yD%u`Pp6RZL|)7ksQm93uC(wLX~z75r=>Re>JPJxgEgJscj^qv5D?-t6@XH_!gj2qZJdK(?U*+x5}2y^}+ zNlEw}^8-B5Eb{tIYS+iG_p@&pb?x>kHi08N7I-+bHsiiSd}`g<(*+9v2MveVHWFGj z>(fFR;8LUeNlD zV3u&Ow9Bgmmz)yi3Xe~*s*=zs9uGprDmPT19w8Kz#Z{Xx30VJtWi_;jn-Qgfr1E?G zji7!|&GkH?&4sj5X+;jM#UxNpjcIfdRy2iS*?QOhAlcz!&blwzc1ZCAIwZ`n*eY@_ zvI|sV#HZrR?;o;9gxHfi!1;#$Aon0}_l@Q37iI&~ao*lVzD{md8DoXU5XV)hn#a10 zwK|9!mg+4zaFTJdg-7o2`^V6&UNV1hAmPf%g`EhYo z@rA+mYoH=tDhmwf|E@E^-8kMe}T5&YX!X4#;xh z2G;*F8Y)`Q!&2Du_0S413G7L7DZ`QLw|^wxs+_j;6_J+2?3qbhvrT-fED>S%He{Zd ze+Yv;KO3H>GKkbJ;{`Yk3GZosLX7&*(sk{`3h7fMn!W18&66wrLSNzuXg{fxht*)R zc}Me7${Xr=C<9vzG$Z;_1^#@m0 zVtgKXq!4#4_Z}3=qIMXcHNXg*bF{P(bBxd^HhHPS-?EPb8jwn6%4!@_fr2d_U1`Go zjHQ-zlSn=_Qe_sU8m17{4)`k>r|H{31-to`&drOie~pJ9DH5BGETSy3R$O0b`Z?VT zZhO6Kb<}T~1JOf6UpuGB%&67)CSIC3}(Pa$OfRFcPvr#xF)$r;27Q2DEvF7n(wUFncCG3gKeYjn{m4 zkPFSX4D>Z-3p>NMfKrO9T#)Z4p1MDR>SBa0bUy20V3C^I+3Z9o8?8w?-ut+?1CtMk z_1}|kj|r!Sl#v1N7qG~L>ZCe1@EdoM+ivO{HiXiV4NCA3C*7BPXqg2X|Au9S1-g;e zpuP{~<;Ddg0u9T_7=>&V<2uiG6wx)SFxvvdH{77*)U{v+E*Za?F|p1VK7zu7j!_cA zmkDN`dvp8D_E^ita_~v*!F50v{r;*aY}GR3du=d2lQ{or@ZGddhulZ5N)8HM%ao-2 zk+MXCNy(=Tk;tM(h1!+e2yc4Fx0=tgp&2f$RY@yQF_SlrsSgI&e+h%Y_4wuVoPrHC zo%<)*i%}#GXp}s1vd8#%#~M+Xr-8S*nYS73GV_N0wa)B{1 z{zwUn%%qOU*qjiP*@q9~{jjE${f8Hpl)U0+s~)H=1{lY8Ws!S}#GwE-WM8~VDl+-x zPu)3775xmcNgkN_7d8TR`EMI7J26!^Xp1}8=yLY5&T${VDlv!xh9t+nH5wOshcNnX zk&anagC~yHqM@rwh!WWu9EZl%%wGU(9igi3oIW&R=k9$Jpup>C7L4j|G-ISO5{J^sex@ zMdsGqyaJtn7zMYsG-^oL^I!vUg1|p6D_iw>c*x)#iG?otGzOJT$)Q$;zmVpnAUGb zg?>lb^{~D@nFGvofbPnsPj&^@9fgizrsCa0FCCfvlCw3{n>yh4H=|~MQjg!G(+!}f z#qa+ZJ)W`K*|7i-R+CF>Gv3VaaPW~MtJ&!!x$yzz50!zCVJwurnuBlmAbDLn*J(uZHiGQrl5$@D~GivT)ReJ$_5^;j-*WZWfqIUt5^RS%XYkGFUp#`twv$wIkQ7xy9n+zrby_OU5a z^3U&80*EODmjWh(Ih$--y=LpNPbAsbp7rNZC7S!D;~mfIRevnbqo{8H-1)A964EId ziVwV*wuZ#PWU=^T1LvN;wql0-wgAF|A`60!eN!@B<_-Myc5#q&R2VeO@A&{JA-Z5D zWtD955%7OX+Tzw$Hsb|5h}KUA{?9?_nh(a z1|6yyMs6-%ThjpK5qIM<5XLaH=3)Cd0IPu$r?Wi+q^)R+O{#_8;2&<7x9mQ3H5a#> zDXvI-0<;OQ!G?#CsB91YeF#BJ;;=iIKADf^nOPZ{B!4SPtuhyT`mz2{l+Z!2F8))! z2MGZ>Hryy=k=dfBJfNP`SM|s_hO!SFyZZu!R|;4FjAjKeMi3bQXQ(@FPaV89G`3t# zs@vtI1MTzm+?)fK$DC`XdPrjVWi2D>My7OJna(M{zqEF7QB=d?Jy}KcK6&8cfD6@A ztWiE3CVv1~WOhI&A*z`63F6kGv!s*N& zXA?`5GSirSHh~5HuSEj;Sr?2v@(%2k0b=tCS)s~Ah2aoTkgi%qvJPxM@-=ro%wL%%& zy-(0-Dy=vfJ%plelFL4ZbB<$Kq+W(oHqp&9HE9Up@m`DrK?z`=xoWo#mj%l)IMnU% zv40oD;bNK`>_NWgXB?aEs_bY#lc+ltGWtOB6C71u5)r?f5nB+?a;ao#uy_DKub+9$ zhTqVc5(d8M(pSC<(H@s-&t7zl0}lY?3CjTW#;JHeY^`k%LaE^P0h=GF8I38SH+9vS znp&ePxJ+34RW?%fivYv{%R~liSF3D0&VOzu$_7oq;nL_wjzL9xRLn+{hK>F$*7`Ai z^dUu1-=mYH`O49DBtv%in7$kpf&VY9j~G2SeM--Jes+-&PBBn|89)Yblb&f4t(XDauKfD`Sixl#`k5do5wsphYAx(el$O?)Im0Q&FyBpMT$*BmL zQ<5d_{-SxSOEW|#QV}rPEWLZdBQc~bfm*_emHBtMuTw|Y%Vi&gPglz}jepolw-L}q z@g#mX91t{~O}ymr(xy%AzfJXN(`#ZTgnQvIVUl~KW(IKO!xzTn%G>(d+d%q!_{#d^ zG^ib#vn_ie>rGo*zSZ9`(<1*+`>U#S5|taZFKYkMq-8awQuyY*Ho(m+bMg90AuI?G zwFe$00Lm-_XST)fb?u<>7k?K&84f8E&8S+AH_xH1?UJ^J0)V^gqIV13lEnjDxfj85 z+lzCO@wg!<6(T3#;hy28R#F+k*PH)dCvC72UD(I=-XWtGDOS(V&(9YZ05}oD6hIde zAtQAPwYlyj0hTvZ3Yl?x(d$#EHIH-2wCA+r9Iit;gx(klW&jnIOn+EasuD|q&4jS` zKhw`fMNY?Oh2QqW!!u{b<5|x;!`z_C8%0etYP1`Nsq4y17-H!Wn?qzAEeRp*AJQl+ zX--uBn|AJ?v{B)kPJb7<$3Xw4@^Q30kCtbbrA1FXmIOXUw%|n?NU*filqu)vEo5jb z+bG9T*0*rQIlVN5yMONP9?Ie+GJn;nCQtpcR18`;astwmDWB;x&LXK#Pvi|?iLw$d zJ98>h(6#QhYMI^{+m%0dvY4qb&vzJnTWsn zee^CrF+0YPUQES$90Jt!%28xa)YnFffAb3#go0o@erp(kHh=oUl*1<-XDXmDI&G)p z`vC4>3B>hwQXmv=oFcG)Z4j;m!(aWRNMUN}OQv_iLYrzotvchEG@A*>0Oa|~2r^(o z5&S<2!0~XlJaU1R$2E=OjPmDI1^Z+(wMIq=~z_HQ6?OXZj(SOsI=B$Bz zhn1FpFUhABkYIYvgZqaxVcJa`q#`e0D&U~e)7Y8-oB?+OP)Tvi+m^}p$^g+~0zGJ- z*I+g2`8Z@E8RptsuN|l3y6x-PO@*FIk)C)D3xCzb6?coZ4-0PzZ^3oG2*5SqU)SdA zp&We~bn-=>&iR(pJ?0nc_n)NQC#XBdibDY88L#8a6T}F2_P-Xe;qdirauAHo%2BwNz0Wi8^~9!Aa{*c0FpP%d)!A3dOYBJ(>kk%^O_WMq);F{T>+ZL4~LXy z0DtXF<8bT1<>yCKOVeR9cxr5&&nAECYSV>H^8q4wJICplH{O@Ch96br$WDYzYv79O zy_#4yJB>On~rJ=iyqjCh?!K3fR_x(wkj7XVE{m zM~5an07Ij;+!?b_pIzsR7EUKCGO899%YSs)ljPu7H0}gPA1WP$xS3BokB!^_9+_#K6qNdJw2y6q2>^iO z!-NwO)1e2^vEP4fjmVI?`aLK6)*`a93W;EVO-VgR*^dQvRnRoQkipUZcz>$+qDR-{ zLNt*P1;3Qq0$42t>P2e;SAb!d#}HK&mp4j`cE;-r415zJveaF-B8M4+_(P?AV*H-H_|}Z_ql6T^^~#|%u#3qwPwx}z0Ab4Ecd=Dl zA=T*#``S_H?T9gXzj-2imVeBh!gLM*wEcDlX$pIngyLat`>7LHL^$fU=#`AqoPaN7 zxUN;k*``1m8DUo|szj~R|4Rla3EM?}4W(jv1z0gC6?w$gsDDWEMZ(4Af@IRF z2*mly13IW}i4dBn-wps>LxV~J!H(f2Nn~35&8HFx+Gi_kn;d$TSeZCZe(cj9HxoY) z08eLUQvd(}JOTg!0001#MF0Q_00016Zp67$FybjHD=jQ3EiW!AD=a85Ffb}ADJm)| zE-^APG1oCKGB7SFDt{>|EG{rKDk~`|HZU|RDKarIF)b`CDm5-JGBPPCD>N@LFDfcB zEi*AGC@?QC^aXcI{h5-3F@CnK>J5;}K%r5T?Wcj>ZAZ^IWqqIPCyO_Q(MzOWdR~gv zueIa^5;x398oPP8nt|}b8Q0*szZe<1)z(hO9AaE%Z$H5*?;LYoRIuBXngIts|Mi^!gj8@_JQqvPsA&3ImfrKv zZkwjsj<$+KGv=#`1*G|Q6M*K+R0OUMoiw0>!Ov6rx_=C*w?v?OSqE2hf&Lk38D$cP z;bdn3toe2kpfihuxII9krA`2>`F5fd ztMyApiV6IwNT2~7?7r+HwrEB9&T7-x@51XSUL&06hH>XiSPIJxy!mz@WR|492%Z%9q%`Q)j?r))A4*zmmi9Anz`0cVRY_FLN(1Tyw-~1 zAoG8_0NnYCQ6P9byte7qDFGd%5yJZas-7=Jd4mp8JfV-R2h@!W)*D4fH%|c6^@=Ex zM1PvYZF4tw7tn{Ge6j^|PNv~!w~eu3LsUkL&&Q=}0x>6v067zKx?`YOXUc>7^{U~0+ro*l}~7TYr7LDb=6oVTEiyhUEm6& z{a(ZDT98&k$J>2;050KYZKJ>zsU`hFo|-Py;ckMU&mHTOt^G}11LPI2QOrZBnK&J` zn->XOgMpp`m76HZIbAYd>GlR%M}M3O(Gr4EaM%I7{dOwic^G9do!M@xlt4=8<(=A^ zF6GZxbsSR#T^ZDp%?u+}c;#ov0i^YI0^o{IXrBKTfWbkX1gs{kTCuj>M&GRgP5dF#v;g4YTcp3zkeljH7zi& zQ~K?Oq_u#30xLDi`rNnkv?Ih8K;z&V?7x<|-sgDX!0#Pv$C zvW4w9hELZQCg>16ytd8$luP*L;cl)cmYkp3MZ8@_-sB?@P<~s5nhc1cZR9Plv6WEQ zl1g(0&Avz=_Rv6#^)tO=rGGJR-I%OAsk}8hyKiN0`?{%Od%Nnqs$s1I>;ZRi!vj;* z4K9J-pKXD1Scw-8%L#ERVo^g}i|sotUK`oMwyr06bbbv!?vp-=1OVhMuS8|=Qii*VG78as?8mOTHSFP|F9HA(H-8+EB8U&GtT&S~ zzqiFKipV^I&^rgl+5o~8D`Af*a6d(R|BpAny3b2Z)Y)^>e%_UQc|WISGHLThGg|M< z^wJZ}j+T`IUeh|df2^l903kwiurJp-s+<494;it7TDZ)5NzEk^QGgw-gh$uEgPY|21&{v}r9Y6fSf$q0Gr8jZJU%KI>;Sr^1eUB`=_m6@P7>LqvF(%^CbDcN^uY zDkey6v;8X*NDbx=KHGXm&5BBfXyyDbM%{ds zgfkB1`@X~Yl>wCbN>RI=WysLO{oiT81cG1l3`fiZM8HpI!F~S|ypwc0vwp%D z{YtqyILX5JVVk!Jq=P!itVw4@0vl4?XR=4ru%nRumGu#jJyhNb0L=MH0UuqOuc+&3 z?45w9!+-NF(<|dnZf_i-jGf*A7)R}Bou7sDHApEcg#euKt}1O?1`M=L?~@}y8OZN{ zLDA47h3GypR8&W`8iegH{u-@w&cs<&tlODj%q_3CRnTRSg1%rK<3$27P=^!wRAzL> z`^QCEVp>(~$F_)U8lOY$)YhM5{*9vmtoe3IyMKpKaBO#P2WTlkZ~@8}a9cL(&P>?Q z)iapQyOq-CUF5r3QBM>dii{bZh=^Wo0Q~uO5M_rj85#S>%?ltU6EcUsrSPxQ22O_Tj*W_BE$?3wMP*U1ojFE5YWT9*nw>p2pkWYA1|NIb5ecR z@#1oz0+wKnVob?XQ~~S8LX-h_NW&KPdgMwmz_vdNlryKOEqkmQ+-OtLT~YeGO(E0t zTXb}3w0Z{j5np^d7vT-WC9fqOkAKfIJwc4{!F*g*flP?yCLGX~6nqA;(qW{?mv4&! zh3G0o3_Q19o938H&+(pHzTQ z5OhytohG4vXieA9^pAO+!xc>V1Sj!;5a|4@0sV*_R=Zxr_Lyyb2thYV{(WX|`};x} zlc{xvI$QoxDC^cQ1y0sTLHCLeN`fwDeP)yZv>9JDRq$;?z-Rkc27gc*tTx|{!;Z%F z#FW!nuf7(4R=Yy^T}AjsXN(basuVh$vN(Eb=mh}Wqbzo7xQtm zyepQqe1uXFuEKb-fBC ztuk3SEXWU!UgMkUHLTAE!~sh=J6c6#+5NElO8_o_$zSi8NLI$Au_}2vWIe6eHr3=b z{ne!p2ekcmV&X{|F5G+DtIvQ-Sj=spy@XAY={-=T-dUfa6@S>e-<2k{Neuzy`N|Xs zDP0ICCU}1ez^dTtsM~lYQglS5B3r6%L6H2Sis1OVh6ui?tGTWgM6a9v&z zKxuHbeawybX@BC1h>+jahffP_i|S3(Joze)`5RQX&;aBe3y>OvW|Z&976aHjtLt1P@uZPf zluVlE6;-+3v0ZFaHOi%@0POjS3Exe7KfIRdHQR#3!R!alAD1$#vafuT@nb1BZgEdiyJXuik`-A)y9V`s_5^Jq&PieFUgzNEw=2mMlys>49o$qTElZE2IW*7a6gkk1rNqKLw)fKQ#`^u zk$4cxXO$$+*@a6XxWtSv+~I zzQL5bMGScUTKKl6jAo%U0L=MzT5VfOsoX~WziPl5_`T%vyPROEXF;C{LKWvar|H`M zKRtU(dQ#%rg1<~ePiJRS00i%40ssI20Dl0KMF0Q`0001O+9+%;G%+YBDJ(KEF)J%A zC@3i~F)=YTDk&!`FEZCIFVoU9FEBGRGbku2EiW+3+soWB%SXdARI}n9s9>4xLu{0V z`71X@+Go04S7VOO9SM0BXvb}HKZ|Yl{zFln@9|Fu0RZ#?OAOt0(%jHxyY~fDgn!0A zf<32Z^Ei<6yBB@lCT!8yAunxgqnqy}-l1vmvN8|=wE3jzklGX^sSp4 zdHufz_=QFRy!}cr`?Ngx;NxD=0e|!=cwRKkj;Ks%APk_d_+G@-H@41jc>~~y6cJ?k zUg6T16;2=&6PPW4#6t?8RdLqHY$^2|AqTVBAE0J}Oar$R!;w#@jsUFnb}Ho&_EibR z0Bk!z8Q2ZD_uBa4l9R4~)xmMT zct0{p3{t6BCh<9q#+@}{x__aBS{sJ^*}4Fn0k3d&2+6+O-tqftM##Y4L87D=SV+6DTuViTpgXr>8Ot9Ss;5sM+G}xK6+#Kwl$XAZ1kg_l6zJz(0F(i*m&W~TpK^pU_5Za7GI%gK>5zJ3 z5z2WH97;@!h0eo%rhj)-dCDdFJo%)v=>XIjuXO48G3+khr+dW$olXbC1%)b`W_@yA z)~i8CxP|mxrMQha>%wC2_-l&sbIAa_{R(j<2&Hs{+}l15Aj3iEBAluDM9Sz|rs~1J z6+A7E9HV=O!}}#&xup&={Teg@^a)Fe^t>w?p#*>bUylImFn|9``fFXARX-aZglF4h z{{l{DM^(8$g)fj@WYz%N2m_?~ib(APtE43@4P(}MHz=s!Qo!8VXIsx|=wHl~ge4~i zW&Tbg@2H0d#ZcM%Q9H&cxf8?zcUe9z0}J=V)>jksI+*>!ji;?O)b4$xJbnT?-L8)N-#T}lB^P?ho}qV#H67^RB@InoVLK;h5&Q{ zcQfM-a0laH`)>hUz?ix8Ij|(H;T0QMW(v++N8L50mJ#|80L%qTI5Bob?~-3I&93h6DgywTEGjucNz<34${P9Xq>3`X4*9{!(eg=>l$lu1s>DdTb ze7_ST2<9O5)W6U=6St`r@i+hf|9jN|yd_^rH9%%Zm10G}wr&AH1rHyEM^l=V>k_#M z@28U*uIG-D3vGR;&f;8z?iQT@V^C$6U)IT* z0)4w*lYhA3MSE>hKW;S}pPj|s@tLy+j;3w%nKEjtctqCGC}|@f7B*CyFV5eqfOe)v zr!x$1=T`ZZ!a4DebwDl^!QjW-#MJiwHKj;%9ZoWn`B4>o*~2X;?Dz$@qHK}-Ow{fZ&`%zz99k^d@y)gT?2hOhjH zyPK&~zBf(tK9_#e^_55Q!M9EJaZ_XN0$c%KQ676Gxp9lYn_>x+0d7;VW{O;^;wQ4^ zoqwG=vdz|sEnKQb?)zpGEL2t=H2P+=wMk0Fjb>L@l_m|ad$cot*(V-hJS-4 zK#k=mazcp~z;N%)P`Pj?SVl7QhmGyi`OQfgJaEc(Hw#LLvq7kUl zw4PsLZms&kaMMW6AE83UL>)SdcW9iV%Px@&T@Fy=`v+u!kw^%Or2?^u+5~y@e|6Uu zI5Z3^0-%ZdYL4{t`@Y2ZnEBQmvVX$w@@q6AX2)9eC)%}Q)K#0Yc1d+%p3#Fdj;xul zjEVA?u5oMAE_b1RZt#mkCPJz7ZhXThkCn(D3fu_=B|S-Ln9wrWf0{rC3xEC1Rkx;! zi8gIxQged4Wh|riJ9pg_4bMLe90-toA83sLv=OhE8sV~~4#fcdS1wQ*tbaw~VW)~D zdoV}CT6`H|`!Xndaj4*JZ2I$Wauj#~>=pe=d1f}22)h3tcY@Lb_dJH2Bsy7{*Lmn8 zw|G~FDs8e@k)n1SFnUp&&m!p(jQxsX&@(g>Qn^iSeYIdMq}v=A{0_@duNLy@X#bA= zcxyanQnU_Dwo?zb+{)wp)PE%yy{K1 z@D;AHD%+CX;BDK_*6a9w#mxbf4GV2;xUlKq6brEXG!s}oJlA+?d+F`hJ&sj8H??AS zEq8CV=&d1a%eUL}o;*%C%N_xU~>X58Tn-+fR`oE$pH|uBSd(S>|-( zZ@Ev)wo&U<_f~AzrQlcua;iQE2>NCWL<#aAK8?JxD!sdR$bXq*Q>0;;3C2oG=cBKf zDR8iV)wH*s44b#RpTf2Lh0G@_Zhtj*Nu5;7uG%VXxl-%6DlWi%o-GPR$zxGluABJ_ zEs7ko_QI{iT>-U>D3?nJ<-<}y?%Ba&&+jH>@W9BzX^W*O7eelj*gjiH-<<`$?2bIR zwUHUwAog#YT7S*Rnwdz#b|+&%Cz1{ZYG}qwd!dbGNi=ZUX~tV@%8yeHMmV`%RQv*X z0KxjVjLrDL?=CC%`$n>#2>^a*^i2fwACR!X)mDpkaw%W~XuBe7Ix5*x;K9oOV_K%R ze)^F6{cP{6!+)}3U7p-_9j8-kva&pxpZ=WBRz|HTx_>g1TpKmRLgZQrzQd9X6VxlD zH9dnFc-yFaTw(ATQ>f(Tele`Ou5x;kgBiXL}6R>x@z4dpO`=7tW#~cKy5Vc*(BW+#rkMJ;S&<(A?jK zCx>uV#(%({0kBD}xiUXp>UiIU^fFqbSz2^rjQm!bd-=706SIPyG@V zJ@6SZl+4gzgyz3T81e~Bj;zZvisoY8ucDG=mRhXKvEl>?G`~XPVQln->AxCbtYO!t eUc*kwEAe(UxE@Z|2e|ok#x8*D7Fv-3Ll*$!Jl5*~ From 5bb0179c25ee34c02e6fd241c43672dc3f09ad72 Mon Sep 17 00:00:00 2001 From: Zealith-Gamer <61980908+Zealith-Gamer@users.noreply.github.com> Date: Sun, 31 Mar 2024 19:28:14 -0700 Subject: [PATCH 097/133] Gave Blast door access permissions (#26606) Added access reader to all blast doors. Added pre configured blast doors for engineering and science. --- .../Structures/Doors/Shutter/blast_door.yml | 1 + .../Doors/Shutter/blast_door_autolink.yml | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door.yml b/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door.yml index 97f3c1b9e30..0700a5a3e7a 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door.yml @@ -4,6 +4,7 @@ name: blast door description: This one says 'BLAST DONGER'. components: + - type: AccessReader - type: Sprite sprite: Structures/Doors/Shutters/blastdoor.rsi layers: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door_autolink.yml b/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door_autolink.yml index 399760616c2..3386703ec90 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door_autolink.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door_autolink.yml @@ -56,6 +56,8 @@ parent: BlastDoor suffix: Autolink, Bridge components: + - type: AccessReader + access: [["Command"]] - type: AutoLinkReceiver channel: Bridge @@ -64,9 +66,51 @@ parent: BlastDoorOpen suffix: Open, Autolink, Bridge components: + - type: AccessReader + access: [["Command"]] - type: AutoLinkReceiver channel: Bridge +- type: entity + id: BlastDoorEngineering + parent: BlastDoor + suffix: Autolink, Engineering + components: + - type: AccessReader + access: [["Command"], ["Engineering"]] + - type: AutoLinkReceiver + channel: Engineering + +- type: entity + id: BlastDoorEngineeringOpen + parent: BlastDoorOpen + suffix: Open, Autolink, Engineering + components: + - type: AccessReader + access: [["Command"], ["Engineering"]] + - type: AutoLinkReceiver + channel: Engineering + +- type: entity + id: BlastDoorScience + parent: BlastDoor + suffix: Autolink, Science + components: + - type: AccessReader + access: [["Command"], ["Research"]] + - type: AutoLinkReceiver + channel: Research + +- type: entity + id: BlastDoorScienceOpen + parent: BlastDoorOpen + suffix: Open, Autolink, Science + components: + - type: AccessReader + access: [["Command"], ["Research"]] + - type: AutoLinkReceiver + channel: Research + - type: entity id: BlastDoorWindows parent: BlastDoor From 29d7b73da72e066436933f869ff7adbee1d24f45 Mon Sep 17 00:00:00 2001 From: Flareguy <78941145+Flareguy@users.noreply.github.com> Date: Sun, 31 Mar 2024 22:23:59 -0500 Subject: [PATCH 098/133] Gives all wheeled objects low friction (#26601) * gives all wheeled objects friction * adjustments to sum stuff --- .../Objects/Specific/Janitorial/janitor.yml | 402 ------------------ .../Devices => Structures/Machines}/nuke.yml | 4 +- .../Piping/Atmospherics/portable.yml | 2 +- .../Power/Generation/portable_generator.yml | 11 +- .../Structures/Specific/Janitor/drain.yml | 62 +++ .../Structures/Specific/Janitor/janicart.yml | 324 ++++++++++++++ .../Structures/Storage/Crates/crates.yml | 2 +- .../Storage/Tanks/base_structuretanks.yml | 18 + .../Structures/Storage/Tanks/tanks.yml | 18 +- .../Entities/Structures/base_structure.yml | 8 + 10 files changed, 433 insertions(+), 418 deletions(-) rename Resources/Prototypes/Entities/{Objects/Devices => Structures/Machines}/nuke.yml (95%) create mode 100644 Resources/Prototypes/Entities/Structures/Specific/Janitor/drain.yml create mode 100644 Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index cad2d9a9245..b192401c8b8 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -87,123 +87,6 @@ - Mop - MopAdv -- type: entity - name: mop bucket - id: MopBucket - description: Holds water and the tears of the janitor. - components: - - type: Clickable - - type: Sprite - sprite: Objects/Specific/Janitorial/janitorial.rsi - noRot: true - layers: - - state: mopbucket - - state: mopbucket_water-1 - map: ["enum.SolutionContainerLayers.Fill"] - visible: false - drawdepth: Objects - - type: InteractionOutline - - type: SolutionContainerManager - solutions: - bucket: - maxVol: 600 - - type: Spillable - solution: bucket - spillDelay: 3.0 - - type: DrainableSolution - solution: bucket - - type: RefillableSolution - solution: bucket - - type: ExaminableSolution - solution: bucket - - type: Tag - tags: - - Wringer - - type: ItemMapper - mapLayers: - mopbucket_shark_blue: - whitelist: - tags: - - PlushieSharkBlue - mopbucket_shark_pink: - whitelist: - tags: - - PlushieSharkPink - mopbucket_shark_grey: - whitelist: - tags: - - PlushieSharkGrey - sprite: Objects/Fun/sharkplush.rsi - - type: Physics - bodyType: Dynamic - - type: Transform - noRot: true - - type: ItemSlots - slots: - shark_slot: - name: mop-bucket-slot-component-slot-name-shark - whitelist: - tags: - - PlushieSharkBlue - - PlushieSharkPink - - PlushieSharkGrey - priority: 3 # Higher than drinking priority - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.40,0.25,0.25" - density: 60 - mask: - - MobMask - layer: - - MobLayer - - type: Pullable - - type: Drink - solution: bucket - - type: Appearance - - type: SolutionContainerVisuals - maxFillLevels: 3 - fillBaseName: mopbucket_water- - - type: ContainerContainer - containers: - storagebase: !type:Container - ents: [] - shark_slot: !type:ContainerSlot {} - - type: GuideHelp - guides: - - Janitorial - - type: Damageable - damageContainer: Inorganic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 70 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - -- type: entity - name: mop bucket - id: MopBucketFull - parent: MopBucket - suffix: full - components: - - type: Sprite - layers: - - state: mopbucket - - state: mopbucket_water-3 - map: [ "enum.SolutionContainerLayers.Fill" ] - - type: SolutionContainerManager - solutions: - bucket: - maxVol: 600 - reagents: - - ReagentId: Water - Quantity: 600 - - type: entity name: wet floor sign id: WetFloorSign @@ -279,291 +162,6 @@ tags: # ignore "WhitelistChameleon" tag - WetFloorSign -- type: entity - name: janitorial trolley - id: JanitorialTrolley - parent: BaseStructureDynamic - description: This is the alpha and omega of sanitation. - components: - - type: Sprite - noRot: true - sprite: Objects/Specific/Janitorial/janitorial_cart.rsi - layers: - - state: cart - - state: cart_water-1 - map: ["enum.SolutionContainerLayers.Fill"] - visible: false - - type: Rotatable - - type: InteractionOutline - # Removing storage until OnInteractUsing logic resolved - #- type: Storage - # popup: false - # capacity: 80 - # blacklist: # there is exclusive item slots for that - # tags: - # - Mop - # - TrashBag - # - Bucket - - type: ItemSlots - slots: - mop_slot: - name: janitorial-trolley-slot-component-slot-name-mop - whitelist: - tags: - - Mop - insertOnInteract: false # or it conflicts with bucket logic - priority: 9 # Higher than bucket slot - plunger_slot: - name: janitorial-trolley-slot-component-slot-name-plunger - whitelist: - tags: - - Plunger - priority: 8 - wetfloorsign_slot4: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot3: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot2: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot1: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - lightreplacer_slot: - name: janitorial-trolley-slot-component-slot-name-lightreplacer - whitelist: - components: - - LightReplacer - priority: 6 - spraybottle_slot: - name: janitorial-trolley-slot-component-slot-name-spray - whitelist: - tags: - - Spray - insertOnInteract: false # or it conflicts with bucket logic - priority: 5 # Higher than bucket slot - bucket_slot: - name: janitorial-trolley-slot-component-slot-name-bucket - whitelist: - tags: - - Bucket - insertOnInteract: false # or it also conflicts with bucket logic - priority: 4 # Higher than trash bag slot - trashbag_slot: - name: janitorial-trolley-slot-component-slot-name-trashbag - whitelist: - tags: - - TrashBag - priority: 3 # Higher than drinking priority - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeCircle - radius: 0.3 - density: 250 - layer: - - MobLayer - mask: - - MobMask - - type: Spillable - solution: bucket - spillDelay: 3.0 - - type: SolutionContainerManager - solutions: - bucket: - maxVol: 800 - reagents: - - ReagentId: Water - Quantity: 600 # 3 quarters full at roundstart to make it more appealing - - type: DrainableSolution - solution: bucket - - type: RefillableSolution - solution: bucket - - type: ExaminableSolution - solution: bucket - - type: Tag - tags: - - Wringer - - type: Damageable - damageContainer: Inorganic - damageModifierSet: Metallic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:EmptyAllContainersBehaviour - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - type: ItemMapper - mapLayers: - cart_plunger: - whitelist: - tags: - - Plunger - cart_mop: - whitelist: - tags: - - MopBasic - cart_advmop: - whitelist: - tags: - - MopAdv - cart_garbage: - whitelist: - tags: - - TrashBag - cart_replacer: - whitelist: - components: - - LightReplacer - cart_spray: - whitelist: - tags: - - Spray - cart_sign1: # this is like stack of floor signs - minCount: 1 - whitelist: - tags: - - WetFloorSign - cart_sign2: - minCount: 2 - whitelist: - tags: - - WetFloorSign - cart_sign3: - minCount: 3 - whitelist: - tags: - - WetFloorSign - cart_sign4: - minCount: 4 - whitelist: - tags: - - WetFloorSign - cart_bucket: - whitelist: - tags: - - Bucket - sprite: Objects/Specific/Janitorial/janitorial_cart.rsi - - type: Appearance - - type: SolutionContainerVisuals - maxFillLevels: 3 - fillBaseName: cart_water- - - type: UserInterface - interfaces: - - key: enum.StorageUiKey.Key - type: StorageBoundUserInterface - - type: Drink - solution: bucket - - type: ContainerContainer - containers: - storagebase: !type:Container - ents: [] - mop_slot: !type:ContainerSlot {} - trashbag_slot: !type:ContainerSlot {} - bucket_slot: !type:ContainerSlot {} - plunger_slot: !type:ContainerSlot {} - wetfloorsign_slot4: !type:ContainerSlot {} - wetfloorsign_slot3: !type:ContainerSlot {} - wetfloorsign_slot2: !type:ContainerSlot {} - wetfloorsign_slot1: !type:ContainerSlot {} - lightreplacer_slot: !type:ContainerSlot {} - spraybottle_slot: !type:ContainerSlot {} - - type: GuideHelp - guides: - - Janitorial - - type: TileFrictionModifier - modifier: 0.4 # makes it slide - -- type: entity - id: FloorDrain - name: drain - description: Drains puddles around it. Useful for dumping mop buckets or keeping certain rooms clean. - placement: - mode: SnapgridCenter - components: - - type: Sprite - drawdepth: FloorObjects - sprite: Objects/Specific/Janitorial/drain.rsi - layers: - - state: icon - - map: [ "enum.SolutionContainerLayers.Fill" ] - state: fill-1 - visible: false - - type: InteractionOutline - - type: Clickable - - type: Transform - anchored: true - - type: Physics - bodyType: Static - canCollide: false - - type: AmbientSound - enabled: false - volume: -8 - range: 8 - sound: - path: /Audio/Ambience/Objects/drain.ogg - - type: Drain - - type: DumpableSolution - solution: drainBuffer - - type: Appearance - - type: SolutionContainerVisuals - maxFillLevels: 1 - fillBaseName: fill- - solutionName: drainBuffer - - type: SolutionContainerManager - solutions: - drainBuffer: - maxVol: 1000 - - type: DrainableSolution - solution: drainBuffer - - type: Damageable - damageContainer: Inorganic - damageModifierSet: Metallic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - type: entity name: plunger id: Plunger diff --git a/Resources/Prototypes/Entities/Objects/Devices/nuke.yml b/Resources/Prototypes/Entities/Structures/Machines/nuke.yml similarity index 95% rename from Resources/Prototypes/Entities/Objects/Devices/nuke.yml rename to Resources/Prototypes/Entities/Structures/Machines/nuke.yml index 811210d429e..f37c42e4745 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/nuke.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/nuke.yml @@ -1,5 +1,5 @@ - type: entity - parent: BaseStructure + parent: [BaseStructure, StructureWheeled] id: NuclearBomb name: nuclear fission explosive description: You probably shouldn't stick around to see if this is armed. @@ -49,7 +49,7 @@ shape: !type:PhysShapeCircle radius: 0.45 - density: 80 #It has wheels and bluespace tech to make it lighter. + density: 255 # Has "bluespace technology" to make it lighter, whatever that means. Don't mind the fact that this is lighter then a high capacity fuel tank. mask: - MachineMask layer: diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/portable.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/portable.yml index 0e2a5f6fe5d..316c403d7aa 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] + parent: [BaseMachinePowered, ConstructibleMachine, StructureWheeled] name: portable scrubber description: It scrubs, portably! components: diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml index b606c01f1dd..0c8afc87a40 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml @@ -6,7 +6,7 @@ - type: entity abstract: true id: PortableGeneratorBase - parent: [ BaseMachine, ConstructibleMachine ] + parent: [ BaseMachine, ConstructibleMachine, StructureWheeled] components: # Basic properties - type: Transform @@ -27,8 +27,8 @@ shape: !type:PhysShapeAabb bounds: "-0.40,-0.40,0.40,0.40" - # It has wheels - density: 45 + # Despite the heavy weight, it has wheels, so it's still fairly portable. + density: 155 mask: - MachineMask layer: @@ -259,8 +259,7 @@ shape: !type:PhysShapeAabb bounds: "-0.30,-0.30,0.30,0.30" - # It has wheels - density: 30 + density: 80 mask: - MachineMask layer: @@ -357,4 +356,4 @@ damage: 75 behaviors: - !type:SolutionExplosionBehavior - solution: tank + solution: tank \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Specific/Janitor/drain.yml b/Resources/Prototypes/Entities/Structures/Specific/Janitor/drain.yml new file mode 100644 index 00000000000..e0247001f20 --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Specific/Janitor/drain.yml @@ -0,0 +1,62 @@ +- type: entity + id: FloorDrain + name: drain + description: Drains puddles around it. Useful for dumping mop buckets or keeping certain rooms clean. + placement: + mode: SnapgridCenter + components: + - type: Sprite + drawdepth: FloorObjects + sprite: Objects/Specific/Janitorial/drain.rsi + layers: + - state: icon + - map: [ "enum.SolutionContainerLayers.Fill" ] + state: fill-1 + visible: false + - type: InteractionOutline + - type: Clickable + - type: Transform + anchored: true + - type: Physics + bodyType: Static + canCollide: false + - type: AmbientSound + enabled: false + volume: -8 + range: 8 + sound: + path: /Audio/Ambience/Objects/drain.ogg + - type: Drain + - type: DumpableSolution + solution: drainBuffer + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 1 + fillBaseName: fill- + solutionName: drainBuffer + - type: SolutionContainerManager + solutions: + drainBuffer: + maxVol: 1000 + - type: DrainableSolution + solution: drainBuffer + - type: Damageable + damageContainer: Inorganic + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak diff --git a/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml b/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml new file mode 100644 index 00000000000..bdbff1b504f --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml @@ -0,0 +1,324 @@ +# Mop Bucket +- type: entity + name: mop bucket + id: MopBucket + parent: [BaseStructureDynamic, StructureWheeled] + description: Holds water and the tears of the janitor. + components: + - type: Clickable + - type: Sprite + sprite: Objects/Specific/Janitorial/janitorial.rsi + noRot: true + layers: + - state: mopbucket + - state: mopbucket_water-1 + map: ["enum.SolutionContainerLayers.Fill"] + visible: false + drawdepth: Objects + - type: InteractionOutline + - type: SolutionContainerManager + solutions: + bucket: + maxVol: 600 + - type: Spillable + solution: bucket + spillDelay: 3.0 + - type: DrainableSolution + solution: bucket + - type: RefillableSolution + solution: bucket + - type: ExaminableSolution + solution: bucket + - type: Tag + tags: + - Wringer + - type: ItemMapper + mapLayers: + mopbucket_shark_blue: + whitelist: + tags: + - PlushieSharkBlue + mopbucket_shark_pink: + whitelist: + tags: + - PlushieSharkPink + mopbucket_shark_grey: + whitelist: + tags: + - PlushieSharkGrey + sprite: Objects/Fun/sharkplush.rsi + - type: Transform + noRot: true + - type: ItemSlots + slots: + shark_slot: + name: mop-bucket-slot-component-slot-name-shark + whitelist: + tags: + - PlushieSharkBlue + - PlushieSharkPink + - PlushieSharkGrey + priority: 3 # Higher than drinking priority + - type: Drink + solution: bucket + - type: SolutionContainerVisuals + maxFillLevels: 3 + fillBaseName: mopbucket_water- + - type: ContainerContainer + containers: + storagebase: !type:Container + ents: [] + shark_slot: !type:ContainerSlot {} + - type: GuideHelp + guides: + - Janitorial + - type: Damageable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 70 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + +- type: entity + name: mop bucket + id: MopBucketFull + parent: MopBucket + suffix: full + components: + - type: Sprite + layers: + - state: mopbucket + - state: mopbucket_water-3 + map: [ "enum.SolutionContainerLayers.Fill" ] + - type: SolutionContainerManager + solutions: + bucket: + maxVol: 600 + reagents: + - ReagentId: Water + Quantity: 600 + +# Janicart +- type: entity + name: janitorial trolley + id: JanitorialTrolley + parent: [BaseStructureDynamic, StructureWheeled] + description: This is the alpha and omega of sanitation. + components: + - type: Sprite + noRot: true + sprite: Objects/Specific/Janitorial/janitorial_cart.rsi + layers: + - state: cart + - state: cart_water-1 + map: ["enum.SolutionContainerLayers.Fill"] + visible: false + - type: Rotatable + - type: InteractionOutline + # Removing storage until OnInteractUsing logic resolved + #- type: Storage + # popup: false + # capacity: 80 + # blacklist: # there is exclusive item slots for that + # tags: + # - Mop + # - TrashBag + # - Bucket + - type: ItemSlots + slots: + mop_slot: + name: janitorial-trolley-slot-component-slot-name-mop + whitelist: + tags: + - Mop + insertOnInteract: false # or it conflicts with bucket logic + priority: 9 # Higher than bucket slot + plunger_slot: + name: janitorial-trolley-slot-component-slot-name-plunger + whitelist: + tags: + - Plunger + priority: 8 + wetfloorsign_slot4: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot3: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot2: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot1: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + lightreplacer_slot: + name: janitorial-trolley-slot-component-slot-name-lightreplacer + whitelist: + components: + - LightReplacer + priority: 6 + spraybottle_slot: + name: janitorial-trolley-slot-component-slot-name-spray + whitelist: + tags: + - Spray + insertOnInteract: false # or it conflicts with bucket logic + priority: 5 # Higher than bucket slot + bucket_slot: + name: janitorial-trolley-slot-component-slot-name-bucket + whitelist: + tags: + - Bucket + insertOnInteract: false # or it also conflicts with bucket logic + priority: 4 # Higher than trash bag slot + trashbag_slot: + name: janitorial-trolley-slot-component-slot-name-trashbag + whitelist: + tags: + - TrashBag + priority: 3 # Higher than drinking priority + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.3 + density: 250 + layer: + - MobLayer + mask: + - MobMask + - type: Spillable + solution: bucket + spillDelay: 3.0 + - type: SolutionContainerManager + solutions: + bucket: + maxVol: 800 + reagents: + - ReagentId: Water + Quantity: 600 # 3 quarters full at roundstart to make it more appealing + - type: DrainableSolution + solution: bucket + - type: RefillableSolution + solution: bucket + - type: ExaminableSolution + solution: bucket + - type: Tag + tags: + - Wringer + - type: Damageable + damageContainer: Inorganic + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 400 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - type: ItemMapper + mapLayers: + cart_plunger: + whitelist: + tags: + - Plunger + cart_mop: + whitelist: + tags: + - MopBasic + cart_advmop: + whitelist: + tags: + - MopAdv + cart_garbage: + whitelist: + tags: + - TrashBag + cart_replacer: + whitelist: + components: + - LightReplacer + cart_spray: + whitelist: + tags: + - Spray + cart_sign1: # this is like stack of floor signs + minCount: 1 + whitelist: + tags: + - WetFloorSign + cart_sign2: + minCount: 2 + whitelist: + tags: + - WetFloorSign + cart_sign3: + minCount: 3 + whitelist: + tags: + - WetFloorSign + cart_sign4: + minCount: 4 + whitelist: + tags: + - WetFloorSign + cart_bucket: + whitelist: + tags: + - Bucket + sprite: Objects/Specific/Janitorial/janitorial_cart.rsi + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 3 + fillBaseName: cart_water- + - type: UserInterface + interfaces: + - key: enum.StorageUiKey.Key + type: StorageBoundUserInterface + - type: Drink + solution: bucket + - type: ContainerContainer + containers: + storagebase: !type:Container + ents: [] + mop_slot: !type:ContainerSlot {} + trashbag_slot: !type:ContainerSlot {} + bucket_slot: !type:ContainerSlot {} + plunger_slot: !type:ContainerSlot {} + wetfloorsign_slot4: !type:ContainerSlot {} + wetfloorsign_slot3: !type:ContainerSlot {} + wetfloorsign_slot2: !type:ContainerSlot {} + wetfloorsign_slot1: !type:ContainerSlot {} + lightreplacer_slot: !type:ContainerSlot {} + spraybottle_slot: !type:ContainerSlot {} + - type: GuideHelp + guides: + - Janitorial diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml index 24bcf7cf7a6..e55612d2c49 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml @@ -563,7 +563,7 @@ sprite: Structures/Storage/Crates/syndicate.rsi - type: entity - parent: CrateBaseWeldable + parent: [StructureWheeled, CrateBaseWeldable] id: CrateTrashCart name: trash cart components: diff --git a/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml b/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml index 238efaf68d0..7b56e6d36b5 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml @@ -65,3 +65,21 @@ - type: ReagentTank - type: Transform noRot: true + +# For highcap tanks +- type: entity + id: StorageTankBig + parent: StorageTank + abstract: true + components: + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.4,-0.4,0.4,0.4" + density: 455 #very heavy, they store 10k units of reagents after all. + mask: + - MachineMask + layer: + - WallLayer \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml b/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml index a644c7afa14..e177cc72b16 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml @@ -2,7 +2,7 @@ - type: entity id: WeldingFuelTank - parent: StorageTank + parent: [StorageTank, StructureWheeled] name: fuel tank description: A fuel tank. It's used to store high amounts of fuel. suffix: Empty @@ -48,7 +48,7 @@ Quantity: 1500 - type: entity - parent: WeldingFuelTank + parent: [StorageTankBig, WeldingFuelTank] # StorageTankBig must come first, or else the desnity won't get inherited. id: WeldingFuelTankHighCapacity name: high-capacity fuel tank description: A highly pressurized fuel tank made to hold gargantuan amounts of welding fuel. @@ -81,7 +81,7 @@ - type: entity id: WaterTank - parent: StorageTank + parent: [StorageTank, StructureWheeled] name: water tank description: A water tank. It's used to store high amounts of water. suffix: Empty @@ -115,7 +115,7 @@ Quantity: 1500 - type: entity - parent: WaterTankFull + parent: StorageTank id: WaterCooler name: water cooler description: Seems like a good place to stand and waste time. It has a stock of paper cups on the side. @@ -157,14 +157,20 @@ - FitsInDispenser tags: - Trash + - type: ExaminableSolution + solution: tank + - type: StaticPrice + price: 500 - type: entity - parent: StorageTank + parent: [StorageTankBig, WaterTank] id: WaterTankHighCapacity name: high-capacity water tank description: A highly pressurized water tank made to hold gargantuan amounts of water. suffix: Full components: + - type: StaticPrice + price: 2500 - type: Sprite sprite: Structures/Storage/tanks.rsi layers: @@ -192,7 +198,7 @@ - type: entity id: GenericTank - parent: StorageTank + parent: [StorageTank, StructureWheeled] suffix: Empty components: - type: StaticPrice diff --git a/Resources/Prototypes/Entities/Structures/base_structure.yml b/Resources/Prototypes/Entities/Structures/base_structure.yml index b5356674aaa..71971a66243 100644 --- a/Resources/Prototypes/Entities/Structures/base_structure.yml +++ b/Resources/Prototypes/Entities/Structures/base_structure.yml @@ -56,5 +56,13 @@ - LowImpassable - type: Anchorable +# For use with yaml composition, so that all wheeled structures can easily be changed. +- type: entity + id: StructureWheeled + abstract: true + components: + - type: TileFrictionModifier + modifier: 0.4 + - type: Tag id: Structure From d8d4feec3813c2667dd33e084da916d394977527 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 03:25:06 +0000 Subject: [PATCH 099/133] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 82536b2ad28..fe4288712a6 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Tin-Man-Tim - changes: - - message: Attempting to microwave metal now can cause the microwave to spark and - explode. - type: Tweak - id: 5779 - time: '2024-01-23T22:59:10.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/23887 - author: DrMelon changes: - message: The Microwave works as intended after power outages again. @@ -3802,3 +3794,13 @@ id: 6278 time: '2024-04-01T02:13:52.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26632 +- author: Flareguy + changes: + - message: All wheeled objects now have lower friction. Portable items, like fuel + canisters & portable scrubbers, should now be easier to carry around. + type: Tweak + - message: High-capacity reagent tanks are now much heavier. + type: Tweak + id: 6279 + time: '2024-04-01T03:23:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26601 From d0d12760a88f21368a19e9b523686b304f331a81 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Sun, 31 Mar 2024 23:39:34 -0400 Subject: [PATCH 100/133] Combine solution injection systems; Fix embeddable injectors (#26268) * Combine injection systems * Update Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../BaseSolutionInjectOnEventComponent.cs | 60 +++++++ .../MeleeChemicalInjectorComponent.cs | 39 +---- .../SolutionInjectOnCollideComponent.cs | 28 ---- .../SolutionInjectOnEmbedComponent.cs | 8 + .../SolutionInjectOnProjectileHitComponent.cs | 8 + .../SolutionInjectOnCollideSystem.cs | 49 ------ .../SolutionInjectOnEventSystem.cs | 147 ++++++++++++++++++ .../Weapons/Melee/MeleeWeaponSystem.cs | 68 +------- .../Prototypes/Entities/Objects/Fun/darts.yml | 9 +- .../Guns/Ammunition/Projectiles/shotgun.yml | 8 +- .../Weapons/Guns/Projectiles/arrows.yml | 4 +- .../Entities/Objects/Weapons/Melee/spear.yml | 5 +- 12 files changed, 250 insertions(+), 183 deletions(-) create mode 100644 Content.Server/Chemistry/Components/BaseSolutionInjectOnEventComponent.cs delete mode 100644 Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs create mode 100644 Content.Server/Chemistry/Components/SolutionInjectOnEmbedComponent.cs create mode 100644 Content.Server/Chemistry/Components/SolutionInjectOnProjectileHitComponent.cs delete mode 100644 Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs create mode 100644 Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs diff --git a/Content.Server/Chemistry/Components/BaseSolutionInjectOnEventComponent.cs b/Content.Server/Chemistry/Components/BaseSolutionInjectOnEventComponent.cs new file mode 100644 index 00000000000..708c1ef0052 --- /dev/null +++ b/Content.Server/Chemistry/Components/BaseSolutionInjectOnEventComponent.cs @@ -0,0 +1,60 @@ +using Content.Shared.FixedPoint; +using Content.Shared.Inventory; + +namespace Content.Server.Chemistry.Components; + +///

+/// Base class for components that inject a solution into a target's bloodstream in response to an event. +/// +public abstract partial class BaseSolutionInjectOnEventComponent : Component +{ + /// + /// How much solution to remove from this entity per target when transferring. + /// + /// + /// Note that this amount is per target, so the total amount removed will be + /// multiplied by the number of targets hit. + /// + [DataField] + public FixedPoint2 TransferAmount = FixedPoint2.New(1); + + [ViewVariables(VVAccess.ReadWrite)] + public float TransferEfficiency { get => _transferEfficiency; set => _transferEfficiency = Math.Clamp(value, 0, 1); } + + /// + /// Proportion of the that will actually be injected + /// into the target's bloodstream. The rest is lost. + /// 0 means none of the transferred solution will enter the bloodstream. + /// 1 means the entire amount will enter the bloodstream. + /// + [DataField("transferEfficiency")] + private float _transferEfficiency = 1f; + + /// + /// Solution to inject from. + /// + [DataField] + public string Solution = "default"; + + /// + /// Whether this will inject through hardsuits or not. + /// + [DataField] + public bool PierceArmor = true; + + /// + /// Contents of popup message to display to the attacker when injection + /// fails due to the target wearing a hardsuit. + /// + /// + /// Passed values: $weapon and $target + /// + [DataField] + public LocId BlockedByHardsuitPopupMessage = "melee-inject-failed-hardsuit"; + + /// + /// If anything covers any of these slots then the injection fails. + /// + [DataField] + public SlotFlags BlockSlots = SlotFlags.NONE; +} diff --git a/Content.Server/Chemistry/Components/MeleeChemicalInjectorComponent.cs b/Content.Server/Chemistry/Components/MeleeChemicalInjectorComponent.cs index 6b6ce830a96..6b64b82f787 100644 --- a/Content.Server/Chemistry/Components/MeleeChemicalInjectorComponent.cs +++ b/Content.Server/Chemistry/Components/MeleeChemicalInjectorComponent.cs @@ -1,31 +1,8 @@ -using Content.Shared.FixedPoint; - -namespace Content.Server.Chemistry.Components -{ - [RegisterComponent] - public sealed partial class MeleeChemicalInjectorComponent : Component - { - [ViewVariables(VVAccess.ReadWrite)] - [DataField("transferAmount")] - public FixedPoint2 TransferAmount { get; set; } = FixedPoint2.New(1); - - [ViewVariables(VVAccess.ReadWrite)] - public float TransferEfficiency { get => _transferEfficiency; set => _transferEfficiency = Math.Clamp(value, 0, 1); } - - [DataField("transferEfficiency")] - private float _transferEfficiency = 1f; - - /// - /// Whether this will inject through hardsuits or not. - /// - [DataField("pierceArmor"), ViewVariables(VVAccess.ReadWrite)] - public bool PierceArmor = true; - - /// - /// Solution to inject from. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("solution")] - public string Solution { get; set; } = "default"; - } -} +namespace Content.Server.Chemistry.Components; + +/// +/// Used for melee weapon entities that should try to inject a +/// contained solution into a target when used to hit it. +/// +[RegisterComponent] +public sealed partial class MeleeChemicalInjectorComponent : BaseSolutionInjectOnEventComponent { } diff --git a/Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs b/Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs deleted file mode 100644 index 76bb5294bce..00000000000 --- a/Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Content.Shared.FixedPoint; -using Content.Shared.Inventory; -using Content.Shared.Projectiles; - -namespace Content.Server.Chemistry.Components; - -/// -/// On colliding with an entity that has a bloodstream will dump its solution onto them. -/// -[RegisterComponent] -public sealed partial class SolutionInjectOnCollideComponent : Component -{ - [ViewVariables(VVAccess.ReadWrite)] - [DataField("transferAmount")] - public FixedPoint2 TransferAmount = FixedPoint2.New(1); - - [ViewVariables(VVAccess.ReadWrite)] - public float TransferEfficiency { get => _transferEfficiency; set => _transferEfficiency = Math.Clamp(value, 0, 1); } - - [DataField("transferEfficiency")] - private float _transferEfficiency = 1f; - - /// - /// If anything covers any of these slots then the injection fails. - /// - [DataField("blockSlots"), ViewVariables(VVAccess.ReadWrite)] - public SlotFlags BlockSlots = SlotFlags.MASK; -} diff --git a/Content.Server/Chemistry/Components/SolutionInjectOnEmbedComponent.cs b/Content.Server/Chemistry/Components/SolutionInjectOnEmbedComponent.cs new file mode 100644 index 00000000000..241da38045c --- /dev/null +++ b/Content.Server/Chemistry/Components/SolutionInjectOnEmbedComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.Chemistry.Components; + +/// +/// Used for embeddable entities that should try to inject a +/// contained solution into a target when they become embedded in it. +/// +[RegisterComponent] +public sealed partial class SolutionInjectOnEmbedComponent : BaseSolutionInjectOnEventComponent { } diff --git a/Content.Server/Chemistry/Components/SolutionInjectOnProjectileHitComponent.cs b/Content.Server/Chemistry/Components/SolutionInjectOnProjectileHitComponent.cs new file mode 100644 index 00000000000..395a075298b --- /dev/null +++ b/Content.Server/Chemistry/Components/SolutionInjectOnProjectileHitComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.Chemistry.Components; + +/// +/// Used for projectile entities that should try to inject a +/// contained solution into a target when they hit it. +/// +[RegisterComponent] +public sealed partial class SolutionInjectOnProjectileHitComponent : BaseSolutionInjectOnEventComponent { } diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs deleted file mode 100644 index fb84aca3e41..00000000000 --- a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Content.Server.Body.Components; -using Content.Server.Body.Systems; -using Content.Server.Chemistry.Components; -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Shared.Inventory; -using Content.Shared.Projectiles; - -namespace Content.Server.Chemistry.EntitySystems; - -public sealed class SolutionInjectOnCollideSystem : EntitySystem -{ - [Dependency] private readonly SolutionContainerSystem _solutionContainersSystem = default!; - [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; - [Dependency] private readonly InventorySystem _inventorySystem = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(HandleInjection); - } - - private void HandleInjection(Entity ent, ref ProjectileHitEvent args) - { - var component = ent.Comp; - var target = args.Target; - - if (!TryComp(target, out var bloodstream) || - !_solutionContainersSystem.TryGetInjectableSolution(ent.Owner, out var solution, out _)) - { - return; - } - - if (component.BlockSlots != 0x0) - { - var containerEnumerator = _inventorySystem.GetSlotEnumerator(target, component.BlockSlots); - - // TODO add a helper method for this? - if (containerEnumerator.MoveNext(out _)) - return; - } - - var solRemoved = _solutionContainersSystem.SplitSolution(solution.Value, component.TransferAmount); - var solRemovedVol = solRemoved.Volume; - - var solToInject = solRemoved.SplitSolution(solRemovedVol * component.TransferEfficiency); - - _bloodstreamSystem.TryAddToChemicals(target, solToInject, bloodstream); - } -} diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs new file mode 100644 index 00000000000..8ba36e3a293 --- /dev/null +++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs @@ -0,0 +1,147 @@ +using Content.Server.Body.Components; +using Content.Server.Body.Systems; +using Content.Server.Chemistry.Components; +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Shared.Inventory; +using Content.Shared.Popups; +using Content.Shared.Projectiles; +using Content.Shared.Tag; +using Content.Shared.Weapons.Melee.Events; + +namespace Content.Server.Chemistry.EntitySystems; + +/// +/// System for handling the different inheritors of . +/// Subscribes to relevent events and performs solution injections when they are raised. +/// +public sealed class SolutionInjectOnCollideSystem : EntitySystem +{ + [Dependency] private readonly BloodstreamSystem _bloodstream = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly TagSystem _tag = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(HandleProjectileHit); + SubscribeLocalEvent(HandleEmbed); + SubscribeLocalEvent(HandleMeleeHit); + } + + private void HandleProjectileHit(Entity entity, ref ProjectileHitEvent args) + { + DoInjection((entity.Owner, entity.Comp), args.Target, args.Shooter); + } + + private void HandleEmbed(Entity entity, ref EmbedEvent args) + { + DoInjection((entity.Owner, entity.Comp), args.Embedded, args.Shooter); + } + + private void HandleMeleeHit(Entity entity, ref MeleeHitEvent args) + { + // MeleeHitEvent is weird, so we have to filter to make sure we actually + // hit something and aren't just examining the weapon. + if (args.IsHit) + TryInjectTargets((entity.Owner, entity.Comp), args.HitEntities, args.User); + } + + private void DoInjection(Entity injectorEntity, EntityUid target, EntityUid? source = null) + { + TryInjectTargets(injectorEntity, [target], source); + } + + /// + /// Filters for valid targets and tries to inject a portion of into + /// each valid target's bloodstream. + /// + /// + /// Targets are invalid if any of the following are true: + /// + /// The target does not have a bloodstream. + /// is false and the target is wearing a hardsuit. + /// is not NONE and the target has an item equipped in any of the specified slots. + /// + /// + /// true if at least one target was successfully injected, otherwise false + private bool TryInjectTargets(Entity injector, IReadOnlyList targets, EntityUid? source = null) + { + // Make sure we have at least one target + if (targets.Count == 0) + return false; + + // Get the solution to inject + if (!_solutionContainer.TryGetSolution(injector.Owner, injector.Comp.Solution, out var injectorSolution)) + return false; + + // Build a list of bloodstreams to inject into + var targetBloodstreams = new ValueList>(); + foreach (var target in targets) + { + if (Deleted(target)) + continue; + + // Yuck, this is way to hardcodey for my tastes + // TODO blocking injection with a hardsuit should probably done with a cancellable event or something + if (!injector.Comp.PierceArmor && _inventory.TryGetSlotEntity(target, "outerClothing", out var suit) && _tag.HasTag(suit.Value, "Hardsuit")) + { + // Only show popup to attacker + if (source != null) + _popup.PopupEntity(Loc.GetString(injector.Comp.BlockedByHardsuitPopupMessage, ("weapon", injector.Owner), ("target", target)), target, source.Value, PopupType.SmallCaution); + + continue; + } + + // Check if the target has anything equipped in a slot that would block injection + if (injector.Comp.BlockSlots != SlotFlags.NONE) + { + var blocked = false; + var containerEnumerator = _inventory.GetSlotEnumerator(target, injector.Comp.BlockSlots); + while (containerEnumerator.MoveNext(out var container)) + { + if (container.ContainedEntity != null) + { + blocked = true; + break; + } + } + if (blocked) + continue; + } + + // Make sure the target has a bloodstream + if (!TryComp(target, out var bloodstream)) + continue; + + + // Checks passed; add this target's bloodstream to the list + targetBloodstreams.Add((target, bloodstream)); + } + + // Make sure we got at least one bloodstream + if (targetBloodstreams.Count == 0) + return false; + + // Extract total needed solution from the injector + var removedSolution = _solutionContainer.SplitSolution(injectorSolution.Value, injector.Comp.TransferAmount * targetBloodstreams.Count); + // Adjust solution amount based on transfer efficiency + var solutionToInject = removedSolution.SplitSolution(removedSolution.Volume * injector.Comp.TransferEfficiency); + // Calculate how much of the adjusted solution each target will get + var volumePerBloodstream = solutionToInject.Volume * (1f / targetBloodstreams.Count); + + var anySuccess = false; + foreach (var targetBloodstream in targetBloodstreams) + { + // Take our portion of the adjusted solution for this target + var individualInjection = solutionToInject.SplitSolution(volumePerBloodstream); + // Inject our portion into the target's bloodstream + if (_bloodstream.TryAddToChemicals(targetBloodstream.Owner, individualInjection, targetBloodstream.Comp)) + anySuccess = true; + } + + // Huzzah! + return anySuccess; + } +} diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index ef4b1614770..2612e99ec9a 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -1,8 +1,4 @@ -using Content.Server.Body.Components; -using Content.Server.Body.Systems; using Content.Server.Chat.Systems; -using Content.Server.Chemistry.Components; -using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.CombatMode.Disarm; using Content.Server.Movement.Systems; using Content.Shared.Actions.Events; @@ -14,12 +10,10 @@ using Content.Shared.Effects; using Content.Shared.Hands.Components; using Content.Shared.IdentityManagement; -using Content.Shared.Inventory; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; using Content.Shared.Speech.Components; using Content.Shared.StatusEffect; -using Content.Shared.Tag; using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Audio; @@ -34,22 +28,17 @@ namespace Content.Server.Weapons.Melee; public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem { - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly BloodstreamSystem _bloodstream = default!; - [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly DamageExamineSystem _damageExamine = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly LagCompensationSystem _lag = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; - [Dependency] private readonly SolutionContainerSystem _solutions = default!; - [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly DamageExamineSystem _damageExamine = default!; + [Dependency] private readonly LagCompensationSystem _lag = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnChemicalInjectorHit); SubscribeLocalEvent(OnSpeechHit); SubscribeLocalEvent(OnMeleeExamineDamage); } @@ -263,47 +252,4 @@ private void OnSpeechHit(EntityUid owner, MeleeSpeechComponent comp, MeleeHitEve } } - - private void OnChemicalInjectorHit(Entity entity, ref MeleeHitEvent args) - { - if (!args.IsHit || - !args.HitEntities.Any() || - !_solutions.TryGetSolution(entity.Owner, entity.Comp.Solution, out var solutionContainer)) - { - return; - } - - var hitBloodstreams = new List<(EntityUid Entity, BloodstreamComponent Component)>(); - var bloodQuery = GetEntityQuery(); - - foreach (var hit in args.HitEntities) - { - if (Deleted(hit)) - continue; - - // prevent deathnettles injecting through hardsuits - if (!entity.Comp.PierceArmor && _inventory.TryGetSlotEntity(hit, "outerClothing", out var suit) && _tag.HasTag(suit.Value, "Hardsuit")) - { - PopupSystem.PopupEntity(Loc.GetString("melee-inject-failed-hardsuit", ("weapon", entity.Owner)), args.User, args.User, PopupType.SmallCaution); - continue; - } - - if (bloodQuery.TryGetComponent(hit, out var bloodstream)) - hitBloodstreams.Add((hit, bloodstream)); - } - - if (!hitBloodstreams.Any()) - return; - - var removedSolution = _solutions.SplitSolution(solutionContainer.Value, entity.Comp.TransferAmount * hitBloodstreams.Count); - var removedVol = removedSolution.Volume; - var solutionToInject = removedSolution.SplitSolution(removedVol * entity.Comp.TransferEfficiency); - var volPerBloodstream = solutionToInject.Volume * (1 / hitBloodstreams.Count); - - foreach (var (ent, bloodstream) in hitBloodstreams) - { - var individualInjection = solutionToInject.SplitSolution(volPerBloodstream); - _bloodstream.TryAddToChemicals(ent, individualInjection, bloodstream); - } - } } diff --git a/Resources/Prototypes/Entities/Objects/Fun/darts.yml b/Resources/Prototypes/Entities/Objects/Fun/darts.yml index 391823dc52f..4c7ae68420b 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/darts.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/darts.yml @@ -53,10 +53,10 @@ solution: melee - type: InjectableSolution solution: melee - - type: SolutionInjectOnCollide + - type: SolutionInjectOnEmbed transferAmount: 2 + solution: melee blockSlots: OUTERCLOTHING - fixtureId: "throw-fixture" - type: SolutionTransfer maxTransferAmount: 2 - type: Damageable @@ -124,10 +124,9 @@ solutions: melee: maxVol: 7 - - type: SolutionInjectOnCollide + - type: SolutionInjectOnEmbed transferAmount: 7 - blockSlots: NONE - fixtureId: "throw-fixture" + solution: melee - type: SolutionTransfer maxTransferAmount: 7 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml index 757b8934d43..e119a846c9c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml @@ -86,8 +86,8 @@ damage: types: Piercing: 3 - Slash: 3 - + Slash: 3 + - type: entity id: PelletShotgunTranquilizer @@ -110,9 +110,9 @@ solution: ammo - type: DrainableSolution solution: ammo - - type: SolutionInjectOnCollide + - type: SolutionInjectOnProjectileHit transferAmount: 15 - blockSlots: NONE #tranquillizer darts shouldn't be blocked by a mask + solution: ammo - type: InjectableSolution solution: ammo diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml index 8dbcf2b3033..52c5dc8a9db 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml @@ -50,9 +50,9 @@ solution: ammo - type: InjectableSolution solution: ammo - - type: SolutionInjectOnCollide + - type: SolutionInjectOnEmbed transferAmount: 2 - blockSlots: NONE + solution: ammo - type: SolutionTransfer maxTransferAmount: 2 - type: Appearance diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml index 279fed80433..3758487bd43 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml @@ -65,10 +65,9 @@ solution: melee - type: InjectableSolution solution: melee - - type: SolutionInjectOnCollide + - type: SolutionInjectOnEmbed transferAmount: 2 - fixtureId: "throw-fixture" - blockSlots: NONE + solution: melee - type: SolutionTransfer maxTransferAmount: 2 - type: Wieldable From 246155bd3736387fece46bae643e90fd1e77d41d Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 03:40:40 +0000 Subject: [PATCH 101/133] 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 fe4288712a6..87eeba79df3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: DrMelon - changes: - - message: The Microwave works as intended after power outages again. - type: Fix - id: 5780 - time: '2024-01-24T01:14:55.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/23997 - author: Krunk changes: - message: To prevent accidental spills, buckets can no longer be quick-equipped. @@ -3804,3 +3797,12 @@ id: 6279 time: '2024-04-01T03:23:59.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26601 +- author: Tayrtahn + changes: + - message: spears, darts, and hypodarts can inject targets again. + type: Fix + - message: arrows can no longer inject targets they don't actually hit. + type: Fix + id: 6280 + time: '2024-04-01T03:39:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26268 From b7a6fb991f768a693103309867d4128852a56a7a Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Mon, 1 Apr 2024 14:41:57 +1100 Subject: [PATCH 102/133] Add ValueList import (#26640) --- .../Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs index 8ba36e3a293..3c57cc31afd 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Projectiles; using Content.Shared.Tag; using Content.Shared.Weapons.Melee.Events; +using Robust.Shared.Collections; namespace Content.Server.Chemistry.EntitySystems; From 83766b5d20e6585decf0df5e941303c562cfb5e8 Mon Sep 17 00:00:00 2001 From: SoulFN <164462467+SoulFN@users.noreply.github.com> Date: Mon, 1 Apr 2024 12:21:11 +0800 Subject: [PATCH 103/133] Change assault borg modules texture (#26502) * Update borg_modules.yml * Fix borg_modules.yml? * Uh --------- Co-authored-by: metalgearsloth --- .../Specific/Robotics/borg_modules.yml | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index e14f29746dc..cbfec8c9cf6 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -517,13 +517,13 @@ components: - type: Sprite layers: - - state: syndicate - - state: icon-tools + - state: syndicate + - state: icon-syndicate - type: ItemBorgModule items: - - Crowbar - - Emag - - PinpointerSyndicateNuclear + - Crowbar + - Emag + - PinpointerSyndicateNuclear - type: entity id: BorgModuleEsword @@ -533,12 +533,12 @@ components: - type: Sprite layers: - - state: syndicate - - state: icon-tools + - state: syndicate + - state: icon-syndicate - type: ItemBorgModule items: - - EnergySwordDouble - - PinpointerSyndicateNuclear + - EnergySwordDouble + - PinpointerSyndicateNuclear - type: entity id: BorgModuleL6C @@ -548,9 +548,9 @@ components: - type: Sprite layers: - - state: syndicate - - state: icon-tools + - state: syndicate + - state: icon-syndicate - type: ItemBorgModule items: - - WeaponLightMachineGunL6C - - PinpointerSyndicateNuclear + - WeaponLightMachineGunL6C + - PinpointerSyndicateNuclear From 8c5e236de4b0e54299abd79fa41bd99895ae932b Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 04:22:17 +0000 Subject: [PATCH 104/133] 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 87eeba79df3..5428dceb3c4 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Krunk - changes: - - message: To prevent accidental spills, buckets can no longer be quick-equipped. - type: Fix - id: 5781 - time: '2024-01-24T02:35:37.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24472 - author: lzk228 changes: - message: Fixed escaping from locked artifact crate. @@ -3806,3 +3799,10 @@ id: 6280 time: '2024-04-01T03:39:35.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26268 +- author: SoulFN + changes: + - message: Changed textures of the assault borg modules + type: Tweak + id: 6281 + time: '2024-04-01T04:21:12.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26502 From cf7567d073ee6a26786a006dd2049b9643338bff Mon Sep 17 00:00:00 2001 From: Keer-Sar <144283718+Keer-Sar@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:35:21 -0400 Subject: [PATCH 105/133] Add Cyborg Emote Sounds (#26594) * Hal 9000's first emote * Add Chime emote & Change variation to 0.05 * Modify Buzz emote * Add Buzz-two emote * modified Horn * add ping emote * add slowclap emote * Convert slowclap.ogg to mono, reflect change in attribution.yml * fix capitalization for all chatMessages && change all catagory to category * remove all traces of slowclap.ogg * forgor one file smh * collating copywrite * spelling mistakes will be the death of me * more spelling mistakes * change yml string to list --- Resources/Audio/Machines/attributions.yml | 9 ++ Resources/Audio/Machines/buzz-sigh.ogg | Bin 0 -> 8972 bytes Resources/Audio/Machines/buzz-two.ogg | Bin 0 -> 12724 bytes Resources/Audio/Machines/chime.ogg | Bin 0 -> 8240 bytes Resources/Audio/Machines/twobeep.ogg | Bin 0 -> 6851 bytes .../Mobs/Cyborgs/base_borg_chassis.yml | 5 +- .../Prototypes/Voice/speech_emote_sounds.yml | 18 +++ Resources/Prototypes/Voice/speech_emotes.yml | 104 ++++++++++++++++++ 8 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 Resources/Audio/Machines/buzz-sigh.ogg create mode 100644 Resources/Audio/Machines/buzz-two.ogg create mode 100644 Resources/Audio/Machines/chime.ogg create mode 100644 Resources/Audio/Machines/twobeep.ogg diff --git a/Resources/Audio/Machines/attributions.yml b/Resources/Audio/Machines/attributions.yml index a0f1c9f7e7f..38267ebd694 100644 --- a/Resources/Audio/Machines/attributions.yml +++ b/Resources/Audio/Machines/attributions.yml @@ -152,3 +152,12 @@ license: "CC0-1.0" copyright: "dakamakat on freesound.org" source: "https://freesound.org/people/Dakamakat/sounds/717370/" + +- files: + - twobeep.ogg + - chime.ogg + - buzz-sigh.ogg + - buzztwo.ogg + license: "CC-BY-SA-3.0" + copyright: "Taken from TG station." + source: "https://github.com/tgstation/tgstation/tree/d4f678a1772007ff8d7eddd21cf7218c8e07bfc0" diff --git a/Resources/Audio/Machines/buzz-sigh.ogg b/Resources/Audio/Machines/buzz-sigh.ogg new file mode 100644 index 0000000000000000000000000000000000000000..109c196e2c4fef313bdb2a17c4c7b4a59668eda4 GIT binary patch literal 8972 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 z zUk9!0d;3}>o&Dsi4=l7-Z$>{%*Xqx1?h?N5VpE+h}prvd?FuTHoDuR4N(cu|9Xs7dss5)QR458ev;{~B!gnJKs zDHaWUh)(V3YP*J5W~NCVWX{Zkcj25eJI(Rzyp*$Ddu<5y z(jnAV&w|`rKk=!pgIN9rz)-Y?vRfr4ga8+qlr@lFk0X|3v>_#BPY04Pc`#qMl`g~tGHg1Q61%7Zs?`K@RqEr0kKmaC%xrBf~@K*`AMGY?e1!Sp+vt{Q{)ES-M6 z6ux_Mw8;eBh1%i%>gfYTro7?Exc9}DlzGZw9MSnHf)U3HdtZJ!^)%r(trw5JG05{O zEpiy(7P)H-0qM55i|dBN%aOclEt)m4DXZ-|(Z0&7qv@Jk$Np;irOO_QHM*OePrWEZSFZYnbCt&($v3 z0AI}R;O+y^j#hR9uxA5gkX;FRAv> zbDw4iNyCW`wj18Hx6ON59vfBkEstkX=Znb(KBA{aUii1%UlqMnuh@$12jR2MI$-vK z+ut%BG~g58$^-B9U{&l*N0NYxk6o0w{ytL)dH}PTPR!HB%XRmvJHK zDFK0Wbr@YgM-O^VQKQshsGv*pfwr0VFBCDUC%qiLU(|n;tgYQ|F zk?x-}Jw?b`1i9aCg4|L8J>y_eUWl&fUeC43l(v<%A z6Z;;fB3vLKk@~v1iVx#J8#;d)rS8CkcT5sjrEgIta+?k!Q|P{ka2$)FikvtMVozVv zakI?02vDK#YBm}y#{Cg#+dd_}={{%Z#ry?4+-JV_;r8sPrS^)%x*^&F9%rf?I%Tz$eVT z_Zw5e1^m99$Eb$KqWhFm$z>}k4+V0GSM}xIQ(E{PX?&k@80Zw?QjcoG7GnbH;<00S zl@_$8W8Zu`5JxxN`DmN@D@SBShiNy)6eQx%A22Tf2>W)QfF2rj9Ki++62r=1q^i#t zjZYptrw=_Vb(EuF*b0GRwo$BZxz0nE^czp#d{&%)bl}6C$=jO7*BCLk+3Y2k zWKK$?oiS9d^O*W0n{#9Gr?0BaqGMyquGT%dwe9%4bMIqRQ%;PI*JDOUR30teSof-K z&3mKt?Y@NkUgC!_*G|&1->Q9oUKvaZkIXMxbANq72BOgELrQu8%n5rgEW=P4{@Lo? z9W>pu&ykB!3ufs9#u3d%OONZNjz79#Er0VR8)jfeO|u>O?d^$%lg@Jc8^-W@){K#? YpWW{(cLm8@!@I_{S#7+Mw$|`}0FkJ~c>n+a literal 0 HcmV?d00001 diff --git a/Resources/Audio/Machines/twobeep.ogg b/Resources/Audio/Machines/twobeep.ogg new file mode 100644 index 0000000000000000000000000000000000000000..26fc5a95a57bb4a0537355f874665e4a9199c02a GIT binary patch literal 6851 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 literal 0 HcmV?d00001 diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 5c7a4e139fe..f04998388b8 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity parent: BaseMob id: BaseBorgChassis name: cyborg @@ -131,6 +131,9 @@ - type: Speech speechVerb: Robotic speechSounds: Pai + - type: Vocal + sounds: + Unsexed: UnisexSilicon - type: UnblockableSpeech - type: Construction graph: Cyborg diff --git a/Resources/Prototypes/Voice/speech_emote_sounds.yml b/Resources/Prototypes/Voice/speech_emote_sounds.yml index 01fabbadf30..5556afa07d8 100644 --- a/Resources/Prototypes/Voice/speech_emote_sounds.yml +++ b/Resources/Prototypes/Voice/speech_emote_sounds.yml @@ -298,6 +298,24 @@ Weh: collection: Weh +- type: emoteSounds + id: UnisexSilicon + params: + variation: 0.05 + sounds: + Beep: + path: /Audio/Machines/twobeep.ogg + Chime: + path: /Audio/Machines/chime.ogg + Buzz: + path: /Audio/Machines/buzz-sigh.ogg + Buzz-Two: + path: /Audio/Machines/buzz-two.ogg + Honk: + path: /Audio/Items/bikehorn.ogg + Ping: + path: /Audio/Effects/Cargo/ping.ogg + # body emotes - type: emoteSounds id: GeneralBodyEmotes diff --git a/Resources/Prototypes/Voice/speech_emotes.yml b/Resources/Prototypes/Voice/speech_emotes.yml index 8e33f483aa3..3b7ffc01072 100644 --- a/Resources/Prototypes/Voice/speech_emotes.yml +++ b/Resources/Prototypes/Voice/speech_emotes.yml @@ -66,6 +66,19 @@ id: Honk category: Vocal chatMessages: [honks] + chatTriggers: + - honk + - honk. + - honk! + - honks + - honks. + - honks! + - honked + - honked. + - honked! + - honking + - honking. + - honking! - type: emote id: Sigh @@ -264,3 +277,94 @@ - chirping - chirping. - chirping! + +# Machine Emotes +- type: emote + id: Beep + category: Vocal + chatMessages: [beeps.] + chatTriggers: + - beep + - beep! + - beep. + - beeps + - beeps. + - beeps! + - beeped + - beeped. + - beeped! + - beeping + - beeping. + - beeping! + +- type: emote + id: Chime + category: Vocal + chatMessages: [chimes.] + chatTriggers: + - chime + - chime. + - chime! + - chimes + - chimes. + - chimes! + - chimed + - chimed. + - chimed! + - chiming + - chiming, + - chiming! + +- type: emote + id: Buzz-Two + category: Vocal + chatMessages: [buzzesTwice.] + chatTriggers: + - buzztwice + - buzztwice. + - buzztwice! + - buzzstwice + - buzzstwice. + - buzzstwice! + - buzzestwice + - buzzestwice. + - buzzestwice! + - buzzingtwice + - buzzingtwice. + - buzzingtwice! + - buzzedtwice + - buzzedtwice. + - buzzedtwice! + - buzz twice + - buzz twice. + - buzz twice! + - buzzs twice + - buzzs twice. + - buzzs twice! + - buzzes twice + - buzzes twice. + - buzzes twice! + - buzzing twice + - buzzing twice. + - buzzing twice! + - buzzed twice + - buzzed twice. + - buzzed twice! + +- type: emote + id: Ping + category: Vocal + chatMessages: [pings.] + chatTriggers: + - ping + - ping. + - ping! + - pings + - pings. + - pings! + - pinged + - pinged. + - pinged! + - pinging + - pinging. + - pinging! From cc35f16839c290dc79f942d6b18c76a334e74a2c Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 04:36:27 +0000 Subject: [PATCH 106/133] 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 5428dceb3c4..ce167dcff3b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: lzk228 - changes: - - message: Fixed escaping from locked artifact crate. - type: Fix - id: 5782 - time: '2024-01-25T00:46:07.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24442 - author: Krunk changes: - message: Players will once again spawn at arrivals instead of cryostorage. @@ -3806,3 +3799,10 @@ id: 6281 time: '2024-04-01T04:21:12.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26502 +- author: Keer-Sar + changes: + - message: Cyborgs now have audio for some emotes. + type: Add + id: 6282 + time: '2024-04-01T04:35:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26594 From bed9e9ac6a068495ff70f09eb852a9c7a156d632 Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Mon, 1 Apr 2024 06:50:00 +0200 Subject: [PATCH 107/133] Coordinates Disks & Shuttle FTL Travel (#23240) * Adds the CentComm Disk and configures it to work with direct-use shuttles * Added functionality for drone shuttles (i.e. cargo shuttle) * Adds support for pods, and a disk console object for disks to be inserted into. Also sprites. * Added the disk to HoP's locker * Removed leftover logs & comments * Fix for integration test * Apply suggestions from code review (formatting & proper DataField) Co-authored-by: 0x6273 <0x40@keemail.me> * Fix integration test & changes based on code review * Includes Disk Cases to contain Coordinate Disks, which are now CDs instead of Floppy Disks * Check pods & non-evac shuttles for CentCom travel, even in FTL * Import * Remove CentCom travel restrictions & pod disk consoles * Major changes that changes the coordinates disk system to work with salvage expeditions * Missed CC diskcase removal * Fix build * Review suggestions and changes * Major additional changes after merge * Minor tag miss * Integration test fix * review --------- Co-authored-by: 0x6273 <0x40@keemail.me> Co-authored-by: metalgearsloth --- Content.Client/Shuttles/UI/MapScreen.xaml.cs | 12 ++--- .../SalvageSystem.ExpeditionConsole.cs | 13 +++++- .../Salvage/SalvageSystem.Expeditions.cs | 5 ++- Content.Server/Salvage/SalvageSystem.cs | 3 ++ .../Salvage/SpawnSalvageMissionJob.cs | 26 ++++++++--- .../Shuttles/Components/EscapePodComponent.cs | 2 +- .../Components/ShuttleConsoleComponent.cs | 6 +++ .../Systems/EmergencyShuttleSystem.cs | 2 +- .../Systems/ShuttleConsoleSystem.FTL.cs | 6 ++- .../Salvage/Expeditions/SalvageExpeditions.cs | 7 ++- .../Components/FTLDestinationComponent.cs | 6 +++ .../SharedShuttleConsoleComponent.cs | 2 +- .../ShuttleDestinationCoordinatesComponent.cs | 15 +++++++ .../Shuttles/Systems/SharedShuttleSystem.cs | 42 +++++++++++++++--- .../Prototypes/Entities/Objects/Misc/cds.yml | 19 ++++++++ .../Entities/Objects/Misc/diskcases.yml | 21 +++++++++ .../Machines/Computers/computers.yml | 16 +++++++ Resources/Prototypes/tags.yml | 3 ++ .../Textures/Objects/Misc/cd.rsi/icon.png | Bin 0 -> 361 bytes .../Textures/Objects/Misc/cd.rsi/meta.json | 14 ++++++ .../Objects/Misc/diskcases.rsi/icon_base.png | Bin 0 -> 240 bytes .../Objects/Misc/diskcases.rsi/icon_cargo.png | Bin 0 -> 234 bytes .../Objects/Misc/diskcases.rsi/icon_cc.png | Bin 0 -> 239 bytes .../Objects/Misc/diskcases.rsi/meta.json | 20 +++++++++ 24 files changed, 217 insertions(+), 23 deletions(-) create mode 100644 Content.Shared/Shuttles/Components/ShuttleDestinationCoordinatesComponent.cs create mode 100644 Resources/Prototypes/Entities/Objects/Misc/cds.yml create mode 100644 Resources/Prototypes/Entities/Objects/Misc/diskcases.yml create mode 100644 Resources/Textures/Objects/Misc/cd.rsi/icon.png create mode 100644 Resources/Textures/Objects/Misc/cd.rsi/meta.json create mode 100644 Resources/Textures/Objects/Misc/diskcases.rsi/icon_base.png create mode 100644 Resources/Textures/Objects/Misc/diskcases.rsi/icon_cargo.png create mode 100644 Resources/Textures/Objects/Misc/diskcases.rsi/icon_cc.png create mode 100644 Resources/Textures/Objects/Misc/diskcases.rsi/meta.json diff --git a/Content.Client/Shuttles/UI/MapScreen.xaml.cs b/Content.Client/Shuttles/UI/MapScreen.xaml.cs index 225d1be14e5..10800b8c5f7 100644 --- a/Content.Client/Shuttles/UI/MapScreen.xaml.cs +++ b/Content.Client/Shuttles/UI/MapScreen.xaml.cs @@ -263,9 +263,10 @@ private void RebuildMapObjects() while (mapComps.MoveNext(out var mapComp, out var mapXform, out var mapMetadata)) { - if (!_shuttles.CanFTLTo(_shuttleEntity.Value, mapComp.MapId)) - continue; - + if (_console != null && !_shuttles.CanFTLTo(_shuttleEntity.Value, mapComp.MapId, _console.Value)) + { + continue; + } var mapName = mapMetadata.EntityName; if (string.IsNullOrEmpty(mapName)) @@ -310,7 +311,6 @@ private void RebuildMapObjects() }; _mapHeadings.Add(mapComp.MapId, gridContents); - foreach (var grid in _mapManager.GetAllMapGrids(mapComp.MapId)) { _entManager.TryGetComponent(grid.Owner, out IFFComponent? iffComp); @@ -327,8 +327,8 @@ private void RebuildMapObjects() { AddMapObject(mapComp.MapId, gridObj); } - else if (iffComp == null || - (iffComp.Flags & IFFFlags.Hide) == 0x0) + else if (!_shuttles.IsBeaconMap(_mapManager.GetMapEntityId(mapComp.MapId)) && (iffComp == null || + (iffComp.Flags & IFFFlags.Hide) == 0x0)) { _pendingMapObjects.Add((mapComp.MapId, gridObj)); } diff --git a/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs b/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs index f7f3718208e..61636bea7c9 100644 --- a/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs +++ b/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs @@ -1,10 +1,16 @@ +using Content.Shared.Shuttles.Components; using Content.Shared.Procedural; using Content.Shared.Salvage.Expeditions; +using Content.Shared.Dataset; +using Robust.Shared.Prototypes; namespace Content.Server.Salvage; public sealed partial class SalvageSystem { + [ValidatePrototypeId] + public const string CoordinatesDisk = "CoordinatesDisk"; + private void OnSalvageClaimMessage(EntityUid uid, SalvageExpeditionConsoleComponent component, ClaimSalvageMessage args) { var station = _station.GetOwningStation(uid); @@ -15,11 +21,16 @@ private void OnSalvageClaimMessage(EntityUid uid, SalvageExpeditionConsoleCompon if (!data.Missions.TryGetValue(args.Index, out var missionparams)) return; - SpawnMission(missionparams, station.Value); + var cdUid = Spawn(CoordinatesDisk, Transform(uid).Coordinates); + SpawnMission(missionparams, station.Value, cdUid); data.ActiveMission = args.Index; var mission = GetMission(_prototypeManager.Index(missionparams.Difficulty), missionparams.Seed); data.NextOffer = _timing.CurTime + mission.Duration + TimeSpan.FromSeconds(1); + + _labelSystem.Label(cdUid, GetFTLName(_prototypeManager.Index("names_borer"), missionparams.Seed)); + _audio.PlayPvs(component.PrintSound, uid); + UpdateConsoles((station.Value, data)); } diff --git a/Content.Server/Salvage/SalvageSystem.Expeditions.cs b/Content.Server/Salvage/SalvageSystem.Expeditions.cs index 7e4a9c9310e..839730ec873 100644 --- a/Content.Server/Salvage/SalvageSystem.Expeditions.cs +++ b/Content.Server/Salvage/SalvageSystem.Expeditions.cs @@ -8,6 +8,7 @@ using Robust.Shared.CPUJob.JobQueues; using Robust.Shared.CPUJob.JobQueues.Queues; using Robust.Shared.GameStates; +using Robust.Shared.Map; namespace Content.Server.Salvage; @@ -148,7 +149,7 @@ private SalvageExpeditionConsoleState GetState(SalvageExpeditionDataComponent co return new SalvageExpeditionConsoleState(component.NextOffer, component.Claimed, component.Cooldown, component.ActiveMission, missions); } - private void SpawnMission(SalvageMissionParams missionParams, EntityUid station) + private void SpawnMission(SalvageMissionParams missionParams, EntityUid station, EntityUid? coordinatesDisk) { var cancelToken = new CancellationTokenSource(); var job = new SpawnSalvageMissionJob( @@ -162,7 +163,9 @@ private void SpawnMission(SalvageMissionParams missionParams, EntityUid station) _biome, _dungeon, _metaData, + _transform, station, + coordinatesDisk, missionParams, cancelToken.Token); diff --git a/Content.Server/Salvage/SalvageSystem.cs b/Content.Server/Salvage/SalvageSystem.cs index a1a3b686b2f..9af4736345e 100644 --- a/Content.Server/Salvage/SalvageSystem.cs +++ b/Content.Server/Salvage/SalvageSystem.cs @@ -32,6 +32,7 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Map.Components; using Robust.Shared.Timing; +using Content.Server.Labels; namespace Content.Server.Salvage { @@ -39,6 +40,7 @@ public sealed partial class SalvageSystem : SharedSalvageSystem { [Dependency] private readonly IChatManager _chat = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!; + [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; @@ -48,6 +50,7 @@ public sealed partial class SalvageSystem : SharedSalvageSystem [Dependency] private readonly BiomeSystem _biome = default!; [Dependency] private readonly DungeonSystem _dungeon = default!; [Dependency] private readonly GravitySystem _gravity = default!; + [Dependency] private readonly LabelSystem _labelSystem = default!; [Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly RadioSystem _radioSystem = default!; diff --git a/Content.Server/Salvage/SpawnSalvageMissionJob.cs b/Content.Server/Salvage/SpawnSalvageMissionJob.cs index e2b17b58724..180c8d145cf 100644 --- a/Content.Server/Salvage/SpawnSalvageMissionJob.cs +++ b/Content.Server/Salvage/SpawnSalvageMissionJob.cs @@ -33,6 +33,9 @@ using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.Utility; +using Content.Server.Shuttles.Components; +using Content.Shared.Coordinates; +using Content.Shared.Shuttles.Components; namespace Content.Server.Salvage; @@ -46,8 +49,10 @@ public sealed class SpawnSalvageMissionJob : Job private readonly BiomeSystem _biome; private readonly DungeonSystem _dungeon; private readonly MetaDataSystem _metaData; + private readonly SharedTransformSystem _xforms; public readonly EntityUid Station; + public readonly EntityUid? CoordinatesDisk; private readonly SalvageMissionParams _missionParams; private readonly ISawmill _sawmill; @@ -63,7 +68,9 @@ public SpawnSalvageMissionJob( BiomeSystem biome, DungeonSystem dungeon, MetaDataSystem metaData, + SharedTransformSystem xform, EntityUid station, + EntityUid? coordinatesDisk, SalvageMissionParams missionParams, CancellationToken cancellation = default) : base(maxTime, cancellation) { @@ -75,7 +82,9 @@ public SpawnSalvageMissionJob( _biome = biome; _dungeon = dungeon; _metaData = metaData; + _xforms = xform; Station = station; + CoordinatesDisk = coordinatesDisk; _missionParams = missionParams; _sawmill = logManager.GetSawmill("salvage_job"); #if !DEBUG @@ -94,6 +103,18 @@ protected override async Task Process() var random = new Random(_missionParams.Seed); var destComp = _entManager.AddComponent(mapUid); destComp.BeaconsOnly = true; + destComp.RequireCoordinateDisk = true; + destComp.Enabled = true; + _metaData.SetEntityName(mapUid, SharedSalvageSystem.GetFTLName(_prototypeManager.Index("names_borer"), _missionParams.Seed)); + _entManager.AddComponent(mapUid); + + // Saving the mission mapUid to a CD is made optional, in case one is somehow made in a process without a CD entity + if (CoordinatesDisk.HasValue) + { + var cd = _entManager.EnsureComponent(CoordinatesDisk.Value); + cd.Destination = mapUid; + _entManager.Dirty(CoordinatesDisk.Value, cd); + } // Setup mission configs // As we go through the config the rating will deplete so we'll go for most important to least important. @@ -144,11 +165,6 @@ protected override async Task Process() expedition.EndTime = _timing.CurTime + mission.Duration; expedition.MissionParams = _missionParams; - // Don't want consoles to have the incorrect name until refreshed. - var ftlUid = _entManager.CreateEntityUninitialized("FTLPoint", new EntityCoordinates(mapUid, grid.TileSizeHalfVector)); - _metaData.SetEntityName(ftlUid, SharedSalvageSystem.GetFTLName(_prototypeManager.Index("names_borer"), _missionParams.Seed)); - _entManager.InitializeAndStartEntity(ftlUid); - var landingPadRadius = 24; var minDungeonOffset = landingPadRadius + 4; diff --git a/Content.Server/Shuttles/Components/EscapePodComponent.cs b/Content.Server/Shuttles/Components/EscapePodComponent.cs index 72c8024e12b..d717e97441b 100644 --- a/Content.Server/Shuttles/Components/EscapePodComponent.cs +++ b/Content.Server/Shuttles/Components/EscapePodComponent.cs @@ -9,7 +9,7 @@ namespace Content.Server.Shuttles.Components; [RegisterComponent, Access(typeof(EmergencyShuttleSystem)), AutoGenerateComponentPause] public sealed partial class EscapePodComponent : Component { - [DataField("launchTime", customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField(customTypeSerializer:typeof(TimeOffsetSerializer))] [AutoPausedField] public TimeSpan? LaunchTime; } diff --git a/Content.Server/Shuttles/Components/ShuttleConsoleComponent.cs b/Content.Server/Shuttles/Components/ShuttleConsoleComponent.cs index 91cecde0445..03e262cef50 100644 --- a/Content.Server/Shuttles/Components/ShuttleConsoleComponent.cs +++ b/Content.Server/Shuttles/Components/ShuttleConsoleComponent.cs @@ -14,5 +14,11 @@ public sealed partial class ShuttleConsoleComponent : SharedShuttleConsoleCompon ///
[DataField("zoom")] public Vector2 Zoom = new(1.5f, 1.5f); + + /// + /// Should this console have access to restricted FTL destinations? + /// + [ViewVariables(VVAccess.ReadWrite), DataField("whitelistSpecific")] + public List FTLWhitelist = new List(); } } diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index a7f83f2e158..c043861b377 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -418,7 +418,7 @@ private void AddCentcomm(StationCentcommComponent component) } var mapId = _mapManager.CreateMap(); - var grid = _map.LoadGrid(mapId, component.Map.ToString(), new MapLoadOptions() + var grid = _map.LoadGrid(mapId, component.Map.ToString(), new MapLoadOptions() { LoadMap = false, }); diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.FTL.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.FTL.cs index 7606d190a45..c214bb015c9 100644 --- a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.FTL.cs +++ b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.FTL.cs @@ -44,6 +44,10 @@ private void OnBeaconFTLMessage(Entity ent, ref Shuttle } var nCoordinates = new NetCoordinates(GetNetEntity(targetXform.ParentUid), targetXform.LocalPosition); + if (targetXform.ParentUid == EntityUid.Invalid) + { + nCoordinates = new NetCoordinates(GetNetEntity(beaconEnt), targetXform.LocalPosition); + } // Check target exists if (!_shuttle.CanFTLBeacon(nCoordinates)) @@ -128,7 +132,7 @@ private void ConsoleFTL(Entity ent, EntityCoordinates t } // Check shuttle can FTL to this target. - if (!_shuttle.CanFTLTo(shuttleUid.Value, targetMap)) + if (!_shuttle.CanFTLTo(shuttleUid.Value, targetMap, ent)) { return; } diff --git a/Content.Shared/Salvage/Expeditions/SalvageExpeditions.cs b/Content.Shared/Salvage/Expeditions/SalvageExpeditions.cs index fe4d59b81a8..4844a1f3fd3 100644 --- a/Content.Shared/Salvage/Expeditions/SalvageExpeditions.cs +++ b/Content.Shared/Salvage/Expeditions/SalvageExpeditions.cs @@ -1,4 +1,5 @@ using Content.Shared.Salvage.Expeditions.Modifiers; +using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; @@ -30,7 +31,11 @@ public SalvageExpeditionConsoleState(TimeSpan nextOffer, bool claimed, bool cool [RegisterComponent, NetworkedComponent] public sealed partial class SalvageExpeditionConsoleComponent : Component { - + /// + /// The sound made when spawning a coordinates disk + /// + [DataField] + public SoundSpecifier PrintSound = new SoundPathSpecifier("/Audio/Machines/terminal_insert_disc.ogg"); } [Serializable, NetSerializable] diff --git a/Content.Shared/Shuttles/Components/FTLDestinationComponent.cs b/Content.Shared/Shuttles/Components/FTLDestinationComponent.cs index 58cff96c2bc..d4bc536a29b 100644 --- a/Content.Shared/Shuttles/Components/FTLDestinationComponent.cs +++ b/Content.Shared/Shuttles/Components/FTLDestinationComponent.cs @@ -23,4 +23,10 @@ public sealed partial class FTLDestinationComponent : Component ///
[DataField, AutoNetworkedField] public bool BeaconsOnly; + + /// + /// Shuttles must use a corresponding CD to travel to this location. + /// + [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + public bool RequireCoordinateDisk = false; } diff --git a/Content.Shared/Shuttles/Components/SharedShuttleConsoleComponent.cs b/Content.Shared/Shuttles/Components/SharedShuttleConsoleComponent.cs index 436c830d20b..2ee03210ba9 100644 --- a/Content.Shared/Shuttles/Components/SharedShuttleConsoleComponent.cs +++ b/Content.Shared/Shuttles/Components/SharedShuttleConsoleComponent.cs @@ -9,7 +9,7 @@ namespace Content.Shared.Shuttles.Components [NetworkedComponent] public abstract partial class SharedShuttleConsoleComponent : Component { - + public static string DiskSlotName = "disk_slot"; } [Serializable, NetSerializable] diff --git a/Content.Shared/Shuttles/Components/ShuttleDestinationCoordinatesComponent.cs b/Content.Shared/Shuttles/Components/ShuttleDestinationCoordinatesComponent.cs new file mode 100644 index 00000000000..009dee49d10 --- /dev/null +++ b/Content.Shared/Shuttles/Components/ShuttleDestinationCoordinatesComponent.cs @@ -0,0 +1,15 @@ +namespace Content.Shared.Shuttles.Components; +using Robust.Shared.GameStates; + +/// +/// Enables a shuttle to travel to a destination with an item inserted into its console +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ShuttleDestinationCoordinatesComponent : Component +{ + /// + /// Uid for entity containing the FTLDestination component + /// + [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + public EntityUid? Destination; +} diff --git a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs index ca25a49b23f..d859d9f4859 100644 --- a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs +++ b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Containers.ItemSlots; using Content.Shared.Shuttles.BUIStates; using Content.Shared.Shuttles.Components; using Content.Shared.Shuttles.UI.MapObjects; @@ -10,7 +11,8 @@ namespace Content.Shared.Shuttles.Systems; public abstract partial class SharedShuttleSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] protected readonly SharedMapSystem Maps = default!; [Dependency] protected readonly SharedTransformSystem XformSystem = default!; @@ -34,7 +36,7 @@ public override void Initialize() /// /// Returns whether an entity can FTL to the specified map. /// - public bool CanFTLTo(EntityUid shuttleUid, MapId targetMap) + public bool CanFTLTo(EntityUid shuttleUid, MapId targetMap, EntityUid consoleUid) { var mapUid = _mapManager.GetMapEntityId(targetMap); var shuttleMap = _xformQuery.GetComponent(shuttleUid).MapID; @@ -42,10 +44,40 @@ public bool CanFTLTo(EntityUid shuttleUid, MapId targetMap) if (shuttleMap == targetMap) return true; - if (!TryComp(mapUid, out var destination) || - !destination.Enabled) - { + if (!TryComp(mapUid, out var destination) || !destination.Enabled) return false; + + if (destination.RequireCoordinateDisk) + { + if (!TryComp(consoleUid, out var slot)) + { + return false; + } + + if (!_itemSlots.TryGetSlot(consoleUid, SharedShuttleConsoleComponent.DiskSlotName, out var itemSlot, component: slot) || !itemSlot.HasItem) + { + return false; + } + + if (itemSlot.Item is { Valid: true } disk) + { + ShuttleDestinationCoordinatesComponent? diskCoordinates = null; + if (!Resolve(disk, ref diskCoordinates)) + { + return false; + } + + var diskCoords = diskCoordinates.Destination; + + if (diskCoords == null || !TryComp(diskCoords.Value, out var diskDestination) || diskDestination != destination) + { + return false; + } + } + else + { + return false; + } } if (HasComp(mapUid)) diff --git a/Resources/Prototypes/Entities/Objects/Misc/cds.yml b/Resources/Prototypes/Entities/Objects/Misc/cds.yml new file mode 100644 index 00000000000..a80a7d1d586 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Misc/cds.yml @@ -0,0 +1,19 @@ +- type: entity + name: coordinates disk + parent: BaseItem + id: CoordinatesDisk + description: A disk containing the coordinates to a location in space. Necessary for any FTL-traversing vessel to reach their destination. Fits inside shuttle consoles. + components: + - type: Sprite + sprite: Objects/Misc/cd.rsi + state: icon + - type: StaticPrice + price: 100 + - type: Tag + tags: + - CoordinatesDisk + - type: DamageOtherOnHit + damage: + types: + Slash: 1 + - type: ShuttleDestinationCoordinates diff --git a/Resources/Prototypes/Entities/Objects/Misc/diskcases.yml b/Resources/Prototypes/Entities/Objects/Misc/diskcases.yml new file mode 100644 index 00000000000..da86d7a00db --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Misc/diskcases.yml @@ -0,0 +1,21 @@ +- type: entity + name: diskcase + parent: BaseStorageItem + id: DiskCase + description: Case for storing a coordinates disk. + components: + - type: Sprite + sprite: Objects/Misc/diskcases.rsi + state: icon_base + - type: Storage + grid: + - 0,0,0,1 + maxItemSize: Normal + whitelist: + tags: + - Document + - CoordinatesDisk + storageOpenSound: /Audio/Machines/screwdriveropen.ogg + storageCloseSound: /Audio/Machines/screwdriverclose.ogg + storageInsertSound: /Audio/Items/crowbar.ogg + storageRemoveSound: /Audio/Items/crowbar.ogg diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 8f58464bd43..5a3dc375824 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -63,6 +63,22 @@ color: "#43ccb5" - type: Rotatable rotateWhileAnchored: true + - type: ItemSlots + slots: + disk_slot: + name: Disk + insertSound: + path: /Audio/Machines/terminal_insert_disc.ogg + ejectSound: + path: /Audio/Machines/terminal_insert_disc.ogg + whitelist: + components: + - ShuttleDestinationCoordinates + - type: ContainerContainer + containers: + board: !type:Container + ents: [] + disk_slot: !type:ContainerSlot {} - type: entity parent: BaseComputerShuttle diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 303a9b7c87b..961912d6092 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -418,6 +418,9 @@ - type: Tag id: ConveyorAssembly +- type: Tag + id: CoordinatesDisk + - type: Tag #Ohioans die happy id: Corn diff --git a/Resources/Textures/Objects/Misc/cd.rsi/icon.png b/Resources/Textures/Objects/Misc/cd.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..346d8c162a920fabb4dc5c692bdf7c156837b70f GIT binary patch literal 361 zcmV-v0ha!WP)Px$BS}O-R9J=WmAy*DKomw##5b@JoMQ4QQ*R?|mqM6o3r!*GPbC(Dg(4QwVyl2f zx&@o9=TVX(fi!uBv5*aJ*gz&(u$j|MhTQXUZ!Q6k$K&~Dg0U}#VQBxJrIZ+Boa>DW z5Qd?BUu8Sbgb>_bUO0z1I)r>nw%!oOvE6&wgHzf*xex+?v(pm*qWNraCkA(*PyU^) zT6tABRCUAa=NC$;L7i|OL06up>D$N416ADsP?jZSSyB|mpc=+9JfFzWwZ4LU}xrCt``w`o*#A#0E{ug z^W%Mco+@_^0FWd}5Y1|#9ABXFpY$(GaGB4H2n neifaE2UzWtj~C9lT*>%qtrgFStzi~GS2K9J`njxgN@xNA=O$S% literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/diskcases.rsi/icon_cargo.png b/Resources/Textures/Objects/Misc/diskcases.rsi/icon_cargo.png new file mode 100644 index 0000000000000000000000000000000000000000..4bee922d75418a46b80fde7f2af1b62f1d1c9acd GIT binary patch literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}OFUg1Ln2y} z6C_v{Cy4YkF|zfvh(tt0{CWQPadN`X%K0kI_O(9`7Cd>A-1Ka-K+u8X$B$2#GUdpJ zXRn+l$!vIgL3YsFS+t>X)g^shI;9`_; z_@=urVBt*W6I(?jc`B4-3fL#evOZC=_~S=j&p literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/diskcases.rsi/icon_cc.png b/Resources/Textures/Objects/Misc/diskcases.rsi/icon_cc.png new file mode 100644 index 0000000000000000000000000000000000000000..a0dd3a87938ad773547a8cc75fb11534487253c5 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}D?MEtLn2y} z6C_v{Cy4YkF|zfvh(tt0{CWQPak9n1f=-p@|9k(Q{qXcnbknoV0zn6kA3r`}%9JAo zf8*Fa`F>P9U=4gV!^3DQTSA7F!-c>}ju!%j)aQ#h81N=+{`2?WbBmu#nifj3GQVp0 z{(Ju7BbHY4LmY~v8X~*;{e{^lygIBHEFdV$G=;_3mG8%wm%@klRDPDQGZGMAV({Vp m+x$(krd)0ZH6&O|SQ(yuGGh9Fxlj=3Xa-MLKbLh*2~7Y~ab9o$ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/diskcases.rsi/meta.json b/Resources/Textures/Objects/Misc/diskcases.rsi/meta.json new file mode 100644 index 00000000000..714b0b19a03 --- /dev/null +++ b/Resources/Textures/Objects/Misc/diskcases.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by SlamBamActionman", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon_base" + }, + { + "name": "icon_cc" + }, + { + "name": "icon_cargo" + } + ] +} From 72bdcac1e271691abb92e3232f0a4d1a592e19c2 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 04:51:06 +0000 Subject: [PATCH 108/133] 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 ce167dcff3b..bccb6798b4f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Krunk - changes: - - message: Players will once again spawn at arrivals instead of cryostorage. - type: Fix - id: 5783 - time: '2024-01-25T00:48:58.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24476 - author: Krunk changes: - message: Paper cups can now be worn as party hats! @@ -3806,3 +3799,11 @@ id: 6282 time: '2024-04-01T04:35:21.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26594 +- author: SlamBamActionman + changes: + - message: Added Coordinates Disks to the Salvage expeditions console, which are + now a requirement for expedition FTL. + type: Add + id: 6283 + time: '2024-04-01T04:50:00.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/23240 From 64bb8dbdd50e0b1e5744e1eb0cc6f24bda959ade Mon Sep 17 00:00:00 2001 From: c4llv07e <38111072+c4llv07e@users.noreply.github.com> Date: Mon, 1 Apr 2024 09:06:13 +0300 Subject: [PATCH 109/133] Add door electronics access configuration menu (#17778) * Add door electronics configuration menu * Use file-scoped namespaces Signed-off-by: c4llv07e * Open door electronics configuration menu only with network configurator Signed-off-by: c4llv07e * Doors will now try to move their AccessReaderComponent to their door electronics when the map is initialized Signed-off-by: c4llv07e * Make the access list in the id card computer a separate control Signed-off-by: c4llv07e * Fix merge conflict Signed-off-by: c4llv07e * Remove DoorElectronics tag Signed-off-by: c4llv07e * Integrate doors with #17927 Signed-off-by: c4llv07e * Move door electornics ui stuff to the right place Signed-off-by: c4llv07e * Some review fixes Signed-off-by: c4llv07e * More fixes Signed-off-by: c4llv07e * review fix Signed-off-by: c4llv07e * move all accesses from airlock prototypes to door electronics Signed-off-by: c4llv07e * rework door electronics config access list Signed-off-by: c4llv07e * Remove Linq from the door electronics user interface * [WIP] Add EntityWhitelist to the activatable ui component Signed-off-by: c4llv07e * Better interaction system Signed-off-by: c4llv07e * Refactor Signed-off-by: c4llv07e * Fix some door electronics not working without AccessReaderComponent Signed-off-by: c4llv07e * Move AccessReaderComponent update code to the AccessReaderSystem Signed-off-by: c4llv07e * Remove unnecesary newlines in the door access prototypes Signed-off-by: c4llv07e * Remove unused variables in access level control Signed-off-by: c4llv07e * Remove unnecessary method from the door electronics configuration menu Signed-off-by: c4llv07e * [WIP] change access type from string to ProtoId Signed-off-by: c4llv07e * Remove unused methods Signed-off-by: c4llv07e * Newline fix Signed-off-by: c4llv07e * Restored to a functional state Signed-off-by: c4llv07e * Fix access configurator not working with door electronics AccessReaderComponent Signed-off-by: c4llv07e * Replace all string access fields with ProtoId Signed-off-by: c4llv07e * move access level control initialization into Populate method Signed-off-by: c4llv07e * Review --------- Signed-off-by: c4llv07e Co-authored-by: metalgearsloth --- .../Access/UI/AccessLevelControl.xaml | 4 + .../Access/UI/AccessLevelControl.xaml.cs | 52 ++ .../UI/AccessOverriderBoundUserInterface.cs | 2 +- .../Access/UI/AccessOverriderWindow.xaml.cs | 17 +- .../UI/IdCardConsoleBoundUserInterface.cs | 4 +- .../Access/UI/IdCardConsoleWindow.xaml | 6 +- .../Access/UI/IdCardConsoleWindow.xaml.cs | 51 +- .../DoorElectronicsBoundUserInterface.cs | 59 ++ .../DoorElectronicsConfigurationMenu.xaml | 6 + .../DoorElectronicsConfigurationMenu.xaml.cs | 41 ++ .../Tests/Access/AccessReaderTest.cs | 60 +- .../Access/Systems/AccessOverriderSystem.cs | 42 +- .../Access/Systems/IdCardConsoleSystem.cs | 17 +- .../Systems/AdminVerbSystem.Tools.cs | 5 +- .../Systems/DoorElectronicsSystem.cs | 69 ++ Content.Server/Sandbox/SandboxSystem.cs | 3 +- .../EntitySystems/BluespaceLockerSystem.cs | 4 +- .../UserInterface/ActivatableUIComponent.cs | 13 +- .../UserInterface/ActivatableUISystem.cs | 11 +- Content.Shared/Access/AccessGroupPrototype.cs | 4 +- Content.Shared/Access/AccessLevelPrototype.cs | 8 + .../Access/Components/AccessComponent.cs | 12 +- .../Components/AccessOverriderComponent.cs | 16 +- .../Components/AccessReaderComponent.cs | 20 +- .../Components/IdCardConsoleComponent.cs | 20 +- .../Access/Systems/AccessReaderSystem.cs | 51 +- .../Access/Systems/SharedAccessSystem.cs | 6 +- .../Systems/SharedIdCardConsoleSystem.cs | 14 + .../Electronics/DoorElectronicsComponent.cs | 42 ++ Content.Shared/Random/RulesPrototype.cs | 4 +- Content.Shared/Roles/JobPrototype.cs | 16 +- .../Objects/Devices/Electronics/door.yml | 12 +- .../Devices/Electronics/door_access.yml | 264 +++++++ .../Structures/Doors/Airlocks/access.yml | 642 ++++++++---------- .../Doors/Airlocks/base_structureairlocks.yml | 3 +- .../Structures/Doors/Airlocks/highsec.yml | 1 + .../Graphs/structures/airlock.yml | 2 +- .../Graphs/structures/shutter.yml | 2 +- .../Graphs/structures/shuttle.yml | 2 +- .../Graphs/structures/windoor.yml | 4 +- 40 files changed, 1079 insertions(+), 532 deletions(-) create mode 100644 Content.Client/Access/UI/AccessLevelControl.xaml create mode 100644 Content.Client/Access/UI/AccessLevelControl.xaml.cs create mode 100644 Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs create mode 100644 Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml create mode 100644 Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs create mode 100644 Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs create mode 100644 Content.Shared/Doors/Electronics/DoorElectronicsComponent.cs create mode 100644 Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml diff --git a/Content.Client/Access/UI/AccessLevelControl.xaml b/Content.Client/Access/UI/AccessLevelControl.xaml new file mode 100644 index 00000000000..56968d89839 --- /dev/null +++ b/Content.Client/Access/UI/AccessLevelControl.xaml @@ -0,0 +1,4 @@ + + diff --git a/Content.Client/Access/UI/AccessLevelControl.xaml.cs b/Content.Client/Access/UI/AccessLevelControl.xaml.cs new file mode 100644 index 00000000000..34db80b7af9 --- /dev/null +++ b/Content.Client/Access/UI/AccessLevelControl.xaml.cs @@ -0,0 +1,52 @@ +using System.Linq; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; +using Content.Shared.Access; +using Content.Shared.Access.Systems; + +namespace Content.Client.Access.UI; + +[GenerateTypedNameReferences] +public sealed partial class AccessLevelControl : GridContainer +{ + public readonly Dictionary, Button> ButtonsList = new(); + + public AccessLevelControl() + { + RobustXamlLoader.Load(this); + } + + public void Populate(List> accessLevels, IPrototypeManager prototypeManager) + { + foreach (var access in accessLevels) + { + if (!prototypeManager.TryIndex(access, out var accessLevel)) + { + Logger.Error($"Unable to find accesslevel for {access}"); + continue; + } + + var newButton = new Button + { + Text = accessLevel.GetAccessLevelName(), + ToggleMode = true, + }; + AddChild(newButton); + ButtonsList.Add(accessLevel.ID, newButton); + } + } + + public void UpdateState( + List> pressedList, + List>? enabledList = null) + { + foreach (var (accessName, button) in ButtonsList) + { + button.Pressed = pressedList.Contains(accessName); + button.Disabled = !(enabledList?.Contains(accessName) ?? true); + } + } +} diff --git a/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs b/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs index 0c23542f798..c1b63dc4d05 100644 --- a/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs +++ b/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs @@ -64,7 +64,7 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(castState); } - public void SubmitData(List newAccessList) + public void SubmitData(List> newAccessList) { SendMessage(new WriteToTargetAccessReaderIdMessage(newAccessList)); } diff --git a/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs b/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs index 2fd00571215..6025c3b551f 100644 --- a/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs +++ b/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs @@ -16,7 +16,6 @@ public sealed partial class AccessOverriderWindow : DefaultWindow [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - private readonly ISawmill _logMill = default!; private readonly AccessOverriderBoundUserInterface _owner; private readonly Dictionary _accessButtons = new(); @@ -25,7 +24,7 @@ public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototype { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - _logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill); + var logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill); _owner = owner; @@ -33,13 +32,13 @@ public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototype { if (!prototypeManager.TryIndex(access, out var accessLevel)) { - _logMill.Error($"Unable to find accesslevel for {access}"); + logMill.Error($"Unable to find accesslevel for {access}"); continue; } var newButton = new Button { - Text = GetAccessLevelName(accessLevel), + Text = accessLevel.GetAccessLevelName(), ToggleMode = true, }; @@ -49,14 +48,6 @@ public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototype } } - private static string GetAccessLevelName(AccessLevelPrototype prototype) - { - if (prototype.Name is { } name) - return Loc.GetString(name); - - return prototype.ID; - } - public void UpdateState(AccessOverriderBoundUserInterfaceState state) { PrivilegedIdLabel.Text = state.PrivilegedIdName; @@ -105,7 +96,7 @@ private void SubmitData() _owner.SubmitData( // Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair - _accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList()); + _accessButtons.Where(x => x.Value.Pressed).Select(x => new ProtoId(x.Key)).ToList()); } } } diff --git a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs index 898792aa030..5b7011c195a 100644 --- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs +++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs @@ -1,5 +1,6 @@ using Content.Shared.Access; using Content.Shared.Access.Components; +using Content.Shared.Access; using Content.Shared.Access.Systems; using Content.Shared.Containers.ItemSlots; using Content.Shared.CrewManifest; @@ -28,7 +29,6 @@ protected override void Open() if (EntMan.TryGetComponent(Owner, out var idCard)) { accessLevels = idCard.AccessLevels; - accessLevels.Sort(); } else { @@ -65,7 +65,7 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(castState); } - public void SubmitData(string newFullName, string newJobTitle, List newAccessList, string newJobPrototype) + public void SubmitData(string newFullName, string newJobTitle, List> newAccessList, string newJobPrototype) { if (newFullName.Length > MaxFullNameLength) newFullName = newFullName[..MaxFullNameLength]; diff --git a/Content.Client/Access/UI/IdCardConsoleWindow.xaml b/Content.Client/Access/UI/IdCardConsoleWindow.xaml index c29adc8ebd3..a2f5f3382bb 100644 --- a/Content.Client/Access/UI/IdCardConsoleWindow.xaml +++ b/Content.Client/Access/UI/IdCardConsoleWindow.xaml @@ -30,10 +30,6 @@
private void TryWriteToTargetAccessReaderId(EntityUid uid, - List newAccessList, + List> newAccessList, EntityUid player, AccessOverriderComponent? component = null) { @@ -211,9 +217,7 @@ private void TryWriteToTargetAccessReaderId(EntityUid uid, return; } - TryComp(component.TargetAccessReaderId, out AccessReaderComponent? accessReader); - - if (accessReader == null) + if (!_accessReader.GetMainAccessReader(component.TargetAccessReaderId, out var accessReader)) return; var oldTags = ConvertAccessHashSetsToList(accessReader.AccessLists); @@ -262,10 +266,10 @@ private bool PrivilegedIdIsAuthorized(EntityUid uid, AccessOverriderComponent? c if (!Resolve(uid, ref component)) return true; - if (!EntityManager.TryGetComponent(uid, out var reader)) + if (_accessReader.GetMainAccessReader(uid, out var accessReader)) return true; var privilegedId = component.PrivilegedIdSlot.Item; - return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader); + return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, accessReader); } } diff --git a/Content.Server/Access/Systems/IdCardConsoleSystem.cs b/Content.Server/Access/Systems/IdCardConsoleSystem.cs index 791159f972b..db8b9d036e8 100644 --- a/Content.Server/Access/Systems/IdCardConsoleSystem.cs +++ b/Content.Server/Access/Systems/IdCardConsoleSystem.cs @@ -12,6 +12,7 @@ using Robust.Shared.Prototypes; using System.Linq; using static Content.Shared.Access.Components.IdCardConsoleComponent; +using Content.Shared.Access; namespace Content.Server.Access.Systems; @@ -54,11 +55,11 @@ private void UpdateUserInterface(EntityUid uid, IdCardConsoleComponent component return; var privilegedIdName = string.Empty; - string[]? possibleAccess = null; + List>? possibleAccess = null; if (component.PrivilegedIdSlot.Item is { Valid: true } item) { privilegedIdName = EntityManager.GetComponent(item).EntityName; - possibleAccess = _accessReader.FindAccessTags(item).ToArray(); + possibleAccess = _accessReader.FindAccessTags(item).ToList(); } IdCardConsoleBoundUserInterfaceState newState; @@ -82,7 +83,7 @@ private void UpdateUserInterface(EntityUid uid, IdCardConsoleComponent component var targetIdComponent = EntityManager.GetComponent(targetId); var targetAccessComponent = EntityManager.GetComponent(targetId); - var jobProto = string.Empty; + var jobProto = new ProtoId(string.Empty); if (TryComp(targetId, out var keyStorage) && keyStorage.Key is {} key && _record.TryGetRecord(key, out var record)) @@ -96,7 +97,7 @@ private void UpdateUserInterface(EntityUid uid, IdCardConsoleComponent component true, targetIdComponent.FullName, targetIdComponent.JobTitle, - targetAccessComponent.Tags.ToArray(), + targetAccessComponent.Tags.ToList(), possibleAccess, jobProto, privilegedIdName, @@ -113,8 +114,8 @@ private void UpdateUserInterface(EntityUid uid, IdCardConsoleComponent component private void TryWriteToTargetId(EntityUid uid, string newFullName, string newJobTitle, - List newAccessList, - string newJobProto, + List> newAccessList, + ProtoId newJobProto, EntityUid player, IdCardConsoleComponent? component = null) { @@ -140,7 +141,7 @@ private void TryWriteToTargetId(EntityUid uid, return; } - var oldTags = _access.TryGetTags(targetId) ?? new List(); + var oldTags = _access.TryGetTags(targetId) ?? new List>(); oldTags = oldTags.ToList(); var privilegedId = component.PrivilegedIdSlot.Item; @@ -189,7 +190,7 @@ private bool PrivilegedIdIsAuthorized(EntityUid uid, IdCardConsoleComponent? com return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader); } - private void UpdateStationRecord(EntityUid uid, EntityUid targetId, string newFullName, string newJobTitle, JobPrototype? newJobProto) + private void UpdateStationRecord(EntityUid uid, EntityUid targetId, string newFullName, ProtoId newJobTitle, JobPrototype? newJobProto) { if (!TryComp(targetId, out var keyStorage) || keyStorage.Key is not { } key diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs index 9d66338c8bf..328fa744846 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs @@ -35,6 +35,7 @@ using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Administration.Systems; @@ -844,14 +845,14 @@ private void GiveAllAccess(EntityUid entity) { var allAccess = _prototypeManager .EnumeratePrototypes() - .Select(p => p.ID).ToArray(); + .Select(p => new ProtoId(p.ID)).ToArray(); _accessSystem.TrySetTags(entity, allAccess); } private void RevokeAllAccess(EntityUid entity) { - _accessSystem.TrySetTags(entity, Array.Empty()); + _accessSystem.TrySetTags(entity, new List>()); } public enum TricksVerbPriorities diff --git a/Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs b/Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs new file mode 100644 index 00000000000..56e8bd50b32 --- /dev/null +++ b/Content.Server/Doors/Electronics/Systems/DoorElectronicsSystem.cs @@ -0,0 +1,69 @@ +using System.Linq; +using Content.Server.Doors.Electronics; +using Content.Shared.Access; +using Content.Shared.Access.Components; +using Content.Shared.Access.Systems; +using Content.Shared.DeviceNetwork.Components; +using Content.Shared.Doors.Electronics; +using Content.Shared.Doors; +using Content.Shared.Interaction; +using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.Server.Doors.Electronics; + +public sealed class DoorElectronicsSystem : EntitySystem +{ + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly AccessReaderSystem _accessReader = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnChangeConfiguration); + SubscribeLocalEvent(OnAccessReaderChanged); + SubscribeLocalEvent(OnBoundUIOpened); + } + + public void UpdateUserInterface(EntityUid uid, DoorElectronicsComponent component) + { + var accesses = new List>(); + + if (TryComp(uid, out var accessReader)) + { + foreach (var accessList in accessReader.AccessLists) + { + var access = accessList.FirstOrDefault(); + accesses.Add(access); + } + } + + var state = new DoorElectronicsConfigurationState(accesses); + _uiSystem.TrySetUiState(uid, DoorElectronicsConfigurationUiKey.Key, state); + } + + private void OnChangeConfiguration( + EntityUid uid, + DoorElectronicsComponent component, + DoorElectronicsUpdateConfigurationMessage args) + { + var accessReader = EnsureComp(uid); + _accessReader.SetAccesses(uid, accessReader, args.AccessList); + } + + private void OnAccessReaderChanged( + EntityUid uid, + DoorElectronicsComponent component, + AccessReaderConfigurationChangedEvent args) + { + UpdateUserInterface(uid, component); + } + + private void OnBoundUIOpened( + EntityUid uid, + DoorElectronicsComponent component, + BoundUIOpenedEvent args) + { + UpdateUserInterface(uid, component); + } +} diff --git a/Content.Server/Sandbox/SandboxSystem.cs b/Content.Server/Sandbox/SandboxSystem.cs index b8bf1230902..30fe4e0fe95 100644 --- a/Content.Server/Sandbox/SandboxSystem.cs +++ b/Content.Server/Sandbox/SandboxSystem.cs @@ -14,6 +14,7 @@ using Robust.Server.Player; using Robust.Shared.Enums; using Robust.Shared.Player; +using Robust.Shared.Prototypes; namespace Content.Server.Sandbox { @@ -121,7 +122,7 @@ private void SandboxGiveAccessReceived(MsgSandboxGiveAccess message, EntitySessi var allAccess = PrototypeManager .EnumeratePrototypes() - .Select(p => p.ID).ToArray(); + .Select(p => new ProtoId(p.ID)).ToList(); if (_inventory.TryGetSlotEntity(attached, "id", out var slotEntity)) { diff --git a/Content.Server/Storage/EntitySystems/BluespaceLockerSystem.cs b/Content.Server/Storage/EntitySystems/BluespaceLockerSystem.cs index 356768769bb..838311c1aac 100644 --- a/Content.Server/Storage/EntitySystems/BluespaceLockerSystem.cs +++ b/Content.Server/Storage/EntitySystems/BluespaceLockerSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Resist; using Content.Server.Station.Components; using Content.Server.Storage.Components; +using Content.Shared.Access; using Content.Shared.Access.Components; using Content.Shared.Coordinates; using Content.Shared.DoAfter; @@ -14,6 +15,7 @@ using Robust.Shared.Containers; using Robust.Shared.Random; using Robust.Shared.Timing; +using Robust.Shared.Prototypes; namespace Content.Server.Storage.EntitySystems; @@ -138,7 +140,7 @@ private bool ValidLink(EntityUid locker, EntityUid link, BluespaceLockerComponen } /// True if any HashSet in would grant access to - private bool AccessMatch(IReadOnlyCollection>? a, IReadOnlyCollection>? b) + private bool AccessMatch(IReadOnlyCollection>>? a, IReadOnlyCollection>>? b) { if ((a == null || a.Count == 0) && (b == null || b.Count == 0)) return true; diff --git a/Content.Server/UserInterface/ActivatableUIComponent.cs b/Content.Server/UserInterface/ActivatableUIComponent.cs index 54639dd2b05..cc0e5008e47 100644 --- a/Content.Server/UserInterface/ActivatableUIComponent.cs +++ b/Content.Server/UserInterface/ActivatableUIComponent.cs @@ -1,3 +1,6 @@ +using Content.Shared.Whitelist; +using Robust.Server.GameObjects; +using Robust.Server.Player; using Robust.Shared.Player; using Robust.Shared.Serialization.TypeSerializers.Implementations; @@ -34,12 +37,19 @@ public sealed partial class ActivatableUIComponent : Component [DataField] public bool RequireHands = true; + /// + /// Entities that are required to open this UI. + /// + [DataField("allowedItems")] + [ViewVariables(VVAccess.ReadWrite)] + public EntityWhitelist? AllowedItems = null; + /// /// Whether you can activate this ui with activateinhand or not /// [ViewVariables(VVAccess.ReadWrite)] [DataField] - public bool rightClickOnly = false; + public bool RightClickOnly; /// /// Whether spectators (non-admin ghosts) should be allowed to view this UI. @@ -63,4 +73,3 @@ public sealed partial class ActivatableUIComponent : Component public ICommonSession? CurrentSingleUser; } } - diff --git a/Content.Server/UserInterface/ActivatableUISystem.cs b/Content.Server/UserInterface/ActivatableUISystem.cs index 387221e00ca..e3a11af4295 100644 --- a/Content.Server/UserInterface/ActivatableUISystem.cs +++ b/Content.Server/UserInterface/ActivatableUISystem.cs @@ -26,6 +26,7 @@ public override void Initialize() SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnHandDeselected); SubscribeLocalEvent((uid, aui, _) => CloseAll(uid, aui)); // *THIS IS A BLATANT WORKAROUND!* RATIONALE: Microwaves need it @@ -100,12 +101,20 @@ private void OnUseInHand(EntityUid uid, ActivatableUIComponent component, UseInH if (args.Handled) return; - if (component.rightClickOnly) + if (component.RightClickOnly) return; args.Handled = InteractUI(args.User, uid, component); } + private void OnInteractUsing(EntityUid uid, ActivatableUIComponent component, InteractUsingEvent args) + { + if (args.Handled) return; + if (component.AllowedItems == null) return; + if (!component.AllowedItems.IsValid(args.Used, EntityManager)) return; + args.Handled = InteractUI(args.User, uid, component); + } + private void OnParentChanged(EntityUid uid, ActivatableUIComponent aui, ref EntParentChangedMessage args) { CloseAll(uid, aui); diff --git a/Content.Shared/Access/AccessGroupPrototype.cs b/Content.Shared/Access/AccessGroupPrototype.cs index 8d3ed8feea1..323fdb91eda 100644 --- a/Content.Shared/Access/AccessGroupPrototype.cs +++ b/Content.Shared/Access/AccessGroupPrototype.cs @@ -14,6 +14,6 @@ public sealed partial class AccessGroupPrototype : IPrototype [IdDataField] public string ID { get; private set; } = default!; - [DataField("tags", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet Tags = default!; + [DataField("tags", required: true)] + public HashSet> Tags = default!; } diff --git a/Content.Shared/Access/AccessLevelPrototype.cs b/Content.Shared/Access/AccessLevelPrototype.cs index 8cc59271585..e3a3b426b09 100644 --- a/Content.Shared/Access/AccessLevelPrototype.cs +++ b/Content.Shared/Access/AccessLevelPrototype.cs @@ -17,5 +17,13 @@ public sealed partial class AccessLevelPrototype : IPrototype /// [DataField("name")] public string? Name { get; set; } + + public string GetAccessLevelName() + { + if (Name is { } name) + return Loc.GetString(name); + + return ID; + } } } diff --git a/Content.Shared/Access/Components/AccessComponent.cs b/Content.Shared/Access/Components/AccessComponent.cs index 2eacf2aa67b..00ee87b3b67 100644 --- a/Content.Shared/Access/Components/AccessComponent.cs +++ b/Content.Shared/Access/Components/AccessComponent.cs @@ -20,17 +20,17 @@ public sealed partial class AccessComponent : Component [AutoNetworkedField] public bool Enabled = true; - [DataField(customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + [DataField] [Access(typeof(SharedAccessSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends [AutoNetworkedField] - public HashSet Tags = new(); + public HashSet> Tags = new(); /// /// Access Groups. These are added to the tags during map init. After map init this will have no effect. /// - [DataField(readOnly: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + [DataField(readOnly: true)] [AutoNetworkedField] - public HashSet Groups = new(); + public HashSet> Groups = new(); } /// @@ -47,9 +47,9 @@ public GetAdditionalAccessEvent() } [ByRefEvent] -public record struct GetAccessTagsEvent(HashSet Tags, IPrototypeManager PrototypeManager) +public record struct GetAccessTagsEvent(HashSet> Tags, IPrototypeManager PrototypeManager) { - public void AddGroup(string group) + public void AddGroup(ProtoId group) { if (!PrototypeManager.TryIndex(group, out var groupPrototype)) return; diff --git a/Content.Shared/Access/Components/AccessOverriderComponent.cs b/Content.Shared/Access/Components/AccessOverriderComponent.cs index 92c09f1ab71..6a1bf2c831a 100644 --- a/Content.Shared/Access/Components/AccessOverriderComponent.cs +++ b/Content.Shared/Access/Components/AccessOverriderComponent.cs @@ -25,9 +25,9 @@ public sealed partial class AccessOverriderComponent : Component [Serializable, NetSerializable] public sealed class WriteToTargetAccessReaderIdMessage : BoundUserInterfaceMessage { - public readonly List AccessList; + public readonly List> AccessList; - public WriteToTargetAccessReaderIdMessage(List accessList) + public WriteToTargetAccessReaderIdMessage(List> accessList) { AccessList = accessList; } @@ -48,15 +48,15 @@ public sealed class AccessOverriderBoundUserInterfaceState : BoundUserInterfaceS public readonly string PrivilegedIdName; public readonly bool IsPrivilegedIdPresent; public readonly bool IsPrivilegedIdAuthorized; - public readonly string[]? TargetAccessReaderIdAccessList; - public readonly string[]? AllowedModifyAccessList; - public readonly string[]? MissingPrivilegesList; + public readonly ProtoId[]? TargetAccessReaderIdAccessList; + public readonly ProtoId[]? AllowedModifyAccessList; + public readonly ProtoId[]? MissingPrivilegesList; public AccessOverriderBoundUserInterfaceState(bool isPrivilegedIdPresent, bool isPrivilegedIdAuthorized, - string[]? targetAccessReaderIdAccessList, - string[]? allowedModifyAccessList, - string[]? missingPrivilegesList, + ProtoId[]? targetAccessReaderIdAccessList, + ProtoId[]? allowedModifyAccessList, + ProtoId[]? missingPrivilegesList, string privilegedIdName, string targetLabel, Color targetLabelColor) diff --git a/Content.Shared/Access/Components/AccessReaderComponent.cs b/Content.Shared/Access/Components/AccessReaderComponent.cs index b1577979223..cbd3f5acd67 100644 --- a/Content.Shared/Access/Components/AccessReaderComponent.cs +++ b/Content.Shared/Access/Components/AccessReaderComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.StationRecords; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; @@ -23,15 +24,15 @@ public sealed partial class AccessReaderComponent : Component /// The set of tags that will automatically deny an allowed check, if any of them are present. /// [ViewVariables(VVAccess.ReadWrite)] - [DataField(customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet DenyTags = new(); + [DataField] + public HashSet> DenyTags = new(); /// /// List of access groups that grant access to this reader. Only a single matching group is required to gain access. /// A group matches if it is a subset of the set being checked against. /// [DataField("access")] [ViewVariables(VVAccess.ReadWrite)] - public List> AccessLists = new(); + public List>> AccessLists = new(); /// /// A list of s that grant access. Only a single matching key is required to gain @@ -88,9 +89,9 @@ public sealed class AccessReaderComponentState : ComponentState { public bool Enabled; - public HashSet DenyTags; + public HashSet> DenyTags; - public List> AccessLists; + public List>> AccessLists; public List<(NetEntity, uint)> AccessKeys; @@ -98,7 +99,7 @@ public sealed class AccessReaderComponentState : ComponentState public int AccessLogLimit; - public AccessReaderComponentState(bool enabled, HashSet denyTags, List> accessLists, List<(NetEntity, uint)> accessKeys, Queue accessLog, int accessLogLimit) + public AccessReaderComponentState(bool enabled, HashSet> denyTags, List>> accessLists, List<(NetEntity, uint)> accessKeys, Queue accessLog, int accessLogLimit) { Enabled = enabled; DenyTags = denyTags; @@ -108,3 +109,10 @@ public AccessReaderComponentState(bool enabled, HashSet denyTags, List AccessList; - public readonly string JobPrototype; + public readonly List> AccessList; + public readonly ProtoId JobPrototype; - public WriteToTargetIdMessage(string fullName, string jobTitle, List accessList, string jobPrototype) + public WriteToTargetIdMessage(string fullName, string jobTitle, List> accessList, ProtoId jobPrototype) { FullName = fullName; JobTitle = jobTitle; @@ -86,18 +88,18 @@ public sealed class IdCardConsoleBoundUserInterfaceState : BoundUserInterfaceSta public readonly string TargetIdName; public readonly string? TargetIdFullName; public readonly string? TargetIdJobTitle; - public readonly string[]? TargetIdAccessList; - public readonly string[]? AllowedModifyAccessList; - public readonly string TargetIdJobPrototype; + public readonly List>? TargetIdAccessList; + public readonly List>? AllowedModifyAccessList; + public readonly ProtoId TargetIdJobPrototype; public IdCardConsoleBoundUserInterfaceState(bool isPrivilegedIdPresent, bool isPrivilegedIdAuthorized, bool isTargetIdPresent, string? targetIdFullName, string? targetIdJobTitle, - string[]? targetIdAccessList, - string[]? allowedModifyAccessList, - string targetIdJobPrototype, + List>? targetIdAccessList, + List>? allowedModifyAccessList, + ProtoId targetIdJobPrototype, string privilegedIdName, string targetIdName) { diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index 812a8e04870..4c12622ab56 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -112,11 +112,36 @@ public bool IsAllowed(EntityUid user, EntityUid target, AccessReaderComponent? r return false; } + public bool GetMainAccessReader(EntityUid uid, [NotNullWhen(true)] out AccessReaderComponent? component) + { + component = null; + if (!TryComp(uid, out AccessReaderComponent? accessReader)) + return false; + + component = accessReader; + + if (component.ContainerAccessProvider == null) + return true; + + if (!_containerSystem.TryGetContainer(uid, component.ContainerAccessProvider, out var container)) + return true; + + foreach (var entity in container.ContainedEntities) + { + if (TryComp(entity, out AccessReaderComponent? containedReader)) + { + component = containedReader; + return true; + } + } + return true; + } + /// /// Check whether the given access permissions satisfy an access reader's requirements. /// public bool IsAllowed( - ICollection access, + ICollection> access, ICollection stationKeys, EntityUid target, AccessReaderComponent reader) @@ -142,7 +167,7 @@ public bool IsAllowed( return false; } - private bool IsAllowedInternal(ICollection access, ICollection stationKeys, AccessReaderComponent reader) + private bool IsAllowedInternal(ICollection> access, ICollection stationKeys, AccessReaderComponent reader) { return !reader.Enabled || AreAccessTagsAllowed(access, reader) @@ -154,7 +179,7 @@ private bool IsAllowedInternal(ICollection access, ICollection /// A list of access tags /// An access reader to check against - public bool AreAccessTagsAllowed(ICollection accessTags, AccessReaderComponent reader) + public bool AreAccessTagsAllowed(ICollection> accessTags, AccessReaderComponent reader) { if (reader.DenyTags.Overlaps(accessTags)) { @@ -218,9 +243,9 @@ public HashSet FindPotentialAccessItems(EntityUid uid) /// /// The entity that is being searched. /// All of the items to search for access. If none are passed in, will be used. - public ICollection FindAccessTags(EntityUid uid, HashSet? items = null) + public ICollection> FindAccessTags(EntityUid uid, HashSet? items = null) { - HashSet? tags = null; + HashSet>? tags = null; var owned = false; items ??= FindPotentialAccessItems(uid); @@ -230,7 +255,7 @@ public ICollection FindAccessTags(EntityUid uid, HashSet? ite FindAccessTagsItem(ent, ref tags, ref owned); } - return (ICollection?) tags ?? Array.Empty(); + return (ICollection>?) tags ?? Array.Empty>(); } /// @@ -260,7 +285,7 @@ public bool FindStationRecordKeys(EntityUid uid, out ICollection - private void FindAccessTagsItem(EntityUid uid, ref HashSet? tags, ref bool owned) + private void FindAccessTagsItem(EntityUid uid, ref HashSet>? tags, ref bool owned) { if (!FindAccessTagsItem(uid, out var targetTags)) { @@ -286,6 +311,16 @@ private void FindAccessTagsItem(EntityUid uid, ref HashSet? tags, ref bo } } + public void SetAccesses(EntityUid uid, AccessReaderComponent component, List> accesses) + { + component.AccessLists.Clear(); + foreach (var access in accesses) + { + component.AccessLists.Add(new HashSet>(){access}); + } + RaiseLocalEvent(uid, new AccessReaderConfigurationChangedEvent()); + } + public bool FindAccessItemsInventory(EntityUid uid, out HashSet items) { items = new(); @@ -308,7 +343,7 @@ public bool FindAccessItemsInventory(EntityUid uid, out HashSet items /// Try to find on this item /// or inside this item (if it's pda) /// - private bool FindAccessTagsItem(EntityUid uid, out HashSet tags) + private bool FindAccessTagsItem(EntityUid uid, out HashSet> tags) { tags = new(); var ev = new GetAccessTagsEvent(tags, _prototype); diff --git a/Content.Shared/Access/Systems/SharedAccessSystem.cs b/Content.Shared/Access/Systems/SharedAccessSystem.cs index 965ccb24128..a4b04c35597 100644 --- a/Content.Shared/Access/Systems/SharedAccessSystem.cs +++ b/Content.Shared/Access/Systems/SharedAccessSystem.cs @@ -51,7 +51,7 @@ public void SetAccessEnabled(EntityUid uid, bool val, AccessComponent? component /// Replaces the set of access tags we have with the provided set. ///
/// The new access tags - public bool TrySetTags(EntityUid uid, IEnumerable newTags, AccessComponent? access = null) + public bool TrySetTags(EntityUid uid, IEnumerable> newTags, AccessComponent? access = null) { if (!Resolve(uid, ref access)) return false; @@ -67,12 +67,12 @@ public bool TrySetTags(EntityUid uid, IEnumerable newTags, AccessCompone /// Gets the set of access tags. /// /// The new access tags - public IEnumerable? TryGetTags(EntityUid uid, AccessComponent? access = null) + public IEnumerable>? TryGetTags(EntityUid uid, AccessComponent? access = null) { return !Resolve(uid, ref access) ? null : access.Tags; } - public bool TryAddGroups(EntityUid uid, IEnumerable newGroups, AccessComponent? access = null) + public bool TryAddGroups(EntityUid uid, IEnumerable> newGroups, AccessComponent? access = null) { if (!Resolve(uid, ref access)) return false; diff --git a/Content.Shared/Access/Systems/SharedIdCardConsoleSystem.cs b/Content.Shared/Access/Systems/SharedIdCardConsoleSystem.cs index 12e25e1e229..81843b5e2d2 100644 --- a/Content.Shared/Access/Systems/SharedIdCardConsoleSystem.cs +++ b/Content.Shared/Access/Systems/SharedIdCardConsoleSystem.cs @@ -1,6 +1,9 @@ using Content.Shared.Access.Components; using Content.Shared.Containers.ItemSlots; using JetBrains.Annotations; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Prototypes; namespace Content.Shared.Access.Systems { @@ -33,5 +36,16 @@ private void OnComponentRemove(EntityUid uid, IdCardConsoleComponent component, _itemSlotsSystem.RemoveItemSlot(uid, component.PrivilegedIdSlot); _itemSlotsSystem.RemoveItemSlot(uid, component.TargetIdSlot); } + + [Serializable, NetSerializable] + private sealed class IdCardConsoleComponentState : ComponentState + { + public List AccessLevels; + + public IdCardConsoleComponentState(List accessLevels) + { + AccessLevels = accessLevels; + } + } } } diff --git a/Content.Shared/Doors/Electronics/DoorElectronicsComponent.cs b/Content.Shared/Doors/Electronics/DoorElectronicsComponent.cs new file mode 100644 index 00000000000..3e9f279ee5d --- /dev/null +++ b/Content.Shared/Doors/Electronics/DoorElectronicsComponent.cs @@ -0,0 +1,42 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Prototypes; +using Content.Shared.Access; + +namespace Content.Shared.Doors.Electronics; + +/// +/// Allows an entity's AccessReader to be configured via UI. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class DoorElectronicsComponent : Component +{ +} + +[Serializable, NetSerializable] +public sealed class DoorElectronicsUpdateConfigurationMessage : BoundUserInterfaceMessage +{ + public List> AccessList; + + public DoorElectronicsUpdateConfigurationMessage(List> accessList) + { + AccessList = accessList; + } +} + +[Serializable, NetSerializable] +public sealed class DoorElectronicsConfigurationState : BoundUserInterfaceState +{ + public List> AccessList; + + public DoorElectronicsConfigurationState(List> accessList) + { + AccessList = accessList; + } +} + +[Serializable, NetSerializable] +public enum DoorElectronicsConfigurationUiKey : byte +{ + Key +} diff --git a/Content.Shared/Random/RulesPrototype.cs b/Content.Shared/Random/RulesPrototype.cs index 6bbc3a68f8f..20961af8e72 100644 --- a/Content.Shared/Random/RulesPrototype.cs +++ b/Content.Shared/Random/RulesPrototype.cs @@ -116,8 +116,8 @@ public sealed partial class NearbyAccessRule : RulesRule [DataField("count")] public int Count = 1; - [DataField("access", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List Access = new(); + [DataField("access", required: true)] + public List> Access = new(); [DataField("range")] public float Range = 10f; diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index 0064fcdf17e..34a8ce64bff 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -105,17 +105,17 @@ public sealed partial class JobPrototype : IPrototype [DataField("special", serverOnly: true)] public JobSpecial[] Special { get; private set; } = Array.Empty(); - [DataField("access", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public IReadOnlyCollection Access { get; private set; } = Array.Empty(); + [DataField("access")] + public IReadOnlyCollection> Access { get; private set; } = Array.Empty>(); - [DataField("accessGroups", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public IReadOnlyCollection AccessGroups { get; private set; } = Array.Empty(); + [DataField("accessGroups")] + public IReadOnlyCollection> AccessGroups { get; private set; } = Array.Empty>(); - [DataField("extendedAccess", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public IReadOnlyCollection ExtendedAccess { get; private set; } = Array.Empty(); + [DataField("extendedAccess")] + public IReadOnlyCollection> ExtendedAccess { get; private set; } = Array.Empty>(); - [DataField("extendedAccessGroups", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public IReadOnlyCollection ExtendedAccessGroups { get; private set; } = Array.Empty(); + [DataField("extendedAccessGroups")] + public IReadOnlyCollection> ExtendedAccessGroups { get; private set; } = Array.Empty>(); } /// diff --git a/Resources/Prototypes/Entities/Objects/Devices/Electronics/door.yml b/Resources/Prototypes/Entities/Objects/Devices/Electronics/door.yml index 275a61d8216..16713a6692f 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Electronics/door.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Electronics/door.yml @@ -7,9 +7,19 @@ - type: Sprite sprite: Objects/Misc/module.rsi state: door_electronics - - type: AccessReader - type: Tag tags: - DoorElectronics + - type: DoorElectronics - type: StaticPrice price: 55 + - type: AccessReader + - type: ActivatableUI + key: enum.DoorElectronicsConfigurationUiKey.Key + allowedItems: + tags: + - Multitool + - type: UserInterface + interfaces: + - key: enum.DoorElectronicsConfigurationUiKey.Key + type: DoorElectronicsBoundUserInterface diff --git a/Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml b/Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml new file mode 100644 index 00000000000..fc6d8e2697d --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml @@ -0,0 +1,264 @@ + +- type: entity + parent: DoorElectronics + id: DoorElectronicsService + suffix: Service, Locked + components: + - type: AccessReader + access: [["Service"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsTheatre + suffix: Theatre, Locked + components: + - type: AccessReader + access: [["Theatre"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsChapel + suffix: Chapel, Locked + components: + - type: AccessReader + access: [["Chapel"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsJanitor + suffix: Janitor, Locked + components: + - type: AccessReader + access: [["Janitor"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsKitchen + suffix: Kitchen, Locked + components: + - type: AccessReader + access: [["Kitchen"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsBar + suffix: Bar, Locked + components: + - type: AccessReader + access: [["Bar"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsHydroponics + suffix: Hydroponics, Locked + components: + - type: AccessReader + access: [["Hydroponics"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsCaptain + suffix: Captain, Locked + components: + - type: AccessReader + access: [["Captain"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsExternal + suffix: External, Locked + components: + - type: AccessReader + access: [["External"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsCargo + suffix: Cargo, Locked + components: + - type: AccessReader + access: [["Cargo"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsEngineering + suffix: Engineering, Locked + components: + - type: AccessReader + access: [["Engineering"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsAtmospherics + suffix: Atmospherics, Locked + components: + - type: AccessReader + access: [["Atmospherics"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsFreezer + suffix: Freezer, Locked + components: + - type: AccessReader + access: [["Kitchen"], ["Hydroponics"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsSalvage + suffix: Salvage, Locked + components: + - type: AccessReader + access: [["Salvage"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsMedical + suffix: Medical, Locked + components: + - type: AccessReader + access: [["Medical"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsChemistry + suffix: Chemistry, Locked + components: + - type: AccessReader + access: [["Chemistry"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsResearch + suffix: Research, Locked + components: + - type: AccessReader + access: [["Research"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsScience + suffix: Science, Locked + components: + - type: AccessReader + access: [["Research"], ["Medical"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsCommand + suffix: Command, Locked + components: + - type: AccessReader + access: [["Command"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsChiefMedicalOfficer + suffix: ChiefMedicalOfficer, Locked + components: + - type: AccessReader + access: [["ChiefMedicalOfficer"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsChiefEngineer + suffix: ChiefEngineer, Locked + components: + - type: AccessReader + access: [["ChiefEngineer"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsHeadOfSecurity + suffix: HeadOfSecurity, Locked + components: + - type: AccessReader + access: [["HeadOfSecurity"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsResearchDirector + suffix: ResearchDirector, Locked + components: + - type: AccessReader + access: [["ResearchDirector"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsHeadOfPersonnel + suffix: HeadOfPersonnel, Locked + components: + - type: AccessReader + access: [["HeadOfPersonnel"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsQuartermaster + suffix: Quartermaster, Locked + components: + - type: AccessReader + access: [["Quartermaster"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsSecurity + suffix: Security, Locked + components: + - type: AccessReader + access: [["Security"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsDetective + suffix: Detective, Locked + components: + - type: AccessReader + access: [["Detective"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsBrig + suffix: Brig, Locked + components: + - type: AccessReader + access: [["Brig"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsArmory + suffix: Armory, Locked + components: + - type: AccessReader + access: [["Armory"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsVault + suffix: Vault, Locked + components: + - type: AccessReader + access: [["Security", "Command"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsMaintenance + suffix: Maintenance, Locked + components: + - type: AccessReader + access: [["Maintenance"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsSyndicateAgent + suffix: SyndicateAgent, Locked + components: + - type: AccessReader + access: [["SyndicateAgent"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsRnDMed + suffix: Medical/Science, Locked + components: + - type: AccessReader + access: [["Research"], ["Medical"]] diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml index 7eed29ad56b..8ffd18e7e29 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml @@ -4,10 +4,9 @@ id: AirlockServiceLocked suffix: Service, Locked components: - - type: AccessReader - access: [["Service"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsService ] - type: entity parent: Airlock @@ -24,108 +23,99 @@ id: AirlockTheatreLocked suffix: Theatre, Locked components: - - type: AccessReader - access: [["Theatre"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsTheatre ] - type: entity parent: Airlock id: AirlockChapelLocked suffix: Chapel, Locked components: - - type: AccessReader - access: [["Chapel"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsChapel ] - type: entity parent: Airlock id: AirlockJanitorLocked suffix: Janitor, Locked components: - - type: AccessReader - access: [["Janitor"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsJanitor ] - type: entity parent: Airlock id: AirlockKitchenLocked suffix: Kitchen, Locked components: - - type: AccessReader - access: [["Kitchen"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsKitchen ] - type: entity parent: Airlock id: AirlockBarLocked suffix: Bar, Locked components: - - type: AccessReader - access: [["Bar"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsBar ] - type: entity parent: Airlock id: AirlockHydroponicsLocked suffix: Hydroponics, Locked components: - - type: AccessReader - access: [["Hydroponics"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsHydroponics ] - type: entity parent: Airlock id: AirlockServiceCaptainLocked suffix: Captain, Locked components: - - type: AccessReader - access: [["Captain"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsCaptain ] - type: entity parent: AirlockExternal id: AirlockExternalLocked suffix: External, Locked components: - - type: AccessReader - access: [["External"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsExternal ] - type: entity parent: AirlockExternal id: AirlockExternalCargoLocked suffix: External, Cargo, Locked components: - - type: AccessReader - access: [["Cargo"]] - - type: Wires - layoutId: AirlockCargo + - type: ContainerFill + containers: + board: [ DoorElectronicsCargo ] - type: entity parent: AirlockExternal id: AirlockExternalEngineeringLocked suffix: External, Engineering, Locked components: - - type: AccessReader - access: [["Engineering"]] - - type: Wires - layoutId: AirlockEngineering + - type: ContainerFill + containers: + board: [ DoorElectronicsEngineering ] - type: entity parent: AirlockExternal id: AirlockExternalAtmosphericsLocked suffix: External, Atmospherics, Locked components: - - type: AccessReader - access: [["Atmospherics"]] - - type: Wires - layoutId: AirlockEngineering + - type: ContainerFill + containers: + board: [ DoorElectronicsAtmospherics ] - type: entity parent: AirlockExternal @@ -148,20 +138,18 @@ id: AirlockFreezerLocked suffix: Kitchen, Locked components: - - type: AccessReader - access: [["Kitchen"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsKitchen ] - type: entity parent: AirlockFreezer id: AirlockFreezerKitchenHydroLocked suffix: Kitchen/Hydroponics, Locked components: - - type: AccessReader - access: [["Kitchen"], ["Hydroponics"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsFreezer ] - type: entity parent: AirlockFreezer @@ -178,40 +166,36 @@ id: AirlockEngineeringLocked suffix: Engineering, Locked components: - - type: AccessReader - access: [["Engineering"]] - - type: Wires - layoutId: AirlockEngineering + - type: ContainerFill + containers: + board: [ DoorElectronicsEngineering ] - type: entity parent: AirlockAtmospherics id: AirlockAtmosphericsLocked suffix: Atmospherics, Locked components: - - type: AccessReader - access: [["Atmospherics"]] - - type: Wires - layoutId: AirlockEngineering + - type: ContainerFill + containers: + board: [ DoorElectronicsAtmospherics ] - type: entity parent: AirlockCargo id: AirlockCargoLocked suffix: Cargo, Locked components: - - type: AccessReader - access: [["Cargo"]] - - type: Wires - layoutId: AirlockCargo + - type: ContainerFill + containers: + board: [ DoorElectronicsCargo ] - type: entity parent: AirlockCargo id: AirlockSalvageLocked suffix: Salvage, Locked components: - - type: AccessReader - access: [["Salvage"]] - - type: Wires - layoutId: AirlockCargo + - type: ContainerFill + containers: + board: [ DoorElectronicsSalvage ] - type: entity parent: AirlockMining @@ -228,50 +212,45 @@ id: AirlockMedicalLocked suffix: Medical, Locked components: - - type: AccessReader - access: [["Medical"]] - - type: Wires - layoutId: AirlockMedical + - type: ContainerFill + containers: + board: [ DoorElectronicsMedical ] - type: entity parent: AirlockVirology id: AirlockVirologyLocked suffix: Virology, Locked components: - - type: AccessReader - access: [["Medical"]] - - type: Wires - layoutId: AirlockMedical + - type: ContainerFill + containers: + board: [ DoorElectronicsMedical ] - type: entity parent: AirlockChemistry id: AirlockChemistryLocked suffix: Chemistry, Locked components: - - type: AccessReader - access: [["Chemistry"]] - - type: Wires - layoutId: AirlockMedical + - type: ContainerFill + containers: + board: [ DoorElectronicsChemistry ] - type: entity parent: AirlockScience id: AirlockScienceLocked suffix: Science, Locked components: - - type: AccessReader - access: [["Research"]] - - type: Wires - layoutId: AirlockScience + - type: ContainerFill + containers: + board: [ DoorElectronicsResearch ] - type: entity parent: AirlockScience id: AirlockMedicalScienceLocked suffix: Medical/Science, Locked components: - - type: AccessReader - access: [["Research"], ["Medical"]] - - type: Wires - layoutId: AirlockScience + - type: ContainerFill + containers: + board: [ DoorElectronicsScience ] - type: entity parent: AirlockCentralCommand @@ -288,8 +267,9 @@ id: AirlockCommandLocked suffix: Command, Locked components: - - type: AccessReader - access: [["Command"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsCommand ] - type: Wires layoutId: AirlockCommand @@ -298,78 +278,72 @@ id: AirlockCaptainLocked suffix: Captain, Locked components: - - type: AccessReader - access: [["Captain"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsCaptain ] - type: entity parent: AirlockCommand id: AirlockChiefMedicalOfficerLocked suffix: ChiefMedicalOfficer, Locked components: - - type: AccessReader - access: [["ChiefMedicalOfficer"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsChiefMedicalOfficer ] - type: entity parent: AirlockCommand id: AirlockChiefEngineerLocked suffix: ChiefEngineer, Locked components: - - type: AccessReader - access: [["ChiefEngineer"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsChiefEngineer ] - type: entity parent: AirlockCommand id: AirlockHeadOfSecurityLocked suffix: HeadOfSecurity, Locked components: - - type: AccessReader - access: [["HeadOfSecurity"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsHeadOfSecurity ] - type: entity parent: AirlockCommand id: AirlockResearchDirectorLocked suffix: ResearchDirector, Locked components: - - type: AccessReader - access: [["ResearchDirector"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsResearchDirector ] - type: entity parent: AirlockCommand id: AirlockHeadOfPersonnelLocked suffix: HeadOfPersonnel, Locked components: - - type: AccessReader - access: [["HeadOfPersonnel"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsHeadOfPersonnel ] - type: entity parent: AirlockCommand id: AirlockQuartermasterLocked suffix: Quartermaster, Locked components: - - type: AccessReader - access: [["Quartermaster"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsQuartermaster ] - type: entity parent: AirlockSecurity id: AirlockSecurityLocked suffix: Security, Locked components: - - type: AccessReader - access: [["Security"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsSecurity ] - type: Wires layoutId: AirlockSecurity @@ -378,8 +352,9 @@ id: AirlockDetectiveLocked suffix: Detective, Locked components: - - type: AccessReader - access: [["Detective"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsDetective ] - type: Wires layoutId: AirlockSecurity @@ -388,8 +363,9 @@ id: AirlockBrigLocked suffix: Brig, Locked components: - - type: AccessReader - access: [["Brig"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsBrig ] - type: Wires layoutId: AirlockSecurity @@ -408,8 +384,9 @@ id: AirlockArmoryLocked suffix: Armory, Locked components: - - type: AccessReader - access: [["Armory"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsArmory ] - type: Wires layoutId: AirlockArmory @@ -418,20 +395,18 @@ id: AirlockVaultLocked suffix: Vault, Locked components: - - type: AccessReader - access: [["Security", "Command"]] - - type: Wires - layoutId: AirlockSecurity + - type: ContainerFill + containers: + board: [ DoorElectronicsVault ] - type: entity parent: AirlockCommand id: AirlockEVALocked suffix: EVA, Locked components: - - type: AccessReader - access: [["External"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsExternal ] # Glass Airlocks - type: entity @@ -439,10 +414,9 @@ id: AirlockServiceGlassLocked suffix: Service, Locked components: - - type: AccessReader - access: [["Service"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsService ] - type: entity parent: AirlockGlass @@ -469,28 +443,27 @@ id: AirlockBarGlassLocked suffix: Bar, Locked components: - - type: AccessReader - access: [["Bar"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsBar ] - type: entity parent: AirlockExternalGlass id: AirlockExternalGlassLocked suffix: External, Glass, Locked components: - - type: AccessReader - access: [["External"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsExternal ] - type: entity parent: AirlockExternalGlass id: AirlockExternalGlassCargoLocked suffix: External, Glass, Cargo, Locked components: - - type: AccessReader - access: [["Cargo"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsCargo ] - type: entity parent: AirlockExternalGlass @@ -513,30 +486,27 @@ id: AirlockExternalGlassEngineeringLocked suffix: External, Glass, Engineering, Locked components: - - type: AccessReader - access: [["Engineering"]] - - type: Wires - layoutId: AirlockEngineering + - type: ContainerFill + containers: + board: [ DoorElectronicsEngineering ] - type: entity parent: AirlockExternalGlass id: AirlockExternalGlassAtmosphericsLocked suffix: External, Glass, Atmospherics, Locked components: - - type: AccessReader - access: [["Atmospherics"]] - - type: Wires - layoutId: AirlockEngineering + - type: ContainerFill + containers: + board: [ DoorElectronicsAtmospherics ] - type: entity parent: AirlockGlass id: AirlockKitchenGlassLocked suffix: Kitchen, Locked components: - - type: AccessReader - access: [["Kitchen"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsKitchen ] - type: entity parent: AirlockGlass @@ -553,60 +523,54 @@ id: AirlockHydroGlassLocked suffix: Hydroponics, Locked components: - - type: AccessReader - access: [["Hydroponics"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsHydroponics ] - type: entity parent: AirlockGlass id: AirlockChapelGlassLocked suffix: Chapel, Locked components: - - type: AccessReader - access: [["Chapel"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsChapel ] - type: entity parent: AirlockEngineeringGlass id: AirlockEngineeringGlassLocked suffix: Engineering, Locked components: - - type: AccessReader - access: [["Engineering"]] - - type: Wires - layoutId: AirlockEngineering + - type: ContainerFill + containers: + board: [ DoorElectronicsEngineering ] - type: entity parent: AirlockAtmosphericsGlass id: AirlockAtmosphericsGlassLocked suffix: Atmospherics, Locked components: - - type: AccessReader - access: [["Atmospherics"]] - - type: Wires - layoutId: AirlockEngineering + - type: ContainerFill + containers: + board: [ DoorElectronicsAtmospherics ] - type: entity parent: AirlockCargoGlass id: AirlockCargoGlassLocked suffix: Cargo, Locked components: - - type: AccessReader - access: [["Cargo"]] - - type: Wires - layoutId: AirlockCargo + - type: ContainerFill + containers: + board: [ DoorElectronicsCargo ] - type: entity parent: AirlockCargoGlass id: AirlockSalvageGlassLocked suffix: Salvage, Locked components: - - type: AccessReader - access: [["Salvage"]] - - type: Wires - layoutId: AirlockCargo + - type: ContainerFill + containers: + board: [ DoorElectronicsSalvage ] - type: entity parent: AirlockMiningGlass @@ -633,40 +597,36 @@ id: AirlockMedicalGlassLocked suffix: Medical, Locked components: - - type: AccessReader - access: [["Medical"]] - - type: Wires - layoutId: AirlockMedical + - type: ContainerFill + containers: + board: [ DoorElectronicsMedical ] - type: entity parent: AirlockVirologyGlass id: AirlockVirologyGlassLocked suffix: Virology, Locked components: - - type: AccessReader - access: [["Medical"]] - - type: Wires - layoutId: AirlockMedical + - type: ContainerFill + containers: + board: [ DoorElectronicsMedical ] - type: entity parent: AirlockScienceGlass id: AirlockScienceGlassLocked suffix: Science, Locked components: - - type: AccessReader - access: [["Research"]] - - type: Wires - layoutId: AirlockScience + - type: ContainerFill + containers: + board: [ DoorElectronicsResearch ] - type: entity parent: AirlockScienceGlass id: AirlockMedicalScienceGlassLocked suffix: Medical/Science, Locked components: - - type: AccessReader - access: [["Research"], ["Medical"]] - - type: Wires - layoutId: AirlockScience + - type: ContainerFill + containers: + board: [ DoorElectronicsScience ] - type: entity parent: AirlockCentralCommandGlass @@ -683,110 +643,99 @@ id: AirlockCommandGlassLocked suffix: Command, Locked components: - - type: AccessReader - access: [["Command"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsCommand ] - type: entity parent: AirlockCommandGlass id: AirlockCaptainGlassLocked suffix: Captain, Locked components: - - type: AccessReader - access: [["Captain"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsCaptain ] - type: entity parent: AirlockCommandGlass id: AirlockChiefMedicalOfficerGlassLocked suffix: ChiefMedicalOfficer, Locked components: - - type: AccessReader - access: [["ChiefMedicalOfficer"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsChiefMedicalOfficer ] - type: entity parent: AirlockCommandGlass id: AirlockChiefEngineerGlassLocked suffix: ChiefEngineer, Locked components: - - type: AccessReader - access: [["ChiefEngineer"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsChiefEngineer ] - type: entity parent: AirlockCommandGlass id: AirlockHeadOfSecurityGlassLocked suffix: HeadOfSecurity, Locked components: - - type: AccessReader - access: [["HeadOfSecurity"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsHeadOfSecurity ] - type: entity parent: AirlockCommandGlass id: AirlockResearchDirectorGlassLocked suffix: ResearchDirector, Locked components: - - type: AccessReader - access: [["ResearchDirector"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsResearchDirector ] - type: entity parent: AirlockCommandGlass id: AirlockHeadOfPersonnelGlassLocked suffix: HeadOfPersonnel, Locked components: - - type: AccessReader - access: [["HeadOfPersonnel"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsHeadOfPersonnel ] - type: entity parent: AirlockCommandGlass id: AirlockQuartermasterGlassLocked suffix: Quartermaster, Locked components: - - type: AccessReader - access: [["Quartermaster"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsQuartermaster ] - type: entity parent: AirlockSecurityGlass id: AirlockSecurityGlassLocked suffix: Security, Locked components: - - type: AccessReader - access: [["Security"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsSecurity ] - type: entity parent: AirlockSecurityGlass id: AirlockDetectiveGlassLocked suffix: Detective, Locked components: - - type: AccessReader - access: [["Detective"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsDetective ] - type: entity parent: AirlockSecurityGlass id: AirlockBrigGlassLocked suffix: Brig, Locked components: - - type: AccessReader - access: [["Brig"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsBrig ] - type: entity parent: AirlockSecurityGlass @@ -803,18 +752,18 @@ id: AirlockArmoryGlassLocked suffix: Armory, Locked components: - - type: AccessReader - access: [["Armory"]] - - type: Wires - layoutId: AirlockSecurity + - type: ContainerFill + containers: + board: [ DoorElectronicsArmory ] - type: entity parent: AirlockCommandGlassLocked id: AirlockEVAGlassLocked suffix: EVA, Locked components: - - type: AccessReader - access: [["External"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsExternal ] - type: entity parent: AirlockSyndicateGlass @@ -838,114 +787,108 @@ id: AirlockMaintLocked suffix: Locked components: - - type: AccessReader - access: [["Maintenance"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsMaintenance ] - type: entity parent: AirlockMaintGlass id: AirlockMaintGlassLocked suffix: Locked components: - - type: AccessReader - access: [["Maintenance"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsMaintenance ] - type: entity parent: AirlockMaint id: AirlockMaintSalvageLocked suffix: Salvage, Locked components: - - type: AccessReader - access: [["Salvage"]] - - type: Wires - layoutId: AirlockCargo + - type: ContainerFill + containers: + board: [ DoorElectronicsSalvage ] - type: entity parent: AirlockMaint id: AirlockMaintCargoLocked suffix: Cargo, Locked components: - - type: AccessReader - access: [["Cargo"]] - - type: Wires - layoutId: AirlockCargo + - type: ContainerFill + containers: + board: [ DoorElectronicsCargo ] - type: entity parent: AirlockMaint id: AirlockMaintCommandLocked suffix: Command, Locked components: - - type: AccessReader - access: [["Command"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsCommand ] - type: entity parent: AirlockMaint id: AirlockMaintCommonLocked suffix: Common, Locked components: - - type: AccessReader - access: [["Maintenance"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsMaintenance ] - type: entity parent: AirlockMaint id: AirlockMaintEngiLocked suffix: Engineering, Locked components: - - type: AccessReader - access: [["Engineering"]] - - type: Wires - layoutId: AirlockEngineering + - type: ContainerFill + containers: + board: [ DoorElectronicsEngineering ] - type: entity parent: AirlockMaint id: AirlockMaintAtmoLocked suffix: Atmospherics, Locked components: - - type: AccessReader - access: [["Atmospherics"]] - - type: Wires - layoutId: AirlockEngineering + - type: ContainerFill + containers: + board: [ DoorElectronicsAtmospherics ] - type: entity parent: AirlockMaint id: AirlockMaintBarLocked suffix: Bar, Locked components: - - type: AccessReader - access: [["Bar"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsBar ] - type: entity parent: AirlockMaint id: AirlockMaintChapelLocked suffix: Chapel, Locked components: - - type: AccessReader - access: [["Chapel"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsChapel ] - type: entity parent: AirlockMaint id: AirlockMaintHydroLocked suffix: Hydroponics, Locked components: - - type: AccessReader - access: [["Hydroponics"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsHydroponics ] - type: entity parent: AirlockMaint id: AirlockMaintJanitorLocked suffix: Janitor, Locked components: - - type: AccessReader - access: [["Janitor"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsJanitor ] - type: entity parent: AirlockMaint @@ -972,108 +915,99 @@ id: AirlockMaintTheatreLocked suffix: Theatre, Locked components: - - type: AccessReader - access: [["Theatre"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsTheatre ] - type: entity parent: AirlockMaint id: AirlockMaintKitchenLocked suffix: Kitchen, Locked components: - - type: AccessReader - access: [["Kitchen"]] - - type: Wires - layoutId: AirlockService + - type: ContainerFill + containers: + board: [ DoorElectronicsKitchen ] - type: entity parent: AirlockMaint id: AirlockMaintIntLocked suffix: Interior, Locked components: - - type: AccessReader - access: [["Maintenance"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsMaintenance ] - type: entity parent: AirlockMaint id: AirlockMaintMedLocked suffix: Medical, Locked components: - - type: AccessReader - access: [["Medical"]] - - type: Wires - layoutId: AirlockMedical + - type: ContainerFill + containers: + board: [ DoorElectronicsMedical ] - type: entity parent: AirlockMaint id: AirlockMaintChemLocked suffix: Chemistry, Locked components: - - type: AccessReader - access: [["Chemistry"]] - - type: Wires - layoutId: AirlockMedical + - type: ContainerFill + containers: + board: [ DoorElectronicsChemistry ] - type: entity parent: AirlockMaint id: AirlockMaintRnDLocked suffix: Science, Locked components: - - type: AccessReader - access: [["Research"]] - - type: Wires - layoutId: AirlockScience + - type: ContainerFill + containers: + board: [ DoorElectronicsResearch ] - type: entity parent: AirlockMaint id: AirlockMaintRnDMedLocked suffix: Medical/Science, Locked components: - - type: AccessReader - access: [["Research"], ["Medical"]] - - type: Wires - layoutId: AirlockScience + - type: ContainerFill + containers: + board: [ DoorElectronicsRnDMed ] - type: entity parent: AirlockMaint id: AirlockMaintSecLocked suffix: Security, Locked components: - - type: AccessReader - access: [["Security"]] - - type: Wires - layoutId: AirlockSecurity + - type: ContainerFill + containers: + board: [ DoorElectronicsSecurity ] - type: entity parent: AirlockMaint id: AirlockMaintDetectiveLocked suffix: Detective, Locked components: - - type: AccessReader - access: [["Detective"]] - - type: Wires - layoutId: AirlockSecurity + - type: ContainerFill + containers: + board: [ DoorElectronicsDetective ] - type: entity parent: AirlockMaint id: AirlockMaintHOPLocked suffix: HeadOfPersonnel, Locked components: - - type: AccessReader - access: [["HeadOfPersonnel"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsHeadOfPersonnel ] - type: entity parent: AirlockMaint id: AirlockMaintCaptainLocked suffix: Captain, Locked components: - - type: AccessReader - access: [["Captain"]] - - type: Wires - layoutId: AirlockCommand + - type: ContainerFill + containers: + board: [ DoorElectronicsCaptain ] - type: entity parent: AirlockMaint @@ -1147,8 +1081,9 @@ id: AirlockExternalShuttleLocked suffix: External, Docking, Locked components: - - type: AccessReader - access: [["External"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsExternal ] - type: entity parent: AirlockShuttleSyndicate @@ -1171,8 +1106,9 @@ id: AirlockExternalGlassShuttleLocked suffix: External, Glass, Docking, Locked components: - - type: AccessReader - access: [["External"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsExternal ] - type: entity parent: AirlockGlassShuttleSyndicate @@ -1197,8 +1133,9 @@ components: - type: PriorityDock tag: DockEmergency - - type: AccessReader - access: [["External"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsExternal ] - type: entity parent: AirlockGlassShuttle @@ -1229,24 +1166,27 @@ id: HighSecCommandLocked suffix: Command, Locked components: - - type: AccessReader - access: [["Command"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsCommand ] - type: entity parent: HighSecDoor id: HighSecCaptainLocked suffix: Captain, Locked components: - - type: AccessReader - access: [["Captain"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsCaptain ] - type: entity parent: HighSecDoor id: HighSecArmoryLocked suffix: Armory, Locked components: - - type: AccessReader - access: [["Armory"]] + - type: ContainerFill + containers: + board: [ DoorElectronicsArmory ] #Airtight hatch @@ -1255,6 +1195,6 @@ id: AirlockHatchSyndicate suffix: Syndicate, Locked components: - - type: AccessReader - access: [["SyndicateAgent"]] - + - type: ContainerFill + containers: + board: [ DoorElectronicsSyndicateAgent ] diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index abc86b2be8e..4ca7df6482e 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -48,6 +48,8 @@ - type: ContainerFill containers: board: [ DoorElectronics ] + - type: AccessReader + containerAccessProvider: board - type: Door crushDamage: types: @@ -140,7 +142,6 @@ - type: PaintableAirlock group: Standard department: Civilian - - type: AccessReader - type: StaticPrice price: 150 - type: LightningTarget diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml index a26226c9578..e9ea05a1c3f 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml @@ -60,6 +60,7 @@ - type: NavMapDoor - type: DoorBolt - type: AccessReader + containerAccessProvider: board - type: Appearance - type: WiresVisuals - type: ApcPowerReceiver diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/structures/airlock.yml b/Resources/Prototypes/Recipes/Construction/Graphs/structures/airlock.yml index 3cd22bbfc72..5529514fdcb 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/structures/airlock.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/structures/airlock.yml @@ -46,7 +46,7 @@ conditions: - !type:EntityAnchored {} steps: - - tag: DoorElectronics + - component: DoorElectronics store: board name: "door electronics circuit board" icon: diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/structures/shutter.yml b/Resources/Prototypes/Recipes/Construction/Graphs/structures/shutter.yml index 70d1c12d845..db53de288a4 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/structures/shutter.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/structures/shutter.yml @@ -44,7 +44,7 @@ - !type:EntityAnchored anchored: true steps: - - tag: DoorElectronics + - component: DoorElectronics name: Door Electronics icon: sprite: "Objects/Misc/module.rsi" diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/structures/shuttle.yml b/Resources/Prototypes/Recipes/Construction/Graphs/structures/shuttle.yml index 457fbfede56..f29629043df 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/structures/shuttle.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/structures/shuttle.yml @@ -23,7 +23,7 @@ conditions: - !type:EntityAnchored {} steps: - - tag: DoorElectronics + - component: DoorElectronics store: board name: "door electronics circuit board" icon: diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/structures/windoor.yml b/Resources/Prototypes/Recipes/Construction/Graphs/structures/windoor.yml index 64809ee1dbc..de896634670 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/structures/windoor.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/structures/windoor.yml @@ -107,7 +107,7 @@ conditions: - !type:EntityAnchored {} steps: - - tag: DoorElectronics + - component: DoorElectronics store: board name: "door electronics circuit board" icon: @@ -378,7 +378,7 @@ conditions: - !type:EntityAnchored { } steps: - - tag: DoorElectronics + - component: DoorElectronics store: board name: "door electronics circuit board" icon: From d9b6e9f1276ac0e589b95e664e371675feb498da Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 06:07:20 +0000 Subject: [PATCH 110/133] 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 bccb6798b4f..946856290aa 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Krunk - changes: - - message: Paper cups can now be worn as party hats! - type: Add - id: 5784 - time: '2024-01-25T01:02:33.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24334 - author: Blackern5000 changes: - message: Botany's hatchet, spade, and hoe have received a damage decrease to 10, @@ -3807,3 +3800,11 @@ id: 6283 time: '2024-04-01T04:50:00.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/23240 +- author: kseandi + changes: + - message: NT has declassified the documentation for door electronics, now anyone + can configure its access using network configurator or multitool. + type: Add + id: 6284 + time: '2024-04-01T06:06:14.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/17778 From 1db178b63254c5b509a6493a631fddb596b376a4 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Mon, 1 Apr 2024 06:27:39 +0000 Subject: [PATCH 111/133] scoopable ash and foam, solution transfer prediction (#25832) * move SolutionTransfer to shared and predict as much as possible * fully move OpenableSystem to shared now that SolutionTransfer is * fix imports for everything * doc for solution transfer system * trolling * add scoopable system * make ash and foam scoopable * untroll * untroll real * make clickable it work * troll * the scooping room --------- Co-authored-by: deltanedas <@deltanedas:kde.org> Co-authored-by: metalgearsloth --- .../Nutrition/EntitySystems/OpenableSystem.cs | 7 - .../EntitySystems/ReagentDispenserSystem.cs | 3 +- .../EntitySystems/SolutionTransferSystem.cs | 234 ------------------ .../Thresholds/Behaviors/OpenBehavior.cs | 2 +- .../Extinguisher/FireExtinguisherSystem.cs | 1 + .../EntitySystems/PuddleSystem.Spillable.cs | 3 +- Content.Server/Glue/GlueSystem.cs | 2 +- Content.Server/Lube/LubeSystem.cs | 2 +- .../Materials/MaterialReclaimerSystem.cs | 4 +- .../NPC/Systems/NPCUtilitySystem.cs | 1 + .../Nutrition/EntitySystems/DrinkSystem.cs | 1 + .../Nutrition/EntitySystems/FoodSystem.cs | 1 + .../Nutrition/EntitySystems/OpenableSystem.cs | 27 -- .../Components/ScoopableSolutionComponent.cs | 31 +++ .../EntitySystems/ScoopableSolutionSystem.cs | 53 ++++ .../EntitySystems/SolutionTransferSystem.cs | 223 +++++++++++++++++ .../Fluids/SharedPuddleSystem.Spillable.cs | 2 +- .../Nutrition/Components/OpenableComponent.cs | 2 +- ...redOpenableSystem.cs => OpenableSystem.cs} | 13 +- .../Nutrition/EntitySystems/SealableSystem.cs | 2 +- .../components/scoopable-component.ftl | 1 + .../Entities/Effects/chemistry_effects.yml | 3 + .../Objects/Specific/Medical/morgue.yml | 2 + 23 files changed, 341 insertions(+), 279 deletions(-) delete mode 100644 Content.Client/Nutrition/EntitySystems/OpenableSystem.cs delete mode 100644 Content.Server/Chemistry/EntitySystems/SolutionTransferSystem.cs delete mode 100644 Content.Server/Nutrition/EntitySystems/OpenableSystem.cs create mode 100644 Content.Shared/Chemistry/Components/ScoopableSolutionComponent.cs create mode 100644 Content.Shared/Chemistry/EntitySystems/ScoopableSolutionSystem.cs create mode 100644 Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs rename Content.Shared/Nutrition/EntitySystems/{SharedOpenableSystem.cs => OpenableSystem.cs} (91%) create mode 100644 Resources/Locale/en-US/chemistry/components/scoopable-component.ftl diff --git a/Content.Client/Nutrition/EntitySystems/OpenableSystem.cs b/Content.Client/Nutrition/EntitySystems/OpenableSystem.cs deleted file mode 100644 index f8c3f7c447f..00000000000 --- a/Content.Client/Nutrition/EntitySystems/OpenableSystem.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Content.Shared.Nutrition.EntitySystems; - -namespace Content.Client.Nutrition.EntitySystems; - -public sealed class OpenableSystem : SharedOpenableSystem -{ -} diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs index a8583e6bcb3..d6433da56a0 100644 --- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs @@ -1,11 +1,12 @@ using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Nutrition.EntitySystems; +using Content.Server.Nutrition.Components; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Dispenser; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Containers.ItemSlots; using Content.Shared.FixedPoint; +using Content.Shared.Nutrition.EntitySystems; using JetBrains.Annotations; using Robust.Server.Audio; using Robust.Server.GameObjects; diff --git a/Content.Server/Chemistry/EntitySystems/SolutionTransferSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionTransferSystem.cs deleted file mode 100644 index 1ed5cec8dd8..00000000000 --- a/Content.Server/Chemistry/EntitySystems/SolutionTransferSystem.cs +++ /dev/null @@ -1,234 +0,0 @@ -using Content.Server.Administration.Logs; -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Shared.Chemistry; -using Content.Shared.Chemistry.Components; -using Content.Shared.Database; -using Content.Shared.FixedPoint; -using Content.Shared.Interaction; -using Content.Shared.Popups; -using Content.Shared.Verbs; -using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.Player; - -namespace Content.Server.Chemistry.EntitySystems -{ - [UsedImplicitly] - public sealed class SolutionTransferSystem : EntitySystem - { - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; - [Dependency] private readonly IAdminLogManager _adminLogger = default!; - - /// - /// Default transfer amounts for the set-transfer verb. - /// - public static readonly List DefaultTransferAmounts = new() { 1, 5, 10, 25, 50, 100, 250, 500, 1000 }; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent>(AddSetTransferVerbs); - SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent(OnTransferAmountSetValueMessage); - } - - private void OnTransferAmountSetValueMessage(Entity entity, ref TransferAmountSetValueMessage message) - { - var newTransferAmount = FixedPoint2.Clamp(message.Value, entity.Comp.MinimumTransferAmount, entity.Comp.MaximumTransferAmount); - entity.Comp.TransferAmount = newTransferAmount; - - if (message.Session.AttachedEntity is { Valid: true } user) - _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", newTransferAmount)), entity.Owner, user); - } - - private void AddSetTransferVerbs(Entity entity, ref GetVerbsEvent args) - { - var (uid, component) = entity; - - if (!args.CanAccess || !args.CanInteract || !component.CanChangeTransferAmount || args.Hands == null) - return; - - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) - return; - - // Custom transfer verb - AlternativeVerb custom = new(); - custom.Text = Loc.GetString("comp-solution-transfer-verb-custom-amount"); - custom.Category = VerbCategory.SetTransferAmount; - custom.Act = () => _userInterfaceSystem.TryOpen(uid, TransferAmountUiKey.Key, actor.PlayerSession); - custom.Priority = 1; - args.Verbs.Add(custom); - - // Add specific transfer verbs according to the container's size - var priority = 0; - var user = args.User; - foreach (var amount in DefaultTransferAmounts) - { - if (amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int()) - continue; - - AlternativeVerb verb = new(); - verb.Text = Loc.GetString("comp-solution-transfer-verb-amount", ("amount", amount)); - verb.Category = VerbCategory.SetTransferAmount; - verb.Act = () => - { - component.TransferAmount = FixedPoint2.New(amount); - _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), uid, user); - }; - - // we want to sort by size, not alphabetically by the verb text. - verb.Priority = priority; - priority--; - - args.Verbs.Add(verb); - } - } - - private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) - { - if (!args.CanReach || args.Target == null) - return; - - var target = args.Target!.Value; - var (uid, component) = entity; - - //Special case for reagent tanks, because normally clicking another container will give solution, not take it. - if (component.CanReceive && !EntityManager.HasComponent(target) // target must not be refillable (e.g. Reagent Tanks) - && _solutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out _) // target must be drainable - && EntityManager.TryGetComponent(uid, out RefillableSolutionComponent? refillComp) - && _solutionContainerSystem.TryGetRefillableSolution((uid, refillComp, null), out var ownerSoln, out var ownerRefill)) - - { - - var transferAmount = component.TransferAmount; // This is the player-configurable transfer amount of "uid," not the target reagent tank. - - if (EntityManager.TryGetComponent(uid, out RefillableSolutionComponent? refill) && refill.MaxRefill != null) // uid is the entity receiving solution from target. - { - transferAmount = FixedPoint2.Min(transferAmount, (FixedPoint2) refill.MaxRefill); // if the receiver has a smaller transfer limit, use that instead - } - - var transferred = Transfer(args.User, target, targetSoln.Value, uid, ownerSoln.Value, transferAmount); - if (transferred > 0) - { - var toTheBrim = ownerRefill.AvailableVolume == 0; - var msg = toTheBrim - ? "comp-solution-transfer-fill-fully" - : "comp-solution-transfer-fill-normal"; - - _popupSystem.PopupEntity(Loc.GetString(msg, ("owner", args.Target), ("amount", transferred), ("target", uid)), uid, args.User); - - args.Handled = true; - return; - } - } - - // if target is refillable, and owner is drainable - if (component.CanSend && _solutionContainerSystem.TryGetRefillableSolution(target, out targetSoln, out var targetRefill) - && _solutionContainerSystem.TryGetDrainableSolution(uid, out ownerSoln, out var ownerDrain)) - { - var transferAmount = component.TransferAmount; - - if (EntityManager.TryGetComponent(target, out RefillableSolutionComponent? refill) && refill.MaxRefill != null) - { - transferAmount = FixedPoint2.Min(transferAmount, (FixedPoint2) refill.MaxRefill); - } - - var transferred = Transfer(args.User, uid, ownerSoln.Value, target, targetSoln.Value, transferAmount); - - if (transferred > 0) - { - var message = Loc.GetString("comp-solution-transfer-transfer-solution", ("amount", transferred), ("target", target)); - _popupSystem.PopupEntity(message, uid, args.User); - - args.Handled = true; - } - } - } - - /// - /// Transfer from a solution to another. - /// - /// The actual amount transferred. - public FixedPoint2 Transfer(EntityUid user, - EntityUid sourceEntity, - Entity source, - EntityUid targetEntity, - Entity target, - FixedPoint2 amount) - { - var transferAttempt = new SolutionTransferAttemptEvent(sourceEntity, targetEntity); - - // Check if the source is cancelling the transfer - RaiseLocalEvent(sourceEntity, transferAttempt, broadcast: true); - if (transferAttempt.Cancelled) - { - _popupSystem.PopupEntity(transferAttempt.CancelReason!, sourceEntity, user); - return FixedPoint2.Zero; - } - - var sourceSolution = source.Comp.Solution; - if (sourceSolution.Volume == 0) - { - _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-is-empty", ("target", sourceEntity)), sourceEntity, user); - return FixedPoint2.Zero; - } - - // Check if the target is cancelling the transfer - RaiseLocalEvent(targetEntity, transferAttempt, broadcast: true); - if (transferAttempt.Cancelled) - { - _popupSystem.PopupEntity(transferAttempt.CancelReason!, sourceEntity, user); - return FixedPoint2.Zero; - } - - var targetSolution = target.Comp.Solution; - if (targetSolution.AvailableVolume == 0) - { - _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-is-full", ("target", targetEntity)), targetEntity, user); - return FixedPoint2.Zero; - } - - var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(sourceSolution.Volume, targetSolution.AvailableVolume)); - - var solution = _solutionContainerSystem.Drain(sourceEntity, source, actualAmount); - _solutionContainerSystem.Refill(targetEntity, target, solution); - - _adminLogger.Add(LogType.Action, LogImpact.Medium, - $"{EntityManager.ToPrettyString(user):player} transferred {string.Join(", ", solution.Contents)} to {EntityManager.ToPrettyString(targetEntity):entity}, which now contains {SolutionContainerSystem.ToPrettyString(targetSolution)}"); - - return actualAmount; - } - } - - /// - /// Raised when attempting to transfer from one solution to another. - /// - public sealed class SolutionTransferAttemptEvent : CancellableEntityEventArgs - { - public SolutionTransferAttemptEvent(EntityUid from, EntityUid to) - { - From = from; - To = to; - } - - public EntityUid From { get; } - public EntityUid To { get; } - - /// - /// Why the transfer has been cancelled. - /// - public string? CancelReason { get; private set; } - - /// - /// Cancels the transfer. - /// - public void Cancel(string reason) - { - base.Cancel(); - CancelReason = reason; - } - } -} diff --git a/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs index f01e4f7048e..7ab1fe11b01 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs @@ -1,4 +1,4 @@ -using Content.Server.Nutrition.EntitySystems; +using Content.Shared.Nutrition.EntitySystems; namespace Content.Server.Destructible.Thresholds.Behaviors; diff --git a/Content.Server/Extinguisher/FireExtinguisherSystem.cs b/Content.Server/Extinguisher/FireExtinguisherSystem.cs index dfecd72398d..b33a1af157f 100644 --- a/Content.Server/Extinguisher/FireExtinguisherSystem.cs +++ b/Content.Server/Extinguisher/FireExtinguisherSystem.cs @@ -73,6 +73,7 @@ private void OnAfterInteract(Entity entity, ref After args.Handled = true; + // TODO: why is this copy paste shit here just have fire extinguisher cancel transfer when safety is on var transfer = containerSolution.AvailableVolume; if (TryComp(entity.Owner, out var solTrans)) { diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs index a365b8d0a45..bd7c55e85ea 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs @@ -1,5 +1,5 @@ using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Nutrition.EntitySystems; +using Content.Server.Fluids.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; @@ -11,6 +11,7 @@ using Content.Shared.Fluids.Components; using Content.Shared.IdentityManagement; using Content.Shared.Inventory.Events; +using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Popups; using Content.Shared.Spillable; using Content.Shared.Throwing; diff --git a/Content.Server/Glue/GlueSystem.cs b/Content.Server/Glue/GlueSystem.cs index 44ff4e54593..ff53ef91cac 100644 --- a/Content.Server/Glue/GlueSystem.cs +++ b/Content.Server/Glue/GlueSystem.cs @@ -1,12 +1,12 @@ using Content.Server.Administration.Logs; using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Nutrition.EntitySystems; using Content.Shared.Database; using Content.Shared.Glue; using Content.Shared.Hands; using Content.Shared.Interaction; using Content.Shared.Interaction.Components; using Content.Shared.Item; +using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Popups; using Content.Shared.Verbs; using Robust.Shared.Audio.Systems; diff --git a/Content.Server/Lube/LubeSystem.cs b/Content.Server/Lube/LubeSystem.cs index 5285cb389c5..06d6456a57c 100644 --- a/Content.Server/Lube/LubeSystem.cs +++ b/Content.Server/Lube/LubeSystem.cs @@ -1,12 +1,12 @@ using Content.Server.Administration.Logs; using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Nutrition.EntitySystems; using Content.Shared.Database; using Content.Shared.Glue; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Item; using Content.Shared.Lube; +using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Popups; using Content.Shared.Verbs; using Robust.Shared.Audio; diff --git a/Content.Server/Materials/MaterialReclaimerSystem.cs b/Content.Server/Materials/MaterialReclaimerSystem.cs index ae4444e059b..0d6d27777a4 100644 --- a/Content.Server/Materials/MaterialReclaimerSystem.cs +++ b/Content.Server/Materials/MaterialReclaimerSystem.cs @@ -1,8 +1,6 @@ using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Chemistry.EntitySystems; using Content.Server.Fluids.EntitySystems; using Content.Server.GameTicking; -using Content.Server.Nutrition.EntitySystems; using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Stack; @@ -10,11 +8,13 @@ using Content.Shared.Body.Systems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Materials; using Content.Shared.Mind; +using Content.Shared.Nutrition.EntitySystems; using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Utility; diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 6793161105e..e8fb54022ee 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -14,6 +14,7 @@ using Content.Shared.Mobs.Systems; using Content.Shared.NPC.Systems; using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Tools.Systems; using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Ranged.Components; diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index 036c855dbba..74637d48137 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -24,6 +24,7 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Nutrition; using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Throwing; using Content.Shared.Verbs; using Robust.Shared.Audio; diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index d87b0bd0b02..49d73740412 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -23,6 +23,7 @@ using Content.Shared.Inventory; using Content.Shared.Mobs.Systems; using Content.Shared.Nutrition; +using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Stacks; using Content.Shared.Storage; using Content.Shared.Verbs; diff --git a/Content.Server/Nutrition/EntitySystems/OpenableSystem.cs b/Content.Server/Nutrition/EntitySystems/OpenableSystem.cs deleted file mode 100644 index 8037b61572a..00000000000 --- a/Content.Server/Nutrition/EntitySystems/OpenableSystem.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Content.Server.Chemistry.EntitySystems; -using Content.Shared.Nutrition.EntitySystems; -using Content.Shared.Nutrition.Components; - -namespace Content.Server.Nutrition.EntitySystems; - -/// -/// Provides API for openable food and drinks, handles opening on use and preventing transfer when closed. -/// -public sealed class OpenableSystem : SharedOpenableSystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnTransferAttempt); - } - - private void OnTransferAttempt(EntityUid uid, OpenableComponent comp, SolutionTransferAttemptEvent args) - { - if (!comp.Opened) - { - // message says its just for drinks, shouldn't matter since you typically dont have a food that is openable and can be poured out - args.Cancel(Loc.GetString("drink-component-try-use-drink-not-open", ("owner", uid))); - } - } -} diff --git a/Content.Shared/Chemistry/Components/ScoopableSolutionComponent.cs b/Content.Shared/Chemistry/Components/ScoopableSolutionComponent.cs new file mode 100644 index 00000000000..6c3f934b7a8 --- /dev/null +++ b/Content.Shared/Chemistry/Components/ScoopableSolutionComponent.cs @@ -0,0 +1,31 @@ +using Content.Shared.Chemistry.EntitySystems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Chemistry.Components; + +/// +/// Basically reverse spiking, instead of using the solution-entity on a beaker, you use the beaker on the solution-entity. +/// If there is not enough volume it will stay in the solution-entity rather than spill onto the floor. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(ScoopableSolutionSystem))] +public sealed partial class ScoopableSolutionComponent : Component +{ + /// + /// Solution name that can be scooped from. + /// + [DataField] + public string Solution = "default"; + + /// + /// If true, when the whole solution is scooped up the entity will be deleted. + /// + [DataField] + public bool Delete = true; + + /// + /// Popup to show the user when scooping. + /// Passed entities "scooped" and "beaker". + /// + [DataField] + public LocId Popup = "scoopable-component-popup"; +} diff --git a/Content.Shared/Chemistry/EntitySystems/ScoopableSolutionSystem.cs b/Content.Shared/Chemistry/EntitySystems/ScoopableSolutionSystem.cs new file mode 100644 index 00000000000..84f1e456160 --- /dev/null +++ b/Content.Shared/Chemistry/EntitySystems/ScoopableSolutionSystem.cs @@ -0,0 +1,53 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Robust.Shared.Network; + +namespace Content.Shared.Chemistry.EntitySystems; + +/// +/// Handles solution transfer when a beaker is used on a scoopable entity. +/// +public sealed class ScoopableSolutionSystem : EntitySystem +{ + [Dependency] private readonly INetManager _netManager = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; + [Dependency] private readonly SolutionTransferSystem _solutionTransfer = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInteractUsing); + } + + private void OnInteractUsing(Entity ent, ref InteractUsingEvent args) + { + TryScoop(ent, args.Used, args.User); + } + + public bool TryScoop(Entity ent, EntityUid beaker, EntityUid user) + { + if (!_solution.TryGetSolution(ent.Owner, ent.Comp.Solution, out var src, out var srcSolution) || + !_solution.TryGetRefillableSolution(beaker, out var target, out _)) + return false; + + var scooped = _solutionTransfer.Transfer(user, ent, src.Value, beaker, target.Value, srcSolution.Volume); + if (scooped == 0) + return false; + + _popup.PopupClient(Loc.GetString(ent.Comp.Popup, ("scooped", ent.Owner), ("beaker", beaker)), user, user); + + if (srcSolution.Volume == 0 && ent.Comp.Delete) + { + // deletion isnt predicted so do this to prevent spam clicking to see "the ash is empty!" + RemCompDeferred(ent); + + if (!_netManager.IsClient) + QueueDel(ent); + } + + return true; + } +} diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs new file mode 100644 index 00000000000..34a64d0edbd --- /dev/null +++ b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs @@ -0,0 +1,223 @@ +using Content.Shared.Administration.Logs; +using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; +using Content.Shared.Database; +using Content.Shared.FixedPoint; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Robust.Shared.Network; +using Robust.Shared.Player; + +namespace Content.Shared.Chemistry.EntitySystems; + +/// +/// Allows an entity to transfer solutions with a customizable amount per click. +/// Also provides API for other systems. +/// +public sealed class SolutionTransferSystem : EntitySystem +{ + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + + /// + /// Default transfer amounts for the set-transfer verb. + /// + public static readonly FixedPoint2[] DefaultTransferAmounts = new FixedPoint2[] { 1, 5, 10, 25, 50, 100, 250, 500, 1000 }; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(AddSetTransferVerbs); + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnTransferAmountSetValueMessage); + } + + private void OnTransferAmountSetValueMessage(Entity ent, ref TransferAmountSetValueMessage message) + { + var newTransferAmount = FixedPoint2.Clamp(message.Value, ent.Comp.MinimumTransferAmount, ent.Comp.MaximumTransferAmount); + ent.Comp.TransferAmount = newTransferAmount; + + if (message.Session.AttachedEntity is { Valid: true } user) + _popup.PopupClient(Loc.GetString("comp-solution-transfer-set-amount", ("amount", newTransferAmount)), ent, user); + } + + private void AddSetTransferVerbs(Entity ent, ref GetVerbsEvent args) + { + var (uid, comp) = ent; + + if (!args.CanAccess || !args.CanInteract || !comp.CanChangeTransferAmount || args.Hands == null) + return; + + if (!TryComp(args.User, out var actor)) + return; + + // Custom transfer verb + args.Verbs.Add(new AlternativeVerb() + { + Text = Loc.GetString("comp-solution-transfer-verb-custom-amount"), + Category = VerbCategory.SetTransferAmount, + // TODO: remove server check when bui prediction is a thing + Act = () => + { + if (_net.IsServer) + _ui.TryOpen(uid, TransferAmountUiKey.Key, actor.PlayerSession); + }, + Priority = 1 + }); + + // Add specific transfer verbs according to the container's size + var priority = 0; + var user = args.User; + foreach (var amount in DefaultTransferAmounts) + { + AlternativeVerb verb = new(); + verb.Text = Loc.GetString("comp-solution-transfer-verb-amount", ("amount", amount)); + verb.Category = VerbCategory.SetTransferAmount; + verb.Act = () => + { + comp.TransferAmount = amount; + _popup.PopupClient(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), uid, user); + }; + + // we want to sort by size, not alphabetically by the verb text. + verb.Priority = priority; + priority--; + + args.Verbs.Add(verb); + } + } + + private void OnAfterInteract(Entity ent, ref AfterInteractEvent args) + { + if (!args.CanReach || args.Target is not {} target) + return; + + var (uid, comp) = ent; + + //Special case for reagent tanks, because normally clicking another container will give solution, not take it. + if (comp.CanReceive + && !HasComp(target) // target must not be refillable (e.g. Reagent Tanks) + && _solution.TryGetDrainableSolution(target, out var targetSoln, out _) // target must be drainable + && TryComp(uid, out var refill) + && _solution.TryGetRefillableSolution((uid, refill, null), out var ownerSoln, out var ownerRefill)) + { + var transferAmount = comp.TransferAmount; // This is the player-configurable transfer amount of "uid," not the target reagent tank. + + // if the receiver has a smaller transfer limit, use that instead + if (refill?.MaxRefill is {} maxRefill) + transferAmount = FixedPoint2.Min(transferAmount, maxRefill); + + var transferred = Transfer(args.User, target, targetSoln.Value, uid, ownerSoln.Value, transferAmount); + if (transferred > 0) + { + var toTheBrim = ownerRefill.AvailableVolume == 0; + var msg = toTheBrim + ? "comp-solution-transfer-fill-fully" + : "comp-solution-transfer-fill-normal"; + + _popup.PopupClient(Loc.GetString(msg, ("owner", args.Target), ("amount", transferred), ("target", uid)), uid, args.User); + + args.Handled = true; + return; + } + } + + // if target is refillable, and owner is drainable + if (comp.CanSend + && TryComp(target, out var targetRefill) + && _solution.TryGetRefillableSolution((target, targetRefill, null), out targetSoln, out _) + && _solution.TryGetDrainableSolution(uid, out ownerSoln, out _)) + { + var transferAmount = comp.TransferAmount; + + if (targetRefill?.MaxRefill is {} maxRefill) + transferAmount = FixedPoint2.Min(transferAmount, maxRefill); + + var transferred = Transfer(args.User, uid, ownerSoln.Value, target, targetSoln.Value, transferAmount); + + if (transferred > 0) + { + var message = Loc.GetString("comp-solution-transfer-transfer-solution", ("amount", transferred), ("target", target)); + _popup.PopupClient(message, uid, args.User); + + args.Handled = true; + } + } + } + + /// + /// Transfer from a solution to another, allowing either entity to cancel it and show a popup. + /// + /// The actual amount transferred. + public FixedPoint2 Transfer(EntityUid user, + EntityUid sourceEntity, + Entity source, + EntityUid targetEntity, + Entity target, + FixedPoint2 amount) + { + var transferAttempt = new SolutionTransferAttemptEvent(sourceEntity, targetEntity); + + // Check if the source is cancelling the transfer + RaiseLocalEvent(sourceEntity, ref transferAttempt); + if (transferAttempt.CancelReason is {} reason) + { + _popup.PopupClient(reason, sourceEntity, user); + return FixedPoint2.Zero; + } + + var sourceSolution = source.Comp.Solution; + if (sourceSolution.Volume == 0) + { + _popup.PopupClient(Loc.GetString("comp-solution-transfer-is-empty", ("target", sourceEntity)), sourceEntity, user); + return FixedPoint2.Zero; + } + + // Check if the target is cancelling the transfer + RaiseLocalEvent(targetEntity, ref transferAttempt); + if (transferAttempt.CancelReason is {} targetReason) + { + _popup.PopupClient(targetReason, targetEntity, user); + return FixedPoint2.Zero; + } + + var targetSolution = target.Comp.Solution; + if (targetSolution.AvailableVolume == 0) + { + _popup.PopupClient(Loc.GetString("comp-solution-transfer-is-full", ("target", targetEntity)), targetEntity, user); + return FixedPoint2.Zero; + } + + var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(sourceSolution.Volume, targetSolution.AvailableVolume)); + + var solution = _solution.SplitSolution(source, actualAmount); + _solution.Refill(targetEntity, target, solution); + + _adminLogger.Add(LogType.Action, LogImpact.Medium, + $"{ToPrettyString(user):player} transferred {SharedSolutionContainerSystem.ToPrettyString(solution)} to {ToPrettyString(targetEntity):target}, which now contains {SharedSolutionContainerSystem.ToPrettyString(targetSolution)}"); + + return actualAmount; + } +} + +/// +/// Raised when attempting to transfer from one solution to another. +/// Raised on both the source and target entities so either can cancel the transfer. +/// To not mispredict this should always be cancelled in shared code and not server or client. +/// +[ByRefEvent] +public record struct SolutionTransferAttemptEvent(EntityUid From, EntityUid To, string? CancelReason = null) +{ + /// + /// Cancels the transfer. + /// + public void Cancel(string reason) + { + CancelReason = reason; + } +} diff --git a/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs b/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs index 92ea9621401..f88f13e8b0d 100644 --- a/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs +++ b/Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs @@ -13,7 +13,7 @@ namespace Content.Shared.Fluids; public abstract partial class SharedPuddleSystem { - [Dependency] protected readonly SharedOpenableSystem Openable = default!; + [Dependency] protected readonly OpenableSystem Openable = default!; protected virtual void InitializeSpillable() { diff --git a/Content.Shared/Nutrition/Components/OpenableComponent.cs b/Content.Shared/Nutrition/Components/OpenableComponent.cs index 3a230fc765d..0381888e282 100644 --- a/Content.Shared/Nutrition/Components/OpenableComponent.cs +++ b/Content.Shared/Nutrition/Components/OpenableComponent.cs @@ -9,7 +9,7 @@ namespace Content.Shared.Nutrition.Components; /// Starts closed, open it with Z or E. /// [NetworkedComponent, AutoGenerateComponentState] -[RegisterComponent, Access(typeof(SharedOpenableSystem))] +[RegisterComponent, Access(typeof(OpenableSystem))] public sealed partial class OpenableComponent : Component { /// diff --git a/Content.Shared/Nutrition/EntitySystems/SharedOpenableSystem.cs b/Content.Shared/Nutrition/EntitySystems/OpenableSystem.cs similarity index 91% rename from Content.Shared/Nutrition/EntitySystems/SharedOpenableSystem.cs rename to Content.Shared/Nutrition/EntitySystems/OpenableSystem.cs index f3b1127578d..0ad0877d222 100644 --- a/Content.Shared/Nutrition/EntitySystems/SharedOpenableSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/OpenableSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; @@ -13,7 +14,7 @@ namespace Content.Shared.Nutrition.EntitySystems; /// /// Provides API for openable food and drinks, handles opening on use and preventing transfer when closed. /// -public abstract partial class SharedOpenableSystem : EntitySystem +public sealed partial class OpenableSystem : EntitySystem { [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; @@ -29,6 +30,7 @@ public override void Initialize() SubscribeLocalEvent(HandleIfClosed); SubscribeLocalEvent(HandleIfClosed); SubscribeLocalEvent>(AddOpenCloseVerbs); + SubscribeLocalEvent(OnTransferAttempt); } private void OnInit(EntityUid uid, OpenableComponent comp, ComponentInit args) @@ -89,6 +91,15 @@ private void AddOpenCloseVerbs(EntityUid uid, OpenableComponent comp, GetVerbsEv args.Verbs.Add(verb); } + private void OnTransferAttempt(Entity ent, ref SolutionTransferAttemptEvent args) + { + if (!ent.Comp.Opened) + { + // message says its just for drinks, shouldn't matter since you typically dont have a food that is openable and can be poured out + args.Cancel(Loc.GetString("drink-component-try-use-drink-not-open", ("owner", ent.Owner))); + } + } + /// /// Returns true if the entity either does not have OpenableComponent or it is opened. /// Drinks that don't have OpenableComponent are automatically open, so it returns true. diff --git a/Content.Shared/Nutrition/EntitySystems/SealableSystem.cs b/Content.Shared/Nutrition/EntitySystems/SealableSystem.cs index b0873f23a12..414b8d182b0 100644 --- a/Content.Shared/Nutrition/EntitySystems/SealableSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/SealableSystem.cs @@ -11,7 +11,7 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnExamined, after: new[] { typeof(SharedOpenableSystem) }); + SubscribeLocalEvent(OnExamined, after: new[] { typeof(OpenableSystem) }); SubscribeLocalEvent(OnOpened); } diff --git a/Resources/Locale/en-US/chemistry/components/scoopable-component.ftl b/Resources/Locale/en-US/chemistry/components/scoopable-component.ftl new file mode 100644 index 00000000000..c2593cc61e3 --- /dev/null +++ b/Resources/Locale/en-US/chemistry/components/scoopable-component.ftl @@ -0,0 +1 @@ +scoopable-component-popup = You scoop up {$scooped} into {THE($beaker)}. diff --git a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml index 739464e9611..5b7ee46946e 100644 --- a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml +++ b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml @@ -9,6 +9,7 @@ - type: SmokeVisuals - type: Transform anchored: true + - type: Clickable - type: Physics - type: Fixtures fixtures: @@ -76,6 +77,8 @@ animationState: foam-dissolve - type: Slippery - type: StepTrigger + - type: ScoopableSolution + solution: solutionArea - type: entity id: MetalFoam diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml index fba12bebec4..32aa114429d 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml @@ -120,6 +120,8 @@ - type: SolutionSpiker sourceSolution: food ignoreEmpty: true + - type: ScoopableSolution + solution: food - type: Extractable grindableSolutionName: food From 2ffd616c41b548e8fcd869dca7e980ffc6e384b1 Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Mon, 1 Apr 2024 06:31:36 +0000 Subject: [PATCH 112/133] Replace the teleportation logic on the SCRAM implant! (#26429) * Replace the teleportation logic on the SCRAM implant! Now instead of just trying to pick a random tile in range 20 times, the scram teleportation logic now: - Gets a list of grids in range - Until a suitable tile is picked it picks a random grid - From that grid it picks a random tile. - If the tile is suitable, then it is set as the target and the user will be teleported there. - Grids and tiles are randomly picked as outlined above until a valid tile is found, or all valid grids and tiles are exhausted. - Should no suitable tile be found then they get teleported to the same position they are at. Effectively not teleporting them. * Actually make the defaults sane which I forgor in the last commit * Extract tile section to its own function. Bias selection for current grid. Use proper coords for box. * Address reviews as much as possible * Address reviews --- .../Components/ScramImplantComponent.cs | 6 - .../Implants/SubdermalImplantSystem.cs | 110 +++++++++++++----- 2 files changed, 82 insertions(+), 34 deletions(-) diff --git a/Content.Server/Implants/Components/ScramImplantComponent.cs b/Content.Server/Implants/Components/ScramImplantComponent.cs index 88c433abfbe..f3bbc9e5842 100644 --- a/Content.Server/Implants/Components/ScramImplantComponent.cs +++ b/Content.Server/Implants/Components/ScramImplantComponent.cs @@ -15,12 +15,6 @@ public sealed partial class ScramImplantComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public float TeleportRadius = 100f; - /// - /// How many times to check for a valid tile to teleport to - /// - [DataField, ViewVariables(VVAccess.ReadOnly)] - public int TeleportAttempts = 20; - [DataField, ViewVariables(VVAccess.ReadWrite)] public SoundSpecifier TeleportSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg"); } diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index fd95720732b..e8af08b2ebb 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -14,13 +14,14 @@ using Content.Shared.Preferences; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; -using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Random; using System.Numerics; using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Systems; +using Robust.Shared.Collections; +using Robust.Shared.Map.Components; namespace Content.Server.Implants; @@ -28,7 +29,6 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem { [Dependency] private readonly CuffableSystem _cuffable = default!; [Dependency] private readonly HumanoidAppearanceSystem _humanoidAppearance = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly StoreSystem _store = default!; @@ -37,8 +37,11 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] private readonly ForensicsSystem _forensicsSystem = default!; [Dependency] private readonly PullingSystem _pullingSystem = default!; + [Dependency] private readonly EntityLookupSystem _lookupSystem = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; private EntityQuery _physicsQuery; + private HashSet> _targetGrids = []; public override void Initialize() { @@ -107,41 +110,92 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, _pullingSystem.TryStopPull(ent, pull); var xform = Transform(ent); - var entityCoords = xform.Coordinates.ToMap(EntityManager, _xform); + var targetCoords = SelectRandomTileInRange(xform, implant.TeleportRadius); - // try to find a valid position to teleport to, teleport to whatever works if we can't - var targetCoords = new MapCoordinates(); - for (var i = 0; i < implant.TeleportAttempts; i++) + if (targetCoords != null) { - var distance = implant.TeleportRadius * MathF.Sqrt(_random.NextFloat()); // to get an uniform distribution - targetCoords = entityCoords.Offset(_random.NextAngle().ToVec() * distance); - - // prefer teleporting to grids - if (!_mapManager.TryFindGridAt(targetCoords, out var gridUid, out var grid)) - continue; + _xform.SetCoordinates(ent, targetCoords.Value); + _audio.PlayPvs(implant.TeleportSound, ent); + args.Handled = true; + } + } - // the implant user probably does not want to be in your walls - var valid = true; - foreach (var entity in grid.GetAnchoredEntities(targetCoords)) + private EntityCoordinates? SelectRandomTileInRange(TransformComponent userXform, float radius) + { + var userCoords = userXform.Coordinates.ToMap(EntityManager, _xform); + _targetGrids.Clear(); + _lookupSystem.GetEntitiesInRange(userCoords, radius, _targetGrids); + Entity? targetGrid = null; + + if (_targetGrids.Count == 0) + return null; + + // Give preference to the grid the entity is currently on. + // This does not guarantee that if the probability fails that the owner's grid won't be picked. + // In reality the probability is higher and depends on the number of grids. + if (userXform.GridUid != null && TryComp(userXform.GridUid, out var gridComp)) + { + var userGrid = new Entity(userXform.GridUid.Value, gridComp); + if (_random.Prob(0.5f)) { - if (!_physicsQuery.TryGetComponent(entity, out var body)) - continue; + _targetGrids.Remove(userGrid); + targetGrid = userGrid; + } + } - if (body.BodyType != BodyType.Static || - !body.Hard || - (body.CollisionLayer & (int) CollisionGroup.Impassable) == 0) - continue; + if (targetGrid == null) + targetGrid = _random.GetRandom().PickAndTake(_targetGrids); - valid = false; - break; + EntityCoordinates? targetCoords = null; + + do + { + var valid = false; + + var range = (float) Math.Sqrt(radius); + var box = Box2.CenteredAround(userCoords.Position, new Vector2(range, range)); + var tilesInRange = _mapSystem.GetTilesEnumerator(targetGrid.Value.Owner, targetGrid.Value.Comp, box, false); + var tileList = new ValueList(); + + while (tilesInRange.MoveNext(out var tile)) + { + tileList.Add(tile.GridIndices); + } + + while (tileList.Count != 0) + { + var tile = tileList.RemoveSwap(_random.Next(tileList.Count)); + valid = true; + foreach (var entity in _mapSystem.GetAnchoredEntities(targetGrid.Value.Owner, targetGrid.Value.Comp, + tile)) + { + if (!_physicsQuery.TryGetComponent(entity, out var body)) + continue; + + if (body.BodyType != BodyType.Static || + !body.Hard || + (body.CollisionLayer & (int) CollisionGroup.MobMask) == 0) + continue; + + valid = false; + break; + } + + if (valid) + { + targetCoords = new EntityCoordinates(targetGrid.Value.Owner, + _mapSystem.TileCenterToVector(targetGrid.Value, tile)); + break; + } } - if (valid) + + if (valid || _targetGrids.Count == 0) // if we don't do the check here then PickAndTake will blow up on an empty set. break; - } - _xform.SetWorldPosition(ent, targetCoords.Position); - _audio.PlayPvs(implant.TeleportSound, ent); - args.Handled = true; + targetGrid = _random.GetRandom().PickAndTake(_targetGrids); + } while (true); + + return targetCoords; } private void OnDnaScramblerImplant(EntityUid uid, SubdermalImplantComponent component, UseDnaScramblerImplantEvent args) From 5613c3d3ddc64c1fccb1bc591308a19fa2276e9d Mon Sep 17 00:00:00 2001 From: "Wrexbe (Josh)" <81056464+wrexbe@users.noreply.github.com> Date: Sun, 31 Mar 2024 23:36:31 -0700 Subject: [PATCH 113/133] Refactored AdvertiseComponent (#26598) * Made it better * ok * alright --------- Co-authored-by: wrexbe --- .../Components/AdvertiseComponent.cs | 6 - .../EntitySystems/AdvertiseSystem.cs | 110 ++++++------------ 2 files changed, 38 insertions(+), 78 deletions(-) diff --git a/Content.Server/Advertise/Components/AdvertiseComponent.cs b/Content.Server/Advertise/Components/AdvertiseComponent.cs index 140bc6b902a..531b31031d2 100644 --- a/Content.Server/Advertise/Components/AdvertiseComponent.cs +++ b/Content.Server/Advertise/Components/AdvertiseComponent.cs @@ -1,7 +1,6 @@ using Content.Server.Advertise.EntitySystems; using Content.Shared.Advertise; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Advertise.Components; @@ -37,9 +36,4 @@ public sealed partial class AdvertiseComponent : Component [DataField] public TimeSpan NextAdvertisementTime { get; set; } = TimeSpan.Zero; - /// - /// Whether the entity will say advertisements or not. - /// - [DataField] - public bool Enabled { get; set; } = true; } diff --git a/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs b/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs index b326321d546..12eac72cfe3 100644 --- a/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs +++ b/Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs @@ -28,112 +28,78 @@ public sealed class AdvertiseSystem : EntitySystem public override void Initialize() { SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnPowerChanged); - SubscribeLocalEvent(OnPowerReceiverEnableChangeAttempt); - SubscribeLocalEvent(OnVendingEnableChangeAttempt); + SubscribeLocalEvent(OnPowerReceiverAttemptAdvertiseEvent); + SubscribeLocalEvent(OnVendingAttemptAdvertiseEvent); - // Force it to check on the next update. _nextCheckTime = TimeSpan.MinValue; } - private void OnMapInit(EntityUid uid, AdvertiseComponent advertise, MapInitEvent args) + private void OnMapInit(EntityUid uid, AdvertiseComponent advert, MapInitEvent args) { - RefreshTimer(uid, advertise); + RandomizeNextAdvertTime(advert); + _nextCheckTime = MathHelper.Min(advert.NextAdvertisementTime, _nextCheckTime); } - private void OnPowerChanged(EntityUid uid, AdvertiseComponent advertise, ref PowerChangedEvent args) + private void RandomizeNextAdvertTime(AdvertiseComponent advert) { - SetEnabled(uid, args.Powered, advertise); - } - - public void RefreshTimer(EntityUid uid, AdvertiseComponent? advertise = null) - { - if (!Resolve(uid, ref advertise)) - return; - - if (!advertise.Enabled) - return; - - var minDuration = Math.Max(1, advertise.MinimumWait); - var maxDuration = Math.Max(minDuration, advertise.MaximumWait); + var minDuration = Math.Max(1, advert.MinimumWait); + var maxDuration = Math.Max(minDuration, advert.MaximumWait); var waitDuration = TimeSpan.FromSeconds(_random.Next(minDuration, maxDuration)); - var nextTime = _gameTiming.CurTime + waitDuration; - advertise.NextAdvertisementTime = nextTime; - - _nextCheckTime = MathHelper.Min(nextTime, _nextCheckTime); + advert.NextAdvertisementTime = _gameTiming.CurTime + waitDuration; } - public void SayAdvertisement(EntityUid uid, AdvertiseComponent? advertise = null) + public void SayAdvertisement(EntityUid uid, AdvertiseComponent? advert = null) { - if (!Resolve(uid, ref advertise)) + if (!Resolve(uid, ref advert)) return; - if (_prototypeManager.TryIndex(advertise.Pack, out var advertisements)) - _chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.Messages)), InGameICChatType.Speak, hideChat: true); - } - - public void SetEnabled(EntityUid uid, bool enable, AdvertiseComponent? advertise = null) - { - if (!Resolve(uid, ref advertise)) - return; - - if (advertise.Enabled == enable) - return; - - var attemptEvent = new AdvertiseEnableChangeAttemptEvent(enable); - RaiseLocalEvent(uid, attemptEvent); - + var attemptEvent = new AttemptAdvertiseEvent(uid); + RaiseLocalEvent(uid, ref attemptEvent); if (attemptEvent.Cancelled) return; - advertise.Enabled = enable; - RefreshTimer(uid, advertise); - } - - private static void OnPowerReceiverEnableChangeAttempt(EntityUid uid, ApcPowerReceiverComponent component, AdvertiseEnableChangeAttemptEvent args) - { - if (args.Enabling && !component.Powered) - args.Cancel(); - } - - private static void OnVendingEnableChangeAttempt(EntityUid uid, VendingMachineComponent component, AdvertiseEnableChangeAttemptEvent args) - { - if (args.Enabling && component.Broken) - args.Cancel(); + if (_prototypeManager.TryIndex(advert.Pack, out var advertisements)) + _chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.Messages)), InGameICChatType.Speak, hideChat: true); } public override void Update(float frameTime) { - var curTime = _gameTiming.CurTime; - if (_nextCheckTime > curTime) + var currentGameTime = _gameTiming.CurTime; + if (_nextCheckTime > currentGameTime) return; - // Note that as _nextCheckTime currently starts at TimeSpan.MinValue, so this has to SET the value, not just - // increment it. - _nextCheckTime = curTime + _maximumNextCheckDuration; + // _nextCheckTime starts at TimeSpan.MinValue, so this has to SET the value, not just increment it. + _nextCheckTime = currentGameTime + _maximumNextCheckDuration; var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var advert)) { - if (!advert.Enabled) - continue; - - // If this isn't advertising yet - if (advert.NextAdvertisementTime > curTime) + if (currentGameTime > advert.NextAdvertisementTime) { - _nextCheckTime = MathHelper.Min(advert.NextAdvertisementTime, _nextCheckTime); - continue; + SayAdvertisement(uid, advert); + // The timer is always refreshed when it expires, to prevent mass advertising (ex: all the vending machines have no power, and get it back at the same time). + RandomizeNextAdvertTime(advert); } - - SayAdvertisement(uid, advert); - RefreshTimer(uid, advert); + _nextCheckTime = MathHelper.Min(advert.NextAdvertisementTime, _nextCheckTime); } } + + + private static void OnPowerReceiverAttemptAdvertiseEvent(EntityUid uid, ApcPowerReceiverComponent powerReceiver, ref AttemptAdvertiseEvent args) + { + args.Cancelled |= !powerReceiver.Powered; + } + + private static void OnVendingAttemptAdvertiseEvent(EntityUid uid, VendingMachineComponent machine, ref AttemptAdvertiseEvent args) + { + args.Cancelled |= machine.Broken; + } } -public sealed class AdvertiseEnableChangeAttemptEvent(bool enabling) : CancellableEntityEventArgs +[ByRefEvent] +public record struct AttemptAdvertiseEvent(EntityUid? Advertiser) { - public bool Enabling { get; } = enabling; + public bool Cancelled = false; } From ea3e98e1208f21aa2a52b0a9938ee22e07b74ccd Mon Sep 17 00:00:00 2001 From: Firewatch <54725557+musicmanvr@users.noreply.github.com> Date: Mon, 1 Apr 2024 02:41:14 -0400 Subject: [PATCH 114/133] Bartender "Essentials" (#25367) * drinks round 1 saving my progress before my hard drive explodes * test 2 please work * name fixes whoops * Update drinks.yml * various fixes am dumb * add sol dry to vends more fixes and changes, yippee! * more fixes & ingame testing shrimple tests * last fixes :trollface: should be ready for pr now * Update soda.yml sate thirst --- .../Locale/en-US/flavors/flavor-profiles.ftl | 7 + .../meta/consumable/drink/alcohol.ftl | 15 ++ .../reagents/meta/consumable/drink/drinks.ftl | 13 +- .../reagents/meta/consumable/drink/soda.ftl | 9 + .../Catalog/ReagentDispensers/beverage.yml | 2 +- .../VendingMachines/Inventories/boozeomat.yml | 2 + .../VendingMachines/Inventories/cola.yml | 1 + .../VendingMachines/Inventories/gib.yml | 1 + .../VendingMachines/Inventories/pwrgame.yml | 1 + .../Inventories/shamblersjuice.yml | 1 + .../VendingMachines/Inventories/soda.yml | 1 + .../VendingMachines/Inventories/spaceup.yml | 1 + .../VendingMachines/Inventories/starkist.yml | 1 + .../Random/Food_Drinks/drinks_glass.yml | 8 + .../Random/Food_Drinks/drinks_soda.yml | 1 + .../Consumable/Drinks/drinks-cartons.yml | 18 ++ .../Objects/Consumable/Drinks/drinks.yml | 157 ++++++++++++++++++ .../Consumable/Drinks/drinks_bottles.yml | 17 ++ .../Objects/Consumable/Drinks/drinks_cans.yml | 18 ++ Resources/Prototypes/Flavors/flavors.yml | 35 ++++ .../Reagents/Consumable/Drink/alcohol.yml | 92 ++++++++++ .../Reagents/Consumable/Drink/drinks.yml | 48 ++++++ .../Reagents/Consumable/Drink/soda.yml | 46 +++++ .../Prototypes/Recipes/Reactions/drinks.yml | 124 ++++++++++++++ .../Drinks/arnoldpalmer.rsi/icon.png | Bin 0 -> 4790 bytes .../Drinks/arnoldpalmer.rsi/meta.json | 14 ++ .../Drinks/bluehawaiian.rsi/icon.png | Bin 0 -> 4796 bytes .../Drinks/bluehawaiian.rsi/meta.json | 14 ++ .../Consumable/Drinks/coconutrum.rsi/icon.png | Bin 0 -> 4451 bytes .../Drinks/coconutrum.rsi/meta.json | 14 ++ .../Drinks/coconutwater.rsi/icon.png | Bin 0 -> 4497 bytes .../Drinks/coconutwater.rsi/icon_open.png | Bin 0 -> 4474 bytes .../Drinks/coconutwater.rsi/meta.json | 17 ++ .../Drinks/cosmopolitan.rsi/icon.png | Bin 0 -> 4363 bytes .../Drinks/cosmopolitan.rsi/meta.json | 14 ++ .../Consumable/Drinks/painkiller.rsi/icon.png | Bin 0 -> 4729 bytes .../Drinks/painkiller.rsi/meta.json | 14 ++ .../Consumable/Drinks/pinacolada.rsi/icon.png | Bin 0 -> 4454 bytes .../Drinks/pinacolada.rsi/meta.json | 14 ++ .../Consumable/Drinks/royrogers.rsi/icon.png | Bin 0 -> 4915 bytes .../Consumable/Drinks/royrogers.rsi/meta.json | 22 +++ .../Drinks/shirleytemple.rsi/icon.png | Bin 0 -> 929 bytes .../Drinks/shirleytemple.rsi/meta.json | 14 ++ .../Consumable/Drinks/sol_dry.rsi/icon.png | Bin 0 -> 4764 bytes .../Drinks/sol_dry.rsi/icon_open.png | Bin 0 -> 4776 bytes .../Drinks/sol_dry.rsi/inhand-left.png | Bin 0 -> 4557 bytes .../Drinks/sol_dry.rsi/inhand-right.png | Bin 0 -> 4565 bytes .../Consumable/Drinks/sol_dry.rsi/meta.json | 53 ++++++ .../Drinks/sol_dry_glass.rsi/icon.png | Bin 0 -> 4367 bytes .../Drinks/sol_dry_glass.rsi/meta.json | 14 ++ 50 files changed, 820 insertions(+), 3 deletions(-) create mode 100644 Resources/Textures/Objects/Consumable/Drinks/arnoldpalmer.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/arnoldpalmer.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/bluehawaiian.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/bluehawaiian.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/coconutrum.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/coconutrum.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/coconutwater.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/coconutwater.rsi/icon_open.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/coconutwater.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/cosmopolitan.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/cosmopolitan.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/painkiller.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/painkiller.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/pinacolada.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/pinacolada.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/royrogers.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/royrogers.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/shirleytemple.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/shirleytemple.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/icon_open.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/sol_dry_glass.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/sol_dry_glass.rsi/meta.json diff --git a/Resources/Locale/en-US/flavors/flavor-profiles.ftl b/Resources/Locale/en-US/flavors/flavor-profiles.ftl index 35de5555a29..61567d86954 100644 --- a/Resources/Locale/en-US/flavors/flavor-profiles.ftl +++ b/Resources/Locale/en-US/flavors/flavor-profiles.ftl @@ -188,6 +188,7 @@ flavor-complex-tonic-water = like angry water flavor-complex-cola = like cola flavor-complex-energy-drink = like battery acid flavor-complex-dr-gibb = like malpractice +flavor-complex-ginger-soda = like ginger flavor-complex-grape-soda = like grape soda flavor-complex-lemon-lime-soda = like lemon-lime soda flavor-complex-pwr-game-soda = like gaming @@ -200,6 +201,7 @@ flavor-complex-vodka = like fermented grain flavor-complex-tequila = like fermented death flavor-complex-sake = like sweet, alcoholic rice flavor-complex-rum = like fermented sugar +flavor-complex-coconut-rum = like nutty fermented sugar flavor-complex-coffee-liquor = like strong, bitter coffee flavor-complex-whiskey = like molasses flavor-complex-shitty-wine = like grape rinds @@ -212,6 +214,11 @@ flavor-complex-ice = like ice flavor-complex-mopwata = like stagnant, dirty water ## Cocktails +flavor-complex-arnold-palmer = like a hole-in-one +flavor-complex-blue-hawaiian = like the tropics +flavor-complex-cosmopolitan = sweet and tangy +flavor-complex-painkiller = like spiked pineapple juice +flavor-complex-pina-colada = like tropical sun flavor-complex-long-island = suspiciously like iced tea flavor-complex-three-mile-island = like tea brewed in nuclear runoff flavor-complex-whiskey-cola = like carbonated molasses diff --git a/Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl b/Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl index 099d23c4656..4c38e925583 100644 --- a/Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl +++ b/Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl @@ -82,6 +82,9 @@ reagent-desc-atomic-bomb = Nuclear proliferation never tasted so good. reagent-name-b52 = b-52 reagent-desc-b52 = Coffee, irish cream, and cognac. You will get bombed. +reagent-name-blue-hawaiian = blue hawaiian +reagent-desc-blue-hawaiian = Aloha! Does that mean hello or goodbye? + reagent-name-bahama-mama = bahama mama reagent-desc-bahama-mama = Tropical cocktail. @@ -106,6 +109,12 @@ reagent-desc-booger = Ewww... reagent-name-brave-bull = brave bull reagent-desc-brave-bull = It's just as effective as Dutch-Courage! +reagent-name-coconut-rum = coconut rum +reagent-desc-coconut-rum = Rum with coconut for that tropical feel. + +reagent-name-cosmopolitan = cosmopolitan +reagent-desc-cosmopolitan = Even in the worst situations, nothing beats a fresh cosmopolitan. + reagent-name-cuba-libre = cuba libre reagent-desc-cuba-libre = Rum, mixed with cola. Viva la revolucion. @@ -190,9 +199,15 @@ reagent-desc-moonshine = Artisanal homemade liquor. What could go wrong? reagent-name-neurotoxin = neurotoxin reagent-desc-neurotoxin = A strong neurotoxin that puts the subject into a death-like state. +reagent-name-painkiller = painkiller +reagent-desc-painkiller = A cure for what ails you. + reagent-name-patron = patron reagent-desc-patron = Tequila with silver in it, a favorite of alcoholic women in the club scene. +reagent-name-pina-colada = piña colada +reagent-desc-pina-colada = For getting lost in the rain. + reagent-name-red-mead = red mead reagent-desc-red-mead = The true Viking's drink! Even though it has a strange red color. diff --git a/Resources/Locale/en-US/reagents/meta/consumable/drink/drinks.ftl b/Resources/Locale/en-US/reagents/meta/consumable/drink/drinks.ftl index ad0ade7c960..359e866fd9d 100644 --- a/Resources/Locale/en-US/reagents/meta/consumable/drink/drinks.ftl +++ b/Resources/Locale/en-US/reagents/meta/consumable/drink/drinks.ftl @@ -4,6 +4,12 @@ reagent-desc-coffee = A drink made from brewed coffee beans. Contains a moderate reagent-name-cream = cream reagent-desc-cream = The fatty, still liquid part of milk. Why don't you mix this with sum scotch, eh? +reagent-name-coconut-water = coconut water +reagent-desc-coconut-water = A favorite of survivors on deserted islands. + +reagent-name-cream-of-coconut = cream of coconut +reagent-desc-cream-of-coconut = Sweet, syrupy version of coconut cream with added sugar. + reagent-name-cafe-latte = cafe latte reagent-desc-cafe-latte = A nice, strong and tasty beverage while you are reading. @@ -17,7 +23,7 @@ reagent-name-iced-coffee = iced coffee reagent-desc-iced-coffee = Coffee and ice, refreshing and cool. reagent-name-iced-green-tea = iced green tea -reagent-desc-iced-green-tea = cold green tea. +reagent-desc-iced-green-tea = Cold green tea. reagent-name-iced-tea = iced tea reagent-desc-iced-tea = No relation to a certain rap artist/actor. @@ -25,6 +31,9 @@ reagent-desc-iced-tea = No relation to a certain rap artist/actor. reagent-name-lemonade = lemonade reagent-desc-lemonade = Drink using lemon juice, water, and a sweetener such as cane sugar or honey. +reagent-name-arnold-palmer = arnold palmer +reagent-desc-arnold-palmer = Now watch this drive. + reagent-name-milk = milk reagent-desc-milk = An opaque white liquid produced by the mammary glands of mammals. @@ -86,4 +95,4 @@ reagent-name-white-gilgamesh = white gilgamesh reagent-desc-white-gilgamesh = A sickening mixture of milk and beer. Makes you feel like you're made of wood. reagent-name-mopwata = mopwata -reagent-desc-mopwata = Dirty, stagnant mop water. \ No newline at end of file +reagent-desc-mopwata = Dirty, stagnant mop water. diff --git a/Resources/Locale/en-US/reagents/meta/consumable/drink/soda.ftl b/Resources/Locale/en-US/reagents/meta/consumable/drink/soda.ftl index 08017c265da..300284aee92 100644 --- a/Resources/Locale/en-US/reagents/meta/consumable/drink/soda.ftl +++ b/Resources/Locale/en-US/reagents/meta/consumable/drink/soda.ftl @@ -1,6 +1,9 @@ reagent-name-cola = cola reagent-desc-cola = A sweet, carbonated soft drink. Caffeine free. +reagent-name-shirley-temple = shirley temple +reagent-desc-shirley-temple = A favorite amongst younger members of the crew. + reagent-name-changeling-sting = changeling sting reagent-desc-changeling-sting = You take a tiny sip and feel a burning sensation... @@ -28,6 +31,12 @@ reagent-desc-root-beer = A very sweet, carbonated drink reminiscent of sarsparil reagent-name-root-beer-float = root beer float reagent-desc-root-beer-float = Root beer, but now with ice cream on top. It truly is the magnum opus of Canadian summertime drinks. +reagent-name-sol-dry = sol dry +reagent-desc-sol-dry = Sweet ginger soda from outer space! + +reagent-name-roy-rogers = roy rogers +reagent-desc-roy-rogers = Solid proof that there IS something known as too sweet. + reagent-name-space-mountain-wind = Space Mountain Wind reagent-desc-space-mountain-wind = Blows right through you like a space wind. diff --git a/Resources/Prototypes/Catalog/ReagentDispensers/beverage.yml b/Resources/Prototypes/Catalog/ReagentDispensers/beverage.yml index d337d23f2ab..975541a502a 100644 --- a/Resources/Prototypes/Catalog/ReagentDispensers/beverage.yml +++ b/Resources/Prototypes/Catalog/ReagentDispensers/beverage.yml @@ -1,11 +1,11 @@ - type: reagentDispenserInventory id: SodaDispenserInventory inventory: + - DrinkCoconutWaterJug - DrinkCoffeeJug - DrinkColaBottleFull - DrinkCreamCartonXL - DrinkDrGibbJug - - DrinkEnergyDrinkJug - DrinkGreenTeaJug - DrinkIceJug - DrinkJuiceLimeCartonXL diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml index 91dc9f51cbb..eaa4c839af2 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml @@ -13,6 +13,7 @@ DrinkBeerBottleFull: 5 DrinkBlueCuracaoBottleFull: 2 DrinkCognacBottleFull: 4 + DrinkCoconutWaterCarton: 3 DrinkColaBottleFull: 4 DrinkMilkCarton: 2 DrinkCreamCarton: 5 @@ -27,6 +28,7 @@ DrinkPatronBottleFull: 2 DrinkRumBottleFull: 4 DrinkSodaWaterCan: 8 + DrinkSolDryCan: 8 DrinkSpaceMountainWindBottleFull: 3 DrinkSpaceUpBottleFull: 3 DrinkTequilaBottleFull: 3 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/cola.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/cola.yml index 2f9ce3d3ae6..ace91025623 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/cola.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/cola.yml @@ -5,6 +5,7 @@ DrinkGrapeCan: 2 DrinkRootBeerCan: 2 DrinkIcedTeaCan: 2 + DrinkSolDryCan: 2 DrinkLemonLimeCan: 2 DrinkFourteenLokoCan: 2 emaggedInventory: diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/gib.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/gib.yml index 38ea3a00129..98513a48a4a 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/gib.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/gib.yml @@ -5,6 +5,7 @@ DrinkGrapeCan: 2 DrinkRootBeerCan: 2 DrinkIcedTeaCan: 2 + DrinkSolDryCan: 2 DrinkLemonLimeCan: 2 DrinkFourteenLokoCan: 2 emaggedInventory: diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/pwrgame.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/pwrgame.yml index 96513fca8a8..cb5a4f5c4bc 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/pwrgame.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/pwrgame.yml @@ -6,6 +6,7 @@ DrinkGrapeCan: 2 DrinkRootBeerCan: 2 DrinkIcedTeaCan: 2 + DrinkSolDryCan: 2 DrinkLemonLimeCan: 2 DrinkFourteenLokoCan: 2 emaggedInventory: diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/shamblersjuice.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/shamblersjuice.yml index 08c0162c080..6ac70b26db2 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/shamblersjuice.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/shamblersjuice.yml @@ -5,6 +5,7 @@ DrinkGrapeCan: 2 DrinkRootBeerCan: 2 DrinkIcedTeaCan: 2 + DrinkSolDryCan: 2 DrinkLemonLimeCan: 2 DrinkFourteenLokoCan: 2 emaggedInventory: diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/soda.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/soda.yml index 494f05f6a31..339683d2c9a 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/soda.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/soda.yml @@ -5,6 +5,7 @@ DrinkGrapeCan: 3 DrinkRootBeerCan: 3 DrinkIcedTeaCan: 3 + DrinkSolDryCan: 3 DrinkLemonLimeCan: 3 DrinkFourteenLokoCan: 3 emaggedInventory: diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/spaceup.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/spaceup.yml index 0dc33cf07f9..a5f570bd46a 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/spaceup.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/spaceup.yml @@ -6,6 +6,7 @@ DrinkGrapeCan: 2 DrinkRootBeerCan: 2 DrinkIcedTeaCan: 2 + DrinkSolDryCan: 2 DrinkLemonLimeCan: 2 DrinkFourteenLokoCan: 2 emaggedInventory: diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/starkist.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/starkist.yml index 6afbe1eae64..b1a74fc13e7 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/starkist.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/starkist.yml @@ -5,6 +5,7 @@ DrinkGrapeCan: 2 DrinkRootBeerCan: 2 DrinkIcedTeaCan: 2 + DrinkSolDryCan: 2 DrinkLemonLimeCan: 2 DrinkFourteenLokoCan: 2 emaggedInventory: diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml index 11042146689..ff9e5124328 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml @@ -19,6 +19,7 @@ - DrinkAloe - DrinkAndalusia - DrinkAntifreeze + - DrinkArnoldPalmer - DrinkB52Glass - DrinkBahamaMama - DrinkBananaHonkGlass @@ -27,12 +28,15 @@ - DrinkBerryJuice - DrinkBlackRussianGlass - DrinkBlueCuracaoGlass + - DrinkBlueHawaiianGlass - DrinkBloodyMaryGlass - DrinkBooger - DrinkBraveBullGlass - DrinkCarrotJuice + - DrinkCoconutRum - DrinkChocolateGlass - DrinkCognacGlass + - DrinkCosmopolitan - DrinkCubaLibreGlass - DrinkDeadRumGlass - DrinkDevilsKiss @@ -66,15 +70,19 @@ - DrinkMilkshake - DrinkMojito - DrinkNTCahors + - DrinkPainkillerGlass - DrinkPatronGlass + - DrinkPinaColadaGlass - DrinkPoscaGlass - DrinkRedMeadGlass - DrinkRewriter + - DrinkRoyRogersGlass - DrinkRootBeerFloatGlass - DrinkRumGlass - DrinkSakeGlass - DrinkSbitenGlass - DrinkScrewdriverCocktailGlass + - DrinkShirleyTempleGlass - DrinkSuiDreamGlass - DrinkSingulo - DrinkSoyLatte diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_soda.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_soda.yml index c8a941c1281..e3339082b34 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_soda.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_soda.yml @@ -31,6 +31,7 @@ - DrinkDrGibbCan - DrinkEnergyDrinkCan - DrinkShamblersJuiceCan + - DrinkSolDryCan - DrinkPwrGameCan - DrinkHotCoco - DrinkHotCoffee diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml index 67d79540382..84639c9af0f 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml @@ -112,6 +112,24 @@ - type: Sprite sprite: Objects/Consumable/Drinks/tomatojuice.rsi +- type: entity + parent: [DrinkBottleVisualsOpenable, DrinkBottlePlasticBaseFull] + id: DrinkCoconutWaterCarton + name: coconut water + description: It's the inside of the coconut that counts. + components: + - type: SolutionContainerManager + solutions: + drink: + reagents: + - ReagentId: CoconutWater + Quantity: 50 + - type: Drink + - type: Label + currentLabel: coconut water + - type: Sprite + sprite: Objects/Consumable/Drinks/coconutwater.rsi + - type: entity parent: [DrinkCartonVisualsOpenable, DrinkCartonBaseFull] id: DrinkCreamCarton diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml index bf00dd6327c..aac2803dd2b 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml @@ -258,6 +258,22 @@ - ReagentId: Antifreeze Quantity: 30 +- type: entity + parent: DrinkGlass + id: DrinkArnoldPalmer + suffix: arnold palmer + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: ArnoldPalmer + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/arnoldpalmer.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkAtomicBombGlass @@ -437,6 +453,22 @@ sprite: Objects/Consumable/Drinks/bloodymaryglass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkBlueHawaiianGlass + suffix: blue hawaiian + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: BlueHawaiian + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/bluehawaiian.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkBooger @@ -501,6 +533,35 @@ sprite: Objects/Consumable/Drinks/chocolateglass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkCoconutRum + suffix: coconut rum + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: CoconutRum + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/coconutrum.rsi + state: icon + +- type: entity + parent: DrinkGlass + id: DrinkCoconutWaterGlass + suffix: coconut water + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: CoconutWater + Quantity: 30 + - type: entity parent: DrinkGlass id: DrinkCoffee @@ -533,6 +594,22 @@ sprite: Objects/Consumable/Drinks/cognacglass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkCosmopolitan + suffix: cosmopolitan + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: Cosmopolitan + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/cosmopolitan.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkCream @@ -1340,6 +1417,22 @@ sprite: Objects/Consumable/Drinks/orangejuice.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkPainkillerGlass + suffix: painkiller + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: Painkiller + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/painkiller.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkPatronGlass @@ -1356,6 +1449,22 @@ sprite: Objects/Consumable/Drinks/patronglass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkPinaColadaGlass + suffix: piña colada + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: PinaColada + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/pinacolada.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkPoisonBerryJuice @@ -1484,6 +1593,22 @@ sprite: Objects/Consumable/Drinks/rumglass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkRoyRogersGlass + suffix: roy rogers + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: RoyRogers + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/royrogers.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkSakeGlass @@ -1609,6 +1734,22 @@ sprite: Objects/Consumable/Drinks/glass_white.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkShirleyTempleGlass + suffix: shirley temple + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: ShirleyTemple + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/shirleytemple.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkSilencerGlass @@ -1641,6 +1782,22 @@ sprite: Objects/Consumable/Drinks/singulo.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkSolDryGlass + suffix: sol dry + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: SolDry + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/sol_dry_glass.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkSnowWhite diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml index face999df82..e2361cfa6ee 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml @@ -789,6 +789,23 @@ - type: Label currentLabel: ice +- type: entity + parent: DrinkBottlePlasticBaseFull + id: DrinkCoconutWaterJug + name: coconut water jug + description: It's on the inside of the coconut that counts. + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 300 + reagents: + - ReagentId: CoconutWater + Quantity: 300 + - type: Drink + - type: Label + currentLabel: coconut water + - type: entity parent: DrinkBottlePlasticBaseFull id: DrinkCoffeeJug diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml index 2aec5761770..585e5ed14d9 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml @@ -239,6 +239,24 @@ - type: Item sprite: Objects/Consumable/Drinks/space-up.rsi +- type: entity + parent: DrinkCanBaseFull + id: DrinkSolDryCan + name: sol dry + description: Sweet ginger soda from outer space! + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: SolDry + Quantity: 30 + - type: Sprite + sprite: Objects/Consumable/Drinks/sol_dry.rsi + - type: Item + sprite: Objects/Consumable/Drinks/sol_dry.rsi + - type: entity parent: DrinkCanBaseFull id: DrinkStarkistCan diff --git a/Resources/Prototypes/Flavors/flavors.yml b/Resources/Prototypes/Flavors/flavors.yml index 446abf99ed4..2b55efc21b5 100644 --- a/Resources/Prototypes/Flavors/flavors.yml +++ b/Resources/Prototypes/Flavors/flavors.yml @@ -624,6 +624,11 @@ flavorType: Complex description: flavor-complex-dr-gibb +- type: flavor + id: gingersoda + flavorType: Complex + description: flavor-complex-ginger-soda + - type: flavor id: lemonlimesoda flavorType: Complex @@ -694,6 +699,11 @@ flavorType: Complex description: flavor-complex-rum +- type: flavor + id: coconutrum + flavorType: Complex + description: flavor-complex-coconut-rum + - type: flavor id: coffeeliquor flavorType: Complex @@ -739,6 +749,31 @@ flavorType: Complex description: flavor-complex-ice +- type: flavor + id: arnoldpalmer + flavorType: Complex + description: flavor-complex-arnold-palmer + +- type: flavor + id: bluehawaiian + flavorType: Complex + description: flavor-complex-blue-hawaiian + +- type: flavor + id: cosmopolitan + flavorType: Complex + description: flavor-complex-cosmopolitan + +- type: flavor + id: painkiller + flavorType: Complex + description: flavor-complex-painkiller + +- type: flavor + id: pinacolada + flavorType: Complex + description: flavor-complex-pina-colada + - type: flavor id: longisland flavorType: Complex diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml b/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml index f6bac28b325..44eba0f848c 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml @@ -79,6 +79,26 @@ reagent: Ethanol amount: 0.1 +- type: reagent + id: BlueHawaiian + name: reagent-name-blue-hawaiian + parent: BaseAlcohol + desc: reagent-desc-blue-hawaiian + physicalDesc: reagent-physical-desc-tropical + flavor: bluehawaiian + color: "#47687b" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/bluehawaiian.rsi + state: icon + metabolisms: + Drink: + effects: + - !type:SatiateThirst + factor: 2 + - !type:AdjustReagent + reagent: Ethanol + amount: 0.2 + - type: reagent id: Cognac name: reagent-name-cognac @@ -732,6 +752,46 @@ reagent: Ethanol amount: 0.2 +- type: reagent + id: CoconutRum + name: reagent-name-coconut-rum + parent: BaseAlcohol + desc: reagent-desc-coconut-rum + physicalDesc: reagent-physical-desc-strong-smelling + flavor: rum + color: "#cccbc8" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/coconutrum.rsi + state: icon + metabolisms: + Drink: + effects: + - !type:SatiateThirst + factor: 2 + - !type:AdjustReagent + reagent: Ethanol + amount: 0.2 + +- type: reagent + id: Cosmopolitan + name: reagent-name-cosmopolitan + parent: BaseAlcohol + desc: reagent-desc-cosmopolitan + physicalDesc: reagent-physical-desc-citric + flavor: cosmopolitan + color: "#f73030" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/cosmopolitan.rsi + state: icon + metabolisms: + Drink: + effects: + - !type:SatiateThirst + factor: 2 + - !type:AdjustReagent + reagent: Ethanol + amount: 0.15 + - type: reagent id: CubaLibre name: reagent-name-cuba-libre @@ -1254,6 +1314,26 @@ types: Poison: 1 +- type: reagent + id: Painkiller + name: reagent-name-painkiller + parent: BaseAlcohol + desc: reagent-desc-painkiller + physicalDesc: reagent-physical-desc-refreshing + flavor: painkiller + color: "#e6cb47" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/painkiller.rsi + state: icon + metabolisms: + Drink: + effects: + - !type:SatiateThirst + factor: 2 + - !type:AdjustReagent + reagent: Ethanol + amount: 0.2 + - type: reagent id: Patron name: reagent-name-patron @@ -1292,6 +1372,18 @@ metamorphicFillBaseName: fill- metamorphicChangeColor: true +- type: reagent + id: PinaColada + name: reagent-name-pina-colada + parent: BaseAlcohol + desc: reagent-desc-pina-colada + physicalDesc: reagent-physical-desc-tropical + flavor: alcohol + color: "#e8dba4" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/pinacolada.rsi + state: icon + - type: reagent id: Sbiten name: reagent-name-sbiten diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml b/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml index 946a2a6e449..5c09b3c909b 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml @@ -48,6 +48,34 @@ - !type:SatiateThirst factor: 1 +- type: reagent + id: CoconutWater + name: reagent-name-coconut-water + parent: BaseDrink + desc: reagent-desc-coconut-water + physicalDesc: reagent-physical-desc-milky + flavor: nutty + color: "#f4eadb" + metabolisms: + Drink: + effects: + - !type:SatiateThirst + factor: 4 # Coconut water is 94% water + +- type: reagent + id: CreamOfCoconut + name: reagent-name-cream-of-coconut + parent: BaseDrink + desc: reagent-desc-cream-of-coconut + physicalDesc: reagent-physical-desc-syrupy + flavor: creamy + color: "#FFEABF" + metabolisms: + Drink: + effects: + - !type:SatiateThirst + factor: 2 + - type: reagent id: CafeLatte name: reagent-name-cafe-latte @@ -406,6 +434,26 @@ - !type:SatiateHunger factor: 4 +- type: reagent + id: ArnoldPalmer + name: reagent-name-arnold-palmer + parent: BaseAlcohol + desc: reagent-desc-arnold-palmer + physicalDesc: reagent-physical-desc-sweet + flavor: arnoldpalmer + color: "#d8cc5d" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/arnoldpalmer.rsi + state: icon + metabolisms: + Drink: + effects: + - !type:SatiateThirst + factor: 4 + - !type:AdjustReagent + reagent: Theobromine + amount: 0.05 + - type: reagent id: Pilk name: reagent-name-pilk diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml b/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml index 63ae5b25d5b..ba5adc4f2ae 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml @@ -8,6 +8,23 @@ color: "#6c2828" recognizable: true +- type: reagent + id: RoyRogers + name: reagent-name-roy-rogers + parent: BaseSoda + desc: reagent-desc-roy-rogers + physicalDesc: reagent-physical-desc-sweet + flavor: cola + color: "#262019" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/royrogers.rsi + state: icon + metabolisms: + Drink: + effects: + - !type:SatiateThirst + factor: 4 + - type: reagent id: ChangelingSting name: reagent-name-changeling-sting @@ -116,6 +133,35 @@ metamorphicFillBaseName: fill- metamorphicChangeColor: false +- type: reagent + id: SolDry + name: reagent-name-sol-dry + parent: BaseSoda + desc: reagent-desc-sol-dry + physicalDesc: reagent-physical-desc-fizzy + flavor: gingersoda + color: "#ccb87e" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/sol_dry_glass.rsi + state: icon + +- type: reagent + id: ShirleyTemple + name: reagent-name-shirley-temple + parent: BaseSoda + desc: reagent-desc-shirley-temple + physicalDesc: reagent-physical-desc-fizzy + flavor: sweet + color: "#af2221" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/shirleytemple.rsi + state: icon + metabolisms: + Drink: + effects: + - !type:SatiateThirst + factor: 4 + - type: reagent id: SpaceMountainWind name: reagent-name-space-mountain-wind diff --git a/Resources/Prototypes/Recipes/Reactions/drinks.yml b/Resources/Prototypes/Recipes/Reactions/drinks.yml index 7608369b2ce..c810ebb0ce5 100644 --- a/Resources/Prototypes/Recipes/Reactions/drinks.yml +++ b/Resources/Prototypes/Recipes/Reactions/drinks.yml @@ -54,6 +54,16 @@ products: Antifreeze: 4 +- type: reaction + id: ArnoldPalmer + reactants: + IcedTea: + amount: 1 + Lemonade: + amount: 1 + products: + ArnoldPalmer: 2 + - type: reaction id: AtomicBomb reactants: @@ -76,6 +86,20 @@ products: B52: 3 +- type: reaction + id: BlueHawaiian + reactants: + CoconutRum: + amount: 2 + JuicePineapple: + amount: 1 + JuiceLemon: + amount: 1 + BlueCuracao: + amount: 1 + products: + BlueHawaiian: 5 + - type: reaction id: CogChamp reactants: @@ -90,6 +114,24 @@ sound: path: /Audio/Magic/Cults/ClockCult/steam_whoosh.ogg +- type: reaction + id: BahamaMama + reactants: + Ice: + amount: 1 + JuiceOrange: + amount: 1 + JuicePineapple: + amount: 1 + Rum: + amount: 1 + CoconutRum: + amount: 1 + Grenadine: + amount: 1 + products: + BahamaMama: 6 + - type: reaction id: Barefoot reactants: @@ -182,6 +224,40 @@ products: CafeLatte: 2 +- type: reaction + id: CoconutRum + reactants: + Rum: + amount: 2 + CoconutWater: + amount: 1 + products: + CoconutRum: 3 + +- type: reaction + id: Cosmopolitan + reactants: + Vodka: + amount: 1 + JuiceBerry: + amount: 1 + JuiceLime: + amount: 1 + products: + Cosmopolitan: 3 + +- type: reaction + id: CreamOfCoconut + reactants: + CoconutWater: + amount: 1 + Sugar: + amount: 1 + Milk: + amount: 1 + products: + CreamOfCoconut: 3 + - type: reaction id: CubaLibre reactants: @@ -638,6 +714,34 @@ products: Patron: 10 +- type: reaction + id: Painkiller + reactants: + JuicePineapple: + amount: 3 + CoconutRum: + amount: 1 + JuiceOrange: + amount: 1 + CreamOfCoconut: + amount: 1 + products: + Painkiller: 6 + +- type: reaction + id: PinaColada + reactants: + JuicePineapple: + amount: 3 + JuiceLime: + amount: 1 + Rum: + amount: 1 + CreamOfCoconut: + amount: 1 + products: + PinaColada: 6 + - type: reaction id: Posca reactants: @@ -680,6 +784,16 @@ products: RootBeerFloat: 3 +- type: reaction + id: RoyRogers + reactants: + Cola: + amount: 2 + Grenadine: + amount: 1 + products: + RoyRogers: 3 + - type: reaction id: Sbiten reactants: @@ -700,6 +814,16 @@ products: ScrewdriverCocktail: 3 +- type: reaction + id: ShirleyTemple + reactants: + SolDry: + amount: 2 + Grenadine: + amount: 1 + products: + ShirleyTemple: 3 + - type: reaction id: Silencer reactants: diff --git a/Resources/Textures/Objects/Consumable/Drinks/arnoldpalmer.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/arnoldpalmer.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..81261d0f54f589b5acb8b36b90ae2edf528fd3bd GIT binary patch literal 4790 zcmeHLc~BH*7H>h0Fbm72vYu^IvXV{D^z<}ybQln4I7S^r80A=9>FMslUYUdLh8gfG zaHFdxY&;_b6OXv=YP_&CRzyX>;FhAINj7TKL|qgW)Lj*g(d;)ffKZ#NEo;~Rn5v%c ze&2h)_ulWl_tku}EF(QNIB0kf1VO=uG<_zx!|p54AAAdCWe>rvsmzokW>QYr!P_ld zAq$J84i;uzoCShh7p)o7LK@+~=QktYuJDx^FIBmEiLOK&Zx`B<}UdD`_QxFYd@@86MoN`Fvfpj+{tyTSJvrr zc31FEPPU9s*xcHB%Pffwj-EJ-k0QqWX2{yI(Nsr!&;#G-j7P1`)$>}uI`A2q5^ml$ zGT*aWQBoxqjvke#YUeB8R0O+yC8n z&E~I;9nZ}U$W99V5h^NiWQr@pfBGOYugT^3_D{~P(aw9&rd)RWg$Q3$3x00d!SyG1 zFE7syjG7o)zp-hGwkr2jgE0ScWAon+jX(3xgsz*nvV&rL=Oab$wl7JpONu|lwbyhl zYy9-B*ruCJtNmxa|77Kz+LQL0$sv+>MIm$kUrm&G@0Z86>^Rtd?EUu>0^4~Cg0#8gCHN-@XowOrm`7)xTS^MLb%EV-8)yyFn z|JFwTA*-$j{IvM$h!>m(>CK z4uawnT@H$#%Zjj>E#Pcg7Cc11ctznSFgrImA z2>`6DNWm^^p-muNTEv4(f@il_hQJ;bajq80F=oIzJI}&|l#pU*vWqLhk%S;Po@Xp% zraq+`0=#LFIilzwWiqGJDRnBOcD_I+*Jv~{442_J3N%om)Fx6c)Fwo`A-ouRR-k## zA#!#b?8c|I1=w~ei z6PQw2CM(#Bd74cwVQpe`UkHZowRaTrg`RX6TE-T#R-h^XukwB_Qw_$9UJEw`1)SC4 zu>xfG(-b+&OS1aK=3eon(>D-c-izB$yC-*#GSD&_Nxhvec86!sYZ149lCjeqLwX+7 zxEx~{21jv z0-y?99;d?OYFt6fJx~lyrr3Ea1&k;TgXIO#i*8goCRGp~b9Z4#kPe`h za(5~K@W??nq>g7P(axLf_ChV0Pz)tfdP-ygD25XxhLdu&Nsg1ahQyR8 z7Dr-z^z961DgAHS?(u=+y+u#s1mM5aBl6Bv7F*4Fna;hFW1s#MI{&j{?2_$bg(sBg|l0>+#K~Qx~ z6c7f824ix-EuZkPQGt0A&Vj!|1}!$|lT0~{9cz!K`y_=fJ$hkrKG9UU^z1PBKuk>2 zN2wD=L`Y`WpKdrk>F(W6zx+eM{i(+!_kCg_QzTnr+UhTjl2l$FEa_}G=gRt>#d-AU zb4%OCKO!&Yov2&39W{m+emm4JzgBytx;!_i{>Fn6pS>o@xuB7=4v+l;I+(b;Xj@B1 zz3|H#;p(D6PpVJ1i}QADy*>Qb@BQ#?*SfZtwFgFCNw|1@!zLgP$$1*x!tUR=#vl#* z`c3w+N*a7iub*~yTI;Z>^G-)Y>SyVZ7r)VN;ImQpT8^& ze@AJBs&BXP`As|gpw{{?#>8A{-q-M%`cXl7AToW)^X$P(&7m_lH(qYLpcq57jHqw# zoPpJq?rCx4U#p1d2)_R!?CbR5kLu}&itT3Ihv5sF@`YX3PC-b6bI$e09~^uhcjw_h z0x(4HyW&<(xoc=kE%ffjm4BY`e%s83szuGTAHVK%42qWcohZI^?9t&_J5841vO9r8 dr|H9>#FI5kRad+Anm~mhLvp%)*Tnpae*@Dd%6k9+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/arnoldpalmer.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/arnoldpalmer.rsi/meta.json new file mode 100644 index 00000000000..f76e6118413 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/arnoldpalmer.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Modified from https://github.com/discordia-space/CEV-Eris/raw/f7aa28fd4b4d0386c3393d829681ebca526f1d2d/icons/obj/drinks.dmi", + "states": [ + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/bluehawaiian.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/bluehawaiian.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d192a884616ee9ae73c071829e15f2a0efaa0db6 GIT binary patch literal 4796 zcmeHLX;c(f7A|BJgBpa0d(mhHjJ+>(mo&)UjW!~nEE1)ys6a4ri%|hVqt3XY0dyF{C>rKZxw+rT#KI(934Xd2CCpq`G{d*Q$*A)k!Ym{`mryh%#^YYm_ z&lMr1Up8%K9YSpk?(6+~G)u~>SJYK@@&aF#h41TW^?RP&o;B~+@jH!p0UP#(UKFko zWGkxTJ^VuxHd>A6E zMjeQpyU#E&?QF!Kf7wUNKadw(8PVogf$U6pVJn-u#bt8n>A&2*-6X$ux-l-sKE{9I zODN5tjid^l`**m+H<+|HHXD0q8oQyram1nPldPjJaDK`?vGd%~yia2s+yW+^`n+M0 zcvsx{Gx}9G>zi6?JTLvzxA(!r7{~XmSJBcocB~7o_4lbEJ4$+U>yOOyYIxAF)$YTB ze-}QkI;SaFIDVWDHx<9q(}2pqzS^jpe5`7(^QDQQE=%{Xa(_^DCS${~_=;n{il2`Q zv{lWiPcOzKiDRW@a6rfm*J#u|>~Kh4F0Zpm{BnQ3Uoc}=f-6fm?{hoXE&s6Z&$)H8 zxUs{_+snm1b7g8DHr?TuF%|1nAJ1wxJhpk_XB`vYv@&IkzO>96iNEXM)@sN2YJPF8 zeL@%HvA7zSbB=b={7k%OS}S#&On0Vij^%wFpY|V;u<-@FsZbl>LAF+&!k1M6oP`~gpyQ? zY27t-G?>K2v>3jOCDR5HNu(rGM?__YM`M{Omclq)_a&C1~7sMJ*FeI6sb|eW=vGBNvFg# z8j!=U{ZnaWvLSf2evk#g2h)UVnQR7&sZudVTIi`@0{|Ix=yxsj(V$0}k%V58uEU66 z1EHooMnd4&ki9ltmud+I$CyMap#rLUuqykFB}1jMh#?Cz1xiw-wO9eN-_WE;#V}cK z_-2+^!WroZFdxEwL;H2?7GsfP zvFUPDK;Rq^MtHM@BOn&*NKln%>PWB5P&k0%vjiLrmymj}euiagpr@jUw1LsUFqO146AWA00zt8X5cH^lz%70PtN{_0 zgsPPU*gpmf_VqmZJH^1lP@K*7=F&MrJ_i(ow}LLj6d0W+;t7S`e2k3>1~NE=uGc6i zBdR0(m4HXU6(~>(SFq<`s=VIx#+XEy^8hHLvpDo&%D6P!v=t8W1$_M@ufaV0kxRhv=pt{$?2n1U88ip6$5Xj zJX&4aWelCFjgyx(?ig>N#@%M zDlTyYllGKU7Ht1;jI+~3_Y;X~B?OI0mInDp$JTdlUo3G!-M%aB-JSNm;wC>bpo12$ zGyO}3^qya}r|HR0<%wgmcUbz07@RY|@hrdmX zE3WBVeXHF+VVj|4%5~&Iw}9K-Q+uiQR__I9Cb;@@+uGtUrk6VA7Bl~<2%dE5yCmkO zrkd*5we`YT8}41*5OATQ8ah%9op-#I&#FP!XZw8X(_rvCJ9qlJ%gsf{iyZkY`)VDJ zow47wX#Sp4I{dR8lUFE@ZVjDwIr{ppn3Vfj1z~D@chk=y=!A{x(gn2At~;saRgb4R zo$I&i(fd8UG2Zo^67H0q`ImJaMQ!m}S&2Mf(}S>UTc3Kj?{3nQtG9`4q53OJR4pHK z=cXXvr+-q{eDzLyx0`C$+P+SwYc5>V+54;cCuYe$ z@+-D(zQ6u{(^R}NyY=`4tj_f_s9^8aLy`GKEsaOqAVK65&*rm|v0kqEIqof2evEgg z&!K$)O$#u{E_D3e4od64TOuK5Z(A0T%ngbU^Q_prEdcRwKhW?g*D2wuxU#YHPVV-O z!X5Y4CWlks{Am_da+rEVR=0hvkiOUvx9miS+Yg%#9>ydAo-R{b>RR^{mfP&KEt}^~ zyNDG0xF}$I?~0?3GM?>udbC3=DssA&b%%Ajz8>5B((0rD3NC&(<{&6UNE#d-^i@D& G_J05u#{S9x literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/bluehawaiian.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/bluehawaiian.rsi/meta.json new file mode 100644 index 00000000000..f76e6118413 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/bluehawaiian.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Modified from https://github.com/discordia-space/CEV-Eris/raw/f7aa28fd4b4d0386c3393d829681ebca526f1d2d/icons/obj/drinks.dmi", + "states": [ + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/coconutrum.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/coconutrum.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4adab5cb6e6b935cc3878d2c6e533abe8fe82dee GIT binary patch literal 4451 zcmeHLdsGzH86S|BYe-SC0Z-I%r4bCXJNtm$9a#`rU{|xS!nQmPIWjwQ7e@BQ&ak_n zz$sW;kYY5g4-Tdnv{I{)O3blUjLOR#lF-K5w2&xCY<&f^O(GJk_RjJ&%{e`s)BMMt zGdnZ){=VP$eZTL!_w3yoLt1LEV5tB>kYJrwlL7B2e+31=Z?>%LOL)6fX3XX?NH^+m z+Rcmwpj@c~puoeJ5yaDPH6$+TK!eVGQ5)}*W4f>UwZ7o%>iD-?A{Gw}*S*;KzTl6K zE7Z6Dg6eept~%d2uq^jY?N=8(O|G=#2iHbK^c^J*R(a-EP>1(^_U}#UM=zc^mfG8O zd3dFz%^-g?)Eilma<`>x7rut{3tum&Cb~b(v$WT@4#jnJA4=K64kV1;8Q2>A_RuC^GL>;*IgmSmkkF$e_dFAq&B(L&@VVa?9cn!U%zxu=yRzTPY(|E zn66&z%E=1MN(^~~6uTT5-0nqB_J!tldK@3^a*r%`-$M@NfMb7KJkR)^^n;o+`+HB; z{x~ZrY<b%+cYQW2{JluWvRIj~p7u^&{5JrY4hjn4x{T+mMt-oD>wW$rM5;s)T4gXT%V9EI*_*n zcSbh_EZOr};FId>A2)Q}j#5O029|BMj8V@AeeQc=hqZFWfa|W`{e*d0xjmbUeA!ldmuD^yJMUjrWU~*IH^w75ceacM zD3yLMZ1_d{@IQyo4_|w%zA}6-P?qWZdGJ^HS76h*7}#9d`j-gGZWWQV-2_A)s{?i& zg2W|w93)i)IMf6R7@Jyn@9f(`l%dtatQbA6cO-#AMqB0tnPq84s;r1o(ZYmyL7azx z1XjS2sK;utv4lr0^zst$nI9GlQ7?omQVX;71~kd;1gKmj7vWg4hjB@T@d7l?Nt=lb zP09oXd{YYxInF_d#csD-hy*w3toZ(#_I4| zL9w4ga*TOetfyk*XT0gm34M#Br=SQH3LkJ$Eq-u z!X(3RnM$RIl>)O|9^<8=DI&%0w34u#jFl_^Vu!83JHZQ1BpP&Tp;UxVj~FZ@XNCrF z4KOy^?q;W_jEogzawM;(M1d=%u`(HqTPl+&71N*}0VfN4k!O|QBAMJf!xx5t=|E{o zzEdHAcN}IzBsl@e*_}qa-J%xqL7}|m)U+NhD4OI*4aosWic94LE+r&NBd#Q5v4kuJ z!(#}12Hs9H=FE>063;HnWUJBhWRQ;h0Lt{20Lpv zb8gZJ5(}V?P%GG=Uaio`iBd&*Rhx>&T?qI(K*=y%icL!vD-=&8E9Q5M$!z1q|D{Kq z7cgUsfp+6#aO;9Qp?GR5oX`vU{w_Zgb@(n_py-^FS?N0`*PL9lQeal#x$c^iYgP)( z3Ov_c|2Me=(}yWwgTH{>@Tl}BqCW{9vjR*TQ#D8`QiyEbQyt9z6ENSQ&0!H_!D9aM zK^hvv;9wx9(X7q z8NS?1fzfV=?H(KJI{9jz^m2WZZ7GE2w_JZ~Y~RU9@r&^>Mc44S)v=Ft>GSI9jD7wQ z{vW0Xyr!tEDBs(8x4ZJAcjY^Mvt6C5(>L7TRkaBhq+E>T2@s?v-Ow7PHC8~q5M6Sb K=8g6FRsR9yS6zSr literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/coconutrum.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/coconutrum.rsi/meta.json new file mode 100644 index 00000000000..f76e6118413 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/coconutrum.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Modified from https://github.com/discordia-space/CEV-Eris/raw/f7aa28fd4b4d0386c3393d829681ebca526f1d2d/icons/obj/drinks.dmi", + "states": [ + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/coconutwater.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/coconutwater.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a270180dcd4e8dc61cd9fc37ae61a2f98118fb46 GIT binary patch literal 4497 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 PW`NkN8!ac-71#U+x9KO& literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/cosmopolitan.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/cosmopolitan.rsi/meta.json new file mode 100644 index 00000000000..f76e6118413 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/cosmopolitan.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Modified from https://github.com/discordia-space/CEV-Eris/raw/f7aa28fd4b4d0386c3393d829681ebca526f1d2d/icons/obj/drinks.dmi", + "states": [ + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/painkiller.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/painkiller.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..caa81b36f0adb1d9dbc5c964dab994c504eea8b1 GIT binary patch literal 4729 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 OKoW7V|B;!Cmj4b0zCE`9 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/royrogers.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/royrogers.rsi/meta.json new file mode 100644 index 00000000000..d16d731d7f8 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/royrogers.rsi/meta.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Modified from https://github.com/discordia-space/CEV-Eris/raw/f7aa28fd4b4d0386c3393d829681ebca526f1d2d/icons/obj/drinks.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 1, + 1, + 1, + 1 + ] + ] + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/shirleytemple.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/shirleytemple.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..88fdd5e79b971275c4012ee5afd12513bc377fb6 GIT binary patch literal 929 zcmV;S177@zP)EX>4Tx04R}tkv&MmKpe$iQ>9WWqIM8*$WWc^q9Tr^ibb$c+6t{Ym|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfc5qU3krMxx6k5c1aNLh~_a1le0HIlBs@W3*RLwHd ziMW`{uZn?J2pB>Tz@WrTJ(*d|!gGAx!^hXVD9`df_vh$W3MK=5B5{oAhDE$VJiBS> zocD>ttSl+S=fsl+U6A;Z>$1yloJ$T1JTq)$)APh(VzJc4au>6*p%Tv!M-)|~d?Dwu z!g-6cTCKD8J^2em1#Kn6b(&*HUG?f@fCx@1U>T3B0I5-3* zN|e3s@$SCv-u^w)?(YXPQ*w>*4?Up(000JJOGiWiuK*wbAP5)L5C8xG32;bRa{vGf z6951U69E94oEQKA00(qQO+^Rj0ul`cIh?r=B>(^cpGibPR9M69l)q~fQ5?iS^Dgnw zBU*0|%d}R4h&I|p#8MkuTPu4-j3D@8!8A!zc3s_28rcPpwS2bMu&&h;w`jKc~(ps zj6gzKVVvi9p1304zW?9{gJEb)!1EiIR)4?!+`pM+$0A^O400000NkvXXu0mjf Djq#tS literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/shirleytemple.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/shirleytemple.rsi/meta.json new file mode 100644 index 00000000000..f76e6118413 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/shirleytemple.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Modified from https://github.com/discordia-space/CEV-Eris/raw/f7aa28fd4b4d0386c3393d829681ebca526f1d2d/icons/obj/drinks.dmi", + "states": [ + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ace08a9ffb0069ae73de8e2063cb25563ad34865 GIT binary patch literal 4764 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 KGk%m>Q28G+kH&NW literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/inhand-left.png b/Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..3c03c07742b267655d40be9d93adefed258463bc GIT binary patch literal 4557 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/meta.json new file mode 100644 index 00000000000..8bc80009c58 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/sol_dry.rsi/meta.json @@ -0,0 +1,53 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Modified from https://github.com/discordia-space/CEV-Eris/raw/9c980cb9bc84d07b1c210c5447798af525185f80/icons/obj/food.dmi, Sol Dry from /tg/.", + "states": [ + { + "name": "icon" + }, + { + "name": "icon_open" + }, + { + "name": "inhand-right", + "directions": 4, + "delays": [ + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ] + ] + }, + { + "name": "inhand-left", + "directions": 4, + "delays": [ + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ] + ] + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/sol_dry_glass.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/sol_dry_glass.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c4bf829e5d159a8501c10a5388bd153f99343453 GIT binary patch literal 4367 zcmeHLdr%bj9bc7Ka8L#hLriMdwK0;qce{6cugx8=y8})+BycI9wz9Vm*l>4y+%DXK zT8)g%K-36nCxntTh}P5uYawP573F0yRmZe6jY&=G7)QLsVC>6SjP|$3dy<(BGtGb8 z%--&PAD`d%`}ux<-?`t_=jCR_#V(75ASlkBZOsRFSiWMSz_(aa^G|T=t8o@d`BVV* z`2~)5v#?a{V_`PPa}X5#*pru@&=1F)9BoKRe{OZkAQ8&h{{C_+4TVXY0FP zeY&r`wke{x;d+Z0abWGiy0rGTxZJ0s7Vlek_E3!>GNZnt>tIXwgVzrY^&F`ydp9XG zw5xLI;df5oPr7^V@)MukzMlEPp5~lv@B245;)UpksyDZ^wpC?3@rUZ08-H@N+td40 z&RgQu7;fpKJ4PFOJP~o7`|(a>m9nJg!qI)(w~+CtK005V(A!*g7J}y0@)k>;-C~)@ z2ISDXt@VZpWI&+UvW0?m7DvKy$2g?NAo_8?I4>s{U_3~tV!{3R`0PZ zR|Z`l^qwg$j4n)%zXw%R`SPX4ga7ISlcN1bQ;_y1D$dBm5g3ko+3ZZ4lM z{_=pH+;A->=~9&P$WtvRqc@E}E7u=qviDv)l&pBZXY`S61C}rqrO1qRUwccCn?r*NogcaTi{NKrLP;nw)E4Bc)JRG!Z1OW4 znQzS;hX8M8q)d{0q)HVC1e5`dQt+3maHG+v!qh6Y8U+@pSnZXlAnFxY$`E51R#v3_ zyieiE~BM8vL6Y+U`4#y8=Q<+Gwt>8Dv+73-B&PKRLVNq=9Z-yKeep;fG#^#D^5tcuTAGRyABoAi(=DCIrA zuoobEhNZ-FQ)JDEP1c0dnH~smpTwPEJ&}9Z7+5(Rq*b6RSV1G#X5g8q@{` z#c7R8?ZUMN1BZv97@Eu!{2mIFllM@itjgyt4J%~A$@DzC8Br^-DM_B2k~rW1_5kl? zgn&4ua`GOwK%!(maXn^G>(mAXQGP&Cj}@! zn_dcd1YCgz4RZyr9xqk$O#46?E7t)~hGJ@TiZTLGjVG&;C&om!Ce=6iFog}KjTqoJ zCIh1jOhVP-ZY9y;IQXS@#I;R*nHZjo8>J4e?XU9)0fR>pJPHAmO17?_puTzCE7 z=!%^>OtD_@3n&1NN=-NF-vGy~DA&3yE0hJ5L0g(?zc*68egc%y=ck1i_+e1P3W=i zIhj*$Xb_0KTkjQdP&<4?l*3`aK2-p=gaLe ef36FM`ZNULrpz@oTD}Y<0@-c3)*~4uJO2wSlrY-> literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/sol_dry_glass.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/sol_dry_glass.rsi/meta.json new file mode 100644 index 00000000000..f76e6118413 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/sol_dry_glass.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Modified from https://github.com/discordia-space/CEV-Eris/raw/f7aa28fd4b4d0386c3393d829681ebca526f1d2d/icons/obj/drinks.dmi", + "states": [ + { + "name": "icon" + } + ] +} From 16e3aed2490f1ace427c9730692fcde373277b26 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 06:42:20 +0000 Subject: [PATCH 115/133] 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 946856290aa..f903f70f292 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Blackern5000 - changes: - - message: Botany's hatchet, spade, and hoe have received a damage decrease to 10, - 10, and 6 damage respectively. - type: Tweak - id: 5785 - time: '2024-01-25T01:37:11.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24481 - author: Minty642 changes: - message: Fixed Reinforced Glass Recipe in Ore Processor! @@ -3808,3 +3800,10 @@ id: 6284 time: '2024-04-01T06:06:14.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/17778 +- author: musicmanvr + changes: + - message: Added Sol Dry, 8 new cocktails and coconut water. + type: Add + id: 6285 + time: '2024-04-01T06:41:14.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25367 From da69b0ab493836971d06cf30943f43734756bd0d Mon Sep 17 00:00:00 2001 From: IProduceWidgets <107586145+IProduceWidgets@users.noreply.github.com> Date: Mon, 1 Apr 2024 02:48:49 -0400 Subject: [PATCH 116/133] Add ERT Chaplain (#25956) * ERT Chaplain * Make BibleUser * It was not intended * reword my poor words * 1984 a comment that I decided was unnecessary. * Update Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../ghost/roles/ghost-role-component.ftl | 3 + .../Locale/en-US/job/job-description.ftl | 1 + Resources/Locale/en-US/job/job-names.ftl | 2 + .../Fills/Backpacks/StarterGear/backpack.yml | 21 ++++++ .../Entities/Clothing/Back/backpacks.yml | 9 +++ .../Clothing/Head/hardsuit-helmets.yml | 15 +++++ .../Clothing/OuterClothing/hardsuits.yml | 14 ++++ .../Entities/Clothing/Uniforms/jumpsuits.yml | 11 ++++ .../Entities/Markers/Spawners/jobs.yml | 12 ++++ .../Entities/Mobs/Player/humanoid.yml | 60 ++++++++++++++++++ .../Roles/Jobs/Fun/emergencyresponseteam.yml | 55 ++++++++++++++++ .../Prototypes/Roles/play_time_trackers.yml | 3 + .../ertchaplain.rsi/equipped-BACKPACK.png | Bin 0 -> 806 bytes .../Back/Backpacks/ertchaplain.rsi/icon.png | Bin 0 -> 553 bytes .../Backpacks/ertchaplain.rsi/inhand-left.png | Bin 0 -> 654 bytes .../ertchaplain.rsi/inhand-right.png | Bin 0 -> 708 bytes .../Back/Backpacks/ertchaplain.rsi/meta.json | 26 ++++++++ .../ERThelmets/ertchaplain.rsi/icon-flash.png | Bin 0 -> 374 bytes .../ERThelmets/ertchaplain.rsi/icon.png | Bin 0 -> 386 bytes .../ERThelmets/ertchaplain.rsi/meta.json | 41 ++++++++++++ .../ertchaplain.rsi/off-equipped-HELMET.png | Bin 0 -> 828 bytes .../ertchaplain.rsi/off-inhand-left.png | Bin 0 -> 677 bytes .../ertchaplain.rsi/off-inhand-right.png | Bin 0 -> 707 bytes .../ertchaplain.rsi/on-equipped-HELMET.png | Bin 0 -> 796 bytes .../ertchaplain.rsi/on-inhand-left.png | Bin 0 -> 685 bytes .../ertchaplain.rsi/on-inhand-right.png | Bin 0 -> 706 bytes .../equipped-OUTERCLOTHING.png | Bin 0 -> 1793 bytes .../ERTSuits/ertchaplain.rsi/icon.png | Bin 0 -> 637 bytes .../ERTSuits/ertchaplain.rsi/inhand-left.png | Bin 0 -> 541 bytes .../ERTSuits/ertchaplain.rsi/inhand-right.png | Bin 0 -> 528 bytes .../ERTSuits/ertchaplain.rsi/meta.json | 26 ++++++++ .../equipped-INNERCLOTHING-monkey.png | Bin 0 -> 974 bytes .../equipped-INNERCLOTHING.png | Bin 0 -> 1393 bytes .../Jumpsuit/ert_chaplain.rsi/icon.png | Bin 0 -> 421 bytes .../Jumpsuit/ert_chaplain.rsi/inhand-left.png | Bin 0 -> 551 bytes .../ert_chaplain.rsi/inhand-right.png | Bin 0 -> 544 bytes .../Jumpsuit/ert_chaplain.rsi/meta.json | 30 +++++++++ 37 files changed, 329 insertions(+) create mode 100644 Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/icon-flash.png create mode 100644 Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/off-equipped-HELMET.png create mode 100644 Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/off-inhand-left.png create mode 100644 Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/off-inhand-right.png create mode 100644 Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/on-equipped-HELMET.png create mode 100644 Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/on-inhand-left.png create mode 100644 Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/on-inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Hardsuits/ERTSuits/ertchaplain.rsi/equipped-OUTERCLOTHING.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Hardsuits/ERTSuits/ertchaplain.rsi/icon.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Hardsuits/ERTSuits/ertchaplain.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Hardsuits/ERTSuits/ertchaplain.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Hardsuits/ERTSuits/ertchaplain.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/equipped-INNERCLOTHING-monkey.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/meta.json diff --git a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl index d8c88f3c34b..23a178d282a 100644 --- a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl +++ b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl @@ -164,6 +164,9 @@ ghost-role-information-cerberus-rules = You are an intelligent, demonic dog. Try ghost-role-information-ert-leader-name = ERT Leader ghost-role-information-ert-leader-description = Lead a team of specialists to resolve the station's issues. +ghost-role-information-ert-chaplain-name = ERT Chaplain +ghost-role-information-ert-chaplain-description = Assist with mourning to resolve the station's crew moral issues. + ghost-role-information-ert-janitor-name = ERT Janitor ghost-role-information-ert-janitor-description = Assist with custodial efforts to resolve the station's issues. diff --git a/Resources/Locale/en-US/job/job-description.ftl b/Resources/Locale/en-US/job/job-description.ftl index c9d53bb5b5e..e8db804688d 100644 --- a/Resources/Locale/en-US/job/job-description.ftl +++ b/Resources/Locale/en-US/job/job-description.ftl @@ -19,6 +19,7 @@ job-description-paramedic = Rescue critically injured patients all over the stat job-description-detective = Investigate crime scenes using forensic tools, ensure that the guilty party is found, and have a couple smokes. job-description-doctor = Diagnose and heal crewmembers through medicinal chemicals, advanced medicine, and defibrillators. Make sure the dead don't rot, and that cadavers are in the morgue. job-description-engineer = Keep the station's main engine & solars active, optimize the power network, and make emergency repairs using your hardsuit in spaced areas. +job-description-ertchaplain = Ensure the station crew's last rights are taken care of. job-description-ertengineer = Ensure that the station has power and clean air. job-description-ertjanitor = Ensure that the station is properly cleaned--for morale. job-description-ertleader = Lead the Emergency Response Team in dealing with threats to Nanotrasen assets. diff --git a/Resources/Locale/en-US/job/job-names.ftl b/Resources/Locale/en-US/job/job-names.ftl index 8cd35cac888..51a81fb06a1 100644 --- a/Resources/Locale/en-US/job/job-names.ftl +++ b/Resources/Locale/en-US/job/job-names.ftl @@ -38,6 +38,7 @@ job-name-cargotech = Cargo Technician job-name-chef = Chef job-name-clown = Clown job-name-ertleader = ERT Leader +job-name-ertchaplain = ERT Chaplain job-name-ertengineer = ERT Engineer job-name-ertsecurity = ERT Security job-name-ertmedic = ERT Medic @@ -63,6 +64,7 @@ JobChiefMedicalOfficer = Chief Medical Officer JobClown = Clown JobDetective = Detective JobBrigmedic = Brigmedic +JobERTChaplain = ERT Chaplain JobERTEngineer = ERT Engineer JobERTJanitor = ERT Janitor JobERTLeader = ERT Leader diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml index e4c094ea2bf..bbea09e9210 100644 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml +++ b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml @@ -328,6 +328,27 @@ - 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 diff --git a/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml b/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml index ca926ba18c9..a69c770f402 100644 --- a/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml +++ b/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml @@ -248,6 +248,15 @@ - type: Sprite sprite: Clothing/Back/Backpacks/ertclown.rsi +- type: entity + parent: ClothingBackpackERTLeader + id: ClothingBackpackERTChaplain + name: ERT chaplain backpack + description: A spacious backpack with lots of pockets, worn by Chaplains of an Emergency Response Team. + components: + - type: Sprite + sprite: Clothing/Back/Backpacks/ertchaplain.rsi + #Syndicate - type: entity parent: ClothingBackpack diff --git a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml index 25f641b1c4e..a35cf498f63 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml @@ -571,6 +571,21 @@ Piercing: 0.9 Heat: 0.9 +#ERT Chaplain Hardsuit +- type: entity + parent: ClothingHeadHelmetHardsuitSyndie + id: ClothingHeadHelmetHardsuitERTChaplain + 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 + sprite: Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi + - type: PointLight + color: "#ffffff" + #ERT Engineer Hardsuit - type: entity parent: ClothingHeadHelmetHardsuitSyndie diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml index b6aaf23376a..e74ace47c84 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml @@ -767,6 +767,20 @@ - type: ToggleableClothing clothingPrototype: ClothingHeadHelmetHardsuitERTLeader +#ERT Chaplain Hardsuit +- type: entity + parent: ClothingOuterHardsuitJuggernaut + id: ClothingOuterHardsuitERTChaplain + name: ERT chaplain's hardsuit + description: A protective hardsuit worn by the chaplains of an Emergency Response Team. + components: + - type: Sprite + sprite: Clothing/OuterClothing/Hardsuits/ERTSuits/ertchaplain.rsi #if you change this, please update the humanoid.yml with a better markers sprite. + - type: Clothing + sprite: Clothing/OuterClothing/Hardsuits/ERTSuits/ertchaplain.rsi + - type: ToggleableClothing + clothingPrototype: ClothingHeadHelmetHardsuitERTChaplain + #ERT Engineer Hardsuit - type: entity parent: ClothingOuterHardsuitCBURN diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml index a7ab4d3e239..5eba3917866 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml @@ -1093,6 +1093,17 @@ - type: Clothing sprite: Clothing/Uniforms/Jumpsuit/musician.rsi +- type: entity + parent: ClothingUniformBase + id: ClothingUniformJumpsuitERTChaplain + name: ERT chaplain uniform + description: A special suit made for Central Command's elite chaplain corps. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi + - type: entity parent: ClothingUniformBase id: ClothingUniformJumpsuitERTEngineer diff --git a/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml b/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml index 012ee18487c..7ad8c619568 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml @@ -510,6 +510,18 @@ - state: green - state: ertleader +- type: entity + id: SpawnPointERTChaplain + parent: SpawnPointJobBase + name: ERTchaplain + components: + - type: SpawnPoint + job_id: ERTChaplain + - type: Sprite + layers: + - state: green + - state: chaplain + - type: entity id: SpawnPointERTEngineer parent: SpawnPointJobBase diff --git a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml index b0a2571875e..20a6403ae3a 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml @@ -119,6 +119,66 @@ prototypes: [ ERTLeaderGearEVALecter ] - type: InitialInfectedExempt +## ERT Chaplain + +- type: entity + id: RandomHumanoidSpawnerERTChaplain + parent: RandomHumanoidSpawnerERTLeader + name: ERT chaplain + suffix: ERTRole, Basic + components: + - type: Sprite + sprite: Markers/jobs.rsi + state: chaplain #needs an ERT variant once a good suit it made. + - type: RandomMetadata + nameSegments: + - NamesFirstMilitary + - NamesLastMilitary + - type: RandomHumanoidSpawner + settings: ERTChaplain + +- type: randomHumanoidSettings + id: ERTChaplain + parent: ERTLeader + components: + - type: MindShield + - type: GhostRole + name: ghost-role-information-ert-chaplain-name + description: ghost-role-information-ert-chaplain-description + - type: GhostTakeoverAvailable + - type: RandomMetadata + nameSegments: + - NamesFirstMilitary + - NamesLastMilitary + - type: Loadout + prototypes: [ ERTChaplainGear ] + - type: InitialInfectedExempt + +- type: entity + id: RandomHumanoidSpawnerERTChaplainEVA + parent: RandomHumanoidSpawnerERTChaplain + name: ERT chaplain + suffix: ERTRole, Enviro EVA + components: + - type: Sprite + sprite: Markers/jobs.rsi + state: chaplain #needs an ERT variant once a good suit it made. + - type: RandomHumanoidSpawner + settings: ERTChaplainEVA + +- type: randomHumanoidSettings + id: ERTChaplainEVA + parent: ERTChaplain + components: + - type: MindShield + - type: GhostRole + name: ghost-role-information-ert-chaplain-name + description: ghost-role-information-ert-chaplain-description + - type: GhostTakeoverAvailable + - type: Loadout + prototypes: [ ERTChaplainGearEVA ] + - type: InitialInfectedExempt + ## ERT Janitor - type: entity diff --git a/Resources/Prototypes/Roles/Jobs/Fun/emergencyresponseteam.yml b/Resources/Prototypes/Roles/Jobs/Fun/emergencyresponseteam.yml index d54baba3dbd..22d0a4d77ff 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/emergencyresponseteam.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/emergencyresponseteam.yml @@ -66,6 +66,61 @@ inhand: - AirTankFilled +# Chaplain +- type: job + id: ERTChaplain + name: job-name-ertchaplain + description: job-description-ertchaplain + playTimeTracker: JobERTChaplain + setPreference: false + startingGear: ERTEngineerGearEVA + icon: "JobIconNanotrasen" + supervisors: job-supervisors-centcom + canBeAntag: false + accessGroups: + - AllAccess + access: + - CentralCommand + special: + - !type:AddComponentSpecial + components: + - type: BibleUser #Lets them heal with bibles + +- type: startingGear + id: ERTChaplainGear + equipment: + jumpsuit: ClothingUniformJumpsuitERTChaplain + back: ClothingBackpackERTChaplainFilled + shoes: ClothingShoesLeather + head: ClothingHeadHatFez + eyes: ClothingEyesGlasses + neck: ClothingNeckStoleChaplain + gloves: ClothingHandsGlovesCombat + outerClothing: ClothingOuterArmorBasicSlim + id: ERTEngineerPDA + ears: ClothingHeadsetAltCentCom + belt: ClothingBeltStorageWaistbag + pocket1: Flare + pocket2: DrinkWaterBottleFull + +- type: startingGear + id: ERTChaplainGearEVA + equipment: + jumpsuit: ClothingUniformJumpsuitERTChaplain + back: ClothingBackpackERTChaplainFilled + shoes: ClothingShoesBootsMagAdv + mask: ClothingMaskGasERT + eyes: ClothingEyesGlasses + neck: ClothingNeckStoleChaplain + gloves: ClothingHandsGlovesCombat + outerClothing: ClothingOuterHardsuitERTChaplain + suitstorage: AirTankFilled + id: ERTChaplainPDA + ears: ClothingHeadsetAltCentCom + belt: ClothingBeltStorageWaistbag + pocket1: Flare + pocket2: DrinkWaterBottleFull + # Engineer - type: job id: ERTEngineer diff --git a/Resources/Prototypes/Roles/play_time_trackers.yml b/Resources/Prototypes/Roles/play_time_trackers.yml index 242dc81fa8e..402d49e90d2 100644 --- a/Resources/Prototypes/Roles/play_time_trackers.yml +++ b/Resources/Prototypes/Roles/play_time_trackers.yml @@ -46,6 +46,9 @@ - type: playTimeTracker id: JobDetective +- type: playTimeTracker + id: JobERTChaplain + - type: playTimeTracker id: JobERTEngineer diff --git a/Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/equipped-BACKPACK.png b/Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/equipped-BACKPACK.png new file mode 100644 index 0000000000000000000000000000000000000000..4bcff09c29dcc40bca6b9f629012361f6a19a8f6 GIT binary patch literal 806 zcmV+>1KIqEP)+#Rp7_XG7l~GD{=7g$ooO1|%f!pV22z~+1`5*Vob`_LT zmI`RCS=;vaa6q2t@H`Iy(ChWew$*9{05HZNNfM0543JJOb&2KcQTpOI;gJwT|z)9 z^&-=2ty$NzKOT>nbF$kC9qvBQ^VYi%!wFLT=IHX)@wf%HEE${!fzp&Lm#rNMn;Pcn%PVY6=1%CbfVgZzOcvqW?qR2*3bo7-` z)x+s0_yvA^|5)l?2W#>w=Nv+atr~oF_M<4W%jJ?C%lPWpQc7Dbns}bqSS)D_0b>lp zFvNU5hwu9@I_vlQ0AMdKFQK)@VzKDd8F3t2DJ32sAJOmkv0N^0L zwAQsUPS;+q*LZ~PtidQ|jA=Z3!x%e=@l_`<#u|68EXy#P&Dgswz1K4DA(nlePOt4q k$JB`+2!bF8f*?+W-_fgZDzF}qVgLXD07*qoM6N<$f=n8HDF6Tf literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/icon.png b/Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..02b99ef372dde424ee315c9f738b6eb24df38fb9 GIT binary patch literal 553 zcmV+^0@nSBP)e&0gJ`r+IH__m&CK#?E3S^Cjh{gZ_g6H zjTJ>P>XP%ZZD9Z)B24Kc03c0MS+8S61OV5ns#+8H(55&M)yeVo`!4{%(`JLuFE4o7 zY#MPQLKucZ`aO)=5|GnQrvQL33?){d*f}SAcDr3GIa*2Jy_eHjYoU~qVl|iVpj zzu$wIF&>X`KA({!35ueC_a4j)V+_i&6e9*ffc1L)?;g-vOG~}?P)dQBk>@##F|seu zbCIem?n+>cL6&8I_yGXQvTQt+Qn1#-T8q_ch1ZkwR|J;J<>+`kBFi!mQDgMpLLA44 z;~4Y#Tuj;PW%M4RC~9=27XsVuwlP|@(V{5AWHOP0%-pGP>${+yhTAsRL|*&CS}U=` r;V|lrz*}F%d!O#%mu$d*0bTGL{=3+lXtJwH00000NkvXXu0mjf8H4;% literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/inhand-left.png b/Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..f6bb073134283cbdc680fe8c6efca3e239231330 GIT binary patch literal 654 zcmV;90&)F`P)AmOoo=x-z2!bF8f*=TjAP9mW2x2m<*K0ra9(PlB==A-Ims0x4 z2cW7dFQxQSN*|04S1F~hs_H5ye*OIB0YLBvaLz}2DEG0P4`a?b1b@Ku>uYbkG3KiE zq?G>i^A17?eENF6jS?`%AkT9+=kWA$!jH#CJiVOYoI{@HFvg(ky20x?=TMd24~EN2OzwroO9ruLn#I49F$Vv zocEex`C8}z@=)+stp5ClOG1VIo4K@bE%5Cjp0 z6rzThc8nsB7+cg3Q;QHa04YQbm$V4i67BzbDW&I}LkNNM`H!gKcsyof1R!V;0AMzo z^>bn0a4uQb^~L?~V;UDk4g39mv@^ZfT7+mN(6()+wMGh2Lrg6~)C@=^Y6$fo2wH@2 z{~AqF)Fzlh)DT;Xa2M0CRHBBMT7;+>kV4d;l)`*IzZjsQT7;WCwqC7PzG<4i4+tTk owFZE`{)aZ`D*MF0Q* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/inhand-right.png b/Resources/Textures/Clothing/Back/Backpacks/ertchaplain.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d527a7daf963ef3d4a0fa825573ad24bd4cb8453 GIT binary patch literal 708 zcmV;#0z3VQP)4E4r~CqGR`dg{&vK7Q|G6lz|~tMo?HA z--P52m0hm<%k=^z?fa8Zz>ebh_4~XQ@&gbAK@bE%5ClOG1VIqQKgDo3H1&G$3z1S9 z`}zLx&U8ARqwjgUuQ3cmvtF+O01uz~czZu~R{-FgS8}!6?Y7x$HtzidZ{X|aI{ifPA0BE&Zt}jhf0D#sSN-0E97l3+1VIo4K@bE%5ClOG1VQ}wT*5O@_82!sZw$lm ziSc0jDy1~r$Li;1d)$iz;FM?JQvCS#!2odMmNB+hGyvE34A`S?A;gn0UwTe)TjZvy zcm}lA2!a69=@g8yQVwYf*MMt#29hN4vMj@Lxh!q+g)?AV|CVcb296mW&YA&L@eIUq zT>2O{&sqIzdIq*lPxgAT04Sxm1hOmx q0H^;t9-VuClZyK)H2o2%DL(=0000_ukU zm=#5}=6M1880TF5Bx|Q>h>k=Z8(J^cip~g`ZnRjksVeu#N4gRRh Ud0?3-o&W#<07*qoM6N<$f-yIu{{R30 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..32457b191649cfc7adf9e99ae9eae333bacfa4d3 GIT binary patch literal 386 zcmV-|0e$|7P)%UNfL zyIagR%Na8ER)W;K^5X#Y%01V?Zht^^O%+191J%lR`vW}Jx#&bM0M*Kh%IkNWPMwdU z6@Wa7vunMW%H!ut<9gfg-m5@l>HAsixlZGHFJm|y4yC=_!Z*#U`_eqe*I)q7xzWo{ znC5x;91wzlkj7kZQ&1RHjRQhDnJWX#lLYf5>72g_fOBqyVF<<;eAC1#%gWp;%ix

L6z8)S)TZ(g$7Q zW($f_X+UuZiFELwX^;p$F?mgU9pYWzvzC%)?v{Q(I9~1^?|uL8ySpUtKfnjoQik*M zIzRpNg!A({ua+_#nh_eb#}OcHTnhxQ&=${Bf`1IOvQH z=_$hHWPK*za{t?}13J8!970mJ7F92^_~0CsI#t^fdXTs3>G8NAGA@uhwy$L@>|V>+`0Kyd-e zM<)Ts7|#`pcr!OA$8*IZ7-J|Oodi@SFXB&UmSCEu9A}Kl`^vXKHk%EIFFp7E7vriC zaTsH=T_?V8(UxV&-`r#}sU>-WAP9mW2!bF8f*=TjAPBAe%Y4-34);?-uwMdnpAYDl zhMo%$P2{^abz@()YAM5`iM+Oz*iQ{T+v8|85uIE??Zcu+5L!)rd$H9-G?7Pb`{{2D zlPiGT&`^Nz2w*qhskY0XUrmLBk6*Wbs>pja6%I-b7e$cR4PQ2S(3cVsNX&Y8MB3J> zl6Sw`uB=_D!4KCuA>w8#v5uHwVBIwNBgT|iW$UKNpFMboisN8wyFc&jxd6h44;{Aw z05QYx9GFl|JQ4wHTNYfHvz^f)ynT^Wy9G9D6WD9b$bbN3^1>}vjc}wdCl7Sb4s6yY z)UAN_&cXn@_x#7h#k|L#Urwi`pt#}!xRcKni*j68kjX=q3~jP^suDUxqq}uc z$y6F>@ZgV5c|-AFLIibH)7Qb4B0FGPLMndd{y<2+k0;(ax|R*^00co01VIo4K@bE% zEDN1Rne$&id8bk4!Y=;y>zha-y!rf=iD!bp_<#XnL`;b9T_tXU6u0=?)*+nc(Y-N6 zHPE&9@x1U}nl~TAAu#9z8;|bJz})#!SdZP(;Sh>y_zTWy_0aQ!qNh!Wo7fF-T5tX9 zr%xVFd|sGou?dNf-GD;XM*8|1sx03dYq>6Nve{4u{twoZxM|CEp~^Dyih>KrLCbX) zHn8zdn>ijgYjNN3!{sFaAg?GBXRkhw?}Hr_sy2$dXE-RHqquv9Le<8n?h7$GpxJB! z04^NI?|@)^7VUOhVvKRaFp$Y){5oUIA7APY7z;qnG$-mc(*$E|Apw<2Me;;@N3bpg zmSyqHREn3gnI|%6$Nk{ zY-`6*W!Vp3++?#e!xby@E8rzgro{uJa7EZ4g>W4Z6d%kXcv*tqUtT|0Sp%{sCqSv> zi}%749#KLRLK*PmEqI literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/off-inhand-right.png b/Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/off-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..2b20dab1727e465f099f29f3af67d69bbbe1e3e6 GIT binary patch literal 707 zcmV;!0zCbRP)4E1FeGbi?xm=nyH{DBrA2{ya@80im-|xLgF1#N=5ClOG1VIo4 zK@h~E(5>e=|8>H<^*r~TeERi?|3LyEo}aS$pWjIyGe9I0T;lsj7n&eVCdZRy7_Ew@ zqoS42Ydpi9)N3io;^WCOFdPEr0EjI8KLb;rKkON0IE12=oP>B{Tdc)DE*|ihFp0+i zqy5rp-@bLzbuA2&V$TupF(6fJAa;BVO_n_!+m?l&D=UH5KJN<)PYbp!3z{rrT~W|9 z474pvh!>wKHc)?+n0bGa&}87v!2tkZT~Sc3GnNFu8F~)eQRD!CiPMk63+@CoEMgw50T6J~Ws#d`mn>s+y zC=?2kE!sZDhGz7~!0TCFD3m*6}|vrMqt>2z8GK@bE%5ClOG1VIo4 zK@h}Mv=BRHu{M0+!^Ko@njjjPfuN@ zz#Lo=10qX+(RUY4j1?eMvXRiEx8~o4T>_2?w(!xly)Q44cwaN%yaI&#iCt*2j9pdb z4;hmd)_{{AeW#KH05n;46;v$rufTq96^G^q0666`w)nPXIkf~LG1pYBWFw&)_g?$~ z;3`1a$>kgq9xN|Iq$S|%L3Lesee|fRik(~zz*#YcNZaXQKS{s8Jiaz+^3UJz;={)# p*L9t%s)}##ve)TP13?g?@DInYUutRUbie=r002ovPDHLkV1o6VJ68Yz literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/on-equipped-HELMET.png b/Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/on-equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..c626307585b4001e40b7efc5330c250a9ed26489 GIT binary patch literal 796 zcmV+%1LOROP)c2h(z=x zVK`Wy!TYKJ5o|0N#OC=f7N+M>?f116z_=?eFzN%FM@NI}`L6eRv#yfFQU$H=?Z1x1 zQbm0QoSvQn02Zd_Wd*p`RdehlkXaZ#=pY#WR* z)M_9u{yG`B~lsZhg|59dzJCLbL0dF9e%3W1OO>fi-rl+2uWTfT!^z5kqFA~;%bk; zPGbhgPEr~KZo5TZM?4iYG4tjj1Q^CLUV4IHQLw8Ctw|iUVDg&0Rtl zcT4HeV30q$G&iIkoDf4@)%5LPOOX;%TSg9&d4F(NI`Nu&9-VZ8djN)E7=~dOhG7_n zVS+-ZULg8)N}YOvLOb}2FK^}&Vb#-x44(=1;2j2d5#bWwJ6^a6ir?as>pK`UcduPr zOO?=VY~xApy)vyHqY*Id1FQEhe}Sp>^KjR9OGhKDrAqdIlbMR=dZ*~CCiso-1`Jwn z?c+xeAGpqE=Bn5QiTB-r+*Sk8lM|#Ok!#mlrit@J!jplw!O9~3v}KwAfW5Or*ZC|4 zp8o9J*m7HqV6Ol|2r>+V(yB^CM|MlADj9}BLI|2xuj@JqAtJ~|3BL8wj!mZQmKS^JZ|?(Dedclegq~0P_ETn{pDH> zQp%YG6pKa05^XhSe<;v(-8F@ET@UP+oU%w0tYb~n!gKc+hG7_nVHk#C7=~dOhGBxl zv~6;)+vqFHWWqcdf>0We%v8ofir%r^I`sm1-ipT}@&UKOu#aS>f^K8`(vknsD9mL2s@cL8u;qSNp@iz$8Opi5>P~n=%KXngK*dl#gZ6GEIE)DJr0Z z%>ZD$_1-c~q#_YJd~u#gp#Clc0G@(Wp<4xR5)T01h8y_UeY)JMfK$9PhhSw1et&*_ zXK4*MIQU_kV1+K6%oCyrl>y&BY~Cx^_WwvJp=p}3@#2~C{PEY&y~`Me2@d}N{%4EZ Tbx!(200000NkvXXu0mjfk>frD literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/on-inhand-right.png b/Resources/Textures/Clothing/Head/Hardsuits/ERThelmets/ertchaplain.rsi/on-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..dea3b36335664d515bf11ca6e18d20c6d8b2dca4 GIT binary patch literal 706 zcmV;z0zLhSP)#A1mh-2f?w z0WncU%0H%BqGU)>qY|vZV`yybI88}da_Rd?me1#Vr~5wN$+qqR48t%C!!QiPFbu=2 z3Y}Vk=*J;-Y6V(2`1sR<#XDI7@pNp>KYlmZV}ML1_{8`2&ox1r434ME7&Iz@hHHru zy7fo6k$J8xv-o(r42(v=un$Dm|DJ)V;|~XV8I7=(D7gXg)Q((>-zy#nm@rAefI;ht zTfTnns_$AFCdHm1K43s*tB&Zw0TQYjXxOrCeBanudhPQ*u<*3XmTe=Us(5j9gw0qC zE!&pj!DqJWs69>1ygw5m1T~vYN@^N;NopE3n@tizEcKf}2tlT4k`RKDnnw9(l#-f8 zLI^SpW9s+=GoaCE001^)F?R-d>zA2!yRBH3MWs>+x~{wRmSws3Cu2fWI>2_d>T9%J zt-`XbsU0jE#bQx$L_3GK{+}>S(>H}o)0FBi0UuXuLm4I+3^$ZDB1rd>= zD9XwjaQLm~W|9CvLREbU6)XJ~*z4ZH=iyxdaMNWR@h#hSa|vW(uBlqdMM76@KmG;4 zmw>RF&$}jkJUM|(OJJda8iwKf=+Si@yZJnT(_{*nw$sCl1pRz}|I)Zf@4nu|>o*_B oFbvXl9baDMF7r~y#N3J literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Hardsuits/ERTSuits/ertchaplain.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Clothing/OuterClothing/Hardsuits/ERTSuits/ertchaplain.rsi/equipped-OUTERCLOTHING.png new file mode 100644 index 0000000000000000000000000000000000000000..2acce2e63e81c2322058aa312a39e30beb17f622 GIT binary patch literal 1793 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 S^8MZb0000~L;ryO3%xEB_EMj6EjjLK zFI#%>p_heRbWkI#1Q{coQ8If-q?Hr1SYyj=>HA#7JjVLI_vVepwjMwb1VIo4@spy} z{eW%T5&+UP)&9@c6r6KuSr#0}DQ%aRmk>fAj^o<;dY#Dw#^jNv!Sg(D&Sl|{=Xp5N zGz=J%O$I+;Og_81nhPEP9LK@4t1Bgay-wzbC&&15Z~((F;JPk2=hAgu7>0o_2L~9Q z9G8h{V&#V?#~AJH!7vOsjx%>1?d>V)YjqspoMU@?yR>Cl2F6%9&hs2m6e*SBvigQ$ zpxtgG%QED74%0Nj7{g>T0f5^2=Cb0h>%uf|rx!t26Od&YOw)wtc>o~SnS?^Wn)oW! z@!h z{#q*F&)~z@<5>IW<8Si6dGFVjfO-A*8UWdDx95@) z$FcU;A4iSoKPbm{j~+?Rxtx4@Bdg<0oZFmpIbcjKx^CoqK&R7@tyb%SeL9`u;^JZ> z-vf4ccVSr;Ow&Z3=MX}aRap0aCVo(a5V*Oy!S(g^O7}5USARO4miAVwg*&@!vI?rF z!TsYd2m+~c`Y2Vvf^uKDEt=wf@SJlw8jX|^ud4+AJrV?g>~^~n1VIo4K@bE%5ClOG z1VIq%#&H}uU`)Q++4<&wLcZGBkpsr0<2Xvb=qkbcz7IVJ@OJnTg|MmvstZtD-wt0w z4+8kUujGT$djZB6(lo`>*QfA(9{}XHs6yZO@$~g6Y}=&Lv(rGyXyNs^%7@1xi2HC_q5UQhP>eI!W&Aq1pc zO|sS&`1`|)TPM04zdZSj^EL0DJ;u^5CQ=3cs9f7yC?X19^a0e>J3(&MPd>eAyaI}zgsS!`4`8Jiik(hJ-t_{_Js8i<&KhZus=_&!`}_M3`h>zTRO(OA zR2--_ECE=lK|wVMMUzAol@2w19S&_WcmRmwSR0SWYq^)MB3|vt`d(1&Pt;UP@V+h2 w^NsWf70;leZSVf#&<_=YAP9mW2x2+@1?2LtutcvRxc~qF07*qoM6N<$f*f+y!vFvP literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/equipped-INNERCLOTHING.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/equipped-INNERCLOTHING.png new file mode 100644 index 0000000000000000000000000000000000000000..ef532d7232b175a3c1a55cb967d2cc3ef9f487c8 GIT binary patch literal 1393 zcmV-%1&;cOP)z}D6lmk)r0LaY0PzE@TgE)>s=}80#A>8o!>rSTw%d&V)t_o-Qecw;rXX8Bl(na2HG#cpj zdI-aild;H)qR7V#VHjdC7@*N;06?-#7M#Z)MG*i%RaJPN$D_~WS@}Pj0k^{tPap5& z)^M0gCqf7>had=2Wk4wvZViWc`gk9=PnFJ#5AjP0q_Yz9#jxdYgkQVL3`aP{idghgZ73^30MD8g(u!*n`bieJoHleTT+ z{$PMstA)RRzl~O_h5Lg6Y};PC<`?Dv_T4+!wvBIEt<*RVf2LHe)oMwlQo+W?1^{3( zneb}>7>!2g_xpuC3fJrPL{SuMZf^4Bkv$cT$76(H2+OiE@nqbPzTNr<9|uhHuOA>=({Wm!(mM}<3pp~!S! z1XRN|Y#3qC|-j zB}$YiQKG~J$hD)gPYCb6(t4fC-@bjjmK`F~G&#RvSzMmyY=W4_cv`PxXqvd8D0ubc zPVW7UW6>m=yW>ob&Lyj z07H@_h@uF+UJr2`uPp=OI7YA6Lli|wlI2e0WOCb&NrGR0`wm~#4Ro!q@%_(#@hG^i zyS53?bzR7^%=aM=|2#O=iQ<=^p5Tw7mO)+D6HU`V2tlXQ!IdjlpsFgb$#%P)`@G(w zXf~Tkv)M!t1c;&tp66jS8lll>!1sL&1_Kl}32K@K*LBhDb|DA?#^dpSKO~C0Rb3>e z(`o7~z<4}{APDGoyKr3>nx++30_VZ=JgBO=mTmF*5@6iRB2xz3bR0;Mgdb)z{uzcL z9(W#X+s5l>_X;=u^|O1hZ5t0f4`CRlbde+pH#444%X~iItZZv*i?6?w(!v|2wOTDP z3Vauj}B#feAyqk%*Ny@xW#1^==b}V z=74^`zqAv+_!7uH3FcjPRy`5^AM_|u;uFTdTz~v`=n(6x00000NkvXXu0mjf)@PH= literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/icon.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fdaf39cf4e3d1ba2d10afad0a4268e2fdc712800 GIT binary patch literal 421 zcmV;W0b2fvP)C`i*ZodA(i}Lb%NkUdlR~ z&7$K=MT(*Tz&Qu-=j>Zmp!sOix%d7@8`?N;6+pZoM9jE33qpuad!W-TAw(>Fk|dp- zcM9NqKGST)Z~B`BuqaEMb1Zi|thKy6EwI+I-0g7Au_(*iuHxx*T1zPz4u=4oPNygX z9FIq~+wE2>Z+=m|9g6RdRp-XGfI}u<}m8X P00000NkvXXu0mjflr+R; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/inhand-left.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..ed878f2d3103e4c5a3f2cefeac70f50d2a716422 GIT binary patch literal 551 zcmV+?0@(eDP)U*}1Z zAc~^1v(M&5QG_H(&dyGo@ZU_p<7$Q1mnW#j0!`DPsw$*uYO*XdP18Uv7I=Mm!sBYy zDKE=1lcuSustR55{^h|&$lLqJhiTguVHje&-C{T#Vzb!*09HS~0SK+-9iQ9a*}4PP z>ot^8XxkRM-427n0He_eWm#e}nLuleqA0vu=iTHE&{{(Xfx50?jDeIA#u(If4Iu=y z)_2XjY6FB2D2l=pLLiP~D5apahEfV~9G^YEN8Z)H0st_d&p$3arLf=cp|ysT68(N3 zWm#f89%H#&diTn^iRUmhv+2})pUuql)BeS`-WC*W`U2mo+696tU3p>q>pdmq8c39z|8;JJDS*xDa(@>js-{(y50 pu)ROvxi|q%?hn|;{Q)8?egb{&cSPq9aohj^002ovPDHLkV1g5?14{q^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/inhand-right.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc2bc1ce223aaeb176ab190a665d4cc6d642d26 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV0`ZB;uunK>+LPau15|cY!Biu zPwNPCQkv9sFD*6fX371f{8wl1^ol>ccD0Ox@v(-GrjG7eu9qjQc>7{Sz|}wZ)dD&{ z?RQ@)P{SYy0}KkDLd83#rOve6n&*8_=kyuX(k);2|2EQm8u)SFduK1s+p|7Qz5cyc z#_VBy(dv2EcYdGxKjz+K>jU3wTM|1g`mD3xs@+%;TVow9 zbDZPwhn#zh+4ign`%zKf8l@9(-Sm=)spq5_n{)!Nn_fw3?EfAPlnfK8pRrxXTg5X8 zC_DX0t<}~azt2BE?0Z>(gDGcwbdQ^Ij@j&Uwb6GbZU27PdTZSJg;Sg^o7{L?Cc)P( z(SKZ{i|O)9k*Mq^mG?G3<@s@ckNSCrwWcisD^_tGlkm_?>~RbY6>xIM&b`XFL-l~Rp7~Q%_b%ps(}7d*P3xYjD#5^}`Ps(I(_h2~@9{QfF3)WM z^7wBut~<+fXDf(|dHnJcd+cn<9U<>$86TKBv$1^+Lh{aEQHDFSB`Y}ovHO2{{D*aQ XbCJ#2@RbvRG0))X>gTe~DWM4f5(xZz literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/meta.json b/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/meta.json new file mode 100644 index 00000000000..dca2585d6be --- /dev/null +++ b/Resources/Textures/Clothing/Uniforms/Jumpsuit/ert_chaplain.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TGstation: https://github.com/tgstation/tgstation/blob/c838ba21dae97db345e0113f99596decd1d66039/icons/mob/clothing/under/security.dmi. Modified and recolored by Nairodian (github) for SS14, monkey made by brainfood1183 (github) for ss14", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-INNERCLOTHING", + "directions": 4 + }, + { + "name": "equipped-INNERCLOTHING-monkey", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} From 085a71eac8d85d2ae65952d24343bf34b19b0ae5 Mon Sep 17 00:00:00 2001 From: f0x-n3rd <150924715+f0x-n3rd@users.noreply.github.com> Date: Mon, 1 Apr 2024 15:20:37 +0800 Subject: [PATCH 117/133] Changes in chemicals page in guidebook (#25831) * Added pages to chemical categories The chemical categories have their own page now. Added the "Chemical Tabs" in /ServerInfo/Guidebook. Moved the Chemicals code from shiftsandjobs.yml to its own .yml file which is "chemicals.yml". * Update guides.ftl * Update chemicals.yml Changed the guide entry's ID for the medical tab from Medicine to Medicinal. Hope this works... * Update Resources/ServerInfo/Guidebook/Chemical Tabs/Biological.xml Co-authored-by: exincore * Update Resources/ServerInfo/Guidebook/Chemical Tabs/Foods.xml Co-authored-by: exincore * Update Resources/ServerInfo/Guidebook/Chemical Tabs/Elements.xml Co-authored-by: exincore * Update Resources/ServerInfo/Guidebook/Chemical Tabs/Narcotics.xml Co-authored-by: exincore * Update Resources/ServerInfo/Guidebook/Chemical Tabs/Toxins.xml Co-authored-by: exincore * Fixed a few errors and stuff! A few typos have been fixed thanks to exincore. Added dedicated .xml files to be used for the dedicated category pages (Medicinal and Botanical pages). Made it so it doesn't use any duplicated IDs anymore. If there's more problems, please do tell so I can fix it! * Update settings.json * Fix? --------- Co-authored-by: exincore --- Resources/Locale/en-US/guidebook/guides.ftl | 8 +++ Resources/Prototypes/Guidebook/chemicals.yml | 63 +++++++++++++++++++ Resources/Prototypes/Guidebook/medical.yml | 2 +- .../Prototypes/Guidebook/shiftandcrew.yml | 7 --- .../Guidebook/ChemicalTabs/Biological.xml | 9 +++ .../Guidebook/ChemicalTabs/Botany.xml | 14 +++++ .../Guidebook/ChemicalTabs/Elements.xml | 9 +++ .../Guidebook/ChemicalTabs/Foods.xml | 8 +++ .../Guidebook/ChemicalTabs/Narcotics.xml | 5 ++ .../Guidebook/ChemicalTabs/Other.xml | 5 ++ .../Guidebook/ChemicalTabs/Pyrotechnic.xml | 5 ++ .../Guidebook/ChemicalTabs/Toxins.xml | 5 ++ 12 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 Resources/Prototypes/Guidebook/chemicals.yml create mode 100644 Resources/ServerInfo/Guidebook/ChemicalTabs/Biological.xml create mode 100644 Resources/ServerInfo/Guidebook/ChemicalTabs/Botany.xml create mode 100644 Resources/ServerInfo/Guidebook/ChemicalTabs/Elements.xml create mode 100644 Resources/ServerInfo/Guidebook/ChemicalTabs/Foods.xml create mode 100644 Resources/ServerInfo/Guidebook/ChemicalTabs/Narcotics.xml create mode 100644 Resources/ServerInfo/Guidebook/ChemicalTabs/Other.xml create mode 100644 Resources/ServerInfo/Guidebook/ChemicalTabs/Pyrotechnic.xml create mode 100644 Resources/ServerInfo/Guidebook/ChemicalTabs/Toxins.xml diff --git a/Resources/Locale/en-US/guidebook/guides.ftl b/Resources/Locale/en-US/guidebook/guides.ftl index c30b49553c4..496e38b9a09 100644 --- a/Resources/Locale/en-US/guidebook/guides.ftl +++ b/Resources/Locale/en-US/guidebook/guides.ftl @@ -22,6 +22,14 @@ guide-entry-cargo-bounties = Cargo Bounties guide-entry-salvage = Salvage guide-entry-survival = Survival guide-entry-chemicals = Chemicals +guide-entry-elements = Elements +guide-entry-narcotics = Narcotics +guide-entry-pyrotechnics = Pyrotechnic +guide-entry-toxins = Toxins +guide-entry-foods = Foods +guide-entry-biological = Biological +guide-entry-others = Others +guide-entry-botanical = Botanicals guide-entry-ss14 = Space Station 14 guide-entry-janitorial = Janitorial guide-entry-bartender = Bartender diff --git a/Resources/Prototypes/Guidebook/chemicals.yml b/Resources/Prototypes/Guidebook/chemicals.yml new file mode 100644 index 00000000000..508b61b4ac6 --- /dev/null +++ b/Resources/Prototypes/Guidebook/chemicals.yml @@ -0,0 +1,63 @@ +- type: guideEntry + id: Chemicals + name: guide-entry-chemicals + text: "/ServerInfo/Guidebook/Chemicals.xml" + children: + - Elements + - Medicine + - Narcotics + - Pyrotechnic + - Toxins + - Foods + - Botanical + - Biological + - Others + filterEnabled: True + +- type: guideEntry + id: Elements + name: guide-entry-elements + text: "/ServerInfo/Guidebook/ChemicalTabs/Elements.xml" + filterEnabled: True + +- type: guideEntry + id: Narcotics + name: guide-entry-narcotics + text: "/ServerInfo/Guidebook/ChemicalTabs/Narcotics.xml" + filterEnabled: True + +- type: guideEntry + id: Pyrotechnic + name: guide-entry-pyrotechnics + text: "/ServerInfo/Guidebook/ChemicalTabs/Pyrotechnic.xml" + filterEnabled: True + +- type: guideEntry + id: Toxins + name: guide-entry-toxins + text: "/ServerInfo/Guidebook/ChemicalTabs/Toxins.xml" + filterEnabled: True + +- type: guideEntry + id: Foods + name: guide-entry-foods + text: "/ServerInfo/Guidebook/ChemicalTabs/Foods.xml" + filterEnabled: True + +- type: guideEntry + id: Botanical + name: guide-entry-botanical + text: "/ServerInfo/Guidebook/ChemicalTabs/Botany.xml" + filterEnabled: True + +- type: guideEntry + id: Biological + name: guide-entry-biological + text: "/ServerInfo/Guidebook/ChemicalTabs/Biological.xml" + filterEnabled: True + +- type: guideEntry + id: Others + name: guide-entry-others + text: "/ServerInfo/Guidebook/ChemicalTabs/Other.xml" + filterEnabled: True diff --git a/Resources/Prototypes/Guidebook/medical.yml b/Resources/Prototypes/Guidebook/medical.yml index 9ea2398a7ce..95a4f1ca75f 100644 --- a/Resources/Prototypes/Guidebook/medical.yml +++ b/Resources/Prototypes/Guidebook/medical.yml @@ -47,4 +47,4 @@ - type: guideEntry id: AdvancedBrute name: guide-entry-brute - text: "/ServerInfo/Guidebook/Medical/AdvancedBrute.xml" \ No newline at end of file + text: "/ServerInfo/Guidebook/Medical/AdvancedBrute.xml" diff --git a/Resources/Prototypes/Guidebook/shiftandcrew.yml b/Resources/Prototypes/Guidebook/shiftandcrew.yml index 9df1b260084..3c4618902e6 100644 --- a/Resources/Prototypes/Guidebook/shiftandcrew.yml +++ b/Resources/Prototypes/Guidebook/shiftandcrew.yml @@ -19,12 +19,6 @@ name: guide-entry-survival text: "/ServerInfo/Guidebook/Survival.xml" -- type: guideEntry - id: Chemicals - name: guide-entry-chemicals - text: "/ServerInfo/Guidebook/Chemicals.xml" - filterEnabled: True - - type: guideEntry id: Janitorial name: guide-entry-janitorial @@ -47,4 +41,3 @@ id: Food Recipes name: guide-entry-foodrecipes text: "/ServerInfo/Guidebook/Service/FoodRecipes.xml" - diff --git a/Resources/ServerInfo/Guidebook/ChemicalTabs/Biological.xml b/Resources/ServerInfo/Guidebook/ChemicalTabs/Biological.xml new file mode 100644 index 00000000000..6f1a4e630be --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ChemicalTabs/Biological.xml @@ -0,0 +1,9 @@ + + +# Biological + +These reagents include chemicals that you can get from certain materials and from living things. To get the other chemicals you have to use machines like the electrolyzer and the centrifuge. + + + + diff --git a/Resources/ServerInfo/Guidebook/ChemicalTabs/Botany.xml b/Resources/ServerInfo/Guidebook/ChemicalTabs/Botany.xml new file mode 100644 index 00000000000..832e32d6447 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ChemicalTabs/Botany.xml @@ -0,0 +1,14 @@ + + +# Botanical Chemicals + +These chemicals are used by botanists. Some of the chemicals change the properties of the plants. + + + + + + + + + \ No newline at end of file diff --git a/Resources/ServerInfo/Guidebook/ChemicalTabs/Elements.xml b/Resources/ServerInfo/Guidebook/ChemicalTabs/Elements.xml new file mode 100644 index 00000000000..3a1c587304d --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ChemicalTabs/Elements.xml @@ -0,0 +1,9 @@ + + +# Elements + +This list contains all the basic reagents used to make other chemicals. + + + + diff --git a/Resources/ServerInfo/Guidebook/ChemicalTabs/Foods.xml b/Resources/ServerInfo/Guidebook/ChemicalTabs/Foods.xml new file mode 100644 index 00000000000..aea68d74cee --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ChemicalTabs/Foods.xml @@ -0,0 +1,8 @@ + +# Foods + +These reagents are mostly used in the kitchen. Very helpful for Chefs and/or Service Workers. + + + + diff --git a/Resources/ServerInfo/Guidebook/ChemicalTabs/Narcotics.xml b/Resources/ServerInfo/Guidebook/ChemicalTabs/Narcotics.xml new file mode 100644 index 00000000000..0dbb5ad3597 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ChemicalTabs/Narcotics.xml @@ -0,0 +1,5 @@ + +# Narcotics +The reagents listed in this category includes stimulants, hallucinogens and other drug-like effects. + + diff --git a/Resources/ServerInfo/Guidebook/ChemicalTabs/Other.xml b/Resources/ServerInfo/Guidebook/ChemicalTabs/Other.xml new file mode 100644 index 00000000000..31ad4ef3cbb --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ChemicalTabs/Other.xml @@ -0,0 +1,5 @@ + +# Other +These are the other regeants listed in the Chemicals page. + + diff --git a/Resources/ServerInfo/Guidebook/ChemicalTabs/Pyrotechnic.xml b/Resources/ServerInfo/Guidebook/ChemicalTabs/Pyrotechnic.xml new file mode 100644 index 00000000000..9941e2ff368 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ChemicalTabs/Pyrotechnic.xml @@ -0,0 +1,5 @@ + +# Pyrotechnics +These chemicals are flammmable and causes hazardous effects when making them (Plasma gas and explosions). It is recommmended to make these chemicals in a safe environment. + + diff --git a/Resources/ServerInfo/Guidebook/ChemicalTabs/Toxins.xml b/Resources/ServerInfo/Guidebook/ChemicalTabs/Toxins.xml new file mode 100644 index 00000000000..19ab91cbf4c --- /dev/null +++ b/Resources/ServerInfo/Guidebook/ChemicalTabs/Toxins.xml @@ -0,0 +1,5 @@ + +# Toxins +The chemicals in this list contain toxins that induce certain effects and can cause death. Use responsibly. + + From de8b78885650f2a7013e6f203be1ec17b264687b Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 07:21:43 +0000 Subject: [PATCH 118/133] 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 f903f70f292..27a7a03a568 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: Minty642 - changes: - - message: Fixed Reinforced Glass Recipe in Ore Processor! - type: Fix - - message: Added Premium Material that can only be processed by Industrial Ore Processor! - type: Add - id: 5786 - time: '2024-01-25T08:33:56.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24345 - author: Blackern5000 changes: - message: Smoking now causes mild lung cancer in the form of cellular damage. @@ -3807,3 +3798,11 @@ id: 6285 time: '2024-04-01T06:41:14.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25367 +- author: f0x-n3rd + changes: + - message: Added pages for each categories from the chemical page in the guidebook, + no more infinite scrolling when looking up certain chemicals. + type: Add + id: 6286 + time: '2024-04-01T07:20:38.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25831 From a4ec01d4719e9489b5cceb23357b3be0ea4b86e7 Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Mon, 1 Apr 2024 11:29:13 +0300 Subject: [PATCH 119/133] Anomalies behaviours (#24683) * Added new anomaly particle * Add basic anomaly behaviour * +2 parametres * add functional to new particle * add components to behaviours * big content * add shuffle, moved thing to server * clean up * fixes * random pick redo * bonjour behavioUr * fix AJCM * fix * add some new behaviours * power modifier behaviour * rmeove timer * new event for update ui fix * refactor! * fixes * enum * Fix mapinit * Minor touches --------- Co-authored-by: metalgearsloth --- .../Anomaly/Ui/AnomalyScannerMenu.xaml | 6 +- .../Anomaly/AnomalySystem.Scanner.cs | 104 +++++++-- Content.Server/Anomaly/AnomalySystem.cs | 115 ++++++++-- .../Components/AnomalousParticleComponent.cs | 24 +- .../Components/SecretDataAnomalyComponent.cs | 45 ++++ .../ShuffleParticlesAnomalyComponent.cs | 28 +++ .../Anomaly/Effects/BluespaceAnomalySystem.cs | 6 +- .../Effects/ElectricityAnomalySystem.cs | 4 +- .../Anomaly/Effects/EntityAnomalySystem.cs | 14 +- .../Anomaly/Effects/InjectionAnomalySystem.cs | 4 +- .../Effects/ProjectileAnomalySystem.cs | 4 +- .../Effects/PuddleCreateAnomalySystem.cs | 3 +- .../Effects/PyroclasticAnomalySystem.cs | 6 +- .../Effects/SecretDataAnomalySystem.cs | 40 ++++ .../Effects/ShuffleParticlesAnomalySystem.cs | 41 ++++ .../Anomaly/Effects/TileAnomalySystem.cs | 14 +- .../Anomaly/Components/AnomalyComponent.cs | 44 +++- .../Effects/SharedGravityAnomalySystem.cs | 10 +- .../Prototypes/AnomalyBehaviorPrototype.cs | 45 ++++ Content.Shared/Anomaly/SharedAnomaly.cs | 1 + Content.Shared/Anomaly/SharedAnomalySystem.cs | 49 ++++- Resources/Locale/en-US/anomaly/anomaly.ftl | 29 +++ .../en-US/machine-linking/receiver_ports.ftl | 3 + Resources/Prototypes/Anomaly/behaviours.yml | 208 ++++++++++++++++++ .../Prototypes/DeviceLinking/sink_ports.yml | 5 + .../Weapons/Guns/Battery/battery_guns.yml | 5 + .../Weapons/Guns/Projectiles/projectiles.yml | 50 +++++ .../Structures/Machines/anomaly_equipment.yml | 4 + .../Structures/Specific/Anomaly/anomalies.yml | 10 +- 29 files changed, 831 insertions(+), 90 deletions(-) create mode 100644 Content.Server/Anomaly/Components/SecretDataAnomalyComponent.cs create mode 100644 Content.Server/Anomaly/Components/ShuffleParticlesAnomalyComponent.cs create mode 100644 Content.Server/Anomaly/Effects/SecretDataAnomalySystem.cs create mode 100644 Content.Server/Anomaly/Effects/ShuffleParticlesAnomalySystem.cs create mode 100644 Content.Shared/Anomaly/Prototypes/AnomalyBehaviorPrototype.cs create mode 100644 Resources/Prototypes/Anomaly/behaviours.yml diff --git a/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml b/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml index ac4adf7e0e4..36a750d0098 100644 --- a/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml +++ b/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml @@ -1,9 +1,9 @@ - + MinSize="350 400" + SetSize="350 400"> diff --git a/Content.Server/Anomaly/AnomalySystem.Scanner.cs b/Content.Server/Anomaly/AnomalySystem.Scanner.cs index 74af8e67e3c..bce508903d0 100644 --- a/Content.Server/Anomaly/AnomalySystem.Scanner.cs +++ b/Content.Server/Anomaly/AnomalySystem.Scanner.cs @@ -1,4 +1,4 @@ -using Content.Server.Anomaly.Components; +using Content.Server.Anomaly.Components; using Content.Shared.Anomaly; using Content.Shared.Anomaly.Components; using Content.Shared.DoAfter; @@ -21,6 +21,7 @@ private void InitializeScanner() SubscribeLocalEvent(OnScannerAnomalySeverityChanged); SubscribeLocalEvent(OnScannerAnomalyHealthChanged); + SubscribeLocalEvent(OnScannerAnomalyBehaviorChanged); } private void OnScannerAnomalyShutdown(ref AnomalyShutdownEvent args) @@ -67,6 +68,17 @@ private void OnScannerAnomalyHealthChanged(ref AnomalyHealthChangedEvent args) } } + private void OnScannerAnomalyBehaviorChanged(ref AnomalyBehaviorChangedEvent args) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var component)) + { + if (component.ScannedAnomaly != args.Anomaly) + continue; + UpdateScannerUi(uid, component); + } + } + private void OnScannerUiOpened(EntityUid uid, AnomalyScannerComponent component, BoundUIOpenedEvent args) { UpdateScannerUi(uid, component); @@ -132,29 +144,95 @@ public FormattedMessage GetScannerMessage(AnomalyScannerComponent component) return msg; } - msg.AddMarkup(Loc.GetString("anomaly-scanner-severity-percentage", ("percent", anomalyComp.Severity.ToString("P")))); + TryComp(anomaly, out var secret); + + //Severity + if (secret != null && secret.Secret.Contains(AnomalySecretData.Severity)) + msg.AddMarkup(Loc.GetString("anomaly-scanner-severity-percentage-unknown")); + else + msg.AddMarkup(Loc.GetString("anomaly-scanner-severity-percentage", ("percent", anomalyComp.Severity.ToString("P")))); msg.PushNewline(); - string stateLoc; - if (anomalyComp.Stability < anomalyComp.DecayThreshold) - stateLoc = Loc.GetString("anomaly-scanner-stability-low"); - else if (anomalyComp.Stability > anomalyComp.GrowthThreshold) - stateLoc = Loc.GetString("anomaly-scanner-stability-high"); + + //Stability + if (secret != null && secret.Secret.Contains(AnomalySecretData.Stability)) + msg.AddMarkup(Loc.GetString("anomaly-scanner-stability-unknown")); else - stateLoc = Loc.GetString("anomaly-scanner-stability-medium"); - msg.AddMarkup(stateLoc); + { + string stateLoc; + if (anomalyComp.Stability < anomalyComp.DecayThreshold) + stateLoc = Loc.GetString("anomaly-scanner-stability-low"); + else if (anomalyComp.Stability > anomalyComp.GrowthThreshold) + stateLoc = Loc.GetString("anomaly-scanner-stability-high"); + else + stateLoc = Loc.GetString("anomaly-scanner-stability-medium"); + msg.AddMarkup(stateLoc); + } msg.PushNewline(); - msg.AddMarkup(Loc.GetString("anomaly-scanner-point-output", ("point", GetAnomalyPointValue(anomaly, anomalyComp)))); + //Point output + if (secret != null && secret.Secret.Contains(AnomalySecretData.OutputPoint)) + msg.AddMarkup(Loc.GetString("anomaly-scanner-point-output-unknown")); + else + msg.AddMarkup(Loc.GetString("anomaly-scanner-point-output", ("point", GetAnomalyPointValue(anomaly, anomalyComp)))); msg.PushNewline(); msg.PushNewline(); + //Particles title msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-readout")); msg.PushNewline(); - msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-danger", ("type", GetParticleLocale(anomalyComp.SeverityParticleType)))); + + //Danger + if (secret != null && secret.Secret.Contains(AnomalySecretData.ParticleDanger)) + msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-danger-unknown")); + else + msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-danger", ("type", GetParticleLocale(anomalyComp.SeverityParticleType)))); + msg.PushNewline(); + + //Unstable + if (secret != null && secret.Secret.Contains(AnomalySecretData.ParticleUnstable)) + msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-unstable-unknown")); + else + msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-unstable", ("type", GetParticleLocale(anomalyComp.DestabilizingParticleType)))); msg.PushNewline(); - msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-unstable", ("type", GetParticleLocale(anomalyComp.DestabilizingParticleType)))); + + //Containment + if (secret != null && secret.Secret.Contains(AnomalySecretData.ParticleContainment)) + msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-containment-unknown")); + else + msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-containment", ("type", GetParticleLocale(anomalyComp.WeakeningParticleType)))); msg.PushNewline(); - msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-containment", ("type", GetParticleLocale(anomalyComp.WeakeningParticleType)))); + + //Transformation + if (secret != null && secret.Secret.Contains(AnomalySecretData.ParticleTransformation)) + msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-transformation-unknown")); + else + msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-transformation", ("type", GetParticleLocale(anomalyComp.TransformationParticleType)))); + + + //Behavior + msg.PushNewline(); + msg.PushNewline(); + msg.AddMarkup(Loc.GetString("anomaly-behavior-title")); + msg.PushNewline(); + + if (secret != null && secret.Secret.Contains(AnomalySecretData.Behavior)) + msg.AddMarkup(Loc.GetString("anomaly-behavior-unknown")); + else + { + if (anomalyComp.CurrentBehavior != null) + { + var behavior = _prototype.Index(anomalyComp.CurrentBehavior.Value); + + msg.AddMarkup("- " + Loc.GetString(behavior.Description)); + msg.PushNewline(); + var mod = Math.Floor((behavior.EarnPointModifier) * 100); + msg.AddMarkup("- " + Loc.GetString("anomaly-behavior-point", ("mod", mod))); + } + else + { + msg.AddMarkup(Loc.GetString("anomaly-behavior-balanced")); + } + } //The timer at the end here is actually added in the ui itself. return msg; diff --git a/Content.Server/Anomaly/AnomalySystem.cs b/Content.Server/Anomaly/AnomalySystem.cs index c3f19aa1777..bae101de878 100644 --- a/Content.Server/Anomaly/AnomalySystem.cs +++ b/Content.Server/Anomaly/AnomalySystem.cs @@ -8,13 +8,18 @@ using Content.Server.Station.Systems; using Content.Shared.Anomaly; using Content.Shared.Anomaly.Components; +using Content.Shared.Anomaly.Prototypes; using Content.Shared.DoAfter; +using Content.Shared.Random; +using Content.Shared.Random.Helpers; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; 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; @@ -33,13 +38,20 @@ public sealed partial class AnomalySystem : SharedAnomalySystem [Dependency] private readonly SharedPointLightSystem _pointLight = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly RadioSystem _radio = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly RadiationSystem _radiation = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; + [Dependency] private readonly IComponentFactory _componentFactory = default!; + [Dependency] private readonly ISerializationManager _serialization = default!; + [Dependency] private readonly IEntityManager _entity = default!; public const float MinParticleVariation = 0.8f; public const float MaxParticleVariation = 1.2f; + [ValidatePrototypeId] + const string WeightListProto = "AnomalyBehaviorList"; + /// public override void Initialize() { @@ -54,25 +66,34 @@ public override void Initialize() InitializeCommands(); } - private void OnMapInit(EntityUid uid, AnomalyComponent component, MapInitEvent args) + private void OnMapInit(Entity anomaly, ref MapInitEvent args) { - component.NextPulseTime = Timing.CurTime + GetPulseLength(component) * 3; // longer the first time - ChangeAnomalyStability(uid, Random.NextFloat(component.InitialStabilityRange.Item1 , component.InitialStabilityRange.Item2), component); - ChangeAnomalySeverity(uid, Random.NextFloat(component.InitialSeverityRange.Item1, component.InitialSeverityRange.Item2), component); + anomaly.Comp.NextPulseTime = Timing.CurTime + GetPulseLength(anomaly.Comp) * 3; // longer the first time + 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); + anomaly.Comp.Continuity = _random.NextFloat(anomaly.Comp.MinContituty, anomaly.Comp.MaxContituty); + SetBehavior(anomaly, GetRandomBehavior()); + } + public void ShuffleParticlesEffect(AnomalyComponent anomaly) + { var particles = new List - { AnomalousParticleType.Delta, AnomalousParticleType.Epsilon, AnomalousParticleType.Zeta }; - component.SeverityParticleType = Random.PickAndTake(particles); - component.DestabilizingParticleType = Random.PickAndTake(particles); - component.WeakeningParticleType = Random.PickAndTake(particles); + { 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); } - private void OnShutdown(EntityUid uid, AnomalyComponent component, ComponentShutdown args) + private void OnShutdown(Entity anomaly, ref ComponentShutdown args) { - EndAnomaly(uid, component); + EndAnomaly(anomaly); } - private void OnStartCollide(EntityUid uid, AnomalyComponent component, ref StartCollideEvent args) + private void OnStartCollide(Entity anomaly, ref StartCollideEvent args) { if (!TryComp(args.OtherEntity, out var particle)) return; @@ -80,21 +101,33 @@ private void OnStartCollide(EntityUid uid, AnomalyComponent component, ref Start if (args.OtherFixtureId != particle.FixtureId) return; + var behaviorMod = 1f; + if (anomaly.Comp.CurrentBehavior != null) + { + var b = _prototype.Index(anomaly.Comp.CurrentBehavior.Value); + behaviorMod = b.ParticleSensivity; + } // small function to randomize because it's easier to read like this - float VaryValue(float v) => v * Random.NextFloat(MinParticleVariation, MaxParticleVariation); + float VaryValue(float v) => v * behaviorMod * Random.NextFloat(MinParticleVariation, MaxParticleVariation); - if (particle.ParticleType == component.DestabilizingParticleType || particle.DestabilzingOverride) + if (particle.ParticleType == anomaly.Comp.DestabilizingParticleType || particle.DestabilzingOverride) { - ChangeAnomalyStability(uid, VaryValue(particle.StabilityPerDestabilizingHit), component); + ChangeAnomalyStability(anomaly, VaryValue(particle.StabilityPerDestabilizingHit), anomaly.Comp); } - if (particle.ParticleType == component.SeverityParticleType || particle.SeverityOverride) + if (particle.ParticleType == anomaly.Comp.SeverityParticleType || particle.SeverityOverride) { - ChangeAnomalySeverity(uid, VaryValue(particle.SeverityPerSeverityHit), component); + ChangeAnomalySeverity(anomaly, VaryValue(particle.SeverityPerSeverityHit), anomaly.Comp); } - if (particle.ParticleType == component.WeakeningParticleType || particle.WeakeningOverride) + if (particle.ParticleType == anomaly.Comp.WeakeningParticleType || particle.WeakeningOverride) { - ChangeAnomalyHealth(uid, VaryValue(particle.HealthPerWeakeningeHit), component); - ChangeAnomalyStability(uid, VaryValue(particle.StabilityPerWeakeningeHit), component); + ChangeAnomalyHealth(anomaly, VaryValue(particle.HealthPerWeakeningeHit), anomaly.Comp); + ChangeAnomalyStability(anomaly, VaryValue(particle.StabilityPerWeakeningeHit), anomaly.Comp); + } + if (particle.ParticleType == anomaly.Comp.TransformationParticleType || particle.TransmutationOverride) + { + ChangeAnomalySeverity(anomaly, VaryValue(particle.SeverityPerSeverityHit), anomaly.Comp); + if (_random.Prob(anomaly.Comp.Continuity)) + SetBehavior(anomaly, GetRandomBehavior()); } } @@ -116,6 +149,13 @@ public int GetAnomalyPointValue(EntityUid anomaly, AnomalyComponent? component = //penalty of up to 50% based on health multiplier *= MathF.Pow(1.5f, component.Health) - 0.5f; + //Apply behavior modifier + if (component.CurrentBehavior != null) + { + var behavior = _prototype.Index(component.CurrentBehavior.Value); + multiplier *= behavior.EarnPointModifier; + } + var severityValue = 1 / (1 + MathF.Pow(MathF.E, -7 * (component.Severity - 0.5f))); return (int) ((component.MaxPointsPerSecond - component.MinPointsPerSecond) * severityValue * multiplier) + component.MinPointsPerSecond; @@ -133,6 +173,7 @@ public string GetParticleLocale(AnomalousParticleType type) AnomalousParticleType.Delta => Loc.GetString("anomaly-particles-delta"), AnomalousParticleType.Epsilon => Loc.GetString("anomaly-particles-epsilon"), AnomalousParticleType.Zeta => Loc.GetString("anomaly-particles-zeta"), + AnomalousParticleType.Sigma => Loc.GetString("anomaly-particles-sigma"), _ => throw new ArgumentOutOfRangeException() }; } @@ -144,4 +185,40 @@ public override void Update(float frameTime) UpdateGenerator(); UpdateVessels(); } + + #region Behavior + private string GetRandomBehavior() + { + var weightList = _prototype.Index(WeightListProto); + return weightList.Pick(_random); + } + + private void SetBehavior(Entity anomaly, ProtoId behaviorProto) + { + if (anomaly.Comp.CurrentBehavior == behaviorProto) + return; + + if (anomaly.Comp.CurrentBehavior != null) + RemoveBehavior(anomaly, anomaly.Comp.CurrentBehavior.Value); + + //event broadcast + var ev = new AnomalyBehaviorChangedEvent(anomaly, anomaly.Comp.CurrentBehavior, behaviorProto); + anomaly.Comp.CurrentBehavior = behaviorProto; + RaiseLocalEvent(anomaly, ref ev, true); + + var behavior = _prototype.Index(behaviorProto); + + EntityManager.AddComponents(anomaly, behavior.Components); + } + + private void RemoveBehavior(Entity anomaly, ProtoId behaviorProto) + { + if (anomaly.Comp.CurrentBehavior == null) + return; + + var behavior = _prototype.Index(anomaly.Comp.CurrentBehavior.Value); + + EntityManager.RemoveComponents(anomaly, behavior.Components); + } + #endregion } diff --git a/Content.Server/Anomaly/Components/AnomalousParticleComponent.cs b/Content.Server/Anomaly/Components/AnomalousParticleComponent.cs index 9141ca6529c..5b05522bb9a 100644 --- a/Content.Server/Anomaly/Components/AnomalousParticleComponent.cs +++ b/Content.Server/Anomaly/Components/AnomalousParticleComponent.cs @@ -13,58 +13,64 @@ public sealed partial class AnomalousParticleComponent : Component /// The type of particle that the projectile /// imbues onto the anomaly on contact. ///

- [DataField("particleType", required: true)] + [DataField(required: true)] public AnomalousParticleType ParticleType; /// /// The fixture that's checked on collision. /// - [DataField("fixtureId")] + [DataField] public string FixtureId = "projectile"; /// /// The amount that the increases by when hit /// of an anomalous particle of . /// - [DataField("severityPerSeverityHit")] + [DataField] public float SeverityPerSeverityHit = 0.025f; /// /// The amount that the increases by when hit /// of an anomalous particle of . /// - [DataField("stabilityPerDestabilizingHit")] + [DataField] public float StabilityPerDestabilizingHit = 0.04f; /// /// The amount that the increases by when hit /// of an anomalous particle of . /// - [DataField("healthPerWeakeningeHit")] + [DataField] public float HealthPerWeakeningeHit = -0.05f; /// /// The amount that the increases by when hit /// of an anomalous particle of . /// - [DataField("stabilityPerWeakeningeHit")] + [DataField] public float StabilityPerWeakeningeHit = -0.1f; /// /// If this is true then the particle will always affect the stability of the anomaly. /// - [DataField("destabilzingOverride")] + [DataField] public bool DestabilzingOverride = false; /// /// If this is true then the particle will always affect the weakeness of the anomaly. /// - [DataField("weakeningOverride")] + [DataField] public bool WeakeningOverride = false; /// /// If this is true then the particle will always affect the severity of the anomaly. /// - [DataField("severityOverride")] + [DataField] public bool SeverityOverride = false; + + /// + /// If this is true then the particle will always affect the behaviour. + /// + [DataField] + public bool TransmutationOverride = false; } diff --git a/Content.Server/Anomaly/Components/SecretDataAnomalyComponent.cs b/Content.Server/Anomaly/Components/SecretDataAnomalyComponent.cs new file mode 100644 index 00000000000..80eecaafc79 --- /dev/null +++ b/Content.Server/Anomaly/Components/SecretDataAnomalyComponent.cs @@ -0,0 +1,45 @@ +using Content.Server.Anomaly.Effects; + +namespace Content.Server.Anomaly.Components; + +/// +/// Hides some information about the anomaly when scanning it +/// +[RegisterComponent, Access(typeof(SecretDataAnomalySystem), typeof(AnomalySystem))] +public sealed partial class SecretDataAnomalyComponent : Component +{ + /// + /// Minimum hidden data elements on MapInit + /// + [DataField] + public int RandomStartSecretMin = 0; + + /// + /// Maximum hidden data elements on MapInit + /// + [DataField] + public int RandomStartSecretMax = 0; + + /// + /// Current secret data + /// + [DataField] + public List Secret = new(); +} + +/// +/// Enum with secret data field variants +/// +[Serializable] +public enum AnomalySecretData : byte +{ + Severity, + Stability, + OutputPoint, + ParticleDanger, + ParticleUnstable, + ParticleContainment, + ParticleTransformation, + Behavior, + Default +} diff --git a/Content.Server/Anomaly/Components/ShuffleParticlesAnomalyComponent.cs b/Content.Server/Anomaly/Components/ShuffleParticlesAnomalyComponent.cs new file mode 100644 index 00000000000..00db76d4864 --- /dev/null +++ b/Content.Server/Anomaly/Components/ShuffleParticlesAnomalyComponent.cs @@ -0,0 +1,28 @@ +using Content.Server.Anomaly.Effects; + +namespace Content.Server.Anomaly.Components; + +/// +/// Shuffle Particle types in some situations +/// +[RegisterComponent, Access(typeof(ShuffleParticlesAnomalySystem))] +public sealed partial class ShuffleParticlesAnomalyComponent : Component +{ + /// + /// Prob() chance to randomize particle types after Anomaly pulation + /// + [DataField] + public bool ShuffleOnPulse = false; + + /// + /// Prob() chance to randomize particle types after APE or CHIMP projectile + /// + [DataField] + public bool ShuffleOnParticleHit = false; + + /// + /// Chance to random particles + /// + [DataField] + public float Prob = 0.5f; +} diff --git a/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs b/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs index dd2da82c9d6..bd8c2f3c7de 100644 --- a/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Numerics; using Content.Server.Anomaly.Components; using Content.Shared.Administration.Logs; @@ -33,7 +33,7 @@ private void OnPulse(EntityUid uid, BluespaceAnomalyComponent component, ref Ano { var xformQuery = GetEntityQuery(); var xform = xformQuery.GetComponent(uid); - var range = component.MaxShuffleRadius * args.Severity; + var range = component.MaxShuffleRadius * args.Severity * args.PowerModifier; var mobs = new HashSet>(); _lookup.GetEntitiesInRange(xform.Coordinates, range, mobs); var allEnts = new ValueList(mobs.Select(m => m.Owner)) { uid }; @@ -56,7 +56,7 @@ private void OnSupercritical(EntityUid uid, BluespaceAnomalyComponent component, { var xform = Transform(uid); var mapPos = _xform.GetWorldPosition(xform); - var radius = component.SupercriticalTeleportRadius; + var radius = component.SupercriticalTeleportRadius * args.PowerModifier; var gridBounds = new Box2(mapPos - new Vector2(radius, radius), mapPos + new Vector2(radius, radius)); var mobs = new HashSet>(); _lookup.GetEntitiesInRange(xform.Coordinates, component.MaxShuffleRadius, mobs); diff --git a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs index 8b5a72449f3..f2a060d6295 100644 --- a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs @@ -28,7 +28,7 @@ public override void Initialize() private void OnPulse(Entity anomaly, ref AnomalyPulseEvent args) { - var range = anomaly.Comp.MaxElectrocuteRange * args.Stability; + var range = anomaly.Comp.MaxElectrocuteRange * args.Stability * args.PowerModifier; int boltCount = (int)MathF.Floor(MathHelper.Lerp((float)anomaly.Comp.MinBoltCount, (float)anomaly.Comp.MaxBoltCount, args.Severity)); @@ -37,7 +37,7 @@ private void OnPulse(Entity anomaly, ref AnomalyPul private void OnSupercritical(Entity anomaly, ref AnomalySupercriticalEvent args) { - var range = anomaly.Comp.MaxElectrocuteRange * 3; + var range = anomaly.Comp.MaxElectrocuteRange * 3 * args.PowerModifier; _emp.EmpPulse(_transform.GetMapCoordinates(anomaly), range, anomaly.Comp.EmpEnergyConsumption, anomaly.Comp.EmpDisabledDuration); _lightning.ShootRandomLightnings(anomaly, range, anomaly.Comp.MaxBoltCount * 3, arcDepth: 3); diff --git a/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs b/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs index 90a655fbba7..86d6937af5e 100644 --- a/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs @@ -35,7 +35,7 @@ private void OnPulse(Entity component, ref AnomalyP if (!entry.Settings.SpawnOnPulse) continue; - SpawnEntities(component, entry, args.Stability, args.Severity); + SpawnEntities(component, entry, args.Stability, args.Severity, args.PowerModifier); } } @@ -46,7 +46,7 @@ private void OnSupercritical(Entity component, ref if (!entry.Settings.SpawnOnSuperCritical) continue; - SpawnEntities(component, entry, 1, 1); + SpawnEntities(component, entry, 1, 1, args.PowerModifier); } } @@ -57,7 +57,7 @@ private void OnShutdown(Entity component, ref Anoma if (!entry.Settings.SpawnOnShutdown || args.Supercritical) continue; - SpawnEntities(component, entry, 1, 1); + SpawnEntities(component, entry, 1, 1, 1); } } @@ -68,7 +68,7 @@ private void OnStabilityChanged(Entity component, r if (!entry.Settings.SpawnOnStabilityChanged) continue; - SpawnEntities(component, entry, args.Stability, args.Severity); + SpawnEntities(component, entry, args.Stability, args.Severity, 1); } } @@ -79,17 +79,17 @@ private void OnSeverityChanged(Entity component, re if (!entry.Settings.SpawnOnSeverityChanged) continue; - SpawnEntities(component, entry, args.Stability, args.Severity); + SpawnEntities(component, entry, args.Stability, args.Severity, 1); } } - private void SpawnEntities(Entity anomaly, EntitySpawnSettingsEntry entry, float stability, float severity) + private void SpawnEntities(Entity anomaly, EntitySpawnSettingsEntry entry, float stability, float severity, float powerMod) { var xform = Transform(anomaly); if (!TryComp(xform.GridUid, out MapGridComponent? grid)) return; - var tiles = _anomaly.GetSpawningPoints(anomaly, stability, severity, entry.Settings); + var tiles = _anomaly.GetSpawningPoints(anomaly, stability, severity, entry.Settings, powerMod); if (tiles == null) return; diff --git a/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs b/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs index 2bef32e3228..731d853280c 100644 --- a/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs @@ -29,12 +29,12 @@ public override void Initialize() private void OnPulse(Entity entity, ref AnomalyPulseEvent args) { - PulseScalableEffect(entity, entity.Comp.InjectRadius, entity.Comp.MaxSolutionInjection * args.Severity); + PulseScalableEffect(entity, entity.Comp.InjectRadius * args.PowerModifier, entity.Comp.MaxSolutionInjection * args.Severity * args.PowerModifier); } private void OnSupercritical(Entity entity, ref AnomalySupercriticalEvent args) { - PulseScalableEffect(entity, entity.Comp.SuperCriticalInjectRadius, entity.Comp.SuperCriticalSolutionInjection); + PulseScalableEffect(entity, entity.Comp.SuperCriticalInjectRadius * args.PowerModifier, entity.Comp.SuperCriticalSolutionInjection * args.PowerModifier); } private void PulseScalableEffect(Entity entity, float injectRadius, float maxInject) diff --git a/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs b/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs index 8483c86dbbf..23e0e472f0c 100644 --- a/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs @@ -31,12 +31,12 @@ public override void Initialize() private void OnPulse(EntityUid uid, ProjectileAnomalyComponent component, ref AnomalyPulseEvent args) { - ShootProjectilesAtEntities(uid, component, args.Severity); + ShootProjectilesAtEntities(uid, component, args.Severity * args.PowerModifier); } private void OnSupercritical(EntityUid uid, ProjectileAnomalyComponent component, ref AnomalySupercriticalEvent args) { - ShootProjectilesAtEntities(uid, component, 1.0f); + ShootProjectilesAtEntities(uid, component, args.PowerModifier); } private void ShootProjectilesAtEntities(EntityUid uid, ProjectileAnomalyComponent component, float severity) diff --git a/Content.Server/Anomaly/Effects/PuddleCreateAnomalySystem.cs b/Content.Server/Anomaly/Effects/PuddleCreateAnomalySystem.cs index 90177bae8e3..d460e6f4b08 100644 --- a/Content.Server/Anomaly/Effects/PuddleCreateAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/PuddleCreateAnomalySystem.cs @@ -25,9 +25,10 @@ private void OnPulse(Entity entity, ref AnomalyPul return; var xform = Transform(entity.Owner); - var puddleSol = _solutionContainer.SplitSolution(sol.Value, entity.Comp.MaxPuddleSize * args.Severity); + var puddleSol = _solutionContainer.SplitSolution(sol.Value, entity.Comp.MaxPuddleSize * args.Severity * args.PowerModifier); _puddle.TrySplashSpillAt(entity.Owner, xform.Coordinates, puddleSol, out _); } + private void OnSupercritical(Entity entity, ref AnomalySupercriticalEvent args) { if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var sol)) diff --git a/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs b/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs index 6bd8be002e0..d38bda562bc 100644 --- a/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Atmos.Components; +using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Effects.Components; @@ -24,14 +24,14 @@ public override void Initialize() private void OnPulse(EntityUid uid, PyroclasticAnomalyComponent component, ref AnomalyPulseEvent args) { var xform = Transform(uid); - var ignitionRadius = component.MaximumIgnitionRadius * args.Stability; + var ignitionRadius = component.MaximumIgnitionRadius * args.Stability * args.PowerModifier; IgniteNearby(uid, xform.Coordinates, args.Severity, ignitionRadius); } private void OnSupercritical(EntityUid uid, PyroclasticAnomalyComponent component, ref AnomalySupercriticalEvent args) { var xform = Transform(uid); - IgniteNearby(uid, xform.Coordinates, 1, component.MaximumIgnitionRadius * 2); + IgniteNearby(uid, xform.Coordinates, 1, component.MaximumIgnitionRadius * 2 * args.PowerModifier); } public void IgniteNearby(EntityUid uid, EntityCoordinates coordinates, float severity, float radius) diff --git a/Content.Server/Anomaly/Effects/SecretDataAnomalySystem.cs b/Content.Server/Anomaly/Effects/SecretDataAnomalySystem.cs new file mode 100644 index 00000000000..cbdc4b04df1 --- /dev/null +++ b/Content.Server/Anomaly/Effects/SecretDataAnomalySystem.cs @@ -0,0 +1,40 @@ +using Content.Server.Anomaly.Components; +using Robust.Shared.Random; + +namespace Content.Server.Anomaly.Effects; + +public sealed class SecretDataAnomalySystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + private readonly List _deita = new(); + + public override void Initialize() + { + SubscribeLocalEvent(OnMapInit); + } + + private void OnMapInit(EntityUid uid, SecretDataAnomalyComponent anomaly, MapInitEvent args) + { + RandomizeSecret(uid,_random.Next(anomaly.RandomStartSecretMin, anomaly.RandomStartSecretMax), anomaly); + } + + public void RandomizeSecret(EntityUid uid, int count, SecretDataAnomalyComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.Secret.Clear(); + + // I also considered just adding all the enum values and pruning but that seems more wasteful. + _deita.Clear(); + _deita.AddRange(Enum.GetValues()); + var actualCount = Math.Min(count, _deita.Count); + + for (int i = 0; i < actualCount; i++) + { + component.Secret.Add(_random.PickAndTake(_deita)); + } + } +} + diff --git a/Content.Server/Anomaly/Effects/ShuffleParticlesAnomalySystem.cs b/Content.Server/Anomaly/Effects/ShuffleParticlesAnomalySystem.cs new file mode 100644 index 00000000000..7d4d9a2ee5d --- /dev/null +++ b/Content.Server/Anomaly/Effects/ShuffleParticlesAnomalySystem.cs @@ -0,0 +1,41 @@ +using Content.Server.Anomaly.Components; +using Content.Shared.Anomaly.Components; +using Robust.Shared.Physics.Events; +using Robust.Shared.Random; + +namespace Content.Server.Anomaly.Effects; +public sealed class ShuffleParticlesAnomalySystem : EntitySystem +{ + [Dependency] private readonly AnomalySystem _anomaly = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnPulse); + SubscribeLocalEvent(OnStartCollide); + } + + private void OnStartCollide(EntityUid uid, ShuffleParticlesAnomalyComponent shuffle, StartCollideEvent args) + { + if (!TryComp(uid, out var anomaly)) + return; + + if (shuffle.ShuffleOnParticleHit && _random.Prob(shuffle.Prob)) + _anomaly.ShuffleParticlesEffect(anomaly); + + if (!TryComp(args.OtherEntity, out var particle)) + return; + } + + private void OnPulse(EntityUid uid, ShuffleParticlesAnomalyComponent shuffle, AnomalyPulseEvent args) + { + if (!TryComp(uid, out var anomaly)) + return; + + if (shuffle.ShuffleOnPulse && _random.Prob(shuffle.Prob)) + { + _anomaly.ShuffleParticlesEffect(anomaly); + } + } +} + diff --git a/Content.Server/Anomaly/Effects/TileAnomalySystem.cs b/Content.Server/Anomaly/Effects/TileAnomalySystem.cs index c1487cfc8c6..92f54152526 100644 --- a/Content.Server/Anomaly/Effects/TileAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/TileAnomalySystem.cs @@ -34,7 +34,7 @@ private void OnPulse(Entity component, ref AnomalyPul if (!entry.Settings.SpawnOnPulse) continue; - SpawnTiles(component, entry, args.Stability, args.Severity); + SpawnTiles(component, entry, args.Stability, args.Severity, args.PowerModifier); } } @@ -45,7 +45,7 @@ private void OnSupercritical(Entity component, ref An if (!entry.Settings.SpawnOnSuperCritical) continue; - SpawnTiles(component, entry, 1, 1); + SpawnTiles(component, entry, 1, 1, args.PowerModifier); } } @@ -56,7 +56,7 @@ private void OnShutdown(Entity component, ref Anomaly if (!entry.Settings.SpawnOnShutdown || args.Supercritical) continue; - SpawnTiles(component, entry, 1, 1); + SpawnTiles(component, entry, 1, 1, 1); } } @@ -67,7 +67,7 @@ private void OnStabilityChanged(Entity component, ref if (!entry.Settings.SpawnOnStabilityChanged) continue; - SpawnTiles(component, entry, args.Stability, args.Severity); + SpawnTiles(component, entry, args.Stability, args.Severity, 1); } } @@ -78,17 +78,17 @@ private void OnSeverityChanged(Entity component, ref if (!entry.Settings.SpawnOnSeverityChanged) continue; - SpawnTiles(component, entry, args.Stability, args.Severity); + SpawnTiles(component, entry, args.Stability, args.Severity, 1); } } - private void SpawnTiles(Entity anomaly, TileSpawnSettingsEntry entry, float stability, float severity) + private void SpawnTiles(Entity anomaly, TileSpawnSettingsEntry entry, float stability, float severity, float powerMod) { var xform = Transform(anomaly); if (!TryComp(xform.GridUid, out var grid)) return; - var tiles = _anomaly.GetSpawningPoints(anomaly, stability, severity, entry.Settings); + var tiles = _anomaly.GetSpawningPoints(anomaly, stability, severity, entry.Settings, powerMod); if (tiles == null) return; diff --git a/Content.Shared/Anomaly/Components/AnomalyComponent.cs b/Content.Shared/Anomaly/Components/AnomalyComponent.cs index bafdf123600..99f7b8373a7 100644 --- a/Content.Shared/Anomaly/Components/AnomalyComponent.cs +++ b/Content.Shared/Anomaly/Components/AnomalyComponent.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Shared.Anomaly.Prototypes; using Content.Shared.Damage; using Robust.Shared.Audio; using Robust.Shared.GameStates; @@ -166,6 +167,12 @@ public sealed partial class AnomalyComponent : Component [DataField] public AnomalousParticleType WeakeningParticleType; + /// + /// The particle type that change anomaly behaviour. + /// + [DataField] + public AnomalousParticleType TransformationParticleType; + #region Points and Vessels /// /// The vessel that the anomaly is connceted to. Stored so that multiple @@ -185,7 +192,7 @@ public sealed partial class AnomalyComponent : Component /// This doesn't include the point bonus for being unstable. /// [DataField("maxPointsPerSecond")] - public int MaxPointsPerSecond = 80; + public int MaxPointsPerSecond = 70; /// /// The multiplier applied to the point value for the @@ -221,6 +228,31 @@ public sealed partial class AnomalyComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public EntProtoId? CoreInertPrototype; + #region Behavior Deviations + + [DataField] + public ProtoId? CurrentBehavior; + + /// + /// Presumption of anomaly to change behavior. The higher the number, the higher the chance that the anomaly will change its behavior. + /// + [DataField] + public float Continuity = 0f; + + /// + /// Minimum contituty probability chance, that can be selected by anomaly on MapInit + /// + [DataField] + public float MinContituty = 0.1f; + + /// + /// Maximum contituty probability chance, that can be selected by anomaly on MapInit + /// + [DataField] + public float MaxContituty = 1.0f; + + #endregion + #region Floating Animation /// /// How long it takes to go from the bottom of the animation to the top. @@ -247,13 +279,13 @@ public sealed partial class AnomalyComponent : Component /// /// [ByRefEvent] -public readonly record struct AnomalyPulseEvent(EntityUid Anomaly, float Stability, float Severity); +public readonly record struct AnomalyPulseEvent(EntityUid Anomaly, float Stability, float Severity, float PowerModifier); /// /// Event raised on an anomaly when it reaches a supercritical point. /// [ByRefEvent] -public readonly record struct AnomalySupercriticalEvent(EntityUid Anomaly); +public readonly record struct AnomalySupercriticalEvent(EntityUid Anomaly, float PowerModifier); /// /// Event broadcast after an anomaly goes supercritical @@ -282,3 +314,9 @@ public sealed partial class AnomalyComponent : Component /// The anomaly being changed [ByRefEvent] public readonly record struct AnomalyHealthChangedEvent(EntityUid Anomaly, float Health); + +/// +/// Event broadcast when an anomaly's behavior is changed. +/// +[ByRefEvent] +public readonly record struct AnomalyBehaviorChangedEvent(EntityUid Anomaly, ProtoId? Old, ProtoId? New); diff --git a/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs b/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs index f4b7cc8bf4f..4c3cdb01465 100644 --- a/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs +++ b/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Effects.Components; using Content.Shared.Ghost; @@ -27,8 +27,8 @@ public override void Initialize() private void OnAnomalyPulse(EntityUid uid, GravityAnomalyComponent component, ref AnomalyPulseEvent args) { var xform = Transform(uid); - var range = component.MaxThrowRange * args.Severity; - var strength = component.MaxThrowStrength * args.Severity; + var range = component.MaxThrowRange * args.Severity * args.PowerModifier; + var strength = component.MaxThrowStrength * args.Severity * args.PowerModifier; var lookup = _lookup.GetEntitiesInRange(uid, range, LookupFlags.Dynamic | LookupFlags.Sundries); var xformQuery = GetEntityQuery(); var worldPos = _xform.GetWorldPosition(xform, xformQuery); @@ -61,8 +61,8 @@ private void OnSupercritical(EntityUid uid, GravityAnomalyComponent component, r var tiles = tileref.Select(t => (t.GridIndices, Tile.Empty)).ToList(); _mapSystem.SetTiles(xform.GridUid.Value, grid, tiles); - var range = component.MaxThrowRange * 2; - var strength = component.MaxThrowStrength * 2; + var range = component.MaxThrowRange * 2 * args.PowerModifier; + var strength = component.MaxThrowStrength * 2 * args.PowerModifier; var lookup = _lookup.GetEntitiesInRange(uid, range, LookupFlags.Dynamic | LookupFlags.Sundries); var xformQuery = GetEntityQuery(); var physQuery = GetEntityQuery(); diff --git a/Content.Shared/Anomaly/Prototypes/AnomalyBehaviorPrototype.cs b/Content.Shared/Anomaly/Prototypes/AnomalyBehaviorPrototype.cs new file mode 100644 index 00000000000..c498f5c595d --- /dev/null +++ b/Content.Shared/Anomaly/Prototypes/AnomalyBehaviorPrototype.cs @@ -0,0 +1,45 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Anomaly.Prototypes; + +[Prototype] +public sealed partial class AnomalyBehaviorPrototype : IPrototype +{ + [IdDataField] public string ID { get; private set; } = default!; + + /// + /// Description for anomaly scanner + /// + [DataField] + public string Description = string.Empty; + + /// + /// modification of the number of points earned from an anomaly + /// + [DataField] + public float EarnPointModifier = 1f; + + /// + /// deceleration or acceleration of the pulsation frequency of the anomaly + /// + [DataField] + public float PulseFrequencyModifier = 1f; + + /// + /// pulse and supercrit power modifier + /// + [DataField] + public float PulsePowerModifier = 1f; + + /// + /// how much the particles will affect the anomaly + /// + [DataField] + public float ParticleSensivity = 1f; + + /// + /// Components that are added to the anomaly when this behavior is selected, and removed when another behavior is selected. + /// + [DataField(serverOnly: true)] + public ComponentRegistry Components = new(); +} diff --git a/Content.Shared/Anomaly/SharedAnomaly.cs b/Content.Shared/Anomaly/SharedAnomaly.cs index b7585cb5f14..cde61ca3366 100644 --- a/Content.Shared/Anomaly/SharedAnomaly.cs +++ b/Content.Shared/Anomaly/SharedAnomaly.cs @@ -33,6 +33,7 @@ public enum AnomalousParticleType : byte Delta, Epsilon, Zeta, + Sigma, Default } diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs index c335cd7b858..da1d31c6ff6 100644 --- a/Content.Shared/Anomaly/SharedAnomalySystem.cs +++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Administration.Logs; using Content.Shared.Anomaly.Components; +using Content.Shared.Anomaly.Prototypes; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.Interaction; @@ -14,6 +15,7 @@ using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -33,6 +35,7 @@ public abstract class SharedAnomalySystem : EntitySystem [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() @@ -90,8 +93,7 @@ public void DoAnomalyPulse(EntityUid uid, AnomalyComponent? component = null) return; DebugTools.Assert(component.MinPulseLength > TimeSpan.FromSeconds(3)); // this is just to prevent lagspikes mispredicting pulses - var variation = Random.NextFloat(-component.PulseVariation, component.PulseVariation) + 1; - component.NextPulseTime = Timing.CurTime + GetPulseLength(component) * variation; + RefreshPulseTimer(uid, component); if (_net.IsServer) Log.Info($"Performing anomaly pulse. Entity: {ToPrettyString(uid)}"); @@ -115,10 +117,25 @@ public void DoAnomalyPulse(EntityUid uid, AnomalyComponent? component = null) pulse.EndTime = Timing.CurTime + pulse.PulseDuration; Appearance.SetData(uid, AnomalyVisuals.IsPulsing, true); - var ev = new AnomalyPulseEvent(uid, component.Stability, component.Severity); + var powerMod = 1f; + if (component.CurrentBehavior != null) + { + var beh = _prototype.Index(component.CurrentBehavior); + powerMod = beh.PulsePowerModifier; + } + var ev = new AnomalyPulseEvent(uid, component.Stability, component.Severity, powerMod); RaiseLocalEvent(uid, ref ev, true); } + public void RefreshPulseTimer(EntityUid uid, AnomalyComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + var variation = Random.NextFloat(-component.PulseVariation, component.PulseVariation) + 1; + component.NextPulseTime = Timing.CurTime + GetPulseLength(component) * variation; + } + /// /// Begins the animation for going supercritical /// @@ -159,7 +176,14 @@ public void DoAnomalySupercriticalEvent(EntityUid uid, AnomalyComponent? compone if (_net.IsServer) Log.Info($"Raising supercritical event. Entity: {ToPrettyString(uid)}"); - var ev = new AnomalySupercriticalEvent(uid); + var powerMod = 1f; + if (component.CurrentBehavior != null) + { + var beh = _prototype.Index(component.CurrentBehavior); + powerMod = beh.PulsePowerModifier; + } + + var ev = new AnomalySupercriticalEvent(uid, powerMod); RaiseLocalEvent(uid, ref ev, true); EndAnomaly(uid, component, true); @@ -276,8 +300,17 @@ public void ChangeAnomalyHealth(EntityUid uid, float change, AnomalyComponent? c public TimeSpan GetPulseLength(AnomalyComponent component) { DebugTools.Assert(component.MaxPulseLength > component.MinPulseLength); - var modifier = Math.Clamp((component.Stability - component.GrowthThreshold) / component.GrowthThreshold, 0, 1); - return (component.MaxPulseLength - component.MinPulseLength) * modifier + component.MinPulseLength; + var modifier = Math.Clamp((component.Stability - component.GrowthThreshold) / component.GrowthThreshold, 0, 1); + + var lenght = (component.MaxPulseLength - component.MinPulseLength) * modifier + component.MinPulseLength; + + //Apply behavior modifier + if (component.CurrentBehavior != null) + { + var behavior = _prototype.Index(component.CurrentBehavior.Value); + lenght *= behavior.PulseFrequencyModifier; + } + return lenght; } /// @@ -335,14 +368,14 @@ public override void Update(float frameTime) /// /// Gets random points around the anomaly based on the given parameters. /// - public List? GetSpawningPoints(EntityUid uid, float stability, float severity, AnomalySpawnSettings settings) + public List? GetSpawningPoints(EntityUid uid, float stability, float severity, AnomalySpawnSettings settings, float powerModifier = 1f) { var xform = Transform(uid); if (!TryComp(xform.GridUid, out var grid)) return null; - var amount = (int) (MathHelper.Lerp(settings.MinAmount, settings.MaxAmount, severity * stability) + 0.5f); + var amount = (int) (MathHelper.Lerp(settings.MinAmount, settings.MaxAmount, severity * stability * powerModifier) + 0.5f); var localpos = xform.Coordinates.Position; var tilerefs = grid.GetLocalTilesIntersecting( diff --git a/Resources/Locale/en-US/anomaly/anomaly.ftl b/Resources/Locale/en-US/anomaly/anomaly.ftl index 3a398d482e7..da5882fa62f 100644 --- a/Resources/Locale/en-US/anomaly/anomaly.ftl +++ b/Resources/Locale/en-US/anomaly/anomaly.ftl @@ -8,20 +8,29 @@ anomaly-particles-delta = Delta particles anomaly-particles-epsilon = Epsilon particles anomaly-particles-zeta = Zeta particles anomaly-particles-omega = Omega particles +anomaly-particles-sigma = Sigma particles anomaly-scanner-component-scan-complete = Scan complete! anomaly-scanner-ui-title = anomaly scanner anomaly-scanner-no-anomaly = No anomaly currently scanned. anomaly-scanner-severity-percentage = Current severity: [color=gray]{$percent}[/color] +anomaly-scanner-severity-percentage-unknown = Current severity: [color=red]ERROR[/color] anomaly-scanner-stability-low = Current anomaly state: [color=gold]Decaying[/color] anomaly-scanner-stability-medium = Current anomaly state: [color=forestgreen]Stable[/color] anomaly-scanner-stability-high = Current anomaly state: [color=crimson]Growing[/color] +anomaly-scanner-stability-unknown = Current anomaly state: [color=red]ERROR[/color] anomaly-scanner-point-output = Point output: [color=gray]{$point}[/color] +anomaly-scanner-point-output-unknown = Point output: [color=red]ERROR[/color] anomaly-scanner-particle-readout = Particle Reaction Analysis: anomaly-scanner-particle-danger = - [color=crimson]Danger type:[/color] {$type} anomaly-scanner-particle-unstable = - [color=plum]Unstable type:[/color] {$type} anomaly-scanner-particle-containment = - [color=goldenrod]Containment type:[/color] {$type} +anomaly-scanner-particle-transformation = - [color=#6b75fa]Transformation type:[/color] {$type} +anomaly-scanner-particle-danger-unknown = - [color=crimson]Danger type:[/color] [color=red]ERROR[/color] +anomaly-scanner-particle-unstable-unknown = - [color=plum]Unstable type:[/color] [color=red]ERROR[/color] +anomaly-scanner-particle-containment-unknown = - [color=goldenrod]Containment type:[/color] [color=red]ERROR[/color] +anomaly-scanner-particle-transformation-unknown = - [color=#6b75fa]Transformation type:[/color] [color=red]ERROR[/color] anomaly-scanner-pulse-timer = Time until next pulse: [color=gray]{$time}[/color] anomaly-gorilla-core-slot-name = Anomaly core @@ -65,3 +74,23 @@ anomaly-command-supercritical = Makes a target anomaly go supercritical # Flavor text on the footer anomaly-generator-flavor-left = Anomaly may spawn inside the operator. anomaly-generator-flavor-right = v1.1 + +anomaly-behavior-unknown = [color=red]ERROR. Cannot be read.[/color] + +anomaly-behavior-title = behavior deviation analysis: +anomaly-behavior-point =[color=gold]Anomaly produces {$mod}% of the points[/color] + +anomaly-behavior-safe = [color=forestgreen]The anomaly is extremely stable. Extremely rare pulsations.[/color] +anomaly-behavior-slow = [color=forestgreen]The frequency of pulsations is much less frequent.[/color] +anomaly-behavior-light = [color=forestgreen]Pulsation power is significantly reduced.[/color] +anomaly-behavior-balanced = No behavior deviations detected. +anomaly-behavior-delayed-force = The frequency of pulsations is greatly reduced, but their power is increased. +anomaly-behavior-rapid = The frequency of the pulsation is much higher, but its strength is attenuated. +anomaly-behavior-reflect = A protective coating was detected. +anomaly-behavior-nonsensivity = A weak reaction to particles was detected. +anomaly-behavior-sensivity = Amplified reaction to particles was detected. +anomaly-behavior-secret = Interference detected. Some data cannot be read +anomaly-behavior-inconstancy = [color=crimson]Impermanence has been detected. Particle types can change over time.[/color] +anomaly-behavior-fast = [color=crimson]The pulsation frequency is strongly increased.[/color] +anomaly-behavior-strenght = [color=crimson]The pulsation power is significantly increased.[/color] +anomaly-behavior-moving = [color=crimson]Coordinate instability was detected.[/color] \ No newline at end of file diff --git a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl index 4d2dd25af2c..dc45677c8d2 100644 --- a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl +++ b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl @@ -70,6 +70,9 @@ signal-port-description-set-particle-epsilon = Sets the type of particle this de signal-port-name-set-particle-zeta = Set particle type: zeta signal-port-description-set-particle-zeta = Sets the type of particle this device emits to zeta. +signal-port-name-set-particle-sigma = Set particle type: sigma +signal-port-description-set-particle-sigma = Sets the type of particle this device emits to sigma. + signal-port-name-logic-input-a = Input A signal-port-description-logic-input-a = First input of a logic gate. diff --git a/Resources/Prototypes/Anomaly/behaviours.yml b/Resources/Prototypes/Anomaly/behaviours.yml new file mode 100644 index 00000000000..b46ba97fc2f --- /dev/null +++ b/Resources/Prototypes/Anomaly/behaviours.yml @@ -0,0 +1,208 @@ +- type: weightedRandom + id: AnomalyBehaviorList + weights: + #safe + Slow: 0.5 + Light: 0.5 + FullSafe: 0.1 + #balanced + Balanced: 3 + DelayedForce: 1 + Rapid: 1 + BalancedSecret: 1 + Reflect: 1 + NonSensivity: 1 + Sensivity: 1 + #Hard + Fast: 0.5 + Strenght: 0.5 + Inconstancy: 0.5 + InconstancyParticle: 0.5 + FullUnknown: 0.5 + Jumping: 0.3 + Moving: 0.1 + #Complex + FastUnknown: 0.2 + JumpingUnknown: 0.1 + InconstancyParticleUnknown: 0.1 + + +# Easy x0.5 point production + +- type: anomalyBehavior + id: FullSafe + pulseFrequencyModifier: 3 + pulsePowerModifier: 0.5 + earnPointModifier: 0.05 + description: anomaly-behavior-safe + +- type: anomalyBehavior + id: Slow + pulseFrequencyModifier: 2 + earnPointModifier: 0.5 + description: anomaly-behavior-slow + +- type: anomalyBehavior + id: Light + pulsePowerModifier: 0.5 + earnPointModifier: 0.5 + description: anomaly-behavior-light + +# Balanced x1 point production + +- type: anomalyBehavior + id: Balanced + earnPointModifier: 1 + description: anomaly-behavior-balanced + +- type: anomalyBehavior + id: DelayedForce + earnPointModifier: 1.15 + description: anomaly-behavior-delayed-force + pulseFrequencyModifier: 0.5 + pulsePowerModifier: 2 + +- type: anomalyBehavior + id: Rapid + earnPointModifier: 1.15 + description: anomaly-behavior-rapid + pulseFrequencyModifier: 2 + pulsePowerModifier: 0.5 + +- type: anomalyBehavior + id: BalancedSecret + earnPointModifier: 1.2 + description: anomaly-behavior-secret + components: + - type: SecretDataAnomaly + randomStartSecretMin: 2 + randomStartSecretMax: 3 + +- type: anomalyBehavior + id: Reflect + earnPointModifier: 1.1 + particleSensivity: 0.5 + description: anomaly-behavior-reflect + components: + - type: Reflect + reflectProb: 0.5 + reflects: + - Energy + +- type: anomalyBehavior + id: NonSensivity + earnPointModifier: 0.8 + particleSensivity: 0.5 + description: anomaly-behavior-nonsensivity + +- type: anomalyBehavior + id: Sensivity + earnPointModifier: 1.2 + particleSensivity: 1.5 + description: anomaly-behavior-sensivity + +# Hard x2 point production + +- type: anomalyBehavior + id: Fast + earnPointModifier: 1.9 + pulseFrequencyModifier: 0.5 + description: anomaly-behavior-fast + +- type: anomalyBehavior + id: Strenght + pulsePowerModifier: 1.5 + earnPointModifier: 1.4 + description: anomaly-behavior-strenght + +- type: anomalyBehavior + id: Inconstancy + earnPointModifier: 1.7 + description: anomaly-behavior-inconstancy + components: + - type: ShuffleParticlesAnomaly + shuffleOnPulse: true + prob: 1 + +- type: anomalyBehavior + id: InconstancyParticle + earnPointModifier: 1.8 + description: anomaly-behavior-inconstancy + components: + - type: ShuffleParticlesAnomaly + shuffleOnParticleHit: true + prob: 0.8 + +- type: anomalyBehavior + id: Moving + earnPointModifier: 2.2 + description: anomaly-behavior-moving + components: + - type: RandomWalk + minSpeed: 0 + maxSpeed: 0.3 + - type: CanMoveInAir + - type: Physics + bodyType: Dynamic + bodyStatus: InAir + +- type: anomalyBehavior + id: Jumping + earnPointModifier: 1.8 + description: anomaly-behavior-moving + components: + - type: ChaoticJump + jumpMinInterval: 15 + jumpMaxInterval: 25 + rangeMin: 1 + rangeMax: 4 + effect: PuddleSparkle + +- type: anomalyBehavior + id: FullUnknown + earnPointModifier: 1.9 + description: anomaly-behavior-secret + components: + - type: SecretDataAnomaly + randomStartSecretMin: 4 + randomStartSecretMax: 6 + +# Complex Effects + +- type: anomalyBehavior + id: JumpingUnknown + earnPointModifier: 1.9 + description: anomaly-behavior-moving + components: + - type: ChaoticJump + jumpMinInterval: 15 + jumpMaxInterval: 25 + rangeMin: 1 + rangeMax: 1 + effect: PuddleSparkle + - type: SecretDataAnomaly + randomStartSecretMin: 3 + randomStartSecretMax: 5 + + +- type: anomalyBehavior + id: FastUnknown + earnPointModifier: 1.9 + pulseFrequencyModifier: 0.5 + description: anomaly-behavior-fast + components: + - type: SecretDataAnomaly + randomStartSecretMin: 3 + randomStartSecretMax: 5 + +- type: anomalyBehavior + id: InconstancyParticleUnknown + earnPointModifier: 1.95 + description: anomaly-behavior-inconstancy + components: + - type: ShuffleParticlesAnomaly + shuffleOnParticleHit: true + prob: 0.5 + - type: SecretDataAnomaly + randomStartSecretMin: 3 + randomStartSecretMax: 5 \ No newline at end of file diff --git a/Resources/Prototypes/DeviceLinking/sink_ports.yml b/Resources/Prototypes/DeviceLinking/sink_ports.yml index f05934f3baa..56b10ec4fcc 100644 --- a/Resources/Prototypes/DeviceLinking/sink_ports.yml +++ b/Resources/Prototypes/DeviceLinking/sink_ports.yml @@ -107,3 +107,8 @@ id: SetParticleZeta name: signal-port-name-set-particle-zeta description: signal-port-description-set-particle-zeta + +- type: sinkPort + id: SetParticleSigma + name: signal-port-name-set-particle-sigma + description: signal-port-description-set-particle-sigma diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 202604b8bf0..c4736ad356f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -643,6 +643,7 @@ - type: Clothing sprite: Objects/Weapons/Guns/Revolvers/chimp.rsi - type: Gun + projectileSpeed: 4 fireRate: 1.5 soundGunshot: path: /Audio/Weapons/Guns/Gunshots/taser2.ogg @@ -657,6 +658,8 @@ fireCost: 100 - proto: AnomalousParticleZetaStrong fireCost: 100 + - proto: AnomalousParticleSigmaStrong + fireCost: 100 - type: Construction graph: UpgradeWeaponPistolCHIMP node: start @@ -679,6 +682,8 @@ fireCost: 100 - proto: AnomalousParticleZetaStrong fireCost: 100 + - proto: AnomalousParticleSigmaStrong + fireCost: 100 - type: entity name: eye of a behonker diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 9445a3cfe12..a28d527535f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -487,6 +487,11 @@ name: delta particles noSpawn: true components: + - type: PointLight + enabled: true + color: "#c2385d" + radius: 2.0 + energy: 7.0 - type: AnomalousParticle particleType: Delta - type: Sprite @@ -513,6 +518,9 @@ Heat: 5 - type: TimedDespawn lifetime: 3 + - type: Reflective + reflective: + - Energy - type: entity @@ -533,6 +541,11 @@ name: epsilon particles noSpawn: true components: + - type: PointLight + enabled: true + color: "#38c296" + radius: 2.0 + energy: 7.0 - type: Sprite layers: - state: magicm_cyan @@ -558,6 +571,11 @@ name: zeta particles noSpawn: true components: + - type: PointLight + enabled: true + color: "#b9c238" + radius: 2.0 + energy: 7.0 - type: Sprite layers: - state: magicm_yellow @@ -583,6 +601,11 @@ name: omega particles noSpawn: true components: + - type: PointLight + enabled: true + color: "#38c24f" + radius: 2.0 + energy: 7.0 - type: Sprite sprite: Objects/Weapons/Guns/Projectiles/magic.rsi layers: @@ -601,6 +624,33 @@ types: Heat: 20 +- type: entity + parent: AnomalousParticleDelta + id: AnomalousParticleSigma + name: sigma particles + noSpawn: true + components: + - type: PointLight + enabled: true + color: "#8d38c2" + radius: 2.0 + energy: 7.0 + - type: Sprite + layers: + - state: magicm + shader: unshaded + - type: AnomalousParticle + particleType: Sigma + +- type: entity + parent: AnomalousParticleSigma + id: AnomalousParticleSigmaStrong + name: sigma particles + noSpawn: true + components: + - type: AnomalousParticle + particleType: Sigma + # Launcher projectiles (grenade / rocket) - type: entity id: BulletRocket diff --git a/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml b/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml index 36d77a236d0..0064e7a4ae9 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml @@ -172,10 +172,12 @@ - AnomalousParticleDelta - AnomalousParticleEpsilon - AnomalousParticleZeta + - AnomalousParticleSigma setTypePorts: SetParticleDelta: AnomalousParticleDelta SetParticleEpsilon: AnomalousParticleEpsilon SetParticleZeta: AnomalousParticleZeta + SetParticleSigma: AnomalousParticleSigma fireBurstSize: 1 fireBurstDelayMin: 2 fireBurstDelayMax: 6 @@ -185,6 +187,7 @@ guides: - APE - type: Gun + projectileSpeed: 4 fireRate: 10 #just has to be fast enough to keep up with upgrades showExamineText: false selectedMode: SemiAuto @@ -219,6 +222,7 @@ - SetParticleDelta - SetParticleEpsilon - SetParticleZeta + - SetParticleSigma - type: entity id: MachineAnomalyGenerator diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml index 14a9997b803..99574ab7be1 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml @@ -21,6 +21,7 @@ anchored: false - type: Physics bodyType: Static + bodyStatus: InAir - type: Fixtures fixtures: fix1: @@ -47,6 +48,9 @@ - type: EmitSoundOnSpawn sound: path: /Audio/Effects/teleport_arrival.ogg + - type: SecretDataAnomaly + randomStartSecretMin: 0 + randomStartSecretMax: 2 - type: entity id: AnomalyPyroclastic @@ -119,11 +123,11 @@ castShadows: false - type: GravityAnomaly - type: GravityWell - - type: RadiationSource + - type: RadiationSource - type: Physics bodyType: Dynamic bodyStatus: InAir - - type: CanMoveInAir + - type: CanMoveInAir - type: RandomWalk - type: SingularityDistortion intensity: 1000 @@ -598,7 +602,7 @@ color: "#6270bb" - type: Anomaly animationTime: 6 - offset: 0.05, 0 + offset: 0, 0 corePrototype: AnomalyCoreFlora coreInertPrototype: AnomalyCoreFloraInert anomalyContactDamage: From 5e15abc5ed704ce5c6d6ee72568d5e1e6ef69ffc Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 08:30:19 +0000 Subject: [PATCH 120/133] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 27a7a03a568..d901a49abd8 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Blackern5000 - changes: - - message: Smoking now causes mild lung cancer in the form of cellular damage. - type: Tweak - id: 5787 - time: '2024-01-25T09:51:26.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24546 - author: metalgearsloth changes: - message: Add title2.ogg to space ambience tracks. @@ -3806,3 +3799,14 @@ id: 6286 time: '2024-04-01T07:20:38.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25831 +- author: TheShuEd + changes: + - message: Added new transformation particles types for APE and CHIMP + type: Add + - message: Added new anomaly behaviour mechanic. Different behaviors can change + the gameplay of an anomaly. Players can get random behaviors by bombarding the + anomaly with new particles + type: Add + id: 6287 + time: '2024-04-01T08:29:13.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/24683 From a05f95269f8e04a29e8d93da37cff47b84026d51 Mon Sep 17 00:00:00 2001 From: eoineoineoin Date: Mon, 1 Apr 2024 09:36:07 +0100 Subject: [PATCH 121/133] Fix clipping/overlap in lathe machine UIs (#26646) * Add scrollbars to lathe material list when necessary * Fix bug where shrinking window would cause elements to overlap --------- Co-authored-by: Eoin Mcloughlin --- Content.Client/Lathe/UI/LatheMenu.xaml | 4 +--- Content.Client/Materials/UI/MaterialStorageControl.xaml | 9 +++++---- .../Materials/UI/MaterialStorageControl.xaml.cs | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml b/Content.Client/Lathe/UI/LatheMenu.xaml index 2b97166f05a..6f484d8c7be 100644 --- a/Content.Client/Lathe/UI/LatheMenu.xaml +++ b/Content.Client/Lathe/UI/LatheMenu.xaml @@ -124,9 +124,7 @@ + Orientation="Vertical"> [GenerateTypedNameReferences] -public sealed partial class MaterialStorageControl : BoxContainer +public sealed partial class MaterialStorageControl : ScrollContainer { [Dependency] private readonly IEntityManager _entityManager = default!; @@ -63,7 +63,7 @@ protected override void FrameUpdate(FrameEventArgs args) } var children = new List(); - children.AddRange(Children.OfType()); + children.AddRange(MaterialList.Children.OfType()); foreach (var display in children) { @@ -71,7 +71,7 @@ protected override void FrameUpdate(FrameEventArgs args) if (extra.Contains(mat)) { - RemoveChild(display); + MaterialList.RemoveChild(display); continue; } @@ -83,7 +83,7 @@ protected override void FrameUpdate(FrameEventArgs args) foreach (var mat in missing) { var volume = mats[mat]; - AddChild(new MaterialDisplay(_owner.Value, mat, volume, canEject)); + MaterialList.AddChild(new MaterialDisplay(_owner.Value, mat, volume, canEject)); } _currentMaterials = mats; From bc31c193c25b297179e0de2bf54c2623fbeeebe0 Mon Sep 17 00:00:00 2001 From: Sk1tch Date: Mon, 1 Apr 2024 13:48:02 -0700 Subject: [PATCH 122/133] Added chat window transparency slider to options (#24990) * Adds a new slider to the misc tab in options that lets the player set chat window transparency * Tweaked variable names * Fixed order to match UI * Renamed set chat window transparency function * Changed and refactored to opacity instead of transparency * Remove unnecessary int to float conversions Slider used to be 0-100 while the CCVar was 0.0-1.0f. This is confusing and was only used for rounding to 2 decimal points. * Round the value to two decimal points * Remove rounding for now * Rename * Unhardcode chat color by moving to stylesheet * Fix indent * Make opacity slider only change opacity --------- Co-authored-by: Your Name Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> --- Content.Client/Options/UI/Tabs/MiscTab.xaml | 11 +++-- .../Options/UI/Tabs/MiscTab.xaml.cs | 12 ++++++ Content.Client/Stylesheets/StyleNano.cs | 29 ++++++++----- .../Systems/Chat/ChatUIController.cs | 43 ++++++++++++++++--- .../{ChatInputBox.cs => ChatInputBox.xaml.cs} | 4 +- .../Systems/Chat/Widgets/ChatBox.xaml | 7 +-- Content.Shared/CCVar/CCVars.cs | 7 +++ .../en-US/escape-menu/ui/options-menu.ftl | 2 + 8 files changed, 90 insertions(+), 25 deletions(-) rename Content.Client/UserInterface/Systems/Chat/Controls/{ChatInputBox.cs => ChatInputBox.xaml.cs} (95%) diff --git a/Content.Client/Options/UI/Tabs/MiscTab.xaml b/Content.Client/Options/UI/Tabs/MiscTab.xaml index 2ee59910f70..5564d7b2263 100644 --- a/Content.Client/Options/UI/Tabs/MiscTab.xaml +++ b/Content.Client/Options/UI/Tabs/MiscTab.xaml @@ -25,6 +25,14 @@ + + - - - diff --git a/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs b/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs index 3b9c41efdfb..476e7289ea9 100644 --- a/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs @@ -66,6 +66,7 @@ public MiscTab() EnableColorNameCheckBox.OnToggled += OnCheckBoxToggled; ColorblindFriendlyCheckBox.OnToggled += OnCheckBoxToggled; ReducedMotionCheckBox.OnToggled += OnCheckBoxToggled; + ChatWindowOpacitySlider.OnValueChanged += OnChatWindowOpacitySliderChanged; ScreenShakeIntensitySlider.OnValueChanged += OnScreenShakeIntensitySliderChanged; // ToggleWalk.OnToggled += OnCheckBoxToggled; StaticStorageUI.OnToggled += OnCheckBoxToggled; @@ -81,6 +82,7 @@ public MiscTab() EnableColorNameCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatEnableColorName); ColorblindFriendlyCheckBox.Pressed = _cfg.GetCVar(CCVars.AccessibilityColorblindFriendly); ReducedMotionCheckBox.Pressed = _cfg.GetCVar(CCVars.ReducedMotion); + ChatWindowOpacitySlider.Value = _cfg.GetCVar(CCVars.ChatWindowOpacity); ScreenShakeIntensitySlider.Value = _cfg.GetCVar(CCVars.ScreenShakeIntensity) * 100f; // ToggleWalk.Pressed = _cfg.GetCVar(CCVars.ToggleWalk); StaticStorageUI.Pressed = _cfg.GetCVar(CCVars.StaticStorageUI); @@ -101,6 +103,13 @@ private void OnHudThemeChanged(OptionButton.ItemSelectedEventArgs args) UpdateApplyButton(); } + private void OnChatWindowOpacitySliderChanged(Range range) + { + ChatWindowOpacityLabel.Text = Loc.GetString("ui-options-chat-window-opacity-percent", + ("opacity", range.Value)); + UpdateApplyButton(); + } + private void OnScreenShakeIntensitySliderChanged(Range obj) { ScreenShakeIntensityLabel.Text = Loc.GetString("ui-options-screen-shake-percent", ("intensity", ScreenShakeIntensitySlider.Value / 100f)); @@ -127,6 +136,7 @@ private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args) _cfg.SetCVar(CCVars.ChatEnableColorName, EnableColorNameCheckBox.Pressed); _cfg.SetCVar(CCVars.AccessibilityColorblindFriendly, ColorblindFriendlyCheckBox.Pressed); _cfg.SetCVar(CCVars.ReducedMotion, ReducedMotionCheckBox.Pressed); + _cfg.SetCVar(CCVars.ChatWindowOpacity, ChatWindowOpacitySlider.Value); _cfg.SetCVar(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider.Value / 100f); // _cfg.SetCVar(CCVars.ToggleWalk, ToggleWalk.Pressed); _cfg.SetCVar(CCVars.StaticStorageUI, StaticStorageUI.Pressed); @@ -154,6 +164,7 @@ private void UpdateApplyButton() var isEnableColorNameSame = EnableColorNameCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatEnableColorName); var isColorblindFriendly = ColorblindFriendlyCheckBox.Pressed == _cfg.GetCVar(CCVars.AccessibilityColorblindFriendly); var isReducedMotionSame = ReducedMotionCheckBox.Pressed == _cfg.GetCVar(CCVars.ReducedMotion); + var isChatWindowOpacitySame = Math.Abs(ChatWindowOpacitySlider.Value - _cfg.GetCVar(CCVars.ChatWindowOpacity)) < 0.01f; var isScreenShakeIntensitySame = Math.Abs(ScreenShakeIntensitySlider.Value / 100f - _cfg.GetCVar(CCVars.ScreenShakeIntensity)) < 0.01f; // var isToggleWalkSame = ToggleWalk.Pressed == _cfg.GetCVar(CCVars.ToggleWalk); var isStaticStorageUISame = StaticStorageUI.Pressed == _cfg.GetCVar(CCVars.StaticStorageUI); @@ -170,6 +181,7 @@ private void UpdateApplyButton() isEnableColorNameSame && isColorblindFriendly && isReducedMotionSame && + isChatWindowOpacitySame && isScreenShakeIntensitySame && // isToggleWalkSame && isStaticStorageUISame; diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs index 2c7a1873a36..a589abb83aa 100644 --- a/Content.Client/Stylesheets/StyleNano.cs +++ b/Content.Client/Stylesheets/StyleNano.cs @@ -45,6 +45,7 @@ public sealed class StyleNano : StyleBase public const string StyleClassBorderedWindowPanel = "BorderedWindowPanel"; public const string StyleClassInventorySlotBackground = "InventorySlotBackground"; public const string StyleClassHandSlotHighlight = "HandSlotHighlight"; + public const string StyleClassChatPanel = "ChatPanel"; public const string StyleClassChatSubPanel = "ChatSubPanel"; public const string StyleClassTransparentBorderedWindowPanel = "TransparentBorderedWindowPanel"; public const string StyleClassHotbarPanel = "HotbarPanel"; @@ -144,6 +145,8 @@ public sealed class StyleNano : StyleBase public const string StyleClassButtonColorRed = "ButtonColorRed"; public const string StyleClassButtonColorGreen = "ButtonColorGreen"; + public static readonly Color ChatBackgroundColor = Color.FromHex("#25252ADD"); + public override Stylesheet Stylesheet { get; } public StyleNano(IResourceCache resCache) : base(resCache) @@ -346,12 +349,16 @@ public StyleNano(IResourceCache resCache) : base(resCache) lineEdit.SetPatchMargin(StyleBox.Margin.All, 3); lineEdit.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5); - var chatSubBGTex = resCache.GetTexture("/Textures/Interface/Nano/chat_sub_background.png"); - var chatSubBG = new StyleBoxTexture + var chatBg = new StyleBoxFlat + { + BackgroundColor = ChatBackgroundColor + }; + + var chatSubBg = new StyleBoxFlat { - Texture = chatSubBGTex, + BackgroundColor = ChatBackgroundColor, }; - chatSubBG.SetPatchMargin(StyleBox.Margin.All, 2); + chatSubBg.SetContentMarginOverride(StyleBox.Margin.All, 2); var actionSearchBoxTex = resCache.GetTexture("/Textures/Interface/Nano/black_panel_dark_thin_border.png"); var actionSearchBox = new StyleBoxTexture @@ -850,19 +857,19 @@ public StyleNano(IResourceCache resCache) : base(resCache) Element().Pseudo(TextEdit.StylePseudoClassPlaceholder) .Prop("font-color", Color.Gray), - // Chat lineedit - we don't actually draw a stylebox around the lineedit itself, we put it around the - // input + other buttons, so we must clear the default stylebox - new StyleRule(new SelectorElement(typeof(LineEdit), new[] {StyleClassChatLineEdit}, null, null), + // chat subpanels (chat lineedit backing, popup backings) + new StyleRule(new SelectorElement(typeof(PanelContainer), new[] {StyleClassChatPanel}, null, null), new[] { - new StyleProperty(LineEdit.StylePropertyStyleBox, new StyleBoxEmpty()), + new StyleProperty(PanelContainer.StylePropertyPanel, chatBg), }), - // chat subpanels (chat lineedit backing, popup backings) - new StyleRule(new SelectorElement(typeof(PanelContainer), new[] {StyleClassChatSubPanel}, null, null), + // Chat lineedit - we don't actually draw a stylebox around the lineedit itself, we put it around the + // input + other buttons, so we must clear the default stylebox + new StyleRule(new SelectorElement(typeof(LineEdit), new[] {StyleClassChatLineEdit}, null, null), new[] { - new StyleProperty(PanelContainer.StylePropertyPanel, chatSubBG), + new StyleProperty(LineEdit.StylePropertyStyleBox, new StyleBoxEmpty()), }), // Action searchbox lineedit diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs index ac0ea5335e6..907268295c8 100644 --- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs +++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs @@ -9,6 +9,7 @@ using Content.Client.Examine; using Content.Client.Gameplay; using Content.Client.Ghost; +using Content.Client.Stylesheets; using Content.Client.UserInterface.Screens; using Content.Client.UserInterface.Systems.Chat.Widgets; using Content.Client.UserInterface.Systems.Gameplay; @@ -54,7 +55,6 @@ public sealed class ChatUIController : UIController [Dependency] private readonly IStateManager _state = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IReplayRecordingManager _replayRecording = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; [UISystemDependency] private readonly ExamineSystem? _examine = default; [UISystemDependency] private readonly GhostSystem? _ghost = default; @@ -179,8 +179,8 @@ public override void Initialize() _net.RegisterNetMessage(OnChatMessage); _net.RegisterNetMessage(OnDeleteChatMessagesBy); SubscribeNetworkEvent(OnDamageForceSay); - _cfg.OnValueChanged(CCVars.ChatEnableColorName, (value) => { _chatNameColorsEnabled = value; }); - _chatNameColorsEnabled = _cfg.GetCVar(CCVars.ChatEnableColorName); + _config.OnValueChanged(CCVars.ChatEnableColorName, (value) => { _chatNameColorsEnabled = value; }); + _chatNameColorsEnabled = _config.GetCVar(CCVars.ChatEnableColorName); _speechBubbleRoot = new LayoutContainer(); @@ -232,6 +232,9 @@ public override void Initialize() { _chatNameColors[i] = nameColors[i].ToHex(); } + + _config.OnValueChanged(CCVars.ChatWindowOpacity, OnChatWindowOpacityChanged); + } public void OnScreenLoad() @@ -240,6 +243,8 @@ public void OnScreenLoad() var viewportContainer = UIManager.ActiveScreen!.FindControl("ViewportContainer"); SetSpeechBubbleRoot(viewportContainer); + + SetChatWindowOpacity(_config.GetCVar(CCVars.ChatWindowOpacity)); } public void OnScreenUnload() @@ -247,6 +252,34 @@ public void OnScreenUnload() SetMainChat(false); } + private void OnChatWindowOpacityChanged(float opacity) + { + SetChatWindowOpacity(opacity); + } + + private void SetChatWindowOpacity(float opacity) + { + var chatBox = UIManager.ActiveScreen?.GetWidget() ?? UIManager.ActiveScreen?.GetWidget(); + + var panel = chatBox?.ChatWindowPanel; + if (panel is null) + return; + + Color color; + if (panel.PanelOverride is StyleBoxFlat styleBoxFlat) + color = styleBoxFlat.BackgroundColor; + else if (panel.TryGetStyleProperty(PanelContainer.StylePropertyPanel, out var style) + && style is StyleBoxFlat propStyleBoxFlat) + color = propStyleBoxFlat.BackgroundColor; + else + color = StyleNano.ChatBackgroundColor; + + panel.PanelOverride = new StyleBoxFlat + { + BackgroundColor = color.WithAlpha(opacity) + }; + } + public void SetMainChat(bool setting) { if (UIManager.ActiveScreen == null) @@ -770,7 +803,7 @@ private void OnChatMessage(MsgChatMessage message) ProcessChatMessage(msg); if ((msg.Channel & ChatChannel.AdminRelated) == 0 || - _cfg.GetCVar(CCVars.ReplayRecordAdminChat)) + _config.GetCVar(CCVars.ReplayRecordAdminChat)) { _replayRecording.RecordClientMessage(msg); } @@ -830,7 +863,7 @@ public void ProcessChatMessage(ChatMessage msg, bool speechBubble = true) break; case ChatChannel.LOOC: - if (_cfg.GetCVar(CCVars.LoocAboveHeadShow)) + if (_config.GetCVar(CCVars.LoocAboveHeadShow)) AddSpeechBubble(msg, SpeechBubble.SpeechType.Looc); break; } diff --git a/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.cs b/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.xaml.cs similarity index 95% rename from Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.cs rename to Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.xaml.cs index 843fd46c1a0..0326664bd69 100644 --- a/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.cs +++ b/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.xaml.cs @@ -1,4 +1,5 @@ -using Content.Shared.Chat; +using Content.Client.Stylesheets; +using Content.Shared.Chat; using Content.Shared.Input; using Robust.Client.UserInterface.Controls; @@ -44,6 +45,7 @@ public ChatInputBox() StyleClasses = {"chatFilterOptionButton"} }; Container.AddChild(FilterButton); + AddStyleClass(StyleNano.StyleClassChatSubPanel); ChannelSelector.OnChannelSelect += UpdateActiveChannel; } diff --git a/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml b/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml index 090041fa93c..36cdce85985 100644 --- a/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml +++ b/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml @@ -7,11 +7,8 @@ HorizontalExpand="True" VerticalExpand="True" MinSize="465 225"> - - - - - + diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 51f782991e9..82d1fdd14a9 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1580,6 +1580,13 @@ public static readonly CVarDef * Accessibility */ + /// + /// Chat window opacity slider, controlling the alpha of the chat window background. + /// Goes from to 0 (completely transparent) to 1 (completely opaque) + /// + public static readonly CVarDef ChatWindowOpacity = + CVarDef.Create("accessibility.chat_window_transparency", 0.85f, CVar.CLIENTONLY | CVar.ARCHIVE); + /// /// Toggle for visual effects that may potentially cause motion sickness. /// Where reasonable, effects affected by this CVar should use an alternate effect. diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl index ff56d542744..67d09c90126 100644 --- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl +++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl @@ -48,6 +48,8 @@ ui-options-fancy-name-background = Add background to speech bubble names ui-options-enable-color-name = Add colors to character names ui-options-colorblind-friendly = Colorblind friendly mode ui-options-reduced-motion = Reduce motion of visual effects +ui-options-chat-window-opacity = Chat window opacity +ui-options-chat-window-opacity-percent = { TOSTRING($opacity, "P0") } ui-options-screen-shake-intensity = Screen shake intensity ui-options-screen-shake-percent = { TOSTRING($intensity, "P0") } ui-options-vsync = VSync From eace2378e73ca68ebc5ab607ccb9ba6bdcf3e310 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 20:50:07 +0000 Subject: [PATCH 123/133] 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 d901a49abd8..de9f27a3d5e 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Add title2.ogg to space ambience tracks. - type: Tweak - id: 5788 - time: '2024-01-25T13:59:49.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24501 - author: Tayrtahn changes: - message: Glued mobs can no longer struggle free from their captor's hands. @@ -3810,3 +3803,10 @@ id: 6287 time: '2024-04-01T08:29:13.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/24683 +- author: Sk1tch + changes: + - message: Added chat window opacity slider to options. + type: Add + id: 6288 + time: '2024-04-01T20:48:02.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/24990 From 7464d8284c5e18effe277f861e734736ccc906ee Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:00:10 +0300 Subject: [PATCH 124/133] Infinity books (#25840) * setup text data * roundstart reshuffling keywords with gibberish words * saved data categorized * add book with hints * start redrawing books * +4 book design * +books +random visual upgrade * finish first file * finish lore file * finish with books.rsi now authorbooks.rsi... * aurora! and some fix * nuke author books * speelbuke update * finish respriting work * fix scientist guide visual * setup datasets * setup stupid funny random story * restore author books, upgrade hint generation * add variety to story generator * add learning system * minor textgen edit * file restruct, hint count variation * more restruct * more renaming add basis learning system logic. Spears locked for special book for test. * nuke all systems, for splitting PR gods * typo fix * update migration with deleted books * add random story books to maint * Update construction-system.ftl * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: Hrosts <35345601+Hrosts@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> * typo fix * interchangeably * final * Update Resources/Prototypes/Datasets/Names/books.yml Co-authored-by: Hrosts <35345601+Hrosts@users.noreply.github.com> * "." * Update Content.Server/Paper/PaperRandomStorySystem.cs Co-authored-by: Hrosts <35345601+Hrosts@users.noreply.github.com> * Ubazer fix * inadequate * localized * Update meta.json * fuck merge conflicts * fix jani book --------- Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Co-authored-by: Hrosts <35345601+Hrosts@users.noreply.github.com> --- .../Paper/PaperRandomStoryComponent.cs | 14 + .../Paper/PaperRandomStorySystem.cs | 29 ++ .../RandomMetadata/RandomMetadataSystem.cs | 6 +- .../Prototypes/ConstructionPrototype.cs | 2 +- .../Locale/en-US/paper/story-generation.ftl | 244 ++++++++++++ .../Catalog/Cargo/cargo_service.yml | 4 +- .../Catalog/Fills/Books/bookshelf.yml | 90 +++-- .../Prototypes/Catalog/Fills/Books/lore.yml | 122 ------ .../Prototypes/Catalog/Fills/Lockers/misc.yml | 2 + .../Prototypes/Datasets/story_generation.yml | 266 +++++++++++++ .../Entities/Objects/Devices/cartridges.yml | 2 +- .../Entities/Objects/Magic/books.yml | 96 ++++- .../Entities/Objects/Misc/books.yml | 329 +++++++++++---- .../{authorbooks.yml => books_author.yml} | 374 +++++++++++++++--- .../Prototypes/Entities/Stations/base.yml | 2 +- .../Magic/spellbooks.rsi/bookfireball.png | Bin 652 -> 0 bytes .../Magic/spellbooks.rsi/bookforcewall.png | Bin 532 -> 0 bytes .../Magic/spellbooks.rsi/bookknock.png | Bin 693 -> 0 bytes .../Objects/Magic/spellbooks.rsi/meta.json | 47 --- .../Magic/spellbooks.rsi/spellbook.png | Bin 743 -> 0 bytes .../Misc/authorbooks.rsi/book_aurora.png | Bin 358 -> 0 bytes .../Misc/authorbooks.rsi/book_cafe.png | Bin 517 -> 0 bytes .../Misc/authorbooks.rsi/book_earth.png | Bin 498 -> 0 bytes .../authorbooks.rsi/book_ian_antarctica.png | Bin 756 -> 0 bytes .../Misc/authorbooks.rsi/book_ian_arctic.png | Bin 531 -> 0 bytes .../Misc/authorbooks.rsi/book_ian_city.png | Bin 538 -> 0 bytes .../Misc/authorbooks.rsi/book_ian_desert.png | Bin 557 -> 0 bytes .../authorbooks.rsi/book_ian_mountain.png | Bin 603 -> 0 bytes .../Misc/authorbooks.rsi/book_ian_ocean.png | Bin 542 -> 0 bytes .../Misc/authorbooks.rsi/book_ian_ranch.png | Bin 530 -> 0 bytes .../Misc/authorbooks.rsi/book_ian_wolfpup.png | Bin 552 -> 0 bytes .../Misc/authorbooks.rsi/book_journ_mount.png | Bin 445 -> 0 bytes .../Objects/Misc/authorbooks.rsi/book_map.png | Bin 379 -> 0 bytes .../Misc/authorbooks.rsi/book_medical.png | Bin 356 -> 0 bytes .../Misc/authorbooks.rsi/book_morgue.png | Bin 630 -> 0 bytes .../Misc/authorbooks.rsi/book_names.png | Bin 317 -> 0 bytes .../authorbooks.rsi/book_narsie_legend.png | Bin 570 -> 0 bytes .../Misc/authorbooks.rsi/book_possum.png | Bin 468 -> 0 bytes .../Misc/authorbooks.rsi/book_rufus.png | Bin 410 -> 0 bytes .../Misc/authorbooks.rsi/book_scmmd.png | Bin 452 -> 0 bytes .../Misc/authorbooks.rsi/book_scpz.png | Bin 438 -> 0 bytes .../Misc/authorbooks.rsi/book_scsss.png | Bin 351 -> 0 bytes .../Misc/authorbooks.rsi/book_struck.png | Bin 480 -> 0 bytes .../Objects/Misc/authorbooks.rsi/book_sun.png | Bin 643 -> 0 bytes .../Misc/authorbooks.rsi/book_temple.png | Bin 553 -> 0 bytes .../Misc/authorbooks.rsi/book_truth.png | Bin 556 -> 0 bytes .../Misc/authorbooks.rsi/book_watched.png | Bin 421 -> 0 bytes .../Misc/authorbooks.rsi/book_world.png | Bin 526 -> 0 bytes .../Objects/Misc/authorbooks.rsi/meta.json | 95 ----- .../Textures/Objects/Misc/books.rsi/book0.png | Bin 593 -> 0 bytes .../Textures/Objects/Misc/books.rsi/book1.png | Bin 802 -> 0 bytes .../Textures/Objects/Misc/books.rsi/book2.png | Bin 270 -> 0 bytes .../Textures/Objects/Misc/books.rsi/book3.png | Bin 265 -> 0 bytes .../Textures/Objects/Misc/books.rsi/book4.png | Bin 266 -> 0 bytes .../Textures/Objects/Misc/books.rsi/book5.png | Bin 255 -> 0 bytes .../Textures/Objects/Misc/books.rsi/book6.png | Bin 266 -> 0 bytes .../Textures/Objects/Misc/books.rsi/book7.png | Bin 341 -> 0 bytes .../Textures/Objects/Misc/books.rsi/book8.png | Bin 289 -> 0 bytes .../Objects/Misc/books.rsi/book_bar.png | Bin 371 -> 0 bytes .../Misc/books.rsi/book_boneworking.png | Bin 687 -> 0 bytes .../Objects/Misc/books.rsi/book_borg.png | Bin 515 -> 0 bytes .../Objects/Misc/books.rsi/book_chemistry.png | Bin 360 -> 0 bytes .../Objects/Misc/books.rsi/book_cloning.png | Bin 352 -> 0 bytes .../Objects/Misc/books.rsi/book_cooking.png | Bin 500 -> 0 bytes .../Misc/books.rsi/book_demonomicon.png | Bin 1015 -> 0 bytes .../Objects/Misc/books.rsi/book_detective.png | Bin 396 -> 0 bytes .../Misc/books.rsi/book_engineering.png | Bin 585 -> 0 bytes .../Misc/books.rsi/book_engineering2.png | Bin 672 -> 0 bytes .../Objects/Misc/books.rsi/book_fish.png | Bin 514 -> 0 bytes .../Objects/Misc/books.rsi/book_hacking.png | Bin 487 -> 0 bytes .../books.rsi/book_hydroponics_pod_people.png | Bin 593 -> 0 bytes .../Objects/Misc/books.rsi/book_icon.png | Bin 0 -> 652 bytes .../Misc/books.rsi/book_infections.png | Bin 427 -> 0 bytes .../Objects/Misc/books.rsi/book_medical.png | Bin 361 -> 0 bytes .../Objects/Misc/books.rsi/book_nuclear.png | Bin 809 -> 0 bytes .../Objects/Misc/books.rsi/book_origami.png | Bin 376 -> 0 bytes .../books.rsi/book_particle_accelerator.png | Bin 402 -> 0 bytes .../Objects/Misc/books.rsi/book_science.png | Bin 291 -> 0 bytes .../Objects/Misc/books.rsi/book_security.png | Bin 319 -> 0 bytes .../Objects/Misc/books.rsi/book_space_law.png | Bin 434 -> 0 bytes .../Objects/Misc/books.rsi/cover_base.png | Bin 0 -> 288 bytes .../Objects/Misc/books.rsi/cover_old.png | Bin 0 -> 578 bytes .../Objects/Misc/books.rsi/cover_strong.png | Bin 0 -> 470 bytes .../Objects/Misc/books.rsi/decor_bottom.png | Bin 0 -> 220 bytes .../Objects/Misc/books.rsi/decor_diagonal.png | Bin 0 -> 246 bytes .../Objects/Misc/books.rsi/decor_middle.png | Bin 0 -> 173 bytes .../Objects/Misc/books.rsi/decor_spine.png | Bin 0 -> 195 bytes .../Misc/books.rsi/decor_vertical_middle.png | Bin 0 -> 196 bytes .../Objects/Misc/books.rsi/decor_wingette.png | Bin 0 -> 179 bytes .../Misc/books.rsi/decor_wingette_circle.png | Bin 0 -> 213 bytes .../Misc/books.rsi/decor_wingette_flat.png | Bin 0 -> 138 bytes .../Misc/books.rsi/detail_bookmark.png | Bin 0 -> 169 bytes .../Objects/Misc/books.rsi/detail_rivets.png | Bin 0 -> 142 bytes .../Objects/Misc/books.rsi/icon_apple.png | Bin 0 -> 218 bytes .../Objects/Misc/books.rsi/icon_atmos.png | Bin 0 -> 246 bytes .../Objects/Misc/books.rsi/icon_aurora.png | Bin 0 -> 248 bytes .../Objects/Misc/books.rsi/icon_banana.png | Bin 0 -> 193 bytes .../Objects/Misc/books.rsi/icon_bar.png | Bin 0 -> 218 bytes .../Objects/Misc/books.rsi/icon_biohazard.png | Bin 0 -> 179 bytes .../Objects/Misc/books.rsi/icon_borg.png | Bin 0 -> 293 bytes .../Objects/Misc/books.rsi/icon_briefcase.png | Bin 0 -> 194 bytes .../Objects/Misc/books.rsi/icon_bucket.png | Bin 0 -> 212 bytes .../Objects/Misc/books.rsi/icon_cabbage.png | Bin 0 -> 439 bytes .../Objects/Misc/books.rsi/icon_chemical.png | Bin 0 -> 218 bytes .../Objects/Misc/books.rsi/icon_corner.png | Bin 0 -> 137 bytes .../Objects/Misc/books.rsi/icon_diamond.png | Bin 0 -> 193 bytes .../Objects/Misc/books.rsi/icon_dna.png | Bin 0 -> 163 bytes .../Objects/Misc/books.rsi/icon_eye.png | Bin 0 -> 172 bytes .../Objects/Misc/books.rsi/icon_fish.png | Bin 0 -> 342 bytes .../Objects/Misc/books.rsi/icon_glow.png | Bin 0 -> 230 bytes .../Objects/Misc/books.rsi/icon_hacking.png | Bin 0 -> 306 bytes .../Objects/Misc/books.rsi/icon_ian.png | Bin 0 -> 390 bytes .../Objects/Misc/books.rsi/icon_law.png | Bin 0 -> 240 bytes .../Objects/Misc/books.rsi/icon_letter_N.png | Bin 0 -> 164 bytes .../Objects/Misc/books.rsi/icon_letter_P.png | Bin 0 -> 179 bytes .../Objects/Misc/books.rsi/icon_lightning.png | Bin 0 -> 181 bytes .../Objects/Misc/books.rsi/icon_magic.png | Bin 0 -> 184 bytes .../Misc/books.rsi/icon_magic_fireball.png | Bin 0 -> 342 bytes .../Misc/books.rsi/icon_magic_forcewall.png | Bin 0 -> 334 bytes .../Misc/books.rsi/icon_magic_knock.png | Bin 0 -> 368 bytes .../Objects/Misc/books.rsi/icon_magnifier.png | Bin 0 -> 266 bytes .../Objects/Misc/books.rsi/icon_medical.png | Bin 0 -> 180 bytes .../Misc/books.rsi/icon_medical_cross.png | Bin 0 -> 215 bytes .../Objects/Misc/books.rsi/icon_mount.png | Bin 0 -> 311 bytes .../Objects/Misc/books.rsi/icon_nuclear.png | Bin 0 -> 316 bytes .../Objects/Misc/books.rsi/icon_origami.png | Bin 0 -> 215 bytes .../Misc/books.rsi/icon_pentagramm.png | Bin 0 -> 201 bytes .../Objects/Misc/books.rsi/icon_planet.png | Bin 0 -> 311 bytes .../Objects/Misc/books.rsi/icon_possum.png | Bin 0 -> 337 bytes .../Objects/Misc/books.rsi/icon_question.png | Bin 0 -> 256 bytes .../Objects/Misc/books.rsi/icon_scmmd.png | Bin 0 -> 280 bytes .../Objects/Misc/books.rsi/icon_skull.png | Bin 0 -> 270 bytes .../Objects/Misc/books.rsi/icon_stars.png | Bin 0 -> 181 bytes .../Objects/Misc/books.rsi/icon_stars2.png | Bin 0 -> 160 bytes .../Objects/Misc/books.rsi/icon_stunbaton.png | Bin 0 -> 183 bytes .../Objects/Misc/books.rsi/icon_temple.png | Bin 0 -> 381 bytes .../Objects/Misc/books.rsi/icon_text.png | Bin 0 -> 182 bytes .../Objects/Misc/books.rsi/icon_text2.png | Bin 0 -> 167 bytes .../Objects/Misc/books.rsi/icon_text3.png | Bin 0 -> 160 bytes .../Objects/Misc/books.rsi/icon_time.png | Bin 0 -> 205 bytes .../Objects/Misc/books.rsi/icon_tree.png | Bin 0 -> 417 bytes .../Objects/Misc/books.rsi/icon_wrench.png | Bin 0 -> 473 bytes .../Textures/Objects/Misc/books.rsi/meta.json | 202 ++++++++-- .../Objects/Misc/books.rsi/overlay_blood.png | Bin 0 -> 335 bytes .../Objects/Misc/books.rsi/overlay_dirt.png | Bin 0 -> 608 bytes .../Objects/Misc/books.rsi/overlay_null.png | Bin 0 -> 96 bytes .../Textures/Objects/Misc/books.rsi/paper.png | Bin 0 -> 327 bytes .../Objects/Misc/books.rsi/paper_blood.png | Bin 0 -> 686 bytes Resources/migration.yml | 15 +- 149 files changed, 1442 insertions(+), 499 deletions(-) create mode 100644 Content.Server/Paper/PaperRandomStoryComponent.cs create mode 100644 Content.Server/Paper/PaperRandomStorySystem.cs create mode 100644 Resources/Locale/en-US/paper/story-generation.ftl delete mode 100644 Resources/Prototypes/Catalog/Fills/Books/lore.yml create mode 100644 Resources/Prototypes/Datasets/story_generation.yml rename Resources/Prototypes/Entities/Objects/Misc/{authorbooks.yml => books_author.yml} (68%) delete mode 100644 Resources/Textures/Objects/Magic/spellbooks.rsi/bookfireball.png delete mode 100644 Resources/Textures/Objects/Magic/spellbooks.rsi/bookforcewall.png delete mode 100644 Resources/Textures/Objects/Magic/spellbooks.rsi/bookknock.png delete mode 100644 Resources/Textures/Objects/Magic/spellbooks.rsi/meta.json delete mode 100644 Resources/Textures/Objects/Magic/spellbooks.rsi/spellbook.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_aurora.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_cafe.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_earth.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_antarctica.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_arctic.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_city.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_desert.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_mountain.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_ocean.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_ranch.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_wolfpup.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_journ_mount.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_map.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_medical.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_morgue.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_names.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_narsie_legend.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_possum.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_rufus.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_scmmd.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_scpz.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_scsss.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_struck.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_sun.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_temple.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_truth.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_watched.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/book_world.png delete mode 100644 Resources/Textures/Objects/Misc/authorbooks.rsi/meta.json delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book0.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book1.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book2.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book3.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book4.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book5.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book6.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book7.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book8.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_bar.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_boneworking.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_borg.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_chemistry.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_cloning.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_cooking.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_demonomicon.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_detective.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_engineering.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_engineering2.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_fish.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_hacking.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_hydroponics_pod_people.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_icon.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_infections.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_medical.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_nuclear.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_origami.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_particle_accelerator.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_science.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_security.png delete mode 100644 Resources/Textures/Objects/Misc/books.rsi/book_space_law.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/cover_base.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/cover_old.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/cover_strong.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/decor_bottom.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/decor_diagonal.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/decor_middle.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/decor_spine.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/decor_vertical_middle.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/decor_wingette.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/decor_wingette_circle.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/decor_wingette_flat.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/detail_bookmark.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/detail_rivets.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_apple.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_atmos.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_aurora.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_banana.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_bar.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_biohazard.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_borg.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_briefcase.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_bucket.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_cabbage.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_chemical.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_corner.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_diamond.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_dna.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_eye.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_fish.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_glow.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_hacking.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_ian.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_law.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_letter_N.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_letter_P.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_lightning.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_magic.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_magic_fireball.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_magic_forcewall.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_magic_knock.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_magnifier.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_medical.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_medical_cross.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_mount.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_nuclear.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_origami.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_pentagramm.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_planet.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_possum.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_question.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_scmmd.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_skull.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_stars.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_stars2.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_stunbaton.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_temple.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_text.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_text2.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_text3.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_time.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_tree.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/icon_wrench.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/overlay_blood.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/overlay_dirt.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/overlay_null.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/paper.png create mode 100644 Resources/Textures/Objects/Misc/books.rsi/paper_blood.png diff --git a/Content.Server/Paper/PaperRandomStoryComponent.cs b/Content.Server/Paper/PaperRandomStoryComponent.cs new file mode 100644 index 00000000000..7c5744f0878 --- /dev/null +++ b/Content.Server/Paper/PaperRandomStoryComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server.Paper; + +/// +/// Adds randomly generated stories to Paper component +/// +[RegisterComponent, Access(typeof(PaperRandomStorySystem))] +public sealed partial class PaperRandomStoryComponent : Component +{ + [DataField] + public List? StorySegments; + + [DataField] + public string StorySeparator = " "; +} diff --git a/Content.Server/Paper/PaperRandomStorySystem.cs b/Content.Server/Paper/PaperRandomStorySystem.cs new file mode 100644 index 00000000000..e7712009c2e --- /dev/null +++ b/Content.Server/Paper/PaperRandomStorySystem.cs @@ -0,0 +1,29 @@ +using Content.Server.RandomMetadata; + +namespace Content.Server.Paper; + +public sealed class PaperRandomStorySystem : EntitySystem +{ + + [Dependency] private readonly RandomMetadataSystem _randomMeta = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapinit); + } + + private void OnMapinit(Entity paperStory, ref MapInitEvent ev) + { + if (!TryComp(paperStory, out var paper)) + return; + + if (paperStory.Comp.StorySegments == null) + return; + + var story = _randomMeta.GetRandomFromSegments(paperStory.Comp.StorySegments, paperStory.Comp.StorySeparator); + + paper.Content += $"\n{story}"; + } +} diff --git a/Content.Server/RandomMetadata/RandomMetadataSystem.cs b/Content.Server/RandomMetadata/RandomMetadataSystem.cs index ebec4d5249f..c088d57fd96 100644 --- a/Content.Server/RandomMetadata/RandomMetadataSystem.cs +++ b/Content.Server/RandomMetadata/RandomMetadataSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Dataset; +using Content.Shared.Dataset; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -48,8 +48,8 @@ public string GetRandomFromSegments(List segments, string? separator) foreach (var segment in segments) { outputSegments.Add(_prototype.TryIndex(segment, out var proto) - ? _random.Pick(proto.Values) - : segment); + ? Loc.GetString(_random.Pick(proto.Values)) + : Loc.GetString(segment)); } return string.Join(separator, outputSegments); } diff --git a/Content.Shared/Construction/Prototypes/ConstructionPrototype.cs b/Content.Shared/Construction/Prototypes/ConstructionPrototype.cs index e9863f83641..a97b045cd83 100644 --- a/Content.Shared/Construction/Prototypes/ConstructionPrototype.cs +++ b/Content.Shared/Construction/Prototypes/ConstructionPrototype.cs @@ -1,4 +1,4 @@ -using Content.Shared.Construction.Conditions; +using Content.Shared.Construction.Conditions; using Content.Shared.Whitelist; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; diff --git a/Resources/Locale/en-US/paper/story-generation.ftl b/Resources/Locale/en-US/paper/story-generation.ftl new file mode 100644 index 00000000000..bcd1c8901e0 --- /dev/null +++ b/Resources/Locale/en-US/paper/story-generation.ftl @@ -0,0 +1,244 @@ +story-gen-book-type1 = book +story-gen-book-type2 = folio +story-gen-book-type3 = collection +story-gen-book-type4 = notes +story-gen-book-type5 = manuscript +story-gen-book-type6 = records +story-gen-book-type7 = tome +story-gen-book-type8 = journal +story-gen-book-type9 = archives +story-gen-book-type10= codex +story-gen-book-type11= memories +story-gen-book-type12= compendium + +story-gen-book-genre1 = work of crime fiction +story-gen-book-genre2 = comedy +story-gen-book-genre3 = horror story +story-gen-book-genre4 = poem +story-gen-book-genre5 = novella +story-gen-book-genre6 = chronicle +story-gen-book-genre7 = work of science-fiction +story-gen-book-genre8 = fantasy story +story-gen-book-genre9 = romance +story-gen-book-genre10= thriller +story-gen-book-genre11= work of historical fiction +story-gen-book-genre12= biography +story-gen-book-genre13= adventure story +story-gen-book-genre14= drama + +story-gen-book-appearance1 = ancient +story-gen-book-appearance2 = shabby +story-gen-book-appearance3 = dirty +story-gen-book-appearance4 = unusual +story-gen-book-appearance5 = faded +story-gen-book-appearance6 = nasty +story-gen-book-appearance7 = dusty +story-gen-book-appearance8 = scary +story-gen-book-appearance9 = bloody +story-gen-book-appearance10= bright +story-gen-book-appearance11= dubious +story-gen-book-appearance12= intriguing +story-gen-book-appearance13= ugly +story-gen-book-appearance14= crooked +story-gen-book-appearance15= crumpled +story-gen-book-appearance16= dirty +story-gen-book-appearance17= elegant +story-gen-book-appearance18= ornate +story-gen-book-appearance19= weathered +story-gen-book-appearance20= chrisp +story-gen-book-appearance21= lavish +story-gen-book-appearance22= tattered +story-gen-book-appearance23= polished +story-gen-book-appearance24= embossed +story-gen-book-appearance25= mismatched +story-gen-book-appearance26= gilded +story-gen-book-appearance27= strange + +story-gen-book-character1 = clown +story-gen-book-character2 = mime +story-gen-book-character3 = reporter +story-gen-book-character4 = butcher +story-gen-book-character5 = bartender +story-gen-book-character6 = janitor +story-gen-book-character7 = engineer +story-gen-book-character8 = scientist +story-gen-book-character9 = guard +story-gen-book-character10 = doctor +story-gen-book-character11 = chemist +story-gen-book-character12 = prisoner +story-gen-book-character13 = researcher +story-gen-book-character14 = trader +story-gen-book-character15 = captain +story-gen-book-character16 = lizard +story-gen-book-character17 = moth +story-gen-book-character18 = diona +story-gen-book-character19 = cat-girl +story-gen-book-character20 = cat +story-gen-book-character21 = corgi +story-gen-book-character22 = dog +story-gen-book-character23 = opossum +story-gen-book-character24 = sloth +story-gen-book-character25 = syndicate agent +story-gen-book-character26 = revenant +story-gen-book-character27 = rat king +story-gen-book-character28 = ninja +story-gen-book-character29 = space dragon +story-gen-book-character30 = revolutionary +story-gen-book-character31 = nuclear operative +story-gen-book-character32 = narsie cultist +story-gen-book-character33 = ratwar cultist +story-gen-book-character34 = greytider +story-gen-book-character35 = arachnid +story-gen-book-character36 = vox +story-gen-book-character37 = dwarf +story-gen-book-character38 = thief +story-gen-book-character39 = wizard +story-gen-book-character40 = slime + +story-gen-book-character-trait1 = stupid +story-gen-book-character-trait2 = smart +story-gen-book-character-trait3 = funny +story-gen-book-character-trait4 = attractive +story-gen-book-character-trait5 = charming +story-gen-book-character-trait6 = nasty +story-gen-book-character-trait7 = dying +story-gen-book-character-trait8 = old +story-gen-book-character-trait9 = young +story-gen-book-character-trait10 = rich +story-gen-book-character-trait11 = poor +story-gen-book-character-trait12 = popular +story-gen-book-character-trait13 = absent-minded +story-gen-book-character-trait14 = stern +story-gen-book-character-trait15 = сharismatic +story-gen-book-character-trait16 = stoic +story-gen-book-character-trait17 = cute +story-gen-book-character-trait18 = dwarven +story-gen-book-character-trait19 = beer-smelling +story-gen-book-character-trait20 = joyful +story-gen-book-character-trait21 = painfully beautiful +story-gen-book-character-trait22 = robotic +story-gen-book-character-trait23 = holographic +story-gen-book-character-trait24 = hysterically laughing + +story-gen-book-event1 = a zombie outbreak +story-gen-book-event2 = a nuclear explosion +story-gen-book-event3 = a mass murder +story-gen-book-event4 = a sudden depressurization +story-gen-book-event5 = a blackout +story-gen-book-event6 = the starvation of the protagonists +story-gen-book-event7 = a wasting illness +story-gen-book-event8 = love at first sight +story-gen-book-event9 = a rush of inspiration +story-gen-book-event10 = the occurrence of some mystical phenomena +story-gen-book-event11 = divine intervention +story-gen-book-event12 = the characters' own selfish motives +story-gen-book-event13 = an unforeseen deception +story-gen-book-event14 = the resurrection of one of these characters from the dead +story-gen-book-event15 = the terrible torture of the protagonist +story-gen-book-event16 = the inadvertent loosing of a gravitational singularity +story-gen-book-event17 = a psychic prediction of future events +story-gen-book-event18 = an antimatter explosion +story-gen-book-event19 = a chance meeting with a cat-girl +story-gen-book-event20 = drinking far too much alcohol +story-gen-book-event21 = eating way too much pizza +story-gen-book-event22 = having a quarrel with a close friend +story-gen-book-event23 = the sudden loss of their home in a fiery blaze +story-gen-book-event24 = the loss of a PDA + +story-gen-book-action1 = share in a kiss with a +story-gen-book-action2 = strangle to death a +story-gen-book-action3 = manage to blow apart a +story-gen-book-action4 = manage to win a game of chess against a +story-gen-book-action5 = narrowly lose a game of chess against a +story-gen-book-action6 = reveal the hidden secrets of a +story-gen-book-action7 = manipulate a +story-gen-book-action8 = sacrifice upon an altar a +story-gen-book-action9 = attend the wedding of a +story-gen-book-action10 = join forces to defeat their common enemy, a +story-gen-book-action11 = are forced to work together to escape a +story-gen-book-action12 = give a valuable gift to + +story-gen-book-action-trait1 = terribly +story-gen-book-action-trait2 = disgustingly +story-gen-book-action-trait3 = marvelously +story-gen-book-action-trait4 = nicely +story-gen-book-action-trait5 = weirdly +story-gen-book-action-trait6 = amusingly +story-gen-book-action-trait7 = fancifully +story-gen-book-action-trait8 = impressively +story-gen-book-action-trait9 = irresponsibly +story-gen-book-action-trait10 = severely +story-gen-book-action-trait11 = ruthlessly +story-gen-book-action-trait12 = playfully +story-gen-book-action-trait13 = thoughtfully + +story-gen-book-location1 = in an underground complex +story-gen-book-location2 = while on an expedition +story-gen-book-location3 = while trapped in outer space +story-gen-book-location4 = while in a news office +story-gen-book-location5 = in a hidden garden +story-gen-book-location6 = in the kitchen of a local restaurant +story-gen-book-location7 = under the counter of the local sports bar +story-gen-book-location8 = in an ancient library +story-gen-book-location9 = while deep in bowels of the space station's maintenance corridors +story-gen-book-location10 = on the bridge of a starship +story-gen-book-location11 = while in a grungy public bathroom +story-gen-book-location12 = while trapped inside a crate +story-gen-book-location13 = while stuck inside a locker +story-gen-book-location14 = while stationed on Barratry +story-gen-book-location15 = while in the hall of rustic church +story-gen-book-location16 = while in a crematorium +story-gen-book-location17 = standing too close to an anomaly +story-gen-book-location18 = while huddling on the evacuation shuttle +story-gen-book-location19 = standing in freshly fallen snow +story-gen-book-location20 = lost in the woods +story-gen-book-location21 = iin the harsh desert +story-gen-book-location22 = worrying about their social media networks +story-gen-book-location23 = atop of a mountain +story-gen-book-location24 = while driving a car +story-gen-book-location25 = in an escape pod +story-gen-book-location26 = while abroad in a fictional country +story-gen-book-location27 = clinging to the wing of an inflight airplane +story-gen-book-location28 = inside a pocket dimension +story-gen-book-location29 = onboard a Wizard Federation shuttle +story-gen-book-location30 = standing atop of a mountain of corpses +story-gen-book-location31 = while psychically projected into their subconscious +story-gen-book-location32 = while trapped in a shadow dimension +story-gen-book-location33 = while trying to escape a destroyed space station +story-gen-book-location34 = while sandwiched between a Tesla ball and a gravitational singularity + +story-gen-book-element1 = The plot +story-gen-book-element2 = The twist +story-gen-book-element3 = The climax +story-gen-book-element4 = The final act +story-gen-book-element5 = The ending +story-gen-book-element6 = The moral of the story +story-gen-book-element7 = The theme of this work +story-gen-book-element8 = The literary style +story-gen-book-element9 = The illustrations + +story-gen-book-element-trait1 = terrifying +story-gen-book-element-trait2 = disgusting +story-gen-book-element-trait3 = wonderful +story-gen-book-element-trait4 = cute +story-gen-book-element-trait5 = boring +story-gen-book-element-trait6 = strange +story-gen-book-element-trait7 = amusing +story-gen-book-element-trait8 = whimsical +story-gen-book-element-trait9 = impressive +story-gen-book-element-trait10 = interesting +story-gen-book-element-trait11 = inadequate +story-gen-book-element-trait12 = sad +story-gen-book-element-trait13 = rather depressing + + + + + + + + + + + + diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_service.yml b/Resources/Prototypes/Catalog/Cargo/cargo_service.yml index f27adb65a4a..267f706f3bd 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_service.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_service.yml @@ -82,7 +82,7 @@ id: ServiceBooks icon: sprite: Objects/Misc/books.rsi - state: book0 + state: book_icon product: CrateServiceBooks cost: 1000 category: cargoproduct-category-name-service @@ -92,7 +92,7 @@ id: ServiceGuidebooks icon: sprite: Objects/Misc/books.rsi - state: book_engineering2 + state: book_icon product: CrateServiceGuidebooks cost: 1300 category: cargoproduct-category-name-service diff --git a/Resources/Prototypes/Catalog/Fills/Books/bookshelf.yml b/Resources/Prototypes/Catalog/Fills/Books/bookshelf.yml index 48fab5dcccd..b52e8530085 100644 --- a/Resources/Prototypes/Catalog/Fills/Books/bookshelf.yml +++ b/Resources/Prototypes/Catalog/Fills/Books/bookshelf.yml @@ -5,93 +5,123 @@ components: - type: StorageFill contents: - - id: BookRandom - prob: 0.4 - amount: 1 - maxAmount: 4 + - id: BookRandomStory + prob: 0.6 + amount: 2 + maxAmount: 6 + - id: BookSpaceEncyclopedia + orGroup: BookPool + - id: BookTheBookOfControl + orGroup: BookPool + - id: BookBartendersManual + orGroup: BookPool + - id: BookChefGaming + orGroup: BookPool + - id: BookLeafLoversSecret + orGroup: BookPool + - id: BookEngineersHandbook + orGroup: BookPool + - id: BookScientistsGuidebook + orGroup: BookPool + - id: BookSecurity + orGroup: BookPool + - id: BookHowToKeepStationClean + orGroup: BookPool + - id: BookHowToRockAndStone + orGroup: BookPool + - id: BookMedicalReferenceBook + orGroup: BookPool + - id: BookHowToSurvive + orGroup: BookPool + - id: BookChemicalCompendium + orGroup: BookPool - id: BookNarsieLegend prob: 0.1 + orGroup: BookAuthor - id: BookTruth prob: 0.1 + orGroup: BookAuthor - id: BookWorld prob: 0.1 + orGroup: BookAuthor - id: BookIanAntarctica prob: 0.1 + orGroup: BookAuthor - id: BookSlothClownSSS prob: 0.1 + orGroup: BookAuthor - id: BookSlothClownPranks prob: 0.1 + orGroup: BookAuthor - id: BookSlothClownMMD prob: 0.1 + orGroup: BookAuthor - id: BookStruck prob: 0.1 + orGroup: BookAuthor - id: BookSun prob: 0.1 + orGroup: BookAuthor - id: BookPossum prob: 0.1 + orGroup: BookAuthor - id: BookCafe prob: 0.1 + orGroup: BookAuthor - id: BookFeather prob: 0.1 + orGroup: BookAuthor - id: BookIanLostWolfPup prob: 0.1 + orGroup: BookAuthor - id: BookIanRanch prob: 0.1 + orGroup: BookAuthor - id: BookIanOcean prob: 0.1 + orGroup: BookAuthor - id: BookIanMountain prob: 0.1 + orGroup: BookAuthor - id: BookIanCity prob: 0.1 + orGroup: BookAuthor - id: BookIanArctic prob: 0.1 + orGroup: BookAuthor - id: BookIanDesert prob: 0.1 + orGroup: BookAuthor - id: BookNames prob: 0.1 + orGroup: BookAuthor - id: BookEarth prob: 0.1 + orGroup: BookAuthor - id: BookAurora prob: 0.1 + orGroup: BookAuthor - id: BookTemple prob: 0.1 + orGroup: BookAuthor - id: BookWatched prob: 0.1 + orGroup: BookAuthor - id: BookMedicalOfficer prob: 0.1 + orGroup: BookAuthor - id: BookMorgue prob: 0.1 + orGroup: BookAuthor - id: BookRufus prob: 0.1 + orGroup: BookAuthor - id: BookMap prob: 0.1 + orGroup: BookAuthor - id: BookJourney prob: 0.1 + orGroup: BookAuthor - id: BookInspiration prob: 0.1 - - id: BookSpaceEncyclopedia - orGroup: BookPool - - id: BookTheBookOfControl - orGroup: BookPool - - id: BookBartendersManual - orGroup: BookPool - - id: BookChefGaming - orGroup: BookPool - - id: BookLeafLoversSecret - orGroup: BookPool - - id: BookEngineersHandbook - orGroup: BookPool - - id: BookScientistsGuidebook - orGroup: BookPool - - id: BookSecurity - orGroup: BookPool - - id: BookHowToKeepStationClean - orGroup: BookPool - - id: BookHowToRockAndStone - orGroup: BookPool - - id: BookMedicalReferenceBook - orGroup: BookPool - - id: BookHowToSurvive - orGroup: BookPool - - id: BookChemicalCompendium - orGroup: BookPool + orGroup: BookAuthor diff --git a/Resources/Prototypes/Catalog/Fills/Books/lore.yml b/Resources/Prototypes/Catalog/Fills/Books/lore.yml deleted file mode 100644 index 0fd712d42f8..00000000000 --- a/Resources/Prototypes/Catalog/Fills/Books/lore.yml +++ /dev/null @@ -1,122 +0,0 @@ -# ---- Library Salvage Fills ---- - -- type: entity - name: demonomicon - parent: BookBase - id: BookDemonomicon - noSpawn: true - description: 'Who knows what dark spells may be contained in these horrid pages?' - components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book_demonomicon - -- type: entity - name: demonomicon - parent: BookDemonomicon - id: BookDemonomiconRandom - suffix: random - components: - - type: RandomSpawner - prototypes: - - BookDemonomicon1 - - BookDemonomicon2 - - BookDemonomicon3 - offset: 0.1 - -- type: entity - parent: BookDemonomicon - id: BookDemonomicon1 - suffix: 1 - components: - - type: Paper - content: book-text-demonomicon1 - -- type: entity - parent: BookDemonomicon - id: BookDemonomicon2 - suffix: 2 - components: - - type: Paper - content: book-text-demonomicon2 - -- type: entity - parent: BookDemonomicon - id: BookDemonomicon3 - suffix: 3 - components: - - type: Paper - content: book-text-demonomicon3 - -- type: entity - name: pharmaceutical manuscript - parent: BookBase - id: BookChemistryInsane - suffix: library salvage - description: 'You can tell whoever wrote this was off the desoxy HARD.' - components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book_chemistry - - type: Paper - content: book-text-chemistry-insane - -- type: entity - name: botanical textbook - parent: BookBase - id: BookBotanicalTextbook - suffix: library salvage - description: 'Only a couple pages are left.' - components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book_hydroponics_pod_people - - type: Paper - content: book-text-botanics - -- type: entity - parent: BookBase - id: BookGnominomicon - name: gnominomicon - suffix: library salvage - description: You don't like the look of this. Looks - components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book5 - - type: Paper - content: book-text-gnome - -- type: entity - parent: BookBase - id: BookFishing - name: Tales from the Fishbowl - suffix: library salvage - description: This book sucks. - components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book_fish - - type: Paper - content: book-text-fishing - -- type: entity - parent: BookBase - id: BookDetective - name: Strokgraeth Holmes, Dwarf Detective - suffix: library salvage - description: Exciting! Invigorating! This author died after his book career failed. - components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book_detective - - type: Paper - content: book-text-detective - -# ---- End Library Salvage Fills ---- diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index 25078dbe57f..d8b6004ec3b 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -155,6 +155,8 @@ prob: 0.20 - id: BarberScissors prob: 0.05 + - id: BookRandomStory + prob: 0.1 # Syndicate loot - id: null prob: 0.95 diff --git a/Resources/Prototypes/Datasets/story_generation.yml b/Resources/Prototypes/Datasets/story_generation.yml new file mode 100644 index 00000000000..1083a6acdb6 --- /dev/null +++ b/Resources/Prototypes/Datasets/story_generation.yml @@ -0,0 +1,266 @@ +- type: dataset + id: book_type + values: + - story-gen-book-type1 + - story-gen-book-type2 + - story-gen-book-type3 + - story-gen-book-type4 + - story-gen-book-type5 + - story-gen-book-type6 + - story-gen-book-type7 + - story-gen-book-type8 + - story-gen-book-type9 + - story-gen-book-type10 + - story-gen-book-type11 + - story-gen-book-type12 + +- type: dataset + id: book_genre + values: + - story-gen-book-genre1 + - story-gen-book-genre2 + - story-gen-book-genre3 + - story-gen-book-genre4 + - story-gen-book-genre5 + - story-gen-book-genre6 + - story-gen-book-genre7 + - story-gen-book-genre8 + - story-gen-book-genre9 + - story-gen-book-genre10 + - story-gen-book-genre11 + - story-gen-book-genre12 + - story-gen-book-genre13 + - story-gen-book-genre14 + +- type: dataset + id: book_hint_appearance + values: + - story-gen-book-appearance1 + - story-gen-book-appearance2 + - story-gen-book-appearance3 + - story-gen-book-appearance4 + - story-gen-book-appearance5 + - story-gen-book-appearance6 + - story-gen-book-appearance7 + - story-gen-book-appearance8 + - story-gen-book-appearance9 + - story-gen-book-appearance10 + - story-gen-book-appearance11 + - story-gen-book-appearance12 + - story-gen-book-appearance13 + - story-gen-book-appearance14 + - story-gen-book-appearance15 + - story-gen-book-appearance16 + - story-gen-book-appearance17 + - story-gen-book-appearance18 + - story-gen-book-appearance19 + - story-gen-book-appearance20 + - story-gen-book-appearance21 + - story-gen-book-appearance22 + - story-gen-book-appearance23 + - story-gen-book-appearance24 + - story-gen-book-appearance25 + - story-gen-book-appearance26 + - story-gen-book-appearance27 + +- type: dataset + id: book_character + values: + - story-gen-book-character1 + - story-gen-book-character2 + - story-gen-book-character3 + - story-gen-book-character4 + - story-gen-book-character5 + - story-gen-book-character6 + - story-gen-book-character7 + - story-gen-book-character8 + - story-gen-book-character9 + - story-gen-book-character10 + - story-gen-book-character11 + - story-gen-book-character12 + - story-gen-book-character13 + - story-gen-book-character14 + - story-gen-book-character15 + - story-gen-book-character16 + - story-gen-book-character17 + - story-gen-book-character18 + - story-gen-book-character19 + - story-gen-book-character20 + - story-gen-book-character21 + - story-gen-book-character22 + - story-gen-book-character23 + - story-gen-book-character24 + - story-gen-book-character25 + - story-gen-book-character26 + - story-gen-book-character27 + - story-gen-book-character28 + - story-gen-book-character29 + - story-gen-book-character30 + - story-gen-book-character31 + - story-gen-book-character32 + - story-gen-book-character33 + - story-gen-book-character34 + - story-gen-book-character35 + - story-gen-book-character36 + - story-gen-book-character37 + - story-gen-book-character38 + - story-gen-book-character39 + - story-gen-book-character40 + +- type: dataset + id: book_character_trait + values: + - story-gen-book-character-trait1 + - story-gen-book-character-trait2 + - story-gen-book-character-trait3 + - story-gen-book-character-trait4 + - story-gen-book-character-trait5 + - story-gen-book-character-trait6 + - story-gen-book-character-trait7 + - story-gen-book-character-trait8 + - story-gen-book-character-trait9 + - story-gen-book-character-trait10 + - story-gen-book-character-trait11 + - story-gen-book-character-trait12 + - story-gen-book-character-trait13 + - story-gen-book-character-trait14 + - story-gen-book-character-trait15 + - story-gen-book-character-trait16 + - story-gen-book-character-trait17 + - story-gen-book-character-trait18 + - story-gen-book-character-trait19 + - story-gen-book-character-trait20 + - story-gen-book-character-trait21 + - story-gen-book-character-trait22 + - story-gen-book-character-trait23 + - story-gen-book-character-trait24 + + +- type: dataset + id: book_event + values: + - story-gen-book-event1 + - story-gen-book-event2 + - story-gen-book-event3 + - story-gen-book-event4 + - story-gen-book-event5 + - story-gen-book-event6 + - story-gen-book-event7 + - story-gen-book-event8 + - story-gen-book-event9 + - story-gen-book-event10 + - story-gen-book-event11 + - story-gen-book-event12 + - story-gen-book-event13 + - story-gen-book-event14 + - story-gen-book-event15 + - story-gen-book-event16 + - story-gen-book-event17 + - story-gen-book-event18 + - story-gen-book-event19 + - story-gen-book-event20 + - story-gen-book-event21 + - story-gen-book-event22 + - story-gen-book-event23 + - story-gen-book-event24 + +- type: dataset + id: book_action + values: + - story-gen-book-action1 + - story-gen-book-action2 + - story-gen-book-action3 + - story-gen-book-action4 + - story-gen-book-action5 + - story-gen-book-action6 + - story-gen-book-action7 + - story-gen-book-action8 + - story-gen-book-action9 + - story-gen-book-action10 + - story-gen-book-action11 + - story-gen-book-action12 + +- type: dataset + id: book_action_trait + values: + - story-gen-book-action-trait1 + - story-gen-book-action-trait2 + - story-gen-book-action-trait3 + - story-gen-book-action-trait4 + - story-gen-book-action-trait5 + - story-gen-book-action-trait6 + - story-gen-book-action-trait7 + - story-gen-book-action-trait8 + - story-gen-book-action-trait9 + - story-gen-book-action-trait10 + - story-gen-book-action-trait11 + - story-gen-book-action-trait12 + - story-gen-book-action-trait13 + +- type: dataset + id: book_location + values: + - story-gen-book-location1 + - story-gen-book-location2 + - story-gen-book-location3 + - story-gen-book-location4 + - story-gen-book-location5 + - story-gen-book-location6 + - story-gen-book-location7 + - story-gen-book-location8 + - story-gen-book-location9 + - story-gen-book-location10 + - story-gen-book-location11 + - story-gen-book-location12 + - story-gen-book-location13 + - story-gen-book-location14 + - story-gen-book-location15 + - story-gen-book-location16 + - story-gen-book-location17 + - story-gen-book-location18 + - story-gen-book-location19 + - story-gen-book-location20 + - story-gen-book-location21 + - story-gen-book-location22 + - story-gen-book-location23 + - story-gen-book-location24 + - story-gen-book-location25 + - story-gen-book-location26 + - story-gen-book-location27 + - story-gen-book-location28 + - story-gen-book-location29 + - story-gen-book-location30 + - story-gen-book-location31 + - story-gen-book-location32 + - story-gen-book-location33 + - story-gen-book-location34 + +- type: dataset + id: book_story_element + values: + - story-gen-book-element1 + - story-gen-book-element2 + - story-gen-book-element3 + - story-gen-book-element4 + - story-gen-book-element5 + - story-gen-book-element6 + - story-gen-book-element7 + - story-gen-book-element8 + - story-gen-book-element9 + +- type: dataset + id: book_story_element_trait + values: + - story-gen-book-element-trait1 + - story-gen-book-element-trait2 + - story-gen-book-element-trait3 + - story-gen-book-element-trait4 + - story-gen-book-element-trait5 + - story-gen-book-element-trait6 + - story-gen-book-element-trait7 + - story-gen-book-element-trait8 + - story-gen-book-element-trait9 + - story-gen-book-element-trait10 + - story-gen-book-element-trait11 + - story-gen-book-element-trait12 + - story-gen-book-element-trait13 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml b/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml index ae454e43a2e..e523bbe16ec 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml @@ -13,7 +13,7 @@ programName: notekeeper-program-name icon: sprite: Objects/Misc/books.rsi - state: book6 + state: book_icon - type: NotekeeperCartridge - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Magic/books.yml b/Resources/Prototypes/Entities/Objects/Magic/books.yml index 89acd9e7dab..dfb875f6771 100644 --- a/Resources/Prototypes/Entities/Objects/Magic/books.yml +++ b/Resources/Prototypes/Entities/Objects/Magic/books.yml @@ -7,7 +7,13 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book_demonomicon + - state: paper_blood + - state: cover_strong + color: "#645a5a" + - state: decor_wingette_flat + color: "#4d0303" + - state: icon_pentagramm + color: "#f7e19f" - type: Spellbook - type: Tag tags: @@ -28,9 +34,19 @@ parent: BaseSpellbook components: - type: Sprite - sprite: Objects/Magic/spellbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: bookforcewall + - state: paper + - state: cover_strong + color: "#366ed6" + - state: decor_vertical_middle + color: "#95ffff" + - state: decor_wingette_circle + color: "#95ffff" + - state: icon_magic_forcewall + shader: unshaded + - state: detail_rivets + color: gold - type: Spellbook spells: ActionForceWall: -1 @@ -41,9 +57,17 @@ parent: BaseSpellbook components: - type: Sprite - sprite: Objects/Magic/spellbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: spellbook + - state: paper + - state: cover_old + color: "#657e9c" + - state: icon_text3 + - state: decor_wingette_circle + color: gold + - state: icon_magic + - state: detail_rivets + color: gold - type: Spellbook spells: ActionBlink: -1 @@ -53,13 +77,23 @@ name: smite spellbook parent: BaseSpellbook components: - - type: Sprite - sprite: Objects/Magic/spellbooks.rsi - layers: - - state: spellbook - - type: Spellbook - spells: - ActionSmite: -1 + - type: Sprite + sprite: Objects/Misc/books.rsi + layers: + - state: paper + - state: cover_old + color: "#c42b40" + - state: decor_wingette_circle + color: gold + - state: icon_magic + - state: detail_rivets + color: gold + - state: detail_bookmark + color: red + - state: overlay_blood + - type: Spellbook + spells: + ActionSmite: -1 - type: entity id: KnockSpellbook @@ -67,9 +101,18 @@ parent: BaseSpellbook components: - type: Sprite - sprite: Objects/Magic/spellbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: bookknock + - state: paper + - state: cover_strong + color: "#117045" + - state: decor_wingette_circle + color: gold + - state: icon_magic_knock + - state: detail_rivets + color: gold + - state: detail_bookmark + color: "#98c495" - type: Spellbook spells: ActionKnock: -1 @@ -79,13 +122,24 @@ name: fireball spellbook parent: BaseSpellbook components: - - type: Sprite - sprite: Objects/Magic/spellbooks.rsi - layers: - - state: bookfireball - - type: Spellbook - spells: - ActionFireball: -1 + - type: Sprite + sprite: Objects/Misc/books.rsi + layers: + - state: paper + - state: cover_old + color: "#ba5a14" + - state: decor_wingette_circle + color: gold + - state: detail_rivets + color: gold + - state: detail_bookmark + color: "#e89b3c" + - state: overlay_blood + - state: icon_magic_fireball + shader: unshaded + - type: Spellbook + spells: + ActionFireball: -1 - type: entity id: ScrollRunes diff --git a/Resources/Prototypes/Entities/Objects/Misc/books.yml b/Resources/Prototypes/Entities/Objects/Misc/books.yml index ab6beb70af1..78f9edc9c5e 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/books.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/books.yml @@ -7,8 +7,17 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book0 - map: [ "enum.DamageStateVisualLayers.Base" ] + - state: paper + - state: cover_base + color: "#332d27" + map: [ "cover" ] + - state: decor_wingette + color: "#453f3a" + map: [ "decor" ] + - state: icon_text + map: [ "icon" ] + - state: overlay_null + map: [ "overlay" ] - type: Paper contentSize: 12000 - type: ActivatableUI @@ -35,7 +44,15 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book0 # placeholder(?). if only we have a better sprite that fits this. + - state: paper + - state: cover_base + color: "#0a2a6b" + - state: decor_wingette + color: "#082561" + - state: icon_text + color: gold + - state: icon_planet + color: "#42b6f5" - type: Tag tags: - Book @@ -53,7 +70,15 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book7 + - state: paper + - state: cover_base + color: black + - state: decor_wingette + color: "#bbbbbb" + - state: icon_glow + color: red + - state: icon_corner + color: red - type: Tag tags: - Book @@ -71,7 +96,12 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book_bar + - state: paper + - state: cover_base + color: "#004848" + - state: decor_wingette + color: "#006666" + - state: icon_bar - type: Tag tags: - Book @@ -89,7 +119,11 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book_cooking + - state: paper + - state: cover_base + color: "#e22541" + - state: decor_wingette + - state: icon_apple - type: Tag tags: - Book @@ -107,7 +141,14 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book_hydroponics_pod_people + - state: paper + - state: cover_base + color: "#0e5a24" + - state: decor_wingette + color: "#2fa151" + - state: icon_cabbage + - state: icon_corner + color: gold - type: Tag tags: - Book @@ -125,7 +166,14 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book_engineering + - state: paper + - state: cover_base + color: "#6c4718" + - state: decor_wingette + color: "#b5913c" + - state: icon_wrench + - state: icon_corner + color: gold - type: Tag tags: - Book @@ -143,7 +191,12 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book_science + - state: paper + - state: cover_base + color: "#542485" + - state: decor_wingette_circle + color: "#be69f0" + - state: icon_dna - type: Tag tags: - Book @@ -161,7 +214,12 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book_security + - state: paper + - state: cover_base + color: "#ab1515" + - state: decor_wingette + color: "#e05334" + - state: icon_stunbaton - type: Tag tags: - Book @@ -184,7 +242,11 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book0 # no janitorial book sprite so this is a placeholder + - state: paper + - state: cover_base + color: "#550c82" + - state: decor_wingette + - state: icon_bucket - type: Tag tags: - Book @@ -202,7 +264,15 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book0 # no salvage book sprite so this is a placeholder + - state: paper + - state: cover_base + color: "#52320b" + - state: decor_wingette + color: "#e69a3e" + - state: icon_glow + - state: icon_diamond + - state: icon_text + color: "#fcdf74" - type: Tag tags: - Book @@ -220,7 +290,13 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book_medical + - state: paper + - state: cover_base + color: "#cccccc" + - state: decor_wingette + color: "#f7f7f7" + - state: icon_medical + color: "#58abcc" - type: Tag tags: - Book @@ -238,7 +314,15 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book_engineering + - state: paper + - state: cover_old + color: "#6c4718" + - state: decor_wingette + color: "#b5913c" + - state: icon_glow + color: red + - state: icon_wrench + - state: overlay_blood - type: Tag tags: - Book @@ -256,7 +340,12 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book_chemistry + - state: paper + - state: cover_base + color: "#2a7b74" + - state: decor_wingette + color: "#2a7b74" + - state: icon_chemical - type: Tag tags: - Book @@ -268,69 +357,120 @@ - type: entity parent: BookBase id: BookRandom - suffix: random + suffix: random visual + description: Each book is unique! What is hidden in this one? components: + - type: RandomMetadata + nameSegments: + - book_hint_appearance + - book_type - type: RandomSprite available: - - enum.DamageStateVisualLayers.Base: - book0: "" - book1: "" - book2: "" - book3: "" - book4: "" - book5: "" - book6: "" - book7: "" - book8: "" + - cover: + cover_base: Sixteen + cover_old: Sixteen + cover_strong: Sixteen + decor: + decor_wingette: Sixteen + decor_wingette_circle: Sixteen + decor_bottom: Sixteen + decor_middle: Sixteen + decor_spine: Sixteen + decor_diagonal: Sixteen + decor_vertical_middle: Sixteen + icon_corner: Sixteen + icon_mount: "" + icon: + icon_biohazard: Sixteen + icon_borg: "" + icon_banana: "" + icon_glow: Sixteen + icon_hacking: "" + icon_law: Sixteen + icon_magnifier: "" + icon_nuclear: "" + icon_time: Sixteen + icon_aurora: Sixteen + icon_briefcase: "" + icon_eye: "" + icon_letter_N: "" + icon_letter_P: "" + icon_lightning: "" + icon_planet: "" + icon_possum: "" + icon_question: Sixteen + icon_scmmd: "" + icon_stars: Sixteen + icon_stars2: Sixteen + icon_temple: Sixteen + icon_tree: "" + icon_pentagramm: Sixteen + icon_fish: "" + icon_origami: "" + icon_skull: "" + icon_text: "" + icon_text2: "" + icon_text3: "" + overlay: + overlay_blood: "" + overlay_dirt: Sixteen + detail_bookmark: Sixteen + detail_rivets: Sixteen + overlay_null: "" - type: entity - parent: BookBase - id: BookEscalation - name: Robert's Rules of Escalation - description: The book is stained with blood. It seems to have been used more as a weapon than reading material. - components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book2 - - type: MeleeWeapon - wideAnimationRotation: 180 - damage: - types: - Blunt: 6 - - type: Paper - content: book-text-escalation - -- type: entity - parent: BookBase - id: BookEscalationSecurity - name: "Robert's Rules of Escalation: Security Edition" - description: The book is stained with blood. It seems to have been used more as a weapon than reading material. + parent: BookRandom + id: BookRandomStory + suffix: random visual, random story components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book2 - - type: MeleeWeapon - wideAnimationRotation: 180 - damage: - types: - Blunt: 6 - - type: Paper - content: book-text-escalation-security - + - type: PaperRandomStory + storySegments: + - "This is a " + - book_genre + - " about a " + - book_character_trait + - " " + - book_character + - " and " + - book_character_trait + - " " + - book_character + - ". Due to " + - book_event + - ", they " + - book_action_trait + - " " + - book_action + - " " + - book_character + - " " + - book_location + - ". \n\n" + - book_story_element + - " is " + - book_story_element_trait + - "." + storySeparator: "" + - type: entity parent: BookBase id: BookAtmosDistro name: "Newton's Guide to Atmos: The Distro" description: There are endless illegible notes scribbled in the margins. Most of the text is covered in handwritten question marks. components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book5 - - type: Paper - content: book-text-atmos-distro + - type: Sprite + sprite: Objects/Misc/books.rsi + layers: + - state: paper + - state: cover_base + color: "#366ed6" + - state: decor_wingette + color: "#2739b0" + - state: icon_atmos + - state: icon_corner + color: gold + - type: Paper + content: book-text-atmos-distro - type: entity parent: BookBase @@ -338,12 +478,19 @@ name: "Newton's Guide to Atmos: Waste" description: There are endless illegible notes scribbled in the margins. Most of the text is covered in handwritten question marks. components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book5 - - type: Paper - content: book-text-atmos-waste + - type: Sprite + sprite: Objects/Misc/books.rsi + layers: + - state: paper + - state: cover_base + color: "#db233f" + - state: decor_wingette + color: "#ab0730" + - state: icon_atmos + - state: icon_corner + color: gold + - type: Paper + content: book-text-atmos-waste - type: entity parent: BookBase @@ -351,12 +498,19 @@ name: "Newton's Guide to Atmos: Air Alarms" description: There are endless illegible notes scribbled in the margins. Most of the text is covered in handwritten question marks. components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book5 - - type: Paper - content: book-text-atmos-alarms + - type: Sprite + sprite: Objects/Misc/books.rsi + layers: + - state: paper + - state: cover_base + color: "#bfb328" + - state: decor_wingette + color: "#9c7c14" + - state: icon_atmos + - state: icon_corner + color: gold + - type: Paper + content: book-text-atmos-alarms - type: entity parent: BookBase @@ -364,9 +518,16 @@ name: "Newton's Guide to Atmos: Vents and More" description: There are endless illegible notes scribbled in the margins. Most of the text is covered in handwritten question marks. components: - - type: Sprite - sprite: Objects/Misc/books.rsi - layers: - - state: book5 - - type: Paper - content: book-text-atmos-vents + - type: Sprite + sprite: Objects/Misc/books.rsi + layers: + - state: paper + - state: cover_base + color: "#3ec78e" + - state: decor_wingette + color: "#28a15a" + - state: icon_atmos + - state: icon_corner + color: gold + - type: Paper + content: book-text-atmos-vents \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Misc/authorbooks.yml b/Resources/Prototypes/Entities/Objects/Misc/books_author.yml similarity index 68% rename from Resources/Prototypes/Entities/Objects/Misc/authorbooks.yml rename to Resources/Prototypes/Entities/Objects/Misc/books_author.yml index 70d984240ea..9adb14f4ebe 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/authorbooks.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/books_author.yml @@ -5,9 +5,19 @@ description: The book is an old, leather-bound tome with intricate engravings on the cover. The pages are yellowed and fragile with age, with the ink of the text faded in some places. It appears to have been well-read and well-loved, with dog-eared pages and marginalia scrawled in the margins. Despite its aged appearance, the book still exudes a sense of mystical power and wonder, hinting at the secrets and knowledge contained within its pages. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_narsie_legend + - state: paper + - state: cover_old + color: "#a6161d" + - state: decor_bottom + color: "#6e1022" + - state: decor_wingette + color: "#4a101b" + - state: icon_pentagramm + color: "#911129" + - state: detail_bookmark + color: red - type: Paper content: book-text-narsielegend @@ -18,9 +28,18 @@ description: A book exploring the different philosophical perspectives on truth and lying has a worn cover, with creases and marks indicating frequent use and thoughtful contemplation. The spine shows signs of wear from being pulled off the shelf again and again. The pages themselves are filled with underlines, notes in the margins, and highlighted passages as readers grapple with the nuances and complexities of the topic. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_truth + - state: paper + - state: cover_strong + color: "#c9752a" + - state: decor_diagonal + color: "#cf792d" + - state: decor_wingette_circle + color: gold + - state: icon_question + - state: detail_bookmark + color: "#cf792d" - type: Paper content: book-text-truth @@ -31,9 +50,15 @@ description: The book is a well-preserved hardcover with a simple, elegant design on the cover, depicting the image of a world in motion. The pages are crisp and clean, with no signs of wear or tear, suggesting that it has been well-cared for and valued by its previous owner. The text is printed in a clear, legible font, and the chapters are organized in a logical and easy-to-follow manner, making it accessible to readers of all levels of expertise. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_world + - state: paper + - state: cover_base + color: "#215e9e" + - state: icon_planet + - state: icon_text + - state: detail_bookmark + color: "#2dab24" - type: Paper content: book-text-world @@ -44,9 +69,16 @@ description: The book is a small paperback in good condition, with an illustration of Ian the corgi and the colony of penguins on the cover. The title, "Ian and Robert's Antarctic Adventure", is written in bold white letters against a blue background. The back cover features a brief summary of the story, highlighting the themes of humility, resilience, and the beauty of nature. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_ian_antarctica + - state: paper + - state: cover_base + color: "#5779c9" + - state: icon_stars2 + - state: decor_bottom + - state: icon_ian + - state: detail_bookmark + color: "#ab5e24" - type: Paper content: book-text-ian-antarctica @@ -57,9 +89,18 @@ description: The book looks new, with a glossy cover featuring Chuckles the clown and Snuggles the sloth floating in space with a backdrop of stars and planets. Chuckles is dressed in his banana costume and Snuggles is sleeping on a hammock made of space ropes. The title "The Sloth and the Clown - Space Station Shenanigans" is written in bold and colorful letters. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_scsss + - state: paper + - state: cover_base + color: "#e838b9" + - state: decor_wingette + color: "#ab22ba" + - state: decor_spine + color: "#f7cc2f" + - state: icon_banana + - state: detail_bookmark + color: "#f7cc2f" - type: Paper content: book-text-sloth-clown-sss @@ -70,9 +111,18 @@ description: The book is in excellent condition, with crisp pages and a bright cover. The cover of the book features Chuckles and Snuggles, surrounded by the different species they encountered during their adventures in space. In the background, the Zorgs can be seen peeking out from behind a spaceship. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_scpz + - state: paper + - state: cover_base + color: "#f7cc2f" + - state: decor_wingette + color: "#ab22ba" + - state: decor_spine + color: "#e838b9" + - state: icon_banana + - state: detail_bookmark + color: "#ab22ba" - type: Paper content: book-text-sloth-clown-pz @@ -83,9 +133,18 @@ description: The book looks new and vibrant, with an image of Chuckles and Snuggles standing in front of the changing maze on the cover. The title "The Sloth and the Clown - Maze Maze Danger" is written in bold, colorful letters that pop against a background of space and stars. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_scmmd + - state: paper + - state: cover_base + color: "#e838b9" + - state: decor_wingette + color: "#ab22ba" + - state: decor_spine + color: "#f7cc2f" + - state: icon_scmmd + - state: detail_bookmark + color: "#ab22ba" - type: Paper content: book-text-sloth-clown-mmd @@ -96,9 +155,21 @@ description: The cover of the book is an electrifying image of lightning striking the ground, with a silhouette of a person standing in the midst of it. The title is written in bold letters in white against a black background, conveying the power and intensity of the experience. The subtitle is written in smaller letters below the title, providing a hint of the philosophical and spiritual themes explored within. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_struck + - state: paper + - state: cover_strong + color: "#2c384d" + - state: decor_wingette + color: "#3a4a66" + - state: decor_wingette_circle + color: "#6d7c9c" + - state: icon_glow + color: "#f7bb2f" + - state: icon_lightning + color: "#f7bb2f" + - state: detail_bookmark + color: "#f7bb2f" - type: Paper content: book-text-struck @@ -109,9 +180,20 @@ description: The book is new, with a bright and vibrant cover featuring a plant stretching its leaves towards the sun. The title, "Reaching for the Sun - A Plant's Quest for Life," is written in bold, green letters, with an image of the sun rising behind the plant. The cover evokes a sense of growth, energy, and the beauty of nature. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_sun + - state: paper + - state: cover_base + color: "#255c8a" + - state: decor_bottom + color: "#2d2e12" + - state: icon_glow + color: "#f7bb2f" + - state: icon_cabbage + - state: detail_bookmark + color: "#f7bb2f" + - state: detail_rivets + color: "#f7bb2f" - type: Paper content: book-text-sun @@ -122,9 +204,15 @@ description: The book is in good condition, with a hardcover and a dark green forest background. In the center of the cover, there is a sad looking possum sitting on a branch, with a distant and lonely expression on its face. The title, "Fallen Ambitions - The Tragic Tale of Morty the Possum," is written in bold, gold letters above the possum. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_possum + - state: paper + - state: cover_old + color: "#a3bfae" + - state: icon_possum + - state: detail_bookmark + color: "#2b593d" + - state: overlay_blood - type: Paper content: book-text-possum @@ -135,9 +223,14 @@ description: The book is in new condition, with a vibrant and whimsical cover that features a charming illustration of a tiny possum peeking out from behind a coffee cup, with a colorful and bustling cafe scene in the background. The title "The Cafe Possum" is written in bold, playful lettering, and the author's name is printed in a smaller font below it. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_cafe + - state: paper + - state: cover_strong + color: "#e8ab6d" + - state: icon_possum + - state: detail_bookmark + color: "#2b593d" - type: Paper content: book-text-cafe @@ -150,7 +243,15 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book4 + - state: paper + - state: cover_strong + color: "#abebed" + - state: decor_wingette + color: "#a081cc" + - state: icon_atmos + - state: icon_magic + - state: detail_bookmark + color: "#7396f5" - type: Paper content: book-text-feather @@ -161,9 +262,16 @@ description: The book is a new condition with a colorful cover, depicting Ian the corgi and Renault the fox on a journey through the forest, with the lost wolf pup to their feet. The title "The Adventures of Ian and Renault - Finding the Lost Wolf Pup" is prominently displayed at the top, with the author's name below. The cover has a whimsical and adventurous feel to it, attracting readers of all ages. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_ian_wolfpup + - state: paper + - state: cover_base + color: "#3a9e57" + - state: decor_bottom + color: "#1c6330" + - state: icon_ian + - state: detail_bookmark + color: "#ab5e24" - type: Paper content: book-text-ian-wolfpup @@ -174,9 +282,16 @@ description: The book appears to be new, with crisp pages and an unblemished cover. The cover features a colorful illustration of Ian and Renault, surrounded by various animals they encountered on the ranch, including horses, cows, and chickens. The title, "The Adventures of Ian and Renault - Ranch Expedition," is written in bold letters above the image, with the subtitle, "Helping Animals in Need," written below. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_ian_ranch + - state: paper + - state: cover_base + color: "#ccab64" + - state: decor_bottom + color: "#946e38" + - state: icon_ian + - state: detail_bookmark + color: "#ab5e24" - type: Paper content: book-text-ian-ranch @@ -187,9 +302,16 @@ description: The book is new and in excellent condition. The cover shows Ian and Renault running and playing on the beach, with the blue ocean and golden sand in the background. The title is written in bold, playful letters, and the subtitle reads "An Ocean Adventure." components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_ian_ocean + - state: paper + - state: cover_base + color: "#567cd6" + - state: decor_bottom + color: "#272782" + - state: icon_ian + - state: detail_bookmark + color: "#ab5e24" - type: Paper content: book-text-ian-ocean @@ -200,9 +322,15 @@ description: The book is in new condition. The cover is a stunning mountain landscape with Ian and Renault in the foreground, looking out over the vista of the surrounding peaks and valleys. The title is written in bold, block letters at the top, with the subtitle, "A Mountain Expedition," written underneath. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_ian_mountain + - state: paper + - state: cover_base + color: "#86b4d9" + - state: icon_mount + - state: icon_ian + - state: detail_bookmark + color: "#ab5e24" - type: Paper content: book-text-ian-mountain @@ -213,9 +341,16 @@ description: The book is in new condition, with crisp pages and a glossy cover. The cover features a colorful illustration of Ian and Renault exploring the city, with tall buildings and bustling streets in the background. Ian is leading the way, with his tail wagging excitedly, while Renault follows close behind, her ears perked up and her eyes wide with wonder. The title, "The Adventures of Ian and Renault," is written in bold, playful letters, with the subtitle, "Exploring the City," written below in smaller font. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_ian_city + - state: paper + - state: cover_base + color: "#9a9b9c" + - state: decor_bottom + color: "#5f6061" + - state: icon_ian + - state: detail_bookmark + color: "#ab5e24" - type: Paper content: book-text-ian-city @@ -226,9 +361,16 @@ description: The book looks new and adventurous, with a picture of Ian and Renault standing in front of an icy landscape with snowflakes falling all around them. The title, "The Adventures of Ian and Renault," is written in bold letters at the top, with a subtitle that reads, "An Arctic Journey of Courage and Friendship." components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_ian_arctic + - state: paper + - state: cover_base + color: "#5779c9" + - state: icon_stars2 + - state: decor_bottom + - state: icon_ian + - state: detail_bookmark + color: "#ab5e24" - type: Paper content: book-text-ian-arctic @@ -239,9 +381,16 @@ description: The book is in new condition and would have a colorful cover depicting Ian and Renault against a desert backdrop. The cover would feature images of various animals and plants that the two encountered on their adventure, such as a rattlesnake, coyotes, sand dunes, and an oasis. The title, "The Adventures of Ian and Renault" is prominently displayed on the cover in bold letters, while the subtitle "Exploring the Mysterious Desert" is written in smaller letters underneath. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_ian_desert + - state: paper + - state: cover_base + color: "#507bad" + - state: decor_bottom + color: "#bd9a55" + - state: icon_ian + - state: detail_bookmark + color: "#ab5e24" - type: Paper content: book-text-ian-desert @@ -252,9 +401,15 @@ description: The book is a gently used philosophy text, with a cover that features a close-up of a person's mouth, with the word "names" written on their lips. The title is "The Power of Names - A Philosophical Exploration," and the author's name is prominently displayed underneath. The overall design is simple and elegant, with the focus on the text rather than any flashy graphics or images. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_names + - state: paper + - state: cover_base + color: "#8c8c8c" + - state: decor_wingette_circle + - state: icon_letter_N + - state: detail_bookmark + - state: overlay_dirt - type: Paper content: book-text-names @@ -265,9 +420,19 @@ description: The book is in good condition, with a slightly faded cover due to exposure to sunlight. The cover of the book depicts a panoramic view of the Earth from space, with a bright blue ocean and green landmasses. In the foreground, a lone astronaut is seen sitting in front of a window, gazing wistfully at the Earth. The title of the book, "Earthly Longing," is written in bold white letters against a black background at the top of the cover. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_earth + - state: paper + - state: cover_strong + color: "#0f204f" + - state: decor_wingette_circle + color: "#2c5491" + - state: decor_vertical_middle + color: "#3c6ab0" + - state: icon_planet + - state: icon_text3 + - state: detail_bookmark + color: "#2c5491" - type: Paper content: book-text-earth @@ -278,9 +443,21 @@ description: The book is in excellent condition, with a shiny cover depicting a spaceship hovering above a planet, perhaps with the Earth in the background. The title "Journey Beyond - The Starship Aurora Mission" is written in bold, silver letters. The cover also features a quote from a review, "A breathtaking tale of human achievement and exploration" to entice potential readers. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_aurora + - state: paper + - state: cover_base + color: "#0f204f" + - state: decor_vertical_middle + color: "#3c6ab0" + - state: decor_spine + color: "#3c6ab0" + - state: icon_stars2 + - state: icon_aurora + - state: detail_bookmark + color: "#2c5491" + - state: detail_rivets + color: "#799dd4" - type: Paper content: book-text-aurora @@ -291,9 +468,18 @@ description: The book appears new with crisp pages and an uncreased spine. The cover features an image of a temple with a glowing, multicolored aura around it, symbolizing the various gods discussed in the book. The title is displayed prominently in gold lettering, with the author's name and a brief summary of the book written in smaller text below. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_temple + - state: paper + - state: cover_base + color: "#542e80" + - state: decor_vertical_middle + color: "#39205e" + - state: decor_wingette_circle + color: "#914fb8" + - state: icon_temple + - state: detail_bookmark + color: "#bfbfbf" - type: Paper content: book-text-temple @@ -304,9 +490,18 @@ description: The book is in good condition, with a slightly worn cover that features a dark and ominous space station looming in the background. The title "Watched" is written in bold letters that seem to be staring back at the reader, conveying the feeling of being constantly observed. The blurb on the back cover hints at a thrilling and suspenseful tale of paranoia and danger in a confined setting. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_watched + - state: paper + - state: cover_base + color: "#611e10" + - state: overlay_dirt + color: "#4f1206" + - state: decor_wingette_circle + color: "#241e1d" + - state: icon_eye + - state: detail_bookmark + color: "#bfbfbf" - type: Paper content: book-text-watched @@ -317,9 +512,17 @@ description: The cover features Smith, the medical officer, in his uniform, looking determined and ready to face any challenge. The backdrop shows the SS Horizon under attack, with explosions and smoke filling the space station. In the foreground, a wizard with a staff can be seen, adding an element of mystery and intrigue to the scene. The title is prominently displayed in bold letters, with the author's name and a tagline indicating the book's action-packed and suspenseful nature. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_medical + - state: paper + - state: cover_base + color: "#d7dadb" + - state: decor_wingette_circle + color: "#55b0d4" + - state: icon_medical_cross + color: "#55b0d4" + - state: detail_bookmark + color: "#55b0d4" - type: Paper content: book-text-medical-officer @@ -330,9 +533,16 @@ description: The book looks old and worn, with faded lettering on the cover. The cover depicts a dark and eerie morgue, with a full moon casting an ominous glow over the scene. In the foreground are Morty the possum and Morticia the raccoon, with mischievous expressions on their faces, peeking out from behind a metal shelf. The title is written in bold, spooky letters, with the subtitle "A Tale of Animal Spirits" written in smaller font below. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_morgue + - state: paper + - state: cover_old + color: "#d7dadb" + - state: icon_text2 + color: "#61363d" + - state: overlay_dirt + - state: detail_bookmark + color: "#61363d" - type: Paper content: book-text-morgue @@ -343,9 +553,21 @@ description: The book is in new condition, with vibrant colors and illustrations on the cover. The cover shows Rufus on his bicycle, with Blossom flying beside him in a playful manner. The title is written in bold, whimsical font, with the characters' names highlighted in a contrasting color. The overall aesthetic is charming and inviting, appealing to children and adults alike. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_rufus + - state: paper + - state: cover_base + color: "#6597c9" + - state: decor_bottom + color: "#113a63" + - state: decor_diagonal + color: "#113a63" + - state: icon_text3 + color: "#ffde7d" + - state: detail_bookmark + color: "#61363d" + - state: detail_rivets + color: "#ffde7d" - type: Paper content: book-text-rufus @@ -356,9 +578,19 @@ description: The book is in a good condition, with a glossy cover depicting a jungle scene with vibrant colors and intricate details. The title "The Map of Adventure," is written in bold, gold lettering. The cover also features an image of a mysterious suitcase with the map spilling out of it. components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_map + - state: paper + - state: cover_base + color: "#56c463" + - state: icon_briefcase + - state: decor_wingette + color: "#298033" + - state: icon_briefcase + - state: detail_bookmark + color: "#61363d" + - state: detail_rivets + color: "#ffde7d" - type: Paper content: book-text-map @@ -369,9 +601,17 @@ description: The book is in excellent condition, with crisp pages and a glossy cover. The cover features a striking image of a mountain range, with a silhouette of a climber with a guitar on their back in the foreground. The title is bold and eye-catching, with the subtitle "A Journey of Music, Mountains, and Self-Discovery." components: - type: Sprite - sprite: Objects/Misc/authorbooks.rsi + sprite: Objects/Misc/books.rsi layers: - - state: book_journ_mount + - state: paper + - state: cover_base + color: "#9bc1c9" + - state: icon_briefcase + - state: icon_mount + - state: detail_bookmark + color: "#61363d" + - state: detail_rivets + color: "#ffde7d" - type: Paper content: book-text-journ-mount @@ -384,7 +624,16 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book6 + - state: paper + - state: cover_base + color: "#1d662c" + - state: decor_spine + color: "#2a8c58" + - state: icon_glow + color: "#b9edc4" + - state: icon_tree + - state: detail_bookmark + color: "#61363d" - type: Paper content: book-text-inspiration @@ -397,7 +646,14 @@ - type: Sprite sprite: Objects/Misc/books.rsi layers: - - state: book0 + - state: paper + - state: cover_old + color: "#c526de" + - state: decor_wingette + - state: icon_bucket + - state: detail_bookmark + color: "#61363d" + - state: overlay_dirt - type: Paper content: book-text-janitor diff --git a/Resources/Prototypes/Entities/Stations/base.yml b/Resources/Prototypes/Entities/Stations/base.yml index e71b3ce46d5..c3fbb998b28 100644 --- a/Resources/Prototypes/Entities/Stations/base.yml +++ b/Resources/Prototypes/Entities/Stations/base.yml @@ -120,4 +120,4 @@ id: BaseStationAllEventsEligible abstract: true components: - - type: StationEventEligible # For when someone makes this more granular in the future. + - type: StationEventEligible # For when someone makes this more granular in the future. \ No newline at end of file diff --git a/Resources/Textures/Objects/Magic/spellbooks.rsi/bookfireball.png b/Resources/Textures/Objects/Magic/spellbooks.rsi/bookfireball.png deleted file mode 100644 index ed18010a728992f4539425de5df522f572bb96cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 652 zcmV;70(1R|P)Yr6%<4e@y`*&19%Vd z3WDeXDhPruyn*7Ti!KB~bRib;&qA@NOf_u?)0mmOH?x@cZA#K6zxTdOrpdflMBoww z5V*tu1THb)zaJb;PkirH#;i_$d{HkTr?~m421M|k-4zUdzMwK$?jBg0ZGR4oOlgl@ zTiixox!|W7px|50Cp0z>VWQQ`15n5XKhqp=K3>Xv!i9%7P>YkFc@k`0H?{kh%Y!(1 ze*Zn$Oa)x(#gEH|pX*;hB|o<@0@TnkLFwc57CvRd&t*V6=|u9}ijO!785@ucz;*Jv z5tm0s2E+qUa+!RG_m@1?NH8)W3P6eaN|?kzITEbq00r+`s$x!U)co8ARD5FtBKX(o zV<(7{eRl>>^NkI-s=cse+vy)A zSr2rlu=DvWNd*c9(=j))53%)FiBXEfU2wY+S0+$$oz$FGCa7lka7P#~WNPmFz z2e8%Wee-iyhjrrW574o7``G#edKD$9YxY^$6h%_2!bmdtD2OxgNynyG9=9uupnKKh zK@_XQ;FNCEc`1w}ho0*if!F>3>iWEIzVru}9w7Yz(jOrG0n#5J{Q;2v0Oyka04Bcl m2c&%|{Q&_;3_#!t-hdyeSrmMJI}Nk|0000A7D0p{dIGiwHo~5d?&#IkmiN$By@c|L;qm!&x_rowYIZoNc-;i%%Krv zLjYkuJe7vEB9K&EzcPN-C02~D=&g{tm7y|b>vibyEQ!)VcAOCRo6c2zn4cVLr z-{z9l^Watj4AcrHt z^T(6AeiXu4eL$M$Q!DCTSlQJ=I03*C#u&s_#sGNs(j2$8QLw8IAakx-e=BZF0QdEQ zND^{n3?MRGWV&tzK;QJ=?*nnH05SlMf)Zo)WB?3-`y5$)0O=u<5X=vX$A(S zbWaz@kcv5PXPxyr>>$#be?Xq4-ogBl{0p}le)C#}yE_8zaK!!+dc@5UB)}s6Xj`{V zjgWcXtS&u~Ka7Fg8p7pS(K-iw|6NmoS)So z$S-blYmbdO-`d68n;Ki{mKi6!(fl3w(ek$9?HyN+E51m&*neGYDyO^sbjI!BTi+!= zeqNPZ-MM7r%JXIiI;!Iye=n6_Tcmd5+HuJP^DiwZ+PE4ho@0KWf5kOj=lj#`nR8w* zW{J_cRk?ei(Zd&;c`a`CZ=0`ptBvvM`6s6iY_s!HIImLtV@juQ?KZ|qw_}~;RUJ;9 zOkwzL|1J6V%$!F-_E#B-&DGBT-SPcmruKJc)i1%k8T0Qw|0O7WHW}z%yK@Xdd^`$I z4DTIL z@4K%b{jy-LIJ|l)ufYZ$k8jVCRg9VRz63JqiLK4CQ@A<1f8rUA9T(g=3)KHi&vE#B z=3IHAa=?nK~_iwzoSU*@k-QnB9tEbr{jHC@#n9D0q6f9WUFI2EGL-jLb zbJb@&`)|t`^CI2s!TPtVQzb#l6rL)`Pxz+z320M@yoAH4!2AQ| Zt4g;>*qvB)0hkUMJYD@<);T3K0RV#^GT;CJ diff --git a/Resources/Textures/Objects/Magic/spellbooks.rsi/meta.json b/Resources/Textures/Objects/Magic/spellbooks.rsi/meta.json deleted file mode 100644 index f9f1703f24c..00000000000 --- a/Resources/Textures/Objects/Magic/spellbooks.rsi/meta.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/Citadel-Station-13/Citadel-Station-13/commit/f3e328af032f0ba0234b866c24ccb0003e1a4993", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "bookfireball", - "delays": [ - [ - 0.1, - 0.1, - 0.1, - 0.1 - ] - ] - }, - { - "name": "bookforcewall", - "delays": [ - [ - 0.1, - 0.1 - ] - ] - }, - { - "name": "bookknock", - "delays": [ - [ - 0.1, - 0.1, - 0.1, - 0.1, - 0.1, - 0.1 - ] - ] - }, - { - "name": "spellbook" - } - ] -} \ No newline at end of file diff --git a/Resources/Textures/Objects/Magic/spellbooks.rsi/spellbook.png b/Resources/Textures/Objects/Magic/spellbooks.rsi/spellbook.png deleted file mode 100644 index d24f198f949f09bd26e513d9a8df051437a5045c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 743 zcmV?P)-Ep z4)8je6vhI$9gFGonaSVB-rg1s-z|VFM+W@r3}Uf+xOwLT0Sq1A(F*v3LA-r=7fL9E zU^t9sclOn4r(a|+S(rb`Pq6@v^r$A=RcVEIVMi_ARg=U+uY6={YT4dh|VUF$?W3F z)#msBf%y<5pBMTNao)}$lt2I)+a4YEW((l)GI2q$6yMmWV0BgLgJ`zfaB8*yIt?a> zeeEP}1wqE|ogfUofzAM|jElcoPvBuM^TU7j2gT9y3Vltz`LCt+ic|M z41CJvglOcV?qg4}6P6h*0a*0Y>NV6VEF90w;PcmK_Nfpf9KtV$c}D||YX8Uof+@fx Z?Khte9agC)|40A;002ovPDHLkV1kOAT(|%L diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_aurora.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_aurora.png deleted file mode 100644 index 0e34258138021492fc684b7fda2203714d8d63f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 358 zcmV-s0h#`ZP)3FcgOWT0<9yPBjV^C=YUem=@yeF<@pIp?ZOlAl2Ve*t+nJ@G5`QX zQPf#eRaKuMr{4uZ0BM>|2jI;^A%YS}k|fBS4e&ebkQCSn_}Pc1Fy1a89^Gt!EX%&0 zT(JO*F|FUD0@N2=efHdi_@BGL81sp}{>~A(9ZM<6&B$7d{C0#|?}uy0esHp$cTyP{ zpihr~16+pz&{~6YAAFn(0BDULAgqD0_1_ng8DI##08cz*&hj?w^#A|>07*qoM6N<$ Ef@9H>(EtDd diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_cafe.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_cafe.png deleted file mode 100644 index 7a3b6667cf86601618c0f5aeadd4ec999245269e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 517 zcmV+g0{Z=lP)#5u)V)i)9kZWHtj%jftCOu)Db+q^cs9NFaSW8T6?D8i8u-ZOc{`I94@b}h{s|? zbsd1DDHbXXPzj31VgT57ipTR2`E*f$&x-0gww+@4Xc&MdOGX`0`M zdtT!K&h8B+?hfeg(g1iaxL_EVrpd&=A#iV_bRVv(){C%2q2M2 zRMnPciCR=i_#w0`3)gjVUAOLVVSve@BmkPGp=nxGPgYVJQ6uhL3ZUS@Xn*oc?^~6g z8VexujR){wt{VdoR?DX%QQg`_7T26#&w~I;>({IW&}@J=_3nqkbOKgz00000NkvXX Hu0mjf%LU%y diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_earth.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_earth.png deleted file mode 100644 index 8e29c3880a48af951ae316342b6aac1dd0675db1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 498 zcmVd^$mMcDv)Sw`LA%{H1_J0LZ{O}fW&tuY(E!W(b*vD}Jpqkg zJ|Kh$tS(7@Q=?Hqh~>e*Ku4x&#!l}0e(c!T005~}s%MPnc{)=je+m)@&{~3yY}-ak z`FXFD!gbxh0Vo!W0F1|X#j zlkHVu_5@lV9+I7>RLw`0Wh4Lq%$?UUI^d(XcKM@U~xZe zZ#C_yv;mY-IIRjC+pwC3sagor3UFS}QA&*#pmWrmm1{6X_^@8Uxz9xUbric8nWp(^ z#oc_s^E`atr}21-nBEv(G5Rjob^A7+pZZ@`tX1jWo`wscQ{zK`yWs#>mPJ4RgU?q@ ofUX-5Vw{3eEAdS@21p#>9X*PTtyA@MdjJ3c07*qoM6N<$g6GxNUjP6A diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_antarctica.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_antarctica.png deleted file mode 100644 index 3843e20069633415e2005a2c6955a81d47a3150f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 756 zcmV##khG85lz}VQB>qG#B;CHTn z#Z}V<7uxn3D9WLGr>2%m0+s|Y3M}toA?vP6F(69q{ zY5)K~V-EpnS3T5*2WVG4d-UZ>StZylzWOG7^~c>^l(I9hWm;$xn|pNH#3oy2@xyzk z)SVuHdPxPqt@b<5Zhfq-r7V9!0Q=u4I@!RSzCdXBF?0F?(a8p9EtE_+Cz*M*Ok_QYuFn&1 z9Y1vMQ6kg@K{Ltwni^hf)7*Y&0&Dm%G0000 diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_arctic.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_arctic.png deleted file mode 100644 index ace42ee7dcb82d0de23fdd90465beafe8309c299..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 531 zcmV+u0_^>XP)HT-@Ed2!ddp z1xH86t`2n&LS1yKt95XSI_TgaXf_d%AU3w*;1Y-0*!r)zXzkJ;1nwU1?)ULtj$9Pq z_i3d%TI${gXafLUVUqIsyzjbhB!hH1t^5k0YQ8jmh0-$)y~Fhh{r%sr-P}BD2-pz7 zbzL+~WA9RXi#&bE_&Bd^D1Ly^)!h4855{< z<0f?|v!&cA@H{Y6^CXgbVCrW~5=k91H6Oq=4FKT&WEX%Dy&FGcGNO0a#bqVcf?BDu zLDX2()$fw7z@ztqSi7r>iM2Z(y_f$1(8qKDI`ygGsg27jQVZ<$0k*7tvV{`Mvwhg> z1Ch)jLIpbYDV`V4$refgWD6yp7te!A1PXa1q$_YXw1RErIJk9j90$WNu&f+HtN`1x z+sOeOi47JE0|3j)0btw52r*d!5gSZ26F`W`FUPg9a=9!85a~W(yj9`5xs0UW0mpGZ znO0-}0`M26kpZOoTfY8V0GV$*fd6y>jQ$=#(=?*o2d_#G06*Rb0hIQySsS3$0Iztd VjF`6Y<^uo#002ovPDHLkV1ml)=STnm diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_city.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_city.png deleted file mode 100644 index abd76a54704c2f69dec12e1b6e07a1f60a2705a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 538 zcmV+#0_FXQP)KIJT0>Ikq|n0IpLzm}X%-Jn>%MSR@D(y3y;K zm04157nmjt`2!4&sCLp=B*@^1%8)-`!~Gcmz{A-A06l6Wu}F{}wXq~FE2$QIR9i*K-a?n7{)0=j21w|7Cc`39l*RN$@JS;h|w>f^C*?y-FZNJtHO8pGLn7= zS`Ie=$WAVQiv0^fC6c-axJi}w6qUQJFo4829>99KtPnuE7dPj@tMev+AJ2mTO6}LI c22gE)SBTz%A9MHWg8%>k07*qoM6N<$g6rAiJpcdz diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_desert.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_desert.png deleted file mode 100644 index 9a2d2ad6587d53e4d53475db616226596f18886f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 557 zcmV+|0@D47P)%SK9Wh4fHq?h9SuG*cZ{aV4+Ar2 z?!EuVIrlPWT&8J~PZj0Wy#P=E07~o(T3PsMp2t#-45k-!*Si27%%?^Kt}o5-wJvi& zMa8Rg7iT820%ip`kEQTdl@eIr1NO2m_@JSgd0XRbl>p#dx!bn+W#ZQMSla*qO_ARi zM*9Ayx6g`uLO~#1jl5v1!?P0Xzw4e;64lh;9_SDfO^G^ zClsV!ac9J3B~^l_s;vG_Wwq1UkCM(n!nj4uo@T_v>?sMu{>FP3fYPV{P^@%W*VDMH z9F;(yt6@p|gFl>P{8t(JT#X}haFBswrHj;91b;XQfIplhH5RclVaeu^kj}vW+DUXR zz~7TIqR}X-s-kHDgqQ?YWtWo!_$Pb}t11AR763rkw-KT<4I+Fru<>yKKfGJOAOJ#i zzW6>Km-2Vk52*K9cOzgyE1a*00000NkvXXu0mjf-?{Hb diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_mountain.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_mountain.png deleted file mode 100644 index ee26df9e9ab6c3d7505c4c977216729652a7fc59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 603 zcmV-h0;K(kP))pTn{RG4%a+$&^GTSjal-A z2fz2b-+R9g@ArG$ON3#_Fm+?7*+&2)0N|!fP_bAHUDxd?L8(+q^aapPzV+x23H=`2 zx*HFen7DN9+S-e*fL#Gx*CmxovGoez=zMh7`udVooKy(_G)6!3nY6T#w#_sGBl`Yk7f#<>ea-O-YA(Ccl1?I8_^UHYwH!uKzA8(_^nlZ-7 z)C$ZP<1xRgluFR4$*J2+PW8I_pOjDF*gM18IE?wMjYE#Tv;PBN%o+fU8H-|c`Ue6~ zE|+6-XLFhzdyj1XB|G*W&gOJa$)x})l}Zd^%vj`f<$&`3N#x%@;dJFdoP?0%l~8WN z_jgut>?*bIM>HA@Ow+`+t60`5@LY8}HGtRFHV;h`0Nbtt;5cuvtc3`~+J@i1lmL{P z(6%eWwyXs)F8V$YfVRx@Q@Ci}*|6>Yxk`y{ui|t%9qPJ{rfDRTN#U>8>%z|Ea>Dj~ zpCAbM`r#>Q^8w9fv%mMiW$H}YkCXs>-m`R#fERQ~Y*Ve8ZN0O-0-tJP9U4)Eoj p3vgxqL81uGa2M-UF__hWg;5X-kws*Ul0xwIikQ`)-tO#0)8r2evv1zK z_v5`CW>#}umqsem(CkfsCIHal=b&jCOi#PmcBlkyKCk^3KsEWFACqWp!_eMeJKx#) z?ow@J-Z#8IfNeu8Mr5S`6nx#Qa({gZ9s z;#3FbdQYh25TXK6k)S+xL^@jnAe}8yo;&g?;VIyjP_Dp1-zuh&Vf)giSS+IJI);%! z%2nXIYB)839l61*t^;5g830Uk7bzzyAaVl=haU&9T)t=aX)NW$oAas)bsx~5s_@>s zij=>Bj?GN~N=r+xY@Y&9kEG!N&d!4S-d)rgK;euBaPN=n1Q3f6=013`e*>2F&-);N g*8DYV0yG-nFF_}T3baD4H2?qr07*qoM6N<$f?9U(RR910 diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_ranch.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_ranch.png deleted file mode 100644 index 7f6b3e86ddc47afc7e6c2aa759a214c0f01eda62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 530 zcmV+t0`2{YP)-3t~LS&Pf9EB@Gf>-}i%3(M@j zdGp?fH#5wv@_nCns;jNpI{+O3peM>fDFuu8RN^J@w|(_0fLii@Usq9mBanFau-`DA z9jZQmj0_(QPyrIqwXq)Q8kV2kugfnQpr%1V_CNKz)(-%jl)-hY@8h5D?Zra1H4OML zAc-j41+EKY)3c1G%^+#90HbM>vFX_Wu4w=OmwP_}cyIPnEL7XDT`JKOgw>yVOMfcv z=zEDPaP8ir*UArUdaca0d)pcSbJzr++nfxZuwANHQ{ZF<*s_0-&3i0OC2=ysr5}>G z0^Q~$Hw%Yk^Bw@%yvNPLVNi)cb#4i91rCN*aO@nv&n_sHN?4YKZRgN>1^6ZnCj;2g zo6K7l0Jfb2z;SlbdZG%VH{oizc>rJDEHlp>09sEx*)LVB`+)Vo3eSB@Bwho_A4veb z8Sjy;B>;^`8Xw?X;9EIuFo58U2k`$MH3%R9aqff1`&aNC#rDg7s`G2s0cbbC9p9dH U$$b4yP5=M^07*qoM6N<$g5Zben*aa+ diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_wolfpup.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_ian_wolfpup.png deleted file mode 100644 index df0b2350ae7c580f13d2790fc4cddb46a7c1ef8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 552 zcmV+@0@wYCP)1P>kRtwRZIh$JUy$f6FeRhu@IuIj{# zUl2$~=ic|>TwU&w5(ELG)Ps?_j{(L2z{98nMNu#eBXArikwK%;Q2q;`XZ~q#1?BN` zc=B{GA)UT;?BZfE5-<|LaU4`t<;7+TXhlxgQ?2$UR$e4PPpMQYwz=ndv2$Ys0KA<( z#C5y(?;gDj<3+y$bXVi`k5!o|<#vJV!ou1*^GjN2TJ$T-FKH~St%q=T1^{sJ@c@9V zHc8R1kkuv!;m2~f^$vca6;>it%q<~Zfzvq?+p2Q( z?K|yu8(r72tSUm7z`pEsascne4tZS%z_O|U*!Bm6Snh&|9XL6@JAkvZQ}X!@0EAfn z>w8%x>Id{+Rk-b0M$+GaX_^3(N~J$y_X02s)5HKW$1GQ0hYKKO4g}YqhXbIhDoOgm qFO5r@&E`M*K>%g^Yc>WLHNa06^o``=foRMC0000~53@Tu_&r>L2o5gAt>_>Gry>Xk zg)9X(2L~N>7MzMp9h~|FTm|>YnHmCt5Em1YG@APpn5ns6#hJt?>DWuvZuW0J^=6O3&Q;lzFAzDX>fzmRY_9O99}V zYlUk8BrE5fYjr^xfO8JUSUO%h0L`3#N~v-elmIN#&8^zDZRIXV158H!T>BYga2zL{ zrxFB`1_**+F-${J01xK}DMT62WYqsZfa!PuLP#z+0m%YQ$AkI-Uarn6Jy{?Tzc9e; z<{}+OQSl}euT=&pD!UDYkcGLdq;A6`pcqsdJb$kjAPhrMD*zz=daVb55F)+z0I0fhdEP)&yAqK2Ao}bp9<-AM&-tW8*C+8;-r4&BZ@|qq10sye0 zGl&Qr?kLK!Q-JDU%Ul3;_{VV%S>J}FGyObG8%t&HK-;_?pd3Tergb_5hFW_qb$({# z(*xAn+Ay|q|7>hE2LRYi^`6JVs!8v&AOP5nfiHEl{MWt9Y0tuC<>$YoCaYeRcK?lg`7&`s5-t)<_mWQs5Mbl4@6R( zF9qP@$Zfhc$@@a&9l-7t*z9!W-GTuu_ILpGaan>;O*5D3`Ro#Z=Zx;vjE^5 Z;1j&fN@Z)Wza#(v002ovPDHLkV1hdAqQ3wD diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_medical.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_medical.png deleted file mode 100644 index e94b6789fb54ce34329953ac7d1cc550bc2d9092..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 356 zcmV-q0h|7bP)+=~6M^(#=zN0&gMM*>gB|@D}wL z?hcBJ5U@)rD56swG!l#2{3vnAJEh@Gzb9Y*J;oSps%AC118@KU>N2J@O&O(BD?l8_ zo)G{KJ{fM|wFW@D|7_@V-nLDWWhG!G0HqX!5G*$2UUMor%HKDQM!m}5QUF|G7|L3a zWtp^B8UO%-AgJ`@d9LzH`>Vh@fYK0%8qfBR@_gFW1~AG%!*7~22cVGwvFGJFolIyA zf!K3iK3`t%2R{Me!5V(^ec+<_4FFH`Ura*Xw2RVSh97+aCz6+po z#sdI5t^h>l0E7_e+y~c14j}q?9|Qo;JJP)rOGWKbh;c9De}OE`tI%Su)vIX1ZQR3nXc=AZfXkL=OmaNw5q>ZaB20PX;Q z$&iPZ%jH2S#kMGz1p(Wlu(4SXSl`5r5AfyXWpoh$Q~dLX*NmobVD{L2APoO{_TAf0 zed+rFtkab7WWvX}mCDwqX9BVqW5^DqppueG+m zQp(aWgo0iGW{mUs9IZ84Yg^O>gwOn;DR%%xrz!51yagRzS}BrWq36T%p!{8xU;aUg1WBHl3_1^ZBek>?T}KkTCMDT zAp~&jxUTEQraT^8U*}t3EJ+eP&m+%sJkO&nOOhlZ&-0TQz@hvxC~q%-$EPRis>1jE zW4BesI!#%pDZcOH`#yD5p_Ib+eb#A8T~+-2{tMurzXt(E_rGR$0JjG48{{V{_|L5+ Qh5!Hn07*qoM6N<$f-PPiod5s; diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_names.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_names.png deleted file mode 100644 index f2396ac527932495e2e9bcd6c4ce80f7c84fb142..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DEQvf z#W5tJ_3d?Az9s_^_7C;Fi`l9eY#OXL3xCw{yLYI$f~PNGri6^l+dRcx*EW=PbV+7jwv-Q_!)?F7l})qjO4RTI`mdKI;Vst0OXZ;Z2$lO diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_narsie_legend.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_narsie_legend.png deleted file mode 100644 index fe7659f396f1f37d6f20908ffecb1a4b5a0617bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 570 zcmV-A0>%A_P)`6pHR9J=WS3OR{FckhAC;d-VbwErUpj*$t%8KX>I2AWwV?=NP zHU*paB5DwRvbZ z9n1Neq1RG)!&v0j&G}x-$*n(stjSRTX4A3UL>x~Kw;+QF;9uk)7>!Eby_#HBeZCsN ze9ds?d$_*2Q!h}0gm3o_mu7(`T;l%qaf_*g0B9Tc+W>%o2~eu$`ksS;iS(KQU;r{{ zjs}o$iGT^558Lp&YL0OUj}rJ@HSV<3{7;-9ifzN%g8}Un06a?IQIfmYwqPQa!$yKi zj!YW0BUZ=(eb2Z=-`lUU_wED#MGvSP*_EVaW+j13Ng7$1<031zR>^?Y(adtwc^M$U zt_rMjAoI%w#NMrWOf+e47PO%78~n z4ycV;m!VnT#mLRFti2Ct8UrS_A~zia88fx_frLv8J5K7@bf(98eql&ZOA=}enAmb& zOvXd3l7TQ5=(q~!*LRd^Y$ltzEvL5rtOr1^<-nx`QLOfObF0-(QvpV^P@}N%L#g05~;(U#ZIcWN2>wvj6}907*qo IM6N<$g1MvthyVZp diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_possum.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_possum.png deleted file mode 100644 index b169c180be074fc0c21536cb30af01592de3cde5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 468 zcmV;_0W1EAP) zadvc7dwJ*M;i2#P;&qv2d3o2xCqXF6MpL17EwgLeI0h~dK zRN7(f>O3%+-I9$U`(c^ilmJH6eIvbd!Sx%uuPMB zqd`H_$SVo}h0ADn1PE4OC{)li0Cc@NkvIrez;Ag)LD#Fy-)sXQ%Q666`DHc$mT7WO zDtTkuwgH%ve?Ihjq4EfC!u{K2a&3!u+Y+5R9RPsM<{ri3p*OD8Y5;UP9pPM&mY`m7 z00RKSFd~b?g!e$UY>oIs7!iw90sqk(^IJ0j0C-UEupaNpZ|neg{%))Me*peY=LwnD zzX5)~|2ka&ksc4=eg?PhPyng4LzMSmTmJ}r4+2QZuUP^RH^4V5^>#?6W`!pJ0000< KMNUMnLSTZ0pTqqC diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_rufus.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_rufus.png deleted file mode 100644 index 22e936fb3f4aecdc78311df46a2eca6cb048840b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 410 zcmV;L0cHM)P)ea%tV!#14oU+AJ?^L>CcG0zUU(nAgYOPQ2tk$Fs5HF>r~!b@ zBBrCWOR)%NS^+l~r$#A&IQ;r}iqSfNwv#qk){oa7p4>_Sr2va?M$@wKhW7WF=Zqse z{?gU?zBIfLK&-9yPEji#^Vh=O!T7(5hTVsQl5h3lbfcQ8mAP7 z>j#L-oGpK&+h0wOs+22`2ca7dntdSk!i~qJ`ha#LDifMi<*m4K0HrBVTIuG~^#Z7@ z@c`oe9&n^SDL~V*(b*3^+`RzKulqp&qy9Ck0jdW01oy^^NcURC!T!nTizj4_YB#C7egVC;=$dE$x!eXb&`+@wm#~Q#kW|pPP*TnPV$);W9U;xy;>gt z1^@sTIt3yE=z)P1P6inJpnm~y$LGDxl6C-DUShsDR*tWiQ!C@v0Ab(( z0JfW%0OIeWI2G;I0Az{cRJ1vei#T0GSzgxmTLa*R{)U?OT|le=WWzk{a`;_xz-bGj zI2GPzd8y=skkhGo0B&@>oCvn>b%Pk1dKzs|NG%S$B(Iy(hi=8L0}19i7! zqX69Kb~97LF5ir;0>}v`=K!E4rj3y9zkukDfqd)FG5dM}O;!z5MAD)*!hQ}$dIlw# u50`rIGbsR|)_cHU3)+_6%klxBcYt41t&_w9@AhZ_0000*mzS z2gp#|vOD@L9fd;0K_{0GItZdjk5fVtvG;3$xjE(Ni1T+N+XdTaixzRlTY(2BG zGZV-?_N`5k#M;y;3EQ16?M|1=i(&p;UAw}o3U2RS0EnZowDy5_8OBk_;czD>@oxl1 z=LY~xrb`~5-k406rF~b3>2D}NO#lD@ diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_scsss.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_scsss.png deleted file mode 100644 index 1262f51118ca0817c38037ecafec635fcf15c84e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmV-l0igbgP)(I+h~NK8J|U;bZ7*U&Yz2FW}_v z;3T4>wNNo*oC2-1=GS!TnQwBwTzYyF!Z}Bq>b2_L0dxQW`mzE-2%;$Bj4>mFEX#-$ zfG~d?Rv^O}5KO8CVc58KJYGlvr2vdEP)ZTcchb$uy5ILoby5JKAPAJri=t4jRR#cn zx5CB)4N$-^1<7VZ(v~iHOLED}d%wK@ySu~*!;nqd)<&~u02u(_ zCUH70mBNYdr%LcP8aYb==;XuB70%8f?CzNk#p0jO9_-&N1Y8I(@qG%O$H{dU=q?PX z?fLaKkzXPS0Jh!4uKXyK*f)7pz|G|lLI5D8oO^EDE7r3>N*N_=HOutc6@(B-DQ!d9 z0|+6aHwEzWW&zVcSV^E02qEaTEBJw;-WV7XsC7%A%k2lsfJ!TYRRy#?+@Atat@VvY zTsuI#^j_Yj)htJS;0KC}%NJ_^YYBAeHH06i=vur%8IKdivu?uKrt7sU0Gypa+JuVC zdK$*H)^fz}uN7ckA}{Y!t@YD{0a8eytI)YSGNxkM)i9L=LI~=O0RZ*Jz_@N}sa8UL z)D5|eSplf&)Ev(&yTP_dptn+JzNby!Sk&5c!2k(Pk?{F2LYVy*DM3rG{7%3 Wv%OLRsXy@m0000kK>^ z3}TEW^#^pYTZaX9h#<(DPCtiF` z&-=X3@A-b-_kG^?_q--e(-@|W4OM*vFaiL~{Ulwz_eMLcl}{v~CL<<80B!NFPfD21 zUI&|_GhuS_*tO2!`<{S30S;?r#%&U_Po+PRy{?Sz;ergic5`GEmgEmgcLDFEWZ0HH*VVp#>iyOJWDOb-^I z(X^9UUZC_vC7ev7uBl{}7r3>U!tL=faq)^UfKU%a*0+%JSsXJ?ie;4>cYTDDX=eTK z(tiPWEQ$%Sw?OC)9PDnh@o1iSFn~jHBImQjg8>R9xS#pnnY_P$`T$C06?ZI3WPOWb zS;g(~;eY-L?K8;vEZ(^#k#gby-nk`gM=lO_x2c&GKD^E1ket-7DY!j8JCh`dclT^jgJbL8Eg;K%!=+Sl~vR=6^G>Ph>Mz`fn@A*3&cg0J djR1xX@Ea%`!+9L+@>>7^002ovPDHLkV1jE5CO-fG diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_temple.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_temple.png deleted file mode 100644 index 6f68c1c2e239a5cfe97e069332d127c90a9fc33f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 553 zcmV+^0@nSBP)Uqc(uI(~^6Od>HP_H%2p}!BE0HCEg2q8G=^x|P~nJdB9pYO$;0A|U*e2x%XkHLKOufz8C z!gu$cd`ksP1sDdGG^#tiIs~}-yVA}2{xp+cD!|N|#>1?xxc=|VzL^04o;`2UYDWwA z9b4yg;HrdF18B7)_K&*wzK^CQ2S~~(0H#^v4N(GfMKWtdba%#_Y4U4!nXmVmc|Uem zXswy2KFrnn0$>3Ecu50<5FBr!ukPFo>~*ww6M!-P?Z+44SM7w7H=pArER z0j};~Z3Ry1-xC*`)|a1Wsm3J&7-jwR#-m~Sv8e$7mOaQ!W(-D;?bB`dqv!Kx29WW_ zM!x>a10baYKqRRaGosLpp1>HF{5OE3QkOzAqT2dHI|z|d#;!0=TUo`x zU_=~jwN9RXBNz7-4|=qNkZX$+v<5KgR_~SRH*#?dR8AkBXTi-)sx_@#U~*q66}M?W zi&831QCm6Hfp!p5ZOI8pbY#Sm%pNe{Q9{>gb0HY?8&PNfw zD~si&aONlgHh+G&f*GFylhg8q+3eSU6FaX3GL4%f)wpwb`md(A{0I+qDl6TZ8rTus=z-7wAs&69Yky%w*Phzg|#1!cLv~_`O=yL!W`fP z%jl~gwk2h+?E*;dcmSaNV>8gO P00000NkvXXu0mjfV1}{0 diff --git a/Resources/Textures/Objects/Misc/authorbooks.rsi/book_world.png b/Resources/Textures/Objects/Misc/authorbooks.rsi/book_world.png deleted file mode 100644 index 8793ef8dfa2ede1e64cf3d1b8a02c4ba4ae3f3af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 526 zcmV+p0`dKcP)&iPXu6NVfL%DDXTdiBz}{D%p3;N?JY~X!o+UCr z2CL_=a>mhcua{xO0o3HADYkaS<>)aKDgayrr%CA9nq_GT0Kmogsaa9F%yPE{&lOl) zs&#yOYYo8cL)4u4_8i=7!o)gEtP{zLufH&Wp3;bA%CI}d>`Xrq`PzgjRER(3J#9ka zQvk514sqDs2bd9wWy)TL5eJY_NT+ToQ6OHI2V@jv6ya1XoQNzTd$=2ANH-?D2^)$0 zOD43!TOZ~h!=8I;E`Z1#4`Af=<^Ti;ve@IS?mjHPN^@Ir%0IzJdul zxcDH(#ns6NU~@3U4+bMBNI#+6dTyawP-(RgqT!#Ab6d{6=XXx;R~S>)3}6QEHvo3m z*R8d!EwptV@l*;4NdoJ32Ydwn6GgkNwpw_sR&iUY;Oz2J2n4WEELy2d275aUiH?r> zLX6%%_c}{O+s0B$U-m7XUbP5db08v zEG3h;dpL&}_AaoehfHOcsN~D5t0VoAb8}cI6zq0160t2a&ENoO%9;;u5XdMUpPb-e zf8TCP>9lPvc*_$YOxak1@E(NA3FHpyST2(>7_EEwktcvfgTea9#U0!&H;gmw1!3d@ z@CWdnc(7pzN33t#hB00McbSKu2mWI{1WlI53*cM*(mNZ|)ByZwG64Ngpeo9A0Qj0Q zL`rBu&$HZL`r1TbyjNgZLMC@S^CByLlM9 z4G((SX^3E9>>xPtFm{=@frz&qJWUv0hCPTUcXLq0Rj07UwQ0K~ZQr~;Z<0L}-%-I*DBp-_NCA_1Z#K|Gm+cBkXaCGnYU+aL%6 zh@uD-ug&9QIMOtDwzeij2AG|mruD-^ShzP1$xa<|7gc=P?EN`?#Tk{%rQnw<3VpJ& z(iegY@MC`;Ov8YMk^!kq29o%bFbX!MIQ!XI!@M^w3%>5{!RCu82aG`tkB;(Q&j&Cu z)GgBly;=nU4aq%!_)xwL4@;%!HNXmvMS!?P*H4!xCP0bDA&&)XHL4IsTmTuvEn}tY zeE=jh%A)XbXUDlu=W-BMT!1caD?&u>ix`A6Vx4$;ytG6gEiOje1%wdjb}jf55rk&q z84m?uV*XX!dQ3q@IIOCt<|sllJ(!=TPghr?ML@zDXyG|Py4_Po&w|2(p*M*9)NujW z*0NoXJY(MI2l-oZ0cyJLM8O|{-*X7UAp9efjcdEjPq556hC>0QQYnaGNcBd8x72~q zn@u3@33z{Qjy_*skGKoa8!NzjPoDSQHTzn6hnCT5aiJsc0zb$}5w~_C0jTI*3q1*{ zfuJqOWV$w24Yx%?CVF|t;LR9cIusF+>yYw8y>*G?uO-39T|Zde(5|2?ce}% z_`PQ!%-^4Q&y>yQ!EE*#mRr}a(YNLI(TY&R){2Fn2INz7jrw(jkOHPa`-TN6@H;;4 zibApIjFsJ8h+*gzw=bmrW&=acdO-_M%r7gMo<$gVjz>SF)JW1{*%CFy8R_)S?r{ zTwD*X+&&TEbfJ+&mSI!Ol*aDFiQiNe-U~5rRCrx49Qdlkk6DLDzjNCk{$`7dS)N`k zUJi>n8>>~8ybmS|L^U2)-ZRlF6NUcJ#e9jLDTf^`SblNUo!-~s@U*L z-+bj`hJfsjIo}>^y`0avjlHwT!R)YEQ(FQvFDH9Pkogv&hbx#f7#R}uHD?4(GWQ4i Og~8L+&t;ucLK6U0m1GnE diff --git a/Resources/Textures/Objects/Misc/books.rsi/book3.png b/Resources/Textures/Objects/Misc/books.rsi/book3.png deleted file mode 100644 index ce11a89b0e39cc89b90386cd6b4f771b9ce8a16f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5D7f3x z#WAE}PI96|g2AhXKz1Hpb`ukWKlR^wY`D|TCM7I6!Dw*0`m?q~M8y@!8XyRjt)Kg_GIq-^)Ey$tvujHaDf^*rHEbZL>=VkDttCOa1xVmz#uKYWz zQN_Td=->)wH(3J}*$@B!wl|hCY)rVqxR_5Oicewbvfn>{8kWjBc**iJ)$hBrq>tHQ ztzyxw=Ieg@Rd2|toMJp+rs(Lrp)qoEn()%R4Anz{>=&4T9_rAlKi+rh0MHu@p00i_ I>zopr00VYj(EtDd diff --git a/Resources/Textures/Objects/Misc/books.rsi/book4.png b/Resources/Textures/Objects/Misc/books.rsi/book4.png deleted file mode 100644 index c5f1aba4f88fb3037a86abaac0c6cd74250230bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 266 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5D7eSd z#WAE}PI96|g2AhXKz1HM_KJ!>=@oxx*+`$8m%8K(W58*9{i4)_hk@w{&!@UhOcN0~ zuyX1}mAMN(w#YJk+POl5IsYT8!gD6(jSv2R6I*mea6a3UCmms@KD-dT=;i6%;^ozH z_0DehNfVt_#Cxm~t}r@ZWc+`>ULt~BfZI(LXsO~e2BEB7^XJP4-(*y|@=@W#^V7as zmJBzfl$P68y!{g0^w#l_%!XukN9PTUpIbJ&tdvgbEV#mG!oU#FsBJQ7!FvgyKNvh+ L{an^LB{Ts5fT(1n diff --git a/Resources/Textures/Objects/Misc/books.rsi/book5.png b/Resources/Textures/Objects/Misc/books.rsi/book5.png deleted file mode 100644 index 18172241ccf543cf2175717a6da12b981501a706..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5D7ewn z#WAE}PI96|g2AhXKz1HM_9cdY(tr3Y<2@}Yp;^pu<K6@+@iiZ+u>0Y2ObM1=psx=R3l^qLiknIGrt?wf!H yZ^)^fW8BOk-NZXXf4=XOU5e8<++`h@85mj<o;K9A)it#xFc%AT1eRkQP8?zMk#C z9xq|!%s=+g1UG&g1ep*}2*Au;*M&1|0q*5OTq}ryE6=-vjHEN*IF4*7P)en1l|qOy z7S^O`s?rjMW7uDSy=A@LS9#to05g9F(1B0}sLbmCZ6UNWkRSxpqTucomsF~~7C@7^ zij)C(znQIdmH{3_=cho|)Chnn^$S4drn}Ko`COO-P`Tro9@)VR0Go!|%!A1>2D14+ n4@v>}zAtQ|qke^O0QiNT4!Tns3Oe1^00000NkvXXu0mjfDxZoj diff --git a/Resources/Textures/Objects/Misc/books.rsi/book8.png b/Resources/Textures/Objects/Misc/books.rsi/book8.png deleted file mode 100644 index 6ba69a8c482c525d9024fcd7efd5f2907354d561..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5D0tb^ z#WAE}&f94lxegf!w5<;)Fce^4{y@Rx2g5!GIoA)WvA-oIxlU;CHkqW?6)^jtW8ma1 zyg@tbPfYl;^EZe1yP_W(1sE9H7!{VTnHscKh(mFcA4C6>(=XXRePnV=`BpN|_r>wu zsgae8dl~dBlX>_yu^Lc@Il zMgav$hgu`YhyTB`0*#-VQ!P3=s!-c>HI0{hB-*&YoqY%mGl#KQD4+ zVBt|`VCR)Zk!BPWz^RqdB%Pq~gdG6#xu_@uZ}9~n=K=$<;Q#g(qSO(e2E@dOR?6_| z712sbON1O8gw_1`L6VU}Bo`0~4#&na*v_6kFb)7)zIZXo`l#XnaxEv>0U(Pm{Ru)5 z(_>i&7MmIgVbe4UK)yU23$ZZE;T6M?BS#pv-|_>?d8%!qPZYo$G3|gD!_#|za9aNV z{Ri4O0ND{w8T`ohIYkaYID#_EDRKa{z|eF6C_AFF=~52B0HyIxz5__8>3)8GOl1d< zP{)J7pD#D5XaR4Z#M?6V4K)ZfIu1Q3p^H008?oXgcoQ RW)c7Z002ovPDHLkV1k|plJNil diff --git a/Resources/Textures/Objects/Misc/books.rsi/book_boneworking.png b/Resources/Textures/Objects/Misc/books.rsi/book_boneworking.png deleted file mode 100644 index 634a1c6f0af51ea599d1c50d1799e5afb3cb3278..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 687 zcmV;g0#N;lP)-4v7Tc;8n!SdGx7xZp>IwiKQRB-zKhQ*yC=XSm4V0tm^haWhF7P= z2t+})fI`EDen{BWn&ArZ+5i?;gJT$nO`v?J!vGi4*0QWRZT7g-5tajkc^?cV;7-$`pZp%;vakIU%C{aP4EDygyf0uk@e}4u V-6QS>v19-M002ovPDHLkV1f@OHx~c^ diff --git a/Resources/Textures/Objects/Misc/books.rsi/book_borg.png b/Resources/Textures/Objects/Misc/books.rsi/book_borg.png deleted file mode 100644 index 8ba4772575f76bd90cf827d6e1634d7a3a13855a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 515 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 XUUrsBnT=L<00000NkvXXu0mjfwrmCe diff --git a/Resources/Textures/Objects/Misc/books.rsi/book_engineering2.png b/Resources/Textures/Objects/Misc/books.rsi/book_engineering2.png deleted file mode 100644 index 853eb6cca42712d3dc47c5b6726321839662a074..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 672 zcmV;R0$=@!P)RG-AHE!_QQm-i!Q=0R$W9;5Zy>n zI%-~MbkQGBP!JJP1a;F7sf)VlB8j>%BB>xKBc#$|4V!4`ril}0!#U^d=;rBNzo3ga z=jc`#`@(bJyyrRZ^EvPNzPyjbKUNMP2e32%-1Xk;?Uk{m=Oahm{&~{+NN=eNX=tdh5%l?ndJ1b&Ddq1*l%Vr zqB8n9AODa8YnchFWcjvL72>E9(aZy2E@ab23*?DUm&}ZXBx;YEnTVHi>g0NY1AVM0 z9~K_5=$=wHK!5i|;Xn)hAIAw6&yf_%JvgE;HX5d+teP)Mk}J)Zsjc$}fVY_ia8-43 zp)tY3=LufE?&9$il~hV49JNwlUrVubCDG_C9#1_tTY51fT>zaQT=<4|QeEk#;ot}k zd5Tx>m*WlW`<3JOrz0d*$xwY`9_SA25J5(@w9Wz}C!I1ioLK-PXM9@8c%Qi177vi_ zh8EHf_H;!=KX^mal9~W_TaFv_^>?+k>dRYNCd>h3Ltg=iTF2}fN=3;40000VQ!P3=s!-c>HI0{hB-*&YoqY%mGl#KQD4+ zVBt|`VCR)Zk!BPWz^RqdB%Pq~gdG6#xu_@uZ}9~n=K=$<;Q#g(qSO(e2IS<3R?6_` z5z$IXON2^FaOTh0Lkw(xjxaDTuR>LG?;c4;l9dZKZ)sp)h;?FMl>fwVIG~-u)}oSu zTtJosd^Rm&cs)6n;eQqb!&_#`ax%%fLB4;bB*wt}<`2U&ZgB=y9tMWPC&&*4k{w{b zd^W@9NhJ)-NMbfp9~sC6Bs<{y@oEMZQ+A-Y|3K*LKf&VvA60;9Mus%nI^fi#R}5x( zVhleySiqV^U%q76a*2b%NACv%Ie=sb`1&+3Y@XD>VD8G#AbQ{t*m4j6(I7ckHvIqo z18p1t10bIxJBnODRxSVqH!RpK4Y}!?3qTfbIgju)DDl}yF^~<&iUOGB;1W>}B1Uc~ zP*jY86XX_%V+2IVGlOIYh)9=Wbp(CV0FIi@S1S%f?&s&nRCWLfbvy|C`Erv=4gj`> z$Y~E=JN=emdyqH>?ASrj5NyCa;Ve<=hDft~)B!XE070*c8E6EpO8@`>07*qoM6N<$ Ef{Pl_WB>pF diff --git a/Resources/Textures/Objects/Misc/books.rsi/book_hacking.png b/Resources/Textures/Objects/Misc/books.rsi/book_hacking.png deleted file mode 100644 index 1642063f7e4bb3e1550cb1ffd8d8632b9154088a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 487 zcmVbc91gqZ9-=cvwS(*Cga3G&uVQ zL{M9UC(+Ol(Gb+qEHoHs;MUP-=im}HH#CS8mO@}$?;Rv$$=&_Z3*o-Yk01AY@B4Yj z@9}R2yOw~~e}JD{ z({Qw?!HKelZmZ6A56n6+e{xXks0jnXpk+;`-FC7iylY4TxQN9d%W^N*bsd>X#XSIS zep@d0^0h<)a1(%=H(0YtlGMwWKohwKLhMF#d8}5^h(@W~&;XVd1*_}lILLhrU9aO(6cGrAu~97I40MriK_HogSSa8+mBLJ^#JbLK7l5Cfn{x=T2$qi=p#P;) z@_F2((`M}S)xN(z4i{k18^6HJfBV*bdl(V`n}#U!;Qd|Gavrn=pt7;ggm5U;=9=-G d2Rwjb>JxBicgGUjRB8YK002ovPDHLkV1n1>)@uL& diff --git a/Resources/Textures/Objects/Misc/books.rsi/book_hydroponics_pod_people.png b/Resources/Textures/Objects/Misc/books.rsi/book_hydroponics_pod_people.png deleted file mode 100644 index df41d9621411feecc26b8468e5a251ad02b3d164..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 593 zcmV-X0W{ChIfD-~RO`GNsJ4rq&IR!PQC>Az66PdyJ_WK!vCSd?B-)DK)InaS{y}*cf$*)KV0I~ zRf`}JYznS}dRC=w*U6=F^bO=Z=Wbh7Rd&a}aY+o9K{b4Uaze*w`G!x&Gbo89Kb)UD zTYgUd{6%)Ye`TdnW7pk=`{iSj&HgRxv8*f-&wJ8*aJm;>MBNj#S1QGch z*zl_j?KPQ959w6eYumO>cQ(Q2cj6FGKr59n7iPi*DCc!n_G*|33*$+ZvZmuWTONq} zb{xFnMPx%MoC0LR9J=WmOX15K@f%?=_F*sZm^pK6$YtMu-)B)q9%8Rah(tH1JtGxT)O-S zfk~O45JEyQhLl!i97_;_5D^%exG+g$leqXUcvuR1$ZcBfk5q|?E&EDbQY8q5!1H2C4!0RU1;09@Cl>imWP z7@l85@*5c-2;TCVG#U+r5CC|dhY$j#6jf&jr&@`Wd7xw*fWxx`>h-z~?0KHH)$4U# z8GuXoG?LF)6_8Tuj0=SV0F6dNg9I&!qgVjzViVIeky293mS}lRgb*5PRFmh$TmS%y z*%ENwj&{4P=L>~`whgWZ`aqfsfIslZzFS_CcDt=hZ`-yG9w_Qx5Ni_#a$Q%Cg%D%k zaTQvDzTanQd1-8}m@T1{(t#(lpDO^|+=Bi6eZBFNQml*3KLQACK{}P5X$$ti-?ShU zpzrtf*r`^4>)+9~ZA{ZdDWzKw*yjomD3{MqK@_tkUHV`?m(OD)jrpI1wOUQzh_f;c zu45x<;Co-``MprTp--l*jqRaamU<(mjA`b{M~aa&c=25Fp!_=2f223z*2ea&Z^A&C mbViT&KJP~Iz0gAcT)zM$)83daP?8P+0000B~Hxqz5dC}`t#f6a@`5o2|&$l+r~R?fp5}~{RqhLtLF1h zh)B2wOw$w%MOl`@u~J27SypFEQ4}&mV%Wnv5K;iX?>FXqYGon}!~Ou6?ufYyHhZc%3sv2YMH9Bo3!&kq z0BDNYFThi6%0;|q_Y2nF`##Wypqb$2aU8P(m6g&mY(wywt4etv;1JwgW%gPC970#a zItyef{HuH;&x^wW$ei(1oAt|K0O)N<`!9YcKQxUFKd&Q!}%!00000NkvXX Hu0mjf;R~1k diff --git a/Resources/Textures/Objects/Misc/books.rsi/book_nuclear.png b/Resources/Textures/Objects/Misc/books.rsi/book_nuclear.png deleted file mode 100644 index dbf95833fdd79e377a89024e13edd91de963b3bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 809 zcmV+^1J?YBP)GddY3PBfMx_(7P- zopbN~-E+=8_j>W4bp>z*a5(@Tr_x`ox!XsXE_DH5mzxkAF8)Qj*q#fZMcb-D33i$o!juVPZ$CGH zb&%ZfPyIO)yVGH_Dy`_x$?|x+4?){u&J7?_{NuFsOij)uf%m|(`FZ@NEt<_{^|svd z=8=tNK$o|+`Umh(4%Da)D6JM?sW}Uai`_d3?b;(y-@XBOYpJ$a4&Qr;hcgMhn%ltm zQxShE_fPcGb}Q&bt^gHD(!F5!08jpW`3AD9#S?v0w(U`_0O4>LUUI3lx7X3mE4NQR z7>`5R--lvUz-pJmbtV8_dBd_IPPQEGI@)rj73e*o()Ft?dqymF^9eIa!q zPJ`EIC5l5_d197*a4BO4(ul@n*bi3hL#)pPb zF-|&$C~a@!8lCqH!s{*5e^zW{1TrOJW3PZ-(GT#%<1=}L zdTm3yXJ^{x$;|ipW_184!!SNTj-6>cj>GD%W=num?s_ihx}F>JJTD%v6aYmCDFncN z9&s%JW}Ak<_P4^zwHko9@}nmWfZ6j*KNmovBH^3Vr`U!~y7Xpwa zApp_r*#Q_%x_fYt48U;4qaHU`5**%X#&L1oWNB?ixpWTt*%$fmafN21@ zuDfZPM#z8=vN#Ih&;0de3DWf)T;1I6ywR%SN zp);T;3fjDFTXZj12r&>m=G*{OzVCys>$|!vOGwics;UAJaRX5EEX$xMilKHK2RzS% zD2kx(`_ltZ)pfmFaU91G1Ob?)33-kW=}`bXuUm}TYa1P`jjQ{`u07*qoM6N<$f`EIeGXMYp diff --git a/Resources/Textures/Objects/Misc/books.rsi/book_science.png b/Resources/Textures/Objects/Misc/books.rsi/book_science.png deleted file mode 100644 index 47047c958a032b1709baf9936c6b06fe55b7cc34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 291 zcmV+;0o?wHP)@Tc5e_}9eN`-LBZJrXi1iW5SLc!VDn2-|A6_RL+ESY z*U*pvk;uQpD4`s=QBYn51&Yp&gj&8iOpv5A4EglwJ{yl-KD%UFtDsdtL4ku~HLn2d z>&wpCEJ!)4KtqO%8byhcoFxkYK#J6w(a?1KqW52xD37jfv&L&PP?JYPFJaOzXC3MJOJGE psUHCj4xjVjai^{4!ARsMxC7sySvz04@h1QP002ovPDHLkV1obPdglND diff --git a/Resources/Textures/Objects/Misc/books.rsi/book_security.png b/Resources/Textures/Objects/Misc/books.rsi/book_security.png deleted file mode 100644 index fb1a4028e8f4ac3c83683d1b1415d0157179d872..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 319 zcmV-F0l@x=P)(BbLBOZci<*>0_6T?8(n}*&Ve1EQ_c?1 zEoTSlma_wN%h>@@!1@nxPP)@>p*MhY#sk24KhqRI1eN>X)LTjKg9gJ-Z~^5CS}WMb diff --git a/Resources/Textures/Objects/Misc/books.rsi/book_space_law.png b/Resources/Textures/Objects/Misc/books.rsi/book_space_law.png deleted file mode 100644 index 524f10b76c4719fe9dc921261121e653c67d8a81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 434 zcmV;j0ZsmiP)VQ!P3=IdgGcf#rN2Uec3=E7EIRIvP-PH;P z79Mp5c3xQ&c}772oO;U`O$a)WpaVdb3jy&I?G6S~K*j$vM5!Y>4G587IVsUaQX-Th zLd`P<(f~!dKzBbo!)3>xlo&y}16;15hJ*Mu1bdGbss1O?0U*m?zI+S!HF5}m*dY9j z#1J9c0dYXfVLo(0Vx#jx>L!p75+sKJ2!KL?6gjFmU=h$)?!X`h0rZ3i^1UupZYQmx z0Gq}5_^Kz#?$0+5vkkO6Ww1c5~)W<`=5083!lq5_r%NCh-Y15|ZD0Wjzn;mG&s zWdbOtEGNM+WEI7rs6ej=VEk)${*h!JQ4RoEC_9^#;c@D3FdLNDL41(EVdVo%45S7G zetv#TJqI8MDUAINY7s86KVNQA#{r;*2s!P+Yp355Y!4FUfJ!0^!3Me$W)r1uXqAMc c4xj}9039Hg+zFV-#sB~S07*qoM6N<$g2#`WJpcdz diff --git a/Resources/Textures/Objects/Misc/books.rsi/cover_base.png b/Resources/Textures/Objects/Misc/books.rsi/cover_base.png new file mode 100644 index 0000000000000000000000000000000000000000..4f6f819a3a116963df4aca8bdda0b0131372373b GIT binary patch literal 288 zcmV+*0pI?KP)Px#+DSw~R9J=WmO%=GFc3w@(nS&>P|3C)!6VqqdJYd`R~0c3sBvdk3I**<0wF^4 zHiArMUPuBTAd&cYGD^|2>lVn4OF8Ge|CLgt{^;PSe--7q_CIwuKA1(gSO4TmcF}1m<~;E8s#B2qHjvs!jrLhlZ}4 z{|JmRkP8t{03h-aNGXF2CkYs1;tKe{9qvpJfi*K|t|5*F5=#cINEVlRcI(7 mrIxmBNs&w`Px${7FPXR9J=Wm9fsEKoo_Kn}uMbiCO>}Lrfq(hEJf058zYz1ipl=w#3HPu^=In zU}&7ei6}5zY#_3~{xiBJTdZG0fRnjr4)+eghY$ZdH5HK%LcFwit){fTv^eMdB?BtP zf5-&e?H23x8etfUG)={K_`d(N(VsHG@puHK6oe4yx{h|c4PDp2=*HtQ@;t|CwGyMz zNI80`(D!}O?REhGgpj)$#uxy=^E@n2#`OLYhFQ(?J}^IGs+?#fHNn8jS{Q z+g7O!Kp_E2sQ`c=2yne#AL@z}fZv}(bpoUbBuRoi&!r1Bn@s?KVHo8zA%u__Q8K_e zmm0W-N($03jN=%lX~J=wyYF4sh3mSNXT|udoCDGX48uSaML3_&m7)cYCPNjefx%#a zBuOg8itC>{e+B6$*zfmLZE3{rC((H^%UG1c?D?#p67v5dUsd1JHMqkxZctOx~@ahwDP_5;Q&f0dc9s*oH2&YW>Xq*T^EcoFvigD_Yp-A0ARPoO4)~C6iW| zOeXL78O>(18l_as=X11LE!eh=dcBS;%PP7o%MgYk;y8xqc~6=B@Zq2G3+bf|^9JeM Q!vFvP07*qoM6N<$f=!SD=>Px# literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/cover_strong.png b/Resources/Textures/Objects/Misc/books.rsi/cover_strong.png new file mode 100644 index 0000000000000000000000000000000000000000..61baf7b5778f1262b40dddb7e311c4d26f3fd733 GIT binary patch literal 470 zcmV;{0V)28P)Px$kV!;AR9J=Wl{t>;KoCG5-GD67*bxFD!2yVTByPfWH~^<0AV5T100fc&Z(#5; z!I$|n%;KJ5kb~hRos_CyS9Pi2$&Fqgr(NWHQN2(?ltyCHorB^WIzDP?&T)9%o?~GM~@s^?LOCed0KN6C!KkY+Xa6X^Q5h(o=<^reFsT_g)^GPYo5%>-d6^NqfegP>ZQc4<) zhE_?Xlo*Bq=nrvkq3<2VlWdL4k-Y*r}nz@qLn2z= zUNPi5tRTR0!1qa0g3D3sAN5DMD_ScVE_oJahkJ^?E?6`<_{|mdGDijn7Z;b56*u+OAdsgX;g$tC?SMc63Dk2w-HWSogY=HK`}*J63>p#jkZuxxnwry4n3`?gh>{ne{qBZA*{u_Z^a;tkk SAJ+xCkipZ{&t;ucLK6VBy;Udx literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/decor_diagonal.png b/Resources/Textures/Objects/Misc/books.rsi/decor_diagonal.png new file mode 100644 index 0000000000000000000000000000000000000000..50f6f591834c155990cce00c1d40198ebda72db9 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}8$4YcLn2z= zPTR%T33Hm4a?@0xj$Pjw2<(q&oASGIFLFZ}NJa6!<|@>vrW zoQ(3{%zHJ8{cdmCrfbs4HS7M!@0Msg7{9yE`!= literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/decor_middle.png b/Resources/Textures/Objects/Misc/books.rsi/decor_middle.png new file mode 100644 index 0000000000000000000000000000000000000000..ca4ca82114c9c87b69ca5cb8b1ae0a6bcccb7e39 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}>7Fi*ArY-_ zFB$SR7;vx#nCLyab*1xnd|Pxuv)Z+daR)klyY&8_wSU5{z{J47k&<@y^qF{(%1srF zp}E!PVx7a&yfl6DH|@_?SaKlp^$jNX;|JmvstX8y=fA_WROHr-<0e3DI|>=sJ{LGO T!Rfs|&`JhRS3j3^P6r)<6{Yn!`g%dY9YWzSs8smQ3%^)K#{j+6`2%ZRA# z(6>9+I;wPN^z+$Xub%Fh!WOZHkI#XD>4WY8v*r2+HhD7`AD*>Yp5eexE-%fr!=Vgm s-&iBQ^EEux4!9ci`S&E(iKW&v<|v%~e{b4GphFluUHx3vIVCg!0AU$MIsgCw literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/decor_vertical_middle.png b/Resources/Textures/Objects/Misc/books.rsi/decor_vertical_middle.png new file mode 100644 index 0000000000000000000000000000000000000000..75f141ce40d4f667c43efeed5ec3a3cffd66dc2f GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}b)GJcArY-_ zry24!D2O<(F8tY|T_DMK&NU_Zcx#KGIvcx*fT`LyUwcK}d>w}q_h-5_E4BpX3oR&g2c3KWIV`;u#5V*l6qOP3w*FE(JLAcG5e21RyLGJ=tg791J&OCj zxKzN^xa_&_W=C2qa^air@?=}t_cNvp4YszGhHQJ~HZ(EvBsgd#Jbfs+H13ePvfcll bjt;)-Q^mim*42mxTFl_->gTe~DWM4fCJjJM literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/decor_wingette_circle.png b/Resources/Textures/Objects/Misc/books.rsi/decor_wingette_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..a9d9afc120782b87264ef3ef0d4c1c362df51e7c GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}{hlt4ArY-_ zryk^OP~dT0UC77O{XnVbxKfEAPxphBot=!{S0pC??|b4bZ@a~Xf#HMUWHX86HTSA- z73HO^DL!&W#3^9^YQAYft3fyV)>>$*iTZpu2h6pSaYc|5IUO)|rQ^*0z5cUfMsr@Aovv;#Tmc$r)}FC%hQ3Ug3McH)_#)oXMXVBQ&7%lwqE~*cglZ( P)-iax`njxgN@xNAg@8R? literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/detail_rivets.png b/Resources/Textures/Objects/Misc/books.rsi/detail_rivets.png new file mode 100644 index 0000000000000000000000000000000000000000..53a4c8bef4ca7536b978627c095e3fad0d156f2d GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}9-c0aArY-_ zuPzj1P~dUCDEFKHs-lI@^p2HnoCib&{@?6+l9j~9V1N2dy3pxZQJ`MDh%=^yS!0x(2^Q@!awDZAY4C}=C54^5jroQaq&g%Iq z85@q2_T0<;EcGg#vCWb>;Y(lQxfh+j)n(yq_l?&SpGsO6@A5>yhT-xa^<(!!oIV5H O$KdJe=d#Wzp$Py&1yg$f literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_atmos.png b/Resources/Textures/Objects/Misc/books.rsi/icon_atmos.png new file mode 100644 index 0000000000000000000000000000000000000000..518623069e8e56f266a1f3f23f86ef52559859d2 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}8$4YcLn2y} z6C_v{Cy4Yk1sYsw{Q1=V|KYg5fA0T#`oDfxgvS0t`#VaXPxlAts2&&eS&~)p;{Az^ zHykMi|8DCC^(xD^K4TSfWTs<_VErGF8=s^D#tB8H9YvDDzUIZPSs?4MBU?m z|3688|8Ktk&kOrl9rtZBqS7W#VY^V<|{Ln2z= zUfIZXSV7>}$NF7X9Mkss$Q}P1x3FbjQ+1qfa+}b>gE#qF*W{iT(72Odbn*8d=YtZ- zJueN;7r(Ymwpdir6IgrBb+=%G%<*||e=%q-6?0-xXmRlJwB-tAcrJ6V`daDH>i%NC z=>7RD?g6YTwg!1IE|}u9_Uj!6C8H;E7*AL#D?Q)ablk+WqC7Ej+TU-t>>oaVqO?hT wSL}M5?YZ&ymwVprTz^#J_VTTU$No$e=hC$=ysPL8bUXutr>mdKI;Vst02!TSmH+?% literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_banana.png b/Resources/Textures/Objects/Misc/books.rsi/icon_banana.png new file mode 100644 index 0000000000000000000000000000000000000000..2c2a65741290b7ca97d94b98966746b8594fb01a GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R})t)YnArY-_ zFK*;KY#`F|(B3V3$w#Ys>cJJJ>lOGPDD4PH40v$o2FsU=;Mv}8k6v#NjjFyc{Y>*Ex0UPMj^+KcZdclrs)*?kji;qOY&{+aK4Nv4nRJ*@ qZsLW?_eQn#|74Q#XU;U;&YYsFb8Y>TD|dm;VDNPHb6Mw<&;$Sx-b*F` literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_bar.png b/Resources/Textures/Objects/Misc/books.rsi/icon_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..3bb7e7631f9268a3e640dcee2614297684d960a8 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Q#@T9Ln2z= zUfRgZWGLWrvHk?_1(t=@5uH<78jLg=<1!91af}o^Mt|ZH=n?zJq4+6 zBUUpC3QlC*SshrhdZFoyY5e+|C4@@FoDZFuRkkilZuU(9XQl@`Y6YAhRoyr4-Ki}j zr5|wB+b5YVVf)UK*7J|vJuQiwzF#-~>XkPx#-$_J4RA_kl>P3*!kAz6f- zzzzr@!RhvnQ?q~&LWq;OFhO-)=j~nJ_taZd@(@BE$8lXDguEx}6Ru2<_a4?-#28_% zh4-F$1^{qlf}C?mDZv=C?uQD9F#-ULF&Ksc^E}f6z8;*WY4S8pFZW&79W#WBhXDHWSSH>`;_!-jvZL!?^isof&zW6ZT)vdM8JqBS zyuACUG=Jq1#-hf1T7I_u9d&2_U*2+I8lMIC7rqyHlW%{tnVwkM+lQVszE+2Dqoy$~#1eNf` zMMr9OFuz;pp>kVhlgX*y-~a#r@q=m3dHw7I9}1EcBhxlcSlrpz*ytE@eman9e0aY9 zJWu_;dWGu}mxL9Ketdpxta?kcoWb-!gZd$$t!@W3Bv=_l=j*)YR_|F4bQy!EtDnm{ Hr-UW|)Ou1O literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_cabbage.png b/Resources/Textures/Objects/Misc/books.rsi/icon_cabbage.png new file mode 100644 index 0000000000000000000000000000000000000000..e9c806f92e25fdfdd989bc373155574ec490e728 GIT binary patch literal 439 zcmV;o0Z9IdP)Px$aY;l$R9J=W)4fYVVHC&lui2X^S`=K2$X=j9q!%KKRBCC88Wij==r4%E(V-y} z`V$&!YtW`_sHG(o0txSf6@?I&1i7Y#SlQOlYgya6xGv^-ENCc-Lbd)3z~$bJslA`GUkN~vWrDvvjrX-KH*xvS6RD?jNt96jiIiHcs~ zUS!dROV-SG+W+GL#EF{nt_9XDS{?6rfMg`$7j`u|J3YriY9C9r&fu6QzA(RdmQm8J hTwv%YNRsrYJ^*#iO&Tt4 z3f3$vOP4(O^Zoq1iLy*OPco7c5)vM4i|#5oz~x@lt9YL=ed3KrDbfC_>=nGJ>;{1g zrfW645pdvbX7p$|mJ)4wLdGHN;lTq9Q8JUGIeLMXbGaQ{z`!6~qnKYe`}TC8`xrc3 L{an^LB{Ts5F0oHI literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_corner.png b/Resources/Textures/Objects/Misc/books.rsi/icon_corner.png new file mode 100644 index 0000000000000000000000000000000000000000..70b68d327ccd07162249c512d83c3ed024a95be5 GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}&YmugArY-_ zFCFA$FyL_t{61eNJB8!?H<1?%tpPvs8BJZ78rWB9O}cV7Qq$nzwsNjJ2e)l5>Qu3Q hoxi!RjtOW&yhW~+#%ig>;opHA22WQ%mvv4FO#nKQE5rZ* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_diamond.png b/Resources/Textures/Objects/Misc/books.rsi/icon_diamond.png new file mode 100644 index 0000000000000000000000000000000000000000..268a659194e695504a7c035898eac45041d75cb4 GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R})t)YnArY;~ z2@rU2;gA8Apd=StK^Ip$%LjVQ#p?>uh*;9ieD186*wj!IMLq7xTKP^{CHuI3roG_ zQkS$vr`Iz#=*4e5dwpi{YKPFRm7K2x=XnPN$X`^C+t_g;xa`pp=kH2W?=}G~V(@hJ Kb6Mw<&;$UTnmG3W literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_eye.png b/Resources/Textures/Objects/Misc/books.rsi/icon_eye.png new file mode 100644 index 0000000000000000000000000000000000000000..11e47cd11d39a70139092188cbceaeec98c9b376 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}X`U{QArY-_ zFCFAP4FVFJ2Y}UZSaNxeBcj1>?MU%VZ zZvR~Kym<3h6VdOgK`W>K*~Jps?tSS?$`6@Cg3E(S%Z=CTeQ15?Z}3N$iD3it&pg%h TYf?2p?qcwC^>bP0l+XkK^?^Wa literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_fish.png b/Resources/Textures/Objects/Misc/books.rsi/icon_fish.png new file mode 100644 index 0000000000000000000000000000000000000000..7baaa1318568ee93c75ec992c2509f5b8b21ae39 GIT binary patch literal 342 zcmV-c0jd6pP)Px$5J^NqR9J=Wl)Xv=Q4oc{jaw`%7$FG(3w;B@N`w_F8&U8%7SiPfd<6>|A3#XE zmUgky&c@n8;Vz4yAY>sr7Gd*))!YlI<|{AEnR5>_1BPK3h9~vgsmSZ5gzB_>ii#`| zfN1xN@kKk>gc|u6NqN~9$&qT%2{NEgpvV#_?}xOiDeq~Y-)onB~JYJOm)JY6L&pGa%6Rpe~bpkM(gM*Xy zzvkx;6SZFKV*pr*-KTMb*A|@ZP3Av8+)vkg3#vg!nnMI2IU+ghTSHhxzHG!d1@9SX oj1AN)37Y@KSDIsgCw07*qoM6N<$f|Z1gegFUf literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_glow.png b/Resources/Textures/Objects/Misc/books.rsi/icon_glow.png new file mode 100644 index 0000000000000000000000000000000000000000..030f8188773d6e9a44ad2aa33c346fa1e6188c8e GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}3p`yMLn2z= zPB!FXa^!K17YldUuw~=i=!(w0m*h@zS29%#x_yt%k&;%-)H%~^FxjE*kP5SFpor_o z_Yo$JmpIBPx#>`6pHR9J=Wk}+z-KoCWr5@QvvRQL$fD7eysyTEonf{x(cMT!tY2)T=n;BG;k zVYdhlDsqL?Zj2qJiA?G-*aYS^vs299Kc4}^Fbu=EGMQ$`d*3_fPJN7#5Q0i;q#1I~ z(XQ90{(i9_grJeqB`}}O`mf!Nq9_0u?e}c9Tb0(0W(Z(9nGDbFFdl1VbOz?$vwHpH z@cqui(?3nCKHhoXwNaBrmpM$Z@sDs!!Z6WKcU_}I{hAD(*OVf07*qoM6N<$ Ef}?JQUjP6A literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_ian.png b/Resources/Textures/Objects/Misc/books.rsi/icon_ian.png new file mode 100644 index 0000000000000000000000000000000000000000..04109c503641052daa2c59f2a1bb05665bc4d34e GIT binary patch literal 390 zcmV;10eSw3P)Px$KuJVFR9J=Wl+Q~7K@`V7Wq4flqy*s{8oCMw#^ANTK?EW264A-KNB;#0FTr9U z^e1HJtw1_>-OeG=VV#2qFWW(OO@iPpSeN<0z?;YW@trrrfWzT%IR2SEN$}ehaTKlX z)A1&E+9C!h6yW$qR&!qx97RxTwyD-t+H~3?uGST`W}8IZ&IAB_zT5*)QF*2<;)=>| z`K26W$^wd|LyD!W*&D|AFDe za(V6%^vju=Lxu_DRD)$_#&BW*7)~t9&McjT6!8RJkGcf@h{yL2!Z1W@jqi^zrVHHd zR=|@PaG^B-zCQvW2%a(KY%RnLV0g8f5BO7$}6Sn{{3jU98|`~IO(kN&7zd}#hX_ODplyZrytfi zqUNrXpm<#H{5(4)&ps`_gy#YYEk7dyBl{;tSk7X~I$|koD&xedpt+;NGhs=Z>m(sX zoyNrO^jT>K8QEVr_UqrDEG49(erUeE-JkkzUj*A+4gSrw-tJ*K$zVtQ_e=lpT3=r< mrOv=kZ)2bVm)pS!j0}E^&HH(O-{=Rrn!(f6&t;ucLK6VTv{;$| literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_letter_N.png b/Resources/Textures/Objects/Misc/books.rsi/icon_letter_N.png new file mode 100644 index 0000000000000000000000000000000000000000..7b1888e8c6aa60151d78b7febb46ea962db4970e GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}ah@)YArY-_ zFC64NV8G*iamntC|66OWGrZIk@LK3<*cP;RvHriZ?6kDBc;})CGX9&l_s!9nE3+VO zj@SfIMRpe#uNE1G1?M|VSEwZD?RJ~Ox`yG)rH0+z<@*!TzU8ZT-_s8)3Erd%w28sf L)z4*}Q$iB}e&9Le literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_letter_P.png b/Resources/Textures/Objects/Misc/books.rsi/icon_letter_P.png new file mode 100644 index 0000000000000000000000000000000000000000..12da61a0701403eeebfe88d0c2f3921119d92252 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}xt=bLArY-_ zFKy&KV8Fq0z<Ce<@1~yCrf`UJ9PSfq^Fs#mByYxR( z!(7c-k-XoYBuK{zZ$8_2i1*9Al~z^E`9afp3jA}@t_Uu3JyP&}(iew^yftPF)j3K^ bn~IoP>*SA>9{qn9XfcDQtDnm{r-UW|puR%F literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_lightning.png b/Resources/Textures/Objects/Misc/books.rsi/icon_lightning.png new file mode 100644 index 0000000000000000000000000000000000000000..92a115d2b6bf25ea262b3c5d0350f7037f490d6c GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}`JOJ0ArY-_ zCmrNHV8Fo~c$M?kJNpDDqo$YLj2sgKRPM{)JSL{tB2Z*>Yc}HsBW8wIJPZbJb#?33 z%AH#n`L?udpWPouhG#EpTV`6ld(qf^e&MG*@wuW(Kdq*@zj$=K-*6A3d-ZDH_i9

JI!(yuN1If%QPE89ZJ6T-G@yGywpXU_>DR literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_magic.png b/Resources/Textures/Objects/Misc/books.rsi/icon_magic.png new file mode 100644 index 0000000000000000000000000000000000000000..80e61b35ad38bacee8d4586ac151af403bed5610 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}MV>B>ArY-_ zuN>rUP!Mprc(F>wEQ#|{Yw8WwX$i_~zb{VaJb3wC!k#nQ4L=)M99&#l1P&E#OfS(r zRvdTt`Ij?4rajVr_PL>`=8nJBp_0Px$5J^NqRCt{2+A$7-Fc`)0N?h68*q9u-g^LSk@n9@0-hvZ2>E_06ol1zo1lpz# z^#2=8;5`a~#D2yA0002q+On|f4!*L`6SQSvt2*EGA_Pl)@$$u17HG@D-Y-Wh>o6{$ zD&caai=e&}vGToNf>oWH>~t`S7odCb#>n?t02ltcK8F%1#mwLP9MmU^)l;}R(^el7V!iC_2%1ecJ7uOJ@)000000KlJ6?+(xT zZLY;zW8VfMQY^|;wq!tK>f%%VM<4qM2=aL+l=;c2cV?vX0WtUI)%nZ^_#DK10P_LN o2mH)eAh?7qdPx$2uVaiRA_ieW91j+G>iIWf_008)R60YIJTI-(kBw_cn*4m=;&{qPLy-qP63?cI?rBHrlf-0V-&D?K!95wiP% zp^=y~1bCy@Lle|AcHzAEhyahkxq$Wl9YcUW3BOC-TdJzZy?|G9GJr^WKOoEiqE#-% gz6gh4GXemBJCL=iN#8p(EC2ui07*qoM6N<$f(;0X!~g&Q literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_magic_knock.png b/Resources/Textures/Objects/Misc/books.rsi/icon_magic_knock.png new file mode 100644 index 0000000000000000000000000000000000000000..506dfdeaee7111d19d1bfa83cf796a5231979da2 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^4nUm1!3HGP9xZtRq!^2X+?^QKos)S9WNUf4IEGZr zd3)QDugO55^w;#2`^tQWC7}C5IvJ-JbC8H zLr-U)Rm-&ABYx5N|E1i}iEHv}9$^42KJ^|j|8&^j;p=zC^>=G3&mDeI12ptcxAj2{$=^EnTEak9c)I$z JtaD0e0svZPx##7RU!R9J=WlOYmOV literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_medical.png b/Resources/Textures/Objects/Misc/books.rsi/icon_medical.png new file mode 100644 index 0000000000000000000000000000000000000000..36799b2f95eb42726182571c54d44f79d10f5442 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}d7dtgArY-_ zFCXMRpuoWzaEo=%hW}|f2Uyt0~QkP3{+)HM9Tm zbH=Fb+ZJxNdhgW3u4ebm%wHJCYtVioXWq28_JNOh1?HM>c%ywH?&q+7zCbqVInZVXPgg&ebxsLQ0E-MkDgXcg literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_medical_cross.png b/Resources/Textures/Objects/Misc/books.rsi/icon_medical_cross.png new file mode 100644 index 0000000000000000000000000000000000000000..970b6d3b4b65c2820fceb61eb083f4ea3faa21d5 GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}6FprVLn2z= zUQ*;^QRHF0uz&VWhHc>wm@4>AdwOt8JHsjHr(CU$*IZh9Y4N`nrA2YO4UgH>GR(MX6>;mV-2$NF O7(8A5T-G@yGywqbrd9I* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_mount.png b/Resources/Textures/Objects/Misc/books.rsi/icon_mount.png new file mode 100644 index 0000000000000000000000000000000000000000..399560fdf65cbe5964288378fab9ac40b52dd278 GIT binary patch literal 311 zcmV-70m%M|P)Px#@kvBMR9J=Wl0gcDFbqXA(?M|OAp}8i)pL0SujLW4bLC3#5ZMU<>8x5tnb9^C zVW1C$6qBEyv;c`jB9ZuK5YZr_iP3n*6cbTQd7J?|IT1n37Q_aK2)eFw@$CkRqS(aJ zEr<*tqWPSJrV zP4DEUX$%0sx~^aUTNBI-<2ZWjdL>p>WwI=TBLfzt6fiSP)8wUn`Uc9fbZb9repD+q zutzDiiTCGFUXtefMv}(C2AEmgPx#_DMuRR9J=W)3HheK@^4I-y~!g)I<`3U<_E8fUT9Kjb$FdVk=7p%MApq-NsT( z3LnNaK18fsBaE3P8=F}Zh{ck^at6f0`<25m=Wv+|LI@#*`0qAsE_wy zDg{7&zs8(*rvg?JSch$$M_k_x@y$J*s|R$PIE7mmFlmO46ZYy=Zn`gAK316M6r&cH zH2eJm07fmC=#+MUlfmc>fadWo=Htt$fYTO0{vRNp$~xR-3xp8j&wT^b9ZLhpv>tx| O0000D74Xb^DX_-QSKz+kKZa-z1_K6UwsZ&A^zMVG>J2LDiAV{||g% yyB4Lp`@KGU!<%zkuX)t1X{wAYC>0Z&C@AeBP%M_u$?_QJ90pHUKbLh*2~7a(j7@?7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_planet.png b/Resources/Textures/Objects/Misc/books.rsi/icon_planet.png new file mode 100644 index 0000000000000000000000000000000000000000..784dcd088276c9137effb2ec0f23e8d2e47f6ea1 GIT binary patch literal 311 zcmV-70m%M|P)Px#@kvBMR9J=Wl%Y-oK@f(&mV`E!CVhn_s1-FmgQ7_V2GCTkLswO7f`qKD=mT&H zQv+zkc`%3}P5K5n$l0sdwzP6Dxzx;;Og1yKGxKjYyPznFqA1FdX;dk))|SSYoi8F} zSyri6bdmSI1aNr@r`-@ce+R%h7u~NJv)NkvKm4i(lUKNV!6nxR`Pu{+W5Td$4!tKR zHuJiDT%NLjEwu;$;Osf9Nh8?zBm4ljm(i`N30RH8$!9n4`2d6aV1LW{1&9c~c!T*6 zMhUc+K}=ENe;0^Iy#lsR&0l=LNh<_bWBga=ARoig{{cl&lr^N|NgZmL0?7aX002ov JPDHLkV1j#Cfwcet literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_possum.png b/Resources/Textures/Objects/Misc/books.rsi/icon_possum.png new file mode 100644 index 0000000000000000000000000000000000000000..5d25c82a913203c1551f3c7fa9f270d585844150 GIT binary patch literal 337 zcmV-X0j~auP)Px$3rR#lR9J=Wlrc`jFcgMABTfJgBhAvi5>uA2F*9~39XNmwlQS?y3eFXpH8&`W zGen9Sa|Sb131qYj1Cl?sS1I>$}Z0MPfncP;N0D5VxtUlc{KICxI5Qp!Dlym5Cc0XThH>h*3iAvizD zzZ=3~7^v5~@5IeH_e0eCNqV;+%d$nRwe~Hszvz?)?O^`ZJ;bm0U)JBO1a2| j67&)M#?2#vuDnZ+#Gr}{hjowD3?DLd^2vQ&hKDg&|Lb^uf~nf*!T>O zOUo1?hJ@SOrUguWvcAJ3d;6+;N{367{#vi#FXpU$YWaM=R{eR4sX?=RU8aB4xLh9| zx|c1^jQQ7tzd8|mj!WzIyQ?Ooz4^T_tO@80>z_=_UacA%?LJoneZk=A>gTe~DWM4f Dt3qUB literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_scmmd.png b/Resources/Textures/Objects/Misc/books.rsi/icon_scmmd.png new file mode 100644 index 0000000000000000000000000000000000000000..d64e9bdd85d4cd9958b8c26e22bb29cddcfa15c3 GIT binary patch literal 280 zcmV+z0q6dSP)Px#(n&-?R9J=WmAwsvFc5{Gqcq-vf=RpqOVCr%x}~CL2{vF77v91pDWXFMN|1aA z7s-8%v25)7d3OdBMN$48le2l5hc(26y0Z8-x~9)MGmB~T>y`CSJaabJl?CvQ1c1$J z0B}uj==(MUI6Ty3kc5Zirx4TW{5AvVqGPCOE-@s_0Ddurorhnd0Z|HE(;MPx#$Vo&&R9J=W)4dA9FcgO2R}uWd&C$O>s23nEom~VMuhhkB=;ETII!Wm$Z7>{l zs)JbSA|!$!oM#CMoWpk#0!k^R{?T#~=AZU1YCc7`+nxDrl9%^C3$R>-`Ebyq*=iSO zSFto2^?bFCDz5#it_81Yx7*#@>U#^$0m`mozbW?%V9W&(%QzSTFb+nDSd6*+CP1_~ zRN~|e!2WmwAWqIin}hTFdj$Y6S&E3I-|al7&r+W@0C=Sa2&X~u4SlCqN-3oP9_lAb UA?^Pd$^ZZW07*qoM6N<$f-MGcKmY&$ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_stars.png b/Resources/Textures/Objects/Misc/books.rsi/icon_stars.png new file mode 100644 index 0000000000000000000000000000000000000000..0970ada3f314c9a5ce730976c12b3e3cb6dd3077 GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}`JOJ0ArY;~ z2@NIc9&fuehDvVDaxNWOBu&D7J!}Rqp zg+#5(bN<)=ytMQGi)jyqmd{l75L~V)v7yC$6SvZg{EOU+7&%)RJu7&4nl*TV1{-j- au>vi89Nit9H8U8$lHZ@!%p?R@~3;m-ao>net7IVaBRkSsklCei0CyYRFLi*>%n%4zH;|TNpfD{an^L HB{Ts5MMpXH literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_stunbaton.png b/Resources/Textures/Objects/Misc/books.rsi/icon_stunbaton.png new file mode 100644 index 0000000000000000000000000000000000000000..d247b9163c5ec989bbcd9a88a403d3520b7bf092 GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}g`O^sArY-_ zFCFA;FyLu@xV)l7)Y`eQ|Ixwf(w29R`1Bshi5;xIvryCRN$vM9{0kWb1p`$l?U{0B zYv`ljI;UTDO}}t-@^p#CcV2&}lVRBM(NB7Rk=uUR-e+?k{Saxod(%Dt!$Uj4@^d$j gFdi`RbNLd@FT$hGpz&@0X`tl{p00i_>zopr08AT73jhEB literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_temple.png b/Resources/Textures/Objects/Misc/books.rsi/icon_temple.png new file mode 100644 index 0000000000000000000000000000000000000000..54bb81a404bc969f4a946a27971db9cac58f3c8c GIT binary patch literal 381 zcmV-@0fPRCP)Px$H%UZ6R9J=Wl|f6xFc^m4&T6G4%5c@A6G7-1t%gdMd`vO=j7R#aI=oC{*!5k*nJbsGQxA=Ur@LIS+JzTu)3YH7MU#Q@-W#{hhF7D8-#XHKPM4m4(8qbh2o*22%fl?Z0oXK9dE&KO3ve#|Pj5A3ojZO5-`UapZN{D(5 z-}g-=erJH35mePmh4Ie~2q89EIQl08buB?i0LRgqy4@N0tUh*<0WG!@(7C&k51nf@ z3DIq6JPF2|kdOd*zEl}F@lG+B&iB6w0jRfcHqTL2D*)RKsgIU{l<{OTo&We58M&}n bECf#Y%1F(;t91oz~44$rjF6*2UngBIaMQ#89 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_text2.png b/Resources/Textures/Objects/Misc/books.rsi/icon_text2.png new file mode 100644 index 0000000000000000000000000000000000000000..c87370e75da7ab47f478555029d369060e6c806e GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}iJmTwArY-_ z&lz$x7znsr?7S{;F#5^A)Vv8n0{j9l8@fYPbTqdKtp1-c^X`Ns6^4e(8}my`zTbQ{ zUCGQ{)mH8O@j1m)mweteHE7l6O>Jf7fB#iB2A!I1cr4-mTRx!q>)9jt9Kv-U*M0?Z O7(8A5T-G@yGywpo3_fK5 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_text3.png b/Resources/Textures/Objects/Misc/books.rsi/icon_text3.png new file mode 100644 index 0000000000000000000000000000000000000000..ef03b24174fb8fe88f2928ac9140e8f4cc07956c GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}QJyZ2ArY;~ z2@xDe57lqL<#td*g0+Q#VSbi{p-ECw1JD)*Pgg&ebxsLQ0ML3f A!vFvP literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_time.png b/Resources/Textures/Objects/Misc/books.rsi/icon_time.png new file mode 100644 index 0000000000000000000000000000000000000000..13f0267eb5e36c53fccb7bdfb22450cc42aa5dee GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}?Vc`Ly?xOor3sz0DI&OMl*0Z1Hm6}JVJl<@&K-Yxt z@Yd7Y1t#wQ&!;$J`URWDjw^GF84gs}pZ6BHs9eKna@F9FMTzloppzIpUHx3vIVCg! E0LjBnPx$TS-JgR9J=Wl)p*?K@f+(h+-)=xfn^}DZwg|!W1#hA=n$m%FZW96GVK3fcOM< zmge#Zhm8b7IJnM$=j9;(8qvb`EM#K{7S4eN^uB7@*_q#OhuMXQh=_>)PAqI<#ViI| zJi%}@;Ie!dx{f%pVip4cC9cI2cXz>K~h6zAQPtt0(>GnMU>J1a$?Md}z28N>nrJW-FqlfSI@NPXyJ4J44Cb_)9 zlBSCdh-TpRZ4t+|NUvw;_C2p(`0G literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/icon_wrench.png b/Resources/Textures/Objects/Misc/books.rsi/icon_wrench.png new file mode 100644 index 0000000000000000000000000000000000000000..99bed38d3c63fa6d465ce7b904ad7c0c116e0b68 GIT binary patch literal 473 zcmV;~0Ve*5P)Px$lSxEDR9J=W(@!YmVHm*i@BC)`nPDgSQ?jCjY(!T6?AGjVjWiCE1GSmjV#xuy ztfIC(Y*V%^DXiLqqx;?*wmn!5lsydD%`}8G#4=d>!{X<0aNUEqxV@kA^E~hKKJW8_ zrfHg{-I3njj{?xq*(WM_#$6oV)$>WJYYOS?{vd&uex+RH2425vlNX-H@;UfO*1tWJ z$?-BdKVHBvoQ#e3Gc`T0RL`yw+!6>#DD;(Pe=1!q`=q$-6y%sVPx$2}wjjR9J=Wl(DVCFcd{^0+uD)a*y1yMixlH4s`Yolpcc4JEUNN-!TJiHh==5 z7@7p-M;~mFz^Cx3tmNhS+V|!Gi9{li_;#$I)qiV91aX`!U;Q?ZXkI*fZw1u=7Ox5j z@>d8QC%>K<$+Q);7}Pu!9*vPqkGbLy^H72EP1!CF?-DTyZ<`lGzdLV-o_`F-iH@yp z|Bz|0g6eMbzKF4D56*#(&GEdgvm8dUqR#UB=Ihhtr(F-N(nhlJx#*u90QyZGCq*w4 z_gUaLSx$ESMzYFrq91wxD2%bE&tgRYfbz|*&Sy}*0RX70FZVODQ@$zH)nk3StgN8b h=6GJk^i3qbf*YzoN_Lo^tlIzp002ovPDHLkV1gP?m4N^N literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/overlay_dirt.png b/Resources/Textures/Objects/Misc/books.rsi/overlay_dirt.png new file mode 100644 index 0000000000000000000000000000000000000000..43d2d741d7b1b07d665723de698d863a327db819 GIT binary patch literal 608 zcmV-m0-ybfP)Px%8c9S!R9J=Wma&TyK@`S+N3h1*(qg$ab)&W!}KqzD$7&5gO_b{55J@-?%&yq)ix_rCXb zVU0D`xL{lg4OCKWc()C=4z#ho|02K_qK)l&f_B`xsf}&>F(LcastRCs`kqJdR7tT7 z{8Umr15)5q=w?+*&;aU=q`+U5p;UlJ%o6gsh~lEEn9ve*fJg57p<6crqmAt~jE`kk;+e^l*)g7h|!`X^LU z#7c@p8@sEdxC>mv^TsMv&VWmM$36ZA+|B?Wc})uc*XMaSfSB*u1QZw;-5D2j067evu6{1-oD!MPx$0ZBwbR9J=Wl}`$SKop06h9H509za@pfiBc7x=jaa83irNbto955KNn%nK?6W zbfnFEAR-R$_wnW*;BYwpjK>{V9k;>{GXzokT_A}eiV{tgBZyLXNelp}uLmqw8*P2I zz~izL{qUb+@9_59TWjn4TmfA;0EnWL%E(x71?GY+&{TQ*rbk7$8&(8}?o@DPRIt|o z1qM;dS`S<)BY}Qlj)+wOlR)m+QByF~-k}4^x&TSYLc4Hxfw904Q`W^K0h3)ZivURw zV?@{o(er**{vX=~b_e`0vl>iH?ttu$;GTp=cY#g<_;f@!WyS)h{jDvmDo$OX@eYSW Z#|!wvcF89-BVzyn002ovPDHLkV1hqTf^q-= literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/books.rsi/paper_blood.png b/Resources/Textures/Objects/Misc/books.rsi/paper_blood.png new file mode 100644 index 0000000000000000000000000000000000000000..b0ed27ad528221596340e4986230f5725963c417 GIT binary patch literal 686 zcmV;f0#W^mP)Px%Xh}ptRCt{2nmcX+K@5hSzyX3*O1(uyOF@p1lcdanQszcm&{FLMR&W3iDUe|p z&BLCxcM$k%fC26Quh;VsHh^ImhG9G{FVr7?y?>mR#~rMO_GWy&2Ix9~P1D0_XkooR zkN@>O{0;;8`dod~LGADM))!TuZ1AUPPX5jNpef|D>%;<}r9Y<{$cY1-pGL@t1RQ;i zhu1PYbR!yUnjSVyFXDk502D-VDFY???pV%84t55(sYnfm6u8ia#(fV|PvkYgH9(n< zo2CaCI9KO+kb)HeG=NANDe;ifKYy2h0;~XVZkTGI6w;PRSci(HKxlr24oDOOrNrhq zH!Ibqfc93H^FY;S2Oz{0>%w-nfaP(Q)GqNbDgc~aVJxAWN5<%nJ_$~B7TC@fSr7Q$ zea%4*K*|8XG+ScI1L~_Sg0U2w>MX#S0-S6l2OzZw`CH@Vv*0lRpFVXVq{3PQVksEf z1#(lS6v|Xw4J}!poB{g-z}r0iBml0#*dJsAfcgNpLGXQO=^KV&7=~dOhG9GuMg0MO zZQdP2p0YmxL30n?ACM|f;Zu=Bt3O~=VI=f9RymS{QX5q350LsDm%Hc>$d#tG{~@m4 zACNME;CGstP_;h*P5AhxG|S_X{Q0ifjqh`vX$NNniAu4RR?^y+0sVYjd+e<1|>kKOkqK zv_4;3Ut|i5sn6HeXDxy;BU7}>-bH_aYa|A@K3`j(-5=nan5sTsTi-AY!+1pg0g~&n Uf3HS9=>Px#07*qoM6N<$f|4&U-v9sr literal 0 HcmV?d00001 diff --git a/Resources/migration.yml b/Resources/migration.yml index eadb76305c7..209434d1db8 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -227,7 +227,20 @@ Observationskit: null # 2024-02-26 CrateBaseWeldable: CrateGenericSteel -# 2024-03-7 +# 2024-03-05 +BookBotanicalTextbook: BookRandomStory +BookEscalation: BookRandomStory +BookEscalationSecurity: BookRandomStory +BookDemonomiconRandom: BookRandomStory +BookDemonomicon1: BookRandomStory +BookDemonomicon2: BookRandomStory +BookDemonomicon3: BookRandomStory +BookChemistryInsane: BookRandomStory +BookGnominomicon: BookRandomStory +BookFishing: BookRandomStory +BookDetective: BookRandomStory + +# 2024-03-07 AirlockExternalEasyPry: AirlockExternal AirlockExternalGlassEasyPry: AirlockExternalGlass AirlockGlassShuttleEasyPry: AirlockGlassShuttle From 144798ba7567b8b816e56cb6247196ec92978a2b Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Apr 2024 21:01:16 +0000 Subject: [PATCH 125/133] 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 de9f27a3d5e..614e1cdf6cd 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Tayrtahn - changes: - - message: Glued mobs can no longer struggle free from their captor's hands. - type: Tweak - id: 5789 - time: '2024-01-25T14:01:13.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24488 - author: mirrorcult changes: - message: Atomic Amnesia MMX, Vibe Ace, Monument and Marhaba have been removed @@ -3810,3 +3803,12 @@ id: 6288 time: '2024-04-01T20:48:02.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/24990 +- author: TheShuEd + changes: + - message: added procedurally generating books to library. + type: Add + - message: full books resprite + type: Add + id: 6289 + time: '2024-04-01T21:00:10.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25840 From 4b0f47c95bdc1bc656257d3f409ad526d4377a6c Mon Sep 17 00:00:00 2001 From: osjarw <62134478+osjarw@users.noreply.github.com> Date: Tue, 2 Apr 2024 03:50:43 +0300 Subject: [PATCH 126/133] Resprite ambuzol plus pills (#26651) --- .../Prototypes/Entities/Objects/Specific/Medical/healing.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml index 79ce03f7006..71e0cf64503 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml @@ -710,6 +710,10 @@ parent: Pill id: PillAmbuzolPlus components: + - type: Pill + pillType: 2 + - type: Sprite + state: pill3 - type: SolutionContainerManager solutions: food: From a4d46091e00c4fdf39bb1a932df9278a1c5ae211 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 2 Apr 2024 00:51:49 +0000 Subject: [PATCH 127/133] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 614e1cdf6cd..f65ac6f2e69 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: mirrorcult - changes: - - message: Atomic Amnesia MMX, Vibe Ace, Monument and Marhaba have been removed - as lobby music tracks - type: Remove - id: 5790 - time: '2024-01-25T14:08:29.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24480 - author: Dutch-VanDerLinde changes: - message: To prevent accidental spills, paper cups can no longer be quick-equipped. @@ -3812,3 +3804,12 @@ id: 6289 time: '2024-04-01T21:00:10.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25840 +- author: osjarw + changes: + - message: Ambuzol plus pills now have different sprites from ambuzol pills. Now + CBURN agents and Nukies who bought the syndicate zombie bundle might realize + that they also have some ambuzol plus. + type: Tweak + id: 6290 + time: '2024-04-02T00:50:43.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26651 From 5d31335f98307f590ed57c3c9d87d4bc5c4d688f Mon Sep 17 00:00:00 2001 From: osjarw <62134478+osjarw@users.noreply.github.com> Date: Tue, 2 Apr 2024 08:17:26 +0300 Subject: [PATCH 128/133] Fixed air injector visuals (#26654) --- .../Entities/Structures/Piping/Atmospherics/unary.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml index c2f8fa339da..03cce096994 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml @@ -206,7 +206,7 @@ visuals: # toggle color of the unshaded light: enum.OutletInjectorVisuals.Enabled: - unshaded: + enum.LightLayers.Unshaded: True: { color: "#5eff5e" } False: { color: "#990000" } - type: Appearance From 307a1c534dd132c8ce90b6fed79135452601a0b3 Mon Sep 17 00:00:00 2001 From: Simon <63975668+Simyon264@users.noreply.github.com> Date: Tue, 2 Apr 2024 07:18:31 +0200 Subject: [PATCH 129/133] Make cyborgs hands explosion proof. (#26515) * Make the advanced treatment modules beakers explosion-proof. * undo changes * Epic rename fail * Explosion recursion data field * Logic for data field --- Content.Server/Hands/Systems/HandsSystem.cs | 3 +++ Content.Shared/Hands/Components/HandsComponent.cs | 6 ++++++ .../Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml | 1 + 3 files changed, 10 insertions(+) diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 06946cd0522..bfd6393790a 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -75,6 +75,9 @@ private void GetComponentState(EntityUid uid, HandsComponent hands, ref Componen private void OnExploded(Entity ent, ref BeforeExplodeEvent args) { + if (ent.Comp.DisableExplosionRecursion) + return; + foreach (var hand in ent.Comp.Hands.Values) { if (hand.HeldEntity is { } uid) diff --git a/Content.Shared/Hands/Components/HandsComponent.cs b/Content.Shared/Hands/Components/HandsComponent.cs index 904b10b3bdc..f1f25a69f7a 100644 --- a/Content.Shared/Hands/Components/HandsComponent.cs +++ b/Content.Shared/Hands/Components/HandsComponent.cs @@ -31,6 +31,12 @@ public sealed partial class HandsComponent : Component ///

public List SortedHands = new(); + /// + /// If true, the items in the hands won't be affected by explosions. + /// + [DataField] + public bool DisableExplosionRecursion = false; + /// /// The amount of throw impulse per distance the player is from the throw target. /// diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index f04998388b8..df5b8d7519c 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -83,6 +83,7 @@ templateId: borg - type: Hands showInHands: false + disableExplosionRecursion: true - type: IntrinsicRadioReceiver - type: IntrinsicRadioTransmitter channels: From c9374969b10efbecb8600e204bb7d34785881aad Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 2 Apr 2024 05:18:33 +0000 Subject: [PATCH 130/133] 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 f65ac6f2e69..f64a5e293f7 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Dutch-VanDerLinde - changes: - - message: To prevent accidental spills, paper cups can no longer be quick-equipped. - type: Fix - id: 5791 - time: '2024-01-25T22:04:31.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24560 - author: Agoichi changes: - message: Bio hoods hiding your face now @@ -3813,3 +3806,10 @@ id: 6290 time: '2024-04-02T00:50:43.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26651 +- author: osjarw + changes: + - message: Air injectors finally have working indicator lights while enabled. + type: Fix + id: 6291 + time: '2024-04-02T05:17:26.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26654 From bd5031fd9e21d84fb174808dacdb8d26ff3ac7e7 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 2 Apr 2024 05:19:43 +0000 Subject: [PATCH 131/133] Automatic changelog update --- Resources/Changelog/Changelog.yml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f64a5e293f7..f0f945b18f3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,15 +1,4 @@ Entries: -- author: Agoichi - changes: - - message: Bio hoods hiding your face now - type: Tweak - - message: Bio hoods now can be used as a breath mask - type: Tweak - - message: You can not eat while you wearing bio hood - type: Tweak - id: 5792 - time: '2024-01-26T00:13:42.0000000+00:00' - url: https://api.github.com/repos/space-wizards/space-station-14/pulls/24570 - author: yathxyz changes: - message: Updated nixpkgs input @@ -3813,3 +3802,11 @@ id: 6291 time: '2024-04-02T05:17:26.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26654 +- author: Simyon + changes: + - message: The hands of cyborgs are now explosion proof. Your beakers wont ever + break again! + type: Fix + id: 6292 + time: '2024-04-02T05:18:31.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26515 From 838ff874f449d0cf6cb4109acbb68977162950e5 Mon Sep 17 00:00:00 2001 From: Morb0 <14136326+Morb0@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:03:12 +0300 Subject: [PATCH 132/133] Update locale --- .../fills/backpacks/startergear/backpack.ftl | 2 + .../catalog/fills/crates/engineering.ftl | 6 +- .../prototypes/catalog/fills/crates/fun.ftl | 3 - .../catalog/fills/items/briefcases.ftl | 10 +- .../entities/clothing/back/backpacks.ftl | 2 + .../clothing/head/hardsuit-helmets.ftl | 2 + .../entities/clothing/head/helmets.ftl | 2 - .../entities/clothing/head/misc.ftl | 6 +- .../entities/clothing/neck/misc.ftl | 2 - .../entities/clothing/outerclothing/armor.ftl | 2 - .../outerclothing/base_clothingouter.ftl | 5 + .../entities/clothing/outerclothing/coats.ftl | 16 ++ .../clothing/outerclothing/hardsuits.ftl | 2 + .../entities/clothing/uniforms/jumpsuits.ftl | 2 + .../prototypes/entities/effects/rcd.ftl | 22 +- .../entities/markers/spawners/jobs.ftl | 2 + .../entities/mobs/player/humanoid.ftl | 6 + .../consumable/drinks/drinks-cartons.ftl | 2 + .../objects/consumable/drinks/drinks.ftl | 30 +++ .../consumable/drinks/drinks_bottles.ftl | 2 + .../objects/consumable/drinks/drinks_cans.ftl | 2 + .../circuitboards/machine/production.ftl | 2 + .../devices/electronics/door_access.ftl | 99 ++++++++ .../entities/objects/misc/books.ftl | 11 +- .../entities/objects/misc/books_author.ftl | 62 +++++ .../entities/objects/misc/briefcases.ftl | 8 +- .../prototypes/entities/objects/misc/cds.ftl | 2 + .../entities/objects/misc/diskcases.ftl | 2 + .../objects/specific/janitorial/janitor.ftl | 9 - .../entities/objects/tools/tools.ftl | 14 +- .../weapons/guns/projectiles/projectiles.ftl | 4 + .../entities/structures/base_structure.ftl | 2 + .../structures/doors/airlocks/airlocks.ftl | 2 - .../doors/airlocks/base_structureairlocks.ftl | 4 + .../structures/doors/airlocks/external.ftl | 2 +- .../doors/shutter/blast_door_autolink.ftl | 12 + .../entities/structures/machines/nuke.ftl | 8 + .../structures/machines/reagent_grinder.ftl | 2 + .../entities/structures/plastic_flaps.ftl | 4 +- .../structures/specific/janitor/drain.ftl | 2 + .../structures/specific/janitor/janicart.ftl | 7 + .../storage/tanks/base_structuretanks.ftl | 2 + .../entities/structures/windows/mining.ftl | 2 +- .../entities/structures/windows/plasma.ftl | 2 +- .../entities/structures/windows/rplasma.ftl | 2 +- .../entities/structures/windows/ruranium.ftl | 2 +- .../entities/structures/windows/shuttle.ftl | 2 +- .../entities/structures/windows/uranium.ftl | 2 +- .../entities/structures/windows/window.ftl | 4 + Resources/Locale/ru-RU/accent/southern.ftl | 12 + .../ru-RU/administration/commands/aghost.ftl | 3 + Resources/Locale/ru-RU/anomaly/anomaly.ftl | 26 ++ .../ru-RU/chat/managers/chat-manager.ftl | 24 ++ .../components/hypospray-component.ftl | 9 +- .../components/scoopable-component.ftl | 1 + .../ru-RU/escape-menu/ui/options-menu.ftl | 2 + .../Locale/ru-RU/flavors/flavor-profiles.ftl | 7 + .../components/foldable-component.ftl | 2 + .../ghost/roles/ghost-role-component.ftl | 2 + Resources/Locale/ru-RU/guidebook/guides.ftl | 8 + .../Locale/ru-RU/job/job-description.ftl | 1 + Resources/Locale/ru-RU/job/job-names.ftl | 2 + .../Locale/ru-RU/lock/lock-component.ftl | 1 + .../ru-RU/machine-linking/receiver_ports.ftl | 2 + Resources/Locale/ru-RU/paint/paint.ftl | 8 - .../Locale/ru-RU/paper/story-generation.ftl | 222 ++++++++++++++++++ .../ru-RU/rcd/components/rcd-component.ftl | 45 +++- .../meta/consumable/drink/alcohol.ftl | 10 + .../reagents/meta/consumable/drink/drinks.ftl | 6 + .../reagents/meta/consumable/drink/soda.ftl | 6 + Resources/Locale/ru-RU/shell.ftl | 1 + .../fills/backpacks/startergear/backpack.ftl | 2 + .../prototypes/catalog/fills/crates/fun.ftl | 3 - .../entities/clothing/back/backpacks.ftl | 2 + .../clothing/head/hardsuit-helmets.ftl | 2 + .../entities/clothing/head/helmets.ftl | 2 - .../entities/clothing/head/misc.ftl | 6 +- .../entities/clothing/neck/misc.ftl | 2 - .../entities/clothing/outerclothing/armor.ftl | 2 - .../outerclothing/base_clothingouter.ftl | 5 + .../entities/clothing/outerclothing/coats.ftl | 16 ++ .../clothing/outerclothing/hardsuits.ftl | 2 + .../entities/clothing/uniforms/jumpsuits.ftl | 2 + .../prototypes/entities/effects/rcd.ftl | 22 +- .../entities/markers/spawners/jobs.ftl | 2 + .../entities/mobs/player/humanoid.ftl | 6 + .../consumable/drinks/drinks-cartons.ftl | 2 + .../objects/consumable/drinks/drinks.ftl | 30 +++ .../consumable/drinks/drinks_bottles.ftl | 2 + .../objects/consumable/drinks/drinks_cans.ftl | 2 + .../circuitboards/machine/production.ftl | 2 + .../devices/electronics/door_access.ftl | 99 ++++++++ .../entities/objects/misc/books.ftl | 7 +- .../entities/objects/misc/books_author.ftl | 62 +++++ .../entities/objects/misc/briefcases.ftl | 4 +- .../prototypes/entities/objects/misc/cds.ftl | 2 + .../entities/objects/misc/diskcases.ftl | 2 + .../objects/specific/janitorial/janitor.ftl | 9 - .../entities/objects/specific/syndicate.ftl | 2 + .../weapons/guns/projectiles/projectiles.ftl | 4 + .../objects/weapons/throwable/grenades.ftl | 1 + .../entities/structures/base_structure.ftl | 2 + .../structures/doors/airlocks/airlocks.ftl | 2 - .../doors/airlocks/base_structureairlocks.ftl | 4 + .../doors/shutter/blast_door_autolink.ftl | 12 + .../entities/structures/machines/nuke.ftl | 8 + .../structures/machines/reagent_grinder.ftl | 2 + .../structures/specific/janitor/drain.ftl | 2 + .../structures/specific/janitor/janicart.ftl | 7 + .../storage/tanks/base_structuretanks.ftl | 2 + .../entities/structures/windows/window.ftl | 4 + .../components/secret-stash-component.ftl | 1 + Resources/Locale/ru-RU/store/store.ftl | 1 + .../Locale/ru-RU/toilet/toilet-component.ftl | 1 + Resources/Locale/ru-RU/traits/traits.ftl | 2 + Resources/Locale/ru-RU/ui/general.ftl | 4 + 116 files changed, 1058 insertions(+), 106 deletions(-) create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/electronics/door_access.ftl create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/books_author.ftl create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/cds.ftl create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/diskcases.ftl create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/machines/nuke.ftl create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/specific/janitor/drain.ftl create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/specific/janitor/janicart.ftl create mode 100644 Resources/Locale/ru-RU/accent/southern.ftl create mode 100644 Resources/Locale/ru-RU/administration/commands/aghost.ftl create mode 100644 Resources/Locale/ru-RU/chemistry/components/scoopable-component.ftl delete mode 100644 Resources/Locale/ru-RU/paint/paint.ftl create mode 100644 Resources/Locale/ru-RU/paper/story-generation.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/electronics/door_access.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/books_author.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/cds.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/diskcases.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/machines/nuke.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/specific/janitor/drain.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/specific/janitor/janicart.ftl create mode 100644 Resources/Locale/ru-RU/ui/general.ftl diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/backpacks/startergear/backpack.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/backpacks/startergear/backpack.ftl index acb7153a981..8fa57649ffb 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/backpacks/startergear/backpack.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/backpacks/startergear/backpack.ftl @@ -56,6 +56,8 @@ ent-ClothingBackpackERTEngineerFilled = { ent-ClothingBackpackERTEngineer } .desc = { ent-ClothingBackpackERTEngineer.desc } ent-ClothingBackpackERTJanitorFilled = { ent-ClothingBackpackERTJanitor } .desc = { ent-ClothingBackpackERTJanitor.desc } +ent-ClothingBackpackERTChaplainFilled = { ent-ClothingBackpackERTChaplain } + .desc = { ent-ClothingBackpackERTChaplain.desc } ent-ClothingBackpackDeathSquadFilled = death squad backpack .desc = Holds the kit of CentComm's most feared agents. ent-ClothingBackpackCargoFilled = { ent-ClothingBackpackCargo } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/crates/engineering.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/crates/engineering.ftl index 5921e53eeaa..987bdffbccf 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/crates/engineering.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/crates/engineering.ftl @@ -22,9 +22,9 @@ ent-CrateAirlockKit = airlock kit .desc = A kit for building 6 airlocks, doesn't include tools. ent-CrateEvaKit = EVA kit .desc = A set consisting of two prestigious EVA suits and helmets. -ent-CrateRCDAmmo = RCD ammo crate - .desc = 3 RCD ammo, each restoring 5 charges. +ent-CrateRCDAmmo = compressed matter crate + .desc = Contains three compressed matter cartridges. ent-CrateRCD = RCD crate - .desc = A crate containing a single Rapid Construction Device. + .desc = A crate containing a single rapid construction device. ent-CrateParticleDecelerators = particle decelerators crate .desc = A crate containing 3 Particle Decelerators. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/crates/fun.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/crates/fun.ftl index 0a1cb2b90f9..4a5a2183120 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/crates/fun.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/crates/fun.ftl @@ -38,8 +38,5 @@ ent-CrateFunBikeHornImplants = bike horn implants .desc = A thousand honks a day keeps security officers away! ent-CrateFunMysteryFigurines = mystery figure crate .desc = A collection of 10 Mystery Figurine boxes. Duplicates non refundable. -ent-CrateFunSprayPaints = spray paint crate - .desc = a crate filled with spray paint. - .suffix = Spray Paint ent-CrateFunDartsSet = dartboard box set .desc = A box with everything you need for a fun game of darts. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/items/briefcases.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/items/briefcases.ftl index 748709bd238..74f47ccc4ac 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/items/briefcases.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/catalog/fills/items/briefcases.ftl @@ -1,12 +1,12 @@ -ent-BriefcaseBrownFilled = brown briefcase +ent-BriefcaseBrownFilled = { ent-BriefcaseBrown } .suffix = Filled, Paper .desc = { ent-BriefcaseBrown.desc } -ent-BriefcaseSyndieSniperBundleFilled = brown briefcase - .suffix = SniperBundle +ent-BriefcaseSyndieSniperBundleFilled = { ent-BriefcaseSyndie } + .suffix = Syndicate, Sniper Bundle .desc = { ent-BriefcaseSyndie.desc } -ent-BriefcaseSyndieLobbyingBundleFilled = brown briefcase +ent-BriefcaseSyndieLobbyingBundleFilled = { ent-BriefcaseSyndie } .suffix = Syndicate, Spesos .desc = { ent-BriefcaseSyndie.desc } -ent-BriefcaseThiefBribingBundleFilled = brown briefcase +ent-BriefcaseThiefBribingBundleFilled = { ent-BriefcaseSyndie } .suffix = Thief, Spesos .desc = { ent-BriefcaseSyndie.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/back/backpacks.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/back/backpacks.ftl index f958477bb0c..3f860796fc8 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/back/backpacks.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/back/backpacks.ftl @@ -46,6 +46,8 @@ ent-ClothingBackpackERTJanitor = ERT janitor backpack .desc = A spacious backpack with lots of pockets, worn by Janitors of an Emergency Response Team. ent-ClothingBackpackERTClown = ERT clown backpack .desc = A spacious backpack with lots of pockets, worn by Clowns of an Emergency Response Team. +ent-ClothingBackpackERTChaplain = ERT chaplain backpack + .desc = A spacious backpack with lots of pockets, worn by Chaplains of an Emergency Response Team. ent-ClothingBackpackSyndicate = syndicate backpack .desc = { ent-ClothingBackpack.desc } ent-ClothingBackpackHolding = bag of holding diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/hardsuit-helmets.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/hardsuit-helmets.ftl index a9919ae5d8e..2a0455ac9eb 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/hardsuit-helmets.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/hardsuit-helmets.ftl @@ -50,6 +50,8 @@ ent-ClothingHeadHelmetHardsuitPirateCap = pirate captain's hardsuit helmet .suffix = Pirate ent-ClothingHeadHelmetHardsuitERTLeader = ERT leader hardsuit helmet .desc = A special hardsuit helmet worn by members of an emergency response team. +ent-ClothingHeadHelmetHardsuitERTChaplain = ERT chaplain hardsuit helmet + .desc = A special hardsuit helmet worn by members of an emergency response team. ent-ClothingHeadHelmetHardsuitERTEngineer = ERT engineer hardsuit helmet .desc = A special hardsuit helmet worn by members of an emergency response team. ent-ClothingHeadHelmetHardsuitERTMedical = ERT medic hardsuit helmet diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/helmets.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/helmets.ftl index d8da80667d3..1fb5e78748d 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/helmets.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/helmets.ftl @@ -16,8 +16,6 @@ ent-ClothingHeadHelmetJanitorBombSuit = janitorial bombsuit helmet .suffix = DO NOT MAP ent-ClothingHeadHelmetCult = cult helmet .desc = A robust, evil-looking cult helmet. -ent-ClothingHeadHelmetScaf = scaf helmet - .desc = A robust, strong helmet. ent-ClothingHeadHelmetSpaceNinja = space ninja helmet .desc = What may appear to be a simple black garment is in fact a highly sophisticated nano-weave helmet. Standard issue ninja gear. ent-ClothingHeadHelmetTemplar = templar helmet diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/misc.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/misc.ftl index 569b706fbcd..ed3ac20cdb8 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/misc.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/misc.ftl @@ -4,10 +4,8 @@ ent-ClothingHeadHatCake = cake hat .desc = You put the cake on your head. Brilliant. ent-ClothingHeadHatChickenhead = chicken head .desc = It's a chicken head. Bok bok bok! -ent-ClothingHeadHatFlowerCrown = flower crown - .desc = A coronet of fresh and fragrant flowers. -ent-ClothingHeadHatHairflower = hairflower - .desc = A red flower for beautiful ladies. +ent-ClothingHeadHatFlowerWreath = flower wreath + .desc = A wreath of colourful flowers. Can be worn both on head and neck. ent-ClothingHeadHatPumpkin = pumpkin hat .desc = A jack o' lantern! Believed to ward off evil spirits. ent-ClothingHeadHatPwig = pwig diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/neck/misc.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/neck/misc.ftl index 50ee42a3f6a..91dd45c5294 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/neck/misc.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/neck/misc.ftl @@ -8,5 +8,3 @@ ent-ClothingNeckLawyerbadge = lawyer badge .desc = A badge to show that the owner is a 'legitimate' lawyer who passed the NT bar exam required to practice law. ent-ActionStethoscope = Listen with stethoscope .desc = { "" } -ent-ClothingNeckFlowerWreath = flower wreath - .desc = A wreath of colourful flowers. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/armor.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/armor.ftl index 429ab8a8d73..9c65d555cb2 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/armor.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/armor.ftl @@ -21,8 +21,6 @@ ent-ClothingOuterArmorMagusblue = blue magus armor .desc = An blue armored suit that provides good protection. ent-ClothingOuterArmorMagusred = red magus armor .desc = A red armored suit that provides good protection. -ent-ClothingOuterArmorScaf = scaf suit - .desc = A green and brown tactical suit for combat situations. ent-ClothingOuterArmorCaptainCarapace = captain's carapace .desc = An armored chestpiece that provides protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest. ent-ClothingOuterArmorChangeling = chitinous armor diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/base_clothingouter.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/base_clothingouter.ftl index b1ab48521c6..1d66236ff44 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/base_clothingouter.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/base_clothingouter.ftl @@ -4,6 +4,11 @@ ent-ClothingOuterBaseLarge = { ent-ClothingOuterBase } .desc = { ent-ClothingOuterBase.desc } ent-ClothingOuterStorageBase = { ent-ClothingOuterBase } .desc = { ent-ClothingOuterBase.desc } +ent-ClothingOuterStorageFoldableBase = { ent-ClothingOuterStorageBase } + .desc = { ent-ClothingOuterStorageBase.desc } +ent-ClothingOuterStorageFoldableBaseOpened = { ent-ClothingOuterStorageFoldableBase } + .suffix = opened + .desc = { ent-ClothingOuterStorageFoldableBase.desc } ent-ClothingOuterStorageToggleableBase = { ent-ClothingOuterStorageBase } .desc = { ent-ClothingOuterStorageBase.desc } ent-ClothingOuterHardsuitBase = base hardsuit diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/coats.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/coats.ftl index 72dafedfc0d..b295d474ac9 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/coats.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/coats.ftl @@ -14,20 +14,36 @@ ent-ClothingOuterCoatTrench = trench coat .desc = A comfy trench coat. ent-ClothingOuterCoatLab = lab coat .desc = A suit that protects against minor chemical spills. +ent-ClothingOuterCoatLabOpened = lab coat + .desc = { ent-ClothingOuterCoatLab.desc } ent-ClothingOuterCoatLabChem = chemist lab coat .desc = A suit that protects against minor chemical spills. Has an orange stripe on the shoulder. +ent-ClothingOuterCoatLabChemOpened = chemist lab coat + .desc = { ent-ClothingOuterCoatLabChem.desc } ent-ClothingOuterCoatLabViro = virologist lab coat .desc = A suit that protects against bacteria and viruses. Has an green stripe on the shoulder. +ent-ClothingOuterCoatLabViroOpened = virologist lab coat + .desc = { ent-ClothingOuterCoatLabViro.desc } ent-ClothingOuterCoatLabGene = geneticist lab coat .desc = A suit that protects against minor chemical spills. Has an blue stripe on the shoulder. +ent-ClothingOuterCoatLabGeneOpened = geneticist lab coat + .desc = { ent-ClothingOuterCoatLabGene.desc } ent-ClothingOuterCoatLabCmo = chief medical officer's lab coat .desc = Bluer than the standard model. +ent-ClothingOuterCoatLabCmoOpened = chief medical officer's lab coat + .desc = { ent-ClothingOuterCoatLabCmo.desc } ent-ClothingOuterCoatRnd = scientist lab coat .desc = A suit that protects against minor chemical spills. Has a purple stripe on the shoulder. +ent-ClothingOuterCoatRndOpened = scientist lab coat + .desc = { ent-ClothingOuterCoatRnd.desc } ent-ClothingOuterCoatRobo = roboticist lab coat .desc = More like an eccentric coat than a labcoat. Helps pass off bloodstains as part of the aesthetic. Comes with red shoulder pads. +ent-ClothingOuterCoatRoboOpened = roboticist lab coat + .desc = { ent-ClothingOuterCoatRobo.desc } ent-ClothingOuterCoatRD = research director lab coat .desc = Woven with top of the line technology, this labcoat helps protect against radiation in similar way to the experimental hardsuit. +ent-ClothingOuterCoatRDOpened = research director lab coat + .desc = { ent-ClothingOuterCoatRD.desc } ent-ClothingOuterCoatPirate = pirate garb .desc = Yarr. ent-ClothingOuterCoatWarden = warden's armored jacket diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/hardsuits.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/hardsuits.ftl index 553e4e983db..fa24b2303c8 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/hardsuits.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/outerclothing/hardsuits.ftl @@ -49,6 +49,8 @@ ent-ClothingOuterHardsuitPirateCap = pirate captain's hardsuit .desc = An ancient armored hardsuit, perfect for defending against space scurvy and toolbox-wielding scallywags. ent-ClothingOuterHardsuitERTLeader = ERT leader's hardsuit .desc = A protective hardsuit worn by the leader of an emergency response team. +ent-ClothingOuterHardsuitERTChaplain = ERT chaplain's hardsuit + .desc = A protective hardsuit worn by the chaplains of an Emergency Response Team. ent-ClothingOuterHardsuitERTEngineer = ERT engineer's hardsuit .desc = A protective hardsuit worn by the engineers of an emergency response team. ent-ClothingOuterHardsuitERTMedical = ERT medic's hardsuit diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/uniforms/jumpsuits.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/uniforms/jumpsuits.ftl index 44f16a25304..29e8ceca19c 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/uniforms/jumpsuits.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/uniforms/jumpsuits.ftl @@ -192,6 +192,8 @@ ent-ClothingUniformJumpsuitMonasticRobeLight = light monastic robe .desc = It's a light robe, often worn by religious folk. ent-ClothingUniformJumpsuitMusician = carpskin suit .desc = An luxurious suit made with only the finest scales, perfect for any lounge act! +ent-ClothingUniformJumpsuitERTChaplain = ERT chaplain uniform + .desc = A special suit made for Central Command's elite chaplain corps. ent-ClothingUniformJumpsuitERTEngineer = ERT engineering uniform .desc = A special suit made for the elite engineers under CentCom. ent-ClothingUniformJumpsuitERTJanitor = ERT janitorial uniform diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/effects/rcd.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/effects/rcd.ftl index 4217dee5c40..37a7e669feb 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/effects/rcd.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/effects/rcd.ftl @@ -1,2 +1,22 @@ -ent-EffectRCDConstruction = { "" } +ent-EffectRCDBase = { "" } .desc = { "" } +ent-EffectRCDDeconstructPreview = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDConstruct0 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDConstruct1 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDConstruct2 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDConstruct3 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDConstruct4 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDDeconstruct2 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDDeconstruct4 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDDeconstruct6 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDDeconstruct8 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/markers/spawners/jobs.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/markers/spawners/jobs.ftl index c8a5fff491e..33134932e6d 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/markers/spawners/jobs.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/markers/spawners/jobs.ftl @@ -81,6 +81,8 @@ ent-SpawnPointBrigmedic = brigmedic .desc = { ent-SpawnPointJobBase.desc } ent-SpawnPointERTLeader = ERTleader .desc = { ent-SpawnPointJobBase.desc } +ent-SpawnPointERTChaplain = ERTchaplain + .desc = { ent-SpawnPointJobBase.desc } ent-SpawnPointERTEngineer = ERTengineer .desc = { ent-SpawnPointJobBase.desc } ent-SpawnPointERTMedical = ERTmedical diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/humanoid.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/humanoid.ftl index 94c41f9f03a..b6f71cd9946 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/humanoid.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/humanoid.ftl @@ -10,6 +10,12 @@ ent-RandomHumanoidSpawnerERTLeaderEVA = ERT leader ent-RandomHumanoidSpawnerERTLeaderEVALecter = { ent-RandomHumanoidSpawnerERTLeaderEVA } .suffix = ERTRole, Lecter, EVA .desc = { ent-RandomHumanoidSpawnerERTLeaderEVA.desc } +ent-RandomHumanoidSpawnerERTChaplain = ERT chaplain + .suffix = ERTRole, Basic + .desc = { ent-RandomHumanoidSpawnerERTLeader.desc } +ent-RandomHumanoidSpawnerERTChaplainEVA = ERT chaplain + .suffix = ERTRole, Enviro EVA + .desc = { ent-RandomHumanoidSpawnerERTChaplain.desc } ent-RandomHumanoidSpawnerERTJanitor = ERT janitor .suffix = ERTRole, Basic .desc = { ent-RandomHumanoidSpawnerERTLeader.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks-cartons.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks-cartons.ftl index 7a76cdbedf0..f4564f46941 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks-cartons.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks-cartons.ftl @@ -10,6 +10,8 @@ ent-DrinkJuiceOrangeCarton = orange juice .desc = Full of vitamins and deliciousness! ent-DrinkJuiceTomatoCarton = tomato juice .desc = Well, at least it LOOKS like tomato juice. You can't tell with all that redness. +ent-DrinkCoconutWaterCarton = coconut water + .desc = It's the inside of the coconut that counts. ent-DrinkCreamCarton = milk cream .desc = It's cream. Made from milk. What else did you think you'd find in there? ent-DrinkMilkCarton = milk diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks.ftl index 57a79d02638..238ad595905 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks.ftl @@ -30,6 +30,9 @@ ent-DrinkAndalusia = { ent-DrinkGlass } ent-DrinkAntifreeze = { ent-DrinkGlass } .suffix = antifreeze .desc = { ent-DrinkGlass.desc } +ent-DrinkArnoldPalmer = { ent-DrinkGlass } + .suffix = arnold palmer + .desc = { ent-DrinkGlass.desc } ent-DrinkAtomicBombGlass = { ent-DrinkGlass } .suffix = atomic bomb .desc = { ent-DrinkGlass.desc } @@ -63,6 +66,9 @@ ent-DrinkBlueCuracaoGlass = { ent-DrinkGlass } ent-DrinkBloodyMaryGlass = { ent-DrinkGlass } .suffix = bloody mary .desc = { ent-DrinkGlass.desc } +ent-DrinkBlueHawaiianGlass = { ent-DrinkGlass } + .suffix = blue hawaiian + .desc = { ent-DrinkGlass.desc } ent-DrinkBooger = { ent-DrinkGlass } .suffix = booger .desc = { ent-DrinkGlass.desc } @@ -75,12 +81,21 @@ ent-DrinkCarrotJuice = { ent-DrinkGlass } ent-DrinkChocolateGlass = { ent-DrinkGlass } .suffix = chocolate .desc = { ent-DrinkGlass.desc } +ent-DrinkCoconutRum = { ent-DrinkGlass } + .suffix = coconut rum + .desc = { ent-DrinkGlass.desc } +ent-DrinkCoconutWaterGlass = { ent-DrinkGlass } + .suffix = coconut water + .desc = { ent-DrinkGlass.desc } ent-DrinkCoffee = { ent-DrinkGlass } .suffix = coffee .desc = { ent-DrinkGlass.desc } ent-DrinkCognacGlass = { ent-DrinkGlass } .suffix = cognac .desc = { ent-DrinkGlass.desc } +ent-DrinkCosmopolitan = { ent-DrinkGlass } + .suffix = cosmopolitan + .desc = { ent-DrinkGlass.desc } ent-DrinkCream = { ent-DrinkGlass } .suffix = cream .desc = { ent-DrinkGlass.desc } @@ -234,9 +249,15 @@ ent-DrinkNuclearColaGlass = { ent-DrinkGlass } ent-DrinkOrangeJuice = { ent-DrinkGlass } .suffix = orange juice .desc = { ent-DrinkGlass.desc } +ent-DrinkPainkillerGlass = { ent-DrinkGlass } + .suffix = painkiller + .desc = { ent-DrinkGlass.desc } ent-DrinkPatronGlass = { ent-DrinkGlass } .suffix = patron .desc = { ent-DrinkGlass.desc } +ent-DrinkPinaColadaGlass = { ent-DrinkGlass } + .suffix = piña colada + .desc = { ent-DrinkGlass.desc } ent-DrinkPoisonBerryJuice = { ent-DrinkGlass } .suffix = poison berry juice .desc = { ent-DrinkGlass.desc } @@ -261,6 +282,9 @@ ent-DrinkRootBeerFloatGlass = { ent-DrinkGlass } ent-DrinkRumGlass = { ent-DrinkGlass } .suffix = rum .desc = { ent-DrinkGlass.desc } +ent-DrinkRoyRogersGlass = { ent-DrinkGlass } + .suffix = roy rogers + .desc = { ent-DrinkGlass.desc } ent-DrinkSakeGlass = { ent-DrinkGlass } .suffix = sake .desc = { ent-DrinkGlass.desc } @@ -285,12 +309,18 @@ ent-DrinkMoonshineGlass = { ent-DrinkGlass } ent-DrinkGlassWhite = { ent-DrinkGlass } .suffix = milk .desc = { ent-DrinkGlass.desc } +ent-DrinkShirleyTempleGlass = { ent-DrinkGlass } + .suffix = shirley temple + .desc = { ent-DrinkGlass.desc } ent-DrinkSilencerGlass = { ent-DrinkGlass } .suffix = silencer .desc = { ent-DrinkGlass.desc } ent-DrinkSingulo = { ent-DrinkGlass } .suffix = singulo .desc = { ent-DrinkGlass.desc } +ent-DrinkSolDryGlass = { ent-DrinkGlass } + .suffix = sol dry + .desc = { ent-DrinkGlass.desc } ent-DrinkSnowWhite = { ent-DrinkGlass } .suffix = snow white .desc = { ent-DrinkGlass.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_bottles.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_bottles.ftl index cc5a0a66e13..829644c9d4d 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_bottles.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_bottles.ftl @@ -77,6 +77,8 @@ ent-DrinkMeadJug = mead jug .desc = storing mead in a plastic jug should be a crime. ent-DrinkIceJug = ice jug .desc = stubborn water. pretty cool. +ent-DrinkCoconutWaterJug = coconut water jug + .desc = It's on the inside of the coconut that counts. ent-DrinkCoffeeJug = coffee jug .desc = wake up juice, of the heated kind. ent-DrinkTeaJug = tea jug diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl index 363d165b61f..7c5efcba549 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl @@ -19,6 +19,8 @@ ent-DrinkSpaceMountainWindCan = space mountain wind can .desc = Blows right through you like a space wind. ent-DrinkSpaceUpCan = space-up can .desc = Tastes like a hull breach in your mouth. +ent-DrinkSolDryCan = sol dry + .desc = Sweet ginger soda from outer space! ent-DrinkStarkistCan = starkist can .desc = The taste of a star in liquid form. And, a bit of tuna...? ent-DrinkTonicWaterCan = tonic water can diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/circuitboards/machine/production.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/circuitboards/machine/production.ftl index d33853cd240..f1a28074125 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/circuitboards/machine/production.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/circuitboards/machine/production.ftl @@ -158,3 +158,5 @@ ent-ShuttleGunDusterCircuitboard = EXP-2100g "Duster" machine board .desc = A machine printed circuit board for an EXP-2100g "Duster" ent-ShuttleGunKineticCircuitboard = PTK-800 "Matter Dematerializer" machine board .desc = A machine printed circuit board for an PTK-800 "Matter Dematerializer" +ent-ReagentGrinderIndustrialMachineCircuitboard = industrial reagent grinder machine board + .desc = { ent-BaseMachineCircuitboard.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/electronics/door_access.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/electronics/door_access.ftl new file mode 100644 index 00000000000..4f31e573c29 --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/electronics/door_access.ftl @@ -0,0 +1,99 @@ +ent-DoorElectronicsService = { ent-DoorElectronics } + .suffix = Service, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsTheatre = { ent-DoorElectronics } + .suffix = Theatre, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsChapel = { ent-DoorElectronics } + .suffix = Chapel, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsJanitor = { ent-DoorElectronics } + .suffix = Janitor, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsKitchen = { ent-DoorElectronics } + .suffix = Kitchen, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsBar = { ent-DoorElectronics } + .suffix = Bar, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsHydroponics = { ent-DoorElectronics } + .suffix = Hydroponics, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsCaptain = { ent-DoorElectronics } + .suffix = Captain, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsExternal = { ent-DoorElectronics } + .suffix = External, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsCargo = { ent-DoorElectronics } + .suffix = Cargo, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsEngineering = { ent-DoorElectronics } + .suffix = Engineering, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsAtmospherics = { ent-DoorElectronics } + .suffix = Atmospherics, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsFreezer = { ent-DoorElectronics } + .suffix = Freezer, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsSalvage = { ent-DoorElectronics } + .suffix = Salvage, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsMedical = { ent-DoorElectronics } + .suffix = Medical, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsChemistry = { ent-DoorElectronics } + .suffix = Chemistry, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsResearch = { ent-DoorElectronics } + .suffix = Research, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsScience = { ent-DoorElectronics } + .suffix = Science, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsCommand = { ent-DoorElectronics } + .suffix = Command, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsChiefMedicalOfficer = { ent-DoorElectronics } + .suffix = ChiefMedicalOfficer, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsChiefEngineer = { ent-DoorElectronics } + .suffix = ChiefEngineer, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsHeadOfSecurity = { ent-DoorElectronics } + .suffix = HeadOfSecurity, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsResearchDirector = { ent-DoorElectronics } + .suffix = ResearchDirector, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsHeadOfPersonnel = { ent-DoorElectronics } + .suffix = HeadOfPersonnel, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsQuartermaster = { ent-DoorElectronics } + .suffix = Quartermaster, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsSecurity = { ent-DoorElectronics } + .suffix = Security, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsDetective = { ent-DoorElectronics } + .suffix = Detective, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsBrig = { ent-DoorElectronics } + .suffix = Brig, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsArmory = { ent-DoorElectronics } + .suffix = Armory, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsVault = { ent-DoorElectronics } + .suffix = Vault, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsMaintenance = { ent-DoorElectronics } + .suffix = Maintenance, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsSyndicateAgent = { ent-DoorElectronics } + .suffix = SyndicateAgent, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsRnDMed = { ent-DoorElectronics } + .suffix = Medical/Science, Locked + .desc = { ent-DoorElectronics.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/books.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/books.ftl index b0858dce50b..142e3713e9e 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/books.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/books.ftl @@ -27,12 +27,11 @@ ent-BookHowToSurvive = how to survive ent-BookChemicalCompendium = chempendium .desc = A comprehensive guide written by some old skeleton of a professor about chemical synthesis. ent-BookRandom = { ent-BookBase } - .suffix = random - .desc = { ent-BookBase.desc } -ent-BookEscalation = Robert's Rules of Escalation - .desc = The book is stained with blood. It seems to have been used more as a weapon than reading material. -ent-BookEscalationSecurity = Robert's Rules of Escalation: Security Edition - .desc = The book is stained with blood. It seems to have been used more as a weapon than reading material. + .desc = Each book is unique! What is hidden in this one? + .suffix = random visual +ent-BookRandomStory = { ent-BookRandom } + .suffix = random visual, random story + .desc = { ent-BookRandom.desc } ent-BookAtmosDistro = Newton's Guide to Atmos: The Distro .desc = There are endless illegible notes scribbled in the margins. Most of the text is covered in handwritten question marks. ent-BookAtmosWaste = Newton's Guide to Atmos: Waste diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/books_author.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/books_author.ftl new file mode 100644 index 00000000000..dbd557ac5be --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/books_author.ftl @@ -0,0 +1,62 @@ +ent-BookNarsieLegend = the legend of nar'sie + .desc = The book is an old, leather-bound tome with intricate engravings on the cover. The pages are yellowed and fragile with age, with the ink of the text faded in some places. It appears to have been well-read and well-loved, with dog-eared pages and marginalia scrawled in the margins. Despite its aged appearance, the book still exudes a sense of mystical power and wonder, hinting at the secrets and knowledge contained within its pages. +ent-BookTruth = exploring different philosophical perspectives on truth and the complexity of lying + .desc = A book exploring the different philosophical perspectives on truth and lying has a worn cover, with creases and marks indicating frequent use and thoughtful contemplation. The spine shows signs of wear from being pulled off the shelf again and again. The pages themselves are filled with underlines, notes in the margins, and highlighted passages as readers grapple with the nuances and complexities of the topic. +ent-BookWorld = shaping the state of the world - interplay of forces and choices + .desc = The book is a well-preserved hardcover with a simple, elegant design on the cover, depicting the image of a world in motion. The pages are crisp and clean, with no signs of wear or tear, suggesting that it has been well-cared for and valued by its previous owner. The text is printed in a clear, legible font, and the chapters are organized in a logical and easy-to-follow manner, making it accessible to readers of all levels of expertise. +ent-BookIanAntarctica = adventures of robert & ian - exploring antarctica + .desc = The book is a small paperback in good condition, with an illustration of Ian the corgi and the colony of penguins on the cover. The title, "Ian and Robert's Antarctic Adventure", is written in bold white letters against a blue background. The back cover features a brief summary of the story, highlighting the themes of humility, resilience, and the beauty of nature. +ent-BookSlothClownSSS = the sloth and the clown - space station shenanigans + .desc = The book looks new, with a glossy cover featuring Chuckles the clown and Snuggles the sloth floating in space with a backdrop of stars and planets. Chuckles is dressed in his banana costume and Snuggles is sleeping on a hammock made of space ropes. The title "The Sloth and the Clown - Space Station Shenanigans" is written in bold and colorful letters. +ent-BookSlothClownPranks = the sloth and the clown - pranks on zorgs + .desc = The book is in excellent condition, with crisp pages and a bright cover. The cover of the book features Chuckles and Snuggles, surrounded by the different species they encountered during their adventures in space. In the background, the Zorgs can be seen peeking out from behind a spaceship. +ent-BookSlothClownMMD = the sloth and the clown - maze maze danger + .desc = The book looks new and vibrant, with an image of Chuckles and Snuggles standing in front of the changing maze on the cover. The title "The Sloth and the Clown - Maze Maze Danger" is written in bold, colorful letters that pop against a background of space and stars. +ent-BookStruck = the humbling and transformative experience of being struck by lightning + .desc = The cover of the book is an electrifying image of lightning striking the ground, with a silhouette of a person standing in the midst of it. The title is written in bold letters in white against a black background, conveying the power and intensity of the experience. The subtitle is written in smaller letters below the title, providing a hint of the philosophical and spiritual themes explored within. +ent-BookSun = reaching for the sun - a plant's quest for life + .desc = The book is new, with a bright and vibrant cover featuring a plant stretching its leaves towards the sun. The title, "Reaching for the Sun - A Plant's Quest for Life," is written in bold, green letters, with an image of the sun rising behind the plant. The cover evokes a sense of growth, energy, and the beauty of nature. +ent-BookPossum = fallen ambitions - the tragic tale of morty the possum + .desc = The book is in good condition, with a hardcover and a dark green forest background. In the center of the cover, there is a sad looking possum sitting on a branch, with a distant and lonely expression on its face. The title, "Fallen Ambitions - The Tragic Tale of Morty the Possum," is written in bold, gold letters above the possum. +ent-BookCafe = the cafe possum + .desc = The book is in new condition, with a vibrant and whimsical cover that features a charming illustration of a tiny possum peeking out from behind a coffee cup, with a colorful and bustling cafe scene in the background. The title "The Cafe Possum" is written in bold, playful lettering, and the author's name is printed in a smaller font below it. +ent-BookFeather = a feather of magic - the wandering bird's journey to belonging + .desc = The book would be in new condition, with a glossy cover depicting the wandering bird surrounded by a glowing forest, with the magical feather at the center. The title, "A Feather of Magic," would be written in bold, glittering letters, while the subtitle, "The Wandering Bird's Journey to Belonging," would be written in smaller print underneath. The back cover would feature a brief summary of the story, along with reviews from critics praising the book's themes of hope and renewal. +ent-BookIanLostWolfPup = the adventures of ian and renault - finding the lost wolf pup + .desc = The book is a new condition with a colorful cover, depicting Ian the corgi and Renault the fox on a journey through the forest, with the lost wolf pup to their feet. The title "The Adventures of Ian and Renault - Finding the Lost Wolf Pup" is prominently displayed at the top, with the author's name below. The cover has a whimsical and adventurous feel to it, attracting readers of all ages. +ent-BookIanRanch = the adventures of ian and renault - ranch expedition + .desc = The book appears to be new, with crisp pages and an unblemished cover. The cover features a colorful illustration of Ian and Renault, surrounded by various animals they encountered on the ranch, including horses, cows, and chickens. The title, "The Adventures of Ian and Renault - Ranch Expedition," is written in bold letters above the image, with the subtitle, "Helping Animals in Need," written below. +ent-BookIanOcean = the adventures of ian and renault - an ocean adventure + .desc = The book is new and in excellent condition. The cover shows Ian and Renault running and playing on the beach, with the blue ocean and golden sand in the background. The title is written in bold, playful letters, and the subtitle reads "An Ocean Adventure." +ent-BookIanMountain = the adventures of ian and renault - A mountain expedition + .desc = The book is in new condition. The cover is a stunning mountain landscape with Ian and Renault in the foreground, looking out over the vista of the surrounding peaks and valleys. The title is written in bold, block letters at the top, with the subtitle, "A Mountain Expedition," written underneath. +ent-BookIanCity = the adventures of ian and renault - exploring the city + .desc = The book is in new condition, with crisp pages and a glossy cover. The cover features a colorful illustration of Ian and Renault exploring the city, with tall buildings and bustling streets in the background. Ian is leading the way, with his tail wagging excitedly, while Renault follows close behind, her ears perked up and her eyes wide with wonder. The title, "The Adventures of Ian and Renault," is written in bold, playful letters, with the subtitle, "Exploring the City," written below in smaller font. +ent-BookIanArctic = the adventures of ian and renault - an arctic journey of courage and friendship + .desc = The book looks new and adventurous, with a picture of Ian and Renault standing in front of an icy landscape with snowflakes falling all around them. The title, "The Adventures of Ian and Renault," is written in bold letters at the top, with a subtitle that reads, "An Arctic Journey of Courage and Friendship." +ent-BookIanDesert = the adventures of ian and renault - exploring the mysterious desert + .desc = The book is in new condition and would have a colorful cover depicting Ian and Renault against a desert backdrop. The cover would feature images of various animals and plants that the two encountered on their adventure, such as a rattlesnake, coyotes, sand dunes, and an oasis. The title, "The Adventures of Ian and Renault" is prominently displayed on the cover in bold letters, while the subtitle "Exploring the Mysterious Desert" is written in smaller letters underneath. +ent-BookNames = the power of names - a philosophical exploration + .desc = The book is a gently used philosophy text, with a cover that features a close-up of a person's mouth, with the word "names" written on their lips. The title is "The Power of Names - A Philosophical Exploration," and the author's name is prominently displayed underneath. The overall design is simple and elegant, with the focus on the text rather than any flashy graphics or images. +ent-BookEarth = earthly longing + .desc = The book is in good condition, with a slightly faded cover due to exposure to sunlight. The cover of the book depicts a panoramic view of the Earth from space, with a bright blue ocean and green landmasses. In the foreground, a lone astronaut is seen sitting in front of a window, gazing wistfully at the Earth. The title of the book, "Earthly Longing," is written in bold white letters against a black background at the top of the cover. +ent-BookAurora = journey beyond - the starship aurora mission + .desc = The book is in excellent condition, with a shiny cover depicting a spaceship hovering above a planet, perhaps with the Earth in the background. The title "Journey Beyond - The Starship Aurora Mission" is written in bold, silver letters. The cover also features a quote from a review, "A breathtaking tale of human achievement and exploration" to entice potential readers. +ent-BookTemple = the nature of the divine - embracing the many gods + .desc = The book appears new with crisp pages and an uncreased spine. The cover features an image of a temple with a glowing, multicolored aura around it, symbolizing the various gods discussed in the book. The title is displayed prominently in gold lettering, with the author's name and a brief summary of the book written in smaller text below. +ent-BookWatched = watched + .desc = The book is in good condition, with a slightly worn cover that features a dark and ominous space station looming in the background. The title "Watched" is written in bold letters that seem to be staring back at the reader, conveying the feeling of being constantly observed. The blurb on the back cover hints at a thrilling and suspenseful tale of paranoia and danger in a confined setting. +ent-BookMedicalOfficer = horizon's battle - a medical officer's tale of trust and survival + .desc = The cover features Smith, the medical officer, in his uniform, looking determined and ready to face any challenge. The backdrop shows the SS Horizon under attack, with explosions and smoke filling the space station. In the foreground, a wizard with a staff can be seen, adding an element of mystery and intrigue to the scene. The title is prominently displayed in bold letters, with the author's name and a tagline indicating the book's action-packed and suspenseful nature. +ent-BookMorgue = the ghostly residents of the abandoned morgue + .desc = The book looks old and worn, with faded lettering on the cover. The cover depicts a dark and eerie morgue, with a full moon casting an ominous glow over the scene. In the foreground are Morty the possum and Morticia the raccoon, with mischievous expressions on their faces, peeking out from behind a metal shelf. The title is written in bold, spooky letters, with the subtitle "A Tale of Animal Spirits" written in smaller font below. +ent-BookRufus = rufus and the mischievous fairy + .desc = The book is in new condition, with vibrant colors and illustrations on the cover. The cover shows Rufus on his bicycle, with Blossom flying beside him in a playful manner. The title is written in bold, whimsical font, with the characters' names highlighted in a contrasting color. The overall aesthetic is charming and inviting, appealing to children and adults alike. +ent-BookMap = the map of adventure + .desc = The book is in a good condition, with a glossy cover depicting a jungle scene with vibrant colors and intricate details. The title "The Map of Adventure," is written in bold, gold lettering. The cover also features an image of a mysterious suitcase with the map spilling out of it. +ent-BookJourney = a journey of music, mountains, and self-discovery + .desc = The book is in excellent condition, with crisp pages and a glossy cover. The cover features a striking image of a mountain range, with a silhouette of a climber with a guitar on their back in the foreground. The title is bold and eye-catching, with the subtitle "A Journey of Music, Mountains, and Self-Discovery." +ent-BookInspiration = finding inspiration - a writer's journey through the woods + .desc = The book is in a new condition with a cover depicting a serene forest scene with a waterfall and colorful wildflowers. The title of the book "Finding Inspiration - A Writer's Journey Through the Woods" and the author's name are prominently displayed at the bottom. +ent-BookJanitorTale = the tales of a tired janitor + .desc = A clean looking book, smelling vaguely of soap and bleach. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/briefcases.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/briefcases.ftl index a694534bb4c..b37a3326c44 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/briefcases.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/briefcases.ftl @@ -1,9 +1,7 @@ ent-BriefcaseBase = { ent-BaseStorageItem } .desc = Useful for carrying items in your hands. ent-BriefcaseBrown = brown briefcase - .desc = A handy briefcase. -ent-BriefcaseSyndieBase = { ent-BriefcaseBase } - .desc = Useful for carrying items in your hands. + .desc = { ent-BriefcaseBase.desc } +ent-BriefcaseSyndie = { ent-BriefcaseBrown } .suffix = Syndicate, Empty -ent-BriefcaseSyndie = brown briefcase - .desc = A handy briefcase. + .desc = { ent-BriefcaseBrown.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/cds.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/cds.ftl new file mode 100644 index 00000000000..23434de6c4a --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/cds.ftl @@ -0,0 +1,2 @@ +ent-CoordinatesDisk = coordinates disk + .desc = A disk containing the coordinates to a location in space. Necessary for any FTL-traversing vessel to reach their destination. Fits inside shuttle consoles. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/diskcases.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/diskcases.ftl new file mode 100644 index 00000000000..e4000db46f5 --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/misc/diskcases.ftl @@ -0,0 +1,2 @@ +ent-DiskCase = diskcase + .desc = Case for storing a coordinates disk. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/specific/janitorial/janitor.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/specific/janitorial/janitor.ftl index c49cce472f7..46a43f0361f 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/specific/janitorial/janitor.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/specific/janitorial/janitor.ftl @@ -2,20 +2,11 @@ ent-MopItem = mop .desc = A mop that can't be stopped, viscera cleanup detail awaits. ent-AdvMopItem = advanced mop .desc = Motorized mop that has a bigger reservoir and quickly replaces reagents inside with water. Automatic Clown Countermeasure not included. -ent-MopBucket = mop bucket - .desc = Holds water and the tears of the janitor. -ent-MopBucketFull = mop bucket - .suffix = full - .desc = { ent-MopBucket.desc } ent-WetFloorSign = wet floor sign .desc = Caution! Wet Floor! ent-WetFloorSignMineExplosive = { ent-WetFloorSign } .suffix = Explosive .desc = { ent-WetFloorSign.desc } -ent-JanitorialTrolley = janitorial trolley - .desc = This is the alpha and omega of sanitation. -ent-FloorDrain = drain - .desc = Drains puddles around it. Useful for dumping mop buckets or keeping certain rooms clean. ent-Plunger = plunger .desc = A plunger with a red plastic suction-cup and a wooden handle. Used to unclog drains. ent-RagItem = damp rag diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/tools/tools.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/tools/tools.ftl index 50b5368f4c4..ec59a1f7e90 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/tools/tools.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/tools/tools.ftl @@ -15,18 +15,18 @@ ent-NetworkConfigurator = network configurator ent-PowerDrill = power drill .desc = A simple powered hand drill. ent-RCD = RCD - .desc = An advanced construction device which can place/remove walls, floors, and airlocks quickly. + .desc = The rapid construction device can be used to quickly place and remove various station structures and fixtures. Requires compressed matter to function. ent-RCDEmpty = { ent-RCD } .suffix = Empty .desc = { ent-RCD.desc } -ent-RCDRecharging = experimental rcd - .desc = A bluespace-enhanced RCD that regenerates charges passively. +ent-RCDRecharging = experimental RCD + .desc = A bluespace-enhanced rapid construction device that passively generates its own compressed matter. .suffix = AutoRecharge -ent-RCDExperimental = experimental rcd - .desc = A bluespace-enhanced RCD that regenerates charges passively. +ent-RCDExperimental = experimental RCD + .desc = A bluespace-enhanced rapid construction device that passively generates its own compressed matter. .suffix = Admeme -ent-RCDAmmo = RCD Ammo - .desc = Ammo cartridge for an RCD. +ent-RCDAmmo = compressed matter + .desc = A cartridge of raw matter compacted by bluespace technology. Used in rapid construction devices. ent-Omnitool = omnitool .desc = A drone's best friend. ent-Shovel = shovel diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/weapons/guns/projectiles/projectiles.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/weapons/guns/projectiles/projectiles.ftl index ca2401f3e2b..d636bea5432 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/weapons/guns/projectiles/projectiles.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/weapons/guns/projectiles/projectiles.ftl @@ -46,6 +46,10 @@ ent-AnomalousParticleZetaStrong = { ent-AnomalousParticleZeta } .desc = { ent-AnomalousParticleZeta.desc } ent-AnomalousParticleOmegaStrong = omega particles .desc = { ent-AnomalousParticleDelta.desc } +ent-AnomalousParticleSigma = sigma particles + .desc = { ent-AnomalousParticleDelta.desc } +ent-AnomalousParticleSigmaStrong = sigma particles + .desc = { ent-AnomalousParticleSigma.desc } ent-BulletRocket = rocket .desc = { ent-BaseBulletTrigger.desc } ent-BulletWeakRocket = weak rocket diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/base_structure.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/base_structure.ftl index 887761c9f3e..d38b843a347 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/base_structure.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/base_structure.ftl @@ -2,3 +2,5 @@ ent-BaseStructure = { "" } .desc = { "" } ent-BaseStructureDynamic = { ent-BaseStructure } .desc = { ent-BaseStructure.desc } +ent-StructureWheeled = { "" } + .desc = { "" } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/airlocks.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/airlocks.ftl index 23386679bb2..8a74f6731c0 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/airlocks.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/airlocks.ftl @@ -43,8 +43,6 @@ ent-AirlockHatch = airtight hatch .desc = { ent-Airlock.desc } ent-AirlockHatchMaintenance = maintenance hatch .desc = { ent-Airlock.desc } -ent-AirlockGlass = glass airlock - .desc = { ent-Airlock.desc } ent-AirlockEngineeringGlass = { ent-AirlockGlass } .suffix = Engineering .desc = { ent-AirlockGlass.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/base_structureairlocks.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/base_structureairlocks.ftl index 81876b6d6a1..75fe77ca9fa 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/base_structureairlocks.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/base_structureairlocks.ftl @@ -1,2 +1,6 @@ ent-Airlock = airlock .desc = It opens, it closes, and maybe crushes you. +ent-AirlockRCDResistant = { ent-Airlock } + .desc = { ent-Airlock.desc } +ent-AirlockGlass = glass airlock + .desc = { ent-Airlock.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/external.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/external.ftl index 290aedf2c55..8442c07603d 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/external.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/airlocks/external.ftl @@ -1,4 +1,4 @@ -ent-AirlockExternal = { ent-Airlock } +ent-AirlockExternal = { ent-AirlockRCDResistant } .desc = It opens, it closes, it might crush you, and there might be only space behind it. .suffix = External ent-AirlockExternalGlass = { ent-AirlockExternal } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/shutter/blast_door_autolink.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/shutter/blast_door_autolink.ftl index 1a01017f5e7..27bf0dfbc36 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/shutter/blast_door_autolink.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/shutter/blast_door_autolink.ftl @@ -22,6 +22,18 @@ ent-BlastDoorBridge = { ent-BlastDoor } ent-BlastDoorBridgeOpen = { ent-BlastDoorOpen } .suffix = Open, Autolink, Bridge .desc = { ent-BlastDoorOpen.desc } +ent-BlastDoorEngineering = { ent-BlastDoor } + .suffix = Autolink, Engineering + .desc = { ent-BlastDoor.desc } +ent-BlastDoorEngineeringOpen = { ent-BlastDoorOpen } + .suffix = Open, Autolink, Engineering + .desc = { ent-BlastDoorOpen.desc } +ent-BlastDoorScience = { ent-BlastDoor } + .suffix = Autolink, Science + .desc = { ent-BlastDoor.desc } +ent-BlastDoorScienceOpen = { ent-BlastDoorOpen } + .suffix = Open, Autolink, Science + .desc = { ent-BlastDoorOpen.desc } ent-BlastDoorWindows = { ent-BlastDoor } .suffix = Autolink, Windows .desc = { ent-BlastDoor.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/machines/nuke.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/machines/nuke.ftl new file mode 100644 index 00000000000..1968816c7bc --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/machines/nuke.ftl @@ -0,0 +1,8 @@ +ent-NuclearBomb = nuclear fission explosive + .desc = You probably shouldn't stick around to see if this is armed. +ent-NuclearBombUnanchored = { ent-NuclearBomb } + .suffix = unanchored + .desc = { ent-NuclearBomb.desc } +ent-NuclearBombKeg = nuclear fission explosive + .desc = You probably shouldn't stick around to see if this is armed. It has a tap on the side. + .suffix = keg diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/machines/reagent_grinder.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/machines/reagent_grinder.ftl index 9cf50ed2aa0..999e504a735 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/machines/reagent_grinder.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/machines/reagent_grinder.ftl @@ -1,3 +1,5 @@ ent-KitchenReagentGrinder = reagent grinder .desc = From BlenderTech. Will It Blend? Let's find out! .suffix = grinder/juicer +ent-ReagentGrinderIndustrial = industrial reagent grinder + .desc = An industrial reagent grinder. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/plastic_flaps.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/plastic_flaps.ftl index 8bb210e36ec..15bdef40b18 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/plastic_flaps.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/plastic_flaps.ftl @@ -6,7 +6,7 @@ ent-PlasticFlapsOpaque = plastic flaps .suffix = Opaque ent-PlasticFlapsAirtightClear = airtight plastic flaps .desc = Heavy duty, slightly stronger, airtight plastic flaps. Definitely can't get past those. No way. - .suffix = Airtight Clear + .suffix = Airtight, Clear ent-PlasticFlapsAirtightOpaque = airtight plastic flaps .desc = Heavy duty, slightly stronger, airtight plastic flaps. Definitely can't get past those. No way. - .suffix = Airtight Opaque + .suffix = Airtight, Opaque diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/specific/janitor/drain.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/specific/janitor/drain.ftl new file mode 100644 index 00000000000..e775fbcd086 --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/specific/janitor/drain.ftl @@ -0,0 +1,2 @@ +ent-FloorDrain = drain + .desc = Drains puddles around it. Useful for dumping mop buckets or keeping certain rooms clean. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/specific/janitor/janicart.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/specific/janitor/janicart.ftl new file mode 100644 index 00000000000..785a94d510a --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/specific/janitor/janicart.ftl @@ -0,0 +1,7 @@ +ent-MopBucket = mop bucket + .desc = Holds water and the tears of the janitor. +ent-MopBucketFull = mop bucket + .suffix = full + .desc = { ent-MopBucket.desc } +ent-JanitorialTrolley = janitorial trolley + .desc = This is the alpha and omega of sanitation. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/storage/tanks/base_structuretanks.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/storage/tanks/base_structuretanks.ftl index 03bc356a464..5e1d055c3a0 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/storage/tanks/base_structuretanks.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/storage/tanks/base_structuretanks.ftl @@ -1,2 +1,4 @@ ent-StorageTank = storage tank .desc = A liquids storage tank. +ent-StorageTankBig = { ent-StorageTank } + .desc = { ent-StorageTank.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/mining.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/mining.ftl index 067a50ec9c5..2ee7660beac 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/mining.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/mining.ftl @@ -1,5 +1,5 @@ ent-MiningWindow = mining window - .desc = { ent-Window.desc } + .desc = { ent-WindowRCDResistant.desc } ent-MiningWindowDiagonal = { ent-ShuttleWindow } .suffix = diagonal .desc = { ent-ShuttleWindow.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/plasma.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/plasma.ftl index 995d3a3ba1c..126d0a30268 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/plasma.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/plasma.ftl @@ -1,5 +1,5 @@ ent-PlasmaWindow = plasma window - .desc = { ent-Window.desc } + .desc = { ent-WindowRCDResistant.desc } ent-PlasmaWindowDirectional = directional plasma window .desc = Don't smudge up the glass down there. ent-PlasmaWindowDiagonal = { ent-PlasmaWindow } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/rplasma.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/rplasma.ftl index b92158ed2b6..e047dc7a508 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/rplasma.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/rplasma.ftl @@ -1,5 +1,5 @@ ent-ReinforcedPlasmaWindow = reinforced plasma window - .desc = { ent-Window.desc } + .desc = { ent-WindowRCDResistant.desc } ent-PlasmaReinforcedWindowDirectional = directional reinforced plasma window .desc = Don't smudge up the glass down there. ent-ReinforcedPlasmaWindowDiagonal = { ent-ReinforcedPlasmaWindow } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/ruranium.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/ruranium.ftl index 12840215fd7..0ba3040f467 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/ruranium.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/ruranium.ftl @@ -1,5 +1,5 @@ ent-ReinforcedUraniumWindow = reinforced uranium window - .desc = { ent-Window.desc } + .desc = { ent-WindowRCDResistant.desc } ent-UraniumReinforcedWindowDirectional = directional reinforced uranium window .desc = Don't smudge up the glass down there. ent-ReinforcedUraniumWindowDiagonal = { ent-ReinforcedUraniumWindow } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/shuttle.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/shuttle.ftl index 65ce1bc1c3a..6c5272cfdd0 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/shuttle.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/shuttle.ftl @@ -1,5 +1,5 @@ ent-ShuttleWindow = shuttle window - .desc = { ent-Window.desc } + .desc = { ent-WindowRCDResistant.desc } ent-ShuttleWindowDiagonal = { ent-ShuttleWindow } .suffix = diagonal .desc = { ent-ShuttleWindow.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/uranium.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/uranium.ftl index 1781cae50c3..012b81a4745 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/uranium.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/uranium.ftl @@ -1,5 +1,5 @@ ent-UraniumWindow = uranium window - .desc = { ent-Window.desc } + .desc = { ent-WindowRCDResistant.desc } ent-UraniumWindowDirectional = directional uranium window .desc = Don't smudge up the glass down there. ent-UraniumWindowDiagonal = { ent-UraniumWindow } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/window.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/window.ftl index 7da6a090ed3..21dd2cc33a0 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/window.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/windows/window.ftl @@ -1,7 +1,11 @@ ent-Window = window .desc = Don't smudge up the glass down there. +ent-WindowRCDResistant = { ent-Window } + .desc = { ent-Window.desc } ent-WindowDirectional = directional window .desc = Don't smudge up the glass down there. +ent-WindowDirectionalRCDResistant = { ent-WindowDirectional } + .desc = { ent-WindowDirectional.desc } ent-WindowFrostedDirectional = directional frosted window .desc = Don't smudge up the glass down there. ent-WindowDiagonal = { ent-Window } diff --git a/Resources/Locale/ru-RU/accent/southern.ftl b/Resources/Locale/ru-RU/accent/southern.ftl new file mode 100644 index 00000000000..1f2ef94a595 --- /dev/null +++ b/Resources/Locale/ru-RU/accent/southern.ftl @@ -0,0 +1,12 @@ +accent-southern-words-1 = you all +accent-southern-words-replace-1 = y'all +accent-southern-words-2 = you guys +accent-southern-words-replace-2 = y'all +accent-southern-words-3 = isn't +accent-southern-words-replace-3 = ain't +accent-southern-words-4 = is not +accent-southern-words-replace-4 = ain't +accent-southern-words-5 = aren't +accent-southern-words-replace-5 = ain't +accent-southern-words-6 = are not +accent-southern-words-replace-6 = ain't diff --git a/Resources/Locale/ru-RU/administration/commands/aghost.ftl b/Resources/Locale/ru-RU/administration/commands/aghost.ftl new file mode 100644 index 00000000000..c1a8a976ea0 --- /dev/null +++ b/Resources/Locale/ru-RU/administration/commands/aghost.ftl @@ -0,0 +1,3 @@ +aghost-description = Makes you an admin ghost. +aghost-no-mind-self = You can't ghost here! +aghost-no-mind-other = They can't ghost here! diff --git a/Resources/Locale/ru-RU/anomaly/anomaly.ftl b/Resources/Locale/ru-RU/anomaly/anomaly.ftl index 4b6eabd412d..f205404fcaf 100644 --- a/Resources/Locale/ru-RU/anomaly/anomaly.ftl +++ b/Resources/Locale/ru-RU/anomaly/anomaly.ftl @@ -6,18 +6,27 @@ anomaly-particles-delta = Дельта-частицы anomaly-particles-epsilon = Эпсилон-частицы anomaly-particles-zeta = Зета-частицы anomaly-particles-omega = Омега-частицы +anomaly-particles-sigma = Sigma particles anomaly-scanner-component-scan-complete = Сканирование завершено! anomaly-scanner-ui-title = сканер аномалий anomaly-scanner-no-anomaly = Нет просканированной аномалии. anomaly-scanner-severity-percentage = Текущая опасность: [color=gray]{ $percent }[/color] +anomaly-scanner-severity-percentage-unknown = Current severity: [color=red]ERROR[/color] anomaly-scanner-stability-low = Текущее состояние аномалии: [color=gold]Распад[/color] anomaly-scanner-stability-medium = Текущее состояние аномалии: [color=forestgreen]Стабильное[/color] anomaly-scanner-stability-high = Текущее состояние аномалии: [color=crimson]Рост[/color] +anomaly-scanner-stability-unknown = Current anomaly state: [color=red]ERROR[/color] anomaly-scanner-point-output = Пассивная генерация очков: [color=gray]{ $point }[/color] +anomaly-scanner-point-output-unknown = Point output: [color=red]ERROR[/color] anomaly-scanner-particle-readout = Анализ реакции на частицы: anomaly-scanner-particle-danger = - [color=crimson]Опасный тип:[/color] { $type } anomaly-scanner-particle-unstable = - [color=plum]Нестабильный тип:[/color] { $type } anomaly-scanner-particle-containment = - [color=goldenrod]Сдерживающий тип:[/color] { $type } +anomaly-scanner-particle-transformation = - [color=#6b75fa]Transformation type:[/color] { $type } +anomaly-scanner-particle-danger-unknown = - [color=crimson]Danger type:[/color] [color=red]ERROR[/color] +anomaly-scanner-particle-unstable-unknown = - [color=plum]Unstable type:[/color] [color=red]ERROR[/color] +anomaly-scanner-particle-containment-unknown = - [color=goldenrod]Containment type:[/color] [color=red]ERROR[/color] +anomaly-scanner-particle-transformation-unknown = - [color=#6b75fa]Transformation type:[/color] [color=red]ERROR[/color] anomaly-scanner-pulse-timer = Время до следующего импульса: [color=gray]{ $time }[/color] anomaly-gorilla-core-slot-name = Ядро аномалии anomaly-gorilla-charge-none = Внутри нет [bold]ядра аномалии[/bold]. @@ -63,3 +72,20 @@ anomaly-command-supercritical = Целевая аномалия переходи # Flavor text on the footer anomaly-generator-flavor-left = Аномалия может возникнуть внутри оператора. anomaly-generator-flavor-right = v1.1 +anomaly-behavior-unknown = [color=red]ERROR. Cannot be read.[/color] +anomaly-behavior-title = behavior deviation analysis: +anomaly-behavior-point = [color=gold]Anomaly produces { $mod }% of the points[/color] +anomaly-behavior-safe = [color=forestgreen]The anomaly is extremely stable. Extremely rare pulsations.[/color] +anomaly-behavior-slow = [color=forestgreen]The frequency of pulsations is much less frequent.[/color] +anomaly-behavior-light = [color=forestgreen]Pulsation power is significantly reduced.[/color] +anomaly-behavior-balanced = No behavior deviations detected. +anomaly-behavior-delayed-force = The frequency of pulsations is greatly reduced, but their power is increased. +anomaly-behavior-rapid = The frequency of the pulsation is much higher, but its strength is attenuated. +anomaly-behavior-reflect = A protective coating was detected. +anomaly-behavior-nonsensivity = A weak reaction to particles was detected. +anomaly-behavior-sensivity = Amplified reaction to particles was detected. +anomaly-behavior-secret = Interference detected. Some data cannot be read +anomaly-behavior-inconstancy = [color=crimson]Impermanence has been detected. Particle types can change over time.[/color] +anomaly-behavior-fast = [color=crimson]The pulsation frequency is strongly increased.[/color] +anomaly-behavior-strenght = [color=crimson]The pulsation power is significantly increased.[/color] +anomaly-behavior-moving = [color=crimson]Coordinate instability was detected.[/color] diff --git a/Resources/Locale/ru-RU/chat/managers/chat-manager.ftl b/Resources/Locale/ru-RU/chat/managers/chat-manager.ftl index 6212c3e69fc..fe6e3467c23 100644 --- a/Resources/Locale/ru-RU/chat/managers/chat-manager.ftl +++ b/Resources/Locale/ru-RU/chat/managers/chat-manager.ftl @@ -42,53 +42,77 @@ chat-speech-verb-suffix-question = ? chat-speech-verb-default = говорит chat-speech-verb-suffix-stutter = - chat-speech-verb-suffix-mumble = .. +chat-speech-verb-name-none = None +chat-speech-verb-name-default = Default chat-speech-verb-exclamation = восклицает +chat-speech-verb-name-exclamation = Exclaiming chat-speech-verb-exclamation-strong = кричит +chat-speech-verb-name-exclamation-strong = Yelling chat-speech-verb-question = спрашивает +chat-speech-verb-name-question = Asking chat-speech-verb-stutter = запинается +chat-speech-verb-name-stutter = Stuttering chat-speech-verb-mumble = бубнит +chat-speech-verb-name-mumble = Mumbling chat-speech-verb-insect-1 = стрекочет +chat-speech-verb-name-arachnid = Arachnid chat-speech-verb-insect-2 = жужжит chat-speech-verb-insect-3 = щёлкает chat-speech-verb-winged-1 = свистит +chat-speech-verb-name-moth = Moth chat-speech-verb-winged-2 = хлопает chat-speech-verb-winged-3 = клокочет chat-speech-verb-slime-1 = шлёпает +chat-speech-verb-name-slime = Slime chat-speech-verb-slime-2 = бурлит chat-speech-verb-slime-3 = булькает chat-speech-verb-plant-1 = шелестит +chat-speech-verb-name-plant = Diona chat-speech-verb-plant-2 = шуршит chat-speech-verb-plant-3 = скрипит chat-speech-verb-robotic-1 = докладывает +chat-speech-verb-name-robotic = Robotic chat-speech-verb-robotic-2 = пищит chat-speech-verb-reptilian-1 = шипит +chat-speech-verb-robotic-3 = boops +chat-speech-verb-name-reptilian = Reptilian chat-speech-verb-reptilian-2 = фыркает chat-speech-verb-reptilian-3 = пыхтит chat-speech-verb-skeleton-1 = гремит +chat-speech-verb-name-skeleton = Skeleton chat-speech-verb-skeleton-2 = клацает chat-speech-verb-skeleton-3 = скрежещет chat-speech-verb-canine-1 = гавкает +chat-speech-verb-name-vox = Vox chat-speech-verb-canine-2 = лает chat-speech-verb-canine-3 = воет chat-speech-verb-vox-1 = скрипит +chat-speech-verb-name-canine = Canine chat-speech-verb-vox-2 = визжит chat-speech-verb-vox-3 = каркает chat-speech-verb-small-mob-1 = скрипит +chat-speech-verb-name-small-mob = Mouse chat-speech-verb-small-mob-2 = пищит chat-speech-verb-large-mob-1 = ревёт +chat-speech-verb-name-large-mob = Carp chat-speech-verb-large-mob-2 = рычит chat-speech-verb-monkey-1 = обезьяничает +chat-speech-verb-name-monkey = Monkey chat-speech-verb-monkey-2 = визжит chat-speech-verb-parrot-1 = кричит +chat-speech-verb-name-cluwne = Cluwne +chat-speech-verb-name-parrot = Parrot chat-speech-verb-parrot-2 = чирикает chat-speech-verb-parrot-3 = щебечет chat-speech-verb-ghost-1 = жалуется chat-speech-verb-ghost-2 = дышит chat-speech-verb-ghost-3 = воет chat-speech-verb-ghost-4 = бормочет +chat-speech-verb-name-ghost = Ghost chat-speech-verb-cluwne-1 = хихикает chat-speech-verb-cluwne-2 = хехекает chat-speech-verb-cluwne-3 = смеётся chat-speech-verb-electricity-1 = трещит +chat-speech-verb-name-electricity = Electricity chat-speech-verb-electricity-2 = гудит chat-speech-verb-electricity-3 = скрипит diff --git a/Resources/Locale/ru-RU/chemistry/components/hypospray-component.ftl b/Resources/Locale/ru-RU/chemistry/components/hypospray-component.ftl index b106125192c..44dfa151c2f 100644 --- a/Resources/Locale/ru-RU/chemistry/components/hypospray-component.ftl +++ b/Resources/Locale/ru-RU/chemistry/components/hypospray-component.ftl @@ -1,6 +1,10 @@ ## UI -hypospray-volume-text = Объем: [color=white]{ $currentVolume }/{ $totalVolume }[/color] +hypospray-all-mode-text = Only Injects +hypospray-mobs-only-mode-text = Draws and Injects +hypospray-invalid-text = Invalid +hypospray-volume-label = Volume: [color=white]{ $currentVolume }/{ $totalVolume }u[/color] + Mode: [color=white]{ $modeString }[/color] ## Entity @@ -10,4 +14,7 @@ hypospray-component-inject-self-clumsy-message = Ой! Вы сделали се hypospray-component-empty-message = Он пустой! hypospray-component-feel-prick-message = Вы чувствуете слабый укольчик! hypospray-component-transfer-already-full-message = { $owner } уже заполнен! +hypospray-verb-mode-label = Toggle Container Draw +hypospray-verb-mode-inject-all = You cannot draw from containers anymore. +hypospray-verb-mode-inject-mobs-only = You can now draw from containers. hypospray-cant-inject = Нельзя сделать инъекцию в { $target }! diff --git a/Resources/Locale/ru-RU/chemistry/components/scoopable-component.ftl b/Resources/Locale/ru-RU/chemistry/components/scoopable-component.ftl new file mode 100644 index 00000000000..cb10c14035a --- /dev/null +++ b/Resources/Locale/ru-RU/chemistry/components/scoopable-component.ftl @@ -0,0 +1 @@ +scoopable-component-popup = You scoop up { $scooped } into { THE($beaker) }. diff --git a/Resources/Locale/ru-RU/escape-menu/ui/options-menu.ftl b/Resources/Locale/ru-RU/escape-menu/ui/options-menu.ftl index 6c5d87d3263..8c0d0c4c6cb 100644 --- a/Resources/Locale/ru-RU/escape-menu/ui/options-menu.ftl +++ b/Resources/Locale/ru-RU/escape-menu/ui/options-menu.ftl @@ -47,6 +47,8 @@ ui-options-fancy-name-background = Добавить фон облачкам с ui-options-enable-color-name = Цветные имена персонажей ui-options-colorblind-friendly = Режим для дальтоников ui-options-reduced-motion = Снижение интенсивности визуальных эффектов +ui-options-chat-window-opacity = Chat window opacity +ui-options-chat-window-opacity-percent = { TOSTRING($opacity, "P0") } ui-options-screen-shake-intensity = Интенсивность дрожания экрана ui-options-screen-shake-percent = { TOSTRING($intensity, "P0") } ui-options-vsync = Вертикальная синхронизация diff --git a/Resources/Locale/ru-RU/flavors/flavor-profiles.ftl b/Resources/Locale/ru-RU/flavors/flavor-profiles.ftl index d0daaf5b05c..4209e438b9f 100644 --- a/Resources/Locale/ru-RU/flavors/flavor-profiles.ftl +++ b/Resources/Locale/ru-RU/flavors/flavor-profiles.ftl @@ -192,6 +192,7 @@ flavor-complex-tonic-water = как озлобленная вода flavor-complex-tequila = как забродившая смерть flavor-complex-energy-drink = как аккумуляторная кислота flavor-complex-dr-gibb = как халатность +flavor-complex-ginger-soda = like ginger flavor-complex-grape-soda = как виноградная газировка flavor-complex-lemon-lime-soda = как лимонно-лаймовая газировка flavor-complex-pwr-game-soda = как гейминг @@ -204,6 +205,7 @@ flavor-complex-sake = как сладкий, алкогольный рис flavor-complex-rum = как забродивший сахар flavor-complex-coffee-liquor = как крепкий, горький кофе flavor-complex-whiskey = как патока +flavor-complex-coconut-rum = like nutty fermented sugar flavor-complex-shitty-wine = как виноградная кожура flavor-complex-iced-tea = как холодный чай flavor-complex-champagne = как свежеиспечённый хлеб @@ -217,6 +219,11 @@ flavor-complex-ice = как лёд flavor-complex-mopwata = как застоявшаяся грязная вода flavor-complex-long-island = подозрительно похож на холодный чай flavor-complex-three-mile-island = как чай, заваренный в ядерных отходах +flavor-complex-arnold-palmer = like a hole-in-one +flavor-complex-blue-hawaiian = like the tropics +flavor-complex-cosmopolitan = sweet and tangy +flavor-complex-painkiller = like spiked pineapple juice +flavor-complex-pina-colada = like tropical sun flavor-complex-whiskey-cola = как газированная патока flavor-complex-singulo = как бездонная дыра flavor-complex-syndie-bomb = как горький виски diff --git a/Resources/Locale/ru-RU/foldable/components/foldable-component.ftl b/Resources/Locale/ru-RU/foldable/components/foldable-component.ftl index c26e35cf8f7..efde1bb40e9 100644 --- a/Resources/Locale/ru-RU/foldable/components/foldable-component.ftl +++ b/Resources/Locale/ru-RU/foldable/components/foldable-component.ftl @@ -4,3 +4,5 @@ foldable-deploy-fail = Вы не можете разложить { $object } з fold-verb = Сложить unfold-verb = Разложить fold-flip-verb = Перевернуть +fold-zip-verb = Zip up +fold-unzip-verb = Unzip diff --git a/Resources/Locale/ru-RU/ghost/roles/ghost-role-component.ftl b/Resources/Locale/ru-RU/ghost/roles/ghost-role-component.ftl index b28d8eb92bb..70feec6e16f 100644 --- a/Resources/Locale/ru-RU/ghost/roles/ghost-role-component.ftl +++ b/Resources/Locale/ru-RU/ghost/roles/ghost-role-component.ftl @@ -113,6 +113,8 @@ ghost-role-information-ert-leader-name = ОБР Лидер ghost-role-information-ert-leader-description = Руководите командой специалистов для решения проблем станции. ghost-role-information-ert-janitor-name = ОБР Уборщик ghost-role-information-ert-janitor-description = Оказывайте содействие в попытках навести чистоту для решения проблем станции. +ghost-role-information-ert-chaplain-name = ERT Chaplain +ghost-role-information-ert-chaplain-description = Assist with mourning to resolve the station's crew moral issues. ghost-role-information-ert-engineer-name = ОБР Инженер ghost-role-information-ert-engineer-description = Оказывайте содействие в инженерных работах для решения проблем станции. ghost-role-information-ert-security-name = ОБР Офицер безопасности diff --git a/Resources/Locale/ru-RU/guidebook/guides.ftl b/Resources/Locale/ru-RU/guidebook/guides.ftl index 03079d523b2..6cfeaa82c98 100644 --- a/Resources/Locale/ru-RU/guidebook/guides.ftl +++ b/Resources/Locale/ru-RU/guidebook/guides.ftl @@ -22,6 +22,14 @@ guide-entry-cargo-bounties = Запросы отдела снабжения guide-entry-salvage = Утилизация обломков guide-entry-controls = Управление guide-entry-chemicals = Химические вещества +guide-entry-elements = Elements +guide-entry-narcotics = Narcotics +guide-entry-pyrotechnics = Pyrotechnic +guide-entry-toxins = Toxins +guide-entry-foods = Foods +guide-entry-biological = Biological +guide-entry-others = Others +guide-entry-botanical = Botanicals guide-entry-jobs = Должности guide-entry-janitorial = Уборка станции guide-entry-bartender = Бармен diff --git a/Resources/Locale/ru-RU/job/job-description.ftl b/Resources/Locale/ru-RU/job/job-description.ftl index 695860fedc0..765af3180e9 100644 --- a/Resources/Locale/ru-RU/job/job-description.ftl +++ b/Resources/Locale/ru-RU/job/job-description.ftl @@ -19,6 +19,7 @@ job-description-paramedic = Спасайте тяжелораненых паци job-description-detective = Изучайте места преступлений с помощью криминалистических инструментов, ищите виновных и курите. job-description-doctor = Обследуйте и лечите членов экипажа с помощью обычных и продвинутых лекарств, и дефибриллятора. Следите чтобы трупы не гнили, а кадавры были в морге. job-description-engineer = Поддерживайте работу основного генератора и солнечных панелей станции, оптимизируйте энергосеть и проводите аварийный ремонт, используя свой космический скафандр в безвоздушных пространствах. +job-description-ertchaplain = Ensure the station crew's last rights are taken care of. job-description-ertengineer = Убедитесь, что на станции имеется электропитание и чистый воздух. job-description-ertjanitor = Убедитесь, что станция убрана должным образом - для поддержания морального духа. job-description-ertleader = Возглавьте отряд быстрого реагирования для устранения угрозы активам компании Nanotrasen. diff --git a/Resources/Locale/ru-RU/job/job-names.ftl b/Resources/Locale/ru-RU/job/job-names.ftl index 24476598549..a75df90b09f 100644 --- a/Resources/Locale/ru-RU/job/job-names.ftl +++ b/Resources/Locale/ru-RU/job/job-names.ftl @@ -38,6 +38,7 @@ job-name-cargotech = грузчик job-name-chef = шеф-повар job-name-clown = клоун job-name-ertleader = лидер ОБР +job-name-ertchaplain = ERT Chaplain job-name-ertengineer = инженер ОБР job-name-ertsecurity = офицер безопасности ОБР job-name-ertmedic = медик ОБР @@ -62,6 +63,7 @@ JobClown = клоун JobDetective = детектив JobERTEngineer = инженер ОБР JobBrigmedic = бригмедик +JobERTChaplain = ERT Chaplain JobERTJanitor = уборщик ОБР JobERTLeader = лидер ОБР JobERTMedical = медик ОБР diff --git a/Resources/Locale/ru-RU/lock/lock-component.ftl b/Resources/Locale/ru-RU/lock/lock-component.ftl index b75220b0153..ac75def3cde 100644 --- a/Resources/Locale/ru-RU/lock/lock-component.ftl +++ b/Resources/Locale/ru-RU/lock/lock-component.ftl @@ -3,6 +3,7 @@ lock-comp-on-examined-is-unlocked = Похоже, { $entityName } разблок lock-comp-do-lock-success = Вы заблокировали { $entityName }. lock-comp-do-unlock-success = Вы разблокировали { $entityName }. lock-comp-has-user-access-fail = Доступ запрещён +lock-comp-generic-fail = { CAPITALIZE(SUBJECT($target)) } { CONJUGATE-BE($target) } locked. ## ToggleLockVerb diff --git a/Resources/Locale/ru-RU/machine-linking/receiver_ports.ftl b/Resources/Locale/ru-RU/machine-linking/receiver_ports.ftl index 5ed59343059..fbc711024e0 100644 --- a/Resources/Locale/ru-RU/machine-linking/receiver_ports.ftl +++ b/Resources/Locale/ru-RU/machine-linking/receiver_ports.ftl @@ -46,6 +46,8 @@ signal-port-name-set-particle-epsilon = Выбрать тип частиц: эп signal-port-description-set-particle-epsilon = Устанавливает тип частиц, излучаемых этим устройством, на эпсилон. signal-port-name-set-particle-zeta = Выбрать тип частиц: зета signal-port-description-set-particle-zeta = Устанавливает тип частиц, излучаемых этим устройством, на зета. +signal-port-name-set-particle-sigma = Set particle type: sigma +signal-port-description-set-particle-sigma = Sets the type of particle this device emits to sigma. signal-port-name-logic-input-a = Порт А signal-port-description-logic-input-a = Первый порт логического элемента. signal-port-name-logic-input-b = Порт В diff --git a/Resources/Locale/ru-RU/paint/paint.ftl b/Resources/Locale/ru-RU/paint/paint.ftl deleted file mode 100644 index 0f6d917cc0f..00000000000 --- a/Resources/Locale/ru-RU/paint/paint.ftl +++ /dev/null @@ -1,8 +0,0 @@ -paint-success = { $target } был покрашен! -paint-failure = Не удалось покрасить { $target }! -paint-failure-painted = { $target } уже покрашен! -paint-empty = { $used } пуст! -paint-removed = Вы стираете краску! -paint-closed = Сначала откройте { $used }! -paint-verb = Покрасить -paint-remove-verb = Убрать краску diff --git a/Resources/Locale/ru-RU/paper/story-generation.ftl b/Resources/Locale/ru-RU/paper/story-generation.ftl new file mode 100644 index 00000000000..595ce17c867 --- /dev/null +++ b/Resources/Locale/ru-RU/paper/story-generation.ftl @@ -0,0 +1,222 @@ +story-gen-book-type1 = book +story-gen-book-type2 = folio +story-gen-book-type3 = collection +story-gen-book-type4 = notes +story-gen-book-type5 = manuscript +story-gen-book-type6 = records +story-gen-book-type7 = tome +story-gen-book-type8 = journal +story-gen-book-type9 = archives +story-gen-book-type10 = codex +story-gen-book-type11 = memories +story-gen-book-type12 = compendium +story-gen-book-genre1 = work of crime fiction +story-gen-book-genre2 = comedy +story-gen-book-genre3 = horror story +story-gen-book-genre4 = poem +story-gen-book-genre5 = novella +story-gen-book-genre6 = chronicle +story-gen-book-genre7 = work of science-fiction +story-gen-book-genre8 = fantasy story +story-gen-book-genre9 = romance +story-gen-book-genre10 = thriller +story-gen-book-genre11 = work of historical fiction +story-gen-book-genre12 = biography +story-gen-book-genre13 = adventure story +story-gen-book-genre14 = drama +story-gen-book-appearance1 = ancient +story-gen-book-appearance2 = shabby +story-gen-book-appearance3 = dirty +story-gen-book-appearance4 = unusual +story-gen-book-appearance5 = faded +story-gen-book-appearance6 = nasty +story-gen-book-appearance7 = dusty +story-gen-book-appearance8 = scary +story-gen-book-appearance9 = bloody +story-gen-book-appearance10 = bright +story-gen-book-appearance11 = dubious +story-gen-book-appearance12 = intriguing +story-gen-book-appearance13 = ugly +story-gen-book-appearance14 = crooked +story-gen-book-appearance15 = crumpled +story-gen-book-appearance16 = dirty +story-gen-book-appearance17 = elegant +story-gen-book-appearance18 = ornate +story-gen-book-appearance19 = weathered +story-gen-book-appearance20 = chrisp +story-gen-book-appearance21 = lavish +story-gen-book-appearance22 = tattered +story-gen-book-appearance23 = polished +story-gen-book-appearance24 = embossed +story-gen-book-appearance25 = mismatched +story-gen-book-appearance26 = gilded +story-gen-book-appearance27 = strange +story-gen-book-character1 = clown +story-gen-book-character2 = mime +story-gen-book-character3 = reporter +story-gen-book-character4 = butcher +story-gen-book-character5 = bartender +story-gen-book-character6 = janitor +story-gen-book-character7 = engineer +story-gen-book-character8 = scientist +story-gen-book-character9 = guard +story-gen-book-character10 = doctor +story-gen-book-character11 = chemist +story-gen-book-character12 = prisoner +story-gen-book-character13 = researcher +story-gen-book-character14 = trader +story-gen-book-character15 = captain +story-gen-book-character16 = lizard +story-gen-book-character17 = moth +story-gen-book-character18 = diona +story-gen-book-character19 = cat-girl +story-gen-book-character20 = cat +story-gen-book-character21 = corgi +story-gen-book-character22 = dog +story-gen-book-character23 = opossum +story-gen-book-character24 = sloth +story-gen-book-character25 = syndicate agent +story-gen-book-character26 = revenant +story-gen-book-character27 = rat king +story-gen-book-character28 = ninja +story-gen-book-character29 = space dragon +story-gen-book-character30 = revolutionary +story-gen-book-character31 = nuclear operative +story-gen-book-character32 = narsie cultist +story-gen-book-character33 = ratwar cultist +story-gen-book-character34 = greytider +story-gen-book-character35 = arachnid +story-gen-book-character36 = vox +story-gen-book-character37 = dwarf +story-gen-book-character38 = thief +story-gen-book-character39 = wizard +story-gen-book-character40 = slime +story-gen-book-character-trait1 = stupid +story-gen-book-character-trait2 = smart +story-gen-book-character-trait3 = funny +story-gen-book-character-trait4 = attractive +story-gen-book-character-trait5 = charming +story-gen-book-character-trait6 = nasty +story-gen-book-character-trait7 = dying +story-gen-book-character-trait8 = old +story-gen-book-character-trait9 = young +story-gen-book-character-trait10 = rich +story-gen-book-character-trait11 = poor +story-gen-book-character-trait12 = popular +story-gen-book-character-trait13 = absent-minded +story-gen-book-character-trait14 = stern +story-gen-book-character-trait15 = сharismatic +story-gen-book-character-trait16 = stoic +story-gen-book-character-trait17 = cute +story-gen-book-character-trait18 = dwarven +story-gen-book-character-trait19 = beer-smelling +story-gen-book-character-trait20 = joyful +story-gen-book-character-trait21 = painfully beautiful +story-gen-book-character-trait22 = robotic +story-gen-book-character-trait23 = holographic +story-gen-book-character-trait24 = hysterically laughing +story-gen-book-event1 = a zombie outbreak +story-gen-book-event2 = a nuclear explosion +story-gen-book-event3 = a mass murder +story-gen-book-event4 = a sudden depressurization +story-gen-book-event5 = a blackout +story-gen-book-event6 = the starvation of the protagonists +story-gen-book-event7 = a wasting illness +story-gen-book-event8 = love at first sight +story-gen-book-event9 = a rush of inspiration +story-gen-book-event10 = the occurrence of some mystical phenomena +story-gen-book-event11 = divine intervention +story-gen-book-event12 = the characters' own selfish motives +story-gen-book-event13 = an unforeseen deception +story-gen-book-event14 = the resurrection of one of these characters from the dead +story-gen-book-event15 = the terrible torture of the protagonist +story-gen-book-event16 = the inadvertent loosing of a gravitational singularity +story-gen-book-event17 = a psychic prediction of future events +story-gen-book-event18 = an antimatter explosion +story-gen-book-event19 = a chance meeting with a cat-girl +story-gen-book-event20 = drinking far too much alcohol +story-gen-book-event21 = eating way too much pizza +story-gen-book-event22 = having a quarrel with a close friend +story-gen-book-event23 = the sudden loss of their home in a fiery blaze +story-gen-book-event24 = the loss of a PDA +story-gen-book-action1 = share in a kiss with a +story-gen-book-action2 = strangle to death a +story-gen-book-action3 = manage to blow apart a +story-gen-book-action4 = manage to win a game of chess against a +story-gen-book-action5 = narrowly lose a game of chess against a +story-gen-book-action6 = reveal the hidden secrets of a +story-gen-book-action7 = manipulate a +story-gen-book-action8 = sacrifice upon an altar a +story-gen-book-action9 = attend the wedding of a +story-gen-book-action10 = join forces to defeat their common enemy, a +story-gen-book-action11 = are forced to work together to escape a +story-gen-book-action12 = give a valuable gift to +story-gen-book-action-trait1 = terribly +story-gen-book-action-trait2 = disgustingly +story-gen-book-action-trait3 = marvelously +story-gen-book-action-trait4 = nicely +story-gen-book-action-trait5 = weirdly +story-gen-book-action-trait6 = amusingly +story-gen-book-action-trait7 = fancifully +story-gen-book-action-trait8 = impressively +story-gen-book-action-trait9 = irresponsibly +story-gen-book-action-trait10 = severely +story-gen-book-action-trait11 = ruthlessly +story-gen-book-action-trait12 = playfully +story-gen-book-action-trait13 = thoughtfully +story-gen-book-location1 = in an underground complex +story-gen-book-location2 = while on an expedition +story-gen-book-location3 = while trapped in outer space +story-gen-book-location4 = while in a news office +story-gen-book-location5 = in a hidden garden +story-gen-book-location6 = in the kitchen of a local restaurant +story-gen-book-location7 = under the counter of the local sports bar +story-gen-book-location8 = in an ancient library +story-gen-book-location9 = while deep in bowels of the space station's maintenance corridors +story-gen-book-location10 = on the bridge of a starship +story-gen-book-location11 = while in a grungy public bathroom +story-gen-book-location12 = while trapped inside a crate +story-gen-book-location13 = while stuck inside a locker +story-gen-book-location14 = while stationed on Barratry +story-gen-book-location15 = while in the hall of rustic church +story-gen-book-location16 = while in a crematorium +story-gen-book-location17 = standing too close to an anomaly +story-gen-book-location18 = while huddling on the evacuation shuttle +story-gen-book-location19 = standing in freshly fallen snow +story-gen-book-location20 = lost in the woods +story-gen-book-location21 = iin the harsh desert +story-gen-book-location22 = worrying about their social media networks +story-gen-book-location23 = atop of a mountain +story-gen-book-location24 = while driving a car +story-gen-book-location25 = in an escape pod +story-gen-book-location26 = while abroad in a fictional country +story-gen-book-location27 = clinging to the wing of an inflight airplane +story-gen-book-location28 = inside a pocket dimension +story-gen-book-location29 = onboard a Wizard Federation shuttle +story-gen-book-location30 = standing atop of a mountain of corpses +story-gen-book-location31 = while psychically projected into their subconscious +story-gen-book-location32 = while trapped in a shadow dimension +story-gen-book-location33 = while trying to escape a destroyed space station +story-gen-book-location34 = while sandwiched between a Tesla ball and a gravitational singularity +story-gen-book-element1 = The plot +story-gen-book-element2 = The twist +story-gen-book-element3 = The climax +story-gen-book-element4 = The final act +story-gen-book-element5 = The ending +story-gen-book-element6 = The moral of the story +story-gen-book-element7 = The theme of this work +story-gen-book-element8 = The literary style +story-gen-book-element9 = The illustrations +story-gen-book-element-trait1 = terrifying +story-gen-book-element-trait2 = disgusting +story-gen-book-element-trait3 = wonderful +story-gen-book-element-trait4 = cute +story-gen-book-element-trait5 = boring +story-gen-book-element-trait6 = strange +story-gen-book-element-trait7 = amusing +story-gen-book-element-trait8 = whimsical +story-gen-book-element-trait9 = impressive +story-gen-book-element-trait10 = interesting +story-gen-book-element-trait11 = inadequate +story-gen-book-element-trait12 = sad +story-gen-book-element-trait13 = rather depressing diff --git a/Resources/Locale/ru-RU/rcd/components/rcd-component.ftl b/Resources/Locale/ru-RU/rcd/components/rcd-component.ftl index 33401e2dd92..1edb81b8c1b 100644 --- a/Resources/Locale/ru-RU/rcd/components/rcd-component.ftl +++ b/Resources/Locale/ru-RU/rcd/components/rcd-component.ftl @@ -1,16 +1,51 @@ ### UI -# Shown when an RCD is examined in details range -rcd-component-examine-detail = Переключён в режим { $mode }. +rcd-component-examine-mode-details = It's currently set to '{ $mode }' mode. +rcd-component-examine-build-details = It's currently set to build { MAKEPLURAL($name) }. ### Interaction Messages +rcd-component-change-build-mode = The RCD is now set to build { MAKEPLURAL($name) }. # Shown when changing RCD Mode rcd-component-change-mode = РСУ переключён в режим { $mode }. +rcd-component-insufficient-ammo-message = The RCD doesn't have enough charges left! rcd-component-no-ammo-message = В РСУ закончились заряды! rcd-component-tile-obstructed-message = Этот тайл заблокирован! +rcd-component-nothing-to-deconstruct-message = There's nothing to deconstruct! rcd-component-tile-indestructible-message = Этот тайл не может быть уничтожен! +# Construction +rcd-component-no-valid-grid = You're too far into open space to build here! +rcd-component-must-build-on-empty-tile-message = A foundation already exists here! +rcd-component-cannot-build-on-empty-tile-message = You can't build that without a foundation! +rcd-component-must-build-on-subfloor-message = You can only build that on exposed subfloor! +rcd-component-cannot-build-on-subfloor-message = You can't build that on exposed subfloor! +rcd-component-cannot-build-on-occupied-tile-message = You can't build here, the space is already occupied! +rcd-component-cannot-build-identical-tile = That tile already exists there! rcd-component-deconstruct-target-not-on-whitelist-message = Вы не можете это деконструировать! -rcd-component-cannot-build-floor-tile-not-empty-message = Пол можно построить только в космосе! -rcd-component-cannot-build-wall-tile-not-empty-message = Вы не можете построить стену в космосе! -rcd-component-cannot-build-airlock-tile-not-empty-message = Вы не можете построить шлюз в космосе! +rcd-component-walls-and-flooring = Walls and flooring +rcd-component-windows-and-grilles = Windows and grilles +rcd-component-airlocks = Airlocks +rcd-component-electrical = Electrical +rcd-component-lighting = Lighting +rcd-component-deconstruct = deconstruct +rcd-component-wall-solid = solid wall +rcd-component-floor-steel = steel tile +rcd-component-plating = hull plate +rcd-component-catwalk = catwalk +rcd-component-wall-reinforced = reinforced wall +rcd-component-grille = grille +rcd-component-window = window +rcd-component-window-directional = directional window +rcd-component-window-reinforced-directional = directional reinforced window +rcd-component-reinforced-window = reinforced window +rcd-component-airlock = standard airlock +rcd-component-airlock-glass = glass airlock +rcd-component-firelock = firelock +rcd-component-computer-frame = computer frame +rcd-component-machine-frame = machine frame +rcd-component-tube-light = light +rcd-component-window-bulb-light = small light +rcd-component-window-lv-cable = LV cable +rcd-component-window-mv-cable = MV cable +rcd-component-window-hv-cable = HV cable +rcd-component-window-cable-terminal = cable terminal diff --git a/Resources/Locale/ru-RU/reagents/meta/consumable/drink/alcohol.ftl b/Resources/Locale/ru-RU/reagents/meta/consumable/drink/alcohol.ftl index 21a49ba8e2d..06eeb657c67 100644 --- a/Resources/Locale/ru-RU/reagents/meta/consumable/drink/alcohol.ftl +++ b/Resources/Locale/ru-RU/reagents/meta/consumable/drink/alcohol.ftl @@ -54,6 +54,8 @@ reagent-name-atomic-bomb = атомная бомба reagent-desc-atomic-bomb = Распространение ядерного оружия никогда не было таким вкусным. reagent-name-b52 = Б-52 reagent-desc-b52 = Кофе, ирландские сливки и коньяк. Просто бомба. +reagent-name-blue-hawaiian = blue hawaiian +reagent-desc-blue-hawaiian = Aloha! Does that mean hello or goodbye? reagent-name-bahama-mama = багама-мама reagent-desc-bahama-mama = Тропический коктейль. reagent-name-banana-honk = банана-хонк @@ -70,6 +72,10 @@ reagent-name-booger = козявка reagent-desc-booger = Фууу... reagent-name-brave-bull = храбрый бык reagent-desc-brave-bull = Так же эффективен, как и голландский кураж! +reagent-name-coconut-rum = coconut rum +reagent-desc-coconut-rum = Rum with coconut for that tropical feel. +reagent-name-cosmopolitan = cosmopolitan +reagent-desc-cosmopolitan = Even in the worst situations, nothing beats a fresh cosmopolitan. reagent-name-cuba-libre = куба либре reagent-desc-cuba-libre = Ром, смешанный с колой. Да здравствует революция. reagent-name-demons-blood = кровь демона @@ -126,8 +132,12 @@ reagent-name-moonshine = самогон reagent-desc-moonshine = Самодельный напиток, изготавливаемый в домашних условиях. Что может пойти не так? reagent-name-neurotoxin = нейротоксин reagent-desc-neurotoxin = Сильный нейротоксин, который вводит субъекта в состояние, напоминающее смерть. +reagent-name-painkiller = painkiller +reagent-desc-painkiller = A cure for what ails you. reagent-name-patron = покровитель reagent-desc-patron = Текила с серебром в своем составе, фаворит женщин-алкоголичек из клубной тусовки. +reagent-name-pina-colada = piña colada +reagent-desc-pina-colada = For getting lost in the rain. reagent-name-red-mead = красная медовуха reagent-desc-red-mead = Настоящий напиток викингов! Несмотря на то, что он имеет странный красный цвет. reagent-name-rewriter = переписчик diff --git a/Resources/Locale/ru-RU/reagents/meta/consumable/drink/drinks.ftl b/Resources/Locale/ru-RU/reagents/meta/consumable/drink/drinks.ftl index 9a031027848..a7af9b08d44 100644 --- a/Resources/Locale/ru-RU/reagents/meta/consumable/drink/drinks.ftl +++ b/Resources/Locale/ru-RU/reagents/meta/consumable/drink/drinks.ftl @@ -2,6 +2,10 @@ reagent-name-coffee = кофе reagent-desc-coffee = Напиток, приготовленный из заваренных кофейных зерен. Содержит умеренное количество кофеина. reagent-name-cream = сливки reagent-desc-cream = Жирная, но еще жидкая часть молока. Почему бы вам не смешать это с виски, а? +reagent-name-coconut-water = coconut water +reagent-desc-coconut-water = A favorite of survivors on deserted islands. +reagent-name-cream-of-coconut = cream of coconut +reagent-desc-cream-of-coconut = Sweet, syrupy version of coconut cream with added sugar. reagent-name-cafe-latte = кофе латте reagent-desc-cafe-latte = Приятный, крепкий и вкусный напиток на время чтения. reagent-name-green-tea = зелёный чай @@ -16,6 +20,8 @@ reagent-name-iced-tea = чай со льдом reagent-desc-iced-tea = Он же айс-ти. Не имеет отношения к определенному рэп-исполнителю/актеру. reagent-name-lemonade = лимонад reagent-desc-lemonade = Напиток из лимонного сока, воды и подсластителя, например, тростникового сахара или меда. +reagent-name-arnold-palmer = arnold palmer +reagent-desc-arnold-palmer = Now watch this drive. reagent-name-milk = молоко reagent-desc-milk = Непрозрачная белая жидкость, вырабатываемая молочными железами млекопитающих. reagent-name-milk-goat = козье молоко diff --git a/Resources/Locale/ru-RU/reagents/meta/consumable/drink/soda.ftl b/Resources/Locale/ru-RU/reagents/meta/consumable/drink/soda.ftl index 50b32bb3656..722df738812 100644 --- a/Resources/Locale/ru-RU/reagents/meta/consumable/drink/soda.ftl +++ b/Resources/Locale/ru-RU/reagents/meta/consumable/drink/soda.ftl @@ -1,5 +1,7 @@ reagent-name-cola = кола reagent-desc-cola = Сладкий газированный безалкогольный напиток. Не содержит кофеина. +reagent-name-shirley-temple = shirley temple +reagent-desc-shirley-temple = A favorite amongst younger members of the crew. reagent-name-changeling-sting = жало генокрада reagent-desc-changeling-sting = Вы делаете маленький глоток и чувствуете жжение... reagent-name-dr-gibb = доктор Гибб @@ -18,6 +20,10 @@ reagent-name-root-beer = рутбир reagent-desc-root-beer = Очень сладкий, газированный напиток, напоминающий сарспариллу. Хорошо сочетается с мороженым. reagent-name-root-beer-float = рутбир с мороженым reagent-desc-root-beer-float = Рутбир, но теперь с мороженым сверху! Это действительно магнум опус канадских летних напитков. +reagent-name-sol-dry = sol dry +reagent-desc-sol-dry = Sweet ginger soda from outer space! +reagent-name-roy-rogers = roy rogers +reagent-desc-roy-rogers = Solid proof that there IS something known as too sweet. reagent-name-space-mountain-wind = космический маунтин винд reagent-desc-space-mountain-wind = Проходит сквозь, словно космический ветер. reagent-name-space-up = спейс-ап diff --git a/Resources/Locale/ru-RU/shell.ftl b/Resources/Locale/ru-RU/shell.ftl index e6ac70613e0..1c2169249ef 100644 --- a/Resources/Locale/ru-RU/shell.ftl +++ b/Resources/Locale/ru-RU/shell.ftl @@ -55,3 +55,4 @@ shell-argument-map-id-invalid = Аргумент { $index } должен быт shell-argument-number-invalid = Аргумент { $index } должен быть валидным числом! # Hints shell-argument-username-hint = +shell-argument-username-optional-hint = [username] diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/catalog/fills/backpacks/startergear/backpack.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/catalog/fills/backpacks/startergear/backpack.ftl index a77a95570a5..236a902e51f 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/catalog/fills/backpacks/startergear/backpack.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/catalog/fills/backpacks/startergear/backpack.ftl @@ -56,6 +56,8 @@ ent-ClothingBackpackERTEngineerFilled = { ent-ClothingBackpackERTEngineer } .desc = { ent-ClothingBackpackERTEngineer.desc } ent-ClothingBackpackERTJanitorFilled = { ent-ClothingBackpackERTJanitor } .desc = { ent-ClothingBackpackERTJanitor.desc } +ent-ClothingBackpackERTChaplainFilled = { ent-ClothingBackpackERTChaplain } + .desc = { ent-ClothingBackpackERTChaplain.desc } ent-ClothingBackpackDeathSquadFilled = рюкзак Эскадрона смерти .desc = Содержит набор самых опасных агентов Центкома. ent-ClothingBackpackCargoFilled = { ent-ClothingBackpackCargo } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/catalog/fills/crates/fun.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/catalog/fills/crates/fun.ftl index a34f50dabba..5221d71f140 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/catalog/fills/crates/fun.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/catalog/fills/crates/fun.ftl @@ -38,8 +38,5 @@ ent-CrateFunBikeHornImplants = ящик хонк-имплантов .desc = Тысяча гудков за день отпугнёт СБ на день! ent-CrateFunMysteryFigurines = ящик минифигурок Загадочные космонавты .desc = Коллекция из 10 коробок загадочных минифигурок. Дубликаты возврату не подлежат. -ent-CrateFunSprayPaints = ящик аэрозольной краски - .desc = Ящик, заполненный балончиками с краской. - .suffix = Баллончики с краской ent-CrateFunDartsSet = набор для дартса .desc = Коробка со всем необходимым для увлекательной игры в дартс. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/back/backpacks.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/back/backpacks.ftl index b813fd740ed..52df76f8f94 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/back/backpacks.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/back/backpacks.ftl @@ -46,6 +46,8 @@ ent-ClothingBackpackERTJanitor = рюкзак уборщика ОБР .desc = Вместительный рюкзак с множеством карманов, который носят уборщики отряда быстрого реагирования. ent-ClothingBackpackERTClown = рюкзак клоуна ОБР .desc = Вместительный рюкзак с множеством карманов, который носят клоуны отряда быстрого реагирования. +ent-ClothingBackpackERTChaplain = ERT chaplain backpack + .desc = A spacious backpack with lots of pockets, worn by Chaplains of an Emergency Response Team. ent-ClothingBackpackSyndicate = рюкзак Синдиката .desc = { ent-ClothingBackpack.desc } ent-ClothingBackpackHolding = бездонный рюкзак diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/hardsuit-helmets.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/hardsuit-helmets.ftl index e1a959ae021..e9d28459c71 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/hardsuit-helmets.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/hardsuit-helmets.ftl @@ -49,6 +49,8 @@ ent-ClothingHeadHelmetCBURN = шлем отряда РХБЗЗ ent-ClothingHeadHelmetHardsuitPirateCap = шлем древнего скафандра .desc = Специальный шлем скафандра, изготвленный для капитана пиратского корабля. .suffix = Пират +ent-ClothingHeadHelmetHardsuitERTChaplain = ERT chaplain hardsuit helmet + .desc = A special hardsuit helmet worn by members of an emergency response team. ent-ClothingHeadHelmetHardsuitPirateEVA = шлем EVA космических глубин .desc = Шлем скафандра EVA космических глубин, очень тяжелый, но обеспечивает неплохую защиту. .suffix = Пират diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/helmets.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/helmets.ftl index cac5541df97..2aea06c12b0 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/helmets.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/helmets.ftl @@ -16,8 +16,6 @@ ent-ClothingHeadHelmetJanitorBombSuit = сапёрно-уборочный шле .suffix = НЕ МАППИТЬ ent-ClothingHeadHelmetRiot = легкий противоударный шлем .desc = Это шлем был специально разработан для защиты в ближнем бою. -ent-ClothingHeadHelmetScaf = шлем скафандра - .desc = Надежный, крепкий шлем. ent-ClothingHeadHelmetSpaceNinja = шлем космического ниндзя .desc = То, что может показаться простой черной одеждой, на самом деле является сложнейшим шлемом с нано-плетением. Стандартное снаряжение ниндзя. ent-ClothingHeadHelmetTemplar = шлем тамплиера diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/misc.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/misc.ftl index e346af0c2ec..ff1132d9d87 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/misc.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/misc.ftl @@ -4,10 +4,8 @@ ent-ClothingHeadHatCake = шляпа с тортом .desc = Вы надели торт на голову. Гениально. ent-ClothingHeadHatChickenhead = голова цыплёнка .desc = Это голова цыплёнка. Бок-бок-бок! -ent-ClothingHeadHatFlowerCrown = цветочная корона - .desc = Корона из свежих и душистых цветов. -ent-ClothingHeadHatHairflower = цветок для волос - .desc = Красный цветок для прекрасных дам. +ent-ClothingHeadHatFlowerWreath = flower wreath + .desc = A wreath of colourful flowers. Can be worn both on head and neck. ent-ClothingHeadHatPumpkin = шляпа из тыквы .desc = Светильник Джека! Считается, что он отпугивает злых духов. ent-ClothingHeadHatPwig = парик diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/neck/misc.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/neck/misc.ftl index 8d31981eb19..69d074097fa 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/neck/misc.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/neck/misc.ftl @@ -8,5 +8,3 @@ ent-ClothingNeckLawyerbadge = значок адвоката .desc = Значок, свидетельствующий что владелец является "законным" юристом, сдавшим экзамен Nanotrasen, необходимый для занятия юридической практикой. ent-ActionStethoscope = Прослушать стетоскопом .desc = { "" } -ent-ClothingNeckFlowerWreath = цветочный венок - .desc = Венок из разноцветных цветов. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/armor.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/armor.ftl index bd45004777d..ceb2f253621 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/armor.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/armor.ftl @@ -21,8 +21,6 @@ ent-ClothingOuterArmorMagusblue = синие доспехи магуса .desc = Синий бронекостюм, обеспечивающий хорошую защиту. ent-ClothingOuterArmorMagusred = красные доспехи магуса .desc = Красный бронекостюм, обеспечивающий хорошую защиту. -ent-ClothingOuterArmorScaf = боевая броня - .desc = Зеленый с коричневым тактический костюм для ведения боевых действий. ent-ClothingOuterArmorCaptainCarapace = панцирь капитана .desc = Бронированный нагрудник, обеспечивающий защиту и при этом обладающий мобильностью и гибкостью. Выдается только лучшим представителям станции. ent-ClothingOuterArmorChangeling = хитиновый панцирь diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/base_clothingouter.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/base_clothingouter.ftl index 6a0c2c58758..2c023a864a1 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/base_clothingouter.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/base_clothingouter.ftl @@ -4,6 +4,11 @@ ent-ClothingOuterBaseLarge = { ent-ClothingOuterBase } .desc = { ent-ClothingOuterBase.desc } ent-ClothingOuterStorageBase = { ent-ClothingOuterBase } .desc = { ent-ClothingOuterBase.desc } +ent-ClothingOuterStorageFoldableBase = { ent-ClothingOuterStorageBase } + .desc = { ent-ClothingOuterStorageBase.desc } +ent-ClothingOuterStorageFoldableBaseOpened = { ent-ClothingOuterStorageFoldableBase } + .suffix = opened + .desc = { ent-ClothingOuterStorageFoldableBase.desc } ent-ClothingOuterStorageToggleableBase = { ent-ClothingOuterStorageBase } .desc = { ent-ClothingOuterStorageBase.desc } ent-ClothingOuterHardsuitBase = base hardsuit diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/coats.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/coats.ftl index 6d906a17eaf..40f568d6bb4 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/coats.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/coats.ftl @@ -14,20 +14,36 @@ ent-ClothingOuterCoatTrench = тренчкот .desc = Удобный тренч. ent-ClothingOuterCoatLab = лабораторный халат .desc = Халат, защищающий от небольших разливов химикатов. +ent-ClothingOuterCoatLabOpened = lab coat + .desc = { ent-ClothingOuterCoatLab.desc } ent-ClothingOuterCoatLabChem = лабораторный халат химика .desc = Халат, защищающий от небольших разливов химикатов. Имеет оранжевые полосы на плечах. +ent-ClothingOuterCoatLabChemOpened = chemist lab coat + .desc = { ent-ClothingOuterCoatLabChem.desc } ent-ClothingOuterCoatLabViro = лабораторный халат вирусолога .desc = Халат, защищающий от бактерий и вирусов. Имеет зелёные полосы на плечах. +ent-ClothingOuterCoatLabViroOpened = virologist lab coat + .desc = { ent-ClothingOuterCoatLabViro.desc } ent-ClothingOuterCoatLabGene = лабораторный халат генетика .desc = Халат, защищающий от небольших разливов химикатов. Имеет синие полосы на плечах. +ent-ClothingOuterCoatLabGeneOpened = geneticist lab coat + .desc = { ent-ClothingOuterCoatLabGene.desc } ent-ClothingOuterCoatLabCmo = лабораторный халат главного врача .desc = Синее, чем стандартная модель. +ent-ClothingOuterCoatLabCmoOpened = chief medical officer's lab coat + .desc = { ent-ClothingOuterCoatLabCmo.desc } ent-ClothingOuterCoatRnd = лабораторный халат учёного .desc = Халат, защищающий от небольших разливов химикатов. Имеет фиолетовые полосы на плечах. +ent-ClothingOuterCoatRndOpened = scientist lab coat + .desc = { ent-ClothingOuterCoatRnd.desc } ent-ClothingOuterCoatRobo = лабораторный халат робототехника .desc = Больше похоже на эксцентричное пальто, чем на лабораторный халат. Помогает выдать пятна крови за эстетическую составляющую. Имеет красные полосы на плечах. +ent-ClothingOuterCoatRoboOpened = roboticist lab coat + .desc = { ent-ClothingOuterCoatRobo.desc } ent-ClothingOuterCoatRD = лабораторный халат научрука .desc = Соткан по новейшим технологиям, этот халат обеспечивает защиту от радиации так же как и экспериментальный скафандр. +ent-ClothingOuterCoatRDOpened = research director lab coat + .desc = { ent-ClothingOuterCoatRD.desc } ent-ClothingOuterCoatPirate = одежда пирата .desc = Яррр. ent-ClothingOuterCoatWarden = бронированная куртка смотрителя diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/hardsuits.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/hardsuits.ftl index 9af237f8e1a..d11f8e0ea69 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/hardsuits.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/outerclothing/hardsuits.ftl @@ -53,6 +53,8 @@ ent-ClothingOuterHardsuitCBURN = экзокостюм отряда РХБЗЗ .desc = Легковесный но прочный экзокостюм, который используют специальные подразделения зачистки. ent-ClothingOuterHardsuitERTLeader = скафандр лидера ОБР .desc = Защитный скафандр, используемый лидерами отряда быстрого реагирования. +ent-ClothingOuterHardsuitERTChaplain = ERT chaplain's hardsuit + .desc = A protective hardsuit worn by the chaplains of an Emergency Response Team. ent-ClothingOuterHardsuitERTEngineer = скафандр инженера ОБР .desc = Защитный скафандр, используемый инженерами отряда быстрого реагирования. ent-ClothingOuterHardsuitERTMedical = скафандр медика ОБР diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/uniforms/jumpsuits.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/uniforms/jumpsuits.ftl index 0cfb441d5f8..a6878decdfa 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/uniforms/jumpsuits.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/uniforms/jumpsuits.ftl @@ -194,6 +194,8 @@ ent-ClothingUniformJumpsuitMonasticRobeLight = светлая монашеска .desc = Это светлая ряса, которую часто носят религиозные люди. ent-ClothingUniformJumpsuitMusician = костюм из карпокожи .desc = Роскошный костюм, сшитый только из лучших чешуек, идеально подходит для любого концерта в стиле лаунж! +ent-ClothingUniformJumpsuitERTChaplain = ERT chaplain uniform + .desc = A special suit made for Central Command's elite chaplain corps. ent-ClothingUniformJumpsuitERTEngineer = униформа инженера ОБР .desc = Специальный костюм, созданный для элитных инженеров Центкома. ent-ClothingUniformJumpsuitERTJanitor = униформа уборщика ОБР diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/effects/rcd.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/effects/rcd.ftl index 4217dee5c40..37a7e669feb 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/effects/rcd.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/effects/rcd.ftl @@ -1,2 +1,22 @@ -ent-EffectRCDConstruction = { "" } +ent-EffectRCDBase = { "" } .desc = { "" } +ent-EffectRCDDeconstructPreview = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDConstruct0 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDConstruct1 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDConstruct2 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDConstruct3 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDConstruct4 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDDeconstruct2 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDDeconstruct4 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDDeconstruct6 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } +ent-EffectRCDDeconstruct8 = { ent-EffectRCDBase } + .desc = { ent-EffectRCDBase.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/jobs.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/jobs.ftl index 714edf78ba9..fc3f6104dc7 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/jobs.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/jobs.ftl @@ -81,6 +81,8 @@ ent-SpawnPointBrigmedic = бригмедик .desc = { ent-SpawnPointJobBase.desc } ent-SpawnPointERTLeader = лидер ОБР .desc = { ent-SpawnPointJobBase.desc } +ent-SpawnPointERTChaplain = ERTchaplain + .desc = { ent-SpawnPointJobBase.desc } ent-SpawnPointERTEngineer = инженер ОБР .desc = { ent-SpawnPointJobBase.desc } ent-SpawnPointERTMedical = медик ОБР diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/mobs/player/humanoid.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/mobs/player/humanoid.ftl index 004f120fdbd..c2cc4004fbf 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/mobs/player/humanoid.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/mobs/player/humanoid.ftl @@ -10,6 +10,12 @@ ent-RandomHumanoidSpawnerERTLeaderEVA = ОБР лидер ent-RandomHumanoidSpawnerERTLeaderEVALecter = { ent-RandomHumanoidSpawnerERTLeaderEVA } .suffix = Роль ОБР, Лектер, EVA .desc = { ent-RandomHumanoidSpawnerERTLeaderEVA.desc } +ent-RandomHumanoidSpawnerERTChaplain = ERT chaplain + .suffix = ERTRole, Basic + .desc = { ent-RandomHumanoidSpawnerERTLeader.desc } +ent-RandomHumanoidSpawnerERTChaplainEVA = ERT chaplain + .suffix = ERTRole, Enviro EVA + .desc = { ent-RandomHumanoidSpawnerERTChaplain.desc } ent-RandomHumanoidSpawnerERTJanitor = ОБР уборщик .suffix = Роль ОБР, Базовый .desc = { ent-RandomHumanoidSpawnerERTLeader.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks-cartons.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks-cartons.ftl index 3a6477ef412..df602b3eedf 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks-cartons.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks-cartons.ftl @@ -10,6 +10,8 @@ ent-DrinkJuiceOrangeCarton = апельсиновый сок .desc = Полное витаминов удовольствие! ent-DrinkJuiceTomatoCarton = томатный сок .desc = Ну, по крайней мере, это ВЫГЛЯДИТ как томатный сок. По этой красноте ничего не скажешь. +ent-DrinkCoconutWaterCarton = coconut water + .desc = It's the inside of the coconut that counts. ent-DrinkCreamCarton = молочные сливки .desc = Это сливки. Сделанные из молока. Что ещё вы ожидали здесь найти? ent-DrinkMilkCarton = молоко diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks.ftl index b4a2dfbbce4..41a0b1b0f0e 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks.ftl @@ -30,6 +30,9 @@ ent-DrinkAndalusia = { ent-DrinkGlass } ent-DrinkAntifreeze = { ent-DrinkGlass } .suffix = Антифриз .desc = { ent-DrinkGlass.desc } +ent-DrinkArnoldPalmer = { ent-DrinkGlass } + .suffix = arnold palmer + .desc = { ent-DrinkGlass.desc } ent-DrinkAtomicBombGlass = { ent-DrinkGlass } .suffix = Александратомная бомба .desc = { ent-DrinkGlass.desc } @@ -63,6 +66,9 @@ ent-DrinkBlueCuracaoGlass = { ent-DrinkGlass } ent-DrinkBloodyMaryGlass = { ent-DrinkGlass } .suffix = Кровавая Мэри .desc = { ent-DrinkGlass.desc } +ent-DrinkBlueHawaiianGlass = { ent-DrinkGlass } + .suffix = blue hawaiian + .desc = { ent-DrinkGlass.desc } ent-DrinkBooger = { ent-DrinkGlass } .suffix = Козявка .desc = { ent-DrinkGlass.desc } @@ -75,12 +81,21 @@ ent-DrinkCarrotJuice = { ent-DrinkGlass } ent-DrinkChocolateGlass = { ent-DrinkGlass } .suffix = Горячий шоколад .desc = { ent-DrinkGlass.desc } +ent-DrinkCoconutRum = { ent-DrinkGlass } + .suffix = coconut rum + .desc = { ent-DrinkGlass.desc } +ent-DrinkCoconutWaterGlass = { ent-DrinkGlass } + .suffix = coconut water + .desc = { ent-DrinkGlass.desc } ent-DrinkCoffee = { ent-DrinkGlass } .suffix = Кофе .desc = { ent-DrinkGlass.desc } ent-DrinkCognacGlass = { ent-DrinkGlass } .suffix = Коньяк .desc = { ent-DrinkGlass.desc } +ent-DrinkCosmopolitan = { ent-DrinkGlass } + .suffix = cosmopolitan + .desc = { ent-DrinkGlass.desc } ent-DrinkCream = { ent-DrinkGlass } .suffix = Сливки .desc = { ent-DrinkGlass.desc } @@ -234,9 +249,15 @@ ent-DrinkNuclearColaGlass = { ent-DrinkGlass } ent-DrinkOrangeJuice = { ent-DrinkGlass } .suffix = Апельсиновый сок .desc = { ent-DrinkGlass.desc } +ent-DrinkPainkillerGlass = { ent-DrinkGlass } + .suffix = painkiller + .desc = { ent-DrinkGlass.desc } ent-DrinkPatronGlass = { ent-DrinkGlass } .suffix = Покровитель .desc = { ent-DrinkGlass.desc } +ent-DrinkPinaColadaGlass = { ent-DrinkGlass } + .suffix = piña colada + .desc = { ent-DrinkGlass.desc } ent-DrinkPoisonBerryJuice = { ent-DrinkGlass } .suffix = Ядовитый ягодный сок .desc = { ent-DrinkGlass.desc } @@ -261,6 +282,9 @@ ent-DrinkRootBeerFloatGlass = { ent-DrinkGlass } ent-DrinkRumGlass = { ent-DrinkGlass } .suffix = Ром .desc = { ent-DrinkGlass.desc } +ent-DrinkRoyRogersGlass = { ent-DrinkGlass } + .suffix = roy rogers + .desc = { ent-DrinkGlass.desc } ent-DrinkSakeGlass = { ent-DrinkGlass } .suffix = Саке .desc = { ent-DrinkGlass.desc } @@ -285,12 +309,18 @@ ent-DrinkMoonshineGlass = { ent-DrinkGlass } ent-DrinkGlassWhite = { ent-DrinkGlass } .suffix = Молоко .desc = { ent-DrinkGlass.desc } +ent-DrinkShirleyTempleGlass = { ent-DrinkGlass } + .suffix = shirley temple + .desc = { ent-DrinkGlass.desc } ent-DrinkSilencerGlass = { ent-DrinkGlass } .suffix = Глушитель .desc = { ent-DrinkGlass.desc } ent-DrinkSingulo = { ent-DrinkGlass } .suffix = Сингуло .desc = { ent-DrinkGlass.desc } +ent-DrinkSolDryGlass = { ent-DrinkGlass } + .suffix = sol dry + .desc = { ent-DrinkGlass.desc } ent-DrinkSnowWhite = { ent-DrinkGlass } .suffix = Белый снег .desc = { ent-DrinkGlass.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_bottles.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_bottles.ftl index f2040454ae8..4e7f1df995e 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_bottles.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_bottles.ftl @@ -77,6 +77,8 @@ ent-DrinkMeadJug = кувшин медовухи .desc = Хранение медовухи в пластиковом кувшине должно считаться преступлением. ent-DrinkIceJug = кувшин льда .desc = Твёрдая вода. Очень круто. +ent-DrinkCoconutWaterJug = coconut water jug + .desc = It's on the inside of the coconut that counts. ent-DrinkCoffeeJug = кувшин кофе .desc = Пробуждающий сок, подогретый. ent-DrinkTeaJug = кувшин чая diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl index 9352afff253..6b0e800f935 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl @@ -19,6 +19,8 @@ ent-DrinkSpaceMountainWindCan = банка космического маунти .desc = Проходит сквозь, словно космический ветер. ent-DrinkSpaceUpCan = банка спейс-ап .desc = На вкус как пробоина в корпусе у вас во рту. +ent-DrinkSolDryCan = sol dry + .desc = Sweet ginger soda from outer space! ent-DrinkStarkistCan = банка старкист .desc = Вкус жидкой звезды. И, немного тунца...? ent-DrinkTonicWaterCan = банка тоника diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/circuitboards/machine/production.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/circuitboards/machine/production.ftl index 5b4f344e20e..33013de6f91 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/circuitboards/machine/production.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/circuitboards/machine/production.ftl @@ -158,3 +158,5 @@ ent-ShuttleGunDusterCircuitboard = EXP-2100g "Дастер" (машинная п .desc = Печатная плата EXP-2100g "Дастер". ent-ShuttleGunKineticCircuitboard = PTK-800 "Дематериализатор материи" (машинная плата) .desc = Печатная плата PTK-800 "Дематериализатор материи". +ent-ReagentGrinderIndustrialMachineCircuitboard = industrial reagent grinder machine board + .desc = { ent-BaseMachineCircuitboard.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/electronics/door_access.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/electronics/door_access.ftl new file mode 100644 index 00000000000..4f31e573c29 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/electronics/door_access.ftl @@ -0,0 +1,99 @@ +ent-DoorElectronicsService = { ent-DoorElectronics } + .suffix = Service, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsTheatre = { ent-DoorElectronics } + .suffix = Theatre, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsChapel = { ent-DoorElectronics } + .suffix = Chapel, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsJanitor = { ent-DoorElectronics } + .suffix = Janitor, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsKitchen = { ent-DoorElectronics } + .suffix = Kitchen, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsBar = { ent-DoorElectronics } + .suffix = Bar, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsHydroponics = { ent-DoorElectronics } + .suffix = Hydroponics, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsCaptain = { ent-DoorElectronics } + .suffix = Captain, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsExternal = { ent-DoorElectronics } + .suffix = External, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsCargo = { ent-DoorElectronics } + .suffix = Cargo, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsEngineering = { ent-DoorElectronics } + .suffix = Engineering, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsAtmospherics = { ent-DoorElectronics } + .suffix = Atmospherics, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsFreezer = { ent-DoorElectronics } + .suffix = Freezer, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsSalvage = { ent-DoorElectronics } + .suffix = Salvage, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsMedical = { ent-DoorElectronics } + .suffix = Medical, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsChemistry = { ent-DoorElectronics } + .suffix = Chemistry, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsResearch = { ent-DoorElectronics } + .suffix = Research, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsScience = { ent-DoorElectronics } + .suffix = Science, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsCommand = { ent-DoorElectronics } + .suffix = Command, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsChiefMedicalOfficer = { ent-DoorElectronics } + .suffix = ChiefMedicalOfficer, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsChiefEngineer = { ent-DoorElectronics } + .suffix = ChiefEngineer, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsHeadOfSecurity = { ent-DoorElectronics } + .suffix = HeadOfSecurity, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsResearchDirector = { ent-DoorElectronics } + .suffix = ResearchDirector, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsHeadOfPersonnel = { ent-DoorElectronics } + .suffix = HeadOfPersonnel, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsQuartermaster = { ent-DoorElectronics } + .suffix = Quartermaster, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsSecurity = { ent-DoorElectronics } + .suffix = Security, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsDetective = { ent-DoorElectronics } + .suffix = Detective, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsBrig = { ent-DoorElectronics } + .suffix = Brig, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsArmory = { ent-DoorElectronics } + .suffix = Armory, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsVault = { ent-DoorElectronics } + .suffix = Vault, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsMaintenance = { ent-DoorElectronics } + .suffix = Maintenance, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsSyndicateAgent = { ent-DoorElectronics } + .suffix = SyndicateAgent, Locked + .desc = { ent-DoorElectronics.desc } +ent-DoorElectronicsRnDMed = { ent-DoorElectronics } + .suffix = Medical/Science, Locked + .desc = { ent-DoorElectronics.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/books.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/books.ftl index b7ec6126fc6..f6536149d50 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/books.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/books.ftl @@ -29,10 +29,9 @@ ent-BookChemicalCompendium = Химпендиум ent-BookRandom = { ent-BookBase } .suffix = случайный .desc = { ent-BookBase.desc } -ent-BookEscalation = Правила Роберта по эскалации - .desc = На книге имеются пятна крови. Похоже, что её больше использовали как оружие, чем как учебное пособие. -ent-BookEscalationSecurity = Правила Роберта по эскалации: Издание для СБ - .desc = На книге имеются пятна крови. Похоже, что её больше использовали как оружие, чем как учебное пособие. +ent-BookRandomStory = { ent-BookRandom } + .suffix = random visual, random story + .desc = { ent-BookRandom.desc } ent-BookAtmosDistro = Руководство Ньютона по атмосу: Дистро .desc = Поля усеяны бесконечными неразборчивыми заметками. Большая часть текста испещрена рукописными вопросительными знаками. ent-BookAtmosWaste = Руководство Ньютона по атмосу: Отходы diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/books_author.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/books_author.ftl new file mode 100644 index 00000000000..dbd557ac5be --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/books_author.ftl @@ -0,0 +1,62 @@ +ent-BookNarsieLegend = the legend of nar'sie + .desc = The book is an old, leather-bound tome with intricate engravings on the cover. The pages are yellowed and fragile with age, with the ink of the text faded in some places. It appears to have been well-read and well-loved, with dog-eared pages and marginalia scrawled in the margins. Despite its aged appearance, the book still exudes a sense of mystical power and wonder, hinting at the secrets and knowledge contained within its pages. +ent-BookTruth = exploring different philosophical perspectives on truth and the complexity of lying + .desc = A book exploring the different philosophical perspectives on truth and lying has a worn cover, with creases and marks indicating frequent use and thoughtful contemplation. The spine shows signs of wear from being pulled off the shelf again and again. The pages themselves are filled with underlines, notes in the margins, and highlighted passages as readers grapple with the nuances and complexities of the topic. +ent-BookWorld = shaping the state of the world - interplay of forces and choices + .desc = The book is a well-preserved hardcover with a simple, elegant design on the cover, depicting the image of a world in motion. The pages are crisp and clean, with no signs of wear or tear, suggesting that it has been well-cared for and valued by its previous owner. The text is printed in a clear, legible font, and the chapters are organized in a logical and easy-to-follow manner, making it accessible to readers of all levels of expertise. +ent-BookIanAntarctica = adventures of robert & ian - exploring antarctica + .desc = The book is a small paperback in good condition, with an illustration of Ian the corgi and the colony of penguins on the cover. The title, "Ian and Robert's Antarctic Adventure", is written in bold white letters against a blue background. The back cover features a brief summary of the story, highlighting the themes of humility, resilience, and the beauty of nature. +ent-BookSlothClownSSS = the sloth and the clown - space station shenanigans + .desc = The book looks new, with a glossy cover featuring Chuckles the clown and Snuggles the sloth floating in space with a backdrop of stars and planets. Chuckles is dressed in his banana costume and Snuggles is sleeping on a hammock made of space ropes. The title "The Sloth and the Clown - Space Station Shenanigans" is written in bold and colorful letters. +ent-BookSlothClownPranks = the sloth and the clown - pranks on zorgs + .desc = The book is in excellent condition, with crisp pages and a bright cover. The cover of the book features Chuckles and Snuggles, surrounded by the different species they encountered during their adventures in space. In the background, the Zorgs can be seen peeking out from behind a spaceship. +ent-BookSlothClownMMD = the sloth and the clown - maze maze danger + .desc = The book looks new and vibrant, with an image of Chuckles and Snuggles standing in front of the changing maze on the cover. The title "The Sloth and the Clown - Maze Maze Danger" is written in bold, colorful letters that pop against a background of space and stars. +ent-BookStruck = the humbling and transformative experience of being struck by lightning + .desc = The cover of the book is an electrifying image of lightning striking the ground, with a silhouette of a person standing in the midst of it. The title is written in bold letters in white against a black background, conveying the power and intensity of the experience. The subtitle is written in smaller letters below the title, providing a hint of the philosophical and spiritual themes explored within. +ent-BookSun = reaching for the sun - a plant's quest for life + .desc = The book is new, with a bright and vibrant cover featuring a plant stretching its leaves towards the sun. The title, "Reaching for the Sun - A Plant's Quest for Life," is written in bold, green letters, with an image of the sun rising behind the plant. The cover evokes a sense of growth, energy, and the beauty of nature. +ent-BookPossum = fallen ambitions - the tragic tale of morty the possum + .desc = The book is in good condition, with a hardcover and a dark green forest background. In the center of the cover, there is a sad looking possum sitting on a branch, with a distant and lonely expression on its face. The title, "Fallen Ambitions - The Tragic Tale of Morty the Possum," is written in bold, gold letters above the possum. +ent-BookCafe = the cafe possum + .desc = The book is in new condition, with a vibrant and whimsical cover that features a charming illustration of a tiny possum peeking out from behind a coffee cup, with a colorful and bustling cafe scene in the background. The title "The Cafe Possum" is written in bold, playful lettering, and the author's name is printed in a smaller font below it. +ent-BookFeather = a feather of magic - the wandering bird's journey to belonging + .desc = The book would be in new condition, with a glossy cover depicting the wandering bird surrounded by a glowing forest, with the magical feather at the center. The title, "A Feather of Magic," would be written in bold, glittering letters, while the subtitle, "The Wandering Bird's Journey to Belonging," would be written in smaller print underneath. The back cover would feature a brief summary of the story, along with reviews from critics praising the book's themes of hope and renewal. +ent-BookIanLostWolfPup = the adventures of ian and renault - finding the lost wolf pup + .desc = The book is a new condition with a colorful cover, depicting Ian the corgi and Renault the fox on a journey through the forest, with the lost wolf pup to their feet. The title "The Adventures of Ian and Renault - Finding the Lost Wolf Pup" is prominently displayed at the top, with the author's name below. The cover has a whimsical and adventurous feel to it, attracting readers of all ages. +ent-BookIanRanch = the adventures of ian and renault - ranch expedition + .desc = The book appears to be new, with crisp pages and an unblemished cover. The cover features a colorful illustration of Ian and Renault, surrounded by various animals they encountered on the ranch, including horses, cows, and chickens. The title, "The Adventures of Ian and Renault - Ranch Expedition," is written in bold letters above the image, with the subtitle, "Helping Animals in Need," written below. +ent-BookIanOcean = the adventures of ian and renault - an ocean adventure + .desc = The book is new and in excellent condition. The cover shows Ian and Renault running and playing on the beach, with the blue ocean and golden sand in the background. The title is written in bold, playful letters, and the subtitle reads "An Ocean Adventure." +ent-BookIanMountain = the adventures of ian and renault - A mountain expedition + .desc = The book is in new condition. The cover is a stunning mountain landscape with Ian and Renault in the foreground, looking out over the vista of the surrounding peaks and valleys. The title is written in bold, block letters at the top, with the subtitle, "A Mountain Expedition," written underneath. +ent-BookIanCity = the adventures of ian and renault - exploring the city + .desc = The book is in new condition, with crisp pages and a glossy cover. The cover features a colorful illustration of Ian and Renault exploring the city, with tall buildings and bustling streets in the background. Ian is leading the way, with his tail wagging excitedly, while Renault follows close behind, her ears perked up and her eyes wide with wonder. The title, "The Adventures of Ian and Renault," is written in bold, playful letters, with the subtitle, "Exploring the City," written below in smaller font. +ent-BookIanArctic = the adventures of ian and renault - an arctic journey of courage and friendship + .desc = The book looks new and adventurous, with a picture of Ian and Renault standing in front of an icy landscape with snowflakes falling all around them. The title, "The Adventures of Ian and Renault," is written in bold letters at the top, with a subtitle that reads, "An Arctic Journey of Courage and Friendship." +ent-BookIanDesert = the adventures of ian and renault - exploring the mysterious desert + .desc = The book is in new condition and would have a colorful cover depicting Ian and Renault against a desert backdrop. The cover would feature images of various animals and plants that the two encountered on their adventure, such as a rattlesnake, coyotes, sand dunes, and an oasis. The title, "The Adventures of Ian and Renault" is prominently displayed on the cover in bold letters, while the subtitle "Exploring the Mysterious Desert" is written in smaller letters underneath. +ent-BookNames = the power of names - a philosophical exploration + .desc = The book is a gently used philosophy text, with a cover that features a close-up of a person's mouth, with the word "names" written on their lips. The title is "The Power of Names - A Philosophical Exploration," and the author's name is prominently displayed underneath. The overall design is simple and elegant, with the focus on the text rather than any flashy graphics or images. +ent-BookEarth = earthly longing + .desc = The book is in good condition, with a slightly faded cover due to exposure to sunlight. The cover of the book depicts a panoramic view of the Earth from space, with a bright blue ocean and green landmasses. In the foreground, a lone astronaut is seen sitting in front of a window, gazing wistfully at the Earth. The title of the book, "Earthly Longing," is written in bold white letters against a black background at the top of the cover. +ent-BookAurora = journey beyond - the starship aurora mission + .desc = The book is in excellent condition, with a shiny cover depicting a spaceship hovering above a planet, perhaps with the Earth in the background. The title "Journey Beyond - The Starship Aurora Mission" is written in bold, silver letters. The cover also features a quote from a review, "A breathtaking tale of human achievement and exploration" to entice potential readers. +ent-BookTemple = the nature of the divine - embracing the many gods + .desc = The book appears new with crisp pages and an uncreased spine. The cover features an image of a temple with a glowing, multicolored aura around it, symbolizing the various gods discussed in the book. The title is displayed prominently in gold lettering, with the author's name and a brief summary of the book written in smaller text below. +ent-BookWatched = watched + .desc = The book is in good condition, with a slightly worn cover that features a dark and ominous space station looming in the background. The title "Watched" is written in bold letters that seem to be staring back at the reader, conveying the feeling of being constantly observed. The blurb on the back cover hints at a thrilling and suspenseful tale of paranoia and danger in a confined setting. +ent-BookMedicalOfficer = horizon's battle - a medical officer's tale of trust and survival + .desc = The cover features Smith, the medical officer, in his uniform, looking determined and ready to face any challenge. The backdrop shows the SS Horizon under attack, with explosions and smoke filling the space station. In the foreground, a wizard with a staff can be seen, adding an element of mystery and intrigue to the scene. The title is prominently displayed in bold letters, with the author's name and a tagline indicating the book's action-packed and suspenseful nature. +ent-BookMorgue = the ghostly residents of the abandoned morgue + .desc = The book looks old and worn, with faded lettering on the cover. The cover depicts a dark and eerie morgue, with a full moon casting an ominous glow over the scene. In the foreground are Morty the possum and Morticia the raccoon, with mischievous expressions on their faces, peeking out from behind a metal shelf. The title is written in bold, spooky letters, with the subtitle "A Tale of Animal Spirits" written in smaller font below. +ent-BookRufus = rufus and the mischievous fairy + .desc = The book is in new condition, with vibrant colors and illustrations on the cover. The cover shows Rufus on his bicycle, with Blossom flying beside him in a playful manner. The title is written in bold, whimsical font, with the characters' names highlighted in a contrasting color. The overall aesthetic is charming and inviting, appealing to children and adults alike. +ent-BookMap = the map of adventure + .desc = The book is in a good condition, with a glossy cover depicting a jungle scene with vibrant colors and intricate details. The title "The Map of Adventure," is written in bold, gold lettering. The cover also features an image of a mysterious suitcase with the map spilling out of it. +ent-BookJourney = a journey of music, mountains, and self-discovery + .desc = The book is in excellent condition, with crisp pages and a glossy cover. The cover features a striking image of a mountain range, with a silhouette of a climber with a guitar on their back in the foreground. The title is bold and eye-catching, with the subtitle "A Journey of Music, Mountains, and Self-Discovery." +ent-BookInspiration = finding inspiration - a writer's journey through the woods + .desc = The book is in a new condition with a cover depicting a serene forest scene with a waterfall and colorful wildflowers. The title of the book "Finding Inspiration - A Writer's Journey Through the Woods" and the author's name are prominently displayed at the bottom. +ent-BookJanitorTale = the tales of a tired janitor + .desc = A clean looking book, smelling vaguely of soap and bleach. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/briefcases.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/briefcases.ftl index 94e59f50998..a929ea4e48c 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/briefcases.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/briefcases.ftl @@ -2,8 +2,6 @@ ent-BriefcaseBase = { ent-BaseItem } .desc = Пригодится для переноски предметов в руках. ent-BriefcaseBrown = коричневый чемодан .desc = Удобный чемоданчик. -ent-BriefcaseSyndieBase = { ent-BriefcaseBase } - .desc = Пригодится для переноски предметов в руках. - .suffix = Синдикат, Пустой ent-BriefcaseSyndie = коричневый чемодан .desc = Удобный чемоданчик. + .suffix = Syndicate, Empty diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/cds.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/cds.ftl new file mode 100644 index 00000000000..23434de6c4a --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/cds.ftl @@ -0,0 +1,2 @@ +ent-CoordinatesDisk = coordinates disk + .desc = A disk containing the coordinates to a location in space. Necessary for any FTL-traversing vessel to reach their destination. Fits inside shuttle consoles. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/diskcases.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/diskcases.ftl new file mode 100644 index 00000000000..e4000db46f5 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/diskcases.ftl @@ -0,0 +1,2 @@ +ent-DiskCase = diskcase + .desc = Case for storing a coordinates disk. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/janitorial/janitor.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/janitorial/janitor.ftl index ead206098c8..ff70375f3e0 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/janitorial/janitor.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/janitorial/janitor.ftl @@ -2,20 +2,11 @@ ent-MopItem = швабра .desc = Швабра, которую невозможно остановить, viscera cleanup detail ждёт вас. ent-AdvMopItem = продвинутая швабра .desc = Моторизованная швабра с увеличенным резервуаром и возможностью уборки нескольких луж одновременно. Система автоматического противодействия клоунам в комплект не входит. -ent-MopBucket = ведро для швабры - .desc = Содержит воду и слезы уборщика. -ent-MopBucketFull = ведро для швабры - .suffix = Полный - .desc = { ent-MopBucket.desc } ent-WetFloorSign = знак "мокрый пол" .desc = Осторожно! Мокрый пол! ent-WetFloorSignMineExplosive = { ent-WetFloorSign } .desc = { ent-WetFloorSign.desc } .suffix = Взрывчатка -ent-JanitorialTrolley = тележка уборщика - .desc = Это альфа и омега санитарии. -ent-FloorDrain = дренаж - .desc = Сливает лужи вокруг в себя. Пригодится для опорожнения ведер или поддержания чистоты в определенных помещениях. ent-Plunger = вантуз .desc = Вантуз с красной пластиковой присоской и деревянной ручкой. Используется для прочистки засоров. ent-RagItem = тряпка diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/syndicate.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/syndicate.ftl index bdf02fbfab6..17acbfda8ae 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/syndicate.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/syndicate.ftl @@ -29,4 +29,6 @@ ent-BaseUplinkRadio60TC = { ent-BaseUplinkRadio } ent-BaseUplinkRadioDebug = радио аплинк Синдиката .suffix = DEBUG .desc = { ent-BaseUplinkRadio.desc } + # Corvax-HiddenDesc-End + diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/guns/projectiles/projectiles.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/guns/projectiles/projectiles.ftl index c39c9c8686c..51f560cd545 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/guns/projectiles/projectiles.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/guns/projectiles/projectiles.ftl @@ -46,6 +46,10 @@ ent-AnomalousParticleZetaStrong = { ent-AnomalousParticleZeta } .desc = { ent-AnomalousParticleZeta.desc } ent-AnomalousParticleOmegaStrong = омега-частицы .desc = { ent-AnomalousParticleDelta.desc } +ent-AnomalousParticleSigma = sigma particles + .desc = { ent-AnomalousParticleDelta.desc } +ent-AnomalousParticleSigmaStrong = sigma particles + .desc = { ent-AnomalousParticleSigma.desc } ent-BulletRocket = ракета .desc = { ent-BaseBulletTrigger.desc } ent-BulletWeakRocket = слабая ракета diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/throwable/grenades.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/throwable/grenades.ftl index 19f2213f90f..6ac130d8fd6 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/throwable/grenades.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/throwable/grenades.ftl @@ -37,5 +37,6 @@ ent-GrenadeDummy = граната-обманка # Corvax-HiddenDesc-Start ent-SyndieTrickyBomb = хитроумная бомба .desc = Взрывное устройство, больше отвлекающее внимание, чем наносящее реальный вред. + # Corvax-HiddenDesc-End diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/base_structure.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/base_structure.ftl index 887761c9f3e..d38b843a347 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/base_structure.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/base_structure.ftl @@ -2,3 +2,5 @@ ent-BaseStructure = { "" } .desc = { "" } ent-BaseStructureDynamic = { ent-BaseStructure } .desc = { ent-BaseStructure.desc } +ent-StructureWheeled = { "" } + .desc = { "" } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/airlocks/airlocks.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/airlocks/airlocks.ftl index 71b4c9d16da..c2ad311460c 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/airlocks/airlocks.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/airlocks/airlocks.ftl @@ -43,8 +43,6 @@ ent-AirlockHatch = герметичный люк .desc = { ent-Airlock.desc } ent-AirlockHatchMaintenance = герметичный люк техобслуживания .desc = { ent-Airlock.desc } -ent-AirlockGlass = стеклянный шлюз - .desc = { ent-Airlock.desc } ent-AirlockEngineeringGlass = { ent-AirlockGlass } .suffix = Инженерный .desc = { ent-AirlockGlass.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/airlocks/base_structureairlocks.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/airlocks/base_structureairlocks.ftl index da86a989a0f..fee1dbb2aa3 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/airlocks/base_structureairlocks.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/airlocks/base_structureairlocks.ftl @@ -1,2 +1,6 @@ ent-Airlock = шлюз .desc = Он открывается, он закрывается, и он может вас раздавить. +ent-AirlockRCDResistant = { ent-Airlock } + .desc = { ent-Airlock.desc } +ent-AirlockGlass = glass airlock + .desc = { ent-Airlock.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/shutter/blast_door_autolink.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/shutter/blast_door_autolink.ftl index 577d4316bec..8eed743fec5 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/shutter/blast_door_autolink.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/shutter/blast_door_autolink.ftl @@ -22,6 +22,18 @@ ent-BlastDoorBridge = { ent-BlastDoor } ent-BlastDoorBridgeOpen = { ent-BlastDoorOpen } .suffix = Открытый, Автолинк, Мостик .desc = { ent-BlastDoorOpen.desc } +ent-BlastDoorEngineering = { ent-BlastDoor } + .suffix = Autolink, Engineering + .desc = { ent-BlastDoor.desc } +ent-BlastDoorEngineeringOpen = { ent-BlastDoorOpen } + .suffix = Open, Autolink, Engineering + .desc = { ent-BlastDoorOpen.desc } +ent-BlastDoorScience = { ent-BlastDoor } + .suffix = Autolink, Science + .desc = { ent-BlastDoor.desc } +ent-BlastDoorScienceOpen = { ent-BlastDoorOpen } + .suffix = Open, Autolink, Science + .desc = { ent-BlastDoorOpen.desc } ent-BlastDoorWindows = { ent-BlastDoor } .suffix = Автолинк, Окна .desc = { ent-BlastDoor.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/machines/nuke.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/machines/nuke.ftl new file mode 100644 index 00000000000..1968816c7bc --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/machines/nuke.ftl @@ -0,0 +1,8 @@ +ent-NuclearBomb = nuclear fission explosive + .desc = You probably shouldn't stick around to see if this is armed. +ent-NuclearBombUnanchored = { ent-NuclearBomb } + .suffix = unanchored + .desc = { ent-NuclearBomb.desc } +ent-NuclearBombKeg = nuclear fission explosive + .desc = You probably shouldn't stick around to see if this is armed. It has a tap on the side. + .suffix = keg diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/machines/reagent_grinder.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/machines/reagent_grinder.ftl index 205767c992f..bc82a2c5225 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/machines/reagent_grinder.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/machines/reagent_grinder.ftl @@ -1,3 +1,5 @@ ent-KitchenReagentGrinder = измельчитель реагентов .desc = От БлендерТех. Будет ли он работать? Давайте узнаем! .suffix = измельчитель/соковыжималка +ent-ReagentGrinderIndustrial = industrial reagent grinder + .desc = An industrial reagent grinder. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/specific/janitor/drain.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/specific/janitor/drain.ftl new file mode 100644 index 00000000000..e775fbcd086 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/specific/janitor/drain.ftl @@ -0,0 +1,2 @@ +ent-FloorDrain = drain + .desc = Drains puddles around it. Useful for dumping mop buckets or keeping certain rooms clean. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/specific/janitor/janicart.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/specific/janitor/janicart.ftl new file mode 100644 index 00000000000..785a94d510a --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/specific/janitor/janicart.ftl @@ -0,0 +1,7 @@ +ent-MopBucket = mop bucket + .desc = Holds water and the tears of the janitor. +ent-MopBucketFull = mop bucket + .suffix = full + .desc = { ent-MopBucket.desc } +ent-JanitorialTrolley = janitorial trolley + .desc = This is the alpha and omega of sanitation. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/storage/tanks/base_structuretanks.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/storage/tanks/base_structuretanks.ftl index 05a7261db16..7673a506c47 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/storage/tanks/base_structuretanks.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/storage/tanks/base_structuretanks.ftl @@ -1,2 +1,4 @@ ent-StorageTank = резервуар .desc = Резервуар для хранения жидкостей. +ent-StorageTankBig = { ent-StorageTank } + .desc = { ent-StorageTank.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/windows/window.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/windows/window.ftl index 2374127e5a2..315019e5c0e 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/windows/window.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/windows/window.ftl @@ -1,7 +1,11 @@ ent-Window = окно .desc = Смотри не заляпай. +ent-WindowRCDResistant = { ent-Window } + .desc = { ent-Window.desc } ent-WindowDirectional = направленное окно .desc = Смотри не заляпай. +ent-WindowDirectionalRCDResistant = { ent-WindowDirectional } + .desc = { ent-WindowDirectional.desc } ent-WindowFrostedDirectional = направленное матовое окно .desc = Смотри не заляпай. ent-WindowDiagonal = { ent-Window } diff --git a/Resources/Locale/ru-RU/storage/components/secret-stash-component.ftl b/Resources/Locale/ru-RU/storage/components/secret-stash-component.ftl index fac013082c8..fe7a23f8343 100644 --- a/Resources/Locale/ru-RU/storage/components/secret-stash-component.ftl +++ b/Resources/Locale/ru-RU/storage/components/secret-stash-component.ftl @@ -5,5 +5,6 @@ comp-secret-stash-action-hide-success = Вы спрятали { $item } в { $th comp-secret-stash-action-hide-container-not-empty = Здесь уже что-то есть?! comp-secret-stash-action-hide-item-too-big = { $item } слишком большой, чтобы поместиться в { $stash }! comp-secret-stash-action-get-item-found-something = Внутри { $stash } что-то было! +comp-secret-stash-on-examine-found-hidden-item = There is something hidden inside. secret-stash-part-plant = растение secret-stash-part-toilet = бачок унитаза diff --git a/Resources/Locale/ru-RU/store/store.ftl b/Resources/Locale/ru-RU/store/store.ftl index d47bb23b1ac..6497bebefe2 100644 --- a/Resources/Locale/ru-RU/store/store.ftl +++ b/Resources/Locale/ru-RU/store/store.ftl @@ -5,3 +5,4 @@ store-ui-price-display = { $amount } { $currency } store-ui-traitor-flavor = Копирайт (C) NT -30643 store-ui-traitor-warning = Оперативники должны блокировать свои аплинки после использования во избежание обнаружения. store-withdraw-button-ui = Вывести { $currency } +store-not-account-owner = This { $store } is not bound to you! diff --git a/Resources/Locale/ru-RU/toilet/toilet-component.ftl b/Resources/Locale/ru-RU/toilet/toilet-component.ftl index fec24eda25d..cb9f48796e8 100644 --- a/Resources/Locale/ru-RU/toilet/toilet-component.ftl +++ b/Resources/Locale/ru-RU/toilet/toilet-component.ftl @@ -7,3 +7,4 @@ toilet-component-suicide-message-others = { $victim } бьётся об { $owner toilet-component-suicide-message = Вы бьётесь об { $owner }! toilet-seat-close = Опустить сидушку toilet-seat-open = Поднять сидушку +plunger-unblock = You unblock the { THE($target) }! diff --git a/Resources/Locale/ru-RU/traits/traits.ftl b/Resources/Locale/ru-RU/traits/traits.ftl index 0a98ee737c8..141f3bfb27d 100644 --- a/Resources/Locale/ru-RU/traits/traits.ftl +++ b/Resources/Locale/ru-RU/traits/traits.ftl @@ -21,5 +21,7 @@ trait-frontal-lisp-name = Сигматизм trait-frontal-lisp-desc = У ваф имеютшя проблемы ш произношением. trait-socialanxiety-name = Социофобия trait-socialanxiety-desc = Вы испытываете тревожность, когда говорите, что приводит к заиканию. +trait-southern-name = Southern Drawl +trait-southern-desc = You have a different way of speakin'. trait-snoring-name = Храп trait-snoring-desc = Вы храпите во время сна. diff --git a/Resources/Locale/ru-RU/ui/general.ftl b/Resources/Locale/ru-RU/ui/general.ftl new file mode 100644 index 00000000000..afb8cdb7af9 --- /dev/null +++ b/Resources/Locale/ru-RU/ui/general.ftl @@ -0,0 +1,4 @@ +### Loc for the various UI-related verbs + +ui-verb-toggle-open = Toggle UI +verb-instrument-openui = Play Music From 0c357625e0f61bee81e744abf698d7715d293608 Mon Sep 17 00:00:00 2001 From: Morb0 <14136326+Morb0@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:17:44 +0300 Subject: [PATCH 133/133] Fix Avrit --- Resources/Maps/corvax_avrit.yml | 79 +++++---------------------------- 1 file changed, 11 insertions(+), 68 deletions(-) diff --git a/Resources/Maps/corvax_avrit.yml b/Resources/Maps/corvax_avrit.yml index 8508a72d428..12cab43da48 100644 --- a/Resources/Maps/corvax_avrit.yml +++ b/Resources/Maps/corvax_avrit.yml @@ -25000,12 +25000,6 @@ entities: parent: 24719 - type: InstantAction container: 24719 - - uid: 43185 - components: - - type: Transform - parent: 43177 - - type: InstantAction - container: 43177 - uid: 43291 components: - type: Transform @@ -57630,58 +57624,6 @@ entities: rot: 3.141592653589793 rad pos: 15.5,-41.5 parent: 2 -- proto: BaseMedicalPDA - entities: - - uid: 43177 - components: - - type: Transform - pos: 2.534728,-1.5658815 - parent: 43176 - - type: ContainerContainer - containers: - PDA-id: !type:ContainerSlot - showEnts: False - occludes: True - ent: null - PDA-pen: !type:ContainerSlot - showEnts: False - occludes: True - ent: null - PDA-pai: !type:ContainerSlot - showEnts: False - occludes: True - ent: null - Cartridge-Slot: !type:ContainerSlot - showEnts: False - occludes: True - ent: null - program-container: !type:Container - showEnts: False - occludes: True - ents: [] - actions: !type:Container - showEnts: False - occludes: True - ents: - - 43185 - - type: UnpoweredFlashlight - toggleActionEntity: 43185 - - type: ActionsContainer - - uid: 44696 - components: - - type: Transform - pos: 1.440978,-1.6127565 - parent: 43176 - - uid: 44707 - components: - - type: Transform - pos: 0.37847805,-1.3783815 - parent: 43176 - - uid: 44722 - components: - - type: Transform - pos: -0.74652195,-1.5502565 - parent: 43176 - proto: BaseSecretDoorAssembly entities: - uid: 44510 @@ -60494,7 +60436,7 @@ entities: parent: 2 - type: Paper content: >2+ - [head=2]Пособие по качественному + [head=2]Пособие по качественному обслуживанию в баре[/head] [bullet][bold]Автор: Майя Линхариева @@ -60520,7 +60462,7 @@ entities: [bullet][head=3]Пункт 3: Напитки[/head] - Старайтесь меньше пользоваться раздатчиками. Бесспорно, они даны вам для удобства и ими проще всего готовить напитки. Однако, у вас имееься полный алкомат добра, шейкеры, бутылки, но при этом вы их не используете. Намного лучше подойти и лично налить клиенту напиток прямо за стойкой, используя всё те же ингредиенты. Слишком много нужно смешать? Используйте шейкер! Демонстрируя посетителям свои навыки, вы лишь утвердите себя как эксперта в своём деле. + Старайтесь меньше пользоваться раздатчиками. Бесспорно, они даны вам для удобства и ими проще всего готовить напитки. Однако, у вас имееься полный алкомат добра, шейкеры, бутылки, но при этом вы их не используете. Намного лучше подойти и лично налить клиенту напиток прямо за стойкой, используя всё те же ингредиенты. Слишком много нужно смешать? Используйте шейкер! Демонстрируя посетителям свои навыки, вы лишь утвердите себя как эксперта в своём деле. [bullet][head=3]Пункт 4: На выдачу[/head] @@ -235864,6 +235806,7 @@ entities: - type: Transform pos: 10.5,-50.5 parent: 2 + - proto: MedicalTechFab entities: - uid: 21722 @@ -238180,7 +238123,7 @@ entities: [bold]Отчёт:[/bold] - В ходе операции на объекте [color=blue][bold]NanoTrasen[/bold][/color] была установлена слежка за целью, в лице [color=#d4a03f][bold]Квартирмейстера[/color] [color=#a31c1c]Д.Виеш[/color][/bold]. Искомое устройство находится при ней. Передаю дело по краже в руки другого Агента Синдиката. + В ходе операции на объекте [color=blue][bold]NanoTrasen[/bold][/color] была установлена слежка за целью, в лице [color=#d4a03f][bold]Квартирмейстера[/color] [color=#a31c1c]Д.Виеш[/color][/bold]. Искомое устройство находится при ней. Передаю дело по краже в руки другого Агента Синдиката. В ходе операции пришлось устранить невиновного грузчика, дабы получить доступы и форму. Тело было утилизировано в соответствии с методичкой организации. @@ -238446,7 +238389,7 @@ entities: parent: 44845 - type: Paper content: >2- - + [head=2]Приглашаем на экскурсию по кухне[/head] Приходите на кухню в тех помещениях и да узрейте все прелести готовки @@ -239581,8 +239524,8 @@ entities: [color=#B50F1D] ███░██████░███[/color] =================================================== - [head=3]ДОКУМЕНТАЦИЯ - КОНТРОЛЬНО-ПРОПУСКНОГО + [head=3]ДОКУМЕНТАЦИЯ + КОНТРОЛЬНО-ПРОПУСКНОГО ПУНКТА[/head] =================================================== @@ -240023,7 +239966,7 @@ entities: [color=#B50F1D] ███░██████░███[/color] =================================================== - [head=3]ДОКУМЕНТАЦИЯ + [head=3]ДОКУМЕНТАЦИЯ ОБ ИЗУЧЕНИЯХ И ЭКСПЕРИМЕНТАХ[/head] =================================================== @@ -240073,7 +240016,7 @@ entities: [color=#B50F1D] ███░██████░███[/color] =================================================== - [head=3]ДОКУМЕНТАЦИЯ + [head=3]ДОКУМЕНТАЦИЯ ОБ ИЗУЧЕНИЯХ И ЭКСПЕРИМЕНТАХ[/head] =================================================== @@ -240091,7 +240034,7 @@ entities: ───────────────────────────────────────── - [bullet][bold] Эксперимент №517 "Демон, рождённый войной" - представляет собой крупное плотоядное двуногое животное неопределённого происхождения, ростом примерно 4,5 м и весом около 600 кг. Внешне оно выглядит как гибрид нескольких видов млекопитающих. Существо имеет две длинных обезьяньих руки, с выдвижными когтями около 8 см в длину. Ноги напоминают медвежьи. Голова отдалённо напоминает кошачью, с мощными челюстями и крупными, близко посаженными глазами, однако снабжена двумя рогами, подобным таковым у газели. Всё тело существа покрыто гладким чёрно-красным мехом, который проявляет значительные огнезащитные свойства. [italic]Приписка "Атакует любой живой организм (кроме своих приспешников) в поле зрения в припадке, казалось бы, бессмысленной ярости."[/italic] + [bullet][bold] Эксперимент №517 "Демон, рождённый войной" - представляет собой крупное плотоядное двуногое животное неопределённого происхождения, ростом примерно 4,5 м и весом около 600 кг. Внешне оно выглядит как гибрид нескольких видов млекопитающих. Существо имеет две длинных обезьяньих руки, с выдвижными когтями около 8 см в длину. Ноги напоминают медвежьи. Голова отдалённо напоминает кошачью, с мощными челюстями и крупными, близко посаженными глазами, однако снабжена двумя рогами, подобным таковым у газели. Всё тело существа покрыто гладким чёрно-красным мехом, который проявляет значительные огнезащитные свойства. [italic]Приписка "Атакует любой живой организм (кроме своих приспешников) в поле зрения в припадке, казалось бы, бессмысленной ярости."[/italic] [bullet] Был найден на планете Кладерус-4, в объекте №2539, отрядом добровольных оперативников №3 под командованием "ЛГБТ Инструктора". [/bold] @@ -240102,7 +240045,7 @@ entities: parent: 45711 - type: Paper content: >2- - + [italic] От "Кардашьян" 'ГУС'[/italic] [head=3]Внимание персоналу[/head]