diff --git a/Content.Client/Clothing/MagbootsSystem.cs b/Content.Client/Clothing/MagbootsSystem.cs deleted file mode 100644 index a3d39eafde..0000000000 --- a/Content.Client/Clothing/MagbootsSystem.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Clothing; - -namespace Content.Client.Clothing; - -public sealed class MagbootsSystem : SharedMagbootsSystem -{ - -} diff --git a/Content.Server/Atmos/Components/MovedByPressureComponent.cs b/Content.Server/Atmos/Components/MovedByPressureComponent.cs deleted file mode 100644 index ca830767bd..0000000000 --- a/Content.Server/Atmos/Components/MovedByPressureComponent.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Content.Server.Atmos.Components -{ - // Unfortunately can't be friends yet due to magboots. - [RegisterComponent] - public sealed partial class MovedByPressureComponent : Component - { - public const float MoveForcePushRatio = 1f; - public const float MoveForceForcePushRatio = 1f; - public const float ProbabilityOffset = 25f; - public const float ProbabilityBasePercent = 10f; - public const float ThrowForce = 100f; - - /// - /// Accumulates time when yeeted by high pressure deltas. - /// - [DataField("accumulator")] - public float Accumulator = 0f; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("enabled")] - public bool Enabled { get; set; } = true; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("pressureResistance")] - public float PressureResistance { get; set; } = 1f; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("moveResist")] - public float MoveResist { get; set; } = 100f; - [ViewVariables(VVAccess.ReadWrite)] - public int LastHighPressureMovementAirCycle { get; set; } = 0; - } -} diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs index cb50ff114e..2e41e45d3f 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs @@ -1,5 +1,6 @@ using Content.Server.Atmos.Components; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; using Content.Shared.Mobs.Components; using Content.Shared.Physics; using Robust.Shared.Audio; diff --git a/Content.Server/Clothing/MagbootsSystem.cs b/Content.Server/Clothing/MagbootsSystem.cs deleted file mode 100644 index 3838ad168d..0000000000 --- a/Content.Server/Clothing/MagbootsSystem.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Content.Server.Atmos.Components; -using Content.Shared.Alert; -using Content.Shared.Clothing; - -namespace Content.Server.Clothing; - -public sealed class MagbootsSystem : SharedMagbootsSystem -{ - [Dependency] private readonly AlertsSystem _alerts = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnGotEquipped); - SubscribeLocalEvent(OnGotUnequipped); - } - - protected override void UpdateMagbootEffects(EntityUid parent, EntityUid uid, bool state, MagbootsComponent? component) - { - if (!Resolve(uid, ref component)) - return; - state = state && component.On; - - if (TryComp(parent, out MovedByPressureComponent? movedByPressure)) - { - movedByPressure.Enabled = !state; - } - - if (state) - { - _alerts.ShowAlert(parent, component.MagbootsAlert); - } - else - { - _alerts.ClearAlert(parent, component.MagbootsAlert); - } - } - - private void OnGotUnequipped(EntityUid uid, MagbootsComponent component, ref ClothingGotUnequippedEvent args) - { - UpdateMagbootEffects(args.Wearer, uid, false, component); - } - - private void OnGotEquipped(EntityUid uid, MagbootsComponent component, ref ClothingGotEquippedEvent args) - { - UpdateMagbootEffects(args.Wearer, uid, true, component); - } -} diff --git a/Content.Server/Damage/Systems/GodmodeSystem.cs b/Content.Server/Damage/Systems/GodmodeSystem.cs index 404cc63905..d896fba71c 100644 --- a/Content.Server/Damage/Systems/GodmodeSystem.cs +++ b/Content.Server/Damage/Systems/GodmodeSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Atmos.Components; +using Content.Shared.Atmos.Components; using Content.Shared.Damage.Components; using Content.Shared.Damage.Systems; diff --git a/Content.Server/Gravity/GravitySystem.cs b/Content.Server/Gravity/GravitySystem.cs index ea62d4a819..6807b9df4a 100644 --- a/Content.Server/Gravity/GravitySystem.cs +++ b/Content.Server/Gravity/GravitySystem.cs @@ -1,7 +1,6 @@ using Content.Shared.Gravity; using JetBrains.Annotations; using Robust.Shared.Map.Components; -using Robust.Shared.Utility; namespace Content.Server.Gravity { diff --git a/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs b/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs index 779b2f5971..f53d658ebd 100644 --- a/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs +++ b/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs @@ -1,6 +1,6 @@ using System.Numerics; -using Content.Server.Atmos.Components; using Content.Server.Singularity.Components; +using Content.Shared.Atmos.Components; using Content.Shared.Ghost; using Content.Shared.Singularity.EntitySystems; using Robust.Shared.Map; diff --git a/Content.Shared/Atmos/Components/MovedByPressureComponent.cs b/Content.Shared/Atmos/Components/MovedByPressureComponent.cs new file mode 100644 index 0000000000..8a4e2c6d4c --- /dev/null +++ b/Content.Shared/Atmos/Components/MovedByPressureComponent.cs @@ -0,0 +1,31 @@ +namespace Content.Shared.Atmos.Components; + +// Unfortunately can't be friends yet due to magboots. +[RegisterComponent] +public sealed partial class MovedByPressureComponent : Component +{ + public const float MoveForcePushRatio = 1f; + public const float MoveForceForcePushRatio = 1f; + public const float ProbabilityOffset = 25f; + public const float ProbabilityBasePercent = 10f; + public const float ThrowForce = 100f; + + /// + /// Accumulates time when yeeted by high pressure deltas. + /// + [DataField] + public float Accumulator; + + [DataField] + public bool Enabled { get; set; } = true; + + [DataField] + public float PressureResistance { get; set; } = 1f; + + [DataField] + public float MoveResist { get; set; } = 100f; + + [ViewVariables(VVAccess.ReadWrite)] + public int LastHighPressureMovementAirCycle { get; set; } = 0; +} + diff --git a/Content.Shared/Clothing/Components/AntiGravityClothingComponent.cs b/Content.Shared/Clothing/Components/AntiGravityClothingComponent.cs new file mode 100644 index 0000000000..a8fcbdd2eb --- /dev/null +++ b/Content.Shared/Clothing/Components/AntiGravityClothingComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Clothing.Components; + +/// +/// This is used for clothing that makes an entity weightless when worn. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class AntiGravityClothingComponent : Component; diff --git a/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs new file mode 100644 index 0000000000..c5b2ee3dfc --- /dev/null +++ b/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs @@ -0,0 +1,23 @@ +using Content.Shared.Clothing.Components; +using Content.Shared.Gravity; +using Content.Shared.Inventory; + +namespace Content.Shared.Clothing.EntitySystems; + +public sealed class AntiGravityClothingSystem : EntitySystem +{ + /// + public override void Initialize() + { + SubscribeLocalEvent>(OnIsWeightless); + } + + private void OnIsWeightless(Entity ent, ref InventoryRelayedEvent args) + { + if (args.Args.Handled) + return; + + args.Args.Handled = true; + args.Args.IsWeightless = true; + } +} diff --git a/Content.Shared/Clothing/SharedMagbootsSystem.cs b/Content.Shared/Clothing/SharedMagbootsSystem.cs index 27fb0c1a50..bb3b05074f 100644 --- a/Content.Shared/Clothing/SharedMagbootsSystem.cs +++ b/Content.Shared/Clothing/SharedMagbootsSystem.cs @@ -1,5 +1,8 @@ using Content.Shared.Actions; +using Content.Shared.Alert; +using Content.Shared.Atmos.Components; using Content.Shared.Clothing.EntitySystems; +using Content.Shared.Gravity; using Content.Shared.Inventory; using Content.Shared.Item; using Content.Shared.Slippery; @@ -9,8 +12,9 @@ using Robust.Shared.Containers; namespace Content.Shared.Clothing; -public abstract class SharedMagbootsSystem : EntitySystem +public sealed class SharedMagbootsSystem : EntitySystem { + [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly ClothingSpeedModifierSystem _clothingSpeedModifier = default!; [Dependency] private readonly ClothingSystem _clothing = default!; [Dependency] private readonly InventorySystem _inventory = default!; @@ -29,6 +33,11 @@ public abstract class SharedMagbootsSystem : EntitySystem SubscribeLocalEvent(OnGetActions); SubscribeLocalEvent(OnToggleMagboots); SubscribeLocalEvent(OnMapInit); + + SubscribeLocalEvent(OnGotEquipped); + SubscribeLocalEvent(OnGotUnequipped); + + SubscribeLocalEvent>(OnIsWeightless); } private void OnMapInit(EntityUid uid, MagbootsComponent component, MapInitEvent args) @@ -37,6 +46,16 @@ public abstract class SharedMagbootsSystem : EntitySystem Dirty(uid, component); } + private void OnGotUnequipped(EntityUid uid, MagbootsComponent component, ref ClothingGotUnequippedEvent args) + { + UpdateMagbootEffects(args.Wearer, uid, false, component); + } + + private void OnGotEquipped(EntityUid uid, MagbootsComponent component, ref ClothingGotEquippedEvent args) + { + UpdateMagbootEffects(args.Wearer, uid, true, component); + } + private void OnToggleMagboots(EntityUid uid, MagbootsComponent component, ToggleMagbootsEvent args) { if (args.Handled) @@ -51,9 +70,11 @@ public abstract class SharedMagbootsSystem : EntitySystem { magboots.On = !magboots.On; - if (_sharedContainer.TryGetContainingContainer(uid, out var container) && + if (_sharedContainer.TryGetContainingContainer((uid, Transform(uid)), out var container) && _inventory.TryGetSlotEntity(container.Owner, "shoes", out var entityUid) && entityUid == uid) + { UpdateMagbootEffects(container.Owner, uid, true, magboots); + } if (TryComp(uid, out var item)) { @@ -66,9 +87,28 @@ public abstract class SharedMagbootsSystem : EntitySystem Dirty(uid, magboots); } - protected virtual void UpdateMagbootEffects(EntityUid parent, EntityUid uid, bool state, MagbootsComponent? component) { } + public void UpdateMagbootEffects(EntityUid parent, EntityUid uid, bool state, MagbootsComponent? component) + { + if (!Resolve(uid, ref component)) + return; + state = state && component.On; - protected void OnChanged(EntityUid uid, MagbootsComponent component) + if (TryComp(parent, out MovedByPressureComponent? movedByPressure)) + { + movedByPressure.Enabled = !state; + } + + if (state) + { + _alerts.ShowAlert(parent, component.MagbootsAlert); + } + else + { + _alerts.ClearAlert(parent, component.MagbootsAlert); + } + } + + private void OnChanged(EntityUid uid, MagbootsComponent component) { _sharedActions.SetToggled(component.ToggleActionEntity, component.On); _clothingSpeedModifier.SetClothingSpeedModifierEnabled(uid, component.On); @@ -79,10 +119,12 @@ public abstract class SharedMagbootsSystem : EntitySystem if (!args.CanAccess || !args.CanInteract) return; - ActivationVerb verb = new(); - verb.Text = Loc.GetString("toggle-magboots-verb-get-data-text"); - verb.Act = () => ToggleMagboots(uid, component); - // TODO VERB ICON add toggle icon? maybe a computer on/off symbol? + ActivationVerb verb = new() + { + Text = Loc.GetString("toggle-magboots-verb-get-data-text"), + Act = () => ToggleMagboots(uid, component), + // TODO VERB ICON add toggle icon? maybe a computer on/off symbol? + }; args.Verbs.Add(verb); } @@ -96,6 +138,18 @@ public abstract class SharedMagbootsSystem : EntitySystem { args.AddAction(ref component.ToggleActionEntity, component.ToggleAction); } + + private void OnIsWeightless(Entity ent, ref InventoryRelayedEvent args) + { + if (args.Args.Handled) + return; + + if (!ent.Comp.On) + return; + + args.Args.IsWeightless = false; + args.Args.Handled = true; + } } -public sealed partial class ToggleMagbootsEvent : InstantActionEvent {} +public sealed partial class ToggleMagbootsEvent : InstantActionEvent; diff --git a/Content.Shared/Gravity/SharedGravitySystem.cs b/Content.Shared/Gravity/SharedGravitySystem.cs index df13be51fd..42a6d5d1f8 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.cs @@ -1,9 +1,7 @@ using Content.Shared.Alert; -using Content.Shared.Clothing; using Content.Shared.Inventory; using Content.Shared.Movement.Components; using Robust.Shared.GameStates; -using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Serialization; @@ -15,7 +13,6 @@ namespace Content.Shared.Gravity { [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] private readonly AlertsSystem _alerts = default!; - [Dependency] private readonly InventorySystem _inventory = default!; [ValidatePrototypeId] public const string WeightlessAlert = "Weightless"; @@ -30,6 +27,11 @@ namespace Content.Shared.Gravity if (TryComp(uid, out var ignoreGravityComponent)) return ignoreGravityComponent.Weightless; + var ev = new IsWeightlessEvent(uid); + RaiseLocalEvent(uid, ref ev); + if (ev.Handled) + return ev.IsWeightless; + if (!Resolve(uid, ref xform)) return true; @@ -40,18 +42,6 @@ namespace Content.Shared.Gravity return false; } - var hasGrav = gravity != null || mapGravity != null; - - // Check for something holding us down - // If the planet has gravity component and no gravity it will still give gravity - // If there's no gravity comp at all (i.e. space) then they don't work. - if (hasGrav && _inventory.TryGetSlotEntity(uid, "shoes", out var ent)) - { - // TODO this should just be a event that gets relayed instead of a specific slot & component check. - if (TryComp(ent, out var boots) && boots.On) - return false; - } - return true; } @@ -74,9 +64,11 @@ namespace Content.Shared.Gravity private void OnHandleState(EntityUid uid, GravityComponent component, ref ComponentHandleState args) { - if (args.Current is not GravityComponentState state) return; + if (args.Current is not GravityComponentState state) + return; - if (component.EnabledVV == state.Enabled) return; + if (component.EnabledVV == state.Enabled) + return; component.EnabledVV = state.Enabled; var ev = new GravityChangedEvent(uid, component.EnabledVV); RaiseLocalEvent(uid, ref ev, true); @@ -90,9 +82,10 @@ namespace Content.Shared.Gravity private void OnGravityChange(ref GravityChangedEvent ev) { var alerts = AllEntityQuery(); - while(alerts.MoveNext(out var uid, out var comp, out var xform)) + while(alerts.MoveNext(out var uid, out _, out var xform)) { - if (xform.GridUid != ev.ChangedGridIndex) continue; + if (xform.GridUid != ev.ChangedGridIndex) + continue; if (!ev.HasGravity) { @@ -145,4 +138,10 @@ namespace Content.Shared.Gravity } } } + + [ByRefEvent] + public record struct IsWeightlessEvent(EntityUid Entity, bool IsWeightless = false, bool Handled = false) : IInventoryRelayEvent + { + SlotFlags IInventoryRelayEvent.TargetSlots => ~SlotFlags.POCKET; + } } diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index 6bd65622f1..ea368884e0 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -3,6 +3,7 @@ using Content.Shared.Damage; using Content.Shared.Electrocution; using Content.Shared.Explosion; using Content.Shared.Eye.Blinding.Systems; +using Content.Shared.Gravity; using Content.Shared.IdentityManagement.Components; using Content.Shared.Inventory.Events; using Content.Shared.Movement.Systems; @@ -30,6 +31,7 @@ public partial class InventorySystem // by-ref events SubscribeLocalEvent(RefRelayInventoryEvent); + SubscribeLocalEvent(RefRelayInventoryEvent); // Eye/vision events SubscribeLocalEvent(RelayInventoryEvent); diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml index cbea9d319d..22cd13af60 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml @@ -134,3 +134,21 @@ event: !type:ToggleClothingSpeedEvent icon: { sprite: Clothing/Shoes/Boots/speedboots.rsi, state: icon } iconOn: { sprite: Clothing/Shoes/Boots/speedboots.rsi, state: icon-on } + +- type: entity + parent: ClothingShoesBase + id: ClothingShoesBootsMoon + name: moon boots + description: Special anti-gravity boots developed with a speciality blend of lunar rock gel. Shipped from the Netherlands. + components: + - type: Sprite + sprite: Clothing/Shoes/Boots/moonboots.rsi + layers: + - state: icon + - type: Clothing + sprite: Clothing/Shoes/Boots/moonboots.rsi + - type: AntiGravityClothing + - type: StaticPrice + price: 75 + - type: Tag + tags: [ ] diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 0ef76381f0..66af73088d 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -208,7 +208,7 @@ - MagazineBoxRifle - MagazineLightRifle - MagazineLightRifleEmpty - - MagazinePistol + - MagazinePistol - MagazinePistolEmpty - MagazinePistolSubMachineGun - MagazinePistolSubMachineGunEmpty @@ -308,6 +308,7 @@ - WeaponGauntletGorilla - SynthesizerInstrument - ClothingShoesBootsMagSci + - ClothingShoesBootsMoon - ClothingShoesBootsSpeed - NodeScanner - HolofanProjector @@ -707,7 +708,7 @@ staticRecipes: - BoxLethalshot - BoxShotgunFlare - - BoxShotgunPractice + - BoxShotgunPractice - BoxShotgunSlug - ClothingEyesHudSecurity - CombatKnife @@ -724,7 +725,7 @@ - MagazineBoxRiflePractice - MagazineLightRifle - MagazineLightRifleEmpty - - MagazinePistol + - MagazinePistol - MagazinePistolEmpty - MagazinePistolSubMachineGun - MagazinePistolSubMachineGunEmpty @@ -837,8 +838,8 @@ - MagazineBoxRifle - MagazineLightRifle - MagazineLightRifleEmpty - - MagazinePistol - - MagazinePistolEmpty + - MagazinePistol + - MagazinePistolEmpty - MagazineRifle - MagazineRifleEmpty - MagazineShotgun diff --git a/Resources/Prototypes/Recipes/Lathes/misc.yml b/Resources/Prototypes/Recipes/Lathes/misc.yml index ab13dc4573..378392bae1 100644 --- a/Resources/Prototypes/Recipes/Lathes/misc.yml +++ b/Resources/Prototypes/Recipes/Lathes/misc.yml @@ -126,6 +126,13 @@ Steel: 1000 Plastic: 500 +- type: latheRecipe + id: ClothingShoesBootsMoon + result: ClothingShoesBootsMoon + completetime: 2 + materials: + Steel: 600 + - type: latheRecipe id: ClothingShoesBootsSpeed result: ClothingShoesBootsSpeed diff --git a/Resources/Prototypes/Research/experimental.yml b/Resources/Prototypes/Research/experimental.yml index 77adfcf5d5..34443e78f0 100644 --- a/Resources/Prototypes/Research/experimental.yml +++ b/Resources/Prototypes/Research/experimental.yml @@ -68,6 +68,7 @@ cost: 7500 recipeUnlocks: - ClothingShoesBootsMagSci + - ClothingShoesBootsMoon - type: technology id: AnomalyCoreHarnessing diff --git a/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/equipped-FEET-vox.png b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/equipped-FEET-vox.png new file mode 100644 index 0000000000..a65deca760 Binary files /dev/null and b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/equipped-FEET-vox.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/equipped-FEET.png b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/equipped-FEET.png new file mode 100644 index 0000000000..2b4cdf400d Binary files /dev/null and b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/equipped-FEET.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/icon.png b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/icon.png new file mode 100644 index 0000000000..04f662d6d3 Binary files /dev/null and b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/inhand-left.png b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/inhand-left.png new file mode 100644 index 0000000000..03bdacf9fb Binary files /dev/null and b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/inhand-right.png b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/inhand-right.png new file mode 100644 index 0000000000..f00d861ca5 Binary files /dev/null and b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/meta.json b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/meta.json new file mode 100644 index 0000000000..2c2d49e031 --- /dev/null +++ b/Resources/Textures/Clothing/Shoes/Boots/moonboots.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Tau Ceti Station at commit https://github.com/TauCetiStation/TauCetiClassic/blob/HEAD/icons/obj/clothing/shoes.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-FEET", + "directions": 4 + }, + { + "name": "equipped-FEET-vox", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +}