From 74541e23a46a827d55de91943b9595f2c05d7a4e Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Wed, 19 Sep 2018 18:54:04 +0200 Subject: [PATCH] Equipment & inhands. (#110) * Equipment WiP * Equipment's starting to work. * Equipment works properly 100% now. * Inhands work. Also more clothes. --- Content.Client/Content.Client.csproj | 4 +- Content.Client/EntryPoint.cs | 6 +- .../Components/Clothing/ClothingComponent.cs | 27 +++ .../Inventory/ClientInventoryComponent.cs | 156 ++++++++++++----- .../Components/Items/ClientHandsComponent.cs | 144 ++++++++++++++-- .../Components/Items/ItemComponent.cs | 60 +++++++ Content.Client/UserInterface/HandsGui.cs | 4 + Content.Server/EntryPoint.cs | 11 +- .../Components/GUI/InventoryComponent.cs | 62 ++++--- Content.Shared/Content.Shared.csproj | 2 +- .../Inventory/EquipmentSlotDefinitions.cs | 3 + .../Inventory/InventoryTemplates.cs | 47 +++++- .../Inventory/SharedInventoryComponent.cs | 22 +-- Resources/Prototypes/Entities/Clothing.yml | 159 +----------------- .../Prototypes/Entities/Clothing/IDs.yml | 13 ++ .../Entities/Clothing/backpacks.yml | 19 +++ .../Prototypes/Entities/Clothing/belts.yml | 26 +++ .../Prototypes/Entities/Clothing/ears.yml | 16 ++ .../Prototypes/Entities/Clothing/eyes.yml | 39 +++++ .../Prototypes/Entities/Clothing/gloves.yml | 39 +++++ .../Prototypes/Entities/Clothing/helmets.yml | 16 ++ .../Prototypes/Entities/Clothing/masks.yml | 35 ++++ .../Prototypes/Entities/Clothing/shoes.yml | 43 +++++ .../Prototypes/Entities/Clothing/suits.yml | 43 +++++ .../Prototypes/Entities/Clothing/uniforms.yml | 76 +++++++++ Resources/Prototypes/Entities/Mobs.yml | 24 ++- Resources/Scenes/Inventory/StorageSlot.tscn | 2 +- .../Textures/Clothing/armor.rsi/armor.png | Bin 0 -> 441 bytes .../armor.rsi/equipped-OUTERCLOTHING.png | Bin 0 -> 660 bytes .../Clothing/armor.rsi/inhand-left.png | Bin 0 -> 526 bytes .../Clothing/armor.rsi/inhand-right.png | Bin 0 -> 508 bytes .../Textures/Clothing/armor.rsi/meta.json | 1 + .../Clothing/backpack.rsi/backpack.png | Bin 0 -> 378 bytes .../backpack.rsi/equipped-BACKPACK.png | Bin 0 -> 638 bytes .../Clothing/backpack.rsi/inhand-left.png | Bin 0 -> 465 bytes .../Clothing/backpack.rsi/inhand-right.png | Bin 0 -> 455 bytes .../Textures/Clothing/backpack.rsi/meta.json | 1 + .../belt_utility.rsi/equipped-BELT.png | Bin 0 -> 294 bytes .../Clothing/belt_utility.rsi/meta.json | 1 + .../Clothing/belt_utility.rsi/utilitybelt.png | Bin 0 -> 463 bytes .../Clothing/gloves_black.rsi/black.png | Bin 0 -> 456 bytes .../gloves_black.rsi/equipped-HAND.png | Bin 0 -> 261 bytes .../Clothing/gloves_black.rsi/inhand-left.png | Bin 0 -> 205 bytes .../gloves_black.rsi/inhand-right.png | Bin 0 -> 214 bytes .../Clothing/gloves_black.rsi/meta.json | 1 + .../gloves_yellow.rsi/equipped-HAND.png | Bin 0 -> 280 bytes .../gloves_yellow.rsi/inhand-left.png | Bin 0 -> 221 bytes .../gloves_yellow.rsi/inhand-right.png | Bin 0 -> 232 bytes .../Clothing/gloves_yellow.rsi/meta.json | 1 + .../Clothing/gloves_yellow.rsi/yellow.png | Bin 0 -> 351 bytes .../Clothing/headset.rsi/equipped-EARS.png | Bin 0 -> 165 bytes .../Textures/Clothing/headset.rsi/headset.png | Bin 0 -> 298 bytes .../Textures/Clothing/headset.rsi/meta.json | 1 + .../helmet_sec.rsi/equipped-HELMET.png | Bin 0 -> 644 bytes .../Clothing/helmet_sec.rsi/helmet.png | Bin 0 -> 378 bytes .../Clothing/helmet_sec.rsi/meta.json | 1 + .../Clothing/mask_breath.rsi/breath.png | Bin 0 -> 307 bytes .../mask_breath.rsi/equipped-MASK.png | Bin 0 -> 378 bytes .../Clothing/mask_breath.rsi/meta.json | 1 + .../meson_scanners.rsi/equipped-EYES.png | Bin 0 -> 286 bytes .../Clothing/meson_scanners.rsi/meson.png | Bin 0 -> 329 bytes .../Clothing/meson_scanners.rsi/meta.json | 1 + .../Clothing/shoes_black.rsi/black.png | Bin 0 -> 324 bytes .../shoes_black.rsi/equipped-FEET.png | Bin 0 -> 374 bytes .../Clothing/shoes_black.rsi/meta.json | 1 + .../shoes_white.rsi/equipped-FEET.png | Bin 0 -> 424 bytes .../Clothing/shoes_white.rsi/meta.json | 1 + .../Clothing/shoes_white.rsi/white.png | Bin 0 -> 259 bytes .../Clothing/sunglasses.rsi/equipped-EYES.png | Bin 0 -> 187 bytes .../Clothing/sunglasses.rsi/meta.json | 1 + .../Textures/Clothing/sunglasses.rsi/sun.png | Bin 0 -> 226 bytes .../Clothing/sunglasses.rsi/sunglasses.png | Bin 0 -> 226 bytes .../uniform_assistant.rsi/assistant.png | Bin 0 -> 426 bytes .../equipped-INNERCLOTHING.png | Bin 0 -> 1209 bytes .../Clothing/uniform_assistant.rsi/meta.json | 1 + .../uniform_engineering.rsi/engine.png | Bin 0 -> 446 bytes .../equipped-INNERCLOTHING.png | Bin 0 -> 1469 bytes .../uniform_engineering.rsi/inhand-left.png | Bin 0 -> 509 bytes .../uniform_engineering.rsi/inhand-right.png | Bin 0 -> 532 bytes .../uniform_engineering.rsi/meta.json | 74 ++++++++ .../equipped-INNERCLOTHING.png | Bin 0 -> 993 bytes .../Clothing/uniform_grey.rsi/grey.png | Bin 0 -> 394 bytes .../Clothing/uniform_grey.rsi/inhand-left.png | Bin 0 -> 487 bytes .../uniform_grey.rsi/inhand-right.png | Bin 0 -> 507 bytes .../Clothing/uniform_grey.rsi/meta.json | 74 ++++++++ .../equipped-INNERCLOTHING.png | Bin 0 -> 1636 bytes .../uniform_janitor.rsi/inhand-left.png | Bin 0 -> 513 bytes .../uniform_janitor.rsi/inhand-right.png | Bin 0 -> 561 bytes .../Clothing/uniform_janitor.rsi/janitor.png | Bin 0 -> 456 bytes .../Clothing/uniform_janitor.rsi/meta.json | 74 ++++++++ .../equipped-OUTERCLOTHING.png | Bin 0 -> 757 bytes .../Clothing/vest_hazard.rsi/hazard.png | Bin 0 -> 448 bytes .../Clothing/vest_hazard.rsi/meta.json | 1 + Resources/Textures/Mob/human.rsi/male.png | Bin 0 -> 1705 bytes Resources/Textures/Mob/human.rsi/meta.json | 29 ++++ SpaceStation14Content.sln.DotSettings | 2 + engine | 2 +- 97 files changed, 1094 insertions(+), 273 deletions(-) create mode 100644 Content.Client/GameObjects/Components/Clothing/ClothingComponent.cs create mode 100644 Content.Client/GameObjects/Components/Items/ItemComponent.cs create mode 100644 Resources/Prototypes/Entities/Clothing/IDs.yml create mode 100644 Resources/Prototypes/Entities/Clothing/backpacks.yml create mode 100644 Resources/Prototypes/Entities/Clothing/belts.yml create mode 100644 Resources/Prototypes/Entities/Clothing/ears.yml create mode 100644 Resources/Prototypes/Entities/Clothing/eyes.yml create mode 100644 Resources/Prototypes/Entities/Clothing/gloves.yml create mode 100644 Resources/Prototypes/Entities/Clothing/helmets.yml create mode 100644 Resources/Prototypes/Entities/Clothing/masks.yml create mode 100644 Resources/Prototypes/Entities/Clothing/shoes.yml create mode 100644 Resources/Prototypes/Entities/Clothing/suits.yml create mode 100644 Resources/Prototypes/Entities/Clothing/uniforms.yml create mode 100644 Resources/Textures/Clothing/armor.rsi/armor.png create mode 100644 Resources/Textures/Clothing/armor.rsi/equipped-OUTERCLOTHING.png create mode 100644 Resources/Textures/Clothing/armor.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/armor.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/armor.rsi/meta.json create mode 100644 Resources/Textures/Clothing/backpack.rsi/backpack.png create mode 100644 Resources/Textures/Clothing/backpack.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Clothing/backpack.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/backpack.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/backpack.rsi/meta.json create mode 100644 Resources/Textures/Clothing/belt_utility.rsi/equipped-BELT.png create mode 100644 Resources/Textures/Clothing/belt_utility.rsi/meta.json create mode 100644 Resources/Textures/Clothing/belt_utility.rsi/utilitybelt.png create mode 100644 Resources/Textures/Clothing/gloves_black.rsi/black.png create mode 100644 Resources/Textures/Clothing/gloves_black.rsi/equipped-HAND.png create mode 100644 Resources/Textures/Clothing/gloves_black.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/gloves_black.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/gloves_black.rsi/meta.json create mode 100644 Resources/Textures/Clothing/gloves_yellow.rsi/equipped-HAND.png create mode 100644 Resources/Textures/Clothing/gloves_yellow.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/gloves_yellow.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/gloves_yellow.rsi/meta.json create mode 100644 Resources/Textures/Clothing/gloves_yellow.rsi/yellow.png create mode 100644 Resources/Textures/Clothing/headset.rsi/equipped-EARS.png create mode 100644 Resources/Textures/Clothing/headset.rsi/headset.png create mode 100644 Resources/Textures/Clothing/headset.rsi/meta.json create mode 100644 Resources/Textures/Clothing/helmet_sec.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Clothing/helmet_sec.rsi/helmet.png create mode 100644 Resources/Textures/Clothing/helmet_sec.rsi/meta.json create mode 100644 Resources/Textures/Clothing/mask_breath.rsi/breath.png create mode 100644 Resources/Textures/Clothing/mask_breath.rsi/equipped-MASK.png create mode 100644 Resources/Textures/Clothing/mask_breath.rsi/meta.json create mode 100644 Resources/Textures/Clothing/meson_scanners.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/meson_scanners.rsi/meson.png create mode 100644 Resources/Textures/Clothing/meson_scanners.rsi/meta.json create mode 100644 Resources/Textures/Clothing/shoes_black.rsi/black.png create mode 100644 Resources/Textures/Clothing/shoes_black.rsi/equipped-FEET.png create mode 100644 Resources/Textures/Clothing/shoes_black.rsi/meta.json create mode 100644 Resources/Textures/Clothing/shoes_white.rsi/equipped-FEET.png create mode 100644 Resources/Textures/Clothing/shoes_white.rsi/meta.json create mode 100644 Resources/Textures/Clothing/shoes_white.rsi/white.png create mode 100644 Resources/Textures/Clothing/sunglasses.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/sunglasses.rsi/meta.json create mode 100644 Resources/Textures/Clothing/sunglasses.rsi/sun.png create mode 100644 Resources/Textures/Clothing/sunglasses.rsi/sunglasses.png create mode 100644 Resources/Textures/Clothing/uniform_assistant.rsi/assistant.png create mode 100644 Resources/Textures/Clothing/uniform_assistant.rsi/equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/uniform_assistant.rsi/meta.json create mode 100644 Resources/Textures/Clothing/uniform_engineering.rsi/engine.png create mode 100644 Resources/Textures/Clothing/uniform_engineering.rsi/equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/uniform_engineering.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/uniform_engineering.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/uniform_engineering.rsi/meta.json create mode 100644 Resources/Textures/Clothing/uniform_grey.rsi/equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/uniform_grey.rsi/grey.png create mode 100644 Resources/Textures/Clothing/uniform_grey.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/uniform_grey.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/uniform_grey.rsi/meta.json create mode 100644 Resources/Textures/Clothing/uniform_janitor.rsi/equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/uniform_janitor.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/uniform_janitor.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/uniform_janitor.rsi/janitor.png create mode 100644 Resources/Textures/Clothing/uniform_janitor.rsi/meta.json create mode 100644 Resources/Textures/Clothing/vest_hazard.rsi/equipped-OUTERCLOTHING.png create mode 100644 Resources/Textures/Clothing/vest_hazard.rsi/hazard.png create mode 100644 Resources/Textures/Clothing/vest_hazard.rsi/meta.json create mode 100644 Resources/Textures/Mob/human.rsi/male.png create mode 100644 Resources/Textures/Mob/human.rsi/meta.json create mode 100644 SpaceStation14Content.sln.DotSettings diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj index b84a414408..e53fbe21b1 100644 --- a/Content.Client/Content.Client.csproj +++ b/Content.Client/Content.Client.csproj @@ -68,7 +68,9 @@ + + @@ -121,4 +123,4 @@ - + \ No newline at end of file diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 6e96ce1976..2a2f4ee15d 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -1,4 +1,5 @@ using Content.Client.GameObjects; +using Content.Client.GameObjects.Components.Clothing; using Content.Client.GameObjects.Components.Construction; using Content.Client.GameObjects.Components.Power; using Content.Client.GameObjects.Components.SmoothWalling; @@ -24,7 +25,6 @@ namespace Content.Client var factory = IoCManager.Resolve(); var prototypes = IoCManager.Resolve(); - factory.RegisterIgnore("Item"); factory.RegisterIgnore("Interactable"); factory.RegisterIgnore("Damageable"); factory.RegisterIgnore("Destructible"); @@ -48,7 +48,6 @@ namespace Content.Client factory.RegisterIgnore("MeleeWeapon"); factory.RegisterIgnore("Storeable"); - factory.RegisterIgnore("Clothing"); factory.RegisterIgnore("Material"); factory.RegisterIgnore("Stack"); @@ -61,6 +60,9 @@ namespace Content.Client factory.Register(); factory.Register(); factory.Register(); + factory.Register(); + factory.Register(); + factory.RegisterReference(); factory.RegisterIgnore("Construction"); factory.RegisterIgnore("Apc"); diff --git a/Content.Client/GameObjects/Components/Clothing/ClothingComponent.cs b/Content.Client/GameObjects/Components/Clothing/ClothingComponent.cs new file mode 100644 index 0000000000..6deb653030 --- /dev/null +++ b/Content.Client/GameObjects/Components/Clothing/ClothingComponent.cs @@ -0,0 +1,27 @@ +using Content.Shared.GameObjects.Components.Inventory; +using SS14.Client.Graphics; + +namespace Content.Client.GameObjects.Components.Clothing +{ + public class ClothingComponent : ItemComponent + { + public override string Name => "Clothing"; + + public (RSI rsi, RSI.StateId stateId)? GetEquippedStateInfo(EquipmentSlotDefines.SlotFlags slot) + { + if (RsiPath == null) + { + return null; + } + + var rsi = GetRSI(); + var stateId = EquippedPrefix != null ? $"{EquippedPrefix}-equipped-{slot}" : $"equipped-{slot}"; + if (rsi.TryGetState(stateId, out _)) + { + return (rsi, stateId); + } + + return null; + } + } +} diff --git a/Content.Client/GameObjects/Components/Inventory/ClientInventoryComponent.cs b/Content.Client/GameObjects/Components/Inventory/ClientInventoryComponent.cs index 6b3b85761d..04a03b8473 100644 --- a/Content.Client/GameObjects/Components/Inventory/ClientInventoryComponent.cs +++ b/Content.Client/GameObjects/Components/Inventory/ClientInventoryComponent.cs @@ -6,46 +6,118 @@ using SS14.Client.Interfaces.Input; using SS14.Client.UserInterface; using SS14.Client.UserInterface.Controls; using SS14.Client.UserInterface.CustomControls; -using SS14.Shared.ContentPack; using SS14.Shared.GameObjects; using SS14.Shared.Input; using SS14.Shared.Interfaces.GameObjects; using SS14.Shared.Interfaces.Network; using SS14.Shared.IoC; -using SS14.Shared.Log; -using SS14.Shared.Maths; using SS14.Shared.Serialization; using SS14.Shared.Utility; using System; using System.Collections.Generic; +using System.Linq; +using Content.Client.GameObjects.Components.Clothing; +using SS14.Shared.Interfaces.Reflection; using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; using static Content.Shared.GameObjects.SharedInventoryComponent.ClientInventoryMessage; -using static Content.Shared.GameObjects.SharedInventoryComponent.ServerInventoryMessage; namespace Content.Client.GameObjects { public class ClientInventoryComponent : SharedInventoryComponent { - private InventoryWindow Window; - private string TemplateName = "HumanInventory"; //stored for serialization purposes + private Dictionary _slots = new Dictionary(); + + private InventoryWindow _window; + private string _templateName = "HumanInventory"; //stored for serialization purposes private InputCmdHandler _openMenuCmdHandler; + private Inventory _inventory; + + private ISpriteComponent _sprite; public override void OnRemove() { base.OnRemove(); - Window.Dispose(); + _window.Dispose(); + } + + public override void OnAdd() + { + base.OnAdd(); + + _openMenuCmdHandler = InputCmdHandler.FromDelegate(session => { _window.AddToScreen(); _window.Open(); }); + } + + public override void Initialize() + { + base.Initialize(); + + var reflectionManager = IoCManager.Resolve(); + var type = reflectionManager.LooseGetType(_templateName); + DebugTools.Assert(type != null); + _inventory = (Inventory)Activator.CreateInstance(type); + + _window = new InventoryWindow(this); + _window.CreateInventory(_inventory); + + if (Owner.TryGetComponent(out _sprite)) + { + foreach (var mask in _inventory.SlotMasks.OrderBy(s => _inventory.SlotDrawingOrder(s))) + { + if (mask == Slots.NONE) + { + continue; + } + _sprite.LayerMapReserveBlank(mask); + } + } + + // Component state already came in but we couldn't set anything visually because, well, we didn't initialize yet. + foreach (var (slot, entity) in _slots) + { + _setSlot(slot, entity); + } } public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); - Window = new InventoryWindow(this); - _openMenuCmdHandler = InputCmdHandler.FromDelegate(session => { Window.AddToScreen(); Window.Open(); }); - serializer.DataField(ref TemplateName, "Template", "HumanInventory"); - Window.CreateInventory(TemplateName); + serializer.DataField(ref _templateName, "Template", "HumanInventory"); + } + + public override void HandleComponentState(ComponentState state) + { + base.HandleComponentState(state); + var cast = (InventoryComponentState) state; + + var doneSlots = new HashSet(); + + var entityManager = IoCManager.Resolve(); + + foreach (var (slot, entityUid) in cast.Entities) + { + if (_slots.ContainsKey(slot)) + { + _slots.Remove(slot); + _clearSlot(slot); + } + + var entity = entityManager.GetEntity(entityUid); + _slots[slot] = entity; + _setSlot(slot, entity); + doneSlots.Add(slot); + } + + foreach (var slot in _slots.Keys.ToList()) + { + if (!doneSlots.Contains(slot)) + { + _clearSlot(slot); + _slots.Remove(slot); + } + } } public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) @@ -53,18 +125,6 @@ namespace Content.Client.GameObjects var inputMgr = IoCManager.Resolve(); switch (message) { - //Updates what we are storing in UI slots - case ServerInventoryMessage msg: - if (msg.Updatetype == ServerInventoryUpdate.Addition) - { - Window.AddToSlot(msg); - } - else if (msg.Updatetype == ServerInventoryUpdate.Removal) - { - Window.RemoveFromSlot(msg); - } - break; - case PlayerAttachedMsg _: inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, _openMenuCmdHandler); break; @@ -75,6 +135,34 @@ namespace Content.Client.GameObjects } } + private void _setSlot(Slots slot, IEntity entity) + { + if (_sprite != null && entity.TryGetComponent(out ClothingComponent clothing)) + { + var flag = SlotMasks[slot]; + var data = clothing.GetEquippedStateInfo(flag); + if (data == null) + { + _sprite.LayerSetVisible(slot, false); + } + else + { + var (rsi, state) = data.Value; + _sprite.LayerSetVisible(slot, true); + _sprite.LayerSetRSI(slot, rsi); + _sprite.LayerSetState(slot, state); + } + } + + _window?.AddToSlot(slot, entity); + } + + private void _clearSlot(Slots slot) + { + _window?.RemoveFromSlot(slot); + _sprite?.LayerSetVisible(slot, false); + } + public void SendUnequipMessage(Slots slot) { var unequipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Unequip); @@ -109,14 +197,10 @@ namespace Content.Client.GameObjects } /// - /// Creates a grid container filled with slot buttons loaded from an inventory template + /// Creates a grid container filled with slot buttons loaded from an inventory template /// - /// - public void CreateInventory(string TemplateName) + public void CreateInventory(Inventory inventory) { - Type type = AppDomain.CurrentDomain.GetAssemblyByName("Content.Shared").GetType("Content.Shared.GameObjects." + TemplateName); - Inventory inventory = (Inventory)Activator.CreateInstance(type); - elements_x = inventory.Columns; GridContainer = (GridContainer)Contents.GetChild("PanelContainer").GetChild("CenterContainer").GetChild("GridContainer"); @@ -151,14 +235,11 @@ namespace Content.Client.GameObjects /// /// Adds the item we have equipped to the slot texture and prepares the slot button for removal /// - /// - public void AddToSlot(ServerInventoryMessage message) + public void AddToSlot(Slots slot, IEntity entity) { - InventoryButton button = InventorySlots[message.Inventoryslot]; - var entity = IoCManager.Resolve().GetEntity(message.EntityUid); + var button = InventorySlots[slot]; - button.EntityUid = message.EntityUid; - var container = button.GetChild("CenterContainer"); + button.EntityUid = entity.Uid; button.GetChild