From b6e101b96f19800ef23c7486efa0004dec6d44f2 Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Sat, 19 Apr 2025 04:17:13 +0200 Subject: [PATCH 01/16] Prevent certain foldable items from being unfolded on structures (#36687) initial commit --- .../EntitySystems/AnchorableSystem.cs | 5 +++- .../Foldable/DeployFoldableSystem.cs | 12 +++++++++ Content.Shared/Foldable/FoldableSystem.cs | 26 +++++++++++++++---- .../Interaction/SharedInteractionSystem.cs | 2 +- .../components/foldable-component.ftl | 3 +++ 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs index a291473b30..ec8edea474 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -277,7 +277,10 @@ public sealed partial class AnchorableSystem : EntitySystem return !attempt.Cancelled; } - private bool TileFree(EntityCoordinates coordinates, PhysicsComponent anchorBody) + /// + /// Returns true if no hard anchored entities exist on the coordinate tile that would collide with the provided physics body. + /// + public bool TileFree(EntityCoordinates coordinates, PhysicsComponent anchorBody) { // Probably ignore CanCollide on the anchoring body? var gridUid = _transformSystem.GetGrid(coordinates); diff --git a/Content.Shared/Foldable/DeployFoldableSystem.cs b/Content.Shared/Foldable/DeployFoldableSystem.cs index 16315cde69..a83518dfa3 100644 --- a/Content.Shared/Foldable/DeployFoldableSystem.cs +++ b/Content.Shared/Foldable/DeployFoldableSystem.cs @@ -1,7 +1,10 @@ +using Content.Shared.Construction.EntitySystems; using Content.Shared.DragDrop; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; +using Content.Shared.Popups; +using Robust.Shared.Physics.Components; namespace Content.Shared.Foldable; @@ -9,6 +12,8 @@ public sealed class DeployFoldableSystem : EntitySystem { [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly FoldableSystem _foldable = default!; + [Dependency] private readonly AnchorableSystem _anchorable = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() { @@ -57,6 +62,13 @@ public sealed class DeployFoldableSystem : EntitySystem if (!TryComp(ent, out var foldable)) return; + if (!TryComp(ent.Owner, out PhysicsComponent? anchorBody) + || !_anchorable.TileFree(args.ClickLocation, anchorBody)) + { + _popup.PopupPredicted(Loc.GetString("foldable-deploy-fail", ("object", ent)), ent, args.User); + return; + } + if (!TryComp(args.User, out HandsComponent? hands) || !_hands.TryDrop(args.User, args.Used, targetDropLocation: args.ClickLocation, handsComp: hands)) return; diff --git a/Content.Shared/Foldable/FoldableSystem.cs b/Content.Shared/Foldable/FoldableSystem.cs index 3ba4201e79..73916f1c15 100644 --- a/Content.Shared/Foldable/FoldableSystem.cs +++ b/Content.Shared/Foldable/FoldableSystem.cs @@ -1,9 +1,11 @@ -using Content.Shared.Body.Components; using Content.Shared.Buckle; using Content.Shared.Buckle.Components; +using Content.Shared.Construction.EntitySystems; +using Content.Shared.Popups; using Content.Shared.Storage.Components; using Content.Shared.Verbs; using Robust.Shared.Containers; +using Robust.Shared.Physics.Components; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -15,6 +17,8 @@ public sealed class FoldableSystem : EntitySystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedBuckleSystem _buckle = default!; [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly AnchorableSystem _anchorable = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() { @@ -83,9 +87,17 @@ public sealed class FoldableSystem : EntitySystem args.Cancel(); } - public bool TryToggleFold(EntityUid uid, FoldableComponent comp) + public bool TryToggleFold(EntityUid uid, FoldableComponent comp, EntityUid? folder = null) { - return TrySetFolded(uid, comp, !comp.IsFolded); + var result = TrySetFolded(uid, comp, !comp.IsFolded); + if (!result && folder != null) + { + if (comp.IsFolded) + _popup.PopupPredicted(Loc.GetString("foldable-unfold-fail", ("object", uid)), uid, folder.Value); + else + _popup.PopupPredicted(Loc.GetString("foldable-fold-fail", ("object", uid)), uid, folder.Value); + } + return result; } public bool CanToggleFold(EntityUid uid, FoldableComponent? fold = null) @@ -97,6 +109,10 @@ public sealed class FoldableSystem : EntitySystem if (_container.IsEntityInContainer(uid) && !fold.CanFoldInsideContainer) return false; + if (!TryComp(uid, out PhysicsComponent? body) || + !_anchorable.TileFree(Transform(uid).Coordinates, body)) + return false; + var ev = new FoldAttemptEvent(fold); RaiseLocalEvent(uid, ref ev); return !ev.Cancelled; @@ -121,12 +137,12 @@ public sealed class FoldableSystem : EntitySystem private void AddFoldVerb(EntityUid uid, FoldableComponent component, GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract || args.Hands == null || !CanToggleFold(uid, component)) + if (!args.CanAccess || !args.CanInteract || args.Hands == null) return; AlternativeVerb verb = new() { - Act = () => TryToggleFold(uid, component), + Act = () => TryToggleFold(uid, component, args.User), Text = component.IsFolded ? Loc.GetString(component.UnfoldVerbText) : Loc.GetString(component.FoldVerbText), Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/fold.svg.192dpi.png")), diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 00246bafcd..eeb961537b 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -855,7 +855,7 @@ namespace Content.Shared.Interaction { // If the target is an item, we ignore any colliding entities. Currently done so that if items get stuck // inside of walls, users can still pick them up. - ignored.UnionWith(_broadphase.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics)); + ignored.UnionWith(_broadphase.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics)); // Note: This also bypasses items underneath doors, which may be problematic if it'd cause undesirable behavior. } else if (_wallMountQuery.TryComp(target, out var wallMount)) { diff --git a/Resources/Locale/en-US/foldable/components/foldable-component.ftl b/Resources/Locale/en-US/foldable/components/foldable-component.ftl index 525820920b..1221efbdf0 100644 --- a/Resources/Locale/en-US/foldable/components/foldable-component.ftl +++ b/Resources/Locale/en-US/foldable/components/foldable-component.ftl @@ -1,5 +1,8 @@ # Foldable +foldable-fold-fail = You can't fold the {$object} here. +foldable-unfold-fail = You can't unfold the {$object} here. + foldable-deploy-fail = You can't deploy the {$object} here. fold-verb = Fold unfold-verb = Unfold From be8fa724a759682bbae3cbb6fd36a3ce64aba5f4 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 19 Apr 2025 12:31:21 +1000 Subject: [PATCH 02/16] Fix import (#36706) --- .../Atmos/Piping/Unary/Components/GasOutletInjectorComponent.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Server/Atmos/Piping/Unary/Components/GasOutletInjectorComponent.cs b/Content.Server/Atmos/Piping/Unary/Components/GasOutletInjectorComponent.cs index 65755d62c5..8cc3f21436 100644 --- a/Content.Server/Atmos/Piping/Unary/Components/GasOutletInjectorComponent.cs +++ b/Content.Server/Atmos/Piping/Unary/Components/GasOutletInjectorComponent.cs @@ -1,6 +1,7 @@ using Content.Server.Atmos.Piping.Binary.Components; using Content.Server.Atmos.Piping.Unary.EntitySystems; using Content.Shared.Atmos; +using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Guidebook; namespace Content.Server.Atmos.Piping.Unary.Components From 9599f582f3e08c711431910b2f4043fc8fdf75cb Mon Sep 17 00:00:00 2001 From: SharkSnake98 Date: Sat, 19 Apr 2025 01:12:10 -0400 Subject: [PATCH 03/16] New Drinks (#36287) Co-authored-by: RedBookcase Co-authored-by: SharkSnake98 --- .../Locale/en-US/flavors/flavor-profiles.ftl | 14 +- .../meta/consumable/drink/alcohol.ftl | 36 ++++ .../Random/Food_Drinks/drinks_glass.yml | 12 ++ .../Objects/Consumable/Drinks/drinks.yml | 195 +++++++++++++++++- Resources/Prototypes/Flavors/flavors.yml | 60 ++++++ .../Reagents/Consumable/Drink/alcohol.yml | 165 +++++++++++++++ .../Reagents/Consumable/Drink/drinks.yml | 15 ++ .../Prototypes/Recipes/Reactions/drinks.yml | 158 +++++++++++++- .../alienbrainhemorrhage.rsi/fill-1.png | Bin 0 -> 168 bytes .../alienbrainhemorrhage.rsi/fill-2.png | Bin 0 -> 219 bytes .../alienbrainhemorrhage.rsi/fill-3.png | Bin 0 -> 228 bytes .../alienbrainhemorrhage.rsi/fill-4.png | Bin 0 -> 249 bytes .../alienbrainhemorrhage.rsi/fill-5.png | Bin 0 -> 258 bytes .../Drinks/alienbrainhemorrhage.rsi/icon.png | Bin 0 -> 306 bytes .../alienbrainhemorrhage.rsi/icon_empty.png | Bin 0 -> 222 bytes .../Drinks/alienbrainhemorrhage.rsi/meta.json | 34 +++ .../Consumable/Drinks/bronx.rsi/fill-1.png | Bin 0 -> 144 bytes .../Consumable/Drinks/bronx.rsi/fill-2.png | Bin 0 -> 162 bytes .../Consumable/Drinks/bronx.rsi/fill-3.png | Bin 0 -> 180 bytes .../Consumable/Drinks/bronx.rsi/fill-4.png | Bin 0 -> 183 bytes .../Consumable/Drinks/bronx.rsi/icon.png | Bin 0 -> 258 bytes .../Drinks/bronx.rsi/icon_empty.png | Bin 0 -> 255 bytes .../Consumable/Drinks/bronx.rsi/meta.json | 31 +++ .../Drinks/crushdepth.rsi/fill-1.png | Bin 0 -> 150 bytes .../Drinks/crushdepth.rsi/fill-2.png | Bin 0 -> 168 bytes .../Drinks/crushdepth.rsi/fill-3.png | Bin 0 -> 186 bytes .../Drinks/crushdepth.rsi/fill-4.png | Bin 0 -> 207 bytes .../Drinks/crushdepth.rsi/fill-5.png | Bin 0 -> 237 bytes .../Consumable/Drinks/crushdepth.rsi/icon.png | Bin 0 -> 306 bytes .../Drinks/crushdepth.rsi/icon_empty.png | Bin 0 -> 276 bytes .../Drinks/crushdepth.rsi/meta.json | 34 +++ .../Drinks/dark&stormy.rsi/fill-1.png | Bin 0 -> 147 bytes .../Drinks/dark&stormy.rsi/fill-2.png | Bin 0 -> 174 bytes .../Drinks/dark&stormy.rsi/fill-3.png | Bin 0 -> 201 bytes .../Drinks/dark&stormy.rsi/fill-4.png | Bin 0 -> 216 bytes .../Drinks/dark&stormy.rsi/fill-5.png | Bin 0 -> 216 bytes .../Drinks/dark&stormy.rsi/icon.png | Bin 0 -> 273 bytes .../Drinks/dark&stormy.rsi/icon_empty.png | Bin 0 -> 231 bytes .../Drinks/dark&stormy.rsi/meta.json | 34 +++ .../Drinks/electricshark.rsi/fill-1.png | Bin 0 -> 153 bytes .../Drinks/electricshark.rsi/fill-2.png | Bin 0 -> 177 bytes .../Drinks/electricshark.rsi/fill-3.png | Bin 0 -> 189 bytes .../Drinks/electricshark.rsi/fill-4.png | Bin 0 -> 201 bytes .../Drinks/electricshark.rsi/fill-5.png | Bin 0 -> 213 bytes .../Drinks/electricshark.rsi/icon.png | Bin 0 -> 348 bytes .../Drinks/electricshark.rsi/icon_empty.png | Bin 0 -> 339 bytes .../Drinks/electricshark.rsi/meta.json | 34 +++ .../Consumable/Drinks/jackrose.rsi/fill-1.png | Bin 0 -> 144 bytes .../Consumable/Drinks/jackrose.rsi/fill-2.png | Bin 0 -> 159 bytes .../Consumable/Drinks/jackrose.rsi/fill-3.png | Bin 0 -> 177 bytes .../Consumable/Drinks/jackrose.rsi/fill-4.png | Bin 0 -> 195 bytes .../Consumable/Drinks/jackrose.rsi/icon.png | Bin 0 -> 291 bytes .../Drinks/jackrose.rsi/icon_empty.png | Bin 0 -> 276 bytes .../Consumable/Drinks/jackrose.rsi/meta.json | 31 +++ .../Drinks/junglebird.rsi/fill-1.png | Bin 0 -> 159 bytes .../Drinks/junglebird.rsi/fill-2.png | Bin 0 -> 192 bytes .../Drinks/junglebird.rsi/fill-3.png | Bin 0 -> 189 bytes .../Drinks/junglebird.rsi/fill-4.png | Bin 0 -> 189 bytes .../Consumable/Drinks/junglebird.rsi/icon.png | Bin 0 -> 300 bytes .../Drinks/junglebird.rsi/icon_empty.png | Bin 0 -> 273 bytes .../Drinks/junglebird.rsi/meta.json | 31 +++ .../Drinks/kalimotxo.rsi/fill-1.png | Bin 0 -> 165 bytes .../Drinks/kalimotxo.rsi/fill-2.png | Bin 0 -> 183 bytes .../Drinks/kalimotxo.rsi/fill-3.png | Bin 0 -> 183 bytes .../Drinks/kalimotxo.rsi/fill-4.png | Bin 0 -> 195 bytes .../Drinks/kalimotxo.rsi/fill-5.png | Bin 0 -> 210 bytes .../Consumable/Drinks/kalimotxo.rsi/icon.png | Bin 0 -> 291 bytes .../Drinks/kalimotxo.rsi/icon_empty.png | Bin 0 -> 243 bytes .../Consumable/Drinks/kalimotxo.rsi/meta.json | 34 +++ .../Drinks/monkeybusiness.rsi/fill-1.png | Bin 0 -> 144 bytes .../Drinks/monkeybusiness.rsi/fill-2.png | Bin 0 -> 162 bytes .../Drinks/monkeybusiness.rsi/fill-3.png | Bin 0 -> 180 bytes .../Drinks/monkeybusiness.rsi/fill-4.png | Bin 0 -> 183 bytes .../Drinks/monkeybusiness.rsi/icon.png | Bin 0 -> 231 bytes .../Drinks/monkeybusiness.rsi/icon_empty.png | Bin 0 -> 213 bytes .../Drinks/monkeybusiness.rsi/meta.json | 31 +++ .../Consumable/Drinks/radler.rsi/fill-1.png | Bin 0 -> 144 bytes .../Consumable/Drinks/radler.rsi/fill-2.png | Bin 0 -> 165 bytes .../Consumable/Drinks/radler.rsi/fill-3.png | Bin 0 -> 201 bytes .../Consumable/Drinks/radler.rsi/fill-4.png | Bin 0 -> 201 bytes .../Consumable/Drinks/radler.rsi/fill-5.png | Bin 0 -> 213 bytes .../Consumable/Drinks/radler.rsi/icon.png | Bin 0 -> 267 bytes .../Drinks/radler.rsi/icon_empty.png | Bin 0 -> 231 bytes .../Consumable/Drinks/radler.rsi/meta.json | 34 +++ .../Consumable/Drinks/tortuga.rsi/fill-1.png | Bin 0 -> 156 bytes .../Consumable/Drinks/tortuga.rsi/fill-2.png | Bin 0 -> 198 bytes .../Consumable/Drinks/tortuga.rsi/fill-3.png | Bin 0 -> 210 bytes .../Consumable/Drinks/tortuga.rsi/fill-4.png | Bin 0 -> 240 bytes .../Consumable/Drinks/tortuga.rsi/icon.png | Bin 0 -> 363 bytes .../Drinks/tortuga.rsi/icon_empty.png | Bin 0 -> 315 bytes .../Consumable/Drinks/tortuga.rsi/meta.json | 31 +++ .../Consumable/Drinks/vampiro.rsi/fill-1.png | Bin 0 -> 177 bytes .../Consumable/Drinks/vampiro.rsi/fill-2.png | Bin 0 -> 213 bytes .../Consumable/Drinks/vampiro.rsi/fill-3.png | Bin 0 -> 240 bytes .../Consumable/Drinks/vampiro.rsi/fill-4.png | Bin 0 -> 285 bytes .../Consumable/Drinks/vampiro.rsi/icon.png | Bin 0 -> 354 bytes .../Drinks/vampiro.rsi/icon_empty.png | Bin 0 -> 291 bytes .../Consumable/Drinks/vampiro.rsi/meta.json | 31 +++ 98 files changed, 1042 insertions(+), 3 deletions(-) create mode 100644 Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-5.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/fill-5.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-5.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-5.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-5.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/radler.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/radler.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/radler.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/radler.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/radler.rsi/fill-5.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/radler.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/radler.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/radler.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/meta.json create mode 100644 Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/fill-1.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/fill-2.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/fill-3.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/fill-4.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/icon.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/icon_empty.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/meta.json diff --git a/Resources/Locale/en-US/flavors/flavor-profiles.ftl b/Resources/Locale/en-US/flavors/flavor-profiles.ftl index 38efd407ae..14e4ed5adb 100644 --- a/Resources/Locale/en-US/flavors/flavor-profiles.ftl +++ b/Resources/Locale/en-US/flavors/flavor-profiles.ftl @@ -236,8 +236,11 @@ 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 flavor-complex-root-beer-float = like ice cream in root beer +flavor-complex-crush-depth = like the Hadal Zone flavor-complex-black-russian = like alcoholic coffee flavor-complex-white-russian = like alcoholic sweetened coffee +flavor-complex-electric-shark = like Shark Week in the tropics +flavor-complex-tortuga = like sweet tea flavor-complex-moonshine = like pure alcohol flavor-complex-singulo = like a bottomless hole flavor-complex-syndie-bomb = like bitter whiskey @@ -253,6 +256,12 @@ flavor-complex-atomic-cola = like hoarding bottle caps flavor-complex-cuba-libre = like spiked cola flavor-complex-gin-tonic = refreshingly bitter flavor-complex-screwdriver = like spiked orange juice +flavor-complex-jack-rose = like a testimony +flavor-complex-jungle-bird = like you’re in a tropical aviary +flavor-complex-kalimotxo = like fancy spiked cola +flavor-complex-vampiro = fruity, savoury, and spicy +flavor-complex-bronx = like mildly sweet, alcoholic fruit +flavor-complex-monkey-business = like going ape flavor-complex-vodka-red-bool = like a heart attack flavor-complex-irish-bool = like caffeine and Ireland flavor-complex-xeno-basher = like killing bugs @@ -260,13 +269,14 @@ flavor-complex-budget-insuls-drink = like door hacking flavor-complex-watermelon-wakeup = like a sweet wakeup call flavor-complex-rubberneck = like synthetics flavor-complex-irish-slammer = like a spiked cola float +flavor-complex-alien-brain-hemorrhage = like an extraterrestrial injury flavor-complex-themartinez = like violets and lemon vodka flavor-complex-cogchamp = like brass flavor-complex-white-gilgamesh = like lightly carbonated cream flavor-complex-antifreeze = warm flavor-complex-caipirinha = like Brazil flavor-complex-daiquiri = like rum, lime and sugar -flavor-complex-deathintheafternoon = like anise and champagne +flavor-complex-deathintheafternoon = like anise and champagne flavor-complex-empress75 = like tyrian purple flavor-complex-espressomartini = like vodka and coffee flavor-complex-mayojito = like stomach turmoil @@ -294,6 +304,7 @@ flavor-complex-driest-martini = like a drunk mimic flavor-complex-erika-surprise = like the bartender made a mistake flavor-complex-gin-fizz = refreshing and lemony flavor-complex-gildlager = like the Tzar's gold +flavor-complex-dark-and-stormy = like ginger ale spiked with rum flavor-complex-grog = like a sea shanty flavor-complex-hippies-delight = like your blood pressure is dropping flavor-complex-hooch = like it would be delicious if you were a diesel engine @@ -304,6 +315,7 @@ flavor-complex-martini = like a spy movie flavor-complex-mojito = like going into the shade after being in the hot sun flavor-complex-neurotoxin = like an underground testing facility flavor-complex-patron = like being serenaded by mariachi +flavor-complex-radler = like spiked lemonade flavor-complex-red-mead = like a viking battle flavor-complex-sbiten = like fire flavor-complex-snowwhite = like sour and bitter hops 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 5a22328900..df9e6ce78f 100644 --- a/Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl +++ b/Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl @@ -61,6 +61,9 @@ reagent-desc-champagne = A premium sparkling wine reagent-name-acid-spit = acidspit reagent-desc-acid-spit = A drink for the daring, can be deadly if incorrectly prepared! +reagent-name-alien-brain-hemorrhage = alien brain hemorrhage +reagent-desc-alien-brain-hemorrhage = You might want to get that checked out at Med. + reagent-name-allies-cocktail = allies cocktail reagent-desc-allies-cocktail = A drink made from your allies, not as sweet as when made from your enemies. @@ -109,15 +112,24 @@ reagent-desc-booger = Ewww... reagent-name-brave-bull = Brave Bull reagent-desc-brave-bull = It's just as effective as Dutch-Courage! +reagent-name-bronx = Bronx +reagent-desc-bronx = The orange-flavoured cousin of the Manhattan and Martini. + 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-crush-depth = crush depth +reagent-desc-crush-depth = A stygian drink, harkening back to the abyssopelagic. Dark and Cold, it serves as a reminder that the most ancient emotion is fear, and the strongest type of fear is that of the unknown. + reagent-name-cuba-libre = Cuba libre reagent-desc-cuba-libre = Rum, mixed with cola. Viva la revolucion. +reagent-name-dark-and-stormy = dark & stormy +reagent-desc-dark-and-stormy = You can almost hear the thunder. + reagent-name-demons-blood = Demon's Blood reagent-desc-demons-blood = AHHHH!!!! @@ -130,6 +142,9 @@ reagent-desc-doctors-delight = A gulp a day keeps the MediBot away. That's proba reagent-name-driest-martini = driest martini reagent-desc-driest-martini = Only for the experienced. You think you see sand floating in the glass. +reagent-name-electric-shark = electric shark +reagent-desc-electric-shark = Fun Shark fact: Selachians make up 20% of Space Station 16’s Engineering staff! + reagent-name-erika-surprise = Erika surprise reagent-desc-erika-surprise = The surprise is, it's green! @@ -166,9 +181,21 @@ reagent-desc-irish-cream = Whiskey-imbued cream. What else could you expect from reagent-name-irish-coffee = Irish coffee reagent-desc-irish-coffee = Coffee served with irish cream. Regular cream just isn't the same! +reagent-name-jack-rose = Jack rose +reagent-desc-jack-rose = Excessively Red. + +reagent-name-jungle-bird = jungle bird +reagent-desc-jungle-bird = Despite the name, it’s not exceptionally popular among Voxes. + +reagent-name-kalimotxo = kalimotxo +reagent-desc-kalimotxo = A high-class Cuba Libre, for the discerning alcoholic. + reagent-name-kira-special = Kira special reagent-desc-kira-special = Long live the guy who everyone had mistaken for a girl. Baka! +reagent-name-tortuga = Tortuga +reagent-desc-tortuga = Perfect for pirates who’ve been selected as the designated driver. Yarr! + reagent-name-long-island-iced-tea = Long Island iced tea reagent-desc-long-island-iced-tea = The liquor cabinet, brought together in a delicious mix. Intended for middle-aged alcoholic women only. @@ -196,6 +223,9 @@ reagent-desc-mead = A Viking's drink, though a cheap one. reagent-name-mojito = Mojito reagent-desc-mojito = If it's good enough for Spesscuba, it's good enough for you. +reagent-name-monkey-business = monkey business +reagent-desc-monkey-business = You’ve got to wonder how the monkeys feel about this drink. + reagent-name-moonshine = moonshine reagent-desc-moonshine = Artisanal homemade liquor. What could go wrong? @@ -211,6 +241,9 @@ reagent-desc-patron = Tequila with silver in it, a favorite of alcoholic women i reagent-name-pina-colada = Piña Colada reagent-desc-pina-colada = For getting lost in the rain. +reagent-name-radler = radler +reagent-desc-radler = A simple but staple classic, straight out of Space-Germany. + reagent-name-red-mead = red mead reagent-desc-red-mead = The true Viking's drink! Even though it has a strange red color. @@ -250,6 +283,9 @@ reagent-desc-three-mile-island = "Made for a woman, strong enough for a man." reagent-name-toxins-special = toxins special reagent-desc-toxins-special = This thing is ON FIRE! CALL THE DAMN SHUTTLE! +reagent-name-vampiro = vampiro +reagent-desc-vampiro = Popular in Mexico and Transylvania. + reagent-name-vodka-martini = vodka martini reagent-desc-vodka-martini = Vodka instead of Gin. Not quite how 007 enjoyed it, but still delicious. 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 c3a9a3a143..8504925528 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 @@ -16,6 +16,7 @@ prototypes: - DrinkAbsintheGlass - DrinkAleGlass + - DrinkAlienBrainHemorrhage - DrinkAloe - DrinkAndalusia - DrinkAntifreeze @@ -32,17 +33,21 @@ - DrinkBloodyMaryGlass - DrinkBooger - DrinkBraveBullGlass + - DrinkBronxGlass - BudgetInsulsDrinkGlass - DrinkCarrotJuice - DrinkCoconutRum - DrinkChocolateGlass - DrinkCognacGlass - DrinkCosmopolitan + - DrinkCrushDepthGlass - DrinkCubaLibreGlass + - DrinkDarkandStormyGlass - DrinkDeadRumGlass - DrinkDevilsKiss - DrinkDriestMartiniGlass - DrinkDrGibbGlass + - DrinkElectricSharkGlass - DrinkErikaSurprise - DrinkFourteenLokoGlass - DrinkGargleBlasterGlass @@ -62,6 +67,9 @@ - DrinkIrishSlammer - DrinkIrishCoffeeGlass - DrinkLemonadeGlass + - DrinkJackRoseGlass + - DrinkJungleBirdGlass + - DrinkKalimotxoGlass - DrinkKiraSpecial - DrinkLongIslandIcedTeaGlass - DrinkManhattanGlass @@ -71,11 +79,13 @@ - DrinkMeadGlass - DrinkMilkshake - DrinkMojito + - DrinkMonkeyBusinessGlass - DrinkNTCahors - DrinkPainkillerGlass - DrinkPatronGlass - DrinkPinaColadaGlass - DrinkPoscaGlass + - DrinkRadlerGlass - DrinkRedMeadGlass - DrinkRewriter - DrinkRoyRogersGlass @@ -92,7 +102,9 @@ - DrinkSyndicatebomb - DrinkTequilaSunriseGlass - DrinkThreeMileIslandGlass + - DrinkTortugaGlass - DrinkToxinsSpecialGlass + - DrinkVampiroGlass - DrinkVodkaMartiniGlass - DrinkVodkaRedBool - DrinkVodkaTonicGlass diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml index 1891ed71ec..9b50ed8a1e 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml @@ -187,6 +187,22 @@ sprite: Objects/Consumable/Drinks/aleglass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkAlienBrainHemorrhage + suffix: alien brain hemorrhage + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: AlienBrainHemorrhage + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/alienbrainhemorrhage.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkAlliesCocktail @@ -507,6 +523,22 @@ sprite: Objects/Consumable/Drinks/bravebullglass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkBronxGlass + suffix: bronx + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: Bronx + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/bronx.rsi + state: icon + - type: entity parent: DrinkGlass id: BudgetInsulsDrinkGlass @@ -661,6 +693,22 @@ - ReagentId: Cream Quantity: 30 +- type: entity + parent: DrinkGlass + id: DrinkCrushDepthGlass + suffix: crush depth + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: CrushDepth + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/crushdepth.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkCubaLibreGlass @@ -677,6 +725,22 @@ sprite: Objects/Consumable/Drinks/cubalibreglass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkDarkandStormyGlass + suffix: dark and stormy + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: DarkandStormy + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/dark&stormy.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkDeadRumGlass @@ -773,6 +837,22 @@ sprite: Objects/Consumable/Drinks/dr_gibb_glass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkElectricSharkGlass + suffix: electric shark + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: ElectricShark + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/electricshark.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkErikaSurprise @@ -1177,6 +1257,54 @@ sprite: Objects/Consumable/Drinks/coffeeliqueurglass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkJackRoseGlass + suffix: jack rose + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: JackRose + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/jackrose.rsi + state: icon + +- type: entity + parent: DrinkGlass + id: DrinkJungleBirdGlass + suffix: jungle bird + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: JungleBird + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/junglebird.rsi + state: icon + +- type: entity + parent: DrinkGlass + id: DrinkKalimotxoGlass + suffix: kalimotxo + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: Kalimotxo + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/kalimotxo.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkKiraSpecial @@ -1193,6 +1321,7 @@ sprite: Objects/Consumable/Drinks/kiraspecial.rsi state: icon + - type: entity parent: DrinkGlass id: DrinkLemonadeGlass @@ -1391,6 +1520,22 @@ sprite: Objects/Consumable/Drinks/mojito.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkMonkeyBusinessGlass + suffix: monkey business + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: MonkeyBusiness + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/monkeybusiness.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkNeurotoxinGlass @@ -1567,6 +1712,22 @@ sprite: Objects/Consumable/Drinks/glass_light_yellow.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkRadlerGlass + suffix: radler + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: Radler + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/radler.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkRedMeadGlass @@ -2045,6 +2206,22 @@ - ReagentId: JuiceTomato Quantity: 30 +- type: entity + parent: DrinkGlass + id: DrinkTortugaGlass + suffix: tortuga + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: Tortuga + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/tortuga.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkToxinsSpecialGlass @@ -2061,6 +2238,22 @@ sprite: Objects/Consumable/Drinks/toxinsspecialglass.rsi state: icon +- type: entity + parent: DrinkGlass + id: DrinkVampiroGlass + suffix: vampiro + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: Vampiro + Quantity: 30 + - type: Icon + sprite: Objects/Consumable/Drinks/vampiro.rsi + state: icon + - type: entity parent: DrinkGlass id: DrinkVermouthGlass @@ -2309,7 +2502,7 @@ drink: maxVol: 30 reagents: - - ReagentId: Daiquiri + - ReagentId: Daiquiri Quantity: 30 - type: Icon sprite: Objects/Consumable/Drinks/daiquiri.rsi diff --git a/Resources/Prototypes/Flavors/flavors.yml b/Resources/Prototypes/Flavors/flavors.yml index ffd6fe40b8..0aa9341647 100644 --- a/Resources/Prototypes/Flavors/flavors.yml +++ b/Resources/Prototypes/Flavors/flavors.yml @@ -599,6 +599,11 @@ flavorType: Complex description: flavor-complex-irish-slammer +- type: flavor + id: alienbrainhemorrhage + flavorType: Complex + description: flavor-complex-alien-brain-hemorrhage + - type: flavor id: vodkaredbool flavorType: Complex @@ -624,6 +629,11 @@ flavorType: Complex description: flavor-complex-watermelon-wakeup +- type: flavor + id: electricshark + flavorType: Complex + description: flavor-complex-electric-shark + - type: flavor id: rubberneck flavorType: Complex @@ -749,6 +759,11 @@ flavorType: Complex description: flavor-complex-coconut-rum +- type: flavor + id: darkandstormy + flavorType: Complex + description: flavor-complex-dark-and-stormy + - type: flavor id: coffeeliquor flavorType: Complex @@ -799,6 +814,11 @@ flavorType: Complex description: flavor-complex-arnold-palmer +- type: flavor + id: tortuga + flavorType: Complex + description: flavor-complex-tortuga + - type: flavor id: bluehawaiian flavorType: Complex @@ -859,6 +879,11 @@ flavorType: Complex description: flavor-complex-iced-beer +- type: flavor + id: radler + flavorType: Complex + description: flavor-complex-radler + - type: flavor id: gargleblaster flavorType: Complex @@ -894,6 +919,11 @@ flavorType: Complex description: flavor-complex-atomic-cola +- type: flavor + id: crushdepth + flavorType: Complex + description: flavor-complex-crush-depth + - type: flavor id: cubalibre flavorType: Complex @@ -904,6 +934,31 @@ flavorType: Complex description: flavor-complex-gin-tonic +- type: flavor + id: jackrose + flavorType: Complex + description: flavor-complex-jack-rose + +- type: flavor + id: junglebird + flavorType: Complex + description: flavor-complex-jungle-bird + +- type: flavor + id: kalimotxo + flavorType: Complex + description: flavor-complex-kalimotxo + +- type: flavor + id: vampiro + flavorType: Complex + description: flavor-complex-vampiro + +- type: flavor + id: bronx + flavorType: Complex + description: flavor-complex-bronx + - type: flavor id: screwdriver flavorType: Complex @@ -1049,6 +1104,11 @@ flavorType: Complex description: flavor-complex-mojito +- type: flavor + id: monkeybusiness + flavorType: Complex + description: flavor-complex-monkey-business + - type: flavor id: neurotoxin flavorType: Complex diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml b/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml index e13b34e5fc..e041e23900 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml @@ -473,6 +473,21 @@ metamorphicFillBaseName: fill- metamorphicChangeColor: false +- type: reagent + id: AlienBrainHemorrhage + name: reagent-name-alien-brain-hemorrhage + parent: BaseAlcohol + desc: reagent-desc-alien-brain-hemorrhage + physicalDesc: reagent-physical-desc-creamy + flavor: alienbrainhemorrhage + color: "#FFFDD0" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/alienbrainhemorrhage.rsi + state: icon_empty + metamorphicMaxFillLevels: 5 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + - type: reagent id: AlliesCocktail name: reagent-name-allies-cocktail @@ -753,6 +768,21 @@ reagent: Ethanol amount: 0.2 +- type: reagent + id: Bronx + name: reagent-name-bronx + parent: BaseAlcohol + desc: reagent-desc-bronx + physicalDesc: reagent-physical-desc-strong-smelling + flavor: bronx + color: "#d68829" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/bronx.rsi + state: icon_empty + metamorphicMaxFillLevels: 4 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + - type: reagent id: CoconutRum name: reagent-name-coconut-rum @@ -799,6 +829,21 @@ reagent: Ethanol amount: 0.15 +- type: reagent + id: CrushDepth + name: reagent-name-crush-depth + parent: BaseAlcohol + desc: reagent-desc-crush-depth + physicalDesc: reagent-physical-desc-cloudy + flavor: crushdepth + color: "#0a0a33" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/crushdepth.rsi + state: icon_empty + metamorphicMaxFillLevels: 5 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + - type: reagent id: CubaLibre name: reagent-name-cuba-libre @@ -823,6 +868,21 @@ amount: 0.07 fizziness: 0.2 +- type: reagent + id: DarkandStormy + name: reagent-name-dark-and-stormy + parent: BaseAlcohol + desc: reagent-desc-dark-and-stormy + physicalDesc: reagent-physical-desc-bubbly + flavor: darkandstormy + color: "#cf7f17" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/dark&stormy.rsi + state: icon_empty + metamorphicMaxFillLevels: 5 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + - type: reagent id: DemonsBlood name: reagent-name-demons-blood @@ -909,6 +969,21 @@ reagent: Ethanol amount: 0.15 +- type: reagent + id: ElectricShark + name: reagent-name-electric-shark + parent: BaseAlcohol + desc: reagent-desc-electric-shark + physicalDesc: reagent-physical-desc-tropical + flavor: electricshark + color: "#3097cf" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/electricshark.rsi + state: icon_empty + metamorphicMaxFillLevels: 5 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + - type: reagent id: ErikaSurprise name: reagent-name-erika-surprise @@ -1143,6 +1218,51 @@ reagent: Ethanol amount: 0.15 +- type: reagent + id: JackRose + name: reagent-name-jack-rose + parent: BaseAlcohol + desc: reagent-desc-jack-rose + physicalDesc: reagent-physical-desc-tart + flavor: jackrose + color: "#f53b3b" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/jackrose.rsi + state: icon_empty + metamorphicMaxFillLevels: 4 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + +- type: reagent + id: JungleBird + name: reagent-name-jungle-bird + parent: BaseAlcohol + desc: reagent-desc-jungle-bird + physicalDesc: reagent-physical-desc-tropical + flavor: junglebird + color: "#f27c3d" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/junglebird.rsi + state: icon_empty + metamorphicMaxFillLevels: 4 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + +- type: reagent + id: Kalimotxo + name: reagent-name-kalimotxo + parent: BaseAlcohol + desc: reagent-desc-kalimotxo + physicalDesc: reagent-physical-desc-bubbly + flavor: kalimotxo + color: "#360606" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/kalimotxo.rsi + state: icon_empty + metamorphicMaxFillLevels: 5 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + - type: reagent id: LongIslandIcedTea name: reagent-name-long-island-iced-tea @@ -1282,6 +1402,21 @@ metamorphicChangeColor: false fizziness: 0.3 +- type: reagent + id: MonkeyBusiness + name: reagent-name-monkey-business + parent: BaseAlcohol + desc: reagent-desc-monkey-business + physicalDesc: reagent-physical-desc-tart + flavor: monkeybusiness + color: "#d6b929" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/monkeybusiness.rsi + state: icon_empty + metamorphicMaxFillLevels: 4 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + - type: reagent id: Moonshine name: reagent-name-moonshine @@ -1426,6 +1561,21 @@ reagent: Ethanol amount: 0.2 +- type: reagent + id: Radler + name: reagent-name-radler + parent: BaseAlcohol + desc: reagent-desc-radler + physicalDesc: reagent-physical-desc-citric + flavor: radler + color: "#edff2b" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/radler.rsi + state: icon_empty + metamorphicMaxFillLevels: 5 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + - type: reagent id: Sbiten name: reagent-name-sbiten @@ -1656,6 +1806,21 @@ metamorphicFillBaseName: fill- metamorphicChangeColor: false +- type: reagent + id: Vampiro + name: reagent-name-vampiro + parent: BaseAlcohol + desc: reagent-desc-vampiro + physicalDesc: reagent-physical-desc-spicy + flavor: vampiro + color: "#b51b1b" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/vampiro.rsi + state: icon_empty + metamorphicMaxFillLevels: 4 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false + - type: reagent id: VodkaMartini name: reagent-name-vodka-martini diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml b/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml index c722c42162..4f65cc0f25 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml @@ -590,3 +590,18 @@ effects: - !type:SatiateThirst factor: 0.6 + +- type: reagent + id: Tortuga + name: reagent-name-tortuga + parent: BaseDrink + desc: reagent-desc-tortuga + physicalDesc: reagent-physical-desc-sweet + flavor: tortuga + color: "#1c8c40" + metamorphicSprite: + sprite: Objects/Consumable/Drinks/tortuga.rsi + state: icon_empty + metamorphicMaxFillLevels: 4 + metamorphicFillBaseName: fill- + metamorphicChangeColor: false diff --git a/Resources/Prototypes/Recipes/Reactions/drinks.yml b/Resources/Prototypes/Recipes/Reactions/drinks.yml index b307166aa8..8cc2da8623 100644 --- a/Resources/Prototypes/Recipes/Reactions/drinks.yml +++ b/Resources/Prototypes/Recipes/Reactions/drinks.yml @@ -8,6 +8,18 @@ products: AcidSpit: 3 +- type: reaction + id: AlienBrainHemorrhage + reactants: + IrishCream: + amount: 1 + BlueCuracao: + amount: 1 + Grenadine: + amount: 1 + products: + AlienBrainHemorrhage: 3 + - type: reaction id: AlliesCocktail requiredMixerCategories: @@ -238,6 +250,20 @@ products: BraveBull: 3 +- type: reaction + id: Bronx + requiredMixerCategories: + - Shake + reactants: + Gin: + amount: 1 + Vermouth: + amount: 1 + JuiceOrange: + amount: 1 + products: + Bronx: 3 + - type: reaction id: CafeLatte reactants: @@ -282,6 +308,16 @@ products: CreamOfCoconut: 3 +- type: reaction + id: CrushDepth + reactants: + RootBeerFloat: + amount: 1 + Rum: + amount: 1 + products: + CrushDepth: 2 + - type: reaction id: CubaLibre reactants: @@ -292,6 +328,16 @@ products: CubaLibre: 3 +- type: reaction + id: DarkandStormy + reactants: + SolDry: + amount: 1 + Rum: + amount: 1 + products: + DarkandStormy: 2 + - type: reaction id: DemonsBlood requiredMixerCategories: @@ -352,6 +398,20 @@ products: DriestMartini: 2 +- type: reaction + id: ElectricShark + requiredMixerCategories: + - Shake + reactants: + DarkandStormy: + amount: 4 + BlueCuracao: + amount: 1 + JuicePineapple: + amount: 1 + products: + ElectricShark: 6 + - type: reaction id: ErikaSurprise requiredMixerCategories: @@ -598,6 +658,48 @@ products: IrishCream: 3 +- type: reaction + id: JackRose + requiredMixerCategories: + - Shake + reactants: + JuiceApple: + amount: 1 + Cognac: + amount: 1 + Grenadine: + amount: 1 + JuiceLemon: + amount: 1 + products: + JackRose: 4 + +- type: reaction + id: JungleBird + requiredMixerCategories: + - Shake + reactants: + Rum: + amount: 3 + JuicePineapple: + amount: 1 + Grenadine: + amount: 1 + JuiceLime: + amount: 1 + products: + JungleBird: 6 + +- type: reaction + id: Kalimotxo + reactants: + Cola: + amount: 1 + Wine: + amount: 1 + products: + Kalimotxo: 2 + - type: reaction id: KiraSpecial requiredMixerCategories: @@ -737,6 +839,22 @@ products: Mojito: 4 +- type: reaction + id: MonkeyBusiness + requiredMixerCategories: + - Shake + reactants: + Gin: + amount: 1 + JuiceOrange: + amount: 1 + Grenadine: + amount: 1 + Absinthe: + amount: 1 + products: + MonkeyBusiness: 4 + - type: reaction id: Moonshine reactants: @@ -838,6 +956,16 @@ products: Posca: 3 +- type: reaction + id: Radler + reactants: + Lemonade: + amount: 1 + Beer: + amount: 1 + products: + Radler: 2 + - type: reaction id: RedMead reactants: @@ -1041,6 +1169,18 @@ products: ThreeMileIsland: 10 +- type: reaction + id: Tortuga + requiredMixerCategories: + - Stir + reactants: + IcedTea: + amount: 1 + Sugar: + amount: 1 + products: + Tortuga: 2 + - type: reaction id: ToxinsSpecial requiredMixerCategories: @@ -1055,6 +1195,22 @@ products: ToxinsSpecial: 5 +- type: reaction + id: Vampiro + requiredMixerCategories: + - Shake + reactants: + TequilaSunrise: + amount: 3 + BloodyMary: + amount: 1 + Hotsauce: + amount: 1 + LemonLime: + amount: 1 + products: + Vampiro: 6 + - type: reaction id: VodkaMartini requiredMixerCategories: @@ -1221,7 +1377,7 @@ amount: 1 products: Cola: 4 - + - type: reaction id: Caipirinha requiredMixerCategories: diff --git a/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..735ad356e8f8102be77ce99a3853fd430d866847 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}37#&FArbD$ zDG~_>EDn4#?y*WRVR3UtBO2BSY>3|d%DyOg-P0d#2G`f6Yo2^IaSdaR;3Vd%vU82Q zkLFy{n{-w=l6kB2#>IRRyp0zHfCM8yBf}g|9z~&!_(edw7(8A5T-G@yG%+v$0H&ri A0{{R3 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-2.png b/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..c17e37680e7eb5d0601f5ecc7f81e199331008d9 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}lRaG=Ln7Rh zQzQ}&SRD9f++&qs!s6zPL}aub=szF-FMoge{D(JIr}O+XKX0&S+gE0DZKkiKp5Fvc zN;!xflqq=isgmtHx3%QGV_`gdm|Mil4Q_nZQN8J`lFd+gHSg`J|0jHRPJ1kU@D5|4 z!})WT1>0wwz2&;8-Q$|WTemkWm|<2PaAsim{M%G<(cvrpK<6=dy85}Sb4qApU;qHZ Cx>F(m literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-3.png b/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/fill-3.png new file mode 100644 index 0000000000000000000000000000000000000000..1852a05e35d1c3333d22a6efe6b7e4b2ae4ba41d GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}^E_P~Ln7Rh zQzQ}&SRD9f++&qs!s6x(Bihn9{`~m!^#8Z&?~-43$~S3W?v8(Yuw=nLy_0fEk<2HQ zW-#~dtvdFg{xi?v?CAZ1uNs1;+;AytM38D!WvUDnzs2Vf-QExaz%Q z+C&+q9Wn(USKBx**r#{$*~B%B&l=u-e`oxn*q&uM>x8EY4(04J)n9uK{(mOfc70o& z@)gGAdh^pi?Ab8+VQ$XR11$`jm|Mil4Q_nZQML31YJU>Cd+X|d%F|tRkDE8`WPa2z zZ*K8}Jj2;ryHoflu4%m0dn1s&r|m!n2asrJXJDAepQV_5pYt2g9}J$ZelF{r5}E+e CaA_t0 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..111507b00b91bac5289f90d63b87bef8738a59c6 GIT binary patch literal 306 zcmV-20nPr2P)Px#>`6pHR9Hu2WEfz;IKcKZj5=U61V%%EKTLo%`*(IZT&{515RB&&)|7s zC&Sy=ih&6MWCsW@?qJyDkVT(Fc+}rLn7Rh zQzQ}&SRD9f++&qs!s6!K@I*l4X3-b>eQRz%PWGKNQ$>?4^4;R~P1|4GDEi39tl6h< zlktd1j7h0O_g*_@c71-o_GYKdA65(^e-z{&aj^!~@*DLpF)?`6a8W>lmob9lfmX(g zX}s)r7yip@u2)&rf$ O;OXk;vd$@?i2(qj1xc6y literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/meta.json new file mode 100644 index 0000000000..fb5566d3dc --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/alienbrainhemorrhage.rsi/meta.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + }, + { + "name": "fill-5" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..7c107128804652f21657ebc95b2db816c4a15502 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}o}Mm_ArbD$ zDG~_>EDn4#?y*WRVR3VY5HH#eOyxTHe?yha^2e4pEDn4#?y*WRVR3T?6Ae!Ueti6S`v38{SrOg^SEr?`%y!?zHsfr#mdgLbH>_mO tpEPUF>AkY*h?}f|6w_iw6oOffi6K*#Tk-Qdb6%i544$rjF6*2UngBnQGlKvC literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/fill-3.png b/Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/fill-3.png new file mode 100644 index 0000000000000000000000000000000000000000..6e3e63da2e3227b50149fa9b4cf00ae7e47ffe80 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ii4<#ArbD$ zDG~_>EDn4#?y*WRVR3T?5^Z4|e}4RV`hRs?>7Lf*F^7XE*|<(hns8RZ-R51$;a412 zr=_dRcHhJ{<7~K=%KyVRtYpuhG;7c4y|U_vo2-Ep(_%#wf?1A<;lHntV(q3!DL{J} PJYD@<);T3KF)#oC5}rJ? literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/fill-4.png b/Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/fill-4.png new file mode 100644 index 0000000000000000000000000000000000000000..88d25fbe7b1906c99a36c59a25338a5083b95036 GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}g`O^sArbD$ zDG~_>EDn4#?y*WRVR3T?5*}QQ^7HrqslU3qZ~L1`*%e-sW~yk)6|gt#;VU}w*w1v$ z!UeZJ^{DVS@+d|!pLwP&I_ZC6&fLD|DYKooxn+eWE#{NpZM-OeLNM|(GRT~eR7`7d R5C>Y$;OXk;vd$@?2>=e!JL3QV literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d56aa94afc20de7cdce913ad653e42c3867db4f4 GIT binary patch literal 258 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}dpunnLn7Rh zQzQ}&SRD9f++&qs!s6!Kpr-htXw!%Kk9W4a%=De26cHJ?>FJAlr>AZ&Ywg)4JUwF7 zl)>JhlfUtLdd1b%eb;#>`-B;O`4J(Ut9Ox$y_MM^j=lBJV?Wb13k@zOnMTY}Q*4;W zC)C$+Kz8GSG~Sb9hfRcKSzpAlxpwyN<2pR8yWHcG;V#P*Ylako6M{=v%(qoYmmbLA z=xIA}ft#_$RLL@x$yL!|MV6Xd-4({giUv|l%yLW&cMBsF4J~*&f&O6dboFyt=akR{ E0F&2TFaQ7m literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/icon_empty.png b/Resources/Textures/Objects/Consumable/Drinks/bronx.rsi/icon_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..bfb6c282c0d1b2a230b827e57a79a3bd4a3b26dd GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}+dW+zLn7Rh zQzQ}&SRD9f++&qs!s6!Kpr-htXw!%Kk9W4a%=De26cHJ?>FJAlr>AZ&Ywg)4JUwF7 zl)>JhlfUtL`hy2g53XcS$=v=(txZlod(m0dC`Cg)hE_fg*|RbXn;7CX4lL?W<8$z5 zKQV1Wvf&w6<|B2gqCU#$V sT*bjpOq2U_yc?J}6dDdJIlyZ1nM3g*?{rz9$qb&ZelF{r5}Fto0M^?q@&Et; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/fill-2.png b/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..aef4008182344559454142041482f91fd201b637 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}37#&FArbCx zuPO2YRkbE^iY+()@K60Sn^m`C`t}DmH0)(7YB)L1sh<1I%qZZ%z{mn58uc5eyson~ zDmM9QQ+hb|jhw=B>ArbD$ zDG~_>EDn4#?y*WRVR3Ut5DiZR9*AH0)4!hU|BruGo@tYoNThshux5C|p(iJ~X5ojo zr-dikx@>B5e8>EDn4#?y*WRVR3VY5gvSv^7HrqnI5#|U}XE=>f%NfW6vO-O{E8959qJ>DIYKT z@8jRnNj9#Nk|rEwe#bCDc}GRchPFf7_5D4*37nK-Yh{jztlZdk;Qjsio*)4SMS}-? t3(}mYtr3tYJ&?iC(*`60j9D0VIIAfN{ag7H=p+VDS3j3^P64 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/fill-5.png b/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/fill-5.png new file mode 100644 index 0000000000000000000000000000000000000000..5a83559b9ff4cedc2419367fc30c7fc968ae8713 GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}OFdm2Ln7Rh zQzQ}&SRD9f++&qs!s6x(B-+wA{`~m!bpMt~wr+~QzI~0HWb3ktZNpQoa~9qow(I*( zN>TIda@@wp#qObb>j6(=dA{C+Cz_K)4m%|{$R5yN@l!rt^xwz7rIT!2CnZfd%KVOD zg7S`vlnrf%fSNtN37nK-Yh{jztlZdk;Qjsio*)4SMS}-?3(}mYtr3tYJ&?iC(*`60 dj9D1$u2?II|1b~&I+(%J)z4*}Q$iC10{}|KSN8w_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..64cdd3eb8c6781642bd7d2aff995c4c067e21357 GIT binary patch literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}uRL8GLn7Rh zQzQ}&SRD9f++&qs!qOJb5fL5zpZ)2o#qRw=Zw(iTDy3har0afAOtJa#+fs75ez7`1MEv}V3~80!d+nOHOtN*;P&qfB zMNIjqph_#l3C4uep>qnSJ?Jd{?bg7&L?pMM&EZnh$}RkjjHL(O+z{DgTJh?U}3^BnzQ;|(!YgA5tS8(Y-QHj>Ox3HGu z!RiSfE16HM>sxuZN8M5!>>W4fhFgjZ4Cf!@DIRomtq1y=!PC{xWt~$(69WSPL^pG< literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/icon_empty.png b/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/icon_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..14bad18f6c0909e2680b85c696252411d73542fc GIT binary patch literal 276 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}=R92;Ln7Rh zQzQ}&SRD9f++&qs!qOJb5fL5zpZ)2o#qRw=Zw(iTDy3har0afAOtJa#+fs75ez7`1MEv}V3~80!d+nIn_4&IVJAUNn zViZs~DyY)RaDwrM4tpe55rf9Z#w$PzE7}?K?qAg9{Y!Ez zUNtxhGu_Zqp73~E%pT_;L!U6I1L2AjXEB^CV=$flN#!|HR+OC-(_%#fDW(Mi3=F<{ W6BQ3$Doq6XjKR~@&t;ucLK6UhI$!4i literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/meta.json new file mode 100644 index 0000000000..fb5566d3dc --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/crushdepth.rsi/meta.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + }, + { + "name": "fill-5" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..ca76f8d344683bf5a60d5b335e626b2641dd58eb GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}zMd|QArbCx zuWjUIFyLTz+~dCN#6RPdt~ZXqrie|Ny_|i`>NQ;bhyHgna40k|FoB6_4)-tDPrZEW qnr^nHT*9UkQA{ij42&#lta3YoITX)rT=x}dFoUP7pUXO@geCwEpf2bD literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-2.png b/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..aefd7e17cf61aafb65982a0fb122fd99823b2287 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}>7Fi*ArbD$ zDG~_>EDn4#?y*WRVR3UtAx>~L%Fo~bCw<30#-+N46UCgTMW$pjSvLHAtSiE9a7tog zs#xT#fA*H&<@aiuZ<291+ols4Xz;4xqJRW%<3)i5X&ej;8%;zM?|S7N09wi5>FVdQ J&MBdZ0RWpWHdp`v literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-3.png b/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-3.png new file mode 100644 index 0000000000000000000000000000000000000000..d9008407c6178b6715399295a10ffd708698c577 GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}jh-%!ArbD$ zDG~_>EDn4#?y*WRVR3Ut5DjYteti6Sx_(*cMGaHS=ud8&&M0{c-(alH3Xm1BXw=z! z;u1>{!{M*I8hj57x)o3BY%*z-Iq>hJSQoQIYR?IyZXMtM^XL5Tzc)oXl5atp^RzV@ n5~T++IC|QEM1V01!$wa9#hWW`r~nETY4F@<=X1Y#Oo4_owF5jV$p*@l(GHv2g&L<4t`nLzj9MDL1nHHIn z`Ro6aJM~|d-8oUjU@E;S#r5C{W~hY?vlJN^UM$p8R6b{W80a_#Pgg&ebxsLQ3;@q| BOPl}z literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-5.png b/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/fill-5.png new file mode 100644 index 0000000000000000000000000000000000000000..3f12cd164bcccf0f4e74c56353dd226142327102 GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}6FprVLn7Rh zQzQ}&SRD9f++&qs!s6x(CK}cV{P_6u^#9$ZQ5s8kl>JxUbVkYh@(N})R}O=@AH^ygj0%%nacRV;Fr17E_9eT+vLWzq~&X1Y#Oo4|a-{+?4I!{Q#n z$h3(|U;dw5{Qu(YBIVPJS8`5F5@lV?C&AlzQ9y#1F{_P%!RD5Y;?|4I;XubRc)I$z KtaD0eVgLYSbV}6# literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..90cd2bf5705c6815212a47dd6779d0eaa8bf6006 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}r#)R9Ln7Rh zQzQ}&SRD9f++&qs!s6!Kprsh_>E1hg4eRe5a!NOCCYd(qXx%Il$x)Pu2sfI+wWDd~ za{D)GHw9I!8B%uk@8fd&`86m_?5|A2>Mw6TZdAMJtdh;p>d?K{?(khJU6JxL<#Fc> zUNuY|Sk0*eLDrYhCS9{BfBtc%%8zh?P- z=3TrNhNl)ye9DkhdT?5I`3qrIiLHtzMkh2|9HKneaj&@BBVT$TgQKVIKtnqN!vwuT V#gkJvh5^0B;OXk;vd$@?2>|lbXd?gs literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/icon_empty.png b/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/icon_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..8ad26bfc2e465aeebb681fb514f24aadfd2c217f GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}3q4&NLn7Rh zQzQ}&SRD9f++&qs!s6!Kprsh_>E1hg4eRe5a!NOCCYd(qXx%Il$x)Pu2sfI+wWDd~ za{D)GHw9I!8B%uk@8fd&`86m_?5|A2>Mw6TZdAMJtdh;p>d?K{j+tGbzbkISj}^>r z&LE@xI1)Z<#-46uEN9rZE8+A}Yn~EMW{IteTapxx7flG8^qMKl{IR>NffUnXMS};5 a3=GR(mnhEIC&&(TEQ6=3pUXO@geCxPqEEE| literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/meta.json new file mode 100644 index 0000000000..fb5566d3dc --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/dark&stormy.rsi/meta.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + }, + { + "name": "fill-5" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..7fd25a8b79ae661d61ad2eed6002c5ecc5eda16f GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}fu1goArbD$ zDG~_>EDn4#?y*WRVR3UtBO0Cv82Cp2mX9`m_hn7Oq_a#joi{CGEN9p%y-}t8zzSxl ghK5^;3=ET$ITS13hwcCx&fw|l=d#Wzp^1S301X8!!vFvP literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-2.png b/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..35985ab459435392881fd3126da905fd2eb85cb2 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}S)MMAArbD$ zDG~_>EDn4#?y*WRVR3UtAw2jR<>&AJvwgiq$HhKN%d4|fHaoOUSaZ^*`gMKIT8&+n z2TXEx!`SD{6ZP4YeuAr!v7BM6^u`ocX;WFlR}B{hBzPMy3NSFNStYD!7TX~Tw3Wfr M)z4*}Q$iC10IK{q!2kdN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-3.png b/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-3.png new file mode 100644 index 0000000000000000000000000000000000000000..c2ed2bbdd376dab6b910fadf6da14717101ac3e7 GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Wu7jMArbD$ zDG~_>EDn4#?y*WRVR3Ut5DiZReti6Sx_FVdQ&MBdZ0RR_?LXZFe literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-4.png b/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/fill-4.png new file mode 100644 index 0000000000000000000000000000000000000000..ef1da7eb096058a2733714c9c53daec994082ef4 GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}O`a}}ArbD$ zDG~_>EDn4#?y*WRVR3VY5gvSv^7Hrq`TnZ1C9wT%_7$a@IujICvZX}$3NrUJ2r{`> z<#>GKxv6OIfN#Tkiw;31_2pHU)mhGL-v00SwXQ|8nSCx_ODa9^GVY qQ&^=EDn4#?y*WRVR3T?6Ae!Ueti6Sy8garzJb?=yH~kXX0sZ#r1&-;VvMuxH*iXb zUo3WVo}x7i+Z{%|UU37b2Oj;rf1MHz$R@-W3MjMq`F&mH&oLuC|8M_VQCD;J8Ozol zebsPzbJI&{P6xy-VxlCCf=Ry2@eTCB*x@UqZWG3V2vc|eyjc)I$ztaD0e GVgLXU{Ygdu literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bddb1cd6a941b840b6263ff9c6c910253fd1aecd GIT binary patch literal 348 zcmV-i0i*tjP)Px$7D+@wR9Hu2WEfz;IKcKZj5=U61cp}#2?EeGi#Is`z9OST;SzojIZVaD!*C=Lkhv|&j0 zSisQZV@;BIWI2GCl9LpNkmO=e2(;MiGx$z=gW(Q+{ih5*`YK@YEf?<)9U4@10L)b& zhY;%svK-*m^9UmZj7;CdeSP5Z%m2gM0WOUfFhan>fsuY8FyIcbuRtvaK*3?_DL|hP zIK2G>)}jF90J`LYgPUGpb%4E}1bxZ@d=7xQjHV?ath7T924eF)x;~N$B3OllEe=TW uHL`w^9Y9QFM2ebG2aJZmXb22=2mk=HRiz<{_5Tq70000Px$4M{{nR9Hu2WEfz;IKcKZj5=U61cp}#2?EeGi#Is`z9OST;SzojIZVaD!*C=Lkhv|&j0 zSisQZV@;BIWI2GCl9LpNkmO=e2(;MiGZ>hfFd(M`Q2c{Jz(-#N?7l4*?+_gtRB-?- zdSIagatN`GAj<(9ygZDc#E2XUpwNK@{ej0X{||o$fPx!26zCoTgTevGj-XozpyzQ| zG=LmHmt26YkbxD?bSVe$IRF*{G%X2XzC&`UgI!$|QpvOba>Kul#s likeXejE2By2n=`#005rE$sxZeKfeF~002ovPDHLkV1kC6f!P26 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/meta.json new file mode 100644 index 0000000000..fb5566d3dc --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/electricshark.rsi/meta.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + }, + { + "name": "fill-5" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..e91fcca8dc7535b778b871355d30aee7a65fc47c GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}o}Mm_ArbD$ zDG~_>EDn4#?y*WRVR3VY5E<~aQ&gZmEHqO)3vceJ;^GWbFUKBtf82K3) ZqWM@9C10}30S#sFboFyt=akUI0070bD53xW literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/fill-2.png b/Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..6b07b1de3ac058a1f3a5c95d600d66805d4710cd GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}k)AG&ArbD$ zDG~_>EDn4#?y*WRVR3T?6AflXUy`|PU~zk p3Ea#iU3wscqo)mp;HltXU|3?$p(uUfh6d0Q22WQ%mvv4FO#s(hF~I-; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/fill-3.png b/Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/fill-3.png new file mode 100644 index 0000000000000000000000000000000000000000..58ac6675e5325b7284cadf757cc9218e89ce09aa GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}*`6+rArbD$ zDG~_>EDn4#?y*WRVR3T?5^ZT5e}4RV`v2|{mo=>`zP-AtQrV?)^n}?wey(5r@0bpk zzVEDn4#?y*WRVR3T?5+}GC<>&AJQ$J;1+g6_7=xseJ#-2eun+2aV?Dp)8kX^91 z%G5J$(h`ZA$38nc{=XQTup>|Q@1PLn7Rh zQzQ}&SRD9f++&qs!qOJc@yFBS$N5bOjE@_c+5cHvSO|PkaAaMQ7CeF3p~|68!A(D~ zYXYy<%_5N;M!g4zKhHjxc93a4w3%J}&)cgAam32PPHUCzHgT5#aflB7vG?2-;e9186&H_q_wP!3xfJ|V}_ zm9L?-rGFpS;c4CFCpOJVVm~0`=(WhX-+a?z21_kBSpz91Wn-2NpZZMe?LLw{Rz$) zZv-6pIg>WYn3*o-li+Q9Kz}iKy85}Sb4qApU;qGHVPJ*; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/meta.json new file mode 100644 index 0000000000..52c392be67 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/jackrose.rsi/meta.json @@ -0,0 +1,31 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..85821df5d9a0f0424404ef1963c28c0845790b1c GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}k)AG&ArbD$ zDG~_>EDn4#?y*WRVR3UtAx>~LzCJtc|3=dklkEPEDn4#?y*WRVR3Ut5DjYteti6S`hWW5Si`KMrK=vR%y!?zHesy-yUna8Z4Vi? zzDkCQT%WD`KRNqE#EDn4#?y*WRVR3VY5p8K4e}4RV`hWAubrPa0gRT}%vUSO{!h+6k#X&kCfB*^vyw_15EDn4#?y*WRVR3VY5E*R;`p?Jzv!DH@E3j81R95|_vr0C@O{-T8j)F}6YC98` zFm8R7tm3YDQp#alT>iE{{c9B$M|v-Jsn~Z-WHmF-Z2!q`6epPm?B+Co)o@Wjg0~S& cF!D1p*z#*DYJ3eS2Rebl)78&qol`;+06`}{NdN!< literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b63c467af23000ef8060319e03695c560363edbc GIT binary patch literal 300 zcmV+{0n`48P)Px#)}`iQhZOc175$f{139Y+^mG* z>dy@*4#=Eh$>43fn4!nVnj}w=q!?sl$(E!4@fF{6aX^c`KEt!gt{Co^rq@HC5Eym9 zpa_9SliV=Uz%2c4`lNy6^Ceg_!dyc-CBo>F1y~)h)O-ScLcm8~1*-$LT)abj2jDA= yNG*g&E{bWn4tgw002ovPDHLkV1fX6;C+w) literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/icon_empty.png b/Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/icon_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..c21ad2a11e4cfac6f16ffa476d5dab7dc84f1e57 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}r#xL8Ln7Rh zQzQ}&SRD9f++&qs!s6!Kutwmc9B+d)&9|bkS~^Dz0$)?3oG) zm!o~jFI2AxU6+K{fuHnB(?u`oq6z$DK7T4_}?<)!;iZ zC+Xya+}$%49zQ-y#ed=wiIn7%(-!~#D0;u7(4l*;9W%Q=f7@fnKIzf}85|c>SqdVZ zw!2t7;QMef{9u$w&d!SVZ>)m*4s~4lp}tdNnJe?OCyQp>>XQJe?r8%O3=HjhIf_N@ T7u12iV(@hJb6Mw<(8K@$N62uS literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/meta.json new file mode 100644 index 0000000000..52c392be67 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/junglebird.rsi/meta.json @@ -0,0 +1,31 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..a48cfa933c18b3ed6eff97b6280b254e69bdb26b GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}ah@)YArbCx zuWjT#pupo2Xw7pV;HJ5>fk>%{%teJ)!jopdzxVBB=<{hd^ZwlDXkcLCPyiDOoOj|D z{YtexmQ|u__j1}66XrLuMQxSGwC_%u<|h`(1XA^q$^LKk literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-3.png b/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-3.png new file mode 100644 index 0000000000000000000000000000000000000000..dba0ef177097e331d2f95d6edcd869f070390e7d GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}g`O^sArbD$ zDG~_>EDn4#?y*WRVR3Ut5DjYteti6Sx<22oZpy5h_y6~&%ygZ$%<(Bhots))|NsAn zJ!=FC(o+8Mf40<7ykN@Wz~|uCsBnDE@9(qMd~unkmSvXWE^8pgv{(^HFv~G9ygi_$ UDD(gN0ifj!p00i_>zopr0Bxc_KL7v# literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-4.png b/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-4.png new file mode 100644 index 0000000000000000000000000000000000000000..47ddc76e44f92771e256aca6904fe4f31d538438 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}HJ&bxArbD$ zDG~_>EDn4#?y*WRVR3VY5hu7B<>&AJ^ZnUl_m#)L{rm4dEixt3shq)HS@`g-`u!)3 zx^;Y;^;jGV4HE(xxc>G3|Ia&zIYD#cI>iTPPW+L7_L;Gmp-65?wcdg>=V@zlEM7HS k6p-L;yeP0BjRWYC^A?ItN_rQ7?qKkA^>bP0l+eTg0M8FZZ~y=R literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-5.png b/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/fill-5.png new file mode 100644 index 0000000000000000000000000000000000000000..9ba296aae22579afd8f9dfd8108d989b326ddd13 GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}-JULvArbCx zrx|iJ81T3X-_@OW@}hKwNB)C_F6@pus?Ofe73z-Mw>^7WnKO{lp7psn(**|J3kS@3 zWp2+qrZ+`ZYRg2+dFzs59N7$7}Gu+{|VECx?kKbLh* H2~7+D7SBkh literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..237652ba749f8bfe03d9a428f817a69643f76d44 GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}w>@1PLn7Rh zQzQ}&SRD9f++&qs!s6!Kutq@RX3-b>s&8wHblA8#3+F4G^qC~mtiurbZt?o2r2X?I z*faA7t3Q378B>%j&qhG}eUDqL)?91Svt z34sPi!VfFst=VRrXIJ5EkZIs~F^~I#w(^AJZ83YC=T8y{Q(hGA@nj04g0Y8Q*UGy+ m@}&ndIC|O+G_*4?SbV8c6l6SW4D=?0r>mdKI;Vst1_l7eVP`i0 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/icon_empty.png b/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/icon_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..e2caac82a4273abf72f2612b434fc215e002d273 GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ydu{YLn7Rh zQzQ}&SRD9f++&qs!s6!Kutq@RX3-b>s&8wHblA8#3+F4G^qC~mtiurbZt?o2r2X?I z*faA7t3|4|^ zX$|9(%Yp1YZ3i+qcq%v&PQPF9#&dg7!U5TaILXFWZMx#`4KAp%B$z5ywYu~@RWSC@ m>sooYN4^xO9;m6Ioq@qstWq&JK-L84a0X9TKbLh*2~7YO%UH7j literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/meta.json new file mode 100644 index 0000000000..fb5566d3dc --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/kalimotxo.rsi/meta.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + }, + { + "name": "fill-5" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..9fdd9db4c40b2e933c12bee4103ee96fd860fa70 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}9-c0aArbD$ zDG~_>EDn4#?y*WRVR3VY5gJ^L{{PnfPn;@w|JdD>nXc2+vaT&y%qPLycu@eEU}T8s YU{U<>FLfr+Oa@O^KbLh*2~7+P0K)4klK=n! literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/fill-2.png b/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..e1fd43182e9305daa0bcc205676dd62fb1a75f6c GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}QJyZ2ArbD$ zDG~_>EDn4#?y*WRVR3VY5HH#e^q-Ia_rIUpZKapR_4g__omH|Owtbwu|8>2@{QSpx to6abC3!7$N2xRYRJCMPFOfWDkUC*sJai@1b&=v+yS3j3^P6EDn4#?y*WRVR3T?6Afzweti6Sy8c^Bz?!8Nk8izIndvzxDMi|`?}xm2&m)d= zU#&gO`crsMnALqM|I7dQbM77AX^|gTe~DWQph0RTm(J|_SG literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/fill-4.png b/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/fill-4.png new file mode 100644 index 0000000000000000000000000000000000000000..2ea71b97025703ca6bd8f38deb8a8a7952a6f18e GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}`JOJ0ArbD$ zDG~_>EDn4#?y*WRVR3T?5^ZT5e}4RVYG3W7am{H;;oGuFHm;MBX7n<@W0>IV(Dy@L zyhn&@&g=3?Zxkn)D%}2Y^8Q!*p7Yxa%_Gw$F72FU`eFq$*c@bnf#JY3amB^HQzL;^ QGkCiCxvX)7_z6RV$B!4Q z4RUr?wCkMKEMqt$xJ1S3%M|Z25e4_7E0}>6Iyf;z*E;k$Yn)@9vTc>Stbr8MVnraq Y;5x-cQSW{+2hgz$p00i_>zopr0L!XRH~;_u literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/icon_empty.png b/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/icon_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..ea020e72a8f8e495bc29126bfca946ea17ea72ac GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}y`CEDn4#?y*WRVR3T?5+}GCC+WKXf2?(&s%DL+(PWjQ3f_*>w%>SdT=3yj!d31` z>PH2g&olmTY7DvGd`8KVC84lCPttN)@Dqjzj~_2q8|3V)Xm6ZS;qG@JrJZ5j1cj|t z@}&ndIC$;~90-%2Fj-WSk1=So?5^*Hj@t!sYhVwS=iXWLoUIQJ*;OXk;vd$@? HiGcwCF#1cv literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/meta.json new file mode 100644 index 0000000000..52c392be67 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/monkeybusiness.rsi/meta.json @@ -0,0 +1,31 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/radler.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/radler.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..030a2ea9267f6c18861a995d413716510a62f592 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}9-c0aArbD$ zDG~_>EDn4#?y*WRVR3UtAvCxeqxtXrZEDn4#?y*WRVR3Ut5DjYtDn6WfZoe%n#xaZgEDn4#?y*WRVR3VY5hu7B<>&AJQ~zIEX0hC4k$Ya7Dpj%>ZgLeJ=sqtf!51*$ zmJ>we*Mpho>vOJJ+?8S2^yeCLxrqA;%{MBijZ-q4e#$-LDAHx0={%8n#l=}tr3W%N gdfH$F1B32jDMizp{&RtjVeoYIb6Mw<(8Rz10L%kLivR!s literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/radler.rsi/fill-4.png b/Resources/Textures/Objects/Consumable/Drinks/radler.rsi/fill-4.png new file mode 100644 index 0000000000000000000000000000000000000000..9e44a208229f4813566b9ef876cf818b654ed261 GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}jh-%!ArbD$ zDG~_>EDn4#?y*WRVR3T?6Afzweti6S`v3R+hYY<6Js+-BneD!bZNl2cdEDn4#?y*WRVR3T?5+}GC<>&AJ^Z#4EvYVvZ5qaNDl`7c`H?1^eKKu)}aO3dc z%2&PVtdiZ(dSC@}TutAB4kp%>x+?7&C#4*=*;PFIEFW#^XU^WBv;XS>*`tD4T-GN) z@kGu_*w=2^6m#0~S-=y9tS!sjWDTU47AwLC24x2`#gyNEmw`@W@O1TaS?83{1OSe~ BM@s+z literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/radler.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/radler.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..dead0ec31d1161317682f58cc8b398b2eb6c9722 GIT binary patch literal 267 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}M?GB}Ln7Rh zQzQ}&SRD9f++&qs!qS$;F(t?HmwfcQmmfE(-E>yTW{7;Zczu)U_q?4I?TsPs6P_~E zq_W4#Tu54f{=?h*Zan2%WWyHtbMSDn@A)s*dWpZt z6N#lbTJ5a*Eb0 z34x4@Jay|PKF+(sxLDDlh~q&P??EB4m7J4O*aLPe8^3C}C?N4cfPq1EW2K^=M7yTW{7;Zczu)U_q?4I?TsPs6P_~E zq_W4#Tu54f{=mW0%?~RAUVwGFxp6%Bq#9eqxPs?EnxN&h<}*q=6`DkyPKZs)wmiz$ zAbRqN$U%et39?KZRxn&#vaIIGqM9p=ixnM;I38s29uyK=$vG*7Jz%%8@vDZ50umnt a7#LPdl_?(MTeK4BSO!m5KbLh*2~7YY+Ew=e literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/radler.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/radler.rsi/meta.json new file mode 100644 index 0000000000..fb5566d3dc --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/radler.rsi/meta.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + }, + { + "name": "fill-5" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..2eb13c4fcbe24a7776060e562901b17328f30293 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}VV*9IArbD$ zDG~_>EDn4#?y*WRVR3UtA==_NJi?;?9KYUGb+4*P#ed=wiL{LsjfP?Fd*&2OOZn_^ i&Ec)tlNHPeZ4B|pITRnBX6yvoz~JfX=d#Wzp$PyFqAEDn4#?y*WRVR3UtB3`r}=szF-Py73BV}qK1hYuV$&6MeP;Kuy_1~zZ)JSQ#p z2;$kq{iZ=Jea>5nS=J7lZauQ!7k1_IHDQ&?E|sGO-AB25Hd;M;+OkEDn4#?y*WRVR3VY5p7`{e}4RVy1!_$@jW*(^QqC2Hi)wdp24< xdfKvCWw!e!wx-Ai7uLmm61CC zR8BFL@mS4X#ut!Rq!!V5=?vqBfPi=9$KQ3FulL}po1m!T%Gkwh_dhY=L29D~%dus% znLoHNYaa9u^UH@S~&#Q7CKS2K9J`njxgN@xNAxBOOk literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0902145f2bc58ba017e40209d100e69a44139b5e GIT binary patch literal 363 zcmV-x0hIoUP)Px$BS}O-R9Hu2WEfz;IKcKZj5=U61V%%E=n$~8xBq|W;6a?~^&0g5gR=CchKod- z2~tnAVnPl88AgsHh;{(T0+1ujO51RjUAxX?F}RsbV*t^_h6K?LDA{uK|EcaOhNdN} za5^Awt{cOo%glwzAE4rsC0XHcE)jn^IjUjJZ-wC|%$ z2p~HEWD%1n2iP&_Y??YixyhMeOwhyuuygShos;qsT{y?z~?^<47@B5 zhrrl$a{!)bASL+83W24^i}9v`XvfJMXNJ$YCi5$iIEB+JQmfRDZkRtJz$ z0>TU+x+Fxl5m%{5Z0Uz=7FiA;SP+n)VAKJlAut*OqagqQZN-8ityJjV00000NkvXX Ju0mjf006EXmCyhH literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/icon_empty.png b/Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/icon_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..433a627e84f4bd0024ed77939c157b73ee73e63f GIT binary patch literal 315 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}pFLe1Ln7Rh zQzQ}&SRD9f++&qs!s6!K@Iqk2=I#IMUp#P}^XjYAm;cS1zASC)cA3o?g=R@*Yw)df}x`Rk+) zFL@Hn6RmeI@x{lDYNv7*i8SW0ST;;anyto{vGIC(!RL=@2c}CT_Q~$!d+^|CqRJFy zhl{RX1sYg&7iK6NEt+(SF@^mBck9FV3{Q3(ohFtc#q=VQ=Pu)tD8C}92FnBP@vPZ1 zUA#Csj2m7|61eFWvs7tgB>zpFO)?H%6Ma11u3&a^278Tx;mU)_iefioW&*v>;OXk; Lvd$@?iGcwCMHGF@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/meta.json new file mode 100644 index 0000000000..52c392be67 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/tortuga.rsi/meta.json @@ -0,0 +1,31 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/fill-1.png b/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/fill-1.png new file mode 100644 index 0000000000000000000000000000000000000000..596d98eef14f83c04bdc345afb9e79db9dedad5e GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}S)MMAArbD$ zDG~_>EDn4#?y*WRVR3UtA==V7-gI93r+@D3sSlrC=NB|{9V?5hnwr8taSdb2!>1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/fill-2.png b/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/fill-2.png new file mode 100644 index 0000000000000000000000000000000000000000..9b3f32db5e19d9c288bd8d62e68c41b974917726 GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}eV#6kArbD$ zDG~_>EDn4#?y*WRVR3UtB3`r}=szF-FaE^lqz7rKNfkf8nm1dRYahOT|Nn!U=}W^s zKZ$sDCH79Yy?EcpEPY0Et9VCI;66Lq#pio=3G|%=^ WTzHbb2Iyu6Pgg&ebxsLQ3=9A=nNu+U literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/fill-4.png b/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/fill-4.png new file mode 100644 index 0000000000000000000000000000000000000000..a47a7fd0a99dff9d9f4e5c6a3e966afa76c6a799 GIT binary patch literal 285 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}S3O-ELn7Rh zQzQ}&SRD9f++&qs!s6x(Au?JI^q-IaCx6l)UEq0I>W^Q)Px$97#k$R9Hu2WEfz;IKcKZj5=U61V%$(XoP^HqvQYm`}fntu|yjRa)fbl3sL1( zkB>Fc<`S*2WXsY2XM1ZH>KCmb=!h*B?=TSS2$CE?&}JkBG;u(Sy*|T{408r?ZdQh` zzyC3O`Slmfe);t`oaSL;W>^zEoi-r=a=`6k8?c2Sfb0;E#V`jg^qWe32Y@UDr2~*7 zF63D;aI-R@I0P07q*x9zljt;n?f{T)LB0k#1UWkB<^Wg-z@h=h-w`>B=-?+g1Yn`y zu{;eW{$c5XRNqtV0OZ67Vw387vKkK=Ds*@RRKTAAJ?9WfCbRAj|-gasf;e zC_7?v0kNeYvR<+rKuVEJNbRTtMnhmU1V%#u03KVTA)ul?FaQ7m07*qoM6N<$f*Q4n Ah5!Hn literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/icon_empty.png b/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/icon_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..5c537f1f7de10f2b0d715f507812f5981eb69912 GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}cRgJkLn7Rh zQzQ}&SRD9f++&qs!s6zfkkQhxc=6-^e}8|!d}O1GG*?keVIgHpie3Xf%HZkh=d#Wzp$Pz-Gj0d~ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/meta.json new file mode 100644 index 0000000000..52c392be67 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/vampiro.rsi/meta.json @@ -0,0 +1,31 @@ +{ + "version": 1, + "size": + { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by SharkSnake98 on Github", + "states": + [ + { + "name": "icon" + }, + { + "name": "icon_empty" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + } + ] +} From 5fad0e074577aeeb517d8ed1e4507c898dabba5d Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 19 Apr 2025 05:13:17 +0000 Subject: [PATCH 04/16] 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 dff89adf6a..3b4056937c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: lzk228 - changes: - - message: Now you are allowed to paint multiple airlocks with the spray painter. - Along with that you can cancel the doafter by clicking on the door you are painting. - type: Tweak - id: 7752 - time: '2024-12-24T02:25:04.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/34001 - author: ArtisticRoomba changes: - message: Reinforced tables now require welding to construct and deconstruct. @@ -3916,3 +3908,10 @@ id: 8252 time: '2025-04-19T01:48:41.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/33835 +- author: SharkSnake98 + changes: + - message: Added 12 new, varied drinks for the bartender to mix during shifts. + type: Add + id: 8253 + time: '2025-04-19T05:12:11.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36287 From 34cc49c175457710c5b025ba71582a7c8cbdc1fc Mon Sep 17 00:00:00 2001 From: Ko4ergaPunk <62609550+Ko4ergaPunk@users.noreply.github.com> Date: Sat, 19 Apr 2025 08:14:50 +0300 Subject: [PATCH 05/16] Add new color turtlenecks in WinterDrobe (#32920) --- .../Inventories/winterdrobe.yml | 32 ++ .../Clothing/Uniforms/color_turtlenecks.yml | 491 ++++++++++++++++++ .../Uniforms/color_turtlenecks_skirt.yml | 491 ++++++++++++++++++ .../equipped-INNERCLOTHING.png | Bin 0 -> 5396 bytes .../Jumpskirt/color_turtle.rsi/icon.png | Bin 0 -> 4869 bytes .../color_turtle.rsi/inhand-left.png | Bin 0 -> 5079 bytes .../color_turtle.rsi/inhand-right.png | Bin 0 -> 5142 bytes .../Jumpskirt/color_turtle.rsi/meta.json | 41 ++ .../skirt-equipped-INNERCLOTHING.png | Bin 0 -> 3034 bytes .../Jumpskirt/color_turtle.rsi/skirt-icon.png | Bin 0 -> 2835 bytes .../color_turtle.rsi/skirt-inhand-left.png | Bin 0 -> 5269 bytes .../color_turtle.rsi/skirt-inhand-right.png | Bin 0 -> 5401 bytes .../equipped-INNERCLOTHING.png | Bin 0 -> 5396 bytes .../Jumpsuit/color_turtle.rsi/icon.png | Bin 0 -> 4869 bytes .../Jumpsuit/color_turtle.rsi/inhand-left.png | Bin 0 -> 5079 bytes .../color_turtle.rsi/inhand-right.png | Bin 0 -> 5142 bytes .../Jumpsuit/color_turtle.rsi/meta.json | 41 ++ .../trousers-equipped-INNERCLOTHING.png | Bin 0 -> 5305 bytes .../color_turtle.rsi/trousers-icon.png | Bin 0 -> 5127 bytes .../color_turtle.rsi/trousers-inhand-left.png | Bin 0 -> 5243 bytes .../trousers-inhand-right.png | Bin 0 -> 5375 bytes 21 files changed, 1096 insertions(+) create mode 100644 Resources/Prototypes/Entities/Clothing/Uniforms/color_turtlenecks.yml create mode 100644 Resources/Prototypes/Entities/Clothing/Uniforms/color_turtlenecks_skirt.yml create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/skirt-equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/skirt-icon.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/skirt-inhand-left.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/skirt-inhand-right.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/trousers-equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/trousers-icon.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/trousers-inhand-left.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/trousers-inhand-right.png diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/winterdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/winterdrobe.yml index 0f8c73dac3..8909fb163f 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/winterdrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/winterdrobe.yml @@ -18,6 +18,38 @@ ClothingOuterCoatBomber: 3 ClothingHeadHatSantahat: 2 ClothingHeadHatXmasCrown: 2 + ClothingUniformTurtleneckColorWhite: 2 + ClothingUniformTurtleneckColorGrey: 2 + ClothingUniformTurtleneckColorBlack: 2 + ClothingUniformTurtleneckColorBlue: 2 + ClothingUniformTurtleneckColorDarkBlue: 2 + ClothingUniformTurtleneckColorTeal: 2 + ClothingUniformTurtleneckColorGreen: 2 + ClothingUniformTurtleneckColorDarkGreen: 2 + ClothingUniformTurtleneckColorOrange: 2 + ClothingUniformTurtleneckColorPink: 2 + ClothingUniformTurtleneckColorRed: 2 + ClothingUniformTurtleneckColorYellow: 2 + ClothingUniformTurtleneckColorPurple: 2 + ClothingUniformTurtleneckColorLightBrown: 2 + ClothingUniformTurtleneckColorBrown: 2 + ClothingUniformTurtleneckColorMaroon: 2 + ClothingUniformTurtleneckSkirtColorWhite: 2 + ClothingUniformTurtleneckSkirtColorGrey: 2 + ClothingUniformTurtleneckSkirtColorBlack: 2 + ClothingUniformTurtleneckSkirtColorBlue: 2 + ClothingUniformTurtleneckSkirtColorDarkBlue: 2 + ClothingUniformTurtleneckSkirtColorTeal: 2 + ClothingUniformTurtleneckSkirtColorGreen: 2 + ClothingUniformTurtleneckSkirtColorDarkGreen: 2 + ClothingUniformTurtleneckSkirtColorOrange: 2 + ClothingUniformTurtleneckSkirtColorPink: 2 + ClothingUniformTurtleneckSkirtColorRed: 2 + ClothingUniformTurtleneckSkirtColorYellow: 2 + ClothingUniformTurtleneckSkirtColorPurple: 2 + ClothingUniformTurtleneckSkirtColorLightBrown: 2 + ClothingUniformTurtleneckSkirtColorBrown: 2 + ClothingUniformTurtleneckSkirtColorMaroon: 2 emaggedInventory: ClothingNeckScarfStripedSyndieGreen: 3 diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/color_turtlenecks.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/color_turtlenecks.yml new file mode 100644 index 0000000000..81d9801482 --- /dev/null +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/color_turtlenecks.yml @@ -0,0 +1,491 @@ +# White Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorWhite + name: white turtleneck + description: A generic white turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + - state: trousers-inhand-left + right: + - state: inhand-right + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + - state: trousers-equipped-INNERCLOTHING + +# Grey Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorGrey + name: grey turtleneck + description: A tasteful grey turtleneck that reminds you of the good old days. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#b3b3b3" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#b3b3b3" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#b3b3b3" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#b3b3b3" + - state: trousers-equipped-INNERCLOTHING + +# Black Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorBlack + name: black turtleneck + description: A generic black turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#3f3f3f" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#3f3f3f" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#3f3f3f" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#3f3f3f" + - state: trousers-equipped-INNERCLOTHING + +# Blue Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorBlue + name: blue turtleneck + description: A generic blue turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#52aecc" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#52aecc" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#52aecc" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#52aecc" + - state: trousers-equipped-INNERCLOTHING + +# Dark Blue Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorDarkBlue + name: dark blue turtleneck + description: A generic dark blue turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#3285ba" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#3285ba" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#3285ba" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#3285ba" + - state: trousers-equipped-INNERCLOTHING + +# Teal Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorTeal + name: teal turtleneck + description: A generic teal turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#77f3b7" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#77f3b7" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#77f3b7" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#77f3b7" + - state: trousers-equipped-INNERCLOTHING + +# Green Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorGreen + name: green turtleneck + description: A generic green turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#9ed63a" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#9ed63a" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#9ed63a" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#9ed63a" + - state: trousers-equipped-INNERCLOTHING + + # Dark Green Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorDarkGreen + name: dark green turtleneck + description: A generic dark green turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#79CC26" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#79CC26" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#79CC26" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#79CC26" + - state: trousers-equipped-INNERCLOTHING + +# Orange Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorOrange + name: orange turtleneck + description: A generic orange turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#ff8c19" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#ff8c19" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#ff8c19" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#ff8c19" + - state: trousers-equipped-INNERCLOTHING + +# Pink Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorPink + name: pink turtleneck + description: A generic pink turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#ffa69b" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#ffa69b" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#ffa69b" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#ffa69b" + - state: trousers-equipped-INNERCLOTHING + +# Red Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorRed + name: red turtleneck + description: A generic red turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#eb0c07" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#eb0c07" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#eb0c07" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#eb0c07" + - state: trousers-equipped-INNERCLOTHING + +# Yellow Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorYellow + name: yellow turtleneck + description: A generic yellow turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#ffe14d" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#ffe14d" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#ffe14d" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#ffe14d" + - state: trousers-equipped-INNERCLOTHING + +# Purple Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorPurple + name: purple turtleneck + description: A generic light purple turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#9f70cc" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#9f70cc" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#9f70cc" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#9f70cc" + - state: trousers-equipped-INNERCLOTHING + +# Light Brown Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorLightBrown + name: light brown turtleneck + description: A generic light brown turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#c59431" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#c59431" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#c59431" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#c59431" + - state: trousers-equipped-INNERCLOTHING + +# Brown Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorBrown + name: brown turtleneck + description: A generic brown turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#a17229" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#a17229" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#a17229" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#a17229" + - state: trousers-equipped-INNERCLOTHING + +# Maroon Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckColorMaroon + name: maroon turtleneck + description: A generic maroon turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + layers: + - state: icon + color: "#cc295f" + - state: trousers-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#cc295f" + - state: trousers-inhand-left + right: + - state: inhand-right + color: "#cc295f" + - state: trousers-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#cc295f" + - state: trousers-equipped-INNERCLOTHING diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/color_turtlenecks_skirt.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/color_turtlenecks_skirt.yml new file mode 100644 index 0000000000..1a61e0803f --- /dev/null +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/color_turtlenecks_skirt.yml @@ -0,0 +1,491 @@ +# White Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorWhite + name: white turtleneck with skirt + description: A generic white turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + - state: skirt-inhand-left + right: + - state: inhand-right + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + - state: skirt-equipped-INNERCLOTHING + +# Grey Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorGrey + name: grey turtleneck with skirt + description: A tasteful grey turtleneck that reminds you of the good old days. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#b3b3b3" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#b3b3b3" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#b3b3b3" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#b3b3b3" + - state: skirt-equipped-INNERCLOTHING + +# Black Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorBlack + name: black turtleneck with skirt + description: A generic black turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#3f3f3f" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#3f3f3f" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#3f3f3f" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#3f3f3f" + - state: skirt-equipped-INNERCLOTHING + +# Blue Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorBlue + name: blue turtleneck with skirt + description: A generic blue turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#52aecc" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#52aecc" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#52aecc" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#52aecc" + - state: skirt-equipped-INNERCLOTHING + +# Dark Blue Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorDarkBlue + name: dark blue turtleneck with skirt + description: A generic dark blue turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#3285ba" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#3285ba" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#3285ba" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#3285ba" + - state: skirt-equipped-INNERCLOTHING + +# Teal Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorTeal + name: teal turtleneck with skirt + description: A generic teal turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#77f3b7" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#77f3b7" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#77f3b7" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#77f3b7" + - state: skirt-equipped-INNERCLOTHING + +# Green Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorGreen + name: green turtleneck with skirt + description: A generic green turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#9ed63a" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#9ed63a" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#9ed63a" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#9ed63a" + - state: skirt-equipped-INNERCLOTHING + + # Dark Green Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorDarkGreen + name: dark green turtleneck with skirt + description: A generic dark green turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#79CC26" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#79CC26" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#79CC26" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#79CC26" + - state: skirt-equipped-INNERCLOTHING + +# Orange Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorOrange + name: orange turtleneck with skirt + description: A generic orange turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#ff8c19" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#ff8c19" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#ff8c19" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#ff8c19" + - state: skirt-equipped-INNERCLOTHING + +# Pink Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorPink + name: pink turtleneck with skirt + description: A generic pink turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#ffa69b" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#ffa69b" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#ffa69b" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#ffa69b" + - state: skirt-equipped-INNERCLOTHING + +# Red Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorRed + name: red turtleneck with skirt + description: A generic red turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#eb0c07" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#eb0c07" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#eb0c07" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#eb0c07" + - state: skirt-equipped-INNERCLOTHING + +# Yellow Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorYellow + name: yellow turtleneck with skirt + description: A generic yellow turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#ffe14d" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#ffe14d" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#ffe14d" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#ffe14d" + - state: skirt-equipped-INNERCLOTHING + +# Purple Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorPurple + name: purple turtleneck with skirt + description: A generic light purple turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#9f70cc" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#9f70cc" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#9f70cc" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#9f70cc" + - state: skirt-equipped-INNERCLOTHING + +# Light Brown Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorLightBrown + name: light brown turtleneck with skirt + description: A generic light brown turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#c59431" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#c59431" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#c59431" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#c59431" + - state: skirt-equipped-INNERCLOTHING + +# Brown Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorBrown + name: brown turtleneck with skirt + description: A generic brown turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#a17229" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#a17229" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#a17229" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#a17229" + - state: skirt-equipped-INNERCLOTHING + +# Maroon Turtleneck +- type: entity + parent: ClothingUniformBase + id: ClothingUniformTurtleneckSkirtColorMaroon + name: maroon turtleneck with skirt + description: A generic maroon turtleneck with no rank markings. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + layers: + - state: icon + color: "#cc295f" + - state: skirt-icon + - type: Item + inhandVisuals: + left: + - state: inhand-left + color: "#cc295f" + - state: skirt-inhand-left + right: + - state: inhand-right + color: "#cc295f" + - state: skirt-inhand-right + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/color_turtle.rsi + clothingVisuals: + jumpsuit: + - state: equipped-INNERCLOTHING + color: "#cc295f" + - state: skirt-equipped-INNERCLOTHING diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/equipped-INNERCLOTHING.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/equipped-INNERCLOTHING.png new file mode 100644 index 0000000000000000000000000000000000000000..30ec8b9d591fb5e2e97e6a093b02cfd5dd5eb67b GIT binary patch literal 5396 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/icon.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1194286cc560199b7bfaae37c3d2cd51386089ed GIT binary patch literal 4869 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 V9eMt#Wd5zd-g=AGH4Co;{{SVO`t1M! literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/inhand-left.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..7d8c952aa084fcd29954838505c2f31ce38a5a58 GIT binary patch literal 5079 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/inhand-right.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c0da296e649577328b872b98099119933996c98b GIT binary patch literal 5142 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/skirt-icon.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/skirt-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9516069cb363781cabaa8e3a6a70bdf258c02a0c GIT binary patch literal 2835 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/skirt-inhand-left.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/skirt-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..74b28bdc7b2b3fd72565072488ae6fa982c1d196 GIT binary patch literal 5269 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/skirt-inhand-right.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/color_turtle.rsi/skirt-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..f3554454405d8b168b2e44e3d86e9671c1abde1c GIT binary patch literal 5401 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/equipped-INNERCLOTHING.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/equipped-INNERCLOTHING.png new file mode 100644 index 0000000000000000000000000000000000000000..30ec8b9d591fb5e2e97e6a093b02cfd5dd5eb67b GIT binary patch literal 5396 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/icon.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1194286cc560199b7bfaae37c3d2cd51386089ed GIT binary patch literal 4869 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 V9eMt#Wd5zd-g=AGH4Co;{{SVO`t1M! literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/inhand-left.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..7d8c952aa084fcd29954838505c2f31ce38a5a58 GIT binary patch literal 5079 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/inhand-right.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c0da296e649577328b872b98099119933996c98b GIT binary patch literal 5142 zcmb_f2|QHm-#??2xMis%O4HybnwfLP?Dm~3x#&_UX2W0>GsB>0v|8G3mWWCsq-%{9 zbXB+|bPJKPRzycyVt9i8qm7F+uS;^ENfy2(a=husD72 z5`wvTxztAATdco%SDeJmjb$`hu;mtv+t!AcGYuWBHY%6hq}o# zd!1I*zK{hpe*iAVRy{ETG86!uU?ol(z{vuG8KPt3xME9 znv+sMskV!bl*t9aT4YUVwJ(FHu57K?>k?@_b!9;Pn=eoY|<% zT%e!=c%>`IxgcaFfXWlrmhS)=J%sj=`23JZZXW9!Tmax*5i|DX$#_?3?)ocLYdv;e z4@o1F%r85&9$0dqU+TI{AB$DH@KH4or?&u*SOr^1w2b#?!y}6$Cce-J1 zTo0P{di$J8N%7ZJl;a%C@f@Ezw`4EEbG-N^Jlol_e>x^ky7mWYb;Yjza>cl!c!9x= z9~EK(Q;gMzJ25k(V*6Ny4Y4ojIZj8c?W(RTv1J?XpN!FzwM|@c%I0HS^xon@`J{4x zz^gf8(S+F!r?T2bgQqaaf>#o)K`G#cxN~35Lxn{Ev}k{vSRMeUQa-NrT@9o?Ik{pD zAX%k&P%dF4^_~g>kjUVjq=~-!ja2qaw@NgwN%TV^*VP?zirPnZ2h3~^+Mh7TY&y8r zX`xnuVyW56!9&}#4_lF?(0r+e+tSyhTDp`CB)e23eV{DQYmKP_?zV+{6p=1TLOqVs zxNYgZKEei6imX~6Z6k}ajKAy5BslMj*VuY@SBCnOI^|fZC1$x1MDTOKg6g5;bbIdGD z#c9pPx%iNncxRKGv^5#2=^j}>IM?L(xw>3ZBgE^ZV=gBCzi=Jsx7q5yiJ#ShHD|tJlv=?mN2&Y1MP!l*t%Im^Zm5yE=WmbbIqcfh?>z3Aq^-_8nz4|K?V}6zOw9F_n%iNy1?d6SHxviHjR-&tUzN6@ z@oApek!Nx4!rRPq7k{(-7(Z8lxbKx1S>+Xbi@o3H+K)p=eYz4t7906x`>Xh7!gJ7vGXynSJloFHn7we`-K^^|khprm9NKDrS!D{(Iprmo~QGn?)(?to|K(!eW); z5^uD(dGGe#J)?U@7mXfPwwK!yYpG07CZTN;J?@L{pGj=F?4-LP*EQEKH}`RNNpVS4 zSXR&jd=I{b{fJFy9BKsv$~l4n)qoEHl+s(J(xvE9?4bQ%&l}>CptkT*anXZ<{F1$0 zY2Dz{^Ofi8yGho%*5?@|&+Gf^`sW(m?0IMA1c%0d7(TL7Z-`o$YL@z#TB08=2o6lt zCsF0glCEjq8025S!xG-Pmia6;&%$k8%gVeB9-U>BvW?GipIVQ1 zXS^$TH}Eb?ahal>232F*FLE04RyU(ZqIF`!z!rL*fV=yOpU6xUKK4|lGifBL!@aDP zRp{0u>d_4Rb@;{mSMP7UFAR+sEp1S<;cwR7e9f?PPejLzot#LSxruz;Dd@ac_d^Rh z-M;5m)$x5!rboRtMW4H(!FIj7oV(m;RcLf+)S+ti9>K6zk+mRn#3{zQwl}`p^+8ii zlj4#s&RU_#%1NdAZ#ZGTALhoQk0c$rXusK;-W!;62*0!Km&|hsrD%!)-0^{q9Wbf5}^O zGiSz|Za#8o2-jD%Q_WT_Q;o+Pc_vEps4)1ge6R3sZ!h|NVh=k1D&|>O4&ADHh;xNg z%c)4XmGJ9Rhf?0Nxk8h8iRK=Qlox@7Y zc1|};mOZvgsi|;Cy?F8t-#zsGlga4LP9r5z#;o?F?{oQ!#)IS<>d()P;IUkFjxDF3 zbJYJ0#b`QYcgpUkh3hzbpY6T<&SKoQK8}^;nC`$a(Qlevk5Z{!=Q z&z3z=?06V%^lGMBZf;dWP?-4a%~yfLRa#f=e#Z*2S3Lr6K6>`DDQIv**12Or`eKr2 z(Dk;k`tj{M9@L$6X)(+{SJA!u`E7@}dfltN&jzj=PENGzFA;~mjTu_je*fC!NJPWz z@M7I{O6!!3op-wqyH5tzKS(qtjZ-Jj4vxNg9i}w(!t}R-k5k4)ABO|e-fbLZSEt?b znmzISk7(M*ZvO|phH4?0-9|>vW3eBw`n%lA-*>y^dzM)-tQYU9^>m#1`NQVv=D8NV zU007EJZcybH|0IU8PrHerEZCq|uop z)j^r?YbKIm6I!9o-YW!?u4hlUYo zVJKHb3qf;*8ectFu!IZ&n=fMXxX5{rG&(OxWQc-O{mg;G|K^q}{Gt=A7&e5)$3hrz zp3^5FlkpA54-y1?3eIF;Sph5#iz^bsSm+y;@5>YMgucA*xc)Z%ZvkL!9UQ*J_{&^4 zoNpn7BFkWy#uq{U5-oHM<+HHPEFmvQz+hPh!)a>Fv*A1)bJ z93DeNYR}gOB*0@11_zM|NRUN>^eD`YNTjwKo6F>hg&Y6mi)BF*u?$hL5grYJXdLJY zLR12o3PHM{9u)*XLmhZbws+`%hvKMuBr5ptPy)pqv=FF2E?LMNGu%jtGxw}5j0=)ul7vXo(vJlAc=-UlOTwMCR6aRDGpCY zlk|w*APEn$m?YX)`z-=C+;y~o|Fs{uIsS{`S1+b8BOzQ>;g{AiqxpOp1+b0gY0jq! z(g+k1L?ojjJ)GV=O_(r5p`#(PHyy;`yeV`V$pAawC$Q1y7W|Vn4unV`olHaH^(Z7X z1Aw?Nn|EO#xa;^hz1eRBoK~G!4V-e$b>+mHwj0e5pdtP zrZe#rvNs8jCPI)NToybNO=eP9XabW0L3A8Q#zWxut(hb^G!92VGa-0=5LY}usOcSt7ec)C5f9ZJa_jn0u!GF){pY_-G zSpCNn;{Tb|mtrt{XwwsQb0|vQrU}vXq5`s1niI;mG+V=C} zfoqH>3P}ptvh+T*eCK`NbIyCN_xrAIuIriSp8Nj&m*0PR{xcJ4ztvJsMpXs? z06A+bGY9CtdG3;41ifdm{Mw=0LQfMr69C9NAv;Z#0st8smZ_<|z`>FNvo<%?M-%n+ z5f~&I0IVXG-WF9;!+$3*YB1l^2Ri}==Q@cXDYG^@EsR4H^r*w?ydHzFEs4-ySo@;hC zJWGPHWyNp=u+~T)slHycK*kBSe}Q;&0zx+O(+NqSb86`00CYMbxv;aA4H4lqFFf2u znBHf`;!*rl$vQbFu30T?n*Z(9Ha zJkuJN1d4PVH%e~43~XLkwLB7dA`dv9xx73Y$aM#Fs!l}+By!II?x!!-!~*Ug0P*<^ z%8Ug|RRH%(OJyA;q{o2rla}Tm0cos+&e7PMz-K#LHR>Dz;A1```pxNBCn=7`^@=sF zZZ`u{2nMEC?VArRIrLWYrgSfpS+(%l10V*w01#UQn28nY#$OQ)i%EuUgjUkt!i+Df z)6o&fqQ9Kklygr)0yy-1)O@Po_ElA2Pz5L&ijKd&CWR>{Zk$1#eWVUFP{cjidus1@ zL9uZPr}TJuo;i2UM^{mn9iF*obn?QIeG+>{3*JDd9Zd(PqGF_~ev(pG>^vx2fXR>L z8|_-RG|KPXCiS5XI1sD3{o(FZ^+|Q&@g#Gk2Kga5RbiDyn5l7! zO4a6;40Z3rtruPE)#NCbpG)-flzl4arR{Q1opSDJLEnl`%B|7HmcN1JeJYEw z6}}*{@;)UmP;XTXy*iI}RWkdoYM%bpWhMJZX`0O;l9A=`&V_0R3^h)IXJA@-aJ}`h zN?Fp2rE3q#-;<-PrN(|te5lp0^lK!?%orV?K&jDIR@9B~ub@|qRP3sd=vGsTtk6h| zakbi?N^nqREy;|%9#d~dGVRwGjj4Rd$k%T##X9Wp!%OCD-(y94a%togA~&@Ua^&A+N;$?32l^&#sr~C)l0$*gBG8 z{`8qwwpZMBi`Jl4(^g&5d5)PV?Z|WOPjR2Ne5QUj4_-S%Ha$pv(1})3*;2WvGOTh{ zCAm^}T@0)86Ri@vqDHPJxQEz7?=f5vAN8xu9`n?EN}5aB&NsKJWVdH!WoF&zmu%SE zAk%O;YsYrW`V4OmZzKHvR(_#(;VW-ZeP#X5e);~3{fYf){fiq=nF_3v_!`_a&P+92 zbqZI<`cweMqoaRQ%KxRnduQ*?1-?pcNwuEoc{a@*c_l5E&#Ioq;x6MyaM}26-eY|5 z_^Kn1A9cq19p`Zb#TbtTb zUtXHAI+8+9TXrobyTGRRcHKj#F{km&dzlX$PuP~`MEASAwdg}WeVN{tW_Tq%XJyLz z=WW+STe)TzE`Lb7aOIBqm)O}{iE8)w{T1%fcUcELi`NYv^X!ZZTx{%};iKY}fqUW8 z%qDOc99&aIdB&r@Q|Q_RpCsSg4~ky~)>o8kRWLHG4?YTY%-Ya|YY?7erN7;U6%?ox z6nY>$OnY2<_KxfwSu}D)*;aO2w7D`~*#Kc3?^-RaJ|Ew7)n0dfwo|ruc6MV%VL@R< zaJv6vTsN+X^^ApoKG+QU-e&WCReeAE5{vE@NfjZAPy@CD-S72V{98kdMEQ?%a|-u$ zrgVXA7t1f!b{SaeT3)0VzN&p&^LF<64qNW|S^nYCpN5XQVFyWhNtC2UQlVieKfo{E z(14^+l2EL5Yk+t2K2vbNIPFFBHM1RSn^s&~@7hs9EZOklc=1d>=T-7x*ppDNE0eZE zHLZy=>K5vfCrZ^z3qJ0QEEuclAH{uI9@rRYJcJoj3TgS z65m_7tJ3gQ^yH!m<*9%v)1K(xu#5g&%>PF z6LkTJrM>}{OEq;c85{eobGPRj6(bhPC9cUQzV2H6ocN50xVW@R;pOVjIyFYVKR2J~ zO8uDovHxSbqP(JwCP{PWDOpVgi?Yb!$c@p#U=!^cpA&N3TSyUxeri+cNEl9NcP?pW z=I!Vfc5C_lI`n#?Z{pTOUQpObQJtCQ!^UP zw)<|yi39c~$K3TIFI?ATIXzs)S!TR4D6%Nx@B{U3{*ZgVB|m7`KFYGHC$`J!aeY+1 z;*xC++ChoR2}OqQ*}-0)XFo+AO*neRc8dqC$1n3R&aL%S+J(3?4VnejQVl~Zkz-|Kl}W3x=+B^%a7*UJOdiE)nSN00`jaj)dyWL%CZf1HG_!qrQ^ZrL;HxIfe+=#!dQL~#tROFAf*%=bZT{#t2kP55+|^~7 z(;hU_;lqQN-h4MTYqc~rE_e8aFvYdh=+5nZf`>glh>7@aM9vN5i{MO}#e+fib#@iI zH12NPuWfck+!wQX8w>k?Dct7&T>j;nccR-bH?#(Yo@u&P`7*e~CSYH)W{W_?EpP8p zEjSw$ni@)+N^Z1h3J9y7DJt8Vel2zIdDz?8_63TIN&Rm9?-csg^?Wv~#me13q9pG& zRX1MJXmPHx)Gq1D>H9qApoy08$c_$UC1L7}&bZerg)5r|2$iH?UL3`tIO=R`_FMKb zpZ7%LslbqPA#HhU+52AXyZ6y-)Vel?neLS?v&}B4ZWB4cBEaLdZ&l0eSonK|x(CT6 zEsE_=Lyh~UAIQ$Gtn&{RC71R24OM90u=x!oK;3ZlD|`0hO}+oX`t%FO{S8G1d;D*< z2G@?d?0Q_2?AS!kxlq~_^6H-5Y_0B%o)`T$$>U=$4VQ?5KST}6zpO4E9}cUV8CtBn zR%xyBCWjEGA?IeO`JaLk+ZqQ@o|)^25NwVhtJAYo+s=mY;W z=xZj7ZslMH0Kw}3Kzs-QK0{mLJ^=6s0ifR<07wY{u#%T_>t`eAUGSJ6jT+%SKWeTpAM@$mT(405IAV$fMGIm_ith>B-`d;U7xwz+o%~ z8NLH=2ioyWnO-cbU_R3^c&igV*oRJJz&9Dgi~>oJ0ya}fg$1&GIRa8389uL<1g+<` zQE=EiMCe0?o6H%6IosL8Ou2j}jEDpgbPzg93>NP@ja+ zC!uk$Z!b7xj?eHQIha{|GX{Mk!@Yz;9tnjKi9|>d7Rlv%qR>PlaZU#YgMbhSK@dks z4McDRn%^~;F$Hu!izj4pIj}j6R2tV`NQOhMe&xXC{ZPvhe2WtlF;pOxhe9L4IZpFH z2K@()=g;?@H_o7=n7&LllOq&BSo9Ap&x~qWZ`2KN)~xYiIYv#vl5^X8$lD z5Sj--G`er;BMu6*9?iNC<~Og9r@h1fofJ z0tpT3f>;s=eudg`87z;We}`g7SON)0_$Me-P7JD$`X9jzI?02}XH%hAu-H^jCW^=L zgu{MBm}JWJFC8PCz2a}%WE zi3VtW0s@W2U=e5o92P-96KM!E!Gi{3Fdjr2)xZcf*C&wB*B1PpGzLT)fHVRXfx{9F z5Og$-hM*GZXoLX{$Iz!iMD@|sKbQVHA^M&4ycb_dp#GZeeCLp?Saa-yzIpA)^!wpI zY`$4YRQlXQfDEV4RX>vf|F+HggKGFYu6~ync`+fSe^L5htqZswLJ^hEH1UKg=YP|6 zs6XQ+pa%RkD>?&*!P7Bl1QSEVAm~gS8bQDkFbEn{0W>_GfX1QGf6fX6bu$She(%!% zV^-giLHD9^JeklSLc#xgo`0{GzY_TO?ETBr=>LW1e64>iS=7H)^ZeQGrWlm|`7LNP z&MkioW$43?vCiZ`A>>0td&?WtEC5K)z`k% z#;N?~7c!7^x$MJ@-b$6hxA_tHW9V}y1ju);1Hdy{W2-@?DxtTiwNmBt`>bMk$g-nn zcBxY2o~1mmCXdUWECE~qIYV(l?K;siN|Pu1Kzqn=`<}92qLfB=-dQlKnZGzi>WLNL zC4p0dkjEB^l{afj{D&>OvAyN95hIdmW}24RB_fZ7&Y}BRXDMw(+?UPQ-Q4)b;y%14 zuTkkny7&Y+}$Kx-c^R|YgW$53%s<$l2E6Sx~41X!7}E61p1IuN@KUCrN(Q$ zWF5gW-?o4d*X#|!W-f;Q-Mn{5vFo&>{0;8G!u4j!*Jd7m5WBn|Qo$$FgXE)0rw1~oPEW;(0`BsU~RtD>>9=Wz&`*M7nBwN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/trousers-icon.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/trousers-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c3d1c9d61185b309b1e08246d396a6a5e8725634 GIT binary patch literal 5127 zcmcgv30zET|376*p#`mEOm#(O=A7AQYD#;l=vJYA9Kv#l=-pB%!pS zO^I+rx@mK1m9!yB(w?+n+w+uB$-!FR;;B{v`b7fGc<@NKTAse>s1`%q~1M#*Xi_Tn^!678UBkxu9|aIDb%GWkXMRT5Az^xC%UvKwrB?Db>1fNN3w2@P(07RPY5;!tlYCL zPKRa4ur@9q2nSXf5z*RfBy;3l5c}qYZiquEL<}911=_~@`+DHj53+OHs<;~ffY&(p zP&;{IucKP|OL;(d5pXH0@$G-iSv~)fctwO z^z2$y)*K~u!2N=fg0qa=2vB_7+OiLj!^!9$iOC9lvej*MwKD+p<+7q)orrOn$6I}^ zWTo5A8-Yn=lKExF#)AtFcFEq5d(CE-&wcU$h{nwULahQULKUk=Ur-DSsfN$UP1HU4 zX`eJFqQZZP`gF=9>z<4ZaIj|Bay;+WWlf-09VqCJioLcniM=?qeiC!~p*B!w7TTG) ztLlC`%#BN!S*M5RsWWGM43rhPVd=Yu$IdO>E3<1j?-jh-+HhbzGJ0P5qIufNZ3h(c zu(>fpqa8meMf#mF(e7_WPYg=zqmKJ_bgn) ziHy)VWPP~KJxh<(AvU1OzLM1uHu6Zzm@+38!D)Yt&X%&DleNdRDMu45(W_}?v}8pM zs}OVJB=zzQPt&x$4>g^4tJPX;cJYrfFZr8tRn`iR7klY#KcH=P=5gMe2|AJiQrinL$PsYm3pem2I2lC%#y*99VIdy zS}GAGtK*~HZ1yFSoisTM(_^kh*IH1`yH^iKmzJ?|iBC`Wm@d&%D3cc|2N{kjjp4_r zL7@{z%~I7JSFD|i35<+!+K`^KB01rLTk0aG(sXYZ=L{`E%(@HcOR+!tq;qRCYh}90 z4re_!4JKJWe&Utsb@G~3Q&5w6lL7TC&q9)NxJGa21J+f%l^x>>HUB_ks} z<9fGj-JUx6x{DcGw^-Mvd3$&p5%x6+^S$$5cuQ(aYqxbT=|0~b-<{IEpbnF+$T?1^ z#82QYG{ZE<@ztE6JTMlD`duaWr#$a%ueZ(dRe6?B<(Zme*Vvj<^z`Cs&C@viMZzFH zld#!)L>5Nw?diCq-y`S7n88+2w z7^&IR(31T8!lcdNBxcIetI?TxcCT+$m${6%jHcg9f8cz~{&rSW_x3KUH|WRDQ=g?6 zUP{eUOIlO&?5boF-{RcG_bKNt{buNDA9{cBY&#jaz}P#@ zN8Kw8-|W-KCG%K3d_!7s+QT=$LRE1-3BI=;6h05EEh*M5VWrz1co^)Qv9(U*ihA*PV=6_Hf4;bfss2a{3*xkc_d9I_i%|x?pS+;JQJpuyt1 zn0A*(wUM>T3pYFI1;wkz6&SwbO1(Z#4MiM@J95c>qX(nYFZ~dHXVb4K=T4rg)5(in z{!prR1J!iX%SBr^tHLVxwdeJ9jq>pq>^SG$F7{N!pL9Muh$c}-y%ME~_$;5Cx{l$f z1%s1=zwI=0MY=t?Q!D7oNC~dEb?V*Dqw68u>4llk*tNTHIB(nx|o}J^}+9Y{SW$|@PGa`HLLZ`lqe_dgD@i4*7D)+ zPV}x<{1tc7Cp;MD1BZICuXA^5*=nU|@%aPI;v~1*M!((KD=O>kM16?uKxJJ=H%rqQ zRu6i)*SO`}+b8dyyzX=e`-F0 z$MCedw%ji6FFx-m#^Zsz&+LAdvx>X7dGEbGi(%WUXm+Yss{Ce$glZF7fK`CUOW*RR zQzK#T6ssR37ClvNc^qu~X5xXulv=gFRFZh-jbDF>-gUd*F(S-$H@`binqSrW_pC`h zchuidLfYkjqe)sdynV-`%0%Y|TGqMS?Ym#xbC{|!xZc^^eSdF2E2CGz7shT+LcIkH=^{aXmYeE{PjwbdDzI!WG8GC8^TlS|hliW}Jeo1|6 z2RRRt?z&GNfAL2I{ZqToqfVMuAbPj4vC~l0M~vamuD3q4Z_V0OWW}^z@N@aDO%p$T z+&Er0)qwl?`mwNIXd%&K9uwSt!*`MeEuvOQNcb2?P8|PrB4;4_RcgT_({rmfu{POH zES(cKIC12Gf6DZklWF7R007b-03h@r0DOd}Lf-&@KL`Nb?f^iI0{}Ha!p$Fz;Dv<( zn{6ThSW+^5$(%G;q6GkQdpItxVpj)yDwEGe(^-558y(0Mz|sIx6Qdme6V}wy46*j{8+J^fU&Em=O{C(2%Cn4iT;njtFzUkd2_AK@<}NAq0tnW`bA{&%mxnU_mSn z17a~C1qBkRSRxg|Bfk8Qusb2kgX&~q^~D+dL_>Os#R4h@BaukZ5*(T@^u$0E3T4^` z7K?%!7YiFCeNuwaXrLXJSp;qwsF7U>MWznF%EL;Z|_E0{IQ6Mf+cW(*TZ z7hoVXIE`sWkj0#p6Zi{#XPmQ`7`88)%jStiuq-qyEAZlr`64g=Uzna<{$CDYZXFzE zef()GT<)w3k=QZ-hVg}vKb01_1PRy}C$@<1FJ!VU1K==qrqKwf=0Y}I%on=w`MzIw z%JFN+2rM2=MCecN4M>2;Tr?IU6A&Pq1mP&GwFrd%Rt}HFmx$JWV~cG;7qe+dSP74U zKok~q0U;`ZOobo=5Jv^U&!P@|7RMv#Uq!K09Oa)y;dElr#q|GEn8l=e@P%AD%ms%_ z_he%PJWnKImSL(n-wzZ%kVLH$p4vPxm2zVla$V3rI z1PTg|!xK>q21G$Y6f($SGMEgA{uu?VZ^38!PjC8HeHN_GVBslb4-y_lgdiM>LBz9A zWEO>uBCseB#K3}NJT$AnS;&E_j_&(!+kuPYe-M7PVhSS?!MiH@QaWaI&o3iij`1|i z=`cY$fkJ|aWE6zM;!qF?k3*3m3Ihd^Js2Ps>p@}ANk*9II)Rlwm*6+rSP&wC3^E<& zheARzAv^;`r!XND36EzH859CUgy`SVCW34Tq7zYc0)q(ijAx?gL@Wd4L8lXNBmxs; z627DTcL@G#-z*YD#$vGq6bpj$lK|q`C_0%9f2o6T)NnUq((#0Ev}YLjOd#g_OwUv; z)rK?8e9#xhoY{V}+#zNPl}cw$Hy|34IbC^d7V^t9=T9>6?*RR(FY#i-M!!>bpS_Ft z9%2by$Tszav-bZ}@tD8DC87s>9~Fy;V=|d62!(^-s-i%6EDA0u7K%wHk(qb~1xxUN z>+^HC|B4Fk5Lhan^tGA%hf#fT%k-l2JlXI|5QF^pasItN@*SMtcEbM(&Y4{QoU)jI z&gPl5Z{DEr_RmbgujT3C?12D&m_1Ukc`$`S_`vX3_9spFu#sqMVd~-<_O6~LKX>(_ zd2O$AkKbE5_gIIX$fyF}q2a_fP)n_0Xn5EbcdmXpsA%7N)bOpp&nPe#0m#U00A&00 zH(c?5`#RU~y?Rnc8D)G0vf~OtO?j7S3$5$ewQ)Uz6teP8tVgN(ixcr0()S!S!JY+i z8kLKH)=r;*?e7oVcv!h-XMgj)_;`(QuieANcaAzjfB;D#301);7K$CD2r4Q_ zQ)z-IMN~kfDM}NhD=H#N7b(60%RBep<9+A5?|m;{CbMVn)&762wP!M+HrA$#rIe)r z09b5pW@L+;H_slD3y|M5wpTlHn(v}-r4ImEC#9!o5&$4&!8SCs5!jjR3Av0u zxNL=jS4o}RsVsEZVg0OeF@H&oo=y#-eY;G%a$rtx5s5dJQ8Y$zIsYL(P!BM1$}~E6 zI8BVXW!X>&uv&+xrMk{{o|HZ6z&z3B7*INNTA6fk*KEJbug8<<+&p*;d zp4xA-yz-?Kp#BSRHN5n>0gxyQVEN0i5&%{T=uZ^JiUVr_V3(@Oav71E7@*BMvvnTe z^HhCY9LUqK+aSLA3b6UtN`+A1$r8XJ`ieq0km&?yRGtnIh-JnBPG>GvM*>bC0MYsN zip+VkN`TX4S!p{l$uXehl&SGYKoTdWaV#>!@9B2OHMMpC@G+Yi{_0Gmy##m7&C*ql zyKnm?kV%HuY?==(I`mfjw&ZITt8)I+G9Uss4-lF77>Q(R$9pK+`Bd#zatn1|Zps(s zsqm2F;a{RRW!x7N0}eeKHJ;2Vyrv8cC;@qc;ZZkNC9oEY8mBSm9;pHi2BO~dJv9%y z5N_-b8}vH6M90OsYsyJ;4yW!Jow&GYzu2D9oL9(dM^o@*ScF97FA}P9ox##M*z8EY z&aPi&!@S})sSb8%O%3~6hRd!Ce??EVIc926dRv|?Ra@ zLU+KaA!xyv!7iJE$_4$=TByu7qP6|vz)RnbgQ<103jlaQaD=Z608PYwS>?I{NO*om zVI?41E_Yb^>`?q8B{4uGiMNp;x(4eh1xqxG)bd5zeg{@pAF&BJNOk}W%nn}qwV85x2Fv=7>`XaoLKX)-#cS_N6%b8DYS89g)Ng{doWUMTg4xVsuQY|6Y<7cYji7g6J=JI1R3fj zC{=ELo}%h@q~(%hy~<*PD^Dg|rS3@9m`c|zc3rzOSk)k|F6Yg%5yh5pJ=2F!$s47G zxKa;DtNejMmfyw|5sa!V`Ze+Nd&*hFYfB3bjMCMb_lk#>pgZTQ9MoQO3W`RlZ$xig z7b%}6xlpp^(31O$4OY`4KgL$5_sL%mo> z#6~!p9Y`eGDzg`*M&695H=-K$tr?9duV7{qpP%d3U$R!ZLW(cvuRS36K( zBrDmhTt5@(7Zz!|IW=KrV*F*t#dQ{C+C(o_hM8!l^Ijrz?!l~bQyFV;u4I`3>f zoM2q{)HU7p>`jvv{}#g*P3n2Bk#Ew`XKP2!j%@i%`)nMrdRo^om{!&a%PVauUr-)Y zzM@>WT=UlmcKHas9Jj1`adkj1rI*pGy(}v1y3`)y#B774ok=@h-Kmt`mX?;9cB@ak zVPAt(!LF@wM!c94HD7e<+{*dyd^g)odyCv0^a z<3lG_9DV$#Gt%pXCs&|nSr^x>YplbXjx1l_lw8 z!n$XzH+-#mMi;MqNV<6SuJM=1nM|=Nr>Fy^PT}|1!7ll~4jgysJnOem&n?AW$u$N4 z!o8V8<}$hXrj(MDM{iEUH8Jk-9))H3ZGQEoCF-TjRP*3Rfp%%@oA3?7ICk>eT{uCG zQckWj=xo@#vv=R{zTpMKM-?rlw}u-l5)?_Gd6Z+7uFAmrhknF#DD5#6^L2aDU zI~?`8d{?=4Px!=wPl}U1lk875qv=C?`*|-fc6W4cQtnj9RPVGz!H|ZhUFWJ|)jbs% zuTR$c#1?t@n98bYU{f}{G0)tVsgn=RUmUwCoAR=2OWn0ao|*pjc?u`j57%0gPG$lOT2ZR35(sjJS2 zPPgp7S9*tw~&|@*ju3Bz!ruTZK9>MQ!Ih}OzY;=QKPSmnT0n2ZL zEq7e)Rn;@9OtN3Q+}hAA6?@r&eaZ1ke^u;RyYs_ZB*)>^et5tmMCS4yBALo}ImCt9!4!@VF zExSIul0%}Hg?s{B(!LCU+<6=?Q-k$qbGc}sGdSCS2)*T;f(_Z8o@F8qqNn2I^_)t*o z^x#6x)$*$qH`(sBA9NV^s(Bo>i8M+bztBJY{#}6l#7q6VnO`P0Wq%p;O8B^bm|d1| z&uRKp&y!HvmoE3my}BxXT6^{MY)8UBW3>NpDE!p5J!4OS3Bz>ZAC-Hor+)vuWwK$W z3HQgXlZTJ%21QIbPjLpd-}^3X7j*arg-k%?3zMg(vW6mFCFecXzqs0(X>B>RbY9Hx z)Uh(}B;+v@#W1tA0)T*D0YG#J06rsAqBj8G4FN!(697`)CN|;bIxR7SRO16iz^f$vhWXCPgkChCvfHc#PpBle{g_sYi0Gr z$6v<6;rwtR5E}a+FuoD;m(l`ze@_<1mL=eM^BF8-A0$k*Su~zhLq3ZpWN5hXkb zLm(EihhQp!Ood@h2uFpWucB5wCfnKnUq!K09EFO7{y`KeCnilu`yYjw45~Aa&!Hh) zusJjr7RHn7f=2ydm}R;XRx5@A^zcpG0QDWGsUT!Zerwk{~<| zq+p3K2r*%Z=uE;AXawwc{jGd9(seYC|JV-F9RH2*yA^!|kpRi6;9Kh$&|JQaJlJ}( zFlWPrh!72Ckr^NfOT>Y62#*J8L^=ax;qXj|h$YeqOuPf{A1h z#$j2kiewCFdYk#@i6prZ6*oH4i-xQnJ`kH1PIRp zX=E1iPaQ)1BD)cTh9~?amrW_?{*h|z!3c3;H=JZGUVjnC3|L8|tD)A5*};S$h%{yi!R zg+wAT$SjbKXOTe$akjtkkn`+_0V5+88O!?4%D>%u~A zf*ACFj`Q#BBR|XDKkbD7FF5CF{cFi${<)gx*8X${MbbYvh1`~Bhd&+&kc%IW6f7=6 zAs=~QC~}q{0f3mixskrTU;O)6cu6oMJO3nJAxP}<@cya&dLq9E+oGi|D|o33>nbX( zkDk6(r(n~sAUF0{$5l*OB0<7cL8O@Q8?aMkBKCJC&P4*bjRS=v@24|9ygMyl(sn&k zJ}J`mdRZQ)ZfE##bz9wJfaQwT&O0~Y$2njX{h^oR>(@C2t;HXfdhacexV=m7 zYQ_b2ta~bcENn*nCU9K*i8x+rv-A@U07PDV{f&u|RoJGUHS?G^9vX6kW8V#)6006+ l?4iwL!Y@gvrL(9m$nJ_RuFD)nUJ?R;xv{m;4Fjix{{>6gTZaGu literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/trousers-inhand-right.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/color_turtle.rsi/trousers-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..2b5979d70d25d79bb858ddbd702c788e0af5e6ad GIT binary patch literal 5375 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 A2><{9 literal 0 HcmV?d00001 From f1f431e7200eddbf4c28ac273937af92989fd204 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 19 Apr 2025 05:15:57 +0000 Subject: [PATCH 06/16] 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 3b4056937c..8222a7943f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: ArtisticRoomba - changes: - - message: Reinforced tables now require welding to construct and deconstruct. - type: Tweak - id: 7753 - time: '2024-12-26T22:47:23.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33992 - author: crazybrain changes: - message: Bluespace lockers and quantum spin inverters can no longer go on the @@ -3915,3 +3908,10 @@ id: 8253 time: '2025-04-19T05:12:11.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36287 +- author: Ko4erga + changes: + - message: Added new color turtlenecks in WinterDrobe. + type: Add + id: 8254 + time: '2025-04-19T05:14:50.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32920 From 63dfd21b146eae00c696ae561da6f40a223af0ad Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 19 Apr 2025 16:20:40 +1000 Subject: [PATCH 07/16] Predict dumping (#32394) * Predict dumping - This got soaped really fucking hard. - Dumping is predicted, this required disposals to be predicte.d - Disposals required mailing (because it's tightly coupled), and a smidge of other content systems. - I also had to fix a compnetworkgenerator issue at the same time so it wouldn't mispredict. * Fix a bunch of stuff * nasty merge * Some reviews * Some more reviews while I stash * Fix merge * Fix merge * Half of review * Review * re(h)f * lizards * feexes * feex --- .../Configurable/ConfigurationSystem.cs | 25 + .../UI/ConfigurationBoundUserInterface.cs | 53 +- .../Configurable/UI/ConfigurationMenu.cs | 67 +- Content.Client/Decals/DecalSystem.cs | 9 +- .../Systems/DeviceNetworkSystem.cs | 8 + .../Disposal/DisposalUnitComponent.cs | 9 - .../Mailing/MailingUnitBoundUserInterface.cs | 79 ++ .../Disposal/Mailing/MailingUnitSystem.cs | 22 + .../{UI => Mailing}/MailingUnitWindow.xaml | 15 +- .../Mailing/MailingUnitWindow.xaml.cs | 27 + .../Disposal/{UI => }/PressureBar.cs | 3 +- .../Disposal/Systems/DisposalUnitSystem.cs | 187 ---- .../DisposalRouterBoundUserInterface.cs | 3 +- .../{UI => Tube}/DisposalRouterWindow.xaml | 0 .../{UI => Tube}/DisposalRouterWindow.xaml.cs | 3 +- .../DisposalTaggerBoundUserInterface.cs | 3 +- .../{UI => Tube}/DisposalTaggerWindow.xaml | 0 .../{UI => Tube}/DisposalTaggerWindow.xaml.cs | 3 +- .../Disposal/Tube/DisposalTubeSystem.cs | 8 + .../UI/DisposalUnitBoundUserInterface.cs | 103 -- .../Disposal/UI/DisposalUnitWindow.xaml.cs | 43 - .../Disposal/UI/MailingUnitWindow.xaml.cs | 55 -- .../Unit/DisposalUnitBoundUserInterface.cs | 63 ++ .../Disposal/Unit/DisposalUnitSystem.cs | 156 ++++ .../{UI => Unit}/DisposalUnitWindow.xaml | 19 +- .../Disposal/Unit/DisposalUnitWindow.xaml.cs | 28 + .../Systems/DeviceListSystem.cs | 1 - .../EntitySystems/PowerReceiverSystem.cs | 12 + .../StorageFillVisualizerSystem.cs | 3 - .../Tests/DeviceNetwork/DeviceNetworkTest.cs | 4 +- .../DeviceNetwork/DeviceNetworkTestSystem.cs | 39 +- .../Tests/Disposal/DisposalUnitTest.cs | 20 +- .../Administration/Systems/AdminVerbSystem.cs | 2 - .../Consoles/AtmosAlertsComputerSystem.cs | 1 - .../Consoles/AtmosMonitoringConsoleSystem.cs | 3 +- .../Atmos/Monitor/Systems/AirAlarmSystem.cs | 6 +- .../Monitor/Systems/AtmosAlarmableSystem.cs | 6 +- .../Monitor/Systems/AtmosMonitoringSystem.cs | 1 + .../Atmos/Monitor/Systems/FireAlarmSystem.cs | 8 +- .../Monitor/WireActions/AirAlarmPanicWire.cs | 2 +- .../EntitySystems/GasVolumePumpSystem.cs | 4 +- .../Components/GasOutletInjectorComponent.cs | 1 - .../EntitySystems/GasThermoMachineSystem.cs | 5 +- .../Unary/EntitySystems/GasVentPumpSystem.cs | 4 +- .../EntitySystems/GasVentScrubberSystem.cs | 9 +- .../CartridgeLoader/CartridgeLoaderSystem.cs | 1 + .../Cartridges/NetProbeCartridgeSystem.cs | 5 +- .../CommunicationsConsoleSystem.cs | 3 +- .../Configurable/ConfigurationSystem.cs | 85 +- .../Containers/ThrowInsertContainerSystem.cs | 8 +- .../DeviceLinking/Systems/DeviceLinkSystem.cs | 4 +- .../Systems/DoorSignalControlSystem.cs | 1 + .../Systems/EdgeDetectorSystem.cs | 2 +- .../DeviceLinking/Systems/LogicGateSystem.cs | 1 + .../DeviceLinking/Systems/MemoryCellSystem.cs | 1 + .../DeviceNetwork/Systems/ApcNetworkSystem.cs | 1 + .../DeviceNetwork/Systems/DeviceListSystem.cs | 4 +- .../Systems/DeviceNetworkJammerSystem.cs | 1 + .../DeviceNetworkRequiresPowerSystem.cs | 1 + .../Systems/DeviceNetworkSystem.cs | 109 +-- .../Systems/Devices/ApcNetSwitchSystem.cs | 3 +- .../Systems/NetworkConfiguratorSystem.cs | 1 - .../Systems/SingletonDeviceNetServerSystem.cs | 2 +- .../Systems/StationLimitedNetworkSystem.cs | 1 + .../Systems/WiredNetworkSystem.cs | 1 + .../Systems/WirelessNetworkSystem.cs | 1 + .../Disposal/Mailing/MailingUnitSystem.cs | 200 +--- .../Tube/Components/DisposalEntryComponent.cs | 11 - .../{Components => }/DisposalBendComponent.cs | 2 +- .../DisposalJunctionComponent.cs | 2 +- .../DisposalRouterComponent.cs | 8 +- .../DisposalSignalRouterComponent.cs | 3 +- .../DisposalSignalRouterSystem.cs | 3 +- .../DisposalTaggerComponent.cs | 9 +- .../DisposalTransitComponent.cs | 2 +- .../{Components => }/DisposalTubeComponent.cs | 4 +- .../Disposal/Tube/DisposalTubeSystem.cs | 38 +- .../Tube/GetDisposalsNextDirectionEvent.cs | 2 +- .../Disposal/TubeConnectionsCommand.cs | 1 - .../BeingDisposedComponent.cs | 2 +- .../BeingDisposedSystem.cs | 3 +- .../Unit/Components/DisposalUnitComponent.cs | 13 - .../{EntitySystems => }/DisposableSystem.cs | 5 +- .../DisposalHolderComponent.cs | 3 +- .../Disposal/Unit/DisposalUnitSystem.cs | 44 + .../DoInsertDisposalUnitEvent.cs | 2 +- Content.Server/Fax/AdminUI/AdminFaxEui.cs | 1 + Content.Server/Fax/FaxSystem.cs | 6 +- .../Light/EntitySystems/PoweredLightSystem.cs | 2 + .../CrewMonitoringConsoleSystem.cs | 2 + .../CrewMonitoringServerSystem.cs | 4 +- .../Medical/SuitSensors/SuitSensorSystem.cs | 3 +- Content.Server/PDA/PdaSystem.cs | 6 +- .../EntitySystems/PowerReceiverSystem.cs | 1 - .../Power/Generation/Teg/TegSystem.cs | 1 + .../Radio/EntitySystems/JammerSystem.cs | 1 - .../Robotics/Systems/RoboticsConsoleSystem.cs | 3 +- Content.Server/RoundEnd/RoundEndSystem.cs | 3 +- .../Screens/Systems/ScreenSystem.cs | 4 +- .../SensorMonitoring/BatterySensorSystem.cs | 1 + .../SensorMonitoringConsoleSystem.UI.cs | 4 +- .../SensorMonitoringConsoleSystem.cs | 4 +- .../Shuttles/Systems/ArrivalsSystem.cs | 7 +- .../Systems/EmergencyShuttleSystem.Console.cs | 2 +- .../Systems/EmergencyShuttleSystem.cs | 2 +- .../Silicons/Borgs/BorgSystem.Transponder.cs | 7 +- .../SurveillanceCameraMonitorSystem.cs | 1 + .../Systems/SurveillanceCameraRouterSystem.cs | 6 +- .../Systems/SurveillanceCameraSystem.cs | 5 +- .../Turrets/DeployableTurretSystem.cs | 2 + .../Climbing/Components/ClimbableComponent.cs | 8 +- .../Climbing/Systems/ClimbSystem.cs | 10 +- .../Configurable/ConfigurationComponent.cs | 38 +- .../Configurable/SharedConfigurationSystem.cs | 77 ++ .../SharedThrowInsertContainerSystem.cs | 8 + .../Components/DeviceNetworkComponent.cs | 11 +- .../DeviceNetwork/DeviceNet.cs | 5 +- .../DeviceNetwork/DeviceNetworkConstants.cs | 4 +- .../Events/BeforeBroadcastAttemptEvent.cs | 17 + .../Events/BeforePacketSentEvent.cs | 35 + .../Events/DeviceNetworkPacketEvent.cs | 47 + .../Systems/SharedDeviceNetworkSystem.cs | 25 + .../Disposal/Mailing/MailingUnitComponent.cs | 18 +- .../{ => Mailing}/MailingUnitUiMessages.cs | 0 .../SharedDisposalRouterComponent.cs | 0 .../SharedDisposalTaggerComponent.cs | 0 .../Mailing/SharedMailingUnitSystem.cs | 174 ++++ .../MailingUnitBoundUserInterfaceState.cs | 45 - .../Disposal/SharedDisposalUnitSystem.cs | 162 ---- .../Disposal/Tube/DisposalEntryComponent.cs | 12 + .../SharedDisposalTubeComponent.cs | 0 .../Disposal/Unit/BeforeDisposalFlushEvent.cs | 10 + .../DisposalUnitComponent.cs} | 74 +- .../Disposal/Unit/SharedDisposalTubeSystem.cs | 14 + .../Disposal/Unit/SharedDisposalUnitSystem.cs | 879 +++++++++--------- .../SharedApcPowerReceiverComponent.cs | 12 +- .../SharedPowerReceiverSystem.cs | 9 +- .../Storage/EntitySystems/DumpableSystem.cs | 8 +- .../Entities/Structures/Furniture/toilet.yml | 2 - .../Structures/Piping/Disposal/units.yml | 16 +- 140 files changed, 1655 insertions(+), 1858 deletions(-) create mode 100644 Content.Client/Configurable/ConfigurationSystem.cs create mode 100644 Content.Client/DeviceNetwork/Systems/DeviceNetworkSystem.cs delete mode 100644 Content.Client/Disposal/DisposalUnitComponent.cs create mode 100644 Content.Client/Disposal/Mailing/MailingUnitBoundUserInterface.cs create mode 100644 Content.Client/Disposal/Mailing/MailingUnitSystem.cs rename Content.Client/Disposal/{UI => Mailing}/MailingUnitWindow.xaml (80%) create mode 100644 Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs rename Content.Client/Disposal/{UI => }/PressureBar.cs (96%) delete mode 100644 Content.Client/Disposal/Systems/DisposalUnitSystem.cs rename Content.Client/Disposal/{UI => Tube}/DisposalRouterBoundUserInterface.cs (95%) rename Content.Client/Disposal/{UI => Tube}/DisposalRouterWindow.xaml (100%) rename Content.Client/Disposal/{UI => Tube}/DisposalRouterWindow.xaml.cs (90%) rename Content.Client/Disposal/{UI => Tube}/DisposalTaggerBoundUserInterface.cs (95%) rename Content.Client/Disposal/{UI => Tube}/DisposalTaggerWindow.xaml (100%) rename Content.Client/Disposal/{UI => Tube}/DisposalTaggerWindow.xaml.cs (90%) create mode 100644 Content.Client/Disposal/Tube/DisposalTubeSystem.cs delete mode 100644 Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs delete mode 100644 Content.Client/Disposal/UI/DisposalUnitWindow.xaml.cs delete mode 100644 Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs create mode 100644 Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs create mode 100644 Content.Client/Disposal/Unit/DisposalUnitSystem.cs rename Content.Client/Disposal/{UI => Unit}/DisposalUnitWindow.xaml (72%) create mode 100644 Content.Client/Disposal/Unit/DisposalUnitWindow.xaml.cs delete mode 100644 Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs rename Content.Server/Disposal/Tube/{Components => }/DisposalBendComponent.cs (70%) rename Content.Server/Disposal/Tube/{Components => }/DisposalJunctionComponent.cs (84%) rename Content.Server/Disposal/Tube/{Components => }/DisposalRouterComponent.cs (57%) rename Content.Server/Disposal/Tube/{Components => }/DisposalSignalRouterComponent.cs (91%) rename Content.Server/Disposal/Tube/{Systems => }/DisposalSignalRouterSystem.cs (94%) rename Content.Server/Disposal/Tube/{Components => }/DisposalTaggerComponent.cs (53%) rename Content.Server/Disposal/Tube/{Components => }/DisposalTransitComponent.cs (82%) rename Content.Server/Disposal/Tube/{Components => }/DisposalTubeComponent.cs (90%) rename Content.Server/Disposal/Unit/{Components => }/BeingDisposedComponent.cs (81%) rename Content.Server/Disposal/Unit/{EntitySystems => }/BeingDisposedSystem.cs (92%) delete mode 100644 Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs rename Content.Server/Disposal/Unit/{EntitySystems => }/DisposableSystem.cs (98%) rename Content.Server/Disposal/Unit/{Components => }/DisposalHolderComponent.cs (94%) create mode 100644 Content.Server/Disposal/Unit/DisposalUnitSystem.cs rename Content.Server/Disposal/Unit/{EntitySystems => }/DoInsertDisposalUnitEvent.cs (65%) create mode 100644 Content.Shared/Configurable/SharedConfigurationSystem.cs create mode 100644 Content.Shared/Containers/SharedThrowInsertContainerSystem.cs rename {Content.Server => Content.Shared}/DeviceNetwork/Components/DeviceNetworkComponent.cs (93%) rename {Content.Server => Content.Shared}/DeviceNetwork/DeviceNet.cs (97%) rename {Content.Server => Content.Shared}/DeviceNetwork/DeviceNetworkConstants.cs (96%) create mode 100644 Content.Shared/DeviceNetwork/Events/BeforeBroadcastAttemptEvent.cs create mode 100644 Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs create mode 100644 Content.Shared/DeviceNetwork/Events/DeviceNetworkPacketEvent.cs create mode 100644 Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkSystem.cs rename {Content.Server => Content.Shared}/Disposal/Mailing/MailingUnitComponent.cs (57%) rename Content.Shared/Disposal/{ => Mailing}/MailingUnitUiMessages.cs (100%) rename Content.Shared/Disposal/{Components => Mailing}/SharedDisposalRouterComponent.cs (100%) rename Content.Shared/Disposal/{Components => Mailing}/SharedDisposalTaggerComponent.cs (100%) create mode 100644 Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs delete mode 100644 Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs delete mode 100644 Content.Shared/Disposal/SharedDisposalUnitSystem.cs create mode 100644 Content.Shared/Disposal/Tube/DisposalEntryComponent.cs rename Content.Shared/Disposal/{Components => Tube}/SharedDisposalTubeComponent.cs (100%) create mode 100644 Content.Shared/Disposal/Unit/BeforeDisposalFlushEvent.cs rename Content.Shared/Disposal/{Components/SharedDisposalUnitComponent.cs => Unit/DisposalUnitComponent.cs} (68%) create mode 100644 Content.Shared/Disposal/Unit/SharedDisposalTubeSystem.cs rename Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs => Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs (53%) diff --git a/Content.Client/Configurable/ConfigurationSystem.cs b/Content.Client/Configurable/ConfigurationSystem.cs new file mode 100644 index 0000000000..6594375ba2 --- /dev/null +++ b/Content.Client/Configurable/ConfigurationSystem.cs @@ -0,0 +1,25 @@ +using Content.Client.Configurable.UI; +using Content.Shared.Configurable; + +namespace Content.Client.Configurable; + +public sealed class ConfigurationSystem : SharedConfigurationSystem +{ + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnConfigurationState); + } + + private void OnConfigurationState(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (_uiSystem.TryGetOpenUi(ent.Owner, + ConfigurationComponent.ConfigurationUiKey.Key, + out var bui)) + { + bui.Refresh(ent); + } + } +} diff --git a/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs b/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs index e4966f1ec4..a3845ba1eb 100644 --- a/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs +++ b/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs @@ -1,6 +1,8 @@ -using System.Text.RegularExpressions; -using Robust.Client.GameObjects; +using System.Numerics; +using System.Text.RegularExpressions; +using Content.Shared.Configurable; using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; using static Content.Shared.Configurable.ConfigurationComponent; namespace Content.Client.Configurable.UI @@ -19,16 +21,53 @@ namespace Content.Client.Configurable.UI base.Open(); _menu = this.CreateWindow(); _menu.OnConfiguration += SendConfiguration; + if (EntMan.TryGetComponent(Owner, out ConfigurationComponent? component)) + Refresh((Owner, component)); } - protected override void UpdateState(BoundUserInterfaceState state) + public void Refresh(Entity entity) { - base.UpdateState(state); - - if (state is not ConfigurationBoundUserInterfaceState configurationState) + if (_menu == null) return; - _menu?.Populate(configurationState); + _menu.Column.Children.Clear(); + _menu.Inputs.Clear(); + + foreach (var field in entity.Comp.Config) + { + var label = new Label + { + Margin = new Thickness(0, 0, 8, 0), + Name = field.Key, + Text = field.Key + ":", + VerticalAlignment = Control.VAlignment.Center, + HorizontalExpand = true, + SizeFlagsStretchRatio = .2f, + MinSize = new Vector2(60, 0) + }; + + var input = new LineEdit + { + Name = field.Key + "-input", + Text = field.Value ?? "", + IsValid = _menu.Validate, + HorizontalExpand = true, + SizeFlagsStretchRatio = .8f + }; + + _menu.Inputs.Add((field.Key, input)); + + var row = new BoxContainer + { + Orientation = BoxContainer.LayoutOrientation.Horizontal + }; + + ConfigurationMenu.CopyProperties(_menu.Row, row); + + row.AddChild(label); + row.AddChild(input); + _menu.Column.AddChild(row); + } } protected override void ReceiveMessage(BoundUserInterfaceMessage message) diff --git a/Content.Client/Configurable/UI/ConfigurationMenu.cs b/Content.Client/Configurable/UI/ConfigurationMenu.cs index 29217eef7b..4ca68c5fa0 100644 --- a/Content.Client/Configurable/UI/ConfigurationMenu.cs +++ b/Content.Client/Configurable/UI/ConfigurationMenu.cs @@ -1,12 +1,8 @@ -using System.Collections.Generic; -using System.Numerics; +using System.Numerics; using System.Text.RegularExpressions; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; -using Robust.Shared.Localization; -using Robust.Shared.Maths; -using static Content.Shared.Configurable.ConfigurationComponent; using static Robust.Client.UserInterface.Controls.BaseButton; using static Robust.Client.UserInterface.Controls.BoxContainer; @@ -14,10 +10,10 @@ namespace Content.Client.Configurable.UI { public sealed class ConfigurationMenu : DefaultWindow { - private readonly BoxContainer _column; - private readonly BoxContainer _row; + public readonly BoxContainer Column; + public readonly BoxContainer Row; - private readonly List<(string name, LineEdit input)> _inputs; + public readonly List<(string name, LineEdit input)> Inputs; [ViewVariables] public Regex? Validation { get; internal set; } @@ -28,7 +24,7 @@ namespace Content.Client.Configurable.UI { MinSize = SetSize = new Vector2(300, 250); - _inputs = new List<(string name, LineEdit input)>(); + Inputs = new List<(string name, LineEdit input)>(); Title = Loc.GetString("configuration-menu-device-title"); @@ -39,14 +35,14 @@ namespace Content.Client.Configurable.UI HorizontalExpand = true }; - _column = new BoxContainer + Column = new BoxContainer { Orientation = LayoutOrientation.Vertical, Margin = new Thickness(8), SeparationOverride = 16, }; - _row = new BoxContainer + Row = new BoxContainer { Orientation = LayoutOrientation.Horizontal, SeparationOverride = 16, @@ -69,61 +65,20 @@ namespace Content.Client.Configurable.UI ModulateSelfOverride = Color.FromHex("#202025") }; - outerColumn.AddChild(_column); + outerColumn.AddChild(Column); baseContainer.AddChild(outerColumn); baseContainer.AddChild(confirmButton); Contents.AddChild(baseContainer); } - public void Populate(ConfigurationBoundUserInterfaceState state) - { - _column.Children.Clear(); - _inputs.Clear(); - - foreach (var field in state.Config) - { - var label = new Label - { - Margin = new Thickness(0, 0, 8, 0), - Name = field.Key, - Text = field.Key + ":", - VerticalAlignment = VAlignment.Center, - HorizontalExpand = true, - SizeFlagsStretchRatio = .2f, - MinSize = new Vector2(60, 0) - }; - - var input = new LineEdit - { - Name = field.Key + "-input", - Text = field.Value ?? "", - IsValid = Validate, - HorizontalExpand = true, - SizeFlagsStretchRatio = .8f - }; - - _inputs.Add((field.Key, input)); - - var row = new BoxContainer - { - Orientation = LayoutOrientation.Horizontal - }; - CopyProperties(_row, row); - - row.AddChild(label); - row.AddChild(input); - _column.AddChild(row); - } - } - private void OnConfirm(ButtonEventArgs args) { - var config = GenerateDictionary(_inputs, "Text"); + var config = GenerateDictionary(Inputs, "Text"); OnConfiguration?.Invoke(config); Close(); } - private bool Validate(string value) + public bool Validate(string value) { return Validation?.IsMatch(value) != false; } @@ -140,7 +95,7 @@ namespace Content.Client.Configurable.UI return dictionary; } - private static void CopyProperties(T from, T to) where T : Control + public static void CopyProperties(T from, T to) where T : Control { foreach (var property in from.AllAttachedProperties) { diff --git a/Content.Client/Decals/DecalSystem.cs b/Content.Client/Decals/DecalSystem.cs index 41e5f39c28..172a06c4cd 100644 --- a/Content.Client/Decals/DecalSystem.cs +++ b/Content.Client/Decals/DecalSystem.cs @@ -13,7 +13,7 @@ namespace Content.Client.Decals [Dependency] private readonly IOverlayManager _overlayManager = default!; [Dependency] private readonly SpriteSystem _sprites = default!; - private DecalOverlay _overlay = default!; + private DecalOverlay? _overlay; private HashSet _removedUids = new(); private readonly List _removedChunks = new(); @@ -31,6 +31,9 @@ namespace Content.Client.Decals public void ToggleOverlay() { + if (_overlay == null) + return; + if (_overlayManager.HasOverlay()) { _overlayManager.RemoveOverlay(_overlay); @@ -44,6 +47,10 @@ namespace Content.Client.Decals public override void Shutdown() { base.Shutdown(); + + if (_overlay == null) + return; + _overlayManager.RemoveOverlay(_overlay); } diff --git a/Content.Client/DeviceNetwork/Systems/DeviceNetworkSystem.cs b/Content.Client/DeviceNetwork/Systems/DeviceNetworkSystem.cs new file mode 100644 index 0000000000..5b11b2bfe7 --- /dev/null +++ b/Content.Client/DeviceNetwork/Systems/DeviceNetworkSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared.DeviceNetwork.Systems; + +namespace Content.Client.DeviceNetwork.Systems; + +public sealed class DeviceNetworkSystem : SharedDeviceNetworkSystem +{ + +} diff --git a/Content.Client/Disposal/DisposalUnitComponent.cs b/Content.Client/Disposal/DisposalUnitComponent.cs deleted file mode 100644 index e63a3fd45e..0000000000 --- a/Content.Client/Disposal/DisposalUnitComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Shared.Disposal.Components; - -namespace Content.Client.Disposal; - -[RegisterComponent] -public sealed partial class DisposalUnitComponent : SharedDisposalUnitComponent -{ - -} diff --git a/Content.Client/Disposal/Mailing/MailingUnitBoundUserInterface.cs b/Content.Client/Disposal/Mailing/MailingUnitBoundUserInterface.cs new file mode 100644 index 0000000000..013c4eaa1b --- /dev/null +++ b/Content.Client/Disposal/Mailing/MailingUnitBoundUserInterface.cs @@ -0,0 +1,79 @@ +using Content.Client.Disposal.Unit; +using Content.Client.Power.EntitySystems; +using Content.Shared.Disposal; +using Content.Shared.Disposal.Components; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.Disposal.Mailing; + +public sealed class MailingUnitBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + public MailingUnitWindow? MailingUnitWindow; + + public MailingUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + private void ButtonPressed(DisposalUnitComponent.UiButton button) + { + SendMessage(new DisposalUnitComponent.UiButtonPressedMessage(button)); + // If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs + // the pressure lerp up. + } + + private void TargetSelected(ItemList.ItemListSelectedEventArgs args) + { + var item = args.ItemList[args.ItemIndex]; + SendMessage(new TargetSelectedMessage(item.Text)); + } + + protected override void Open() + { + base.Open(); + + MailingUnitWindow = this.CreateWindow(); + MailingUnitWindow.OpenCenteredRight(); + + MailingUnitWindow.Eject.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Eject); + MailingUnitWindow.Engage.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Engage); + MailingUnitWindow.Power.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Power); + + MailingUnitWindow.TargetListContainer.OnItemSelected += TargetSelected; + + if (EntMan.TryGetComponent(Owner, out MailingUnitComponent? component)) + Refresh((Owner, component)); + } + + public void Refresh(Entity entity) + { + if (MailingUnitWindow == null) + return; + + // TODO: This should be decoupled from disposals + if (EntMan.TryGetComponent(entity.Owner, out DisposalUnitComponent? disposals)) + { + var disposalSystem = EntMan.System(); + + var disposalState = disposalSystem.GetState(Owner, disposals); + var fullPressure = disposalSystem.EstimatedFullPressure(Owner, disposals); + + MailingUnitWindow.UnitState.Text = Loc.GetString($"disposal-unit-state-{disposalState}"); + MailingUnitWindow.FullPressure = fullPressure; + MailingUnitWindow.PressureBar.UpdatePressure(fullPressure); + MailingUnitWindow.Power.Pressed = EntMan.System().IsPowered(Owner); + MailingUnitWindow.Engage.Pressed = disposals.Engaged; + } + + MailingUnitWindow.Title = Loc.GetString("ui-mailing-unit-window-title", ("tag", entity.Comp.Tag ?? " ")); + //UnitTag.Text = state.Tag; + MailingUnitWindow.Target.Text = entity.Comp.Target; + + MailingUnitWindow.TargetListContainer.Clear(); + foreach (var target in entity.Comp.TargetList) + { + MailingUnitWindow.TargetListContainer.AddItem(target); + } + } +} diff --git a/Content.Client/Disposal/Mailing/MailingUnitSystem.cs b/Content.Client/Disposal/Mailing/MailingUnitSystem.cs new file mode 100644 index 0000000000..780656b4f0 --- /dev/null +++ b/Content.Client/Disposal/Mailing/MailingUnitSystem.cs @@ -0,0 +1,22 @@ +using Content.Shared.Disposal; +using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Mailing; + +namespace Content.Client.Disposal.Mailing; + +public sealed class MailingUnitSystem : SharedMailingUnitSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMailingState); + } + + private void OnMailingState(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (UserInterfaceSystem.TryGetOpenUi(ent.Owner, MailingUnitUiKey.Key, out var bui)) + { + bui.Refresh(ent); + } + } +} diff --git a/Content.Client/Disposal/UI/MailingUnitWindow.xaml b/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml similarity index 80% rename from Content.Client/Disposal/UI/MailingUnitWindow.xaml rename to Content.Client/Disposal/Mailing/MailingUnitWindow.xaml index c57ca7b4c1..0acd300895 100644 --- a/Content.Client/Disposal/UI/MailingUnitWindow.xaml +++ b/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml @@ -1,12 +1,14 @@ - - + diff --git a/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs b/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs new file mode 100644 index 0000000000..8f1dcaf7dc --- /dev/null +++ b/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs @@ -0,0 +1,27 @@ +using Content.Client.UserInterface.Controls; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Timing; + +namespace Content.Client.Disposal.Mailing +{ + /// + /// Client-side UI used to control a + /// + [GenerateTypedNameReferences] + public sealed partial class MailingUnitWindow : FancyWindow + { + public TimeSpan FullPressure; + + public MailingUnitWindow() + { + RobustXamlLoader.Load(this); + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + PressureBar.UpdatePressure(FullPressure); + } + } +} diff --git a/Content.Client/Disposal/UI/PressureBar.cs b/Content.Client/Disposal/PressureBar.cs similarity index 96% rename from Content.Client/Disposal/UI/PressureBar.cs rename to Content.Client/Disposal/PressureBar.cs index bff95ca430..7b2ebacaf7 100644 --- a/Content.Client/Disposal/UI/PressureBar.cs +++ b/Content.Client/Disposal/PressureBar.cs @@ -1,9 +1,10 @@ using Content.Shared.Disposal; +using Content.Shared.Disposal.Unit; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; -namespace Content.Client.Disposal.UI; +namespace Content.Client.Disposal; public sealed class PressureBar : ProgressBar { diff --git a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs deleted file mode 100644 index da548c1e54..0000000000 --- a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Content.Shared.Disposal; -using Content.Shared.Disposal.Components; -using Content.Shared.DragDrop; -using Content.Shared.Emag.Systems; -using Robust.Client.GameObjects; -using Robust.Client.Animations; -using Robust.Client.Graphics; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.GameStates; -using Robust.Shared.Physics.Events; -using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent; - -namespace Content.Client.Disposal.Systems; - -public sealed class DisposalUnitSystem : SharedDisposalUnitSystem -{ - [Dependency] private readonly AppearanceSystem _appearanceSystem = default!; - [Dependency] private readonly AnimationPlayerSystem _animationSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - - private const string AnimationKey = "disposal_unit_animation"; - - private const string DefaultFlushState = "disposal-flush"; - private const string DefaultChargeState = "disposal-charging"; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnHandleState); - SubscribeLocalEvent(OnPreventCollide); - SubscribeLocalEvent(OnCanDragDropOn); - SubscribeLocalEvent(OnEmagged); - - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnAppearanceChange); - } - - private void OnHandleState(EntityUid uid, DisposalUnitComponent component, ref ComponentHandleState args) - { - if (args.Current is not DisposalUnitComponentState state) - return; - - component.FlushSound = state.FlushSound; - component.State = state.State; - component.NextPressurized = state.NextPressurized; - component.AutomaticEngageTime = state.AutomaticEngageTime; - component.NextFlush = state.NextFlush; - component.Powered = state.Powered; - component.Engaged = state.Engaged; - component.RecentlyEjected.Clear(); - component.RecentlyEjected.AddRange(EnsureEntityList(state.RecentlyEjected, uid)); - } - - public override bool HasDisposals(EntityUid? uid) - { - return HasComp(uid); - } - - public override bool ResolveDisposals(EntityUid uid, [NotNullWhen(true)] ref SharedDisposalUnitComponent? component) - { - if (component != null) - return true; - - TryComp(uid, out var storage); - component = storage; - return component != null; - } - - public override void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null) - { - return; - } - - private void OnComponentInit(EntityUid uid, SharedDisposalUnitComponent sharedDisposalUnit, ComponentInit args) - { - if (!TryComp(uid, out var sprite) || !TryComp(uid, out var appearance)) - return; - - UpdateState(uid, sharedDisposalUnit, sprite, appearance); - } - - private void OnAppearanceChange(EntityUid uid, SharedDisposalUnitComponent unit, ref AppearanceChangeEvent args) - { - if (args.Sprite == null) - return; - - UpdateState(uid, unit, args.Sprite, args.Component); - } - - /// - /// Update visuals and tick animation - /// - 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.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.OverlayFlushing) - { - if (!_animationSystem.HasRunningAnimation(uid, AnimationKey)) - { - var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer) - ? sprite.LayerGetState(flushLayer) - : new RSI.StateId(DefaultFlushState); - - // Setup the flush animation to play - var anim = new Animation - { - Length = unit.FlushDelay, - AnimationTracks = - { - new AnimationTrackSpriteFlick - { - LayerKey = DisposalUnitVisualLayers.OverlayFlush, - KeyFrames = - { - // Play the flush animation - new AnimationTrackSpriteFlick.KeyFrame(flushState, 0), - // Return to base state (though, depending on how the unit is - // configured we might get an appearance change event telling - // us to go to charging state) - new AnimationTrackSpriteFlick.KeyFrame(chargingState, (float) unit.FlushDelay.TotalSeconds) - } - }, - } - }; - - if (unit.FlushSound != null) - { - anim.AnimationTracks.Add( - new AnimationTrackPlaySound - { - KeyFrames = - { - new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(unit.FlushSound), 0) - } - }); - } - - _animationSystem.Play(uid, anim, AnimationKey); - } - } - else if (state == VisualState.OverlayCharging) - sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, chargingState); - 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); - sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayReady, - (lightState & LightStates.Ready) != 0); - sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFull, - (lightState & LightStates.Full) != 0); - } -} - -public enum DisposalUnitVisualLayers : byte -{ - Unanchored, - Base, - BaseCharging, - OverlayFlush, - OverlayCharging, - OverlayReady, - OverlayFull, - OverlayEngaged -} diff --git a/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs b/Content.Client/Disposal/Tube/DisposalRouterBoundUserInterface.cs similarity index 95% rename from Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs rename to Content.Client/Disposal/Tube/DisposalRouterBoundUserInterface.cs index 296e71d3a9..1116dc7257 100644 --- a/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs +++ b/Content.Client/Disposal/Tube/DisposalRouterBoundUserInterface.cs @@ -1,9 +1,8 @@ using JetBrains.Annotations; -using Robust.Client.GameObjects; using Robust.Client.UserInterface; using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; -namespace Content.Client.Disposal.UI +namespace Content.Client.Disposal.Tube { /// /// Initializes a and updates it when new server messages are received. diff --git a/Content.Client/Disposal/UI/DisposalRouterWindow.xaml b/Content.Client/Disposal/Tube/DisposalRouterWindow.xaml similarity index 100% rename from Content.Client/Disposal/UI/DisposalRouterWindow.xaml rename to Content.Client/Disposal/Tube/DisposalRouterWindow.xaml diff --git a/Content.Client/Disposal/UI/DisposalRouterWindow.xaml.cs b/Content.Client/Disposal/Tube/DisposalRouterWindow.xaml.cs similarity index 90% rename from Content.Client/Disposal/UI/DisposalRouterWindow.xaml.cs rename to Content.Client/Disposal/Tube/DisposalRouterWindow.xaml.cs index 39ee9fbe23..c9d7d286eb 100644 --- a/Content.Client/Disposal/UI/DisposalRouterWindow.xaml.cs +++ b/Content.Client/Disposal/Tube/DisposalRouterWindow.xaml.cs @@ -1,11 +1,10 @@ using Content.Shared.Disposal.Components; using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; -namespace Content.Client.Disposal.UI +namespace Content.Client.Disposal.Tube { /// /// Client-side UI used to control a diff --git a/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs b/Content.Client/Disposal/Tube/DisposalTaggerBoundUserInterface.cs similarity index 95% rename from Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs rename to Content.Client/Disposal/Tube/DisposalTaggerBoundUserInterface.cs index 7fc0eb8540..5618e83ecf 100644 --- a/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs +++ b/Content.Client/Disposal/Tube/DisposalTaggerBoundUserInterface.cs @@ -1,9 +1,8 @@ using JetBrains.Annotations; -using Robust.Client.GameObjects; using Robust.Client.UserInterface; using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; -namespace Content.Client.Disposal.UI +namespace Content.Client.Disposal.Tube { /// /// Initializes a and updates it when new server messages are received. diff --git a/Content.Client/Disposal/UI/DisposalTaggerWindow.xaml b/Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml similarity index 100% rename from Content.Client/Disposal/UI/DisposalTaggerWindow.xaml rename to Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml diff --git a/Content.Client/Disposal/UI/DisposalTaggerWindow.xaml.cs b/Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml.cs similarity index 90% rename from Content.Client/Disposal/UI/DisposalTaggerWindow.xaml.cs rename to Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml.cs index b49d5d997b..ae8286cf14 100644 --- a/Content.Client/Disposal/UI/DisposalTaggerWindow.xaml.cs +++ b/Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml.cs @@ -1,11 +1,10 @@ using Content.Shared.Disposal.Components; using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; -namespace Content.Client.Disposal.UI +namespace Content.Client.Disposal.Tube { /// /// Client-side UI used to control a diff --git a/Content.Client/Disposal/Tube/DisposalTubeSystem.cs b/Content.Client/Disposal/Tube/DisposalTubeSystem.cs new file mode 100644 index 0000000000..fd86f7ec88 --- /dev/null +++ b/Content.Client/Disposal/Tube/DisposalTubeSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared.Disposal.Unit; + +namespace Content.Client.Disposal.Tube; + +public sealed class DisposalTubeSystem : SharedDisposalTubeSystem +{ + +} diff --git a/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs b/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs deleted file mode 100644 index d2bec6e94f..0000000000 --- a/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Content.Client.Disposal.Systems; -using Content.Shared.Disposal; -using Content.Shared.Disposal.Components; -using JetBrains.Annotations; -using Robust.Client.GameObjects; -using Robust.Client.UserInterface.Controls; -using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent; - -namespace Content.Client.Disposal.UI -{ - /// - /// Initializes a or a and updates it when new server messages are received. - /// - [UsedImplicitly] - public sealed class DisposalUnitBoundUserInterface : BoundUserInterface - { - // What are you doing here - [ViewVariables] - public MailingUnitWindow? MailingUnitWindow; - - [ViewVariables] - public DisposalUnitWindow? DisposalUnitWindow; - - public DisposalUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) - { - } - - private void ButtonPressed(UiButton button) - { - SendMessage(new UiButtonPressedMessage(button)); - // If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs - // the pressure lerp up. - } - - private void TargetSelected(ItemList.ItemListSelectedEventArgs args) - { - var item = args.ItemList[args.ItemIndex]; - SendMessage(new TargetSelectedMessage(item.Text)); - } - - protected override void Open() - { - base.Open(); - - if (UiKey is MailingUnitUiKey) - { - MailingUnitWindow = new MailingUnitWindow(); - - MailingUnitWindow.OpenCenteredRight(); - MailingUnitWindow.OnClose += Close; - - MailingUnitWindow.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject); - MailingUnitWindow.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage); - MailingUnitWindow.Power.OnPressed += _ => ButtonPressed(UiButton.Power); - - MailingUnitWindow.TargetListContainer.OnItemSelected += TargetSelected; - } - else if (UiKey is DisposalUnitUiKey) - { - DisposalUnitWindow = new DisposalUnitWindow(); - - DisposalUnitWindow.OpenCenteredRight(); - DisposalUnitWindow.OnClose += Close; - - DisposalUnitWindow.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject); - DisposalUnitWindow.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage); - DisposalUnitWindow.Power.OnPressed += _ => ButtonPressed(UiButton.Power); - } - } - - protected override void UpdateState(BoundUserInterfaceState state) - { - base.UpdateState(state); - - if (state is not MailingUnitBoundUserInterfaceState && state is not DisposalUnitBoundUserInterfaceState) - { - return; - } - - switch (state) - { - case MailingUnitBoundUserInterfaceState mailingUnitState: - MailingUnitWindow?.UpdateState(mailingUnitState); - break; - - case DisposalUnitBoundUserInterfaceState disposalUnitState: - DisposalUnitWindow?.UpdateState(disposalUnitState); - break; - } - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (!disposing) - return; - - MailingUnitWindow?.Dispose(); - DisposalUnitWindow?.Dispose(); - } - } -} diff --git a/Content.Client/Disposal/UI/DisposalUnitWindow.xaml.cs b/Content.Client/Disposal/UI/DisposalUnitWindow.xaml.cs deleted file mode 100644 index 3440fe208a..0000000000 --- a/Content.Client/Disposal/UI/DisposalUnitWindow.xaml.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Content.Shared.Disposal.Components; -using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.CustomControls; -using Robust.Client.UserInterface.XAML; -using Robust.Shared.Timing; -using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent; - -namespace Content.Client.Disposal.UI -{ - /// - /// Client-side UI used to control a - /// - [GenerateTypedNameReferences] - public sealed partial class DisposalUnitWindow : DefaultWindow - { - public TimeSpan FullPressure; - - public DisposalUnitWindow() - { - IoCManager.InjectDependencies(this); - RobustXamlLoader.Load(this); - } - - /// - /// Update the interface state for the disposals window. - /// - /// true if we should stop updating every frame. - public void UpdateState(DisposalUnitBoundUserInterfaceState state) - { - Title = state.UnitName; - UnitState.Text = state.UnitState; - Power.Pressed = state.Powered; - Engage.Pressed = state.Engaged; - FullPressure = state.FullPressureTime; - } - - protected override void FrameUpdate(FrameEventArgs args) - { - base.FrameUpdate(args); - PressureBar.UpdatePressure(FullPressure); - } - } -} diff --git a/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs b/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs deleted file mode 100644 index 489d749a0c..0000000000 --- a/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Content.Shared.Disposal; -using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.CustomControls; -using Robust.Client.UserInterface.XAML; -using Robust.Shared.Timing; - -namespace Content.Client.Disposal.UI -{ - /// - /// Client-side UI used to control a - /// - [GenerateTypedNameReferences] - public sealed partial class MailingUnitWindow : DefaultWindow - { - public TimeSpan FullPressure; - - public MailingUnitWindow() - { - RobustXamlLoader.Load(this); - } - - /// - /// Update the interface state for the disposals window. - /// - /// true if we should stop updating every frame. - public bool UpdateState(MailingUnitBoundUserInterfaceState state) - { - var disposalState = state.DisposalState; - - Title = Loc.GetString("ui-mailing-unit-window-title", ("tag", state.Tag ?? " ")); - UnitState.Text = disposalState.UnitState; - FullPressure = disposalState.FullPressureTime; - var pressureReached = PressureBar.UpdatePressure(disposalState.FullPressureTime); - Power.Pressed = disposalState.Powered; - Engage.Pressed = disposalState.Engaged; - - //UnitTag.Text = state.Tag; - Target.Text = state.Target; - - TargetListContainer.Clear(); - foreach (var target in state.TargetList) - { - TargetListContainer.AddItem(target); - } - - return !disposalState.Powered || pressureReached; - } - - protected override void FrameUpdate(FrameEventArgs args) - { - base.FrameUpdate(args); - PressureBar.UpdatePressure(FullPressure); - } - } -} diff --git a/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs b/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs new file mode 100644 index 0000000000..62386291a4 --- /dev/null +++ b/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs @@ -0,0 +1,63 @@ +using Content.Client.Disposal.Mailing; +using Content.Client.Power.EntitySystems; +using Content.Shared.Disposal.Components; +using JetBrains.Annotations; +using Robust.Client.UserInterface; + +namespace Content.Client.Disposal.Unit +{ + /// + /// Initializes a or a and updates it when new server messages are received. + /// + [UsedImplicitly] + public sealed class DisposalUnitBoundUserInterface : BoundUserInterface + { + [ViewVariables] private DisposalUnitWindow? _disposalUnitWindow; + + public DisposalUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + private void ButtonPressed(DisposalUnitComponent.UiButton button) + { + SendPredictedMessage(new DisposalUnitComponent.UiButtonPressedMessage(button)); + // If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs + // the pressure lerp up. + } + + protected override void Open() + { + base.Open(); + + _disposalUnitWindow = this.CreateWindow(); + + _disposalUnitWindow.OpenCenteredRight(); + + _disposalUnitWindow.Eject.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Eject); + _disposalUnitWindow.Engage.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Engage); + _disposalUnitWindow.Power.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Power); + + if (EntMan.TryGetComponent(Owner, out DisposalUnitComponent? component)) + { + Refresh((Owner, component)); + } + } + + public void Refresh(Entity entity) + { + if (_disposalUnitWindow == null) + return; + + var disposalSystem = EntMan.System(); + + _disposalUnitWindow.Title = EntMan.GetComponent(entity.Owner).EntityName; + + var state = disposalSystem.GetState(entity.Owner, entity.Comp); + + _disposalUnitWindow.UnitState.Text = Loc.GetString($"disposal-unit-state-{state}"); + _disposalUnitWindow.Power.Pressed = EntMan.System().IsPowered(Owner); + _disposalUnitWindow.Engage.Pressed = entity.Comp.Engaged; + _disposalUnitWindow.FullPressure = disposalSystem.EstimatedFullPressure(entity.Owner, entity.Comp); + } + } +} diff --git a/Content.Client/Disposal/Unit/DisposalUnitSystem.cs b/Content.Client/Disposal/Unit/DisposalUnitSystem.cs new file mode 100644 index 0000000000..30ca320a2a --- /dev/null +++ b/Content.Client/Disposal/Unit/DisposalUnitSystem.cs @@ -0,0 +1,156 @@ +using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Unit; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Shared.Audio.Systems; + +namespace Content.Client.Disposal.Unit; + +public sealed class DisposalUnitSystem : SharedDisposalUnitSystem +{ + [Dependency] private readonly AppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly AnimationPlayerSystem _animationSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + + private const string AnimationKey = "disposal_unit_animation"; + + private const string DefaultFlushState = "disposal-flush"; + private const string DefaultChargeState = "disposal-charging"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHandleState); + + SubscribeLocalEvent(OnAppearanceChange); + } + + private void OnHandleState(EntityUid uid, DisposalUnitComponent component, ref AfterAutoHandleStateEvent args) + { + UpdateUI((uid, component)); + } + + protected override void UpdateUI(Entity entity) + { + if (_uiSystem.TryGetOpenUi(entity.Owner, DisposalUnitComponent.DisposalUnitUiKey.Key, out var bui)) + { + bui.Refresh(entity); + } + } + + protected override void OnDisposalInit(Entity ent, ref ComponentInit args) + { + base.OnDisposalInit(ent, ref args); + + if (!TryComp(ent, out var sprite) || !TryComp(ent, out var appearance)) + return; + + UpdateState(ent, sprite, appearance); + } + + private void OnAppearanceChange(Entity ent, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + + UpdateState(ent, args.Sprite, args.Component); + } + + /// + /// Update visuals and tick animation + /// + private void UpdateState(Entity ent, SpriteComponent sprite, AppearanceComponent appearance) + { + if (!_appearanceSystem.TryGetData(ent, DisposalUnitComponent.Visuals.VisualState, out var state, appearance)) + return; + + sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == DisposalUnitComponent.VisualState.UnAnchored); + sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == DisposalUnitComponent.VisualState.Anchored); + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFlush, state is DisposalUnitComponent.VisualState.OverlayFlushing or DisposalUnitComponent.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 == DisposalUnitComponent.VisualState.OverlayFlushing) + { + if (!_animationSystem.HasRunningAnimation(ent, AnimationKey)) + { + var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer) + ? sprite.LayerGetState(flushLayer) + : new RSI.StateId(DefaultFlushState); + + // Setup the flush animation to play + var anim = new Animation + { + Length = ent.Comp.FlushDelay, + AnimationTracks = + { + new AnimationTrackSpriteFlick + { + LayerKey = DisposalUnitVisualLayers.OverlayFlush, + KeyFrames = + { + // Play the flush animation + new AnimationTrackSpriteFlick.KeyFrame(flushState, 0), + // Return to base state (though, depending on how the unit is + // configured we might get an appearance change event telling + // us to go to charging state) + new AnimationTrackSpriteFlick.KeyFrame(chargingState, (float) ent.Comp.FlushDelay.TotalSeconds) + } + }, + } + }; + + if (ent.Comp.FlushSound != null) + { + anim.AnimationTracks.Add( + new AnimationTrackPlaySound + { + KeyFrames = + { + new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(ent.Comp.FlushSound), 0) + } + }); + } + + _animationSystem.Play(ent, anim, AnimationKey); + } + } + else if (state == DisposalUnitComponent.VisualState.OverlayCharging) + sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, chargingState); + else + _animationSystem.Stop(ent.Owner, AnimationKey); + + if (!_appearanceSystem.TryGetData(ent, DisposalUnitComponent.Visuals.Handle, out var handleState, appearance)) + handleState = DisposalUnitComponent.HandleState.Normal; + + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != DisposalUnitComponent.HandleState.Normal); + + if (!_appearanceSystem.TryGetData(ent, DisposalUnitComponent.Visuals.Light, out var lightState, appearance)) + lightState = DisposalUnitComponent.LightStates.Off; + + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging, + (lightState & DisposalUnitComponent.LightStates.Charging) != 0); + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayReady, + (lightState & DisposalUnitComponent.LightStates.Ready) != 0); + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFull, + (lightState & DisposalUnitComponent.LightStates.Full) != 0); + } +} + +public enum DisposalUnitVisualLayers : byte +{ + Unanchored, + Base, + BaseCharging, + OverlayFlush, + OverlayCharging, + OverlayReady, + OverlayFull, + OverlayEngaged +} diff --git a/Content.Client/Disposal/UI/DisposalUnitWindow.xaml b/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml similarity index 72% rename from Content.Client/Disposal/UI/DisposalUnitWindow.xaml rename to Content.Client/Disposal/Unit/DisposalUnitWindow.xaml index c8acef98ea..60ca7ba0db 100644 --- a/Content.Client/Disposal/UI/DisposalUnitWindow.xaml +++ b/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml @@ -1,20 +1,21 @@ - - + - + [UsedImplicitly] - public sealed class DeviceNetworkSystem : EntitySystem + public sealed class DeviceNetworkSystem : SharedDeviceNetworkSystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!; @@ -60,16 +60,7 @@ namespace Content.Server.DeviceNetwork.Systems SwapQueues(); } - /// - /// Sends the given payload as a device network packet to the entity with the given address and frequency. - /// Addresses are given to the DeviceNetworkComponent of an entity when connecting. - /// - /// The EntityUid of the sending entity - /// The address of the entity that the packet gets sent to. If null, the message is broadcast to all devices on that frequency (except the sender) - /// The frequency to send on - /// The data to be sent - /// Returns true when the packet was successfully enqueued. - public bool QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, int? network = null, DeviceNetworkComponent? device = null) + public override bool QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, int? network = null, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) return false; @@ -368,96 +359,4 @@ namespace Content.Server.DeviceNetwork.Systems } } } - - /// - /// Event raised before a device network packet is send. - /// Subscribed to by other systems to prevent the packet from being sent. - /// - public sealed class BeforePacketSentEvent : CancellableEntityEventArgs - { - /// - /// The EntityUid of the entity the packet was sent from. - /// - public readonly EntityUid Sender; - - public readonly TransformComponent SenderTransform; - - /// - /// The senders current position in world coordinates. - /// - public readonly Vector2 SenderPosition; - - /// - /// The network the packet will be sent to. - /// - public readonly string NetworkId; - - public BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition, string networkId) - { - Sender = sender; - SenderTransform = xform; - SenderPosition = senderPosition; - NetworkId = networkId; - } - } - - /// - /// Sent to the sending entity before broadcasting network packets to recipients - /// - public sealed class BeforeBroadcastAttemptEvent : CancellableEntityEventArgs - { - public readonly IReadOnlySet Recipients; - public HashSet? ModifiedRecipients; - - public BeforeBroadcastAttemptEvent(IReadOnlySet recipients) - { - Recipients = recipients; - } - } - - /// - /// Event raised when a device network packet gets sent. - /// - public sealed class DeviceNetworkPacketEvent : EntityEventArgs - { - /// - /// The id of the network that this packet is being sent on. - /// - public int NetId; - - /// - /// The frequency the packet is sent on. - /// - public readonly uint Frequency; - - /// - /// Address of the intended recipient. Null if the message was broadcast. - /// - public string? Address; - - /// - /// The device network address of the sending entity. - /// - public readonly string SenderAddress; - - /// - /// The entity that sent the packet. - /// - public EntityUid Sender; - - /// - /// The data that is being sent. - /// - public readonly NetworkPayload Data; - - public DeviceNetworkPacketEvent(int netId, string? address, uint frequency, string senderAddress, EntityUid sender, NetworkPayload data) - { - NetId = netId; - Address = address; - Frequency = frequency; - SenderAddress = senderAddress; - Sender = sender; - Data = data; - } - } } diff --git a/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs b/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs index 9a4a81a4c0..588020a963 100644 --- a/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs @@ -1,7 +1,8 @@ -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Components.Devices; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Interaction; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.DeviceNetwork.Systems.Devices { diff --git a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs index 645d28f6d2..6bcbe30456 100644 --- a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs @@ -1,7 +1,6 @@ using System.Linq; using Content.Server.Administration.Logs; using Content.Server.DeviceLinking.Systems; -using Content.Server.DeviceNetwork.Components; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Content.Shared.Database; diff --git a/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs b/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs index 6c997828fa..8c1f48e93f 100644 --- a/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs @@ -1,9 +1,9 @@ using System.Diagnostics.CodeAnalysis; using Content.Server.DeviceNetwork.Components; using Content.Server.Medical.CrewMonitoring; -using Content.Server.Power.Components; using Content.Server.Station.Systems; using Content.Shared.Power; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.DeviceNetwork.Systems; diff --git a/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs index 675cacc4d7..cebe1a3b9d 100644 --- a/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs @@ -1,5 +1,6 @@ using Content.Server.DeviceNetwork.Components; using Content.Server.Station.Systems; +using Content.Shared.DeviceNetwork.Events; using JetBrains.Annotations; using Robust.Shared.Map; diff --git a/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs index 758a333c7a..54bd5256c7 100644 --- a/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs @@ -1,4 +1,5 @@ using Content.Server.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using JetBrains.Annotations; namespace Content.Server.DeviceNetwork.Systems diff --git a/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs index 1741afd4ce..8bca47e041 100644 --- a/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs @@ -1,4 +1,5 @@ using Content.Server.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using JetBrains.Annotations; namespace Content.Server.DeviceNetwork.Systems diff --git a/Content.Server/Disposal/Mailing/MailingUnitSystem.cs b/Content.Server/Disposal/Mailing/MailingUnitSystem.cs index 6249b9497d..6ee282a310 100644 --- a/Content.Server/Disposal/Mailing/MailingUnitSystem.cs +++ b/Content.Server/Disposal/Mailing/MailingUnitSystem.cs @@ -1,204 +1,8 @@ -using Content.Server.Configurable; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; -using Content.Server.DeviceNetwork.Systems; -using Content.Server.Disposal.Unit.EntitySystems; -using Content.Server.Power.Components; -using Content.Shared.DeviceNetwork; -using Content.Shared.Disposal; -using Content.Shared.Interaction; -using Robust.Server.GameObjects; -using Robust.Shared.Player; -using Robust.Shared.Utility; +using Content.Shared.Disposal.Mailing; namespace Content.Server.Disposal.Mailing; -public sealed class MailingUnitSystem : EntitySystem +public sealed class MailingUnitSystem : SharedMailingUnitSystem { - [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; - [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; - private const string MailTag = "mail"; - - private const string TagConfigurationKey = "tag"; - - private const string NetTag = "tag"; - private const string NetSrc = "src"; - private const string NetTarget = "target"; - private const string NetCmdSent = "mail_sent"; - private const string NetCmdRequest = "get_mailer_tag"; - private const string NetCmdResponse = "mailer_tag"; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnPacketReceived); - SubscribeLocalEvent(OnBeforeFlush); - SubscribeLocalEvent(OnConfigurationUpdated); - SubscribeLocalEvent(HandleActivate, before: new[] { typeof(DisposalUnitSystem) }); - SubscribeLocalEvent(OnDisposalUnitUIStateChange); - SubscribeLocalEvent(OnTargetSelected); - } - - - private void OnComponentInit(EntityUid uid, MailingUnitComponent component, ComponentInit args) - { - UpdateTargetList(uid, component); - } - - private void OnPacketReceived(EntityUid uid, MailingUnitComponent component, DeviceNetworkPacketEvent args) - { - if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || !IsPowered(uid)) - return; - - switch (command) - { - case NetCmdRequest: - SendTagRequestResponse(uid, args, component.Tag); - break; - case NetCmdResponse when args.Data.TryGetValue(NetTag, out string? tag): - //Add the received tag request response to the list of targets - component.TargetList.Add(tag); - UpdateUserInterface(uid, component); - break; - } - } - - /// - /// Sends the given tag as a response to a if it's not null - /// - private void SendTagRequestResponse(EntityUid uid, DeviceNetworkPacketEvent args, string? tag) - { - if (tag == null) - return; - - var payload = new NetworkPayload - { - [DeviceNetworkConstants.Command] = NetCmdResponse, - [NetTag] = tag - }; - - _deviceNetworkSystem.QueuePacket(uid, args.Address, payload, args.Frequency); - } - - /// - /// Prevents the unit from flushing if no target is selected - /// - private void OnBeforeFlush(EntityUid uid, MailingUnitComponent component, BeforeDisposalFlushEvent args) - { - if (string.IsNullOrEmpty(component.Target)) - { - args.Cancel(); - return; - } - - args.Tags.Add(MailTag); - args.Tags.Add(component.Target); - - BroadcastSentMessage(uid, component); - } - - /// - /// Broadcast that a mail was sent including the src and target tags - /// - private void BroadcastSentMessage(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null) - { - if (string.IsNullOrEmpty(component.Tag) || string.IsNullOrEmpty(component.Target) || !Resolve(uid, ref device)) - return; - - var payload = new NetworkPayload - { - [DeviceNetworkConstants.Command] = NetCmdSent, - [NetSrc] = component.Tag, - [NetTarget] = component.Target - }; - - _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device); - } - - /// - /// Clears the units target list and broadcasts a . - /// The target list will then get populated with responses from all active mailing units on the same grid - /// - private void UpdateTargetList(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null) - { - if (!Resolve(uid, ref device, false)) - return; - - var payload = new NetworkPayload - { - [DeviceNetworkConstants.Command] = NetCmdRequest - }; - - component.TargetList.Clear(); - _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device); - } - - /// - /// Gets called when the units tag got updated - /// - private void OnConfigurationUpdated(EntityUid uid, MailingUnitComponent component, ConfigurationSystem.ConfigurationUpdatedEvent args) - { - var configuration = args.Configuration.Config; - if (!configuration.ContainsKey(TagConfigurationKey) || configuration[TagConfigurationKey] == string.Empty) - { - component.Tag = null; - return; - } - - component.Tag = configuration[TagConfigurationKey]; - UpdateUserInterface(uid, component); - } - - private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args) - { - if (args.Handled || !args.Complex) - return; - - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) - { - return; - } - - args.Handled = true; - UpdateTargetList(uid, component); - _userInterfaceSystem.OpenUi(uid, MailingUnitUiKey.Key, actor.PlayerSession); - } - - /// - /// Gets called when the disposal unit components ui state changes. This is required because the mailing unit requires a disposal unit component and overrides its ui - /// - private void OnDisposalUnitUIStateChange(EntityUid uid, MailingUnitComponent component, DisposalUnitUIStateUpdatedEvent args) - { - component.DisposalUnitInterfaceState = args.State; - UpdateUserInterface(uid, component); - } - - private void UpdateUserInterface(EntityUid uid, MailingUnitComponent component) - { - if (component.DisposalUnitInterfaceState == null) - return; - - var state = new MailingUnitBoundUserInterfaceState(component.DisposalUnitInterfaceState, component.Target, component.TargetList.ShallowClone(), component.Tag); - _userInterfaceSystem.SetUiState(uid, MailingUnitUiKey.Key, state); - } - - private void OnTargetSelected(EntityUid uid, MailingUnitComponent component, TargetSelectedMessage args) - { - component.Target = args.Target; - UpdateUserInterface(uid, component); - } - - /// - /// Checks if the unit is powered if an is present - /// - /// True if the power receiver component is powered or not present - private bool IsPowered(EntityUid uid, ApcPowerReceiverComponent? powerReceiver = null) - { - if (Resolve(uid, ref powerReceiver) && !powerReceiver.Powered) - return false; - - return true; - } } diff --git a/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs deleted file mode 100644 index e4b8955c9c..0000000000 --- a/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Content.Server.Disposal.Unit.EntitySystems; - -namespace Content.Server.Disposal.Tube.Components -{ - [RegisterComponent] - [Access(typeof(DisposalTubeSystem), typeof(DisposalUnitSystem))] - public sealed partial class DisposalEntryComponent : Component - { - public const string HolderPrototypeId = "DisposalHolder"; - } -} diff --git a/Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs b/Content.Server/Disposal/Tube/DisposalBendComponent.cs similarity index 70% rename from Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs rename to Content.Server/Disposal/Tube/DisposalBendComponent.cs index ace8a68462..7aa48f4bd3 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalBendComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Disposal.Tube.Components; +namespace Content.Server.Disposal.Tube; [RegisterComponent] [Access(typeof(DisposalTubeSystem))] diff --git a/Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs b/Content.Server/Disposal/Tube/DisposalJunctionComponent.cs similarity index 84% rename from Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs rename to Content.Server/Disposal/Tube/DisposalJunctionComponent.cs index d929dd16bf..af0d7eda68 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalJunctionComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Disposal.Tube.Components; +namespace Content.Server.Disposal.Tube; [RegisterComponent] [Access(typeof(DisposalTubeSystem))] diff --git a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs b/Content.Server/Disposal/Tube/DisposalRouterComponent.cs similarity index 57% rename from Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs rename to Content.Server/Disposal/Tube/DisposalRouterComponent.cs index 18c0945cef..2446cf27f7 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalRouterComponent.cs @@ -1,12 +1,6 @@ -using Content.Server.UserInterface; -using Robust.Server.GameObjects; using Robust.Shared.Audio; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Player; -using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; -namespace Content.Server.Disposal.Tube.Components +namespace Content.Server.Disposal.Tube { [RegisterComponent] [Access(typeof(DisposalTubeSystem))] diff --git a/Content.Server/Disposal/Tube/Components/DisposalSignalRouterComponent.cs b/Content.Server/Disposal/Tube/DisposalSignalRouterComponent.cs similarity index 91% rename from Content.Server/Disposal/Tube/Components/DisposalSignalRouterComponent.cs rename to Content.Server/Disposal/Tube/DisposalSignalRouterComponent.cs index b4ef81d898..bee8293f58 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalSignalRouterComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalSignalRouterComponent.cs @@ -1,8 +1,7 @@ -using Content.Server.Disposal.Tube.Systems; using Content.Shared.DeviceLinking; using Robust.Shared.Prototypes; -namespace Content.Server.Disposal.Tube.Components; +namespace Content.Server.Disposal.Tube; /// /// Requires to function. diff --git a/Content.Server/Disposal/Tube/Systems/DisposalSignalRouterSystem.cs b/Content.Server/Disposal/Tube/DisposalSignalRouterSystem.cs similarity index 94% rename from Content.Server/Disposal/Tube/Systems/DisposalSignalRouterSystem.cs rename to Content.Server/Disposal/Tube/DisposalSignalRouterSystem.cs index f1fdedb522..0d6c03b8c5 100644 --- a/Content.Server/Disposal/Tube/Systems/DisposalSignalRouterSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalSignalRouterSystem.cs @@ -1,8 +1,7 @@ using Content.Server.DeviceLinking.Systems; -using Content.Server.Disposal.Tube.Components; using Content.Shared.DeviceLinking.Events; -namespace Content.Server.Disposal.Tube.Systems; +namespace Content.Server.Disposal.Tube; /// /// Handles signals and the routing get next direction event. diff --git a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs b/Content.Server/Disposal/Tube/DisposalTaggerComponent.cs similarity index 53% rename from Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs rename to Content.Server/Disposal/Tube/DisposalTaggerComponent.cs index a291f4a941..3f072a80c7 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalTaggerComponent.cs @@ -1,13 +1,6 @@ -using Content.Server.Disposal.Unit.Components; -using Content.Server.UserInterface; -using Robust.Server.GameObjects; using Robust.Shared.Audio; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Player; -using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; -namespace Content.Server.Disposal.Tube.Components +namespace Content.Server.Disposal.Tube { [RegisterComponent] public sealed partial class DisposalTaggerComponent : DisposalTransitComponent diff --git a/Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs b/Content.Server/Disposal/Tube/DisposalTransitComponent.cs similarity index 82% rename from Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs rename to Content.Server/Disposal/Tube/DisposalTransitComponent.cs index e916327516..10bc7d5df7 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalTransitComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Disposal.Tube.Components +namespace Content.Server.Disposal.Tube { // TODO: Different types of tubes eject in random direction with no exit point [RegisterComponent] diff --git a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs b/Content.Server/Disposal/Tube/DisposalTubeComponent.cs similarity index 90% rename from Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs rename to Content.Server/Disposal/Tube/DisposalTubeComponent.cs index c16f1fcc22..15d02ad1ef 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeComponent.cs @@ -1,9 +1,9 @@ -using Content.Server.Disposal.Unit.EntitySystems; +using Content.Server.Disposal.Unit; using Content.Shared.Damage; using Robust.Shared.Audio; using Robust.Shared.Containers; -namespace Content.Server.Disposal.Tube.Components; +namespace Content.Server.Disposal.Tube; [RegisterComponent] [Access(typeof(DisposalTubeSystem), typeof(DisposableSystem))] diff --git a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs index 20626a5eee..f1e094db20 100644 --- a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs @@ -2,12 +2,12 @@ using System.Linq; using System.Text; using Content.Server.Atmos.EntitySystems; using Content.Server.Construction.Completions; -using Content.Server.Disposal.Tube.Components; -using Content.Server.Disposal.Unit.Components; -using Content.Server.Disposal.Unit.EntitySystems; +using Content.Server.Disposal.Unit; using Content.Server.Popups; using Content.Shared.Destructible; using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Tube; +using Content.Shared.Disposal.Unit; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -16,12 +16,10 @@ using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Random; -using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; -using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; namespace Content.Server.Disposal.Tube { - public sealed class DisposalTubeSystem : EntitySystem + public sealed class DisposalTubeSystem : SharedDisposalTubeSystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; @@ -49,8 +47,8 @@ namespace Content.Server.Disposal.Tube SubscribeLocalEvent(OnGetBendConnectableDirections); SubscribeLocalEvent(OnGetBendNextDirection); - SubscribeLocalEvent(OnGetEntryConnectableDirections); - SubscribeLocalEvent(OnGetEntryNextDirection); + SubscribeLocalEvent(OnGetEntryConnectableDirections); + SubscribeLocalEvent(OnGetEntryNextDirection); SubscribeLocalEvent(OnGetJunctionConnectableDirections); SubscribeLocalEvent(OnGetJunctionNextDirection); @@ -64,13 +62,13 @@ namespace Content.Server.Disposal.Tube SubscribeLocalEvent(OnGetTaggerConnectableDirections); SubscribeLocalEvent(OnGetTaggerNextDirection); - Subs.BuiEvents(DisposalRouterUiKey.Key, subs => + Subs.BuiEvents(SharedDisposalRouterComponent.DisposalRouterUiKey.Key, subs => { subs.Event(OnOpenRouterUI); subs.Event(OnUiAction); }); - Subs.BuiEvents(DisposalTaggerUiKey.Key, subs => + Subs.BuiEvents(SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key, subs => { subs.Event(OnOpenTaggerUI); subs.Event(OnUiAction); @@ -161,12 +159,12 @@ namespace Content.Server.Disposal.Tube args.Next = previousDF == ev.Connectable[0] ? ev.Connectable[1] : ev.Connectable[0]; } - private void OnGetEntryConnectableDirections(EntityUid uid, DisposalEntryComponent component, ref GetDisposalsConnectableDirectionsEvent args) + private void OnGetEntryConnectableDirections(EntityUid uid, Shared.Disposal.Tube.DisposalEntryComponent component, ref GetDisposalsConnectableDirectionsEvent args) { args.Connectable = new[] { Transform(uid).LocalRotation.GetDir() }; } - private void OnGetEntryNextDirection(EntityUid uid, DisposalEntryComponent component, ref GetDisposalsNextDirectionEvent args) + private void OnGetEntryNextDirection(EntityUid uid, Shared.Disposal.Tube.DisposalEntryComponent component, ref GetDisposalsNextDirectionEvent args) { // Ejects contents when they come from the same direction the entry is facing. if (args.Holder.PreviousDirectionFrom != Direction.Invalid) @@ -283,10 +281,10 @@ namespace Content.Server.Disposal.Tube private void OnOpenTaggerUI(EntityUid uid, DisposalTaggerComponent tagger, BoundUIOpenedEvent args) { - if (_uiSystem.HasUi(uid, DisposalTaggerUiKey.Key)) + if (_uiSystem.HasUi(uid, SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key)) { - _uiSystem.SetUiState(uid, DisposalTaggerUiKey.Key, - new DisposalTaggerUserInterfaceState(tagger.Tag)); + _uiSystem.SetUiState(uid, SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key, + new SharedDisposalTaggerComponent.DisposalTaggerUserInterfaceState(tagger.Tag)); } } @@ -298,7 +296,7 @@ namespace Content.Server.Disposal.Tube { if (router.Tags.Count <= 0) { - _uiSystem.SetUiState(uid, DisposalRouterUiKey.Key, new DisposalRouterUserInterfaceState("")); + _uiSystem.SetUiState(uid, SharedDisposalRouterComponent.DisposalRouterUiKey.Key, new SharedDisposalRouterComponent.DisposalRouterUserInterfaceState("")); return; } @@ -312,7 +310,7 @@ namespace Content.Server.Disposal.Tube taglist.Remove(taglist.Length - 2, 2); - _uiSystem.SetUiState(uid, DisposalRouterUiKey.Key, new DisposalRouterUserInterfaceState(taglist.ToString())); + _uiSystem.SetUiState(uid, SharedDisposalRouterComponent.DisposalRouterUiKey.Key, new SharedDisposalRouterComponent.DisposalRouterUserInterfaceState(taglist.ToString())); } private void OnAnchorChange(EntityUid uid, DisposalTubeComponent component, ref AnchorStateChangedEvent args) @@ -419,13 +417,13 @@ namespace Content.Server.Disposal.Tube _popups.PopupEntity(Loc.GetString("disposal-tube-component-popup-directions-text", ("directions", directions)), tubeId, recipient); } - public bool TryInsert(EntityUid uid, DisposalUnitComponent from, IEnumerable? tags = default, DisposalEntryComponent? entry = null) + public override bool TryInsert(EntityUid uid, DisposalUnitComponent from, IEnumerable? tags = default, DisposalEntryComponent? entry = null) { if (!Resolve(uid, ref entry)) return false; var xform = Transform(uid); - var holder = Spawn(DisposalEntryComponent.HolderPrototypeId, _transform.GetMapCoordinates(uid, xform: xform)); + var holder = Spawn(entry.HolderPrototypeId, _transform.GetMapCoordinates(uid, xform: xform)); var holderComponent = Comp(holder); foreach (var entity in from.Container.ContainedEntities.ToArray()) @@ -436,7 +434,7 @@ namespace Content.Server.Disposal.Tube _atmosSystem.Merge(holderComponent.Air, from.Air); from.Air.Clear(); - if (tags != default) + if (tags != null) holderComponent.Tags.UnionWith(tags); return _disposableSystem.EnterTube(holder, uid, holderComponent); diff --git a/Content.Server/Disposal/Tube/GetDisposalsNextDirectionEvent.cs b/Content.Server/Disposal/Tube/GetDisposalsNextDirectionEvent.cs index 2872a0e6d0..30dd1c3769 100644 --- a/Content.Server/Disposal/Tube/GetDisposalsNextDirectionEvent.cs +++ b/Content.Server/Disposal/Tube/GetDisposalsNextDirectionEvent.cs @@ -1,4 +1,4 @@ -using Content.Server.Disposal.Unit.Components; +using Content.Server.Disposal.Unit; namespace Content.Server.Disposal.Tube; diff --git a/Content.Server/Disposal/TubeConnectionsCommand.cs b/Content.Server/Disposal/TubeConnectionsCommand.cs index 564c46be7a..6719a9a2ab 100644 --- a/Content.Server/Disposal/TubeConnectionsCommand.cs +++ b/Content.Server/Disposal/TubeConnectionsCommand.cs @@ -1,6 +1,5 @@ using Content.Server.Administration; using Content.Server.Disposal.Tube; -using Content.Server.Disposal.Tube.Components; using Content.Shared.Administration; using Robust.Shared.Console; diff --git a/Content.Server/Disposal/Unit/Components/BeingDisposedComponent.cs b/Content.Server/Disposal/Unit/BeingDisposedComponent.cs similarity index 81% rename from Content.Server/Disposal/Unit/Components/BeingDisposedComponent.cs rename to Content.Server/Disposal/Unit/BeingDisposedComponent.cs index 060c7c98bd..3cff669855 100644 --- a/Content.Server/Disposal/Unit/Components/BeingDisposedComponent.cs +++ b/Content.Server/Disposal/Unit/BeingDisposedComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Disposal.Unit.Components; +namespace Content.Server.Disposal.Unit; /// /// A component added to entities that are currently in disposals. diff --git a/Content.Server/Disposal/Unit/EntitySystems/BeingDisposedSystem.cs b/Content.Server/Disposal/Unit/BeingDisposedSystem.cs similarity index 92% rename from Content.Server/Disposal/Unit/EntitySystems/BeingDisposedSystem.cs rename to Content.Server/Disposal/Unit/BeingDisposedSystem.cs index 6fbfb1523a..fcff4ba3b5 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/BeingDisposedSystem.cs +++ b/Content.Server/Disposal/Unit/BeingDisposedSystem.cs @@ -1,8 +1,7 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Systems; -using Content.Server.Disposal.Unit.Components; -namespace Content.Server.Disposal.Unit.EntitySystems; +namespace Content.Server.Disposal.Unit; public sealed class BeingDisposedSystem : EntitySystem { diff --git a/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs b/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs deleted file mode 100644 index 548af039da..0000000000 --- a/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Content.Server.Atmos; -using Content.Shared.Atmos; -using Content.Shared.Disposal.Components; - -namespace Content.Server.Disposal.Unit.Components; - -// GasMixture life. -[RegisterComponent] -public sealed partial class DisposalUnitComponent : SharedDisposalUnitComponent -{ - [DataField("air")] - public GasMixture Air = new(Atmospherics.CellVolume); -} diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs b/Content.Server/Disposal/Unit/DisposableSystem.cs similarity index 98% rename from Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs rename to Content.Server/Disposal/Unit/DisposableSystem.cs index 0e624ca6f5..a94176246c 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs +++ b/Content.Server/Disposal/Unit/DisposableSystem.cs @@ -1,9 +1,8 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Disposal.Tube; -using Content.Server.Disposal.Tube.Components; -using Content.Server.Disposal.Unit.Components; using Content.Shared.Body.Components; using Content.Shared.Damage; +using Content.Shared.Disposal.Components; using Content.Shared.Item; using Content.Shared.Throwing; using Robust.Shared.Audio.Systems; @@ -12,7 +11,7 @@ using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; -namespace Content.Server.Disposal.Unit.EntitySystems +namespace Content.Server.Disposal.Unit { public sealed class DisposableSystem : EntitySystem { diff --git a/Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs b/Content.Server/Disposal/Unit/DisposalHolderComponent.cs similarity index 94% rename from Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs rename to Content.Server/Disposal/Unit/DisposalHolderComponent.cs index 690b33968b..be90ae9f9c 100644 --- a/Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs +++ b/Content.Server/Disposal/Unit/DisposalHolderComponent.cs @@ -1,9 +1,8 @@ using Content.Server.Atmos; -using Content.Server.Disposal.Tube.Components; using Content.Shared.Atmos; using Robust.Shared.Containers; -namespace Content.Server.Disposal.Unit.Components +namespace Content.Server.Disposal.Unit { [RegisterComponent] public sealed partial class DisposalHolderComponent : Component, IGasMixtureHolder diff --git a/Content.Server/Disposal/Unit/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/DisposalUnitSystem.cs new file mode 100644 index 0000000000..4c6436c6c9 --- /dev/null +++ b/Content.Server/Disposal/Unit/DisposalUnitSystem.cs @@ -0,0 +1,44 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Shared.Atmos; +using Content.Shared.Destructible; +using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Unit; +using Content.Shared.Explosion; + +namespace Content.Server.Disposal.Unit; + +public sealed class DisposalUnitSystem : SharedDisposalUnitSystem +{ + [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDestruction); + SubscribeLocalEvent(OnExploded); + } + + protected override void HandleAir(EntityUid uid, DisposalUnitComponent component, TransformComponent xform) + { + var air = component.Air; + var indices = TransformSystem.GetGridTilePositionOrDefault((uid, xform)); + + if (_atmosSystem.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is { Temperature: > 0f } environment) + { + var transferMoles = 0.1f * (0.25f * Atmospherics.OneAtmosphere * 1.01f - air.Pressure) * air.Volume / (environment.Temperature * Atmospherics.R); + + component.Air = environment.Remove(transferMoles); + } + } + + private void OnDestruction(EntityUid uid, DisposalUnitComponent component, DestructionEventArgs args) + { + TryEjectContents(uid, component); + } + + private void OnExploded(Entity ent, ref BeforeExplodeEvent args) + { + args.Contents.AddRange(ent.Comp.Container.ContainedEntities); + } +} diff --git a/Content.Server/Disposal/Unit/EntitySystems/DoInsertDisposalUnitEvent.cs b/Content.Server/Disposal/Unit/DoInsertDisposalUnitEvent.cs similarity index 65% rename from Content.Server/Disposal/Unit/EntitySystems/DoInsertDisposalUnitEvent.cs rename to Content.Server/Disposal/Unit/DoInsertDisposalUnitEvent.cs index 6fdfce61da..3f3fee9785 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DoInsertDisposalUnitEvent.cs +++ b/Content.Server/Disposal/Unit/DoInsertDisposalUnitEvent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Disposal.Unit.EntitySystems +namespace Content.Server.Disposal.Unit { public record DoInsertDisposalUnitEvent(EntityUid? User, EntityUid ToInsert, EntityUid Unit); } diff --git a/Content.Server/Fax/AdminUI/AdminFaxEui.cs b/Content.Server/Fax/AdminUI/AdminFaxEui.cs index fe6b03fab7..2921bd5ef6 100644 --- a/Content.Server/Fax/AdminUI/AdminFaxEui.cs +++ b/Content.Server/Fax/AdminUI/AdminFaxEui.cs @@ -7,6 +7,7 @@ using Content.Shared.Fax; using Content.Shared.Follower; using Content.Shared.Ghost; using Content.Shared.Paper; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.Fax.AdminUI; diff --git a/Content.Server/Fax/FaxSystem.cs b/Content.Server/Fax/FaxSystem.cs index 1ac7bd23ca..79710e3f97 100644 --- a/Content.Server/Fax/FaxSystem.cs +++ b/Content.Server/Fax/FaxSystem.cs @@ -1,8 +1,6 @@ using Content.Server.Administration; using Content.Server.Administration.Managers; using Content.Server.Chat.Managers; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Popups; using Content.Server.Power.Components; @@ -12,7 +10,7 @@ using Content.Shared.Administration.Logs; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.DeviceNetwork; -using Content.Shared.Emag.Components; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Emag.Systems; using Content.Shared.Fax; using Content.Shared.Fax.Systems; @@ -27,9 +25,9 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Player; -using Robust.Shared.Prototypes; using Content.Shared.NameModifier.Components; using Content.Shared.Power; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.Fax; diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index 53c60296d5..b98ba15623 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -19,6 +19,8 @@ using Robust.Shared.Timing; using Robust.Shared.Audio.Systems; using Content.Shared.Damage.Systems; using Content.Shared.Damage.Components; +using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Power; namespace Content.Server.Light.EntitySystems diff --git a/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs b/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs index a53df6dbae..0e1d27a3c5 100644 --- a/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs +++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs @@ -2,6 +2,8 @@ using System.Linq; using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Systems; using Content.Server.PowerCell; +using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Medical.CrewMonitoring; using Content.Shared.Medical.SuitSensor; using Content.Shared.Pinpointer; diff --git a/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs index d7b8cc67a5..ad50a8f957 100644 --- a/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs +++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs @@ -1,10 +1,10 @@ -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Medical.SuitSensors; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Medical.SuitSensor; using Robust.Shared.Timing; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.Medical.CrewMonitoring; diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs index fa4344cc78..2ab09e746f 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs @@ -1,7 +1,5 @@ using System.Numerics; using Content.Server.Access.Systems; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Emp; using Content.Server.Medical.CrewMonitoring; @@ -26,6 +24,7 @@ using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.Medical.SuitSensors; diff --git a/Content.Server/PDA/PdaSystem.cs b/Content.Server/PDA/PdaSystem.cs index a9527020b0..bdf688efe7 100644 --- a/Content.Server/PDA/PdaSystem.cs +++ b/Content.Server/PDA/PdaSystem.cs @@ -2,27 +2,23 @@ using Content.Server.Access.Systems; using Content.Server.AlertLevel; using Content.Server.CartridgeLoader; using Content.Server.Chat.Managers; -using Content.Server.DeviceNetwork.Components; using Content.Server.Instruments; -using Content.Server.Light.EntitySystems; using Content.Server.PDA.Ringer; using Content.Server.Station.Systems; -using Content.Server.Store.Components; using Content.Server.Store.Systems; using Content.Server.Traitor.Uplink; using Content.Shared.Access.Components; using Content.Shared.CartridgeLoader; using Content.Shared.Chat; using Content.Shared.Light; -using Content.Shared.Light.Components; using Content.Shared.Light.EntitySystems; using Content.Shared.PDA; -using Content.Shared.Store.Components; using Robust.Server.Containers; using Robust.Server.GameObjects; using Robust.Shared.Containers; using Robust.Shared.Player; using Robust.Shared.Utility; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.PDA { diff --git a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs index 0239273455..f3405486e6 100644 --- a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs @@ -15,7 +15,6 @@ namespace Content.Server.Power.EntitySystems public sealed class PowerReceiverSystem : SharedPowerReceiverSystem { [Dependency] private readonly IAdminManager _adminManager = default!; - private EntityQuery _recQuery; private EntityQuery _provQuery; diff --git a/Content.Server/Power/Generation/Teg/TegSystem.cs b/Content.Server/Power/Generation/Teg/TegSystem.cs index 437d805dcd..04f876c2c2 100644 --- a/Content.Server/Power/Generation/Teg/TegSystem.cs +++ b/Content.Server/Power/Generation/Teg/TegSystem.cs @@ -9,6 +9,7 @@ using Content.Server.NodeContainer.Nodes; using Content.Server.Power.Components; using Content.Shared.Atmos; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Examine; using Content.Shared.Power; using Content.Shared.Power.EntitySystems; diff --git a/Content.Server/Radio/EntitySystems/JammerSystem.cs b/Content.Server/Radio/EntitySystems/JammerSystem.cs index 1fe48d22b4..1cea981d3c 100644 --- a/Content.Server/Radio/EntitySystems/JammerSystem.cs +++ b/Content.Server/Radio/EntitySystems/JammerSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.DeviceNetwork.Components; using Content.Server.Power.EntitySystems; using Content.Server.PowerCell; using Content.Shared.DeviceNetwork.Components; diff --git a/Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs b/Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs index 916694fdd8..c4554d65d6 100644 --- a/Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs +++ b/Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Administration.Logs; -using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Systems; using Content.Server.Radio.EntitySystems; using Content.Shared.Lock; @@ -10,7 +9,7 @@ using Content.Shared.Robotics.Components; using Content.Shared.Robotics.Systems; using Robust.Server.GameObjects; using Robust.Shared.Timing; -using System.Diagnostics.CodeAnalysis; +using Content.Shared.DeviceNetwork.Events; namespace Content.Server.Research.Systems; diff --git a/Content.Server/RoundEnd/RoundEndSystem.cs b/Content.Server/RoundEnd/RoundEndSystem.cs index bb5934f3f0..900a52057e 100644 --- a/Content.Server/RoundEnd/RoundEndSystem.cs +++ b/Content.Server/RoundEnd/RoundEndSystem.cs @@ -4,8 +4,6 @@ using Content.Server.AlertLevel; using Content.Shared.CCVar; using Content.Server.Chat.Managers; using Content.Server.Chat.Systems; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.GameTicking; using Content.Server.Screens.Components; @@ -21,6 +19,7 @@ using Robust.Shared.Configuration; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Timing; +using Content.Shared.DeviceNetwork.Components; using Timer = Robust.Shared.Timing.Timer; namespace Content.Server.RoundEnd diff --git a/Content.Server/Screens/Systems/ScreenSystem.cs b/Content.Server/Screens/Systems/ScreenSystem.cs index 782fe38c88..c159bfe1d5 100644 --- a/Content.Server/Screens/Systems/ScreenSystem.cs +++ b/Content.Server/Screens/Systems/ScreenSystem.cs @@ -2,6 +2,8 @@ using Content.Shared.TextScreen; using Content.Server.Screens.Components; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; +using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using Robust.Shared.Timing; @@ -63,7 +65,7 @@ public sealed class ScreenSystem : EntitySystem /// /// Determines if/how a timer packet affects this screen. /// Currently there are 2 broadcast domains: Arrivals, and every other screen. - /// Domain is determined by the on each timer. + /// Domain is determined by the on each timer. /// Each broadcast domain is divided into subnets. Screen MapUid determines subnet. /// Subnets are the shuttle, source, and dest. Source/dest change each jump. /// This is required to send different timers to the shuttle/terminal/station. diff --git a/Content.Server/SensorMonitoring/BatterySensorSystem.cs b/Content.Server/SensorMonitoring/BatterySensorSystem.cs index 501b094c89..5047cd1d29 100644 --- a/Content.Server/SensorMonitoring/BatterySensorSystem.cs +++ b/Content.Server/SensorMonitoring/BatterySensorSystem.cs @@ -2,6 +2,7 @@ using Content.Server.DeviceNetwork.Systems; using Content.Server.Power.Components; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; namespace Content.Server.SensorMonitoring; diff --git a/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.UI.cs b/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.UI.cs index dec3e6c36e..a562abd926 100644 --- a/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.UI.cs +++ b/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.UI.cs @@ -1,7 +1,7 @@ -using Content.Server.DeviceNetwork.Components; -using Content.Shared.SensorMonitoring; +using Content.Shared.SensorMonitoring; using Robust.Shared.Collections; using ConsoleUIState = Content.Shared.SensorMonitoring.SensorMonitoringConsoleBoundInterfaceState; +using Content.Shared.DeviceNetwork.Components; using IncrementalUIState = Content.Shared.SensorMonitoring.SensorMonitoringIncrementalUpdate; namespace Content.Server.SensorMonitoring; diff --git a/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.cs b/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.cs index ddd7812394..ebe8f304ba 100644 --- a/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.cs +++ b/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.cs @@ -1,10 +1,7 @@ using Content.Server.Atmos.Monitor.Components; using Content.Server.Atmos.Monitor.Systems; -using Content.Server.Atmos.Piping.Binary.Components; using Content.Server.Atmos.Piping.Components; using Content.Server.Atmos.Piping.Unary.Components; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Power.Generation.Teg; using Content.Shared.Atmos.Monitor; @@ -12,6 +9,7 @@ using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.DeviceNetwork.Systems; using Content.Shared.SensorMonitoring; using Robust.Server.GameObjects; diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 896570fb34..aa1c2e6dff 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Numerics; using Content.Server.Administration; using Content.Server.Chat.Managers; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.GameTicking; using Content.Server.GameTicking.Events; @@ -19,6 +18,7 @@ using Content.Shared.Administration; using Content.Shared.CCVar; using Content.Shared.Damage.Components; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Components; using Content.Shared.GameTicking; using Content.Shared.Mobs.Components; using Content.Shared.Movement.Components; @@ -26,20 +26,17 @@ using Content.Shared.Parallax.Biomes; using Content.Shared.Salvage; using Content.Shared.Shuttles.Components; using Content.Shared.Tiles; -using Robust.Server.GameObjects; using Robust.Shared.Collections; using Robust.Shared.Configuration; using Robust.Shared.Console; -using Robust.Shared.EntitySerialization; using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; -using Robust.Shared.Map.Components; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Spawners; using Robust.Shared.Timing; using Robust.Shared.Utility; -using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; namespace Content.Server.Shuttles.Systems; diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs index 09aad1d931..95c6ab5a1b 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs @@ -1,5 +1,4 @@ using System.Threading; -using Content.Server.DeviceNetwork.Components; using Content.Server.Screens.Components; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; @@ -14,6 +13,7 @@ using Content.Shared.Shuttles.Systems; using Content.Shared.UserInterface; using Robust.Shared.Map; using Robust.Shared.Player; +using Content.Shared.DeviceNetwork.Components; using Timer = Robust.Shared.Timing.Timer; namespace Content.Server.Shuttles.Systems; diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index afa77421bd..8e3e01bfb6 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -6,7 +6,6 @@ using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; using Content.Server.Chat.Systems; using Content.Server.Communications; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.GameTicking.Events; using Content.Server.Pinpointer; @@ -37,6 +36,7 @@ using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.Utility; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.Shuttles.Systems; diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs b/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs index 4d2a8912e8..e950d3f288 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs @@ -1,13 +1,10 @@ using Content.Shared.DeviceNetwork; -using Content.Shared.Emag.Components; using Content.Shared.Movement.Components; using Content.Shared.Popups; using Content.Shared.Robotics; using Content.Shared.Silicons.Borgs.Components; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; -using Content.Server.DeviceNetwork.Systems; -using Content.Server.Explosion.Components; +using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Emag.Systems; using Robust.Shared.Utility; diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs index 21e71c4316..30bbb2f0e9 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs @@ -3,6 +3,7 @@ using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Systems; using Content.Server.Power.Components; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Power; using Content.Shared.UserInterface; using Content.Shared.SurveillanceCamera; diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs index 315273a0cc..c6886dee33 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs @@ -1,15 +1,13 @@ -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; -using Content.Server.Power.Components; using Content.Shared.ActionBlocker; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Power; using Content.Shared.SurveillanceCamera; using Content.Shared.Verbs; using Robust.Server.GameObjects; -using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.SurveillanceCamera; diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs index f1d1b58bf5..709e383c06 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs @@ -1,16 +1,15 @@ -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Emp; -using Content.Server.Power.Components; using Content.Shared.ActionBlocker; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Power; using Content.Shared.SurveillanceCamera; using Content.Shared.Verbs; using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.SurveillanceCamera; diff --git a/Content.Server/Turrets/DeployableTurretSystem.cs b/Content.Server/Turrets/DeployableTurretSystem.cs index 359d91fd1d..72c011bc90 100644 --- a/Content.Server/Turrets/DeployableTurretSystem.cs +++ b/Content.Server/Turrets/DeployableTurretSystem.cs @@ -8,6 +8,8 @@ using Content.Server.Power.Components; using Content.Server.Repairable; using Content.Shared.Destructible; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Power; using Content.Shared.Turrets; using Content.Shared.Weapons.Ranged.Events; diff --git a/Content.Shared/Climbing/Components/ClimbableComponent.cs b/Content.Shared/Climbing/Components/ClimbableComponent.cs index 1a924e5c30..22a42dea78 100644 --- a/Content.Shared/Climbing/Components/ClimbableComponent.cs +++ b/Content.Shared/Climbing/Components/ClimbableComponent.cs @@ -13,7 +13,13 @@ namespace Content.Shared.Climbing.Components /// /// The range from which this entity can be climbed. /// - [DataField("range")] public float Range = SharedInteractionSystem.InteractionRange / 1.4f; + [DataField] public float Range = SharedInteractionSystem.InteractionRange; + + /// + /// Can drag-drop / verb vaulting be done? Set to false if climbing is being handled manually. + /// + [DataField] + public bool Vaultable = true; /// /// The time it takes to climb onto the entity. diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index f8f2ffda88..d7d9df7fdd 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -149,7 +149,7 @@ public sealed partial class ClimbSystem : VirtualController private void OnCanDragDropOn(EntityUid uid, ClimbableComponent component, ref CanDropTargetEvent args) { - if (args.Handled) + if (args.Handled || !component.Vaultable) return; // If already climbing then don't show outlines. @@ -261,7 +261,7 @@ public sealed partial class ClimbSystem : VirtualController args.Handled = true; } - private void Climb(EntityUid uid, EntityUid user, EntityUid climbable, bool silent = false, ClimbingComponent? climbing = null, + public void Climb(EntityUid uid, EntityUid user, EntityUid climbable, bool silent = false, ClimbingComponent? climbing = null, PhysicsComponent? physics = null, FixturesComponent? fixtures = null, ClimbableComponent? comp = null) { if (!Resolve(uid, ref climbing, ref physics, ref fixtures, false)) @@ -456,6 +456,12 @@ public sealed partial class ClimbSystem : VirtualController /// The reason why it cant be dropped public bool CanVault(ClimbableComponent component, EntityUid user, EntityUid target, out string reason) { + if (!component.Vaultable) + { + reason = string.Empty; + return false; + } + if (!_actionBlockerSystem.CanInteract(user, target)) { reason = Loc.GetString("comp-climbable-cant-interact"); diff --git a/Content.Shared/Configurable/ConfigurationComponent.cs b/Content.Shared/Configurable/ConfigurationComponent.cs index 621871af3c..63c0845083 100644 --- a/Content.Shared/Configurable/ConfigurationComponent.cs +++ b/Content.Shared/Configurable/ConfigurationComponent.cs @@ -2,34 +2,38 @@ using System.Text.RegularExpressions; using Content.Shared.Tools; using Content.Shared.Tools.Systems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Configurable { - [RegisterComponent, NetworkedComponent] + /// + /// Configuration for mailing units. + /// + /// + /// If you want a more detailed description ask the original coder. + /// + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ConfigurationComponent : Component { - [DataField("config")] + /// + /// Tags for mail unit routing. + /// + [DataField, AutoNetworkedField] public Dictionary Config = new(); - [DataField("qualityNeeded", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string QualityNeeded = SharedToolSystem.PulseQuality; + /// + /// Quality to open up the configuration UI. + /// + [DataField] + public ProtoId QualityNeeded = SharedToolSystem.PulseQuality; - [DataField("validation")] + /// + /// Validate tags in . + /// + [DataField] public Regex Validation = new("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled); - [Serializable, NetSerializable] - public sealed class ConfigurationBoundUserInterfaceState : BoundUserInterfaceState - { - public Dictionary Config { get; } - - public ConfigurationBoundUserInterfaceState(Dictionary config) - { - Config = config; - } - } - /// /// Message data sent from client to server when the device configuration is updated. /// diff --git a/Content.Shared/Configurable/SharedConfigurationSystem.cs b/Content.Shared/Configurable/SharedConfigurationSystem.cs new file mode 100644 index 0000000000..704965188e --- /dev/null +++ b/Content.Shared/Configurable/SharedConfigurationSystem.cs @@ -0,0 +1,77 @@ +using Content.Shared.Interaction; +using Content.Shared.Tools.Systems; +using Robust.Shared.Containers; +using static Content.Shared.Configurable.ConfigurationComponent; + +namespace Content.Shared.Configurable; + +/// +/// +/// +public abstract class SharedConfigurationSystem : EntitySystem +{ + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly SharedToolSystem _toolSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUpdate); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnInsert); + } + + private void OnInteractUsing(EntityUid uid, ConfigurationComponent component, InteractUsingEvent args) + { + // TODO use activatable ui system + if (args.Handled) + return; + + if (!_toolSystem.HasQuality(args.Used, component.QualityNeeded)) + return; + + args.Handled = _uiSystem.TryOpenUi(uid, ConfigurationUiKey.Key, args.User); + } + + private void OnUpdate(EntityUid uid, ConfigurationComponent component, ConfigurationUpdatedMessage args) + { + foreach (var key in component.Config.Keys) + { + var value = args.Config.GetValueOrDefault(key); + + if (string.IsNullOrWhiteSpace(value) || component.Validation != null && !component.Validation.IsMatch(value)) + continue; + + component.Config[key] = value; + } + + Dirty(uid, component); + var updatedEvent = new ConfigurationUpdatedEvent(component); + RaiseLocalEvent(uid, updatedEvent); + + // TODO support float (spinbox) and enum (drop-down) configurations + // TODO support verbs. + } + + private void OnInsert(EntityUid uid, ConfigurationComponent component, ContainerIsInsertingAttemptEvent args) + { + if (!_toolSystem.HasQuality(args.EntityUid, component.QualityNeeded)) + return; + + args.Cancel(); + } +} + +/// +/// Sent when configuration values got changes +/// +public sealed class ConfigurationUpdatedEvent : EntityEventArgs +{ + public ConfigurationComponent Configuration; + + public ConfigurationUpdatedEvent(ConfigurationComponent configuration) + { + Configuration = configuration; + } +} diff --git a/Content.Shared/Containers/SharedThrowInsertContainerSystem.cs b/Content.Shared/Containers/SharedThrowInsertContainerSystem.cs new file mode 100644 index 0000000000..a5c300c284 --- /dev/null +++ b/Content.Shared/Containers/SharedThrowInsertContainerSystem.cs @@ -0,0 +1,8 @@ +namespace Content.Shared.Containers; + +/// +/// Sent before the insertion is made. +/// Allows preventing the insertion if any system on the entity should need to. +/// +[ByRefEvent] +public record struct BeforeThrowInsertEvent(EntityUid ThrownEntity, bool Cancelled = false); diff --git a/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs b/Content.Shared/DeviceNetwork/Components/DeviceNetworkComponent.cs similarity index 93% rename from Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs rename to Content.Shared/DeviceNetwork/Components/DeviceNetworkComponent.cs index 186da57e5d..37a86e7161 100644 --- a/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs +++ b/Content.Shared/DeviceNetwork/Components/DeviceNetworkComponent.cs @@ -1,11 +1,10 @@ -using Content.Server.DeviceNetwork.Systems; -using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Systems; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Server.DeviceNetwork.Components +namespace Content.Shared.DeviceNetwork.Components { [RegisterComponent] - [Access(typeof(DeviceNetworkSystem), typeof(DeviceNet))] + [Access(typeof(SharedDeviceNetworkSystem), typeof(DeviceNet))] public sealed partial class DeviceNetworkComponent : Component { public enum DeviceNetIdDefaults @@ -113,14 +112,14 @@ namespace Content.Server.DeviceNetwork.Components /// A list of device-lists that this device is on. /// [DataField] - [Access(typeof(DeviceListSystem))] + [Access(typeof(SharedDeviceListSystem))] public HashSet DeviceLists = new(); /// /// A list of configurators that this device is on. /// [DataField] - [Access(typeof(NetworkConfiguratorSystem))] + [Access(typeof(SharedNetworkConfiguratorSystem))] public HashSet Configurators = new(); } } diff --git a/Content.Server/DeviceNetwork/DeviceNet.cs b/Content.Shared/DeviceNetwork/DeviceNet.cs similarity index 97% rename from Content.Server/DeviceNetwork/DeviceNet.cs rename to Content.Shared/DeviceNetwork/DeviceNet.cs index c77d45c061..4234b30385 100644 --- a/Content.Server/DeviceNetwork/DeviceNet.cs +++ b/Content.Shared/DeviceNetwork/DeviceNet.cs @@ -1,8 +1,7 @@ -using Content.Server.DeviceNetwork.Components; using Robust.Shared.Random; -using static Content.Server.DeviceNetwork.Components.DeviceNetworkComponent; +using Content.Shared.DeviceNetwork.Components; -namespace Content.Server.DeviceNetwork; +namespace Content.Shared.DeviceNetwork; /// /// Data class for storing and retrieving information about devices connected to a device network. diff --git a/Content.Server/DeviceNetwork/DeviceNetworkConstants.cs b/Content.Shared/DeviceNetwork/DeviceNetworkConstants.cs similarity index 96% rename from Content.Server/DeviceNetwork/DeviceNetworkConstants.cs rename to Content.Shared/DeviceNetwork/DeviceNetworkConstants.cs index 6cbad603b4..7fec72bb69 100644 --- a/Content.Server/DeviceNetwork/DeviceNetworkConstants.cs +++ b/Content.Shared/DeviceNetwork/DeviceNetworkConstants.cs @@ -1,7 +1,7 @@ -using Content.Server.DeviceNetwork.Components; using Robust.Shared.Utility; +using Content.Shared.DeviceNetwork.Components; -namespace Content.Server.DeviceNetwork +namespace Content.Shared.DeviceNetwork { /// /// A collection of constants to help with using device networks diff --git a/Content.Shared/DeviceNetwork/Events/BeforeBroadcastAttemptEvent.cs b/Content.Shared/DeviceNetwork/Events/BeforeBroadcastAttemptEvent.cs new file mode 100644 index 0000000000..f495847482 --- /dev/null +++ b/Content.Shared/DeviceNetwork/Events/BeforeBroadcastAttemptEvent.cs @@ -0,0 +1,17 @@ +using Content.Shared.DeviceNetwork.Components; + +namespace Content.Shared.DeviceNetwork.Events; + +/// +/// Sent to the sending entity before broadcasting network packets to recipients +/// +public sealed class BeforeBroadcastAttemptEvent : CancellableEntityEventArgs +{ + public readonly IReadOnlySet Recipients; + public HashSet? ModifiedRecipients; + + public BeforeBroadcastAttemptEvent(IReadOnlySet recipients) + { + Recipients = recipients; + } +} diff --git a/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs b/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs new file mode 100644 index 0000000000..5d5c038dbf --- /dev/null +++ b/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs @@ -0,0 +1,35 @@ +using System.Numerics; + +namespace Content.Shared.DeviceNetwork.Events; + +/// +/// Event raised before a device network packet is send. +/// Subscribed to by other systems to prevent the packet from being sent. +/// +public sealed class BeforePacketSentEvent : CancellableEntityEventArgs +{ + /// + /// The EntityUid of the entity the packet was sent from. + /// + public readonly EntityUid Sender; + + public readonly TransformComponent SenderTransform; + + /// + /// The senders current position in world coordinates. + /// + public readonly Vector2 SenderPosition; + + /// + /// The network the packet will be sent to. + /// + public readonly string NetworkId; + + public BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition, string networkId) + { + Sender = sender; + SenderTransform = xform; + SenderPosition = senderPosition; + NetworkId = networkId; + } +} \ No newline at end of file diff --git a/Content.Shared/DeviceNetwork/Events/DeviceNetworkPacketEvent.cs b/Content.Shared/DeviceNetwork/Events/DeviceNetworkPacketEvent.cs new file mode 100644 index 0000000000..4ae6afeef7 --- /dev/null +++ b/Content.Shared/DeviceNetwork/Events/DeviceNetworkPacketEvent.cs @@ -0,0 +1,47 @@ +namespace Content.Shared.DeviceNetwork.Events; + +/// +/// Event raised when a device network packet gets sent. +/// +public sealed class DeviceNetworkPacketEvent : EntityEventArgs +{ + /// + /// The id of the network that this packet is being sent on. + /// + public int NetId; + + /// + /// The frequency the packet is sent on. + /// + public readonly uint Frequency; + + /// + /// Address of the intended recipient. Null if the message was broadcast. + /// + public string? Address; + + /// + /// The device network address of the sending entity. + /// + public readonly string SenderAddress; + + /// + /// The entity that sent the packet. + /// + public EntityUid Sender; + + /// + /// The data that is being sent. + /// + public readonly NetworkPayload Data; + + public DeviceNetworkPacketEvent(int netId, string? address, uint frequency, string senderAddress, EntityUid sender, NetworkPayload data) + { + NetId = netId; + Address = address; + Frequency = frequency; + SenderAddress = senderAddress; + Sender = sender; + Data = data; + } +} \ No newline at end of file diff --git a/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkSystem.cs b/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkSystem.cs new file mode 100644 index 0000000000..5992c40413 --- /dev/null +++ b/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkSystem.cs @@ -0,0 +1,25 @@ +using Content.Shared.DeviceNetwork.Components; + +namespace Content.Shared.DeviceNetwork.Systems; + +public abstract class SharedDeviceNetworkSystem : EntitySystem +{ + /// + /// Sends the given payload as a device network packet to the entity with the given address and frequency. + /// Addresses are given to the DeviceNetworkComponent of an entity when connecting. + /// + /// The EntityUid of the sending entity + /// The address of the entity that the packet gets sent to. If null, the message is broadcast to all devices on that frequency (except the sender) + /// The frequency to send on + /// The data to be sent + /// Returns true when the packet was successfully enqueued. + public virtual bool QueuePacket(EntityUid uid, + string? address, + NetworkPayload data, + uint? frequency = null, + int? network = null, + DeviceNetworkComponent? device = null) + { + return false; + } +} diff --git a/Content.Server/Disposal/Mailing/MailingUnitComponent.cs b/Content.Shared/Disposal/Mailing/MailingUnitComponent.cs similarity index 57% rename from Content.Server/Disposal/Mailing/MailingUnitComponent.cs rename to Content.Shared/Disposal/Mailing/MailingUnitComponent.cs index be5eca99c4..255d6cdf01 100644 --- a/Content.Server/Disposal/Mailing/MailingUnitComponent.cs +++ b/Content.Shared/Disposal/Mailing/MailingUnitComponent.cs @@ -1,30 +1,28 @@ -using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Mailing; +using Robust.Shared.GameStates; -namespace Content.Server.Disposal.Mailing; +namespace Content.Shared.Disposal.Components; -[Access(typeof(MailingUnitSystem))] -[RegisterComponent] +[Access(typeof(SharedMailingUnitSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] public sealed partial class MailingUnitComponent : Component { /// /// List of targets the mailing unit can send to. /// Each target is just a disposal routing tag /// - [DataField("targetList")] + [DataField, AutoNetworkedField] public List TargetList = new(); /// /// The target that gets attached to the disposal holders tag list on flush /// - [DataField("target")] + [DataField, AutoNetworkedField] public string? Target; /// /// The tag for this mailing unit /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("tag")] + [DataField, AutoNetworkedField] public string? Tag; - - public SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState? DisposalUnitInterfaceState; } diff --git a/Content.Shared/Disposal/MailingUnitUiMessages.cs b/Content.Shared/Disposal/Mailing/MailingUnitUiMessages.cs similarity index 100% rename from Content.Shared/Disposal/MailingUnitUiMessages.cs rename to Content.Shared/Disposal/Mailing/MailingUnitUiMessages.cs diff --git a/Content.Shared/Disposal/Components/SharedDisposalRouterComponent.cs b/Content.Shared/Disposal/Mailing/SharedDisposalRouterComponent.cs similarity index 100% rename from Content.Shared/Disposal/Components/SharedDisposalRouterComponent.cs rename to Content.Shared/Disposal/Mailing/SharedDisposalRouterComponent.cs diff --git a/Content.Shared/Disposal/Components/SharedDisposalTaggerComponent.cs b/Content.Shared/Disposal/Mailing/SharedDisposalTaggerComponent.cs similarity index 100% rename from Content.Shared/Disposal/Components/SharedDisposalTaggerComponent.cs rename to Content.Shared/Disposal/Mailing/SharedDisposalTaggerComponent.cs diff --git a/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs b/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs new file mode 100644 index 0000000000..cb7a8c46c8 --- /dev/null +++ b/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs @@ -0,0 +1,174 @@ +using Content.Shared.Configurable; +using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; +using Content.Shared.DeviceNetwork.Systems; +using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Unit; +using Content.Shared.Disposal.Unit.Events; +using Content.Shared.Interaction; +using Content.Shared.Power.EntitySystems; +using Robust.Shared.Player; + +namespace Content.Shared.Disposal.Mailing; + +public abstract class SharedMailingUnitSystem : EntitySystem +{ + [Dependency] private readonly SharedDeviceNetworkSystem _deviceNetworkSystem = default!; + [Dependency] private readonly SharedPowerReceiverSystem _power = default!; + [Dependency] protected readonly SharedUserInterfaceSystem UserInterfaceSystem = default!; + + private const string MailTag = "mail"; + + private const string TagConfigurationKey = "tag"; + + private const string NetTag = "tag"; + private const string NetSrc = "src"; + private const string NetTarget = "target"; + private const string NetCmdSent = "mail_sent"; + private const string NetCmdRequest = "get_mailer_tag"; + private const string NetCmdResponse = "mailer_tag"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnPacketReceived); + SubscribeLocalEvent(OnBeforeFlush); + SubscribeLocalEvent(OnConfigurationUpdated); + SubscribeLocalEvent(HandleActivate, before: new[] { typeof(SharedDisposalUnitSystem) }); + SubscribeLocalEvent(OnTargetSelected); + } + + private void OnComponentInit(EntityUid uid, MailingUnitComponent component, ComponentInit args) + { + UpdateTargetList(uid, component); + } + + private void OnPacketReceived(EntityUid uid, MailingUnitComponent component, DeviceNetworkPacketEvent args) + { + if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || !_power.IsPowered(uid)) + return; + + switch (command) + { + case NetCmdRequest: + SendTagRequestResponse(uid, args, component.Tag); + break; + case NetCmdResponse when args.Data.TryGetValue(NetTag, out string? tag): + //Add the received tag request response to the list of targets + component.TargetList.Add(tag); + Dirty(uid, component); + break; + } + } + + /// + /// Sends the given tag as a response to a if it's not null + /// + private void SendTagRequestResponse(EntityUid uid, DeviceNetworkPacketEvent args, string? tag) + { + if (tag == null) + return; + + var payload = new NetworkPayload + { + [DeviceNetworkConstants.Command] = NetCmdResponse, + [NetTag] = tag + }; + + _deviceNetworkSystem.QueuePacket(uid, args.Address, payload, args.Frequency); + } + + /// + /// Prevents the unit from flushing if no target is selected + /// + private void OnBeforeFlush(EntityUid uid, MailingUnitComponent component, BeforeDisposalFlushEvent args) + { + if (string.IsNullOrEmpty(component.Target)) + { + args.Cancel(); + return; + } + + Dirty(uid, component); + args.Tags.Add(MailTag); + args.Tags.Add(component.Target); + + BroadcastSentMessage(uid, component); + } + + /// + /// Broadcast that a mail was sent including the src and target tags + /// + private void BroadcastSentMessage(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null) + { + if (string.IsNullOrEmpty(component.Tag) || string.IsNullOrEmpty(component.Target) || !Resolve(uid, ref device)) + return; + + var payload = new NetworkPayload + { + [DeviceNetworkConstants.Command] = NetCmdSent, + [NetSrc] = component.Tag, + [NetTarget] = component.Target + }; + + _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device); + } + + /// + /// Clears the units target list and broadcasts a . + /// The target list will then get populated with responses from all active mailing units on the same grid + /// + private void UpdateTargetList(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null) + { + if (!Resolve(uid, ref device, false)) + return; + + var payload = new NetworkPayload + { + [DeviceNetworkConstants.Command] = NetCmdRequest + }; + + component.TargetList.Clear(); + _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device); + } + + /// + /// Gets called when the units tag got updated + /// + private void OnConfigurationUpdated(EntityUid uid, MailingUnitComponent component, ConfigurationUpdatedEvent args) + { + var configuration = args.Configuration.Config; + if (!configuration.ContainsKey(TagConfigurationKey) || configuration[TagConfigurationKey] == string.Empty) + { + component.Tag = null; + return; + } + + component.Tag = configuration[TagConfigurationKey]; + Dirty(uid, component); + } + + private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args) + { + if (args.Handled || !args.Complex) + return; + + if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + { + return; + } + + args.Handled = true; + UpdateTargetList(uid, component); + UserInterfaceSystem.OpenUi(uid, MailingUnitUiKey.Key, actor.PlayerSession); + } + + private void OnTargetSelected(EntityUid uid, MailingUnitComponent component, TargetSelectedMessage args) + { + component.Target = args.Target; + Dirty(uid, component); + } +} diff --git a/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs b/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs deleted file mode 100644 index 65be092072..0000000000 --- a/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Content.Shared.Disposal.Components; -using Robust.Shared.Serialization; - -namespace Content.Shared.Disposal; - -[Serializable, NetSerializable] -public sealed class MailingUnitBoundUserInterfaceState : BoundUserInterfaceState, IEquatable -{ - public string? Target; - public List TargetList; - public string? Tag; - public SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState DisposalState; - - public MailingUnitBoundUserInterfaceState(SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState disposalState, string? target, List targetList, string? tag) - { - DisposalState = disposalState; - Target = target; - TargetList = targetList; - Tag = tag; - } - - public bool Equals(MailingUnitBoundUserInterfaceState? other) - { - if (other is null) - return false; - if (ReferenceEquals(this, other)) - return true; - return DisposalState.Equals(other.DisposalState) - && Target == other.Target - && TargetList.Equals(other.TargetList) - && Tag == other.Tag; - } - - public override bool Equals(object? other) - { - if (other is MailingUnitBoundUserInterfaceState otherState) - return Equals(otherState); - return false; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } -} diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs deleted file mode 100644 index a650ef72f8..0000000000 --- a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs +++ /dev/null @@ -1,162 +0,0 @@ -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.Throwing; -using Content.Shared.Whitelist; -using Robust.Shared.Audio; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Events; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Serialization; -using Robust.Shared.Timing; - -namespace Content.Shared.Disposal; - -[Serializable, NetSerializable] -public sealed partial class DisposalDoAfterEvent : SimpleDoAfterEvent -{ -} - -public abstract class SharedDisposalUnitSystem : EntitySystem -{ - [Dependency] protected readonly IGameTiming GameTiming = default!; - [Dependency] protected readonly EmagSystem _emag = default!; - [Dependency] protected readonly MetaDataSystem Metadata = default!; - [Dependency] protected readonly SharedJointSystem Joints = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; - - protected static TimeSpan ExitAttemptDelay = TimeSpan.FromSeconds(0.5); - - // Percentage - public const float PressurePerSecond = 0.05f; - - public abstract bool HasDisposals([NotNullWhen(true)] EntityUid? uid); - - public abstract bool ResolveDisposals(EntityUid uid, [NotNullWhen(true)] ref SharedDisposalUnitComponent? component); - - /// - /// Gets the current pressure state of a disposals unit. - /// - /// - /// - /// - /// - public DisposalsPressureState GetState(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null) - { - var nextPressure = Metadata.GetPauseTime(uid, metadata) + component.NextPressurized - GameTiming.CurTime; - var pressurizeTime = 1f / PressurePerSecond; - var pressurizeDuration = pressurizeTime - component.FlushDelay.TotalSeconds; - - if (nextPressure.TotalSeconds > pressurizeDuration) - { - return DisposalsPressureState.Flushed; - } - - if (nextPressure > TimeSpan.Zero) - { - return DisposalsPressureState.Pressurizing; - } - - return DisposalsPressureState.Ready; - } - - public float GetPressure(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null) - { - if (!Resolve(uid, ref metadata)) - return 0f; - - var pauseTime = Metadata.GetPauseTime(uid, metadata); - return MathF.Min(1f, - (float) (GameTiming.CurTime - pauseTime - component.NextPressurized).TotalSeconds / PressurePerSecond); - } - - protected void OnPreventCollide(EntityUid uid, SharedDisposalUnitComponent component, - ref PreventCollideEvent args) - { - var otherBody = args.OtherEntity; - - // Items dropped shouldn't collide but items thrown should - if (HasComp(otherBody) && !HasComp(otherBody)) - { - args.Cancelled = true; - return; - } - - if (component.RecentlyEjected.Contains(otherBody)) - { - args.Cancelled = true; - } - } - - protected void OnCanDragDropOn(EntityUid uid, SharedDisposalUnitComponent component, ref CanDropTargetEvent args) - { - if (args.Handled) - return; - - args.CanDrop = CanInsert(uid, component, args.Dragged); - args.Handled = true; - } - - protected void OnEmagged(EntityUid uid, SharedDisposalUnitComponent component, ref GotEmaggedEvent args) - { - if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) - return; - - if (component.DisablePressure == true) - return; - - component.DisablePressure = true; - args.Handled = true; - } - - public virtual bool CanInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid entity) - { - if (!Transform(uid).Anchored) - return false; - - var storable = HasComp(entity); - if (!storable && !HasComp(entity)) - return false; - - if (_whitelistSystem.IsBlacklistPass(component.Blacklist, entity) || - _whitelistSystem.IsWhitelistFail(component.Whitelist, entity)) - return false; - - if (TryComp(entity, out var physics) && (physics.CanCollide) || storable) - return true; - else - return false; - - } - - public abstract void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null); - - [Serializable, NetSerializable] - protected sealed class DisposalUnitComponentState : ComponentState - { - public SoundSpecifier? FlushSound; - public DisposalsPressureState State; - public TimeSpan NextPressurized; - public TimeSpan AutomaticEngageTime; - public TimeSpan? NextFlush; - public bool Powered; - public bool Engaged; - public List RecentlyEjected; - - public DisposalUnitComponentState(SoundSpecifier? flushSound, DisposalsPressureState state, TimeSpan nextPressurized, TimeSpan automaticEngageTime, TimeSpan? nextFlush, bool powered, bool engaged, List recentlyEjected) - { - FlushSound = flushSound; - State = state; - NextPressurized = nextPressurized; - AutomaticEngageTime = automaticEngageTime; - NextFlush = nextFlush; - Powered = powered; - Engaged = engaged; - RecentlyEjected = recentlyEjected; - } - } -} diff --git a/Content.Shared/Disposal/Tube/DisposalEntryComponent.cs b/Content.Shared/Disposal/Tube/DisposalEntryComponent.cs new file mode 100644 index 0000000000..066b16ad1f --- /dev/null +++ b/Content.Shared/Disposal/Tube/DisposalEntryComponent.cs @@ -0,0 +1,12 @@ +using Content.Shared.Disposal.Unit; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Disposal.Tube; + +[RegisterComponent] +[Access(typeof(SharedDisposalTubeSystem), typeof(SharedDisposalUnitSystem))] +public sealed partial class DisposalEntryComponent : Component +{ + [DataField] + public EntProtoId HolderPrototypeId = "DisposalHolder"; +} diff --git a/Content.Shared/Disposal/Components/SharedDisposalTubeComponent.cs b/Content.Shared/Disposal/Tube/SharedDisposalTubeComponent.cs similarity index 100% rename from Content.Shared/Disposal/Components/SharedDisposalTubeComponent.cs rename to Content.Shared/Disposal/Tube/SharedDisposalTubeComponent.cs diff --git a/Content.Shared/Disposal/Unit/BeforeDisposalFlushEvent.cs b/Content.Shared/Disposal/Unit/BeforeDisposalFlushEvent.cs new file mode 100644 index 0000000000..ee141d6c4d --- /dev/null +++ b/Content.Shared/Disposal/Unit/BeforeDisposalFlushEvent.cs @@ -0,0 +1,10 @@ +namespace Content.Shared.Disposal.Unit.Events; + +/// +/// Sent before the disposal unit flushes it's contents. +/// Allows adding tags for sorting and preventing the disposal unit from flushing. +/// +public sealed class BeforeDisposalFlushEvent : CancellableEntityEventArgs +{ + public readonly List Tags = new(); +} \ No newline at end of file diff --git a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs b/Content.Shared/Disposal/Unit/DisposalUnitComponent.cs similarity index 68% rename from Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs rename to Content.Shared/Disposal/Unit/DisposalUnitComponent.cs index 36dd14f9b2..34fe223013 100644 --- a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs +++ b/Content.Shared/Disposal/Unit/DisposalUnitComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Atmos; using Robust.Shared.Audio; using Content.Shared.Whitelist; using Robust.Shared.Containers; @@ -7,15 +8,24 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Disposal.Components; -[NetworkedComponent] -public abstract partial class SharedDisposalUnitComponent : Component +/// +/// Takes in entities and flushes them out to attached disposals tubes after a timer. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +public sealed partial class DisposalUnitComponent : Component { public const string ContainerId = "disposals"; + /// + /// Air contained in the disposal unit. + /// + [DataField] + public GasMixture Air = new(Atmospherics.CellVolume); + /// /// Sounds played upon the unit flushing. /// - [ViewVariables(VVAccess.ReadWrite), DataField("soundFlush")] + [DataField("soundFlush"), AutoNetworkedField] public SoundSpecifier? FlushSound = new SoundPathSpecifier("/Audio/Machines/disposalflush.ogg"); /// @@ -39,20 +49,13 @@ public abstract partial class SharedDisposalUnitComponent : Component /// /// State for this disposals unit. /// - [DataField] + [DataField, AutoNetworkedField] 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] - public List RecentlyEjected = new(); - /// /// Next time the disposal unit will be pressurized. /// - [DataField(customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] public TimeSpan NextPressurized = TimeSpan.Zero; /// @@ -70,26 +73,24 @@ public abstract partial class SharedDisposalUnitComponent : Component /// /// Removes the pressure requirement for flushing. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public bool DisablePressure; /// /// Last time that an entity tried to exit this disposal unit. /// - [ViewVariables] + [DataField, AutoNetworkedField] public TimeSpan LastExitAttempt; [DataField] public bool AutomaticEngage = true; - [ViewVariables(VVAccess.ReadWrite)] - [DataField] + [DataField, AutoNetworkedField] public TimeSpan AutomaticEngageTime = TimeSpan.FromSeconds(30); /// /// Delay from trying to enter disposals ourselves. /// - [ViewVariables(VVAccess.ReadWrite)] [DataField] public float EntryDelay = 0.5f; @@ -104,20 +105,16 @@ public abstract partial class SharedDisposalUnitComponent : Component /// [ViewVariables] public Container Container = default!; - // TODO: Network power shit instead fam. - [ViewVariables, DataField] - public bool Powered; - /// /// Was the disposals unit engaged for a manual flush. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField, AutoNetworkedField] public bool Engaged; /// /// Next time this unit will flush. Is the lesser of and /// - [ViewVariables, DataField(customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] public TimeSpan? NextFlush; [Serializable, NetSerializable] @@ -162,37 +159,6 @@ public abstract partial class SharedDisposalUnitComponent : Component Power } - [Serializable, NetSerializable] - public sealed class DisposalUnitBoundUserInterfaceState : BoundUserInterfaceState, IEquatable - { - public readonly string UnitName; - public readonly string UnitState; - public readonly TimeSpan FullPressureTime; - public readonly bool Powered; - public readonly bool Engaged; - - public DisposalUnitBoundUserInterfaceState(string unitName, string unitState, TimeSpan fullPressureTime, bool powered, - bool engaged) - { - UnitName = unitName; - UnitState = unitState; - FullPressureTime = fullPressureTime; - Powered = powered; - Engaged = engaged; - } - - public bool Equals(DisposalUnitBoundUserInterfaceState? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return UnitName == other.UnitName && - UnitState == other.UnitState && - Powered == other.Powered && - Engaged == other.Engaged && - FullPressureTime.Equals(other.FullPressureTime); - } - } - /// /// Message data sent from client to server when a disposal unit ui button is pressed. /// diff --git a/Content.Shared/Disposal/Unit/SharedDisposalTubeSystem.cs b/Content.Shared/Disposal/Unit/SharedDisposalTubeSystem.cs new file mode 100644 index 0000000000..58d4da7eba --- /dev/null +++ b/Content.Shared/Disposal/Unit/SharedDisposalTubeSystem.cs @@ -0,0 +1,14 @@ +using Content.Shared.Disposal.Components; + +namespace Content.Shared.Disposal.Unit; + +public abstract class SharedDisposalTubeSystem : EntitySystem +{ + public virtual bool TryInsert(EntityUid uid, + DisposalUnitComponent from, + IEnumerable? tags = default, + Tube.DisposalEntryComponent? entry = null) + { + return false; + } +} diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs similarity index 53% rename from Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs rename to Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs index 136ac2c440..1db24c700d 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs @@ -1,24 +1,15 @@ -using System.Diagnostics.CodeAnalysis; using System.Linq; -using Content.Server.Administration.Logs; -using Content.Server.Atmos.EntitySystems; -using Content.Server.Containers; -using Content.Server.Disposal.Tube; -using Content.Server.Disposal.Tube.Components; -using Content.Server.Disposal.Unit.Components; -using Content.Server.Popups; -using Content.Server.Power.Components; -using Content.Server.Power.EntitySystems; using Content.Shared.ActionBlocker; -using Content.Shared.Atmos; +using Content.Shared.Administration.Logs; +using Content.Shared.Body.Components; +using Content.Shared.Climbing.Systems; +using Content.Shared.Containers; using Content.Shared.Database; -using Content.Shared.Destructible; -using Content.Shared.Disposal; using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Unit.Events; using Content.Shared.DoAfter; using Content.Shared.DragDrop; using Content.Shared.Emag.Systems; -using Content.Shared.Explosion; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; @@ -27,59 +18,59 @@ using Content.Shared.Item; using Content.Shared.Movement.Events; using Content.Shared.Popups; using Content.Shared.Power; +using Content.Shared.Power.EntitySystems; +using Content.Shared.Throwing; using Content.Shared.Verbs; -using Robust.Server.Audio; -using Robust.Server.GameObjects; +using Content.Shared.Whitelist; +using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using Robust.Shared.GameStates; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; -using Robust.Shared.Player; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Serialization; +using Robust.Shared.Timing; using Robust.Shared.Utility; -namespace Content.Server.Disposal.Unit.EntitySystems; +namespace Content.Shared.Disposal.Unit; -public sealed class DisposalUnitSystem : SharedDisposalUnitSystem +[Serializable, NetSerializable] +public sealed partial class DisposalDoAfterEvent : SimpleDoAfterEvent { - [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - [Dependency] private readonly AppearanceSystem _appearance = default!; - [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; - [Dependency] private readonly AudioSystem _audioSystem = default!; - [Dependency] private readonly DisposalTubeSystem _disposalTubeSystem = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly PowerReceiverSystem _power = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; - [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly SharedMapSystem _map = default!; +} + +public abstract class SharedDisposalUnitSystem : EntitySystem +{ + [Dependency] protected readonly ActionBlockerSystem ActionBlockerSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] protected readonly MetaDataSystem Metadata = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] protected readonly SharedAudioSystem Audio = default!; + [Dependency] protected readonly IGameTiming GameTiming = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLog = default!; + [Dependency] private readonly ClimbSystem _climb = default!; + [Dependency] protected readonly SharedContainerSystem Containers = default!; + [Dependency] protected readonly SharedJointSystem Joints = default!; + [Dependency] private readonly SharedPowerReceiverSystem _power = default!; + [Dependency] private readonly SharedDisposalTubeSystem _disposalTubeSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + + protected static TimeSpan ExitAttemptDelay = TimeSpan.FromSeconds(0.5); + + // Percentage + public const float PressurePerSecond = 0.05f; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnPreventCollide); SubscribeLocalEvent(OnCanDragDropOn); - SubscribeLocalEvent(OnEmagged); - - // Shouldn't need re-anchoring. - SubscribeLocalEvent(OnAnchorChanged); - // TODO: Predict me when hands predicted - SubscribeLocalEvent(OnMovement); - SubscribeLocalEvent(OnPowerChange); - SubscribeLocalEvent(OnDisposalInit); - - SubscribeLocalEvent(OnActivate); - SubscribeLocalEvent(OnAfterInteractUsing); - SubscribeLocalEvent(OnDragDropOn); - SubscribeLocalEvent(OnDestruction); - SubscribeLocalEvent(OnExploded); - SubscribeLocalEvent>(AddInsertVerb); SubscribeLocalEvent>(AddDisposalAltVerbs); SubscribeLocalEvent>(AddClimbInsideVerb); @@ -88,27 +79,27 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem SubscribeLocalEvent(OnThrowInsert); - SubscribeLocalEvent(OnUiButtonPressed); + SubscribeLocalEvent(OnUiButtonPressed); + + SubscribeLocalEvent(OnEmagged); + SubscribeLocalEvent(OnAnchorChanged); + SubscribeLocalEvent(OnPowerChange); + SubscribeLocalEvent(OnDisposalInit); + + SubscribeLocalEvent(OnActivate); + SubscribeLocalEvent(OnAfterInteractUsing); + SubscribeLocalEvent(OnDragDropOn); + SubscribeLocalEvent(OnMovement); } - private void OnGetState(EntityUid uid, DisposalUnitComponent component, ref ComponentGetState args) - { - args.State = new DisposalUnitComponentState( - component.FlushSound, - component.State, - component.NextPressurized, - component.AutomaticEngageTime, - component.NextFlush, - component.Powered, - component.Engaged, - GetNetEntityList(component.RecentlyEjected)); - } - - private void AddDisposalAltVerbs(EntityUid uid, SharedDisposalUnitComponent component, GetVerbsEvent args) + private void AddDisposalAltVerbs(Entity ent, ref GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract) return; + var uid = ent.Owner; + var component = ent.Comp; + // Behavior for if the disposals bin has items in it if (component.Container.ContainedEntities.Count > 0) { @@ -133,41 +124,12 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem } } - private void AddClimbInsideVerb(EntityUid uid, SharedDisposalUnitComponent component, GetVerbsEvent args) - { - // 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 (!args.CanAccess || - !args.CanInteract || - component.Container.ContainedEntities.Contains(args.User) || - !_actionBlockerSystem.CanMove(args.User)) - { - return; - } - - if (!CanInsert(uid, component, args.User)) - return; - - // Add verb to climb inside of the unit, - Verb verb = new() - { - Act = () => TryInsert(uid, args.User, args.User), - DoContactInteraction = true, - Text = Loc.GetString("disposal-self-insert-verb-get-data-text") - }; - // TODO VERB ICON - // TODO VERB CATEGORY - // create a verb category for "enter"? - // See also, medical scanner. Also maybe add verbs for entering lockers/body bags? - args.Verbs.Add(verb); - } - - private void AddInsertVerb(EntityUid uid, SharedDisposalUnitComponent component, GetVerbsEvent args) + private void AddInsertVerb(EntityUid uid, DisposalUnitComponent component, GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract || args.Hands == null || args.Using == null) return; - if (!_actionBlockerSystem.CanDrop(args.User)) + if (!ActionBlockerSystem.CanDrop(args.User)) return; if (!CanInsert(uid, component, args.Using.Value)) @@ -180,7 +142,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem Act = () => { _handsSystem.TryDropIntoContainer(args.User, args.Using.Value, component.Container, checkActionBlocker: false, args.Hands); - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}"); + _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}"); AfterInsert(uid, component, args.Using.Value, args.User); } }; @@ -188,7 +150,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem args.Verbs.Add(insertVerb); } - private void OnDoAfter(EntityUid uid, SharedDisposalUnitComponent component, DoAfterEvent args) + private void OnDoAfter(EntityUid uid, DisposalUnitComponent component, DoAfterEvent args) { if (args.Handled || args.Cancelled || args.Args.Target == null || args.Args.Used == null) return; @@ -204,89 +166,46 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem args.Cancelled = true; } - public override void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null) - { - if (!ResolveDisposals(uid, ref disposal)) - return; - - if (!_containerSystem.Insert(toInsert, disposal.Container)) - return; - - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(uid)}"); - AfterInsert(uid, disposal, toInsert, user); - } - public override void Update(float frameTime) { base.Update(frameTime); - var query = AllEntityQuery(); + var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var unit, out var metadata)) { - if (!metadata.EntityPaused) - Update(uid, unit, metadata, frameTime); + Update(uid, unit, metadata); } } - #region UI Handlers - private void OnUiButtonPressed(EntityUid uid, SharedDisposalUnitComponent component, SharedDisposalUnitComponent.UiButtonPressedMessage args) + // TODO: This should just use the same thing as entity storage? + private void OnMovement(EntityUid uid, DisposalUnitComponent component, ref ContainerRelayMovementEntityEvent args) { - if (args.Actor is not { Valid: true } player) - { + var currentTime = GameTiming.CurTime; + + if (!ActionBlockerSystem.CanMove(args.Entity)) return; - } - switch (args.Button) - { - case SharedDisposalUnitComponent.UiButton.Eject: - TryEjectContents(uid, component); - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit eject button on {ToPrettyString(uid)}"); - break; - case SharedDisposalUnitComponent.UiButton.Engage: - ToggleEngage(uid, component); - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit flush button on {ToPrettyString(uid)}, it's now {(component.Engaged ? "on" : "off")}"); - break; - case SharedDisposalUnitComponent.UiButton.Power: - _power.TogglePower(uid, user: args.Actor); - break; - default: - throw new ArgumentOutOfRangeException($"{ToPrettyString(player):player} attempted to hit a nonexistant button on {ToPrettyString(uid)}"); - } + if (!TryComp(args.Entity, out HandsComponent? hands) || + hands.Count == 0 || + currentTime < component.LastExitAttempt + ExitAttemptDelay) + return; + + Dirty(uid, component); + component.LastExitAttempt = currentTime; + Remove(uid, component, args.Entity); + UpdateUI((uid, component)); } - public void ToggleEngage(EntityUid uid, SharedDisposalUnitComponent component) - { - component.Engaged ^= true; - - if (component.Engaged) - { - ManualEngage(uid, component); - } - else - { - Disengage(uid, component); - } - } - - #endregion - - #region Eventbus Handlers - - private void OnActivate(EntityUid uid, SharedDisposalUnitComponent component, ActivateInWorldEvent args) + private void OnActivate(EntityUid uid, DisposalUnitComponent component, ActivateInWorldEvent args) { if (args.Handled || !args.Complex) return; - if (!TryComp(args.User, out ActorComponent? actor)) - { - return; - } - args.Handled = true; - _ui.OpenUi(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, actor.PlayerSession); + _ui.TryToggleUi(uid, DisposalUnitComponent.DisposalUnitUiKey.Key, args.User); } - private void OnAfterInteractUsing(EntityUid uid, SharedDisposalUnitComponent component, AfterInteractUsingEvent args) + private void OnAfterInteractUsing(EntityUid uid, DisposalUnitComponent component, AfterInteractUsingEvent args) { if (args.Handled || !args.CanReach) return; @@ -301,26 +220,23 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem return; } - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Used)} into {ToPrettyString(uid)}"); + _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Used)} into {ToPrettyString(uid)}"); AfterInsert(uid, component, args.Used, args.User); args.Handled = true; } - private void OnDisposalInit(EntityUid uid, SharedDisposalUnitComponent component, ComponentInit args) + protected virtual void OnDisposalInit(Entity ent, ref ComponentInit args) { - component.Container = _containerSystem.EnsureContainer(uid, SharedDisposalUnitComponent.ContainerId); - - UpdateInterface(uid, component, component.Powered); + ent.Comp.Container = Containers.EnsureContainer(ent, DisposalUnitComponent.ContainerId); } - private void OnPowerChange(EntityUid uid, SharedDisposalUnitComponent component, ref PowerChangedEvent args) + private void OnPowerChange(EntityUid uid, DisposalUnitComponent component, ref PowerChangedEvent args) { - if (!component.Running || args.Powered == component.Powered) + if (!component.Running) return; - component.Powered = args.Powered; + UpdateUI((uid, component)); UpdateVisualState(uid, component); - UpdateInterface(uid, component, args.Powered); if (!args.Powered) { @@ -336,24 +252,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem } } - // TODO: This should just use the same thing as entity storage? - private void OnMovement(EntityUid uid, SharedDisposalUnitComponent component, ref ContainerRelayMovementEntityEvent args) - { - var currentTime = GameTiming.CurTime; - - if (!_actionBlockerSystem.CanMove(args.Entity)) - return; - - if (!TryComp(args.Entity, out HandsComponent? hands) || - hands.Count == 0 || - currentTime < component.LastExitAttempt + ExitAttemptDelay) - return; - - component.LastExitAttempt = currentTime; - Remove(uid, component, args.Entity); - } - - private void OnAnchorChanged(EntityUid uid, SharedDisposalUnitComponent component, ref AnchorStateChangedEvent args) + private void OnAnchorChanged(EntityUid uid, DisposalUnitComponent component, ref AnchorStateChangedEvent args) { if (Terminating(uid)) return; @@ -363,108 +262,238 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem TryEjectContents(uid, component); } - private void OnDestruction(EntityUid uid, SharedDisposalUnitComponent component, DestructionEventArgs args) - { - TryEjectContents(uid, component); - } - - private void OnDragDropOn(EntityUid uid, SharedDisposalUnitComponent component, ref DragDropTargetEvent args) + private void OnDragDropOn(EntityUid uid, DisposalUnitComponent component, ref DragDropTargetEvent args) { args.Handled = TryInsert(uid, args.Dragged, args.User); } - #endregion - - private void UpdateState(EntityUid uid, DisposalsPressureState state, SharedDisposalUnitComponent component, MetaDataComponent metadata) + protected virtual void UpdateUI(Entity entity) { - if (component.State == state) - return; - component.State = state; - UpdateVisualState(uid, component); - UpdateInterface(uid, component, component.Powered); - Dirty(uid, component, metadata); - - if (state == DisposalsPressureState.Ready) - { - component.NextPressurized = TimeSpan.Zero; - - // Manually engaged - if (component.Engaged) - { - component.NextFlush = GameTiming.CurTime + component.ManualFlushTime; - } - else if (component.Container.ContainedEntities.Count > 0) - { - component.NextFlush = GameTiming.CurTime + component.AutomaticEngageTime; - } - else - { - component.NextFlush = null; - } - } } /// - /// Work out if we can stop updating this disposals component i.e. full pressure and nothing colliding. + /// Returns the estimated time when the disposal unit will be back to full pressure. /// - private void Update(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent metadata, float frameTime) + public TimeSpan EstimatedFullPressure(EntityUid uid, DisposalUnitComponent component) { - var state = GetState(uid, component, metadata); + if (component.NextPressurized < GameTiming.CurTime) + return TimeSpan.Zero; - // Pressurizing, just check if we need a state update. - if (component.NextPressurized > GameTiming.CurTime) + return component.NextPressurized; + } + + public bool CanFlush(EntityUid unit, DisposalUnitComponent component) + { + return GetState(unit, component) == DisposalsPressureState.Ready + && _power.IsPowered(unit) + && Comp(unit).Anchored; + } + + public void Remove(EntityUid uid, DisposalUnitComponent component, EntityUid toRemove) + { + if (GameTiming.ApplyingState) + return; + + if (!Containers.Remove(toRemove, component.Container)) + return; + + if (component.Container.ContainedEntities.Count == 0) + { + // If not manually engaged then reset the flushing entirely. + if (!component.Engaged) + { + component.NextFlush = null; + Dirty(uid, component); + UpdateUI((uid, component)); + } + } + + _climb.Climb(toRemove, toRemove, uid, silent: true); + + UpdateVisualState(uid, component); + } + + public void UpdateVisualState(EntityUid uid, DisposalUnitComponent component, bool flush = false) + { + if (!TryComp(uid, out AppearanceComponent? appearance)) { - UpdateState(uid, state, component, metadata); return; } - if (component.NextFlush != null) + if (!Transform(uid).Anchored) { - if (component.NextFlush.Value < GameTiming.CurTime) - { - TryFlush(uid, component); - } + _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.UnAnchored, appearance); + _appearance.SetData(uid, DisposalUnitComponent.Visuals.Handle, DisposalUnitComponent.HandleState.Normal, appearance); + _appearance.SetData(uid, DisposalUnitComponent.Visuals.Light, DisposalUnitComponent.LightStates.Off, appearance); + return; } - UpdateState(uid, state, component, metadata); + var state = GetState(uid, component); - Box2? disposalsBounds = null; - var count = component.RecentlyEjected.Count; - - if (count > 0) + switch (state) { - if (!HasComp(uid)) - { - component.RecentlyEjected.Clear(); - } - else - { - disposalsBounds = _lookup.GetWorldAABB(uid); - } + case DisposalsPressureState.Flushed: + _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.OverlayFlushing, appearance); + break; + case DisposalsPressureState.Pressurizing: + _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.OverlayCharging, appearance); + break; + case DisposalsPressureState.Ready: + _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.Anchored, appearance); + break; } - for (var i = 0; i < component.RecentlyEjected.Count; i++) - { - var ejectedId = component.RecentlyEjected[i]; - if (HasComp(ejectedId)) - { - // TODO: We need to use a specific collision method (which sloth hasn't coded yet) for actual bounds overlaps. - // TODO: Come do this sloth :^) - // Check for itemcomp as we won't just block the disposal unit "sleeping" for something it can't collide with anyway. - if (!HasComp(ejectedId) - && _lookup.GetWorldAABB(ejectedId).Intersects(disposalsBounds!.Value)) - { - continue; - } + _appearance.SetData(uid, DisposalUnitComponent.Visuals.Handle, component.Engaged + ? DisposalUnitComponent.HandleState.Engaged + : DisposalUnitComponent.HandleState.Normal, appearance); - component.RecentlyEjected.RemoveAt(i); - i--; - } + if (!_power.IsPowered(uid)) + { + _appearance.SetData(uid, DisposalUnitComponent.Visuals.Light, DisposalUnitComponent.LightStates.Off, appearance); + return; } - if (count != component.RecentlyEjected.Count) - Dirty(uid, component, metadata); + var lightState = DisposalUnitComponent.LightStates.Off; + + if (component.Container.ContainedEntities.Count > 0) + { + lightState |= DisposalUnitComponent.LightStates.Full; + } + + if (state is DisposalsPressureState.Pressurizing or DisposalsPressureState.Flushed) + { + lightState |= DisposalUnitComponent.LightStates.Charging; + } + else + { + lightState |= DisposalUnitComponent.LightStates.Ready; + } + + _appearance.SetData(uid, DisposalUnitComponent.Visuals.Light, lightState, appearance); + } + + /// + /// Gets the current pressure state of a disposals unit. + /// + /// + /// + /// + /// + public DisposalsPressureState GetState(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null) + { + var nextPressure = Metadata.GetPauseTime(uid, metadata) + component.NextPressurized - GameTiming.CurTime; + var pressurizeTime = 1f / PressurePerSecond; + var pressurizeDuration = pressurizeTime - component.FlushDelay.TotalSeconds; + + if (nextPressure.TotalSeconds > pressurizeDuration) + { + return DisposalsPressureState.Flushed; + } + + if (nextPressure > TimeSpan.Zero) + { + return DisposalsPressureState.Pressurizing; + } + + return DisposalsPressureState.Ready; + } + + public float GetPressure(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null) + { + if (!Resolve(uid, ref metadata)) + return 0f; + + var pauseTime = Metadata.GetPauseTime(uid, metadata); + return MathF.Min(1f, + (float)(GameTiming.CurTime - pauseTime - component.NextPressurized).TotalSeconds / PressurePerSecond); + } + + protected void OnPreventCollide(EntityUid uid, DisposalUnitComponent component, + ref PreventCollideEvent args) + { + var otherBody = args.OtherEntity; + + // Items dropped shouldn't collide but items thrown should + if (HasComp(otherBody) && !HasComp(otherBody)) + { + args.Cancelled = true; + } + } + + protected void OnCanDragDropOn(EntityUid uid, DisposalUnitComponent component, ref CanDropTargetEvent args) + { + if (args.Handled) + return; + + args.CanDrop = CanInsert(uid, component, args.Dragged); + args.Handled = true; + } + + protected void OnEmagged(EntityUid uid, DisposalUnitComponent component, ref GotEmaggedEvent args) + { + component.DisablePressure = true; + args.Handled = true; + } + + public virtual bool CanInsert(EntityUid uid, DisposalUnitComponent component, EntityUid entity) + { + // TODO: All of the below should be using the EXISTING EVENT + if (!Containers.CanInsert(entity, component.Container)) + return false; + + if (!Transform(uid).Anchored) + return false; + + var storable = HasComp(entity); + if (!storable && !HasComp(entity)) + return false; + + if (_whitelistSystem.IsBlacklistPass(component.Blacklist, entity) || + _whitelistSystem.IsWhitelistFail(component.Whitelist, entity)) + return false; + + if (TryComp(entity, out var physics) && (physics.CanCollide) || storable) + return true; + else + return false; + } + + public void DoInsertDisposalUnit(EntityUid uid, + EntityUid toInsert, + EntityUid user, + DisposalUnitComponent? disposal = null) + { + if (!Resolve(uid, ref disposal)) + return; + + if (!Containers.Insert(toInsert, disposal.Container)) + return; + + _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(uid)}"); + AfterInsert(uid, disposal, toInsert, user); + } + + public virtual void AfterInsert(EntityUid uid, + DisposalUnitComponent component, + EntityUid inserted, + EntityUid? user = null, + bool doInsert = false) + { + Audio.PlayPredicted(component.InsertSound, uid, user: user); + if (doInsert && !Containers.Insert(inserted, component.Container)) + return; + + if (user != inserted && user != null) + _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user.Value):player} inserted {ToPrettyString(inserted)} into {ToPrettyString(uid)}"); + + QueueAutomaticEngage(uid, component); + + _ui.CloseUi(uid, DisposalUnitComponent.DisposalUnitUiKey.Key, inserted); + + // Maybe do pullable instead? Eh still fine. + Joints.RecursiveClearJoints(inserted); + UpdateVisualState(uid, component); } public bool TryInsert(EntityUid unitId, EntityUid toInsertId, EntityUid? userId, DisposalUnitComponent? unit = null) @@ -507,8 +536,61 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem return true; } + private void UpdateState(EntityUid uid, DisposalsPressureState state, DisposalUnitComponent component, MetaDataComponent metadata) + { + if (component.State == state) + return; - public bool TryFlush(EntityUid uid, SharedDisposalUnitComponent component) + component.State = state; + UpdateVisualState(uid, component); + Dirty(uid, component, metadata); + + if (state == DisposalsPressureState.Ready) + { + component.NextPressurized = TimeSpan.Zero; + + // Manually engaged + if (component.Engaged) + { + component.NextFlush = GameTiming.CurTime + component.ManualFlushTime; + } + else if (component.Container.ContainedEntities.Count > 0) + { + component.NextFlush = GameTiming.CurTime + component.AutomaticEngageTime; + } + else + { + component.NextFlush = null; + } + } + } + + /// + /// Work out if we can stop updating this disposals component i.e. full pressure and nothing colliding. + /// + private void Update(EntityUid uid, DisposalUnitComponent component, MetaDataComponent metadata) + { + var state = GetState(uid, component, metadata); + + // Pressurizing, just check if we need a state update. + if (component.NextPressurized > GameTiming.CurTime) + { + UpdateState(uid, state, component, metadata); + return; + } + + if (component.NextFlush != null) + { + if (component.NextFlush.Value < GameTiming.CurTime) + { + TryFlush(uid, component); + } + } + + UpdateState(uid, state, component, metadata); + } + + public bool TryFlush(EntityUid uid, DisposalUnitComponent component) { if (!CanFlush(uid, component)) { @@ -533,11 +615,12 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem var coords = xform.Coordinates; var entry = _map.GetLocal(xform.GridUid.Value, grid, coords) - .FirstOrDefault(HasComp); + .FirstOrDefault(HasComp); if (entry == default || component is not DisposalUnitComponent sDisposals) { component.Engaged = false; + UpdateUI((uid, component)); Dirty(uid, component); return false; } @@ -555,140 +638,23 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem component.NextFlush = null; UpdateVisualState(uid, component, true); - UpdateInterface(uid, component, component.Powered); - Dirty(uid, component); + UpdateUI((uid, component)); return true; } - private void HandleAir(EntityUid uid, DisposalUnitComponent component, TransformComponent xform) + protected virtual void HandleAir(EntityUid uid, DisposalUnitComponent component, TransformComponent xform) { - var air = component.Air; - var indices = _transformSystem.GetGridTilePositionOrDefault((uid, xform)); - if (_atmosSystem.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is { Temperature: > 0f } environment) - { - var transferMoles = 0.1f * (0.25f * Atmospherics.OneAtmosphere * 1.01f - air.Pressure) * air.Volume / (environment.Temperature * Atmospherics.R); - - component.Air = environment.Remove(transferMoles); - } } - public void UpdateInterface(EntityUid uid, SharedDisposalUnitComponent component, bool powered) - { - var compState = GetState(uid, component); - var stateString = Loc.GetString($"disposal-unit-state-{compState}"); - var state = new SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState(Name(uid), stateString, EstimatedFullPressure(uid, component), powered, component.Engaged); - _ui.SetUiState(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, state); - - var stateUpdatedEvent = new DisposalUnitUIStateUpdatedEvent(state); - RaiseLocalEvent(uid, stateUpdatedEvent); - } - - /// - /// Returns the estimated time when the disposal unit will be back to full pressure. - /// - private TimeSpan EstimatedFullPressure(EntityUid uid, SharedDisposalUnitComponent component) - { - if (component.NextPressurized < GameTiming.CurTime) - return TimeSpan.Zero; - - return component.NextPressurized; - } - - public void UpdateVisualState(EntityUid uid, SharedDisposalUnitComponent component, bool flush = false) - { - if (!TryComp(uid, out AppearanceComponent? appearance)) - { - return; - } - - if (!Transform(uid).Anchored) - { - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.UnAnchored, appearance); - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Handle, SharedDisposalUnitComponent.HandleState.Normal, appearance); - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightStates.Off, appearance); - return; - } - - var state = GetState(uid, component); - - switch (state) - { - case DisposalsPressureState.Flushed: - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.OverlayFlushing, appearance); - break; - case DisposalsPressureState.Pressurizing: - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.OverlayCharging, appearance); - break; - case DisposalsPressureState.Ready: - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Anchored, appearance); - break; - } - - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Handle, component.Engaged - ? SharedDisposalUnitComponent.HandleState.Engaged - : SharedDisposalUnitComponent.HandleState.Normal, appearance); - - if (!component.Powered) - { - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightStates.Off, appearance); - return; - } - - var lightState = SharedDisposalUnitComponent.LightStates.Off; - - if (component.Container.ContainedEntities.Count > 0) - { - lightState |= SharedDisposalUnitComponent.LightStates.Full; - } - - if (state is DisposalsPressureState.Pressurizing or DisposalsPressureState.Flushed) - { - lightState |= SharedDisposalUnitComponent.LightStates.Charging; - } - else - { - lightState |= SharedDisposalUnitComponent.LightStates.Ready; - } - - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, lightState, appearance); - } - - public void Remove(EntityUid uid, SharedDisposalUnitComponent component, EntityUid toRemove) - { - _containerSystem.Remove(toRemove, component.Container); - - if (component.Container.ContainedEntities.Count == 0) - { - // If not manually engaged then reset the flushing entirely. - if (!component.Engaged) - { - component.NextFlush = null; - } - } - - if (!component.RecentlyEjected.Contains(toRemove)) - component.RecentlyEjected.Add(toRemove); - - UpdateVisualState(uid, component); - Dirty(uid, component); - } - - public bool CanFlush(EntityUid unit, SharedDisposalUnitComponent component) - { - return GetState(unit, component) == DisposalsPressureState.Ready - && component.Powered - && Comp(unit).Anchored; - } - - public void ManualEngage(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null) + public void ManualEngage(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null) { component.Engaged = true; UpdateVisualState(uid, component); - UpdateInterface(uid, component, component.Powered); Dirty(uid, component); + UpdateUI((uid, component)); if (!CanFlush(uid, component)) return; @@ -701,7 +667,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem component.NextFlush = TimeSpan.FromSeconds(Math.Min((component.NextFlush ?? TimeSpan.MaxValue).TotalSeconds, nextEngage.TotalSeconds)); } - public void Disengage(EntityUid uid, SharedDisposalUnitComponent component) + public void Disengage(EntityUid uid, DisposalUnitComponent component) { component.Engaged = false; @@ -711,14 +677,14 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem } UpdateVisualState(uid, component); - UpdateInterface(uid, component, component.Powered); Dirty(uid, component); + UpdateUI((uid, component)); } /// /// Remove all entities currently in the disposal unit. /// - public void TryEjectContents(EntityUid uid, SharedDisposalUnitComponent component) + public void TryEjectContents(EntityUid uid, DisposalUnitComponent component) { foreach (var entity in component.Container.ContainedEntities.ToArray()) { @@ -729,38 +695,16 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem { component.NextFlush = null; Dirty(uid, component); + UpdateUI((uid, component)); } } - public override bool HasDisposals(EntityUid? uid) - { - return HasComp(uid); - } - - public override bool ResolveDisposals(EntityUid uid, [NotNullWhen(true)] ref SharedDisposalUnitComponent? component) - { - if (component != null) - return true; - - TryComp(uid, out var storage); - component = storage; - return component != null; - } - - public override bool CanInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid entity) - { - if (!base.CanInsert(uid, component, entity)) - return false; - - return _containerSystem.CanInsert(entity, component.Container); - } - /// /// If something is inserted (or the likes) then we'll queue up an automatic flush in the future. /// - public void QueueAutomaticEngage(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null) + public void QueueAutomaticEngage(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null) { - if (component.Deleted || !component.AutomaticEngage || !component.Powered && component.Container.ContainedEntities.Count == 0) + if (component.Deleted || !component.AutomaticEngage || !_power.IsPowered(uid) && component.Container.ContainedEntities.Count == 0) { return; } @@ -771,53 +715,74 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem component.NextFlush = flushTime; Dirty(uid, component); + UpdateUI((uid, component)); } - public void AfterInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid inserted, EntityUid? user = null, bool doInsert = false) + private void OnUiButtonPressed(EntityUid uid, DisposalUnitComponent component, DisposalUnitComponent.UiButtonPressedMessage args) { - _audioSystem.PlayPvs(component.InsertSound, uid); + if (args.Actor is not { Valid: true } player) + { + return; + } - if (doInsert && !_containerSystem.Insert(inserted, component.Container)) + switch (args.Button) + { + case DisposalUnitComponent.UiButton.Eject: + TryEjectContents(uid, component); + _adminLog.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit eject button on {ToPrettyString(uid)}"); + break; + case DisposalUnitComponent.UiButton.Engage: + ToggleEngage(uid, component); + _adminLog.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit flush button on {ToPrettyString(uid)}, it's now {(component.Engaged ? "on" : "off")}"); + break; + case DisposalUnitComponent.UiButton.Power: + _power.TogglePower(uid, user: args.Actor); + break; + default: + throw new ArgumentOutOfRangeException($"{ToPrettyString(player):player} attempted to hit a nonexistant button on {ToPrettyString(uid)}"); + } + } + + public void ToggleEngage(EntityUid uid, DisposalUnitComponent component) + { + component.Engaged ^= true; + + if (component.Engaged) + { + ManualEngage(uid, component); + } + else + { + Disengage(uid, component); + } + } + + private void AddClimbInsideVerb(EntityUid uid, DisposalUnitComponent component, GetVerbsEvent args) + { + // 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 (!args.CanAccess || + !args.CanInteract || + component.Container.ContainedEntities.Contains(args.User) || + !ActionBlockerSystem.CanMove(args.User)) + { + return; + } + + if (!CanInsert(uid, component, args.User)) return; - if (user != inserted && user != null) - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user.Value):player} inserted {ToPrettyString(inserted)} into {ToPrettyString(uid)}"); - - QueueAutomaticEngage(uid, component); - - _ui.CloseUi(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, inserted); - - // Maybe do pullable instead? Eh still fine. - Joints.RecursiveClearJoints(inserted); - UpdateVisualState(uid, component); - } - - private void OnExploded(Entity ent, ref BeforeExplodeEvent args) - { - args.Contents.AddRange(ent.Comp.Container.ContainedEntities); - } - -} - -/// -/// Sent before the disposal unit flushes it's contents. -/// Allows adding tags for sorting and preventing the disposal unit from flushing. -/// -public sealed class DisposalUnitUIStateUpdatedEvent : EntityEventArgs -{ - public SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState State; - - public DisposalUnitUIStateUpdatedEvent(SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState state) - { - State = state; + // Add verb to climb inside of the unit, + Verb verb = new() + { + Act = () => TryInsert(uid, args.User, args.User), + DoContactInteraction = true, + Text = Loc.GetString("disposal-self-insert-verb-get-data-text") + }; + // TODO VERB ICON + // TODO VERB CATEGORY + // create a verb category for "enter"? + // See also, medical scanner. Also maybe add verbs for entering lockers/body bags? + args.Verbs.Add(verb); } } - -/// -/// Sent before the disposal unit flushes it's contents. -/// Allows adding tags for sorting and preventing the disposal unit from flushing. -/// -public sealed class BeforeDisposalFlushEvent : CancellableEntityEventArgs -{ - public readonly List Tags = new(); -} diff --git a/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs b/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs index 69c3bd790e..80bacd24bd 100644 --- a/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs +++ b/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs @@ -8,9 +8,15 @@ public abstract partial class SharedApcPowerReceiverComponent : Component [ViewVariables] public bool Powered; - [ViewVariables] - public virtual bool NeedsPower { get; set; } + /// + /// When false, causes this to appear powered even if not receiving power from an Apc. + /// + [ViewVariables(VVAccess.ReadWrite)] + public virtual bool NeedsPower { get; set;} - [ViewVariables] + /// + /// When true, causes this to never appear powered. + /// + [ViewVariables(VVAccess.ReadWrite)] public virtual bool PowerDisabled { get; set; } } diff --git a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs index 2d152d8b45..d86273974b 100644 --- a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs +++ b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs @@ -62,8 +62,13 @@ public abstract class SharedPowerReceiverSystem : EntitySystem return !receiver.PowerDisabled; // i.e. PowerEnabled } - /// - /// Checks if entity is APC-powered device, and if it have power. + protected virtual void RaisePower(Entity entity) + { + // NOOP on server because client has 0 idea of load so we can't raise it properly in shared. + } + + /// + /// Checks if entity is APC-powered device, and if it have power. /// public bool IsPowered(Entity entity) { diff --git a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs index 93c4b69e4d..d0ad27eee5 100644 --- a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs +++ b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs @@ -1,5 +1,7 @@ using System.Linq; using Content.Shared.Disposal; +using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Unit; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared.Item; @@ -40,7 +42,7 @@ public sealed class DumpableSystem : EntitySystem if (!args.CanReach || args.Handled) return; - if (!_disposalUnitSystem.HasDisposals(args.Target) && !HasComp(args.Target)) + if (!HasComp(args.Target) && !HasComp(args.Target)) return; if (!TryComp(uid, out var storage)) @@ -81,7 +83,7 @@ public sealed class DumpableSystem : EntitySystem if (!TryComp(uid, out var storage) || !storage.Container.ContainedEntities.Any()) return; - if (_disposalUnitSystem.HasDisposals(args.Target)) + if (HasComp(args.Target)) { UtilityVerb verb = new() { @@ -146,7 +148,7 @@ public sealed class DumpableSystem : EntitySystem var dumped = false; - if (_disposalUnitSystem.HasDisposals(args.Args.Target)) + if (HasComp(args.Args.Target)) { dumped = true; diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index cb3ae3d065..2605af8019 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -32,8 +32,6 @@ components: - HumanoidAppearance - type: DisposalUnit - autoEngageEnabled: false - noUI: true blacklist: components: - HumanoidAppearance diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml index 9d04b32563..7388a814f7 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml @@ -8,6 +8,8 @@ snap: - Disposal components: + - type: Climbable + vaultable: false - type: Sprite sprite: Structures/Piping/disposal.rsi layers: @@ -29,6 +31,17 @@ map: [ "enum.DisposalUnitVisualLayers.OverlayEngaged" ] - type: Physics bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.45,-0.45,0.45,0.45" + density: 55 + mask: + - TableMask + layer: + - TableLayer - type: Destructible thresholds: - trigger: @@ -116,7 +129,6 @@ graph: DisposalMachine node: mailing_unit - type: DisposalUnit - autoEngageEnabled: false whitelist: components: - Item @@ -136,6 +148,6 @@ - type: UserInterface interfaces: enum.MailingUnitUiKey.Key: - type: DisposalUnitBoundUserInterface + type: MailingUnitBoundUserInterface enum.ConfigurationUiKey.Key: type: ConfigurationBoundUserInterface From 4682149e749adff82749e2402bccb6872ba281d3 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 19 Apr 2025 16:51:12 +1000 Subject: [PATCH 08/16] Predict virtual hands and co (#36617) These are the easy ones anything else gets slightly spicier. --- .../Interaction/SharedInteractionSystem.cs | 13 ++++---- .../VirtualItem/SharedVirtualItemSystem.cs | 21 ++----------- .../EntitySystems/EncryptionKeySystem.cs | 8 +---- .../Systems/TechnologyDiskSystem.cs | 3 +- .../Wieldable/SharedWieldableSystem.cs | 30 ++++++++----------- 5 files changed, 23 insertions(+), 52 deletions(-) diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index eeb961537b..494fbdf032 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -51,7 +51,6 @@ namespace Content.Shared.Interaction public abstract partial class SharedInteractionSystem : EntitySystem { [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; @@ -223,24 +222,24 @@ namespace Content.Shared.Interaction { if (!item.DeleteOnDrop) RemCompDeferred(uid); - else if (_net.IsServer) - QueueDel(uid); + else + PredictedQueueDel(uid); } private void OnUnequipHand(EntityUid uid, UnremoveableComponent item, GotUnequippedHandEvent args) { if (!item.DeleteOnDrop) RemCompDeferred(uid); - else if (_net.IsServer) - QueueDel(uid); + else + PredictedQueueDel(uid); } private void OnDropped(EntityUid uid, UnremoveableComponent item, DroppedEvent args) { if (!item.DeleteOnDrop) RemCompDeferred(uid); - else if (_net.IsServer) - QueueDel(uid); + else + PredictedQueueDel(uid); } private bool HandleTryPullObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid) diff --git a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs index 9eac60adc4..393a4c09eb 100644 --- a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs +++ b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs @@ -157,11 +157,6 @@ public abstract class SharedVirtualItemSystem : EntitySystem /// public void DeleteInHandsMatching(EntityUid user, EntityUid matching) { - // Client can't currently predict deleting networked entities so we use this workaround, another - // problem can popup when the hands leave PVS for example and this avoids that too - if (_netManager.IsClient) - return; - foreach (var hand in _handsSystem.EnumerateHands(user)) { if (TryComp(hand.HeldEntity, out VirtualItemComponent? virt) && virt.BlockingEntity == matching) @@ -206,11 +201,6 @@ public abstract class SharedVirtualItemSystem : EntitySystem /// Set this param if you have the name of the slot, it avoids unnecessary queries public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slotName = null) { - // Client can't currently predict deleting networked entities so we use this workaround, another - // problem can popup when the hands leave PVS for example and this avoids that too - if (_netManager.IsClient) - return; - if (slotName != null) { if (!_inventorySystem.TryGetSlotEntity(user, slotName, out var slotEnt)) @@ -244,14 +234,8 @@ public abstract class SharedVirtualItemSystem : EntitySystem /// The virtual item, if spawned public bool TrySpawnVirtualItem(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem) { - if (_netManager.IsClient) - { - virtualItem = null; - return false; - } - var pos = Transform(user).Coordinates; - virtualItem = Spawn(VirtualItem, pos); + virtualItem = PredictedSpawnAttachedTo(VirtualItem, pos); var virtualItemComp = Comp(virtualItem.Value); virtualItemComp.BlockingEntity = blockingEnt; Dirty(virtualItem.Value, virtualItemComp); @@ -273,7 +257,6 @@ public abstract class SharedVirtualItemSystem : EntitySystem return; _transformSystem.DetachEntity(item, Transform(item)); - if (_netManager.IsServer) - QueueDel(item); + PredictedQueueDel(item); } } diff --git a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs index 9ddcb423b4..e63c6ac7b7 100644 --- a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs +++ b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs @@ -58,13 +58,7 @@ public sealed partial class EncryptionKeySystem : EntitySystem _hands.PickupOrDrop(args.User, ent, dropNear: true); } - if (!_timing.IsFirstTimePredicted) - return; - - // TODO add predicted pop-up overrides. - if (_net.IsServer) - _popup.PopupEntity(Loc.GetString("encryption-keys-all-extracted"), uid, args.User); - + _popup.PopupPredicted(Loc.GetString("encryption-keys-all-extracted"), uid, args.User); _audio.PlayPredicted(component.KeyExtractionSound, uid, args.User); } diff --git a/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs b/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs index 93c7c22471..4ca4728681 100644 --- a/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs +++ b/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs @@ -74,8 +74,7 @@ public sealed class TechnologyDiskSystem : EntitySystem } } _popup.PopupClient(Loc.GetString("tech-disk-inserted"), target, args.User); - if (_net.IsServer) - QueueDel(ent); + PredictedQueueDel(ent); args.Handled = true; } diff --git a/Content.Shared/Wieldable/SharedWieldableSystem.cs b/Content.Shared/Wieldable/SharedWieldableSystem.cs index b4a6144405..d3b8c4e4c6 100644 --- a/Content.Shared/Wieldable/SharedWieldableSystem.cs +++ b/Content.Shared/Wieldable/SharedWieldableSystem.cs @@ -21,6 +21,7 @@ using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; using Content.Shared.Wieldable.Components; using Robust.Shared.Audio.Systems; +using Robust.Shared.Collections; using Robust.Shared.Network; using Robust.Shared.Timing; @@ -260,26 +261,21 @@ public abstract class SharedWieldableSystem : EntitySystem _audio.PlayPredicted(component.WieldSound, used, user); //This section handles spawning the virtual item(s) to occupy the required additional hand(s). - //Since the client can't currently predict entity spawning, only do this if this is running serverside. - //Remove this check if TrySpawnVirtualItem in SharedVirtualItemSystem is allowed to complete clientside. - if (_netManager.IsServer) + var virtuals = new ValueList(); + for (var i = 0; i < component.FreeHandsRequired; i++) { - var virtuals = new List(); - for (var i = 0; i < component.FreeHandsRequired; i++) + if (_virtualItem.TrySpawnVirtualItemInHand(used, user, out var virtualItem, true)) { - if (_virtualItem.TrySpawnVirtualItemInHand(used, user, out var virtualItem, true)) - { - virtuals.Add(virtualItem.Value); - continue; - } - - foreach (var existingVirtual in virtuals) - { - QueueDel(existingVirtual); - } - - return false; + virtuals.Add(virtualItem.Value); + continue; } + + foreach (var existingVirtual in virtuals) + { + QueueDel(existingVirtual); + } + + return false; } var selfMessage = Loc.GetString("wieldable-component-successful-wield", ("item", used)); From 694190f2a4fe60c0c4dcd080550f202cdc2c0384 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 19 Apr 2025 06:52:19 +0000 Subject: [PATCH 09/16] 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 8222a7943f..584aefc415 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: crazybrain - changes: - - message: Bluespace lockers and quantum spin inverters can no longer go on the - arrivals shuttle. - type: Tweak - id: 7754 - time: '2024-12-27T12:34:31.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/34072 - author: Plykiya changes: - message: You now see a popup when being cuffed or uncuffed again. @@ -3915,3 +3907,10 @@ id: 8254 time: '2025-04-19T05:14:50.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/32920 +- author: metalgearsloth + changes: + - message: Virtual items (e.g. wielding) are now predicted. + type: Add + id: 8255 + time: '2025-04-19T06:51:12.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36617 From 64cd1805569f39eb23ad436bbdd1dd02f35364ff Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:29:32 +1000 Subject: [PATCH 10/16] Update submodule to 254.1.0 (#36711) * Update submodule to 254.1.0 * API update --- .../Inventory/VirtualItem/SharedVirtualItemSystem.cs | 3 +-- .../Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs | 2 +- RobustToolbox | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs index 393a4c09eb..256a5ec2a9 100644 --- a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs +++ b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs @@ -256,7 +256,6 @@ public abstract class SharedVirtualItemSystem : EntitySystem if (TerminatingOrDeleted(item)) return; - _transformSystem.DetachEntity(item, Transform(item)); - PredictedQueueDel(item); + PredictedQueueDel(item.Owner); } } diff --git a/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs b/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs index 4ca4728681..8caf1c39a6 100644 --- a/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs +++ b/Content.Shared/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs @@ -74,7 +74,7 @@ public sealed class TechnologyDiskSystem : EntitySystem } } _popup.PopupClient(Loc.GetString("tech-disk-inserted"), target, args.User); - PredictedQueueDel(ent); + PredictedQueueDel(ent.Owner); args.Handled = true; } diff --git a/RobustToolbox b/RobustToolbox index b146b1b82c..191d7ab81c 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit b146b1b82cda98f9445e7a9e2146e569e6cf4197 +Subproject commit 191d7ab81c3693503bc9f483c5829228d9cb57cf From 9bdd7307fdd3e422e95a4987208498f36ec4c6c6 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 19 Apr 2025 18:05:20 +1000 Subject: [PATCH 11/16] Predicted multihanded component (#36712) * Predicted multihanded component * Refview * reh --- .../Items/Systems/MultiHandedItemSystem.cs | 15 ----- Content.Server/Item/MultiHandedItemSystem.cs | 24 -------- .../Item/MultiHandedItemComponent.cs | 2 +- Content.Shared/Item/MultiHandedItemSystem.cs | 56 +++++++++++++++++++ .../Item/SharedMultiHandedItemSystem.cs | 47 ---------------- 5 files changed, 57 insertions(+), 87 deletions(-) delete mode 100644 Content.Client/Items/Systems/MultiHandedItemSystem.cs delete mode 100644 Content.Server/Item/MultiHandedItemSystem.cs create mode 100644 Content.Shared/Item/MultiHandedItemSystem.cs delete mode 100644 Content.Shared/Item/SharedMultiHandedItemSystem.cs diff --git a/Content.Client/Items/Systems/MultiHandedItemSystem.cs b/Content.Client/Items/Systems/MultiHandedItemSystem.cs deleted file mode 100644 index 716a4ad1a4..0000000000 --- a/Content.Client/Items/Systems/MultiHandedItemSystem.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Content.Shared.Hands; -using Content.Shared.Item; - -namespace Content.Client.Items.Systems; - -public sealed class MultiHandedItemSystem : SharedMultiHandedItemSystem -{ - protected override void OnEquipped(EntityUid uid, MultiHandedItemComponent component, GotEquippedHandEvent args) - { - } - - protected override void OnUnequipped(EntityUid uid, MultiHandedItemComponent component, GotUnequippedHandEvent args) - { - } -} diff --git a/Content.Server/Item/MultiHandedItemSystem.cs b/Content.Server/Item/MultiHandedItemSystem.cs deleted file mode 100644 index 9146c7c982..0000000000 --- a/Content.Server/Item/MultiHandedItemSystem.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Content.Server.Hands.Systems; -using Content.Server.Inventory; -using Content.Shared.Hands; -using Content.Shared.Item; - -namespace Content.Server.Item; - -public sealed class MultiHandedItemSystem : SharedMultiHandedItemSystem -{ - [Dependency] private readonly VirtualItemSystem _virtualItem = default!; - - protected override void OnEquipped(EntityUid uid, MultiHandedItemComponent component, GotEquippedHandEvent args) - { - for (var i = 0; i < component.HandsNeeded - 1; i++) - { - _virtualItem.TrySpawnVirtualItemInHand(uid, args.User); - } - } - - protected override void OnUnequipped(EntityUid uid, MultiHandedItemComponent component, GotUnequippedHandEvent args) - { - _virtualItem.DeleteInHandsMatching(args.User, uid); - } -} diff --git a/Content.Shared/Item/MultiHandedItemComponent.cs b/Content.Shared/Item/MultiHandedItemComponent.cs index 9a90d063ba..3a0ac23bcd 100644 --- a/Content.Shared/Item/MultiHandedItemComponent.cs +++ b/Content.Shared/Item/MultiHandedItemComponent.cs @@ -9,6 +9,6 @@ namespace Content.Shared.Item; [RegisterComponent, NetworkedComponent] public sealed partial class MultiHandedItemComponent : Component { - [DataField("handsNeeded"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public int HandsNeeded = 2; } diff --git a/Content.Shared/Item/MultiHandedItemSystem.cs b/Content.Shared/Item/MultiHandedItemSystem.cs new file mode 100644 index 0000000000..da9d895dd2 --- /dev/null +++ b/Content.Shared/Item/MultiHandedItemSystem.cs @@ -0,0 +1,56 @@ +using Content.Shared.Hands; +using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Inventory.VirtualItem; +using Content.Shared.Popups; +using Robust.Shared.Timing; + +namespace Content.Shared.Item; + +public sealed class MultiHandedItemSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedVirtualItemSystem _virtualItem = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnAttemptPickup); + SubscribeLocalEvent(OnVirtualItemDeleted); + SubscribeLocalEvent(OnEquipped); + SubscribeLocalEvent(OnUnequipped); + } + + private void OnEquipped(Entity ent, ref GotEquippedHandEvent args) + { + for (var i = 0; i < ent.Comp.HandsNeeded - 1; i++) + { + _virtualItem.TrySpawnVirtualItemInHand(ent.Owner, args.User); + } + } + + private void OnUnequipped(Entity ent, ref GotUnequippedHandEvent args) + { + _virtualItem.DeleteInHandsMatching(args.User, ent.Owner); + } + + private void OnAttemptPickup(Entity ent, ref GettingPickedUpAttemptEvent args) + { + if (TryComp(args.User, out var hands) && hands.CountFreeHands() >= ent.Comp.HandsNeeded) + return; + + args.Cancel(); + _popup.PopupPredictedCursor(Loc.GetString("multi-handed-item-pick-up-fail", + ("number", ent.Comp.HandsNeeded - 1), ("item", ent.Owner)), args.User); + } + + private void OnVirtualItemDeleted(Entity ent, ref VirtualItemDeletedEvent args) + { + if (args.BlockingEntity != ent.Owner || _timing.ApplyingState) + return; + + _hands.TryDrop(args.User, ent.Owner); + } +} diff --git a/Content.Shared/Item/SharedMultiHandedItemSystem.cs b/Content.Shared/Item/SharedMultiHandedItemSystem.cs deleted file mode 100644 index 0259b361b5..0000000000 --- a/Content.Shared/Item/SharedMultiHandedItemSystem.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Content.Shared.Hands; -using Content.Shared.Hands.Components; -using Content.Shared.Hands.EntitySystems; -using Content.Shared.Popups; -using Robust.Shared.Timing; - -namespace Content.Shared.Item; - -public abstract class SharedMultiHandedItemSystem : EntitySystem -{ - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnAttemptPickup); - SubscribeLocalEvent(OnVirtualItemDeleted); - SubscribeLocalEvent(OnEquipped); - SubscribeLocalEvent(OnUnequipped); - } - - protected abstract void OnEquipped(EntityUid uid, MultiHandedItemComponent component, GotEquippedHandEvent args); - protected abstract void OnUnequipped(EntityUid uid, MultiHandedItemComponent component, GotUnequippedHandEvent args); - - private void OnAttemptPickup(EntityUid uid, MultiHandedItemComponent component, GettingPickedUpAttemptEvent args) - { - if (TryComp(args.User, out var hands) && hands.CountFreeHands() >= component.HandsNeeded) - return; - - args.Cancel(); - if (_timing.IsFirstTimePredicted) - { - _popup.PopupCursor(Loc.GetString("multi-handed-item-pick-up-fail", - ("number", component.HandsNeeded - 1), ("item", uid)), args.User); - } - } - - private void OnVirtualItemDeleted(EntityUid uid, MultiHandedItemComponent component, VirtualItemDeletedEvent args) - { - if (args.BlockingEntity != uid) - return; - - _hands.TryDrop(args.User, uid); - } -} From bffd951867cbcab08abf296ebcbfb0067bf6d98c Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 19 Apr 2025 18:49:02 +1000 Subject: [PATCH 12/16] Fix charges (#36714) We have the absolute value so use that. --- Content.Shared/Charges/Systems/SharedChargesSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Charges/Systems/SharedChargesSystem.cs b/Content.Shared/Charges/Systems/SharedChargesSystem.cs index 2eb05f8bfc..4805e5a441 100644 --- a/Content.Shared/Charges/Systems/SharedChargesSystem.cs +++ b/Content.Shared/Charges/Systems/SharedChargesSystem.cs @@ -118,7 +118,7 @@ public abstract class SharedChargesSystem : EntitySystem action.Comp.LastUpdate = _timing.CurTime; } - action.Comp.LastCharges = Math.Clamp(action.Comp.LastCharges + addCharges, 0, action.Comp.MaxCharges); + action.Comp.LastCharges = Math.Clamp(charges, 0, action.Comp.MaxCharges); Dirty(action); } From c45f291425ed7414d474c6fe0bf7d3f723fd318d Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 19 Apr 2025 08:50:09 +0000 Subject: [PATCH 13/16] 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 584aefc415..1488fb5207 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Plykiya - changes: - - message: You now see a popup when being cuffed or uncuffed again. - type: Fix - id: 7755 - time: '2024-12-27T13:34:32.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33639 - author: Alpaccalypse changes: - message: Power monitoring computer boards can no longer be researched or printed, @@ -3914,3 +3907,10 @@ id: 8255 time: '2025-04-19T06:51:12.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36617 +- author: metalgearsloth + changes: + - message: Fix charges sometimes breaking for auto-recharge devices such as RCDs. + type: Fix + id: 8256 + time: '2025-04-19T08:49:02.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36714 From 49da5c540b2681bc847bf1bad88e10ef0e666839 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sat, 19 Apr 2025 22:17:03 +1000 Subject: [PATCH 14/16] Try fix RestartTest (#36725) --- .../Tests/GameRules/FailAndStartPresetTest.cs | 4 ++-- Content.Server/GameTicking/GameTicker.GamePreset.cs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs index 3109df890a..b9a02339fb 100644 --- a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs @@ -85,7 +85,7 @@ public sealed class FailAndStartPresetTest Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay)); // Try to start nukeops without readying up - await pair.WaitCommand("setgamepreset TestPresetTenPlayers"); + await pair.WaitCommand("setgamepreset TestPresetTenPlayers 9999"); await pair.WaitCommand("startround"); await pair.RunTicksSync(10); @@ -99,7 +99,7 @@ public sealed class FailAndStartPresetTest // Ready up and start nukeops await pair.WaitClientCommand("toggleready True"); Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.ReadyToPlay)); - await pair.WaitCommand("setgamepreset TestPreset"); + await pair.WaitCommand("setgamepreset TestPreset 9999"); await pair.WaitCommand("startround"); await pair.RunTicksSync(10); diff --git a/Content.Server/GameTicking/GameTicker.GamePreset.cs b/Content.Server/GameTicking/GameTicker.GamePreset.cs index 14985051e7..c062b95361 100644 --- a/Content.Server/GameTicking/GameTicker.GamePreset.cs +++ b/Content.Server/GameTicking/GameTicker.GamePreset.cs @@ -109,7 +109,11 @@ public sealed partial class GameTicker // Reset counter is checked and changed at the end of each round // So if the game is in the lobby, the first requested round will happen before the check, and we need one less check if (CurrentPreset is null) - ResetCountdown = resetDelay.Value -1; + ResetCountdown = resetDelay.Value - 1; + } + else + { + ResetCountdown = null; } Preset = preset; From e9b49b00efb1d6ad604f67a0276e5798f5877856 Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Sat, 19 Apr 2025 08:37:10 -0400 Subject: [PATCH 15/16] Fix a minor warning in BatterySystem.cs (#36726) change timing from protected to private, rename, and adjust references. --- Content.Server/Power/EntitySystems/BatterySystem.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Content.Server/Power/EntitySystems/BatterySystem.cs b/Content.Server/Power/EntitySystems/BatterySystem.cs index 6e636622e6..89e3ed2c1f 100644 --- a/Content.Server/Power/EntitySystems/BatterySystem.cs +++ b/Content.Server/Power/EntitySystems/BatterySystem.cs @@ -13,7 +13,7 @@ namespace Content.Server.Power.EntitySystems [UsedImplicitly] public sealed class BatterySystem : EntitySystem { - [Dependency] protected readonly IGameTiming Timing = default!; + [Dependency] private readonly IGameTiming _timing = default!; public override void Initialize() { @@ -92,7 +92,7 @@ namespace Content.Server.Power.EntitySystems if (comp.AutoRechargePause) { - if (comp.NextAutoRecharge > Timing.CurTime) + if (comp.NextAutoRecharge > _timing.CurTime) continue; } @@ -179,7 +179,7 @@ namespace Content.Server.Power.EntitySystems if (value < 0) value = batteryself.AutoRechargePauseTime; - if (Timing.CurTime + TimeSpan.FromSeconds(value) <= batteryself.NextAutoRecharge) + if (_timing.CurTime + TimeSpan.FromSeconds(value) <= batteryself.NextAutoRecharge) return; SetChargeCooldown(uid, batteryself.AutoRechargePauseTime, batteryself); @@ -194,9 +194,9 @@ namespace Content.Server.Power.EntitySystems return; if (value >= 0) - batteryself.NextAutoRecharge = Timing.CurTime + TimeSpan.FromSeconds(value); + batteryself.NextAutoRecharge = _timing.CurTime + TimeSpan.FromSeconds(value); else - batteryself.NextAutoRecharge = Timing.CurTime; + batteryself.NextAutoRecharge = _timing.CurTime; } /// From b435d0f2e67b0c0990b124539a1d7ee757781f90 Mon Sep 17 00:00:00 2001 From: Boaz1111 <149967078+Boaz1111@users.noreply.github.com> Date: Sat, 19 Apr 2025 15:10:43 +0200 Subject: [PATCH 16/16] Reduces handheld security radios range for picking people's messages up. (#34878) weh --- Resources/Prototypes/Entities/Objects/Devices/radio.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Objects/Devices/radio.yml b/Resources/Prototypes/Entities/Objects/Devices/radio.yml index 84e15878af..adf094d12e 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/radio.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/radio.yml @@ -33,6 +33,7 @@ components: - type: RadioMicrophone broadcastChannel: Security + listenRange: 1 - type: RadioSpeaker channels: - Security